diff --git a/.gitignore b/.gitignore index b5c418b..fa471f8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,11 @@ FlameGraph/ /perf.data.old **/.auctex-auto/ **/build/ +/.odbc/ +**/__pycache__ +/build-tsan/ +/benchmarks/datasets/ssb/generated/ +/benchmarks/datasets/ssb/raw/ +/.plans/ +/report/build-practice/ +/report/build-vkr/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4c644f5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "research/datasets"] + path = research/datasets + url = git@github.com:ronaldbradford/data.git +[submodule "research/imdb-sqlserver"] + path = research/imdb-sqlserver + url = git@github.com:sqlsunday/imdb-to-sqlserver.git +[submodule "research/ssb-dbgen"] + path = research/ssb-dbgen + url = git@github.com:stewkk/ssb-dbgen.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 96a5572..3eefa3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,10 @@ if (SANITIZE_THREADS) add_link_options(-fsanitize=thread) endif() +if(STEWKK_SQL_DEBUG) + add_definitions(-DSTEWKK_SQL_DEBUG) +endif() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) diff --git a/Makefile b/Makefile index b7b89d1..846caa7 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,16 @@ PARSER_SOURCE_DIR := $(CURRENT_DIR)/src/stewkk/sql/logic/parser build: cmake --build build -- -j 6 +sanitize: + cmake --build build-sanitizers -- -j 8 + codegen: @antlr -Dlanguage=Cpp -visitor -o $(CODEGEN_DIR) -package stewkk::sql::codegen $(PARSER_SOURCE_DIR)/PostgreSQLParser.g4 $(PARSER_SOURCE_DIR)/PostgreSQLLexer.g4 -.PHONY: codegen build +test-ssb-converter: + pytest benchmarks/datasets/ssb + +dot: + dot -T png -O .plans/*.dot + +.PHONY: codegen build sanitize test-ssb-converter diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 8cfaea0..a58c2a2 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -1,6 +1,6 @@ include(FetchGBenchmark) -add_executable(benchmarks main.cpp) +add_executable(benchmarks main.cpp operator_cost.cpp) target_compile_features(benchmarks PRIVATE cxx_std_23) set_target_properties(benchmarks PROPERTIES CXX_STANDART 23 diff --git a/benchmarks/datasets/ssb/README.md b/benchmarks/datasets/ssb/README.md new file mode 100644 index 0000000..3ec27c8 --- /dev/null +++ b/benchmarks/datasets/ssb/README.md @@ -0,0 +1,59 @@ +# Star Schema Benchmark data + +This directory contains the schema and conversion tooling for Star Schema +Benchmark data. It does not download or build an SSB data generator; generate +the `.tbl` files with an external SSB dbgen-compatible tool first. + +## Convert `.tbl` files + +Expected input files: + +- `lineorder.tbl` +- `customer.tbl` +- `supplier.tbl` +- `part.tbl` +- `date.tbl` + +Convert them to the CSV format used by this project: + +```sh +python3 benchmarks/datasets/ssb/convert_tbl_to_csv.py \ + --input-dir /path/to/ssb/tbl \ + --output-dir benchmarks/datasets/ssb/generated/sf1 +``` + +The converter reads `schema.json` for table and column definitions. Output CSV +headers use `column:type`, for example `lo_orderkey:int`. String columns are +preserved as `string` values in the generated CSV. + +Generated full-scale data should stay under `benchmarks/datasets/ssb/generated/` +or another ignored location. + +## Queries + +The standard 13 SSB query templates are in `queries/`. They are written in the +subset supported by this engine: explicit joins, table aliases, string +predicates, `BETWEEN`, list `IN`, `SUM`, `COUNT`, `GROUP BY`, and `ORDER BY`. + +Run one query through the CLI: + +```sh +./build/bin/sql --data-dir benchmarks/datasets/ssb/generated/sf1 \ + < benchmarks/datasets/ssb/queries/q1.1.sql +``` + +Run SSB benchmarks after generating data: + +```sh +SSB_DATA_DIR=benchmarks/datasets/ssb/generated/sf1 \ + ./build/bin/benchmarks --benchmark_filter='SSB/' +``` + +The JIT expression executor still rejects string and `IN` expressions, so the +SSB benchmark registrations currently use the interpreted expression executor. + +## Test the converter + +```sh +make test-ssb-converter +``` diff --git a/benchmarks/datasets/ssb/convert_tbl_to_csv.py b/benchmarks/datasets/ssb/convert_tbl_to_csv.py new file mode 100644 index 0000000..e7a6c7a --- /dev/null +++ b/benchmarks/datasets/ssb/convert_tbl_to_csv.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +"""Convert Star Schema Benchmark .tbl files to this project's CSV format.""" + +from __future__ import annotations + +import argparse +import csv +import json +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable + + +SUPPORTED_TYPES = {"int", "string"} + + +@dataclass(frozen=True) +class Column: + name: str + type: str + + +@dataclass(frozen=True) +class Table: + name: str + columns: tuple[Column, ...] + + +class ConversionError(RuntimeError): + pass + + +def load_schema(path: Path) -> list[Table]: + with path.open(encoding="utf-8") as f: + raw = json.load(f) + + tables = [] + for table_raw in raw.get("tables", []): + columns = [] + for column_raw in table_raw.get("columns", []): + column_type = column_raw["type"] + if column_type not in SUPPORTED_TYPES: + raise ConversionError( + f"{path}: unsupported type {column_type!r} in table {table_raw['name']}" + ) + columns.append(Column(column_raw["name"], column_type)) + if not columns: + raise ConversionError(f"{path}: table {table_raw.get('name')!r} has no columns") + tables.append(Table(table_raw["name"], tuple(columns))) + + if not tables: + raise ConversionError(f"{path}: schema does not define any tables") + return tables + + +def parse_tbl_line(line: str) -> list[str]: + fields = line.rstrip("\r\n").split("|") + if fields and fields[-1] == "": + fields.pop() + return fields + + +def validate_row(table: Table, row: list[str], input_path: Path, line_no: int) -> None: + if len(row) != len(table.columns): + raise ConversionError( + f"{input_path}:{line_no}: expected {len(table.columns)} fields for " + f"{table.name}, got {len(row)}" + ) + + for value, column in zip(row, table.columns, strict=True): + if column.type == "int": + try: + int(value) + except ValueError as exc: + raise ConversionError( + f"{input_path}:{line_no}: {column.name} expects int, got {value!r}" + ) from exc + + +def header(table: Table) -> list[str]: + return [f"{column.name}:{column.type}" for column in table.columns] + + +def convert_table(table: Table, input_dir: Path, output_dir: Path) -> int: + input_path = input_dir / f"{table.name}.tbl" + output_path = output_dir / f"{table.name}.csv" + if not input_path.exists(): + raise ConversionError(f"missing input file: {input_path}") + + rows = 0 + with input_path.open(encoding="utf-8", newline="") as src, output_path.open( + "w", encoding="utf-8", newline="" + ) as dst: + writer = csv.writer(dst, lineterminator="\n") + writer.writerow(header(table)) + for line_no, line in enumerate(src, start=1): + row = parse_tbl_line(line) + validate_row(table, row, input_path, line_no) + writer.writerow(row) + rows += 1 + return rows + + +def convert_all(schema_path: Path, input_dir: Path, output_dir: Path) -> dict[str, int]: + tables = load_schema(schema_path) + output_dir.mkdir(parents=True, exist_ok=True) + return { + table.name: convert_table(table, input_dir, output_dir) + for table in tables + } + + +def parse_args(argv: Iterable[str]) -> argparse.Namespace: + default_schema = Path(__file__).with_name("schema.json") + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--input-dir", required=True, type=Path, help="Directory containing SSB .tbl files") + parser.add_argument("--output-dir", required=True, type=Path, help="Directory for generated CSV files") + parser.add_argument("--schema", default=default_schema, type=Path, help="Schema JSON path") + return parser.parse_args(argv) + + +def main(argv: Iterable[str] = sys.argv[1:]) -> int: + args = parse_args(argv) + try: + counts = convert_all(args.schema, args.input_dir, args.output_dir) + except ConversionError as exc: + print(f"error: {exc}", file=sys.stderr) + return 1 + + for table_name, rows in counts.items(): + print(f"wrote {args.output_dir / (table_name + '.csv')} ({rows} rows)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/datasets/ssb/fixtures/expected_csv/customer.csv b/benchmarks/datasets/ssb/fixtures/expected_csv/customer.csv new file mode 100644 index 0000000..82689fb --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/expected_csv/customer.csv @@ -0,0 +1,3 @@ +c_custkey:int,c_name:string,c_address:string,c_city:string,c_nation:string,c_region:string,c_phone:string,c_mktsegment:string +10,Customer#000000010,Addr 10,UNITED KI1,UNITED KINGDOM,EUROPE,10-111-111-1111,BUILDING +20,Customer#000000020,"Addr, 20",BRAZIL 1,BRAZIL,AMERICA,20-222-222-2222,AUTOMOBILE diff --git a/benchmarks/datasets/ssb/fixtures/expected_csv/date.csv b/benchmarks/datasets/ssb/fixtures/expected_csv/date.csv new file mode 100644 index 0000000..f898834 --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/expected_csv/date.csv @@ -0,0 +1,3 @@ +d_datekey:int,d_date:string,d_dayofweek:string,d_month:string,d_year:int,d_yearmonthnum:int,d_yearmonth:string,d_daynuminweek:int,d_daynuminmonth:int,d_daynuminyear:int,d_monthnuminyear:int,d_weeknuminyear:int,d_sellingseason:string,d_lastdayinweekfl:int,d_lastdayinmonthfl:int,d_holidayfl:int,d_weekdayfl:int +19940101,"January 1, 1994",Saturday,January,1994,199401,Jan1994,6,1,1,1,1,Winter,0,0,1,0 +19940102,"January 2, 1994",Sunday,January,1994,199401,Jan1994,7,2,2,1,1,Winter,1,0,0,0 diff --git a/benchmarks/datasets/ssb/fixtures/expected_csv/lineorder.csv b/benchmarks/datasets/ssb/fixtures/expected_csv/lineorder.csv new file mode 100644 index 0000000..38f03a6 --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/expected_csv/lineorder.csv @@ -0,0 +1,3 @@ +lo_orderkey:int,lo_linenumber:int,lo_custkey:int,lo_partkey:int,lo_suppkey:int,lo_orderdate:int,lo_orderpriority:string,lo_shippriority:string,lo_quantity:int,lo_extendedprice:int,lo_ordtotalprice:int,lo_discount:int,lo_revenue:int,lo_supplycost:int,lo_tax:int,lo_commitdate:int,lo_shipmode:string +1,1,10,100,1000,19940101,1-URGENT,0,5,10000,12000,3,9700,6000,1,19940105,AIR +2,1,20,200,2000,19940102,2-HIGH,0,7,20000,23000,4,19200,12000,2,19940106,RAIL diff --git a/benchmarks/datasets/ssb/fixtures/expected_csv/part.csv b/benchmarks/datasets/ssb/fixtures/expected_csv/part.csv new file mode 100644 index 0000000..6c6dc3a --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/expected_csv/part.csv @@ -0,0 +1,3 @@ +p_partkey:int,p_name:string,p_mfgr:string,p_category:string,p_brand:string,p_color:string,p_type:string,p_size:int,p_container:string +100,almond antique,MFGR#1,MFGR#11,MFGR#111,almond,STANDARD ANODIZED TIN,3,SM CASE +200,green burnished,MFGR#2,MFGR#22,MFGR#222,green,PROMO POLISHED STEEL,7,LG BOX diff --git a/benchmarks/datasets/ssb/fixtures/expected_csv/supplier.csv b/benchmarks/datasets/ssb/fixtures/expected_csv/supplier.csv new file mode 100644 index 0000000..c5f58d1 --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/expected_csv/supplier.csv @@ -0,0 +1,3 @@ +s_suppkey:int,s_name:string,s_address:string,s_city:string,s_nation:string,s_region:string,s_phone:string +1000,Supplier#000001000,Supplier Addr 1000,RUSSIA 1,RUSSIA,EUROPE,30-333-333-3333 +2000,Supplier#000002000,Supplier Addr 2000,ARGENTINA1,ARGENTINA,AMERICA,40-444-444-4444 diff --git a/benchmarks/datasets/ssb/fixtures/tbl/customer.tbl b/benchmarks/datasets/ssb/fixtures/tbl/customer.tbl new file mode 100644 index 0000000..2f1c800 --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/tbl/customer.tbl @@ -0,0 +1,2 @@ +10|Customer#000000010|Addr 10|UNITED KI1|UNITED KINGDOM|EUROPE|10-111-111-1111|BUILDING| +20|Customer#000000020|Addr, 20|BRAZIL 1|BRAZIL|AMERICA|20-222-222-2222|AUTOMOBILE| diff --git a/benchmarks/datasets/ssb/fixtures/tbl/date.tbl b/benchmarks/datasets/ssb/fixtures/tbl/date.tbl new file mode 100644 index 0000000..99b880c --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/tbl/date.tbl @@ -0,0 +1,2 @@ +19940101|January 1, 1994|Saturday|January|1994|199401|Jan1994|6|1|1|1|1|Winter|0|0|1|0| +19940102|January 2, 1994|Sunday|January|1994|199401|Jan1994|7|2|2|1|1|Winter|1|0|0|0| diff --git a/benchmarks/datasets/ssb/fixtures/tbl/lineorder.tbl b/benchmarks/datasets/ssb/fixtures/tbl/lineorder.tbl new file mode 100644 index 0000000..8219280 --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/tbl/lineorder.tbl @@ -0,0 +1,2 @@ +1|1|10|100|1000|19940101|1-URGENT|0|5|10000|12000|3|9700|6000|1|19940105|AIR| +2|1|20|200|2000|19940102|2-HIGH|0|7|20000|23000|4|19200|12000|2|19940106|RAIL| diff --git a/benchmarks/datasets/ssb/fixtures/tbl/part.tbl b/benchmarks/datasets/ssb/fixtures/tbl/part.tbl new file mode 100644 index 0000000..064fd5e --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/tbl/part.tbl @@ -0,0 +1,2 @@ +100|almond antique|MFGR#1|MFGR#11|MFGR#111|almond|STANDARD ANODIZED TIN|3|SM CASE| +200|green burnished|MFGR#2|MFGR#22|MFGR#222|green|PROMO POLISHED STEEL|7|LG BOX| diff --git a/benchmarks/datasets/ssb/fixtures/tbl/supplier.tbl b/benchmarks/datasets/ssb/fixtures/tbl/supplier.tbl new file mode 100644 index 0000000..07a8143 --- /dev/null +++ b/benchmarks/datasets/ssb/fixtures/tbl/supplier.tbl @@ -0,0 +1,2 @@ +1000|Supplier#000001000|Supplier Addr 1000|RUSSIA 1|RUSSIA|EUROPE|30-333-333-3333| +2000|Supplier#000002000|Supplier Addr 2000|ARGENTINA1|ARGENTINA|AMERICA|40-444-444-4444| diff --git a/benchmarks/datasets/ssb/queries/q1.1.sql b/benchmarks/datasets/ssb/queries/q1.1.sql new file mode 100644 index 0000000..5e812f9 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q1.1.sql @@ -0,0 +1,6 @@ +SELECT SUM(lo.lo_extendedprice * lo.lo_discount) AS revenue +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +WHERE d.d_year = 1993 + AND lo.lo_discount BETWEEN 1 AND 3 + AND lo.lo_quantity < 25; diff --git a/benchmarks/datasets/ssb/queries/q1.2.sql b/benchmarks/datasets/ssb/queries/q1.2.sql new file mode 100644 index 0000000..9a07697 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q1.2.sql @@ -0,0 +1,6 @@ +SELECT SUM(lo.lo_extendedprice * lo.lo_discount) AS revenue +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +WHERE d.d_yearmonthnum = 199401 + AND lo.lo_discount BETWEEN 4 AND 6 + AND lo.lo_quantity BETWEEN 26 AND 35; diff --git a/benchmarks/datasets/ssb/queries/q1.3.sql b/benchmarks/datasets/ssb/queries/q1.3.sql new file mode 100644 index 0000000..d001733 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q1.3.sql @@ -0,0 +1,7 @@ +SELECT SUM(lo.lo_extendedprice * lo.lo_discount) AS revenue +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +WHERE d.d_weeknuminyear = 6 + AND d.d_year = 1994 + AND lo.lo_discount BETWEEN 5 AND 7 + AND lo.lo_quantity BETWEEN 26 AND 35; diff --git a/benchmarks/datasets/ssb/queries/q2.1.sql b/benchmarks/datasets/ssb/queries/q2.1.sql new file mode 100644 index 0000000..37a59f2 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q2.1.sql @@ -0,0 +1,9 @@ +SELECT SUM(lo.lo_revenue) AS lo_revenue, d.d_year, p.p_brand +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +JOIN part AS p ON lo.lo_partkey = p.p_partkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +WHERE p.p_category = 'MFGR#12' + AND s.s_region = 'AMERICA' +GROUP BY d.d_year, p.p_brand +ORDER BY d.d_year, p.p_brand; diff --git a/benchmarks/datasets/ssb/queries/q2.2.sql b/benchmarks/datasets/ssb/queries/q2.2.sql new file mode 100644 index 0000000..9488eaa --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q2.2.sql @@ -0,0 +1,9 @@ +SELECT SUM(lo.lo_revenue) AS lo_revenue, d.d_year, p.p_brand +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +JOIN part AS p ON lo.lo_partkey = p.p_partkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +WHERE p.p_brand BETWEEN 'MFGR#2221' AND 'MFGR#2228' + AND s.s_region = 'ASIA' +GROUP BY d.d_year, p.p_brand +ORDER BY d.d_year, p.p_brand; diff --git a/benchmarks/datasets/ssb/queries/q2.3.sql b/benchmarks/datasets/ssb/queries/q2.3.sql new file mode 100644 index 0000000..88d4b48 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q2.3.sql @@ -0,0 +1,9 @@ +SELECT SUM(lo.lo_revenue) AS lo_revenue, d.d_year, p.p_brand +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +JOIN part AS p ON lo.lo_partkey = p.p_partkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +WHERE p.p_brand = 'MFGR#2221' + AND s.s_region = 'EUROPE' +GROUP BY d.d_year, p.p_brand +ORDER BY d.d_year, p.p_brand; diff --git a/benchmarks/datasets/ssb/queries/q3.1.sql b/benchmarks/datasets/ssb/queries/q3.1.sql new file mode 100644 index 0000000..f615732 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q3.1.sql @@ -0,0 +1,10 @@ +SELECT c.c_nation, s.s_nation, d.d_year, SUM(lo.lo_revenue) AS lo_revenue +FROM lineorder AS lo +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +WHERE c.c_region = 'ASIA' + AND s.s_region = 'ASIA' + AND d.d_year BETWEEN 1992 AND 1997 +GROUP BY c.c_nation, s.s_nation, d.d_year +ORDER BY d.d_year, lo_revenue DESC; diff --git a/benchmarks/datasets/ssb/queries/q3.2.sql b/benchmarks/datasets/ssb/queries/q3.2.sql new file mode 100644 index 0000000..484de9b --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q3.2.sql @@ -0,0 +1,10 @@ +SELECT c.c_city, s.s_city, d.d_year, SUM(lo.lo_revenue) AS lo_revenue +FROM lineorder AS lo +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +WHERE c.c_nation = 'UNITED STATES' + AND s.s_nation = 'UNITED STATES' + AND d.d_year BETWEEN 1992 AND 1997 +GROUP BY c.c_city, s.s_city, d.d_year +ORDER BY d.d_year, lo_revenue DESC; diff --git a/benchmarks/datasets/ssb/queries/q3.3.sql b/benchmarks/datasets/ssb/queries/q3.3.sql new file mode 100644 index 0000000..6900fba --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q3.3.sql @@ -0,0 +1,10 @@ +SELECT c.c_city, s.s_city, d.d_year, SUM(lo.lo_revenue) AS lo_revenue +FROM lineorder AS lo +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +WHERE c.c_city IN ('UNITED KI1', 'UNITED KI5') + AND s.s_city IN ('UNITED KI1', 'UNITED KI5') + AND d.d_year BETWEEN 1992 AND 1997 +GROUP BY c.c_city, s.s_city, d.d_year +ORDER BY d.d_year, lo_revenue DESC; diff --git a/benchmarks/datasets/ssb/queries/q3.4.sql b/benchmarks/datasets/ssb/queries/q3.4.sql new file mode 100644 index 0000000..c220c29 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q3.4.sql @@ -0,0 +1,10 @@ +SELECT c.c_city, s.s_city, d.d_year, SUM(lo.lo_revenue) AS lo_revenue +FROM lineorder AS lo +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +WHERE c.c_city IN ('UNITED KI1', 'UNITED KI5') + AND s.s_city IN ('UNITED KI1', 'UNITED KI5') + AND d.d_yearmonth = 'Dec1997' +GROUP BY c.c_city, s.s_city, d.d_year +ORDER BY d.d_year, lo_revenue DESC; diff --git a/benchmarks/datasets/ssb/queries/q4.1.sql b/benchmarks/datasets/ssb/queries/q4.1.sql new file mode 100644 index 0000000..6efbe87 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q4.1.sql @@ -0,0 +1,11 @@ +SELECT d.d_year, c.c_nation, SUM(lo.lo_revenue - lo.lo_supplycost) AS profit +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN part AS p ON lo.lo_partkey = p.p_partkey +WHERE c.c_region = 'AMERICA' + AND s.s_region = 'AMERICA' + AND p.p_mfgr IN ('MFGR#1', 'MFGR#2') +GROUP BY d.d_year, c.c_nation +ORDER BY d.d_year, c.c_nation; diff --git a/benchmarks/datasets/ssb/queries/q4.2.sql b/benchmarks/datasets/ssb/queries/q4.2.sql new file mode 100644 index 0000000..09b04b9 --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q4.2.sql @@ -0,0 +1,12 @@ +SELECT d.d_year, s.s_nation, p.p_category, SUM(lo.lo_revenue - lo.lo_supplycost) AS profit +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN part AS p ON lo.lo_partkey = p.p_partkey +WHERE c.c_region = 'AMERICA' + AND s.s_region = 'AMERICA' + AND d.d_year IN (1997, 1998) + AND p.p_mfgr IN ('MFGR#1', 'MFGR#2') +GROUP BY d.d_year, s.s_nation, p.p_category +ORDER BY d.d_year, s.s_nation, p.p_category; diff --git a/benchmarks/datasets/ssb/queries/q4.3.sql b/benchmarks/datasets/ssb/queries/q4.3.sql new file mode 100644 index 0000000..a83e97d --- /dev/null +++ b/benchmarks/datasets/ssb/queries/q4.3.sql @@ -0,0 +1,11 @@ +SELECT d.d_year, s.s_city, p.p_brand, SUM(lo.lo_revenue - lo.lo_supplycost) AS profit +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN part AS p ON lo.lo_partkey = p.p_partkey +WHERE s.s_nation = 'UNITED STATES' + AND d.d_year IN (1997, 1998) + AND p.p_category = 'MFGR#14' +GROUP BY d.d_year, s.s_city, p.p_brand +ORDER BY d.d_year, s.s_city, p.p_brand; diff --git a/benchmarks/datasets/ssb/schema.json b/benchmarks/datasets/ssb/schema.json new file mode 100644 index 0000000..0bf0a2d --- /dev/null +++ b/benchmarks/datasets/ssb/schema.json @@ -0,0 +1,87 @@ +{ + "tables": [ + { + "name": "lineorder", + "columns": [ + {"name": "lo_orderkey", "type": "int"}, + {"name": "lo_linenumber", "type": "int"}, + {"name": "lo_custkey", "type": "int"}, + {"name": "lo_partkey", "type": "int"}, + {"name": "lo_suppkey", "type": "int"}, + {"name": "lo_orderdate", "type": "int"}, + {"name": "lo_orderpriority", "type": "string"}, + {"name": "lo_shippriority", "type": "string"}, + {"name": "lo_quantity", "type": "int"}, + {"name": "lo_extendedprice", "type": "int"}, + {"name": "lo_ordtotalprice", "type": "int"}, + {"name": "lo_discount", "type": "int"}, + {"name": "lo_revenue", "type": "int"}, + {"name": "lo_supplycost", "type": "int"}, + {"name": "lo_tax", "type": "int"}, + {"name": "lo_commitdate", "type": "int"}, + {"name": "lo_shipmode", "type": "string"} + ] + }, + { + "name": "customer", + "columns": [ + {"name": "c_custkey", "type": "int"}, + {"name": "c_name", "type": "string"}, + {"name": "c_address", "type": "string"}, + {"name": "c_city", "type": "string"}, + {"name": "c_nation", "type": "string"}, + {"name": "c_region", "type": "string"}, + {"name": "c_phone", "type": "string"}, + {"name": "c_mktsegment", "type": "string"} + ] + }, + { + "name": "supplier", + "columns": [ + {"name": "s_suppkey", "type": "int"}, + {"name": "s_name", "type": "string"}, + {"name": "s_address", "type": "string"}, + {"name": "s_city", "type": "string"}, + {"name": "s_nation", "type": "string"}, + {"name": "s_region", "type": "string"}, + {"name": "s_phone", "type": "string"} + ] + }, + { + "name": "part", + "columns": [ + {"name": "p_partkey", "type": "int"}, + {"name": "p_name", "type": "string"}, + {"name": "p_mfgr", "type": "string"}, + {"name": "p_category", "type": "string"}, + {"name": "p_brand", "type": "string"}, + {"name": "p_color", "type": "string"}, + {"name": "p_type", "type": "string"}, + {"name": "p_size", "type": "int"}, + {"name": "p_container", "type": "string"} + ] + }, + { + "name": "date", + "columns": [ + {"name": "d_datekey", "type": "int"}, + {"name": "d_date", "type": "string"}, + {"name": "d_dayofweek", "type": "string"}, + {"name": "d_month", "type": "string"}, + {"name": "d_year", "type": "int"}, + {"name": "d_yearmonthnum", "type": "int"}, + {"name": "d_yearmonth", "type": "string"}, + {"name": "d_daynuminweek", "type": "int"}, + {"name": "d_daynuminmonth", "type": "int"}, + {"name": "d_daynuminyear", "type": "int"}, + {"name": "d_monthnuminyear", "type": "int"}, + {"name": "d_weeknuminyear", "type": "int"}, + {"name": "d_sellingseason", "type": "string"}, + {"name": "d_lastdayinweekfl", "type": "int"}, + {"name": "d_lastdayinmonthfl", "type": "int"}, + {"name": "d_holidayfl", "type": "int"}, + {"name": "d_weekdayfl", "type": "int"} + ] + } + ] +} diff --git a/benchmarks/datasets/ssb/test_convert_tbl_to_csv.py b/benchmarks/datasets/ssb/test_convert_tbl_to_csv.py new file mode 100644 index 0000000..96d00d8 --- /dev/null +++ b/benchmarks/datasets/ssb/test_convert_tbl_to_csv.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +import shutil +import subprocess +import sys +from pathlib import Path + + +SSB_DIR = Path(__file__).resolve().parent +CONVERTER = SSB_DIR / "convert_tbl_to_csv.py" +SCHEMA = SSB_DIR / "schema.json" +FIXTURE_TBL = SSB_DIR / "fixtures" / "tbl" +EXPECTED_CSV = SSB_DIR / "fixtures" / "expected_csv" + + +def run_converter(input_dir: Path, output_dir: Path) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [ + sys.executable, + str(CONVERTER), + "--schema", + str(SCHEMA), + "--input-dir", + str(input_dir), + "--output-dir", + str(output_dir), + ], + text=True, + capture_output=True, + check=False, + ) + + +def test_converts_all_ssb_tables(tmp_path: Path) -> None: + output_dir = tmp_path / "out" + + result = run_converter(FIXTURE_TBL, output_dir) + + assert result.returncode == 0, result.stderr + expected_files = sorted(path.name for path in EXPECTED_CSV.glob("*.csv")) + actual_files = sorted(path.name for path in output_dir.glob("*.csv")) + assert actual_files == expected_files + for filename in expected_files: + assert (output_dir / filename).read_text() == (EXPECTED_CSV / filename).read_text() + + +def test_rejects_malformed_row_width(tmp_path: Path) -> None: + input_dir = tmp_path / "tbl" + shutil.copytree(FIXTURE_TBL, input_dir) + with (input_dir / "lineorder.tbl").open("a", encoding="utf-8") as f: + f.write("3|too-few-fields|\n") + + result = run_converter(input_dir, tmp_path / "out") + + assert result.returncode != 0 + assert "expected 17 fields for lineorder" in result.stderr + + +def test_rejects_missing_input_file(tmp_path: Path) -> None: + input_dir = tmp_path / "tbl" + shutil.copytree(FIXTURE_TBL, input_dir) + (input_dir / "supplier.tbl").unlink() + + result = run_converter(input_dir, tmp_path / "out") + + assert result.returncode != 0 + assert "missing input file" in result.stderr + assert "supplier.tbl" in result.stderr + + +def test_rejects_invalid_integer(tmp_path: Path) -> None: + input_dir = tmp_path / "tbl" + shutil.copytree(FIXTURE_TBL, input_dir) + (input_dir / "date.tbl").write_text( + "not-an-int|January 1, 1994|Saturday|January|1994|199401|Jan1994|6|1|1|1|1|Winter|0|0|1|0|\n", + encoding="utf-8", + ) + + result = run_converter(input_dir, tmp_path / "out") + + assert result.returncode != 0 + assert "d_datekey expects int" in result.stderr diff --git a/benchmarks/datasets/ssb/test_queries.py b/benchmarks/datasets/ssb/test_queries.py new file mode 100644 index 0000000..34982fe --- /dev/null +++ b/benchmarks/datasets/ssb/test_queries.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import subprocess +from pathlib import Path + +import pytest + + +SSB_DIR = Path(__file__).resolve().parent +PROJECT_DIR = SSB_DIR.parents[2] +SQL_CLI = PROJECT_DIR / "build" / "bin" / "sql" +QUERY_DIR = SSB_DIR / "queries" +EXPECTED_CSV = SSB_DIR / "fixtures" / "expected_csv" + +SSB_QUERIES = tuple(sorted(QUERY_DIR.glob("*.sql"))) + + +@pytest.mark.parametrize("query_path", SSB_QUERIES, ids=lambda p: p.stem) +def test_ssb_query_executes_against_expected_csv(query_path: Path) -> None: + assert SQL_CLI.exists(), f"missing SQL CLI: {SQL_CLI}; build the sql target first" + + result = subprocess.run( + [str(SQL_CLI), "--data-dir", str(EXPECTED_CSV)], + input=query_path.read_text(encoding="utf-8"), + text=True, + capture_output=True, + check=False, + ) + + assert result.returncode == 0, ( + f"{query_path.name} failed with exit code {result.returncode}\n" + f"stderr:\n{result.stderr}\n" + f"stdout:\n{result.stdout}" + ) diff --git a/benchmarks/generate-multiway-data.sh b/benchmarks/generate-multiway-data.sh new file mode 100755 index 0000000..0c58b92 --- /dev/null +++ b/benchmarks/generate-multiway-data.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Generates three CSV tables for multi-way join benchmarking with skewed +# cardinalities: regions (10) << customers (500) << orders (5000). +# +# Schema (all int): +# regions(id) +# customers(id, region_id) -- region_id is FK into regions +# orders(id, customer_id) -- customer_id is FK into customers +# +# Idempotent: overwrites existing files at OUT_DIR. + +set -euo pipefail + +OUT_DIR="${1:-$(dirname "$0")/../test/static/executor/test_data}" + +n_regions=10 +n_customers=500 +n_orders=5000 + +regions="$OUT_DIR/regions.csv" +customers="$OUT_DIR/customers.csv" +orders="$OUT_DIR/orders.csv" + +{ + echo 'id:int' + seq 1 "$n_regions" +} > "$regions" + +{ + echo 'id:int,region_id:int' + awk -v n="$n_customers" -v r="$n_regions" 'BEGIN { + srand(1); + for (i = 1; i <= n; ++i) printf "%d,%d\n", i, 1 + int(rand() * r); + }' +} > "$customers" + +{ + echo 'id:int,customer_id:int' + awk -v n="$n_orders" -v c="$n_customers" 'BEGIN { + srand(2); + for (i = 1; i <= n; ++i) printf "%d,%d\n", i, 1 + int(rand() * c); + }' +} > "$orders" + +echo "wrote $regions ($(wc -l < "$regions") lines)" +echo "wrote $customers ($(wc -l < "$customers") lines)" +echo "wrote $orders ($(wc -l < "$orders") lines)" diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp index 4356209..ac93677 100644 --- a/benchmarks/main.cpp +++ b/benchmarks/main.cpp @@ -1,19 +1,193 @@ #include +#include +#include #include +#include +#include #include +#include #include +#include +#include +#include +#include #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include namespace stewkk::sql { +namespace { + +PhysicalPlanNode ToPhysicalPlan(const Operator& op) { + return std::visit(utils::Overloaded{ + [](const Table& t) -> PhysicalPlanNode { + return SeqScan{.table = t.name, .alias = t.alias}; + }, + [](const Projection& p) -> PhysicalPlanNode { + return PhysicalProjection{ + .source = std::make_shared(ToPhysicalPlan(*p.source)), + .expressions = p.expressions, + .aliases = p.aliases, + }; + }, + [](const Filter& f) -> PhysicalPlanNode { + return PhysicalFilter{ + .source = std::make_shared(ToPhysicalPlan(*f.source)), + .predicate = f.expr, + }; + }, + [](const Aggregation& a) -> PhysicalPlanNode { + return PhysicalAggregation{ + .source = std::make_shared(ToPhysicalPlan(*a.source)), + .group_by = a.group_by, + .aggregates = a.aggregates, + }; + }, + [](const CrossJoin& j) -> PhysicalPlanNode { + return NestedLoopCrossJoin{ + .lhs = std::make_shared(ToPhysicalPlan(*j.lhs)), + .rhs = std::make_shared(ToPhysicalPlan(*j.rhs)), + }; + }, + [](const Join& j) -> PhysicalPlanNode { + return NestedLoopJoin{ + .lhs = std::make_shared(ToPhysicalPlan(*j.lhs)), + .rhs = std::make_shared(ToPhysicalPlan(*j.rhs)), + .type = j.type, + .qual = j.qual, + }; + }, + }, op); +} + +CardinalityEstimates MakeBenchCardinality() { + return CardinalityEstimates({ + {"users", 17}, + {"employees", 11}, + {"employees_200", 200}, + {"departments", 5}, + {"departments_500", 1000}, + {"departments_1000", 1000}, + {"departments_2000", 2000}, + {"departments_4000", 4000}, + {"departments_8000", 8000}, + {"departments_16000", 16000}, + {"books", 3}, + {"regions", 10}, + {"customers", 500}, + {"orders", 5000}, + }); +} + +SchemaCatalog MakeBenchSchema() { + return SchemaCatalog({ + {"users", {Attribute{"users", "id"}, Attribute{"users", "age"}}}, + {"employees", {Attribute{"employees", "id"}, Attribute{"employees", "department_id"}}}, + {"employees_200", {Attribute{"employees_200", "id"}, + Attribute{"employees_200", "department_id"}}}, + {"departments", {Attribute{"departments", "id"}}}, + {"departments_500", {Attribute{"departments_500", "id"}}}, + {"departments_1000", {Attribute{"departments_1000", "id"}}}, + {"departments_2000", {Attribute{"departments_2000", "id"}}}, + {"departments_4000", {Attribute{"departments_4000", "id"}}}, + {"departments_8000", {Attribute{"departments_8000", "id"}}}, + {"departments_16000", {Attribute{"departments_16000", "id"}}}, + {"books", {Attribute{"books", "id"}}}, + {"regions", {Attribute{"regions", "id"}}}, + {"customers", {Attribute{"customers", "id"}, Attribute{"customers", "region_id"}}}, + {"orders", {Attribute{"orders", "id"}, Attribute{"orders", "customer_id"}}}, + }); +} + +CardinalityEstimates LoadCardinalityFromCsvDir(const std::filesystem::path& dir) { + std::unordered_map counts; + if (!std::filesystem::is_directory(dir)) { + return CardinalityEstimates{}; + } + for (const auto& entry : std::filesystem::directory_iterator{dir}) { + if (entry.path().extension() != ".csv") continue; + std::ifstream in{entry.path()}; + std::string line; + int64_t rows = -1; + while (std::getline(in, line)) ++rows; + counts.emplace(entry.path().stem().string(), std::max(0, rows)); + } + return CardinalityEstimates{std::move(counts)}; +} + +class ScopedClogSuppression { +public: + ScopedClogSuppression() : nullstream_("/dev/null"), previous_(std::clog.rdbuf(nullstream_.rdbuf())) {} + ~ScopedClogSuppression() { std::clog.rdbuf(previous_); } + +private: + std::ofstream nullstream_; + std::streambuf* previous_; +}; + +enum class PlannerMode { kNaive, kOptimized }; + +struct PlanStats { + std::chrono::nanoseconds optimizer_runtime{}; + std::optional cost; +}; + +template +PhysicalPlanNode MakePlan(const Operator& op) { + if constexpr (Mode == PlannerMode::kNaive) { + return ToPhysicalPlan(op); + } else { + Optimizer optimizer(op, MakeMainRules(), MakeBenchCardinality(), MakeBenchSchema()); + return optimizer.Optimize(); + } +} + +template +PhysicalPlanNode MakePlan(const ParsedQuery& query, CardinalityEstimates cardinality, + SchemaCatalog schema, PlanStats* stats = nullptr) { + if constexpr (Mode == PlannerMode::kNaive) { + auto plan = ToPhysicalPlan(query.op); + if (query.required_order) { + return PhysicalSort{ + .source = std::make_shared(std::move(plan)), + .keys = *query.required_order, + }; + } + return plan; + } else { + PropertySet required = query.required_order + ? PropertySet{SortProperty{*query.required_order}} + : PropertySet::Any(); + const auto started = std::chrono::steady_clock::now(); + Optimizer optimizer(query.op, MakeMainRules(), std::move(cardinality), std::move(schema), + std::move(required)); + auto plan = optimizer.Optimize(); + if (stats) { + stats->optimizer_runtime = std::chrono::steady_clock::now() - started; + stats->cost = optimizer.GetBestCost(); + } + return plan; + } +} + +} // namespace + const static std::string kProjectDir = std::getenv("PWD"); static constexpr char kSimpleSelectSmall[]{"SELECT users.id FROM users;"}; @@ -26,16 +200,24 @@ static constexpr char kComplex4000[]{"SELECT departments_4000.id*2, employees_20 static constexpr char kComplex8000[]{"SELECT departments_8000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_8000 ON employees_200.department_id = departments_8000.id AND departments_8000.id > 3 AND departments_8000.id*2*2/2/2*2 < 30;"}; static constexpr char kComplex16000[]{"SELECT departments_16000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_16000 ON employees_200.department_id = departments_16000.id AND departments_16000.id > 3 AND departments_16000.id*2*2/2/2*2 < 30;"}; -template +static constexpr char kMultiwayOCR[]{ + "SELECT orders.id, customers.id, regions.id FROM orders " + "JOIN customers ON orders.customer_id = customers.id " + "JOIN regions ON customers.region_id = regions.id;"}; +static constexpr char kMultiwayROC[]{ + "SELECT orders.id, customers.id, regions.id FROM regions " + "JOIN customers ON customers.region_id = regions.id " + "JOIN orders ON orders.customer_id = customers.id;"}; + +template void BM_SQL(benchmark::State& state) { - std::ofstream nullstream("/dev/null"); - std::clog.rdbuf(nullstream.rdbuf()); + ScopedClogSuppression suppress_clog; boost::asio::io_context ctx; boost::asio::co_spawn( ctx, [&state]() -> boost::asio::awaitable { std::stringstream s{Query}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = MakePlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -51,44 +233,37 @@ void BM_SQL(benchmark::State& state) { ctx.run(); } -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL)->UseRealTime(); - -template +#define REGISTER_BM_SQL(Query) \ + BENCHMARK(BM_SQL)->UseRealTime(); \ + BENCHMARK(BM_SQL) \ + ->UseRealTime(); \ + BENCHMARK(BM_SQL) \ + ->UseRealTime(); \ + BENCHMARK(BM_SQL) \ + ->UseRealTime(); + +REGISTER_BM_SQL(kSimpleSelectSmall) +REGISTER_BM_SQL(kJoinSmall) +REGISTER_BM_SQL(kComplex5) +REGISTER_BM_SQL(kComplex500) +REGISTER_BM_SQL(kComplex1000) +REGISTER_BM_SQL(kComplex2000) +REGISTER_BM_SQL(kComplex4000) +REGISTER_BM_SQL(kComplex8000) +REGISTER_BM_SQL(kComplex16000) +REGISTER_BM_SQL(kMultiwayOCR) +REGISTER_BM_SQL(kMultiwayROC) + +template void BM_SQL_Multithreaded(benchmark::State& state) { - std::ofstream nullstream("/dev/null"); - std::clog.rdbuf(nullstream.rdbuf()); + ScopedClogSuppression suppress_clog; boost::asio::io_context ctx; boost::asio::co_spawn( ctx, [&state]() -> boost::asio::awaitable { boost::asio::thread_pool pool{4}; std::stringstream s{Query}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = MakePlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), pool.executor()); @@ -107,26 +282,127 @@ void BM_SQL_Multithreaded(benchmark::State& state) { ctx.run(); } -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); - -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); - -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +#define REGISTER_BM_SQL_MT(Query) \ + BENCHMARK(BM_SQL_Multithreaded) \ + ->UseRealTime(); \ + BENCHMARK(BM_SQL_Multithreaded) \ + ->UseRealTime(); \ + BENCHMARK(BM_SQL_Multithreaded) \ + ->UseRealTime(); \ + BENCHMARK( \ + BM_SQL_Multithreaded) \ + ->UseRealTime(); + +REGISTER_BM_SQL_MT(kComplex5) +REGISTER_BM_SQL_MT(kComplex500) +REGISTER_BM_SQL_MT(kComplex1000) +REGISTER_BM_SQL_MT(kComplex2000) +REGISTER_BM_SQL_MT(kComplex4000) +REGISTER_BM_SQL_MT(kComplex8000) +REGISTER_BM_SQL_MT(kComplex16000) + +namespace { + +std::string ReadTextFile(const std::filesystem::path& path) { + std::ifstream in{path}; + std::ostringstream out; + out << in.rdbuf(); + return out.str(); +} -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +std::filesystem::path SsbDataDir() { + if (const char* env = std::getenv("SSB_DATA_DIR")) { + return env; + } + return std::filesystem::path{kProjectDir} / "benchmarks/datasets/ssb/generated/sf1"; +} -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +template +void BM_SSB(benchmark::State& state, std::string query, std::filesystem::path data_dir) { + ScopedClogSuppression suppress_clog; + + if (!std::filesystem::is_directory(data_dir)) { + state.SkipWithError(("SSB data directory not found: " + data_dir.string() + + " (set SSB_DATA_DIR)").c_str()); + return; + } + + std::stringstream sql{query}; + auto parsed = GetAST(sql); + if (!parsed.has_value()) { + state.SkipWithError(("SSB query parse failed: " + What(parsed.error())).c_str()); + return; + } + + PhysicalPlanNode plan; + PlanStats plan_stats; + try { + plan = MakePlan(parsed.value(), LoadCardinalityFromCsvDir(data_dir), + LoadSchemaFromCsvDir(data_dir), &plan_stats); + } catch (const std::exception& e) { + state.SkipWithError(e.what()); + return; + } -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); + boost::asio::io_context ctx; + CsvDirSequentialScanner seq_scan{data_dir.string()}; + Executor executor(std::move(seq_scan), ctx.get_executor()); + + auto run_once = [&]() { + auto fut = boost::asio::co_spawn(ctx, executor.Execute(plan), boost::asio::use_future); + ctx.restart(); + ctx.run(); + return fut.get(); + }; + + std::chrono::nanoseconds execution_runtime{}; + try { + benchmark::DoNotOptimize(run_once()); + for (auto _ : state) { + const auto started = std::chrono::steady_clock::now(); + benchmark::DoNotOptimize(run_once()); + execution_runtime += std::chrono::steady_clock::now() - started; + } + } catch (const std::exception& e) { + state.SkipWithError(e.what()); + return; + } + + using Microseconds = std::chrono::duration; + state.counters["execution_us"] = benchmark::Counter{ + Microseconds{execution_runtime}.count(), benchmark::Counter::kAvgIterations}; + state.counters["includes_order_by"] = parsed.value().required_order.has_value() ? 1.0 : 0.0; + if (plan_stats.cost) { + state.counters["optimizer_us"] = Microseconds{plan_stats.optimizer_runtime}.count(); + state.counters["plan_cost"] = static_cast(*plan_stats.cost); + } +} -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); -BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +struct SsbRegistration { + SsbRegistration() { + const auto query_dir = std::filesystem::path{kProjectDir} / "benchmarks/datasets/ssb/queries"; + const auto data_dir = SsbDataDir(); + if (!std::filesystem::is_directory(query_dir)) return; + + for (const auto& entry : std::filesystem::directory_iterator{query_dir}) { + if (entry.path().extension() != ".sql") continue; + auto query = ReadTextFile(entry.path()); + auto name = "SSB/" + entry.path().stem().string(); + benchmark::RegisterBenchmark( + (name + "/Interpreted/Naive").c_str(), + &BM_SSB, query, data_dir) + ->UseRealTime(); + benchmark::RegisterBenchmark( + (name + "/Interpreted/Optimized").c_str(), + &BM_SSB, query, data_dir) + ->UseRealTime(); + } + } +}; + +const SsbRegistration kRegisterSsb; + +} // namespace } // namespace stewkk::sql diff --git a/benchmarks/operator_cost.cpp b/benchmarks/operator_cost.cpp new file mode 100644 index 0000000..fc56ac1 --- /dev/null +++ b/benchmarks/operator_cost.cpp @@ -0,0 +1,453 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace stewkk::sql { +namespace { + +struct BenchTable { + AttributesInfo attrs; + Tuples tuples; +}; + +using BenchTables = std::unordered_map; + +Value IntValue(int64_t value) { + return Value{false, {.int_value = value}}; +} + +Attribute Attr(std::string table, std::string name) { + return Attribute{std::move(table), std::move(name)}; +} + +Expression AttrExpr(std::string table, std::string name) { + return Expression{Attr(std::move(table), std::move(name))}; +} + +Expression IntExpr(int64_t value) { + return Expression{IntConst{value}}; +} + +Expression Binary(Expression lhs, BinaryOp op, Expression rhs) { + return Expression{BinaryExpression{ + .lhs = std::make_shared(std::move(lhs)), + .binop = op, + .rhs = std::make_shared(std::move(rhs)), + }}; +} + +Expression CountStar() { + return Expression{AggregateExpression{ + .function = AggregateFunction::kCount, + .argument = nullptr, + .is_star = true, + }}; +} + +PhysicalPlanNode Scan(std::string table) { + return PhysicalPlanNode{SeqScan{.table = std::move(table), .alias = std::nullopt}}; +} + +std::shared_ptr PlanPtr(PhysicalPlanNode plan) { + return std::make_shared(std::move(plan)); +} + +BenchTable MakeTable(std::string table, int64_t rows) { + BenchTable result; + result.attrs = { + AttributeInfo{table, "id", Type::kInt}, + AttributeInfo{table, "k", Type::kInt}, + AttributeInfo{table, "v", Type::kInt}, + }; + result.tuples.reserve(static_cast(rows)); + for (int64_t i = 0; i < rows; ++i) { + result.tuples.push_back(Tuple{ + IntValue(i), + IntValue(i), + IntValue(i % 1024), + }); + } + return result; +} + +std::shared_ptr MakeTables(int64_t rows_t, int64_t rows_u = 0) { + auto tables = std::make_shared(); + tables->emplace("t", MakeTable("t", rows_t)); + tables->emplace("u", MakeTable("u", rows_u)); + return tables; +} + +std::shared_ptr MakeSortTables(int64_t rows) { + auto tables = std::make_shared(); + auto table = MakeTable("t", rows); + std::mt19937 generator{0}; + std::shuffle(table.tuples.begin(), table.tuples.end(), generator); + tables->emplace("t", std::move(table)); + return tables; +} + +class InMemorySequentialScanner { +public: + explicit InMemorySequentialScanner(std::shared_ptr tables) + : tables_(std::move(tables)) {} + + boost::asio::awaitable> operator()(const std::string& table_name, + const std::string& output_table_name, + AttributesInfoChannel& attrs_chan, + TuplesChannel& tuples_chan) const { + auto it = tables_->find(table_name); + if (it == tables_->end()) { + throw std::runtime_error{"unknown benchmark table: " + table_name}; + } + + auto attrs = it->second.attrs; + for (auto& attr : attrs) { + attr.table = output_table_name; + } + co_await attrs_chan.async_send(boost::system::error_code{}, std::move(attrs), + boost::asio::use_awaitable); + attrs_chan.close(); + + Tuples buf; + buf.reserve(kBufSize); + for (const auto& tuple : it->second.tuples) { + buf.push_back(tuple); + if (buf.size() == kBufSize) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + buf.clear(); + buf.reserve(kBufSize); + } + } + if (!buf.empty()) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + } + tuples_chan.close(); + co_return Ok(); + } + +private: + std::shared_ptr tables_; +}; + +Relation RunPlan(const PhysicalPlanNode& plan, std::shared_ptr tables) { + boost::asio::io_context ctx; + Executor executor(InMemorySequentialScanner{std::move(tables)}, + ctx.get_executor()); + auto fut = boost::asio::co_spawn(ctx, executor.Execute(plan), boost::asio::use_future); + ctx.run(); + auto result = fut.get(); + if (!result.has_value()) { + throw std::runtime_error{What(result.error())}; + } + return std::move(result).value(); +} + +int64_t SortModelCost(int64_t rows) { + return 11 * (rows > 1 ? rows * static_cast(std::bit_width(static_cast(rows))) : rows); +} + +int64_t HashJoinModelCost(int64_t lhs_rows, int64_t rhs_rows) { + constexpr int64_t kInputWidth = 3; + constexpr int64_t kOutputWidth = 2 * kInputWidth; + constexpr int64_t kHashBuild = 100; + constexpr int64_t kHashProbe = 35; + constexpr int64_t kTupleCopy = 10; + return kHashBuild * lhs_rows * kInputWidth + + kHashProbe * rhs_rows + + kTupleCopy * std::min(lhs_rows, rhs_rows) * kOutputWidth; +} + +int64_t FindSortRowsForTargetCost(int64_t target_cost) { + int64_t best_rows = 1; + for (int64_t rows = 2; SortModelCost(rows) <= 2 * target_cost; ++rows) { + if (std::abs(SortModelCost(rows) - target_cost) + < std::abs(SortModelCost(best_rows) - target_cost)) { + best_rows = rows; + } + } + return best_rows; +} + +int64_t DivideRounded(int64_t value, int64_t divisor) { + return std::max(1, (value + divisor / 2) / divisor); +} + +int64_t SqrtRounded(int64_t value) { + const auto floor = static_cast(std::sqrt(value)); + return value - floor * floor < (floor + 1) * (floor + 1) - value ? floor : floor + 1; +} + +void SetUnaryCounters(benchmark::State& state, int64_t rows, int64_t model_cost, + int64_t plan_cost, int64_t output_rows) { + state.counters["rows"] = static_cast(rows); + state.counters["model_cost"] = static_cast(model_cost); + state.counters["plan_cost"] = static_cast(plan_cost); + state.counters["output_rows"] = static_cast(output_rows); +} + +void SetBinaryCounters(benchmark::State& state, int64_t lhs_rows, int64_t rhs_rows, + int64_t model_cost, int64_t plan_cost, int64_t output_rows) { + state.counters["lhs_rows"] = static_cast(lhs_rows); + state.counters["rhs_rows"] = static_cast(rhs_rows); + state.counters["model_cost"] = static_cast(model_cost); + state.counters["plan_cost"] = static_cast(plan_cost); + state.counters["output_rows"] = static_cast(output_rows); +} + +class ScopedClogSuppression { +public: + ScopedClogSuppression() : nullstream_("/dev/null"), previous_(std::clog.rdbuf(nullstream_.rdbuf())) {} + ~ScopedClogSuppression() { std::clog.rdbuf(previous_); } + +private: + std::ofstream nullstream_; + std::streambuf* previous_; +}; + +void BM_OperatorSeqScan(benchmark::State& state) { + const int64_t rows = state.range(0); + auto tables = MakeTables(rows); + auto plan = Scan("t"); + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + SetUnaryCounters(state, rows, 100 * rows, 100 * rows, rows); +} + +void BM_OperatorFilter(benchmark::State& state) { + const int64_t rows = state.range(0); + constexpr int64_t kThreshold = 512; + auto tables = MakeTables(rows); + auto plan = PhysicalPlanNode{PhysicalFilter{ + .source = PlanPtr(Scan("t")), + .predicate = Binary(AttrExpr("t", "v"), BinaryOp::kLt, IntExpr(kThreshold)), + }}; + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + const int64_t full_cycles = rows / 1024; + const int64_t tail = rows % 1024; + const int64_t output_rows = full_cycles * kThreshold + std::min(tail, kThreshold); + SetUnaryCounters(state, rows, 100 * rows, 200 * rows, output_rows); +} + +void BM_OperatorProjection(benchmark::State& state) { + const int64_t rows = state.range(0); + auto tables = MakeTables(rows); + auto plan = PhysicalPlanNode{PhysicalProjection{ + .source = PlanPtr(Scan("t")), + .expressions = { + AttrExpr("t", "id"), + Binary(AttrExpr("t", "v"), BinaryOp::kPlus, IntExpr(1)), + }, + .aliases = {std::nullopt, std::nullopt}, + }}; + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + SetUnaryCounters(state, rows, 22 * rows, 122 * rows, rows); +} + +void BM_OperatorSort(benchmark::State& state) { + const int64_t rows = state.range(0); + auto tables = MakeSortTables(rows); + auto plan = PhysicalPlanNode{PhysicalSort{ + .source = PlanPtr(Scan("t")), + .keys = SortOrder{{SortKey{.table = "t", .column = "id", .dir = Direction::kAsc}}}, + }}; + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + SetUnaryCounters(state, rows, SortModelCost(rows), SortModelCost(rows) + 100 * rows, rows); +} + +void BM_OperatorAggregation(benchmark::State& state) { + const int64_t rows = state.range(0); + auto tables = MakeTables(rows); + auto plan = PhysicalPlanNode{PhysicalAggregation{ + .source = PlanPtr(Scan("t")), + .group_by = {AttrExpr("t", "k")}, + .aggregates = {CountStar()}, + }}; + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + SetUnaryCounters(state, rows, 510 * rows, 610 * rows, rows); +} + +void BM_OperatorNestedLoopJoin(benchmark::State& state) { + const int64_t lhs_rows = state.range(0); + const int64_t rhs_rows = state.range(1); + auto tables = MakeTables(lhs_rows, rhs_rows); + auto plan = PhysicalPlanNode{NestedLoopJoin{ + .lhs = PlanPtr(Scan("t")), + .rhs = PlanPtr(Scan("u")), + .type = JoinType::kInner, + .qual = Binary(AttrExpr("t", "k"), BinaryOp::kEq, AttrExpr("u", "k")), + }}; + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + SetBinaryCounters(state, lhs_rows, rhs_rows, 70 * lhs_rows * rhs_rows, + 70 * lhs_rows * rhs_rows + 100 * (lhs_rows + rhs_rows), + std::min(lhs_rows, rhs_rows)); +} + +void BM_OperatorNestedLoopCrossJoin(benchmark::State& state) { + const int64_t lhs_rows = state.range(0); + const int64_t rhs_rows = state.range(1); + auto tables = MakeTables(lhs_rows, rhs_rows); + auto plan = PhysicalPlanNode{NestedLoopCrossJoin{ + .lhs = PlanPtr(Scan("t")), + .rhs = PlanPtr(Scan("u")), + }}; + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + SetBinaryCounters(state, lhs_rows, rhs_rows, 104 * lhs_rows * rhs_rows, + 104 * lhs_rows * rhs_rows + 100 * (lhs_rows + rhs_rows), + lhs_rows * rhs_rows); +} + +void BM_OperatorHashJoin(benchmark::State& state) { + const int64_t lhs_rows = state.range(0); + const int64_t rhs_rows = state.range(1); + auto tables = MakeTables(lhs_rows, rhs_rows); + auto plan = PhysicalPlanNode{HashJoin{ + .lhs = PlanPtr(Scan("t")), + .rhs = PlanPtr(Scan("u")), + .type = JoinType::kInner, + .qual = Binary(AttrExpr("t", "k"), BinaryOp::kEq, AttrExpr("u", "k")), + }}; + + ScopedClogSuppression suppress_clog; + + benchmark::DoNotOptimize(RunPlan(plan, tables)); + for (auto _ : state) { + benchmark::DoNotOptimize(RunPlan(plan, tables)); + } + SetBinaryCounters(state, lhs_rows, rhs_rows, HashJoinModelCost(lhs_rows, rhs_rows), + HashJoinModelCost(lhs_rows, rhs_rows) + 100 * (lhs_rows + rhs_rows), + std::min(lhs_rows, rhs_rows)); +} + +void RegisterUnary(void (*benchmark_fn)(benchmark::State&), const char* name) { + for (int64_t rows : {1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144}) { + benchmark::RegisterBenchmark(name, benchmark_fn)->Arg(rows)->UseRealTime(); + } +} + +void RegisterCostMatched(int64_t target_cost) { + const auto prefix = "OperatorCostMatched/target_cost:" + std::to_string(target_cost) + "/"; + const auto register_unary = [&](const char* name, void (*benchmark_fn)(benchmark::State&), + int64_t rows) { + benchmark::RegisterBenchmark((prefix + name).c_str(), benchmark_fn)->Arg(rows)->UseRealTime(); + }; + const auto register_binary = [&](const char* name, void (*benchmark_fn)(benchmark::State&), + int64_t rows) { + benchmark::RegisterBenchmark((prefix + name).c_str(), benchmark_fn) + ->Args({rows, rows}) + ->UseRealTime(); + }; + + register_unary("SeqScan", BM_OperatorSeqScan, DivideRounded(target_cost, 100)); + register_unary("Filter", BM_OperatorFilter, DivideRounded(target_cost, 100)); + register_unary("Projection", BM_OperatorProjection, DivideRounded(target_cost, 22)); + register_unary("Sort", BM_OperatorSort, FindSortRowsForTargetCost(target_cost)); + register_unary("Aggregation", BM_OperatorAggregation, DivideRounded(target_cost, 510)); + register_binary("HashJoin", BM_OperatorHashJoin, + DivideRounded(target_cost, HashJoinModelCost(1, 1))); + register_binary("NestedLoopJoin", BM_OperatorNestedLoopJoin, + SqrtRounded(DivideRounded(target_cost, 70))); + register_binary("NestedLoopCrossJoin", BM_OperatorNestedLoopCrossJoin, + SqrtRounded(DivideRounded(target_cost, 104))); +} + +struct OperatorCostRegistration { + OperatorCostRegistration() { + RegisterUnary(BM_OperatorSeqScan, "OperatorCost/SeqScan"); + RegisterUnary(BM_OperatorFilter, "OperatorCost/Filter"); + RegisterUnary(BM_OperatorProjection, "OperatorCost/Projection"); + RegisterUnary(BM_OperatorSort, "OperatorCost/Sort"); + RegisterUnary(BM_OperatorAggregation, "OperatorCost/Aggregation"); + + for (auto size : {1024, 2048, 4096, 8192, 16384, 32768, 65536}) { + benchmark::RegisterBenchmark("OperatorCost/HashJoin", BM_OperatorHashJoin) + ->Args({size, size}) + ->UseRealTime(); + } + for (auto size : {64, 128, 256, 512, 1024, 2048}) { + benchmark::RegisterBenchmark("OperatorCost/NestedLoopJoin", BM_OperatorNestedLoopJoin) + ->Args({size, size}) + ->UseRealTime(); + } + for (auto size : {32, 64, 128, 256, 512, 1024}) { + benchmark::RegisterBenchmark("OperatorCost/NestedLoopCrossJoin", + BM_OperatorNestedLoopCrossJoin) + ->Args({size, size}) + ->UseRealTime(); + } + + for (auto target_cost : {640000, 1000000, 3240000, 6760000, 12250000, 26010000}) { + RegisterCostMatched(target_cost); + } + } +}; + +const OperatorCostRegistration kRegisterOperatorCost; + +} // namespace +} // namespace stewkk::sql diff --git a/cmake/FetchBoost.cmake b/cmake/FetchBoost.cmake index 359c609..0d6274c 100644 --- a/cmake/FetchBoost.cmake +++ b/cmake/FetchBoost.cmake @@ -9,6 +9,6 @@ FetchContent_Declare( OVERRIDE_FIND_PACKAGE ) -set(BOOST_INCLUDE_LIBRARIES asio thread filesystem) +set(BOOST_INCLUDE_LIBRARIES asio thread filesystem scope) FetchContent_MakeAvailable(Boost) diff --git a/cmake/SetupLLVM.cmake b/cmake/SetupLLVM.cmake index a61d32d..0364d84 100644 --- a/cmake/SetupLLVM.cmake +++ b/cmake/SetupLLVM.cmake @@ -1,6 +1,4 @@ find_package(ZLIB QUIET) - -set(LLVM_DIR "/home/st/c/llvm-project/build/lib/cmake/llvm") find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/flake.lock b/flake.lock index 1ff1a60..7bda188 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", "owner": "edolstra", "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { @@ -36,15 +36,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1760027740, - "narHash": "sha256-53qTARbdnAAmHU29yOivHQCh4Ir1orhhOBtJdol23xk=", - "owner": "NixOS", + "lastModified": 1779560665, + "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "4b3a7fbda14c1310e607b79caefe103cbffb1fa9", + "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", "type": "github" }, "original": { - "owner": "NixOS", + "owner": "nixos", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index d3566d8..0557b90 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "An example project using flutter"; inputs.nixpkgs = { - url = "github:NixOS/nixpkgs"; + url = "github:nixos/nixpkgs/nixos-unstable"; }; inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.flake-compat = { @@ -22,6 +22,13 @@ ps.pip ps.virtualenv ps.pygments + ps.jupyter + ps.ipykernel + ps.notebook + ps.pymssql + ps.pytest + ps.pandas + ps.matplotlib ]); tex = (pkgs.texlive.combine { inherit (pkgs.texlive) scheme-full @@ -30,7 +37,17 @@ in { devShells.default = pkgs.mkShell.override {stdenv = pkgs.llvmPackages_21.stdenv;} { buildInputs = with pkgs; [ - code-cursor-fhs + (pkgs.vscode-with-extensions.override { + vscode = pkgs.vscode-fhs; + vscodeExtensions = with pkgs.vscode-extensions; [ + ms-python.python + ms-python.vscode-pylance + ms-toolsai.jupyter + anthropic.claude-code + ms-vscode.cpptools + asvetliakov.vscode-neovim + ]; + }) pythonEnv antlr cmake @@ -41,7 +58,13 @@ perl tex plantuml - inkscape + inkscape + llvmPackages_21.llvm + llvmPackages_21.llvm.dev + nodejs_24 + graphviz + codex + claude-code ]; nativeBuildInputs = [ @@ -72,6 +95,8 @@ pip install -r requirements.txt fi + python -m ipykernel install --user --name iu9-sql-compiler --display-name "Python (iu9-sql-compiler)" + python --version ''; }; diff --git a/include/stewkk/sql/logic/executor/buffer_size.hpp b/include/stewkk/sql/logic/executor/buffer_size.hpp index cf4bb29..176aeef 100644 --- a/include/stewkk/sql/logic/executor/buffer_size.hpp +++ b/include/stewkk/sql/logic/executor/buffer_size.hpp @@ -6,5 +6,4 @@ namespace stewkk::sql { constexpr static std::size_t kBufSize = 2048; -} // stewkk::sql - +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index 8cb3765..8549848 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -1,23 +1,19 @@ #pragma once -#include -#include -#include -#include #include #include +#include -#include -#include #include #include +#include +#include -#include #include #include #include #include -#include +#include #include namespace stewkk::sql { @@ -51,58 +47,57 @@ template class Executor { public: using SequentialScan = std::function>( - const std::string& table_name, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan)>; + const std::string& table_name, const std::string& output_table_name, + AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan)>; + using IndexScan = std::function>( + const std::string& table_name, const std::string& output_table_name, + const Expression& predicate, const std::optional& index_column, + AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan)>; Executor(SequentialScan seq_scan, boost::asio::any_io_executor executor); + Executor(SequentialScan seq_scan, IndexScan index_scan, boost::asio::any_io_executor executor); - boost::asio::awaitable> Execute(const Operator& op); + boost::asio::awaitable> Execute(const PhysicalPlanNode& op); private: - boost::asio::awaitable Execute(const Operator& op, AttributesInfoChannel& attr_chan, + boost::asio::awaitable Execute(const PhysicalPlanNode& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan); - boost::asio::awaitable ExecuteProjection(const Projection& proj, AttributesInfoChannel& attr_chan, + boost::asio::awaitable ExecuteSeqScan(const SeqScan& seq_scan, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan); + boost::asio::awaitable ExecuteIndexSeek(const IndexSeek& seek, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan); + boost::asio::awaitable ExecuteProjection(const PhysicalProjection& proj, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan); - boost::asio::awaitable ExecuteFilter(const Filter& filter, AttributesInfoChannel& attr_chan, + boost::asio::awaitable ExecuteFilter(const PhysicalFilter& filter, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan); - boost::asio::awaitable ExecuteCrossJoin(const CrossJoin& cross_join, + boost::asio::awaitable ExecuteCrossJoin(const NestedLoopCrossJoin& cross_join, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan); - boost::asio::awaitable ExecuteJoin(const Join& join, AttributesInfoChannel& attr_chan, + boost::asio::awaitable ExecuteJoin(const NestedLoopJoin& join, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan); + boost::asio::awaitable ExecuteHashJoin(const HashJoin& join, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan); + boost::asio::awaitable ExecuteMergeJoin(const MergeJoin& join, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan); + boost::asio::awaitable ExecuteHashAggregate(PhysicalAggregation agg, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan); + boost::asio::awaitable ExecuteStreamAggregate(const PhysicalStreamAggregation& agg, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan); + boost::asio::awaitable ExecuteSort(const PhysicalSort& sort, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan); - boost::asio::awaitable SpawnExecutor(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuple_chan); + boost::asio::experimental::promise SpawnExecutor( + boost::asio::any_io_executor exec, + const PhysicalPlanNode& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuple_chan); private: SequentialScan sequential_scan_; + IndexScan index_scan_; ExpressionExecutor expression_executor_; }; Type GetExpressionType(const Expression& expr, const AttributesInfo& available_attrs); Type GetExpressionTypeUnchecked(const Expression& expr, const AttributesInfo& available_attrs); -AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const Projection& proj); - -template - requires std::invocable - && std::same_as, Ret> -Value ApplyIntegersOperator(Value lhs, Value rhs) { - if (lhs.is_null || rhs.is_null) { - return Value{true}; - } - return Value{false, Op{}(lhs.value.int_value, rhs.value.int_value)}; -} - -template - requires std::invocable - && std::same_as, bool> -Value ApplyBooleanOperator(Value lhs, Value rhs) { - if (lhs.is_null || rhs.is_null) { - return Value{true}; - } - return Value{false, Op{}(lhs.value.bool_value, rhs.value.bool_value)}; -} - -struct IntPow { - int64_t operator()(int64_t base, int64_t exp) const { - return static_cast(std::pow(base, exp)); - } -}; +AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const PhysicalProjection& proj); Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, const Expression& expr); Tuple ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const std::vector& expressions); @@ -113,342 +108,4 @@ boost::asio::awaitable ConcatAttrs(AttributesInfoChannel& lhs_at boost::asio::awaitable MaterializeChannel(TuplesChannel& tuples_chan); Tuple ConcatTuples(const Tuple& lhs, const Tuple& rhs); -template -Executor::Executor(SequentialScan seq_scan, boost::asio::any_io_executor executor) - : sequential_scan_(std::move(seq_scan)), expression_executor_(executor) {} - -template -boost::asio::awaitable> Executor::Execute(const Operator& op) { - auto [attr_chan, tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(op, attr_chan, tuples_chan); - - auto attrs = co_await attr_chan.async_receive(boost::asio::use_awaitable); -#ifdef DEBUG - std::clog << "Received attrs in root\n"; -#endif - - Tuples result; - for (;;) { - auto buf = co_await ReceiveTuples(tuples_chan); - if (buf.empty()) { - break; - } -#ifdef DEBUG - std::clog << std::format("Received {} tuples in root\n", buf.size()); -#endif - std::move(buf.begin(), buf.end(), std::back_inserter(result)); - } - -#ifdef DEBUG - std::clog << std::format("Total {} tuples in root\n", result.size()); -#endif - co_return Ok(Relation{std::move(attrs), std::move(result)}); -} - -template -boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { - struct ExecuteVisitor{ - boost::asio::awaitable operator()(const Table& table) { - co_await executor.sequential_scan_(table.name, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const Projection& projection) { - // NOTE: We are using multiset relational algebra projection (i.e. not - // eleminating duplicate tuples) - co_await executor.ExecuteProjection(projection, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const Filter& filter) { - co_await executor.ExecuteFilter(filter, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const Join& join) { - co_await executor.ExecuteJoin(join, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const CrossJoin& cross_join) { - co_await executor.ExecuteCrossJoin(cross_join, attr_chan, tuples_chan); - co_return; - } - - AttributesInfoChannel& attr_chan; - TuplesChannel& tuples_chan; - Executor& executor; - }; - co_await std::visit(ExecuteVisitor{attr_chan, tuples_chan, *this}, op); - co_return; -} - -template -boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, - AttributesInfoChannel& out_attr_chan, - TuplesChannel& out_tuples_chan) { -#ifdef DEBUG - std::clog << "Executing projection\n"; -#endif - auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*proj.source, in_attrs_chan, in_tuples_chan); - - auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); - auto attrs_after = GetAttributesAfterProjection(attrs, proj); - co_await out_attr_chan.async_send(boost::system::error_code{}, attrs_after, - boost::asio::use_awaitable); - out_attr_chan.close(); - - std::vector executors; - executors.reserve(proj.expressions.size()); - for (const auto& expr : proj.expressions) { - executors.push_back(co_await expression_executor_.GetExpressionExecutor(expr, attrs)); - } - - for (;;) { - auto buf = co_await ReceiveTuples(in_tuples_chan); - if (buf.empty()) { - break; - } -#ifdef DEBUG - std::clog << std::format("Received {} tuples in projection\n", buf.size()); -#endif - buf = buf | std::views::transform([&](const auto& tuple) { - return ApplyProjection(tuple, attrs, executors); - }) | std::ranges::to(); - co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(buf), - boost::asio::use_awaitable); - } - out_tuples_chan.close(); -} - -template -boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, - AttributesInfoChannel& out_attr_chan, - TuplesChannel& out_tuples_chan) { -#ifdef DEBUG - std::clog << "Executing filter\n"; -#endif - auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*filter.source, in_attrs_chan, in_tuples_chan); - - auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); -#ifdef DEBUG - std::clog << "Filter received attrs\n"; -#endif - - if (GetExpressionType(filter.expr, attrs) != Type::kBool) { - throw std::logic_error{"filter expr should return bool"}; - } - -#ifdef DEBUG - std::clog << "Filter sending attrs\n"; -#endif - co_await out_attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); - out_attr_chan.close(); -#ifdef DEBUG - std::clog << "Filter sent attrs\n"; -#endif - - auto filter_executor = co_await expression_executor_.GetExpressionExecutor(filter.expr, attrs); - - Tuples output_buf; - output_buf.reserve(kBufSize); - for (;;) { - auto input_buf = co_await ReceiveTuples(in_tuples_chan); - if (input_buf.empty()) { - break; - } -#ifdef DEBUG - std::clog << std::format("Received {} tuples in filter\n", input_buf.size()); -#endif - auto filtered_view = input_buf | std::views::filter([&](const auto& tuple) { - return ApplyFilter(tuple, attrs, filter_executor); - }) | std::views::as_rvalue; - for (auto&& tuple : filtered_view) { - output_buf.push_back(std::move(tuple)); - if (output_buf.size() == kBufSize) { -#ifdef DEBUG - std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); -#endif - co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), - boost::asio::use_awaitable); - output_buf.clear(); - } - } - } -#ifdef DEBUG - std::clog << std::format("{} tuples left in output_buf\n", output_buf.size()); -#endif - if (!output_buf.empty()) { -#ifdef DEBUG - std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); -#endif - co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), - boost::asio::use_awaitable); - } - out_tuples_chan.close(); -} - -template -boost::asio::awaitable Executor::ExecuteCrossJoin(const CrossJoin& cross_join, - AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) { - std::clog << "Executing cross join\n"; - auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*cross_join.lhs, lhs_attrs_chan, lhs_tuples_chan); - auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*cross_join.rhs, rhs_attrs_chan, rhs_tuples_chan); - - auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); - std::clog << "Cross join received attrs\n"; - - std::clog << "Cross join sending attrs\n"; - co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); - attr_chan.close(); - std::clog << "Cross join sent attrs\n"; - - auto reader = co_await MaterializeChannel(lhs_tuples_chan); - std::clog << std::format("Materialized tuples in cross join\n"); - - for (;;) { - auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); - if (buf_rhs.empty()) { - break; - } - std::clog << std::format("Received {} tuples in cross join as rhs\n", buf_rhs.size()); - - for (;;) { - auto buf_lhs = reader.Read(); - if (buf_lhs.empty()) { - break; - } - std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); - for (const auto& tuple_lhs : buf_lhs) { - Tuples buf_joined; - buf_joined.reserve(kBufSize); - // NOTE: not optimal if rhs is a small table (<< kBufSize tuples) - for (const auto& tuple_rhs : buf_rhs) { - auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); - buf_joined.push_back(std::move(joined_tuple)); - } -#ifdef DEBUG - std::clog << std::format("Sending {} tuples from cross join\n", buf_joined.size()); -#endif - co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_joined), - boost::asio::use_awaitable); - } - } - } - tuples_chan.close(); -} - -template -boost::asio::awaitable Executor::ExecuteJoin(const Join& join, - AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) { -#ifdef DEBUG - std::clog << "Executing join\n"; -#endif - if (join.type == JoinType::kFull) { - throw std::logic_error{"Full joins are not supported by executor"}; - } - if (join.type == JoinType::kLeft) { - std::swap(*join.lhs, *join.rhs); - } - auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*join.lhs, lhs_attrs_chan, lhs_tuples_chan); - auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*join.rhs, rhs_attrs_chan, rhs_tuples_chan); - - auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); -#ifdef DEBUG - std::clog << "Join received attrs\n"; -#endif - -#ifdef DEBUG - std::clog << "Join sending attrs\n"; -#endif - co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); - attr_chan.close(); -#ifdef DEBUG - std::clog << "Join sent attrs\n"; -#endif - - auto qual_executor = co_await expression_executor_.GetExpressionExecutor(join.qual, attrs); - - auto reader = co_await MaterializeChannel(lhs_tuples_chan); - for (;;) { - auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); - if (buf_rhs.empty()) { - break; - } -#ifdef DEBUG - std::clog << std::format("Received {} tuples in join as rhs\n", buf_rhs.size()); -#endif - - std::vector used(buf_rhs.size(), false); - for (;;) { - auto buf_lhs = reader.Read(); - if (buf_lhs.empty()) { - break; - } -#ifdef DEBUG - std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); -#endif - - for (const auto& tuple_lhs : buf_lhs) { - Tuples buf_res; - buf_res.reserve(kBufSize); - for (const auto& [rhs_index, tuple_rhs] : buf_rhs | std::views::enumerate) { - auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); - auto qual_expr_res = qual_executor(joined_tuple, attrs); - if (qual_expr_res.value.bool_value) { - buf_res.push_back(std::move(joined_tuple)); - used[rhs_index] = true; - } - } - if (!buf_res.empty()) { -#ifdef DEBUG - std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); -#endif - co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), - boost::asio::use_awaitable); - } - } - } - - if (join.type == JoinType::kRight || join.type == JoinType::kLeft) { - Tuples buf_res; - buf_res.reserve(kBufSize); - for (auto [rhs_index, is_used] : used | std::views::enumerate) { - if (is_used) { - continue; - } - - auto rhs_tuple = std::move(buf_rhs[rhs_index]); - - auto lhs_size = attrs.size() - rhs_tuple.size(); - Tuple lhs_tuple(lhs_size, Value{true}); - - auto joined_tuple = ConcatTuples(lhs_tuple, rhs_tuple); - buf_res.push_back(std::move(joined_tuple)); - } - if (!buf_res.empty()) { -#ifdef DEBUG - std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); -#endif - co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), - boost::asio::use_awaitable); - } - } - } - tuples_chan.close(); -} - -template -boost::asio::awaitable Executor::SpawnExecutor(const Operator& op, - AttributesInfoChannel& attr_chan, - TuplesChannel& tuple_chan) { - auto executor = co_await boost::asio::this_coro::executor; - boost::asio::co_spawn(executor, Execute(op, attr_chan, tuple_chan), boost::asio::detached); -} - - } // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/materialization.hpp b/include/stewkk/sql/logic/executor/materialization.hpp index e8a3387..6ed9d7d 100644 --- a/include/stewkk/sql/logic/executor/materialization.hpp +++ b/include/stewkk/sql/logic/executor/materialization.hpp @@ -17,6 +17,7 @@ class DiskFileReader { DiskFileReader(DiskFileReader&& other) = default; DiskFileReader& operator=(DiskFileReader&& other) = default; Tuples Read(); + void Rewind(); private: fs::path path_; std::ifstream f_; @@ -31,7 +32,7 @@ class DiskFileWriter { private: fs::path path_; std::ofstream f_; - std::size_t tuple_size_; + std::size_t tuple_size_ = 0; }; } // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/plan.hpp b/include/stewkk/sql/logic/executor/plan.hpp new file mode 100644 index 0000000..821271e --- /dev/null +++ b/include/stewkk/sql/logic/executor/plan.hpp @@ -0,0 +1,162 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace stewkk::sql { + +struct SeqScan; +struct PhysicalProjection; +struct PhysicalFilter; +struct NestedLoopJoin; +struct NestedLoopCrossJoin; +struct HashJoin; +struct MergeJoin; +struct IndexSeek; +struct PhysicalSort; +struct PhysicalAggregation; +struct PhysicalStreamAggregation; +struct PhysicalPlanNode; + +struct SeqScan { + std::string table; + std::optional alias; + + bool operator==(const SeqScan&) const = default; +}; + +std::string_view OutputTable(const SeqScan& scan); + +struct PhysicalProjection { + std::shared_ptr source; + std::vector expressions; + std::vector> aliases; + + bool operator==(const PhysicalProjection&) const; +}; + +struct PhysicalFilter { + std::shared_ptr source; + Expression predicate; + + bool operator==(const PhysicalFilter&) const; +}; + +struct NestedLoopJoin { + std::shared_ptr lhs; + std::shared_ptr rhs; + JoinType type; + Expression qual; + + bool operator==(const NestedLoopJoin&) const; +}; + +struct NestedLoopCrossJoin { + std::shared_ptr lhs; + std::shared_ptr rhs; + + bool operator==(const NestedLoopCrossJoin&) const; +}; + +struct HashJoin { + std::shared_ptr lhs; + std::shared_ptr rhs; + JoinType type; + Expression qual; + + bool operator==(const HashJoin&) const; +}; + +struct MergeJoin { + std::shared_ptr lhs; + std::shared_ptr rhs; + JoinType type; + Expression qual; + + bool operator==(const MergeJoin&) const; +}; + +struct IndexSeek { + std::string table; + std::optional alias; + Expression predicate; + std::optional index_column = std::nullopt; + + bool operator==(const IndexSeek&) const; +}; + +struct PhysicalSort { + std::shared_ptr source; + SortOrder keys; + + bool operator==(const PhysicalSort&) const; +}; + +struct PhysicalAggregation { + std::shared_ptr source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const PhysicalAggregation&) const; +}; + +struct PhysicalStreamAggregation { + std::shared_ptr source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const PhysicalStreamAggregation&) const; +}; + +struct PhysicalPartialAggregation { + std::shared_ptr source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const PhysicalPartialAggregation&) const; +}; + +struct PhysicalFinalAggregation { + std::shared_ptr source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const PhysicalFinalAggregation&) const; +}; + +struct PlanNodeMetadata { + std::int64_t cardinality = 0; + std::int64_t local_cost = 0; + + bool operator==(const PlanNodeMetadata&) const = default; +}; + +using PhysicalPlanAlternative = std::variant; + +struct PhysicalPlanNode { + PhysicalPlanAlternative node; + std::optional metadata; + + PhysicalPlanNode() = default; + + template + requires (!std::same_as, PhysicalPlanNode> + && std::constructible_from) + PhysicalPlanNode(T&& value) : node(std::forward(value)) {} + + bool operator==(const PhysicalPlanNode& other) const; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/plan_serializer.hpp b/include/stewkk/sql/logic/executor/plan_serializer.hpp new file mode 100644 index 0000000..2c6c4e9 --- /dev/null +++ b/include/stewkk/sql/logic/executor/plan_serializer.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include + +namespace stewkk::sql { + +std::string Serialize(const PhysicalPlanNode& node); +PhysicalPlanNode Deserialize(std::string_view text); + +std::string SerializeDot(const PhysicalPlanNode& node); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/sequential_scan.hpp b/include/stewkk/sql/logic/executor/sequential_scan.hpp index 51451e1..ecaf2a8 100644 --- a/include/stewkk/sql/logic/executor/sequential_scan.hpp +++ b/include/stewkk/sql/logic/executor/sequential_scan.hpp @@ -2,7 +2,10 @@ #include +#include + #include +#include #include namespace stewkk::sql { @@ -11,6 +14,18 @@ struct CsvDirSequentialScanner { std::string dir; boost::asio::awaitable> operator()(const std::string& table_name, + const std::string& output_table_name, + AttributesInfoChannel& attrs_chan, + TuplesChannel& tuples_chan) const; +}; + +struct CsvDirIndexedScanner { + std::string dir; + + boost::asio::awaitable> operator()(const std::string& table_name, + const std::string& output_table_name, + const Expression& predicate, + const std::optional& index_column, AttributesInfoChannel& attrs_chan, TuplesChannel& tuples_chan) const; }; diff --git a/include/stewkk/sql/logic/implementation_rules/implement_aggregation.hpp b/include/stewkk/sql/logic/implementation_rules/implement_aggregation.hpp new file mode 100644 index 0000000..c541068 --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_aggregation.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementAggregation : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_cross_join.hpp b/include/stewkk/sql/logic/implementation_rules/implement_cross_join.hpp new file mode 100644 index 0000000..5db26a0 --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_cross_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementCrossJoin : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_filter.hpp b/include/stewkk/sql/logic/implementation_rules/implement_filter.hpp new file mode 100644 index 0000000..884618c --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_filter.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementFilter : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_hash_join.hpp b/include/stewkk/sql/logic/implementation_rules/implement_hash_join.hpp new file mode 100644 index 0000000..1eecab2 --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_hash_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementHashJoin : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_index_seek.hpp b/include/stewkk/sql/logic/implementation_rules/implement_index_seek.hpp new file mode 100644 index 0000000..1842ba9 --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_index_seek.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class ImplementIndexSeek : public ImplementationRule { +public: + explicit ImplementIndexSeek(IndexCatalog indexes); + + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; + +private: + IndexCatalog indexes_; +}; + +bool HasCompatibleIndexSeek(const logical::Filter& filter, const IndexCatalog& indexes); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_join.hpp b/include/stewkk/sql/logic/implementation_rules/implement_join.hpp new file mode 100644 index 0000000..5ec710e --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementJoin : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_merge_join.hpp b/include/stewkk/sql/logic/implementation_rules/implement_merge_join.hpp new file mode 100644 index 0000000..5a9b564 --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_merge_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementMergeJoin : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_projection.hpp b/include/stewkk/sql/logic/implementation_rules/implement_projection.hpp new file mode 100644 index 0000000..2437041 --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_projection.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementProjection : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/implementation_rules/implement_table.hpp b/include/stewkk/sql/logic/implementation_rules/implement_table.hpp new file mode 100644 index 0000000..b1e96ca --- /dev/null +++ b/include/stewkk/sql/logic/implementation_rules/implement_table.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ImplementTable : public ImplementationRule { + public: + bool IsApplicable(utils::NotNull expr) override; + std::vector> Apply(utils::NotNull expr, Memo& memo) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/cardinality.hpp b/include/stewkk/sql/logic/optimizer/cardinality.hpp new file mode 100644 index 0000000..7be0dc3 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/cardinality.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +class CardinalityEstimates { +public: + CardinalityEstimates(std::unordered_map table_sizes = {}); + + int64_t GetCardinality(utils::NotNull group); + +private: + int64_t GetCardinality(const LogicalOperator& op); + + std::unordered_map table_sizes_; + std::unordered_map cache_; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/enforcer.hpp b/include/stewkk/sql/logic/optimizer/enforcer.hpp new file mode 100644 index 0000000..4a7173d --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/enforcer.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace stewkk::sql { + +class Enforcer { +public: + virtual ~Enforcer() = default; + + virtual std::optional TryBuild( + utils::NotNull group, + const PropertySet& required, + SchemaCatalog& schema) const = 0; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/group.hpp b/include/stewkk/sql/logic/optimizer/group.hpp new file mode 100644 index 0000000..590aef9 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/group.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include +#include + +namespace stewkk::sql { + +using LogicalOperator = decltype(LogicalExpr::root_operator); +using PhysicalOperator = decltype(PhysicalExpr::root_operator); + +class Group { + private: + struct ToNotNull { + utils::NotNull operator()(LogicalExpr& e) const { return &e; } + }; + struct ToNotNullPhysical { + utils::NotNull operator()(PhysicalExpr& e) const { return &e; } + }; + + public: + using LogicalExprs = std::ranges::transform_view< + std::ranges::ref_view>, + ToNotNull>; + using PhysicalExprs = std::ranges::transform_view< + std::ranges::ref_view>, + ToNotNullPhysical>; + + utils::NotNull AddLogicalExpr(LogicalOperator root_operator); + utils::NotNull AddPhysicalExpr(PhysicalOperator root_operator, bool is_enforcer = false); + LogicalExprs GetLogicalExprs(); + PhysicalExprs GetPhysicalExprs(); + + size_t GetId() const; + + private: + friend class Memo; + explicit Group(size_t id) : id_(id) {} + + size_t id_; + std::deque logical_exprs_; + std::deque physical_exprs_; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/logical_expr.hpp b/include/stewkk/sql/logic/optimizer/logical_expr.hpp new file mode 100644 index 0000000..67050c1 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/logical_expr.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include +#include + +#include +#include + +namespace stewkk::sql { + +struct Group; + +namespace logical { + +using Table = Table; + +struct Filter { + utils::NotNull source; + Expression predicate; + + bool operator==(const Filter&) const = default; +}; + +struct Projection { + utils::NotNull source; + std::vector expressions; + std::vector> aliases; + + bool operator==(const Projection&) const = default; +}; + +struct Aggregation { + utils::NotNull source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const Aggregation&) const = default; +}; + +struct PartialAggregation { + utils::NotNull source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const PartialAggregation&) const = default; +}; + +struct FinalAggregation { + utils::NotNull source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const FinalAggregation&) const = default; +}; + +struct CrossJoin { + utils::NotNull lhs; + utils::NotNull rhs; + + bool operator==(const CrossJoin&) const = default; +}; + +struct Join { + utils::NotNull lhs; + utils::NotNull rhs; + using Type = JoinType; + Type type; + Expression qual; + + bool operator==(const Join&) const = default; +}; + +} // namespace logical + +struct LogicalExpr { + std::variant root_operator; + utils::NotNull group; + + struct Provenance { + size_t rule_id; + std::string_view rule_name; + LogicalExpr* source; + }; + std::optional provenance; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/memo.hpp b/include/stewkk/sql/logic/optimizer/memo.hpp new file mode 100644 index 0000000..4fef909 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/memo.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace stewkk::sql { + +class Memo { + public: + struct LogicalProvenance { + size_t rule_id; + std::string_view rule_name; + LogicalExpr* source; + }; + + class ScopedLogicalProvenance { + public: + ScopedLogicalProvenance(Memo& memo, LogicalProvenance provenance); + ~ScopedLogicalProvenance(); + + ScopedLogicalProvenance(const ScopedLogicalProvenance&) = delete; + ScopedLogicalProvenance& operator=(const ScopedLogicalProvenance&) = delete; + + private: + Memo& memo_; + std::optional previous_; + }; + + size_t GroupCount() const; + utils::NotNull AddGroup(LogicalOperator root_operator); + LogicalExpr* GetGroup(LogicalOperator root_operator) const; + utils::NotNull AddLogicalExprToGroup(utils::NotNull group, + LogicalOperator root_operator); + utils::NotNull Populate(const Operator& op); + + private: + LogicalExpr* GetGroup(const std::string& key) const; + void SetProvenanceIfNew(utils::NotNull expr); + + std::deque groups_; + std::unordered_map expr_index_; + std::optional current_provenance_; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/optimizer.hpp b/include/stewkk/sql/logic/optimizer/optimizer.hpp new file mode 100644 index 0000000..82ee638 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/optimizer.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +std::vector> GetChildren(utils::NotNull expr); +std::vector> GetChildren(utils::NotNull expr); + +enum class RuleLineageKind { + kTransformation, + kImplementation, + kEnforcer, +}; + +struct RuleLineageStat { + RuleLineageKind kind; + size_t rule_id; + std::string_view rule_name; + size_t count; +}; + +template +class Optimizer { +public: + Optimizer(const Operator& expr, Rules&& rules, + CardinalityEstimates cardinality, SchemaCatalog schema, + PropertySet required = PropertySet::Any(), + ConstraintCatalog constraints = {}); + + PhysicalPlanNode Optimize(); + PhysicalPlanNode OptimizeExhaustive(); + + std::int64_t GetBestCost() const; + utils::NotNull GetRootGroup() const; + std::vector GetSelectedPlanRuleLineage(); + +private: + using Limit = std::optional; + void RunSearch(Limit limit); + + bool IsExplored(utils::NotNull group) const; + void SetExplored(utils::NotNull group); + + void OptimizeInputs(utils::NotNull expr, PropertySet required, std::vector child_delivered, int64_t accum, Limit limit, size_t child_index = 0); + + std::int64_t LowerBoundCost(utils::NotNull group); + bool MarkOptimizeGroupRequested(const WinnerKey& key, Limit limit); + + void ApplyRule(TransformationRuleId rule, utils::NotNull expr, Limit limit); + + void TryRules(utils::NotNull expr, Limit limit); + void ExploreExpression(utils::NotNull expr, Limit limit); + void ExploreGroup(utils::NotNull group, Limit limit); + void OptimizeGroup(utils::NotNull group, PropertySet required = PropertySet::Any(), Limit limit = std::nullopt); + + PhysicalPlanNode BuildOptimalPlan(Group* group, PropertySet required = PropertySet::Any()); + void CollectSelectedPlanRuleLineage( + Group* group, PropertySet required, + std::unordered_map& stats, + std::unordered_set& visited_physical, + std::unordered_set& visited_logical); + void CollectLogicalRuleLineage( + LogicalExpr* expr, + std::unordered_map& stats, + std::unordered_set& visited_logical); + + struct WinnerEntry { + int64_t cost; + PhysicalExpr* plan; + PropertySet delivered; + }; + + Memo memo_; + RulesApplier rules_applier_; + std::stack> tasks_; + std::unordered_set explored_groups_; + std::unordered_set explored_exprs_; + utils::NotNull root_; + CardinalityEstimates cardinality_; + SchemaCatalog schema_; + ConstraintCatalog constraints_; + PropertySet global_required_; + std::unordered_map local_cost_; + std::unordered_map winner_; + std::unordered_set enforcers_added_; + std::unordered_map optimize_group_limits_; + std::unordered_map> group_parents_; + std::unordered_map lower_bounds_; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/physical_expr.hpp b/include/stewkk/sql/logic/optimizer/physical_expr.hpp new file mode 100644 index 0000000..44f559b --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/physical_expr.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace stewkk::sql { + +struct Group; +struct LogicalExpr; + +namespace physical { + +struct SeqScan { + std::string table; + std::optional alias; + + bool operator==(const SeqScan&) const = default; +}; + +struct IndexSeek { + std::string table; + std::optional alias; + Expression predicate; + std::string index_column; + + bool operator==(const IndexSeek&) const = default; +}; + +struct Projection { + utils::NotNull source; + std::vector expressions; + std::vector> aliases; + + bool operator==(const Projection&) const = default; +}; + +struct Filter { + utils::NotNull source; + Expression predicate; + + bool operator==(const Filter&) const = default; +}; + +struct NestedLoopJoin { + utils::NotNull lhs; + utils::NotNull rhs; + JoinType type; + Expression qual; + + bool operator==(const NestedLoopJoin&) const = default; +}; + +struct NestedLoopCrossJoin { + utils::NotNull lhs; + utils::NotNull rhs; + + bool operator==(const NestedLoopCrossJoin&) const = default; +}; + +struct HashJoin { + utils::NotNull lhs; + utils::NotNull rhs; + JoinType type; + Expression qual; + + bool operator==(const HashJoin&) const = default; +}; + +struct MergeJoin { + utils::NotNull lhs; + utils::NotNull rhs; + JoinType type; + Expression qual; + + bool operator==(const MergeJoin&) const = default; +}; + +struct Sort { + utils::NotNull input; + SortOrder keys; + + bool operator==(const Sort&) const = default; +}; + +struct Aggregation { + utils::NotNull source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const Aggregation&) const = default; +}; + +struct StreamAggregation { + utils::NotNull source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const StreamAggregation&) const = default; +}; + +struct PartialAggregation { + utils::NotNull source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const PartialAggregation&) const = default; +}; + +struct FinalAggregation { + utils::NotNull source; + std::vector group_by; + std::vector aggregates; + + bool operator==(const FinalAggregation&) const = default; +}; + +} // namespace physical + +struct PhysicalExpr { + enum class ProvenanceKind { + kImplementation, + kEnforcer, + }; + + struct Provenance { + ProvenanceKind kind; + size_t rule_id; + std::string_view rule_name; + LogicalExpr* source; + }; + + std::variant root_operator; + utils::NotNull group; + bool is_enforcer = false; + std::optional provenance; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/properties/property.hpp b/include/stewkk/sql/logic/optimizer/properties/property.hpp new file mode 100644 index 0000000..425425f --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/properties/property.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class Property { +public: + virtual ~Property() = default; + + virtual std::unique_ptr Clone() const = 0; + virtual bool Satisfies(const Property& required) const = 0; + virtual bool Equals(const Property& other) const = 0; + virtual std::size_t Hash() const = 0; +}; + +template +class PropertyBase : public Property { +public: + bool Satisfies(const Property& other) const final { + return static_cast(this)->SatisfiesTyped( + static_cast(other)); + } + bool Equals(const Property& other) const final { + return static_cast(this)->EqualsTyped( + static_cast(other)); + } +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/properties/property_set.hpp b/include/stewkk/sql/logic/optimizer/properties/property_set.hpp new file mode 100644 index 0000000..c3a006c --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/properties/property_set.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace stewkk::sql { + +class PropertySet { +public: + PropertySet() = default; + PropertySet(const PropertySet& other); + PropertySet(PropertySet&&) noexcept = default; + PropertySet& operator=(const PropertySet& other); + PropertySet& operator=(PropertySet&&) noexcept = default; + ~PropertySet() = default; + + template + requires (sizeof...(Ps) > 0) && + (std::derived_from, Property> && ...) + explicit PropertySet(Ps&&... ps) { + props_.reserve(sizeof...(Ps)); + (props_.push_back(std::make_unique>(std::forward(ps))), ...); + Normalize(); + } + + static PropertySet Any(); + + bool Satisfies(const PropertySet& required) const; + + template + const T* Get() const noexcept { + for (const auto& p : props_) { + const Property& prop = *p; + if (typeid(prop) == typeid(T)) return static_cast(&prop); + } + return nullptr; + } + + const std::vector>& Items() const noexcept; + + bool operator==(const PropertySet& other) const; + +private: + void Normalize(); + + std::vector> props_; +}; + +} // namespace stewkk::sql + +namespace std { + +template <> +struct hash { + size_t operator()(const stewkk::sql::PropertySet& ps) const noexcept; +}; + +} // namespace std diff --git a/include/stewkk/sql/logic/optimizer/properties/sort_order.hpp b/include/stewkk/sql/logic/optimizer/properties/sort_order.hpp new file mode 100644 index 0000000..de68d97 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/properties/sort_order.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +namespace stewkk::sql { + +enum class Direction { kAsc, kDesc }; + +struct SortKey { + std::string table; + std::string column; + Direction dir; + + bool operator==(const SortKey&) const = default; +}; + +struct SortOrder { + std::vector keys; + + bool Satisfies(const SortOrder& required) const; + + bool operator==(const SortOrder&) const = default; +}; + +} // namespace stewkk::sql + +namespace std { + +template <> +struct hash { + size_t operator()(const stewkk::sql::SortKey& k) const noexcept; +}; + +template <> +struct hash { + size_t operator()(const stewkk::sql::SortOrder& o) const noexcept; +}; + +} // namespace std diff --git a/include/stewkk/sql/logic/optimizer/properties/sort_property.hpp b/include/stewkk/sql/logic/optimizer/properties/sort_property.hpp new file mode 100644 index 0000000..562a954 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/properties/sort_property.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include +#include + +namespace stewkk::sql { + +class SortProperty final : public PropertyBase { +public: + SortOrder order; + + SortProperty() = default; + explicit SortProperty(SortOrder o) : order(std::move(o)) {} + + std::unique_ptr Clone() const override; + bool SatisfiesTyped(const SortProperty& required) const; + bool EqualsTyped(const SortProperty& other) const; + std::size_t Hash() const override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/reachability.hpp b/include/stewkk/sql/logic/optimizer/reachability.hpp new file mode 100644 index 0000000..be064c0 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/reachability.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +// FIXME: use std::expected +struct MatchResult { + bool reachable; + std::string mismatch; + int closest_distance = 0; +}; + +MatchResult IsReachable(utils::NotNull root, const PhysicalPlanNode& target); + +MatchResult IsPlanReachable(std::istream& sql, const PhysicalPlanNode& target, + CardinalityEstimates cardinality, + SchemaCatalog schema, + IndexCatalog indexes = {}); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/rule.hpp b/include/stewkk/sql/logic/optimizer/rule.hpp new file mode 100644 index 0000000..fa074cc --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/rule.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace stewkk::sql { + +class Memo; + +struct RuleContext { + SchemaCatalog& schema; + ConstraintCatalog& constraints; +}; + +class TransformationRule { + public: + virtual bool IsApplicable(utils::NotNull expr, RuleContext& ctx) = 0; + utils::NotNull Apply(utils::NotNull expr, Memo& memo, RuleContext& ctx); + virtual ~TransformationRule() = default; + + private: + virtual LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) = 0; +}; + +class ImplementationRule { + public: + virtual bool IsApplicable(utils::NotNull expr) = 0; + virtual std::vector> Apply(utils::NotNull, Memo& memo) = 0; + virtual ~ImplementationRule() = default; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/rules.hpp b/include/stewkk/sql/logic/optimizer/rules.hpp new file mode 100644 index 0000000..5b97b50 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/rules.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +template +struct Rules { + std::array, NTransformation> transformation_rules; + std::array, NImplementation> implementation_rules; + std::array transformation_rule_names; + std::array implementation_rule_names; +}; + +Rules<14, 9> MakeMainRules(IndexCatalog indexes = {}); +Rules<0, 6> MakeNaiveRules(); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/rules_applier.hpp b/include/stewkk/sql/logic/optimizer/rules_applier.hpp new file mode 100644 index 0000000..b511b24 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/rules_applier.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + + +struct TransformationRuleId { + size_t value; +}; + +struct ImplementationRuleId { + size_t value; +}; + +template +class RulesApplier { + public: + explicit RulesApplier(Rules rules); + + bool IsApplicable(TransformationRuleId rule, utils::NotNull expr, + RuleContext& ctx); + utils::NotNull Apply(TransformationRuleId rule, + utils::NotNull expr, Memo& memo, + RuleContext& ctx); + + bool IsApplicable(ImplementationRuleId rule, utils::NotNull expr); + std::vector> Apply(ImplementationRuleId rule, utils::NotNull expr, Memo& memo); + + std::string_view GetTransformationRuleName(TransformationRuleId rule) const; + std::string_view GetImplementationRuleName(ImplementationRuleId rule) const; + + private: + Rules rules_; + std::unordered_map> applied_transformation_rules_; + std::unordered_map> applied_implementation_rules_; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/schema_catalog.hpp b/include/stewkk/sql/logic/optimizer/schema_catalog.hpp new file mode 100644 index 0000000..c5c1e5e --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/schema_catalog.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace stewkk::sql { + +using Schema = std::vector; + +struct IndexInfo { + std::string table; + std::string column; + std::string type; + std::string file; +}; + +struct UniqueKeyInfo { + std::string table; + std::string column; +}; + +struct ForeignKeyInfo { + std::string from_table; + std::string from_column; + std::string to_table; + std::string to_column; +}; + +class IndexCatalog { +public: + IndexCatalog(std::vector indexes = {}); + + bool HasSortedIndex(const std::string& table, const std::string& column) const; + +private: + std::vector indexes_; +}; + +class ConstraintCatalog { +public: + ConstraintCatalog(std::vector unique_keys = {}, + std::vector foreign_keys = {}); + + bool IsUnique(const Attribute& attr) const; + bool HasForeignKey(const Attribute& from, const Attribute& to) const; + +private: + std::vector unique_keys_; + std::vector foreign_keys_; +}; + +class SchemaCatalog { +public: + SchemaCatalog(std::unordered_map tables = {}); + + Schema GetSchema(utils::NotNull group); + std::int64_t GetWidth(utils::NotNull group); + std::optional ResolveBaseAttribute(const Attribute& attr, + utils::NotNull group); + +private: + Schema Derive(const LogicalOperator& op); + + std::unordered_map tables_; + std::unordered_map cache_; +}; + +SchemaCatalog LoadSchemaFromCsvDir(const std::filesystem::path& dir); + +IndexCatalog LoadIndexCatalogFromCsvDir(const std::filesystem::path& dir); + +ConstraintCatalog LoadConstraintCatalogFromCsvDir(const std::filesystem::path& dir); + +std::unordered_map LoadTableSizesFromCsvDir( + const std::filesystem::path& dir); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/sort_enforcer.hpp b/include/stewkk/sql/logic/optimizer/sort_enforcer.hpp new file mode 100644 index 0000000..4273f3b --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/sort_enforcer.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +class SortEnforcer final : public Enforcer { +public: + std::optional TryBuild( + utils::NotNull group, + const PropertySet& required, + SchemaCatalog& schema) const override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/optimizer/winner_key.hpp b/include/stewkk/sql/logic/optimizer/winner_key.hpp new file mode 100644 index 0000000..7c7f791 --- /dev/null +++ b/include/stewkk/sql/logic/optimizer/winner_key.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include +#include + +namespace stewkk::sql { + +struct WinnerKey { + Group* group; + PropertySet required; + bool operator==(const WinnerKey&) const = default; +}; + +} // namespace stewkk::sql + +namespace std { + +template <> +struct hash { + size_t operator()(const stewkk::sql::WinnerKey& k) const noexcept; +}; + +} // namespace std diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index cf99d18..0ccc371 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -1,13 +1,20 @@ #pragma once #include +#include #include #include +#include namespace stewkk::sql { -Result GetAST(std::istream& in); +struct ParsedQuery { + Operator op; + std::optional required_order; +}; + +Result GetAST(std::istream& in); std::string GetDotRepresentation(const Operator& op); diff --git a/include/stewkk/sql/logic/transformation_rules/aggregation_join_transpose.hpp b/include/stewkk/sql/logic/transformation_rules/aggregation_join_transpose.hpp new file mode 100644 index 0000000..25e17c9 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/aggregation_join_transpose.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class AggregationJoinTranspose : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/aggregation_pushdown_through_join.hpp b/include/stewkk/sql/logic/transformation_rules/aggregation_pushdown_through_join.hpp new file mode 100644 index 0000000..c29e77a --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/aggregation_pushdown_through_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class AggregationPushdownThroughJoin : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/cross_join_to_join.hpp b/include/stewkk/sql/logic/transformation_rules/cross_join_to_join.hpp new file mode 100644 index 0000000..a54ece0 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/cross_join_to_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class CrossJoinToJoin : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/filter_lift_through_join.hpp b/include/stewkk/sql/logic/transformation_rules/filter_lift_through_join.hpp new file mode 100644 index 0000000..0f61ec2 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/filter_lift_through_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class FilterLiftThroughJoin : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/filter_merge.hpp b/include/stewkk/sql/logic/transformation_rules/filter_merge.hpp new file mode 100644 index 0000000..e330c79 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/filter_merge.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class FilterMerge : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/filter_pushdown_through_join.hpp b/include/stewkk/sql/logic/transformation_rules/filter_pushdown_through_join.hpp new file mode 100644 index 0000000..80d8c08 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/filter_pushdown_through_join.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class FilterPushdownThroughJoin : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/filter_pushdown_through_projection.hpp b/include/stewkk/sql/logic/transformation_rules/filter_pushdown_through_projection.hpp new file mode 100644 index 0000000..a74d6f2 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/filter_pushdown_through_projection.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class FilterPushdownThroughProjection : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/filter_split.hpp b/include/stewkk/sql/logic/transformation_rules/filter_split.hpp new file mode 100644 index 0000000..7e36f8b --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/filter_split.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class FilterSplit : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/filter_to_join_predicate.hpp b/include/stewkk/sql/logic/transformation_rules/filter_to_join_predicate.hpp new file mode 100644 index 0000000..ddd6de7 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/filter_to_join_predicate.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class FilterToJoinPredicate : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/in_to_or_chain.hpp b/include/stewkk/sql/logic/transformation_rules/in_to_or_chain.hpp new file mode 100644 index 0000000..ae0c809 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/in_to_or_chain.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class InToOrChain : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/join_associativity.hpp b/include/stewkk/sql/logic/transformation_rules/join_associativity.hpp new file mode 100644 index 0000000..00c3f9c --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/join_associativity.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class JoinAssociativity : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/join_commutativity.hpp b/include/stewkk/sql/logic/transformation_rules/join_commutativity.hpp new file mode 100644 index 0000000..3004be5 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/join_commutativity.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace stewkk::sql { + +class JoinCommutativity : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/outer_join_to_inner.hpp b/include/stewkk/sql/logic/transformation_rules/outer_join_to_inner.hpp new file mode 100644 index 0000000..0465da6 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/outer_join_to_inner.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class OuterJoinToInner : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/predicate_utils.hpp b/include/stewkk/sql/logic/transformation_rules/predicate_utils.hpp new file mode 100644 index 0000000..dd47c18 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/predicate_utils.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace stewkk::sql { + +bool IsTrue(const Expression& e); + +void CollectConjuncts(const Expression& e, std::vector& out); + +Expression AndConjuncts(const std::vector& conjs); + +Expression CanonicalizePredicate(const Expression& e); + +bool EquivalentPredicate(const Expression& lhs, const Expression& rhs); + +void CollectAttrTables(const Expression& e, std::unordered_set& out); + +void CollectAttributes(const Expression& e, std::vector& out); + +std::unordered_set ExprTables(const Expression& e); + +std::unordered_set GroupTables(utils::NotNull g); + +bool ExprUsesOnlyTables(const Expression& e, const std::unordered_set& tables); + +bool IsNullRejectingForTables(const Expression& e, const std::unordered_set& tables); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/transformation_rules/projection_pushdown_through_join.hpp b/include/stewkk/sql/logic/transformation_rules/projection_pushdown_through_join.hpp new file mode 100644 index 0000000..2c041b6 --- /dev/null +++ b/include/stewkk/sql/logic/transformation_rules/projection_pushdown_through_join.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class ProjectionPushdownThroughJoin : public TransformationRule { + public: + bool IsApplicable(utils::NotNull expr, RuleContext& ctx) override; + LogicalOperator ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext& ctx) override; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/models/executor/tuple.hpp b/include/stewkk/sql/models/executor/tuple.hpp index b7189a6..2e6544c 100644 --- a/include/stewkk/sql/models/executor/tuple.hpp +++ b/include/stewkk/sql/models/executor/tuple.hpp @@ -8,6 +8,7 @@ namespace stewkk::sql { enum class Type { kInt, kBool, + kString, }; std::string ToString(Type type); @@ -24,6 +25,7 @@ struct AttributeInfo { union NonNullValue { int64_t int_value; bool bool_value; + int64_t string_id; }; std::string ToString(bool v); @@ -37,6 +39,8 @@ struct Value { }; std::string ToString(Value v, const AttributeInfo& attr); +int64_t InternString(std::string value); +const std::string& GetInternedString(int64_t id); using Tuple = std::vector; using Tuples = std::vector; diff --git a/include/stewkk/sql/models/parser/expression.hpp b/include/stewkk/sql/models/parser/expression.hpp new file mode 100644 index 0000000..54d8a18 --- /dev/null +++ b/include/stewkk/sql/models/parser/expression.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +struct Attribute { + std::string table; + std::string name; + + auto operator<=>(const Attribute& other) const = default; +}; + +std::string ToString(const Attribute& attr); + +using IntConst = std::int64_t; +using StringConst = std::string; + +enum class Literal { + kNull, + kTrue, + kFalse, + kUnknown, +}; + +std::string ToString(Literal literal); + +enum class BinaryOp { + kGt, + kLt, + kLe, + kGe, + kNotEq, + kEq, + kOr, + kAnd, + kPlus, + kMinus, + kMul, + kDiv, + kMod, + kPow, +}; + +std::string ToString(BinaryOp binop); + +enum class UnaryOp { + kNot, + kMinus, + kIsNull, +}; + +std::string ToString(UnaryOp op); + +struct BinaryExpression; +struct UnaryExpression; +struct InExpression; +struct AggregateExpression; + +enum class AggregateFunction { + kSum, + kCount, +}; + +std::string ToString(AggregateFunction function); + +using Expression = std::variant; + +std::string ToString(const Expression& expr); + +struct BinaryExpression { + std::shared_ptr lhs; + BinaryOp binop; + std::shared_ptr rhs; + + bool operator==(const BinaryExpression& other) const; +}; + +struct UnaryExpression { + UnaryOp op; + std::shared_ptr child; + + bool operator==(const UnaryExpression& other) const; +}; + +struct InExpression { + std::shared_ptr lhs; + std::vector values; + bool negated = false; + + bool operator==(const InExpression& other) const; +}; + +struct AggregateExpression { + AggregateFunction function; + std::shared_ptr argument; + bool is_star = false; + + bool operator==(const AggregateExpression& other) const; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/models/parser/join_type.hpp b/include/stewkk/sql/models/parser/join_type.hpp new file mode 100644 index 0000000..c0eeae9 --- /dev/null +++ b/include/stewkk/sql/models/parser/join_type.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace stewkk::sql { + +enum class JoinType { + kInner, + kFull, + kLeft, + kRight, +}; + +std::string ToString(JoinType type); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp index 0f8ea9d..2f3fcc4 100644 --- a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp +++ b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp @@ -1,103 +1,45 @@ #pragma once #include +#include #include #include #include +#include + +#include +#include namespace stewkk::sql { constexpr static std::string kEmptyTableName = "_EMPTY_TABLE_"; -struct Attribute { - std::string table; - std::string name; - - auto operator<=>(const Attribute& other) const = default; -}; - -std::string ToString(const Attribute& attr); - -using IntConst = std::int64_t; - struct Table; struct Projection; struct Filter; +struct Aggregation; struct CrossJoin; struct Join; -using Operator = std::variant; +using Operator = std::variant; struct Table { std::string name; + std::optional alias; auto operator<=>(const Table& other) const = default; }; -struct BinaryExpression; -struct UnaryExpression; - -enum class Literal { - kNull, - kTrue, - kFalse, - kUnknown, -}; - -std::string ToString(Literal literal); - -using Expression = std::variant; - -std::string ToString(const Expression& expr); +std::string_view VisibleName(const Table& table); struct Projection { std::vector expressions; std::shared_ptr source; + std::vector> aliases; bool operator==(const Projection& other) const; }; -enum class BinaryOp { - kGt, - kLt, - kLe, - kGe, - kNotEq, - kEq, - kOr, - kAnd, - kPlus, - kMinus, - kMul, - kDiv, - kMod, - kPow, -}; - -std::string ToString(BinaryOp binop); - -struct BinaryExpression { - std::shared_ptr lhs; - BinaryOp binop; - std::shared_ptr rhs; - - bool operator==(const BinaryExpression& other) const; -}; - -enum class UnaryOp { - kNot, - kMinus, -}; - -std::string ToString(UnaryOp op); - -struct UnaryExpression { - UnaryOp op; - std::shared_ptr child; - - bool operator==(const UnaryExpression& other) const; -}; - struct Filter { Expression expr; std::shared_ptr source; @@ -105,6 +47,14 @@ struct Filter { bool operator==(const Filter& other) const; }; +struct Aggregation { + std::vector group_by; + std::vector aggregates; + std::shared_ptr source; + + bool operator==(const Aggregation& other) const; +}; + struct CrossJoin { std::shared_ptr lhs; std::shared_ptr rhs; @@ -112,15 +62,6 @@ struct CrossJoin { bool operator==(const CrossJoin& other) const; }; -enum class JoinType { - kInner, - kFull, - kLeft, - kRight, -}; - -std::string ToString(JoinType type); - struct Join { JoinType type; Expression qual; diff --git a/include/stewkk/sql/utils/log.hpp b/include/stewkk/sql/utils/log.hpp new file mode 100644 index 0000000..ee99705 --- /dev/null +++ b/include/stewkk/sql/utils/log.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace stewkk::sql { + +inline constexpr bool kDebug = +#ifdef STEWKK_SQL_DEBUG + true; +#else + false; +#endif + +template +void Log(std::format_string fmt, Args&&... args) { + if constexpr (kDebug) { + std::println(stderr, fmt, std::forward(args)...); + } +} + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/utils/not_null.hpp b/include/stewkk/sql/utils/not_null.hpp new file mode 100644 index 0000000..d34f78a --- /dev/null +++ b/include/stewkk/sql/utils/not_null.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace stewkk::sql::utils { + +template + requires std::is_pointer_v +class NotNull { + public: + NotNull(T ptr) : ptr_(ptr) { assert(ptr_ != nullptr); } + NotNull(std::nullptr_t) = delete; + + T get() const { return ptr_; } + auto& operator*() const { return *ptr_; } + T operator->() const { return ptr_; } + + operator T() const { return ptr_; } + + bool operator==(const NotNull& other) const = default; + + private: + T ptr_; +}; + +} // namespace stewkk::sql::utils diff --git a/include/stewkk/sql/utils/output_dot_plans.hpp b/include/stewkk/sql/utils/output_dot_plans.hpp new file mode 100644 index 0000000..3cd5556 --- /dev/null +++ b/include/stewkk/sql/utils/output_dot_plans.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace stewkk::sql { + +void OutputDot(const PhysicalPlanNode& optimizer_plan, const std::optional other_plan); + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/utils/overloaded.hpp b/include/stewkk/sql/utils/overloaded.hpp new file mode 100644 index 0000000..9eb1b19 --- /dev/null +++ b/include/stewkk/sql/utils/overloaded.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace stewkk::sql::utils { + +template struct Overloaded : Ts... { using Ts::operator()...; }; + +} // namespace stewkk::sql::utils diff --git a/query.sql b/query.sql new file mode 100644 index 0000000..6efbe87 --- /dev/null +++ b/query.sql @@ -0,0 +1,11 @@ +SELECT d.d_year, c.c_nation, SUM(lo.lo_revenue - lo.lo_supplycost) AS profit +FROM lineorder AS lo +JOIN date AS d ON lo.lo_orderdate = d.d_datekey +JOIN customer AS c ON lo.lo_custkey = c.c_custkey +JOIN supplier AS s ON lo.lo_suppkey = s.s_suppkey +JOIN part AS p ON lo.lo_partkey = p.p_partkey +WHERE c.c_region = 'AMERICA' + AND s.s_region = 'AMERICA' + AND p.p_mfgr IN ('MFGR#1', 'MFGR#2') +GROUP BY d.d_year, c.c_nation +ORDER BY d.d_year, c.c_nation; diff --git a/report/Makefile b/report/Makefile index bca1c92..99d8896 100644 --- a/report/Makefile +++ b/report/Makefile @@ -3,7 +3,7 @@ all: build build: - @latexmk -f -pdf -output-directory=build -shell-escape ./report.tex + @latexmk -f -pdf -output-directory=build-vkr -shell-escape ./vkr.tex bib: @cp ./biblio.bib ./build @@ -20,4 +20,4 @@ view: @evince ./build/report.pdf pres: - @latexmk -f -pdf -output-directory=build -shell-escape ./presentation.tex + @latexmk -f -pdf -output-directory=build-vkr -shell-escape ./presentation-vkr.tex diff --git a/report/beamerthemeIU9light.sty b/report/beamerthemeIU9light.sty new file mode 100644 index 0000000..a267e9a --- /dev/null +++ b/report/beamerthemeIU9light.sty @@ -0,0 +1,127 @@ +\RequirePackage{tikz} +\RequirePackage{inputenc} +\RequirePackage{contour} +\RequirePackage{babel} +\RequirePackage{iftex} +%\ifPDFTeX +% \RequirePackage{garamondx} +%\fi + +\AtBeginDocument{\setbeamertemplate{logo}{}} + +\newcommand{\iunine@logo}{\raisebox{-3.2ex}{\includegraphics[width=0.115\textwidth]{emblem.png}}} +\newcommand{\iunine@supervisor}{} +\newcommand{\iunine@department}{} +\newcommand{\iunine@titlepagelogoA}{\includegraphics[width=0.25\textwidth]{emblem.png}} +\newcommand{\iunine@titlepagelogoB}{} +\newcommand{\iunine@backgroundlogo}{} + +\renewcommand{\logo}[1]{\renewcommand{\iunine@logo}{#1}} +\newcommand{\supervisor}[1]{\renewcommand{\iunine@supervisor}{#1}} +\newcommand{\department}[1]{\renewcommand{\iunine@department}{#1}} +\newcommand{\titlepagelogoA}[1]{\renewcommand{\iunine@titlepagelogoA}{#1}} +\newcommand{\titlepagelogoB}[1]{\renewcommand{\iunine@titlepagelogoB}{#1}} +\newcommand{\backgroundlogo}[1]{\renewcommand{\iunine@backgroundlogo}{#1}} + +\newcommand{\thanksframe}[1]{{ + \setbeamertemplate{background}{ + \begin{tikzpicture} + \useasboundingbox (0,0) rectangle(\the\paperwidth,\the\paperheight); + \fill[MainBlue] (0,0) rectangle(\the\paperwidth,\the\paperheight); + \end{tikzpicture} + } + \begin{frame}[c] + \centering\Large\color{white} #1 + \end{frame} +}} + +\definecolor{MainBlue}{RGB}{70,70,150} +\definecolor{LightBlue}{RGB}{230,230,250} +\definecolor{DarkBlue}{RGB}{120,120,160} +\definecolor{TitleBlue}{RGB}{150,150,180} +\definecolor{TextGray}{RGB}{66,66,66} +\definecolor{TitleLine}{RGB}{23,33,113} +\definecolor{FrameBackground}{RGB}{234,234,234} + +\setbeamercolor{normal text}{fg=TextGray, bg=} +\setbeamercolor{frametitle}{fg=MainBlue!65!black} +\setbeamercolor{footline}{bg=MainBlue, fg=MainBlue} +\setbeamercolor{logo}{parent={footline}} +\setbeamercolor{institute in head/foot}{parent={footline}} +\setbeamercolor{item}{parent={normal text}} +\setbeamercolor{title page}{fg=MainBlue!65!black} +\setbeamercolor{block title}{fg=MainBlue,bg=LightBlue!80!white} +\setbeamercolor{block title example}{fg=MainBlue} +\setbeamercolor{section in toc}{fg=MainBlue} +\setbeamercolor{subsection in toc}{fg=MainBlue} + +\setbeamerfont{title}{size=\LARGE, series=\bfseries} +\setbeamerfont{author}{shape=\itshape} +\setbeamerfont{frametitle}{series=\bfseries} +\setbeamerfont{institute}{family=\rmfamily, series=\bfseries, size=\small} + +\setbeamertemplate{navigation symbols}{} +\setbeamertemplate{itemize items}[circle] +\setbeamertemplate{background canvas}[vertical shading][bottom=FrameBackground,top=white] + +\setbeamertemplate{footline}{ +\leavevmode% +\hbox{% + \begin{beamercolorbox}[wd=0.1\paperwidth, ht=0.7cm, dp=1ex, right, rightskip=0.5cm]{white}% +\ifnum\thepage>1 {\begin{tikzpicture} +% \useasboundingbox (1,0) rectangle (\the\paperwidth,\the\paperheight); + \fill[right color=DarkBlue, left color=LightBlue!70!DarkBlue] (0, 0.5) rectangle (\the\paperwidth, 0.55); + \fill[right color=DarkBlue, left color=LightBlue!70!DarkBlue] (0, 0.65) rectangle (\the\paperwidth, 0.7); + \fill[FrameBackground] (0, 0) rectangle (\the\paperwidth, -0.01); +\end{tikzpicture} }\fi + \end{beamercolorbox}% + \begin{beamercolorbox}[wd=0.55\paperwidth, ht=0.7cm, dp=1ex, left, leftskip=0cm]{white}% + \ifnum\thepage>1 \raisebox{2ex}{\textbf{\insertframenumber/\inserttotalframenumber},\; \insertshorttitle{},\; \insertshortdate{}}\fi + \end{beamercolorbox}% + \begin{beamercolorbox}[wd=0.4\paperwidth, ht=0.7cm, dp=1ex, right, rightskip=1.15cm]{white}% + \ifnum\thepage>1 \iunine@logo \fi + \end{beamercolorbox}% +}% +} + +\setbeamertemplate{background}{% +\ifnum\thepage>1\relax% + \begin{tikzpicture} + \useasboundingbox (0,0) rectangle(\the\paperwidth,\the\paperheight); + \fill[top color=MainBlue!5!white, bottom color=MainBlue!20!white] (0,-34+\the\paperheight) rectangle (\the\paperwidth,\the\paperheight); + \draw[TitleLine!90!white,thick] ([yshift=-1.2cm]current page.north west) -- ([yshift=-1.2cm]current page.north east); + \end{tikzpicture} + \else% + \begin{tikzpicture} + \useasboundingbox (0,0) rectangle(\the\paperwidth,\the\paperheight); + \fill[top color=LightBlue!90!TitleBlue, middle color=LightBlue, bottom color=white] (0,0) rectangle (\the\paperwidth,\the\paperheight); + \end{tikzpicture}% + \fi% +} + +\setbeamertemplate{title page}{ +\usebeamercolor{title page} +\begin{tikzpicture} + \useasboundingbox (1,0) rectangle (\the\paperwidth,\the\paperheight); + \node[opacity=.3] at (12.5, 0.7) {\iunine@backgroundlogo}; +% \draw[step=1, help lines] (0,0) grid (\the\paperwidth,\the\paperheight); +% \fill[top color=white, middle color=LightBlue, bottom color=DarkBlue!30!LightBlue] (0, 8.75) rectangle (\the\paperwidth, \the\paperheight); +% \draw[TitleLine!50] ([yshift=-0.85cm]current page.north west) -- ([yshift=-0.85cm]current page.north east); + \draw[TitleLine!70!black,thick] ([yshift=-2.65cm]current page.north west) -- ([yshift=-2.65cm]current page.north east); + \node at (1.45, 8.15) {\iunine@titlepagelogoA}; +% \draw[thick] (3.4, 8.25) -- (3.4, 7.25); + \node[right, align=left, font=\usebeamerfont{institute}] at (2.75, 8.0) {\insertinstitute}; + \node[right] at (9.9, 7.8) {\iunine@titlepagelogoB}; + \node[align=center, font=\usebeamerfont{title}, text width=10.5cm] at (6.1, 4.5) {\inserttitle}; + \node[align=center, font=\usebeamerfont{subtitle}] at (6.1, 3.5) {\insertsubtitle}; + \node[align=left, font=\usebeamerfont{author}] at (10.3, 1.4) {\insertauthor}; + \node[align=left, font=\usebeamerfont{author}, right] at (1.3, 1.4) {\iunine@supervisor}; +\end{tikzpicture} +} + +\setbeamertemplate{frametitle}{ +\vspace*{0.1cm}% +\begin{beamercolorbox}{frametitle} + \usebeamerfont{frametitle}{\contour[5]{LightBlue}{\insertframetitle}} +\end{beamercolorbox} +} diff --git a/report/biblio.bib b/report/biblio.bib index edb22c7..cd24608 100644 --- a/report/biblio.bib +++ b/report/biblio.bib @@ -1,30 +1,117 @@ +@inproceedings{Selinger1979, + author = {Selinger, P. Griffiths and Astrahan, M. M. and Chamberlin, D. D. and Lorie, R. A. and Price, T. G.}, + title = {Access path selection in a relational database management system}, + booktitle = {Proceedings of the 1979 ACM SIGMOD International Conference on Management of Data}, + series = {SIGMOD '79}, + pages = {23--34}, + year = {1979}, + doi = {10.1145/582095.582099}, + language = {english}, + note = {(дата обращения: 01.05.2026)} +} + +@article{Graefe1995, + author = {Graefe, Goetz}, + title = {The Cascades Framework for Query Optimization}, + journal = {IEEE Data Engineering Bulletin}, + volume = {18}, + number = {3}, + pages = {19--29}, + year = {1995}, + language = {english}, + note = {(дата обращения: 01.05.2026)} +} + +@inproceedings{GraefeMcKenna1993, + author = {Graefe, Goetz and McKenna, William J.}, + title = {The {Volcano} Optimizer Generator: Extensibility and Efficient Search}, + booktitle = {Proceedings of IEEE 9th International Conference on Data Engineering}, + pages = {209--218}, + year = {1993}, + doi = {10.1109/ICDE.1993.344061}, + language = {english}, + note = {(дата обращения: 01.05.2026)} +} + +@article{DingNarasayya2024, + author = {Ding, Bailu and Narasayya, Vivek and Chaudhuri, Surajit}, + title = {Extensible Query Optimizers in Practice}, + journal = {Foundations and Trends in Databases}, + volume = {14}, + number = {3-4}, + pages = {186--402}, + year = {2024}, + doi = {10.1561/1900000077}, + language = {english}, + note = {(дата обращения: 01.05.2026)} +} + +@inproceedings{Begoli2018Calcite, + author = {Begoli, Edmon and Camacho-Rodr{\'i}guez, Jes{\'u}s and Hyde, Julian + and Mior, Michael J. and Lemire, Daniel}, + title = {Apache {Calcite}: A foundational framework for optimized query + processing over heterogeneous data sources}, + booktitle = {Proceedings of the 2018 International Conference on Management of Data}, + series = {SIGMOD '18}, + pages = {221--230}, + year = {2018}, + doi = {10.1145/3183713.3190662} +} + +@phdthesis{XuColumbia1998, + author = {Xu, Yongwen}, + title = {Efficiency in the {Columbia} Database Query Optimizer}, + school = {Portland State University}, + year = {1998} +} + +@misc{IntroToTheJoinOrderingProblem, +title = {Introduction to the Join Ordering Problem}, +url = {https://www.querifylabs.com/blog/introduction-to-the-join-ordering-problem}, +language = {english}, +note = {(дата обращения: 01.05.2026)} +} + +@article{leis2015good, + title={How good are query optimizers, really?}, + author={Leis, Viktor and Gubichev, Andrey and Mirchev, Atanas and Boncz, Peter and Kemper, Alfons and Neumann, Thomas}, + journal={Proceedings of the VLDB Endowment}, + volume={9}, + number={3}, + pages={204--215}, + year={2015}, + publisher={VLDB Endowment}, + language = {english}, + note = {(дата обращения: 01.05.2026)} +} + @misc{DBReport, title = {Исследование рынка СУБД}, url = {https://www.mordorintelligence.com/industry-reports/database-market}, language = {english}, -note = {(дата обращения: 17.01.2026)} +note = {(дата обращения: 17.05.2026)} } @misc{PostgresDocs, title = {Документация PostgreSQL}, url = {https://www.postgresql.org/docs/current/sql-select.html}, language = {english}, -note = {(дата обращения: 17.01.2026)} +note = {(дата обращения: 17.05.2026)} } @misc{PostgresBisonGrammar, title = {Грамматика PostgreSQL}, url = {https://github.com/postgres/postgres/blob/master/src/backend/parser/gram.y}, language = {english}, -note = {(дата обращения: 17.01.2026)} +note = {(дата обращения: 17.05.2026)} } @misc{Antlr4PostgreSQL, title = {Грамматика PostgreSQL для ANTLR4}, url = {https://github.com/antlr/grammars-v4/blob/master/sql/postgresql/PostgreSQLParser.g4}, language = {english}, -note = {(дата обращения: 17.01.2026)} +note = {(дата обращения: 17.05.2026)} } @book{silberschatz2020database, @@ -33,7 +120,7 @@ @book{silberschatz2020database year={2020}, publisher={Mcgraw-hill New York}, language = {english}, - note = {(дата обращения: 17.01.2026)} + note = {(дата обращения: 17.05.2026)} } @inproceedings{pantilimonov2019machine, @@ -44,7 +131,7 @@ @inproceedings{pantilimonov2019machine year={2019}, organization={IEEE}, language = {english}, - note = {(дата обращения: 17.01.2026)} + note = {(дата обращения: 17.05.2026)} } @book{petrov2019database, @@ -53,7 +140,7 @@ @book{petrov2019database year={2019}, publisher={O'Reilly Media}, language = {english}, - note = {(дата обращения: 17.01.2026)} + note = {(дата обращения: 17.05.2026)} } @article{graefe2002volcano, @@ -66,5 +153,5 @@ @article{graefe2002volcano year={2002}, publisher={IEEE}, language = {english}, - note = {(дата обращения: 17.01.2026)} + note = {(дата обращения: 17.05.2026)} } diff --git a/report/calibration.png b/report/calibration.png new file mode 100644 index 0000000..865144e Binary files /dev/null and b/report/calibration.png differ diff --git a/report/cascades.dot b/report/cascades.dot new file mode 100644 index 0000000..307d9f9 --- /dev/null +++ b/report/cascades.dot @@ -0,0 +1,27 @@ +digraph optimizer { + node [shape=box] + + Entry [shape=none, label=""] + OptGrp [label="Оптимизация группы\n(OptGrp)"] + OptExpr [label="Оптимизация выражения\n(OptExpr)"] + OptInputs [label="Оптимизация входных групп\n(OptInputs)"] + ApplyRule [label="Применение правила\n(ApplyRule)"] + ExplGrp [label="Исследование группы\n(ExplGrp)"] + ExplExpr [label="Исследование выражения\n(ExplExpr)"] + + Entry -> OptGrp [label="Выражение"] + + OptGrp -> OptExpr + OptExpr -> OptExpr + OptExpr -> OptGrp + OptExpr -> OptInputs + OptExpr -> ApplyRule + OptExpr -> ExplGrp + OptInputs -> OptInputs + OptInputs -> OptGrp + ApplyRule -> OptInputs + ExplGrp -> ApplyRule + ExplGrp -> ExplExpr + ExplExpr -> ExplGrp + ExplExpr -> OptGrp +} diff --git a/report/cascades.pdf b/report/cascades.pdf new file mode 100644 index 0000000..00f5a01 Binary files /dev/null and b/report/cascades.pdf differ diff --git a/report/cost-on-random-queries.png b/report/cost-on-random-queries.png new file mode 100644 index 0000000..2cd2a7f Binary files /dev/null and b/report/cost-on-random-queries.png differ diff --git a/report/header.tex b/report/header.tex new file mode 100644 index 0000000..b279b1a --- /dev/null +++ b/report/header.tex @@ -0,0 +1,483 @@ +\let\counterwithout\relax +\let\counterwithin\relax +\usepackage{float} +\usepackage{xcolor} +\usepackage{subfig} +\usepackage[export]{adjustbox} +\usepackage{tocvsec2} % возможность менять учитываемую глубину разделов в оглавлении +\usepackage[subfigure]{tocloft} +\usepackage[newfloat]{minted} +\captionsetup[listing]{position=top} + +\makeatletter +\AddToHook{begindocument/before}{\@ifpackageloaded{minted}{\removefromtoclist[float]{lol}}{}} +\makeatother + +\AtBeginEnvironment{figure}{\vspace{0.5cm}} +\AtBeginEnvironment{table}{\vspace{0.5cm}} +\AtBeginEnvironment{listing}{\vspace{0.5cm}} +\AtBeginEnvironment{algorithm}{\vspace{0.5cm}} +\AtBeginEnvironment{minted}{\vspace{-0.5cm}} + +\usepackage{fancyvrb} +\usepackage{ulem,bm,mathrsfs,ifsym} %зачеркивания, особо жирный стиль и RSFS начертание +\usepackage{sectsty} % переопределение стилей подразделов +%%%%%%%%%%%%%%%%%%%%%%% + +%%% Поля и разметка страницы %%% +\usepackage{pdflscape} % Для включения альбомных страниц +\usepackage{geometry} % Для последующего задания полей +\geometry{a4paper,tmargin=2cm,bmargin=2cm,lmargin=3cm,rmargin=1cm,includefoot} % тоже самое, но лучше + +%%% Математические пакеты %%% +\usepackage{amsthm,amsfonts,amsmath,amssymb,amscd} % Математические дополнения от AMS +\usepackage{mathtools} % Добавляет окружение multlined +\usepackage[perpage]{footmisc} +%\usepackage{times} + +%%%% Установки для размера шрифта 14 pt %%%% +%% Формирование переменных и констант для сравнения (один раз для всех подключаемых файлов)%% +%% должно располагаться до вызова пакета fontspec или polyglossia, потому что они сбивают его работу +%\newlength{\curtextsize} +%\newlength{\bigtextsize} +%\setlength{\bigtextsize}{13pt} +\KOMAoptions{fontsize=14pt} + +\makeatletter +\def\showfontsize{\f@size{} point} +\makeatother + +\makeatletter +\setlength{\@fptop}{0pt} +\makeatother + +%\makeatletter +%\show\f@size % неплохо для отслеживания, но вызывает стопорение процесса, если документ компилируется без команды -interaction=nonstopmode +%\setlength{\curtextsize}{\f@size pt} +%\makeatother + +%шрифт times +\usepackage{tempora} +%\usepackage{pscyr} +%\setmainfont[Ligatures={TeX,Historic}]{Times New Roman} + +%%% Решение проблемы копирования текста в буфер кракозябрами +% \input glyphtounicode.tex +% \input glyphtounicode-cmr.tex %from pdfx package +% \pdfgentounicode=1 +\usepackage{cmap} % Улучшенный поиск русских слов в полученном pdf-файле +\usepackage[T2A]{fontenc} % Поддержка русских букв +\usepackage[utf8]{inputenc} % Кодировка utf8 +\usepackage[english, main=russian]{babel} % Языки: русский, английский +% \IfFileExists{pscyr.sty}{\usepackage{pscyr}}{} % Красивые русские шрифты +%\renewcommand{\rmdefault}{ftm} +%%% Оформление абзацев %%% +\usepackage{indentfirst} % Красная строка +%\usepackage{eskdpz} + +%%% Таблицы %%% +\usepackage{longtable} % Длинные таблицы +\usepackage{multirow,makecell,array} % Улучшенное форматирование таблиц +\usepackage{booktabs} % Возможность оформления таблиц в классическом книжном стиле (при правильном использовании не противоречит ГОСТ) + +%%% Общее форматирование +\usepackage{soulutf8} % Поддержка переносоустойчивых подчёркиваний и зачёркиваний +\usepackage{icomma} % Запятая в десятичных дробях + +\usepackage{tikz} +\usetikzlibrary{automata,patterns, positioning, arrows,shadows,shapes,datavisualization} +\usetikzlibrary{decorations.pathreplacing} +\usetikzlibrary{arrows.meta,patterns.meta,graphs} + + +%%% Изображения %%% +\usepackage{graphicx} % Подключаем пакет работы с графикой +\usepackage{wrapfig} + +%%% Списки %%% +\usepackage{enumitem} + +%%% Подписи %%% +\usepackage{caption} % Для управления подписями (рисунков и таблиц) % Может управлять номерами рисунков и таблиц с caption %Иногда может управлять заголовками в списках рисунков и таблиц +%% Использование: +%\begin{table}[h!]\ContinuedFloat - чтобы не переключать счетчик +%\captionsetup{labelformat=continued}% должен стоять до самого caption +%\caption{} +% либо ручками \caption*{Продолжение таблицы~\ref{...}.} :) + +%%% Интервалы %%% +\addto\captionsrussian{% + \renewcommand{\listingname}{Листинг}% +} +%%% Счётчики %%% +\usepackage[figure,table,section]{totalcount} % Счётчик рисунков и таблиц +\DeclareTotalCounter{lstlisting} +\usepackage{totcount} % Пакет создания счётчиков на основе последнего номера подсчитываемого элемента (может требовать дважды компилировать документ) +\usepackage{totpages} % Счётчик страниц, совместимый с hyperref (ссылается на номер последней страницы). Желательно ставить последним пакетом в преамбуле + +%%% Продвинутое управление групповыми ссылками (пока только формулами) %%% +%% Кодировки и шрифты %%% + +% \newfontfamily{\cyrillicfont}{Times New Roman} +% \newfontfamily{\cyrillicfonttt}{CMU Typewriter Text} +%\setmainfont{Times New Roman} +%\newfontfamily\cyrillicfont{Times New Roman} +%\setsansfont{Times New Roman} %% задаёт шрифт без засечек +% \setmonofont{Liberation Mono} %% задаёт моноширинный шрифт +% \IfFileExists{pscyr.sty}{\renewcommand{\rmdefault}{ftm}}{} +%%% Интервалы %%% +%linespread-реализация ближе к реализации полуторного интервала в ворде. +%setspace реализация заточена под шрифты 10, 11, 12pt, под остальные кегли хуже, но всё же ближе к типографской классике. +\linespread{1.3} % Полуторный интервал (ГОСТ Р 7.0.11-2011, 5.3.6) +%\renewcommand{\@biblabel}[1]{#1} + +%%% Гиперссылки %%% +\usepackage{hyperref} + +%%% Выравнивание и переносы %%% +\sloppy % Избавляемся от переполнений +\clubpenalty=10000 % Запрещаем разрыв страницы после первой строки абзаца +\widowpenalty=10000 % Запрещаем разрыв страницы после последней строки абзаца + +\makeatletter % малые заглавные, small caps shape +\let\@@scshape=\scshape +\renewcommand{\scshape}{% + \ifnum\strcmp{\f@series}{bx}=\z@ + \usefont{T1}{cmr}{bx}{sc}% + \else + \ifnum\strcmp{\f@shape}{it}=\z@ + \fontshape{scsl}\selectfont + \else + \@@scshape + \fi + \fi} +\makeatother + +%%% Подписи %%% +%\captionsetup{% +%singlelinecheck=off, % Многострочные подписи, например у таблиц +%skip=2pt, % Вертикальная отбивка между подписью и содержимым рисунка или таблицы определяется ключом +%justification=centering, % Центрирование подписей, заданных командой \caption +%} +%%% Подключение пакетов %%% +\usepackage{ifthen} % добавляет ifthenelse +%%% Инициализирование переменных, не трогать! %%% +\newcounter{intvl} +\newcounter{otstup} +\newcounter{contnumeq} +\newcounter{contnumfig} +\newcounter{contnumtab} +\newcounter{pgnum} +\newcounter{bibliosel} +\newcounter{chapstyle} +\newcounter{headingdelim} +\newcounter{headingalign} +\newcounter{headingsize} +\newcounter{tabcap} +\newcounter{tablaba} +\newcounter{tabtita} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%% Область упрощённого управления оформлением %%% + +%% Интервал между заголовками и между заголовком и текстом +% Заголовки отделяют от текста сверху и снизу тремя интервалами (ГОСТ Р 7.0.11-2011, 5.3.5) +\setcounter{intvl}{3} % Коэффициент кратности к размеру шрифта + +%% Отступы у заголовков в тексте +\setcounter{otstup}{0} % 0 --- без отступа; 1 --- абзацный отступ + +%% Нумерация формул, таблиц и рисунков +\setcounter{contnumeq}{1} % Нумерация формул: 0 --- пораздельно (во введении подряд, без номера раздела); 1 --- сквозная нумерация по всей диссертации +\setcounter{contnumfig}{1} % Нумерация рисунков: 0 --- пораздельно (во введении подряд, без номера раздела); 1 --- сквозная нумерация по всей диссертации +\setcounter{contnumtab}{1} % Нумерация таблиц: 0 --- пораздельно (во введении подряд, без номера раздела); 1 --- сквозная нумерация по всей диссертации + +%% Оглавление +\setcounter{pgnum}{0} % 0 --- номера страниц никак не обозначены; 1 --- Стр. над номерами страниц (дважды компилировать после изменения) + +%% Библиография +\setcounter{bibliosel}{1} % 0 --- встроенная реализация с загрузкой файла через движок bibtex8; 1 --- реализация пакетом biblatex через движок biber + +%% Текст и форматирование заголовков +\setcounter{chapstyle}{1} % 0 --- разделы только под номером; 1 --- разделы с названием "Глава" перед номером +\setcounter{headingdelim}{1} % 0 --- номер отделен пропуском в 1em или \quad; 1 --- номера разделов и приложений отделены точкой с пробелом, подразделы пропуском без точки; 2 --- номера разделов, подразделов и приложений отделены точкой с пробелом. + +%% Выравнивание заголовков в тексте +\setcounter{headingalign}{0} % 0 --- по центру; 1 --- по левому краю + +%% Размеры заголовков в тексте +\setcounter{headingsize}{0} % 0 --- по ГОСТ, все всегда 14 пт; 1 --- пропорционально изменяющийся размер в зависимости от базового шрифта + +%% Подпись таблиц +\setcounter{tabcap}{0} % 0 --- по ГОСТ, номер таблицы и название разделены тире, выровнены по левому краю, при необходимости на нескольких строках; 1 --- подпись таблицы не по ГОСТ, на двух и более строках, дальнейшие настройки: +%Выравнивание первой строки, с подписью и номером +\setcounter{tablaba}{2} % 0 --- по левому краю; 1 --- по центру; 2 --- по правому краю +%Выравнивание строк с самим названием таблицы +\setcounter{tabtita}{1} % 0 --- по левому краю; 1 --- по центру; 2 --- по правому краю + +%%% Рисунки %%% +\DeclareCaptionLabelSeparator*{emdash}{~--- } % (ГОСТ 2.105, 4.3.1) +\captionsetup[figure]{labelsep=emdash,font=onehalfspacing,position=bottom} + +%%% Таблицы %%% +\ifthenelse{\equal{\thetabcap}{0}}{% + \newcommand{\tabcapalign}{\raggedright} % по левому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetablaba}{0} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabcapalign}{\raggedright} % по левому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetablaba}{1} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabcapalign}{\centering} % по центру страницы или аналога parbox +} + +\ifthenelse{\equal{\thetablaba}{2} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabcapalign}{\raggedleft} % по правому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetabtita}{0} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabtitalign}{\raggedright} % по левому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetabtita}{1} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabtitalign}{\centering} % по центру страницы или аналога parbox +} + +\ifthenelse{\equal{\thetabtita}{2} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabtitalign}{\raggedleft} % по правому краю страницы или аналога parbox +} + +\DeclareCaptionFormat{tablenocaption}{\tabcapalign #1\strut} % Наименование таблицы отсутствует +\ifthenelse{\equal{\thetabcap}{0}}{% + \DeclareCaptionFormat{tablecaption}{\tabcapalign #1#2#3} + \captionsetup[table]{labelsep=emdash} % тире как разделитель идентификатора с номером от наименования +}{% + \DeclareCaptionFormat{tablecaption}{\tabcapalign #1#2\par% % Идентификатор таблицы на отдельной строке + \tabtitalign{#3}} % Наименование таблицы строкой ниже + \captionsetup[table]{labelsep=space} % пробельный разделитель идентификатора с номером от наименования +} +\captionsetup[table]{format=tablecaption,singlelinecheck=off,font=onehalfspacing,position=top,skip=-5pt} % многострочные наименования и прочее +\DeclareCaptionLabelFormat{continued}{Продолжение таблицы~#2} +\setlength{\belowcaptionskip}{.2cm} +\setlength{\intextsep}{0ex} + +%%% Подписи подрисунков %%% +\renewcommand{\thesubfigure}{\asbuk{subfigure}} % Буквенные номера подрисунков +\captionsetup[subfigure]{font={normalsize}, % Шрифт подписи названий подрисунков (не отличается от основного) + labelformat=brace, % Формат обозначения подрисунка + justification=centering, % Выключка подписей (форматирование), один из вариантов +} +%\DeclareCaptionFont{font12pt}{\fontsize{12pt}{13pt}\selectfont} % объявляем шрифт 12pt для использования в подписях, тут же надо интерлиньяж объявлять, если не наследуется +%\captionsetup[subfigure]{font={font12pt}} % Шрифт подписи названий подрисунков (всегда 12pt) + +%%% Настройки гиперссылок %%% + +\definecolor{linkcolor}{rgb}{0.0,0,0} +\definecolor{citecolor}{rgb}{0,0.0,0} +\definecolor{urlcolor}{rgb}{0,0,0} + +\hypersetup{ + linktocpage=true, % ссылки с номера страницы в оглавлении, списке таблиц и списке рисунков + % linktoc=all, % both the section and page part are links + % pdfpagelabels=false, % set PDF page labels (true|false) + plainpages=true, % Forces page anchors to be named by the Arabic form of the page number, rather than the formatted form + colorlinks, % ссылки отображаются раскрашенным текстом, а не раскрашенным прямоугольником, вокруг текста + linkcolor={linkcolor}, % цвет ссылок типа ref, eqref и подобных + citecolor={citecolor}, % цвет ссылок-цитат + urlcolor={urlcolor}, % цвет гиперссылок + pdflang={ru}, +} +\urlstyle{same} +%%% Шаблон %%% +%\DeclareRobustCommand{\todo}{\textcolor{red}} % решаем проблему превращения названия цвета в результате \MakeUppercase, http://tex.stackexchange.com/a/187930/79756 , \DeclareRobustCommand protects \todo from expanding inside \MakeUppercase +\setlength{\parindent}{2.5em} % Абзацный отступ. Должен быть одинаковым по всему тексту и равен пяти знакам (ГОСТ Р 7.0.11-2011, 5.3.7). + +%%% Списки %%% +% Используем дефис для ненумерованных списков (ГОСТ 2.105-95, 4.1.7) +%\renewcommand{\labelitemi}{\normalfont\bfseries~{---}} +\renewcommand{\labelitemi}{\bfseries~{---}} +\setlist{nosep,% % Единый стиль для всех списков (пакет enumitem), без дополнительных интервалов. + labelindent=\parindent,leftmargin=*% % Каждый пункт, подпункт и перечисление записывают с абзацного отступа (ГОСТ 2.105-95, 4.1.8) +} +%%%%%%%%%%%%%%%%%%%%%% +%\usepackage{xltxtra} % load xunicode + +\usepackage{ragged2e} +\usepackage[explicit]{titlesec} +\usepackage{placeins} +\usepackage{xparse} +\usepackage{csquotes} + +\usepackage{listingsutf8} +\usepackage{url} %пакеты расширений +\usepackage{algorithm, algorithmicx} +\usepackage[noend]{algpseudocode} +\usepackage{blkarray} +\usepackage{chngcntr} +\usepackage{tabularx} +\usepackage[backend=biber, + bibstyle=gost-numeric, + citestyle=nature]{biblatex} +\newcommand*\template[1]{\text{<}#1\text{>}} + +\titleformat{name=\section,numberless}[block]{\normalfont\Large\centering}{}{0em}{#1} +\titleformat{\section}[block]{\normalfont\Large\bfseries\raggedright}{}{0em}{\thesection\hspace{0.25em}#1} +\titleformat{\subsection}[block]{\normalfont\Large\bfseries\raggedright}{}{0em}{\thesubsection\hspace{0.25em}#1} +\titleformat{\subsubsection}[block]{\normalfont\large\bfseries\raggedright}{}{0em}{\thesubsubsection\hspace{0.25em}#1} + +\newcounter{subsubsubsection}[subsubsection] +\renewcommand{\thesubsubsubsection}{\thesubsubsection.\arabic{subsubsubsection}} +\setcounter{secnumdepth}{4} +\setcounter{tocdepth}{4} +\titleclass{\subsubsubsection}{straight}[\subsubsection] +\titleformat{\subsubsubsection}[block]{\normalfont\normalsize\bfseries\raggedright}{}{0em}{\thesubsubsubsection\hspace{0.25em}#1} +\titlespacing*{\subsubsubsection}{0pt}{3.25ex plus 1ex minus .2ex}{1.5ex plus .2ex} +\makeatletter +\newcommand*\l@subsubsubsection{\@dottedtocline{4}{0pt}{5em}} +\makeatother + +\let\Algorithm\algorithm +\renewcommand\algorithm[1][]{\Algorithm[#1]\setstretch{1.5}} +%\renewcommand{\listingscaption}{Листинг} + +\usepackage{pifont} +\usepackage{calc} +\usepackage{suffix} +\usepackage{csquotes} +\DeclareQuoteStyle{russian} +{\guillemotleft}{\guillemotright}[0.025em] +{\quotedblbase}{\textquotedblleft} +\ExecuteQuoteOptions{style=russian} +\newcommand{\enq}[1]{\enquote{#1}} +\newcommand{\eng}[1]{\begin{english}#1\end{english}} +% Подчиненные счетчики в окружениях http://old.kpfu.ru/journals/izv_vuz/arch/sample1251.tex +\newcounter{cTheorem} +\newcounter{cDefinition} +\newcounter{cConsequent} +\newcounter{cExample} +\newcounter{cLemma} +\newcounter{cConjecture} +\newtheorem{Theorem}{Теорема}[cTheorem] +\newtheorem{Definition}{Определение}[cDefinition] +\newtheorem{Consequent}{Следствие}[cConsequent] +\newtheorem{Example}{Пример}[cExample] +\newtheorem{Lemma}{Лемма}[cLemma] +\newtheorem{Conjecture}{Гипотеза}[cConjecture] + +\renewcommand{\theTheorem}{\arabic{Theorem}} +\renewcommand{\theDefinition}{\arabic{Definition}} +\renewcommand{\theConsequent}{\arabic{Consequent}} +\renewcommand{\theExample}{\arabic{Example}} +\renewcommand{\theLemma}{\arabic{Lemma}} +\renewcommand{\theConjecture}{\arabic{Conjecture}} +%\makeatletter +\NewDocumentCommand{\Newline}{}{\text{\\}} +\newcommand{\sequence}[2]{\ensuremath \left(#1,\ \dots,\ #2\right)} + +\definecolor{mygreen}{rgb}{0,0.6,0} +\definecolor{mygray}{rgb}{0.5,0.5,0.5} +\definecolor{mymauve}{rgb}{0.58,0,0.82} +\renewcommand{\listalgorithmname}{Список алгоритмов} +\floatname{algorithm}{Листинг} +\renewcommand{\lstlistingname}{Листинг} +\renewcommand{\thealgorithm}{\arabic{algorithm}} + +\newcommand{\refAlgo}[1]{(листинг \ref{#1})} +\newcommand{\refImage}[1]{(рисунок \ref{#1})} + +\renewcommand{\theenumi}{\arabic{enumi}.}% Меняем везде перечисления на цифра.цифра +\renewcommand{\labelenumi}{\arabic{enumi}.}% Меняем везде перечисления на цифра.цифра +\renewcommand{\theenumii}{\arabic{enumii}}% Меняем везде перечисления на цифра.цифра +\renewcommand{\labelenumii}{(\arabic{enumii})}% Меняем везде перечисления на цифра.цифра +\renewcommand{\theenumiii}{\roman{enumiii}}% Меняем везде перечисления на цифра.цифра +\renewcommand{\labelenumiii}{(\roman{enumiii})}% Меняем везде перечисления на цифра.цифра +\renewcommand{\labelitemi}{---} +\renewcommand{\labelitemii}{---} + +\makeatletter +\def\p@subsection{} +\def\p@subsubsection{\thesection\,\thesubsection\,} +\makeatother +\newcommand{\anonsection}[1]{\cleardoublepage + \phantomsection + \addcontentsline{toc}{section}{\protect\numberline{}#1} + \section*{#1}\vspace*{2.5ex} % По госту положены 3 пустые строки после заголовка ненумеруемого раздела +} +\newcommand{\sectionbreak}{\clearpage} +\renewcommand{\sectionfont}{\normalsize} % Сбиваем стиль оглавления в стандартный +\renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}} % Точки в оглавлении напротив разделов + +\renewcommand{\cftsecfont}{\normalfont\large} % Переключение на times в содержании +\renewcommand{\cftsubsecfont}{\normalfont\large} % Переключение на times в содержании + +\setlength{\cftsecindent}{0pt}% Убираем отступы в содержании для \section +\setlength{\cftsubsecindent}{0pt}% Убираем отступы в содержании для \subsection +\setlength{\cftsubsubsecindent}{0pt}% Убираем отступы в содержании для \subsubsection + +\usepackage{caption} +%\captionsetup[table]{justification=raggedleft} +%\captionsetup[figure]{justification=centering,labelsep=endash} +\usepackage{amsmath} % \bar (матрицы и проч. ...) +\usepackage{amsfonts} % \mathbb (символ для множества действительных чисел и проч. ...) +\usepackage{mathtools} % \abs, \norm +\DeclarePairedDelimiter\abs{\lvert}{\rvert} % операция модуля +\DeclarePairedDelimiter\norm{\lVert}{\rVert} % операция нормы +\DeclareTextCommandDefault{\textvisiblespace}{% + \mbox{\kern.06em\vrule \@height.3ex}% + \vbox{\hrule \@width.3em}% + \hbox{\vrule \@height.3ex}} +\newsavebox{\spacebox} +\begin{lrbox}{\spacebox} + \verb*! ! +\end{lrbox} +\newcommand{\aspace}{\usebox{\spacebox}} +\DeclareTotalCounter{listing} +\def\figurename{Рисунок} + +\makeatletter +\renewcommand*{\p@subsubsection}{} +\makeatother + +\newcommand{\bmstuheader} +{\vspace*{-30pt} + \hspace{-45pt} + \begin{minipage}{0.17\textwidth} + \hspace*{-20pt}\centering + \includegraphics[width=1.3\textwidth]{emblem.png} + \end{minipage} + \begin{minipage}{0.82\textwidth}\small \textbf{ + \vspace*{-0.7ex} + \hspace*{-10pt}\centerline{Министерство науки и высшего образования Российской Федерации} + \vspace*{-0.7ex} + \centerline{Федеральное государственное автономное образовательное учреждение } + \vspace*{-0.7ex} + \centerline{высшего образования} + \vspace*{-0.7ex} + \centerline{<<Московский государственный технический университет} + \vspace*{-0.7ex} + \centerline{имени Н.Э. Баумана} + \vspace*{-0.7ex} + \centerline{(национальный исследовательский университет)>>} + \vspace*{-0.7ex} + \centerline{(МГТУ им. Н.Э. Баумана)}} + \end{minipage} + + \vspace{-2pt} + \hspace{-34.5pt}\rule{\textwidth}{0.5pt} + + \vspace*{-18.3pt} + \hspace{-34.5pt}\rule{\textwidth}{2.5pt} + + \vspace{0.5ex} + \noindent \small ФАКУЛЬТЕТ\hspace{80pt} <<Информатика и системы управления>> + + \vspace*{-16pt} + \hspace{35pt}\rule{0.855\textwidth}{0.4pt} + + \vspace{0.5ex} + \noindent \small КАФЕДРА\hspace{50pt} <<Теоретическая информатика и компьютерные технологии>> + + \vspace*{-16pt} + \hspace{25pt}\rule{0.875\textwidth}{0.4pt} +} diff --git a/report/naive-vs-optimizer.png b/report/naive-vs-optimizer.png new file mode 100644 index 0000000..8a32075 Binary files /dev/null and b/report/naive-vs-optimizer.png differ diff --git a/report/optimizer-speedup.png b/report/optimizer-speedup.png new file mode 100644 index 0000000..fc5d128 Binary files /dev/null and b/report/optimizer-speedup.png differ diff --git a/report/practice.tex b/report/practice.tex new file mode 100644 index 0000000..76c316a --- /dev/null +++ b/report/practice.tex @@ -0,0 +1,1336 @@ +% !TeX TXS-program:bibliography = txs:///biber +% chktex-file 1 +\documentclass[fontsize=14pt, russian]{scrartcl} +\input{header.tex} +\addbibresource{biblio.bib} + +\begin{document} +\sloppy + +\def\figurename{Рисунок} + +\newcommand{\sqlkw}[1]{\texttt{#1}} +\newcommand{\SELECT}{\sqlkw{SELECT}} +\newcommand{\FROM}{\sqlkw{FROM}} +\newcommand{\WHERE}{\sqlkw{WHERE}} +\newcommand{\JOIN}{\sqlkw{JOIN}} +\newcommand{\GROUPBY}{\sqlkw{GROUP BY}} +\newcommand{\ORDERBY}{\sqlkw{ORDER BY}} + +\newcommand{\SeqScan}{\sqlkw{SeqScan}} +\newcommand{\HashJoin}{\sqlkw{HashJoin}} +\newcommand{\NestedLoopJoin}{\sqlkw{NestedLoopJoin}} +\newcommand{\Sort}{\sqlkw{Sort}} +\newcommand{\FilterOp}{\sqlkw{Filter}} +\newcommand{\ProjectionOp}{\sqlkw{Projection}} + +\newcommand{\screenplaceholder}[3]{% + \begin{figure}[H] + \centering + \fbox{% + \begin{minipage}[c][2.5cm][c]{0.91\textwidth} + \centering + \textbf{МЕСТО ДЛЯ СКРИНШОТА}\\[0.4cm] + #3 + \end{minipage}% + } + \caption{#1} + \label{#2} + \end{figure} +} + +\newcommand{\graphplaceholder}[3]{% + \begin{figure}[H] + \centering + \fbox{% + \begin{minipage}[c][4.5cm][c]{0.91\textwidth} + \centering + \textbf{МЕСТО ДЛЯ ГРАФИКА}\\[0.4cm] + #3 + \end{minipage}% + } + \caption{#1} + \label{#2} + \end{figure} +} + +\setcounter{page}{2} + +\newpage +\renewcommand\contentsname{\hfill{\normalfont{СОДЕРЖАНИЕ}}\hfill} +\tableofcontents +\newpage + +\anonsection{ВВЕДЕНИЕ} + +В эпоху стремительного роста объемов данных системы управления базами данных +(СУБД) являются важной частью информационных систем. По данным аналитических +исследований, объем мирового рынка СУБД ежегодно увеличивается, что обусловлено +как расширением круга решаемых задач, так и усложнением структуры обрабатываемых +данных и увеличением их объема. Реляционные базы данных, работающие с различными +диалектами языка SQL, продолжают занимать доминирующее положение в сегменте +обработки транзакций и аналитических запросов. + +Одним из определяющих факторов конкурентоспособности между различными +реализациями СУБД является качество оптимизатора запросов. Оптимизатор +представляет собой компонент, ответственный за преобразование декларативного +SQL-запроса в эффективный физический план исполнения. Именно от качества работы +оптимизатора в значительной степени зависит производительность всей системы: +выбор неоптимального плана исполнения может привести к увеличению времени +выполнения запроса в десятки раз относительно оптимального случая. + +Также оптимизатор запросов считается наиболее сложной составной частью любой +СУБД. Задача нахождения оптимального плана является NP-полной в общем случае, +что обуславливает необходимость применения эвристических методов и алгоритмов +ограниченного перебора. Одним из первых практических подходов к стоимостной +оптимизации стал алгоритм System~R~\cite{Selinger1979}. В дальнейшем были +разработаны расширяемые архитектуры Volcano~\cite{GraefeMcKenna1993} и +Cascades~\cite{Graefe1995}. + +Преимущества архитектуры Cascades заключаются в расширяемости, модульности и +возможности управлять поиском с помощью набора правил. + +Целью данной работы является разработка и реализация оптимизатора подмножества +SQL-запросов на основе архитектуры Cascades, а также создание каркаса для его +итеративного улучшения путем сравнения с промышленными реализациями. + +Для достижения поставленной цели необходимо решить следующие задачи: +\begin{itemize} + \item изучить существующие архитектуры оптимизаторов запросов; + \item реализовать разбор подмножества SQL и внутреннее представление + операторов реляционной алгебры; + \item реализовать структуру данных Memo и алгоритм поиска оптимального плана + на основе архитектуры Cascades; + \item реализовать набор правил трансформации и реализации для основных + операторов реляционной алгебры; + \item реализовать метод ветвей и границ для эффективного отсечения + неоптимальных планов; + \item реализовать исполнение физических планов над таблицами в формате CSV; + \item реализовать метод дифференциального анализа физических планов для + автоматизированного поиска примеров неоптимальных планов. +\end{itemize} + +\section{Проектирование} + +В рамках данной работы был разработан оптимизатор запросов для модельной +реляционной СУБД. + +Структурно реализация модельной СУБД состоит из следующих модулей: +\begin{itemize} + \item модуль синтаксического анализа SQL-запросов; + \item модуль построения логического представления запроса; + \item модуль стоимостной оптимизации на основе структуры Memo; + \item подсистема физического хранения на основе статических CSV-таблиц; + \item модуль исполнения физических операторов. +\end{itemize} + +Основой для проектирования оптимизатора была выбрана архитектура Cascades. В ней +пространство допустимых планов формируется с помощью набора правил +преобразования и хранится в структуре Memo. Эта структура объединяет логически +эквивалентные выражения в группы, что позволяет избежать повторного рассмотрения +одинаковых поддеревьев. Поиск оптимального физического плана выполняется с учетом +стоимостной модели и требуемых свойств результата, например порядка сортировки. + +Данная архитектура была выбрана по причине ее модульности и расширяемости. +Добавление новых логических преобразований и физических операторов выполняется +путем реализации отдельных правил и не требует изменения основного алгоритма +поиска. Это также удобно для итеративного процесса разработки, предполагающего +постепенное улучшение качества итоговых планов путем уточнения и расширения +пространства поиска. + +Модуль синтаксического анализа SQL-запросов отвечает за лексический и +синтаксический разбор поступающих на вход SQL-запросов. Для реализации этого +модуля было решено использовать генератор синтаксических и лексических +анализаторов, ввиду того, что грамматика SQL является очень объемной, и +использование готового формального описания для генератора гарантирует +совместимость с одним из диалектов языка. + +Модуль построения логического представления запроса отвечает за преобразование +результата синтаксического анализа во внутреннее представление, пригодное для +оптимизации. В качестве такого представления решено использовать дерево +операторов реляционной алгебры. Этот подход является общепринятым и позволяет +отделить особенности синтаксиса SQL от последующих этапов обработки запроса, а +также выполнять оптимизацию над ограниченным набором формализованных операций: +фильтрацией, проекцией, соединением, агрегацией и сортировкой. + +Подсистема физического хранения отвечает за предоставление модулю исполнения +табличных данных. Для хранения выбраны статические таблицы в формате CSV. +Использование простого текстового формата позволяет сосредоточиться на +проектировании оптимизатора и не усложнять прототип реализацией транзакций, +индексов, буферного кэша и управления дисковыми страницами. Кроме того, +CSV-файлы удобно создавать, изменять и использовать при проведении тестов. + +Модуль исполнения физических операторов отвечает за выполнение выбранного +оптимизатором плана и формирование результата запроса. Физический план решено +представлять в виде дерева операторов с единым интерфейсом взаимодействия. +Каждый оператор получает данные от дочерних узлов, выполняет определенное +преобразование и передает результат вышестоящему оператору. Такой подход +обеспечивает модульность подсистемы исполнения и позволяет добавлять новые +физические операторы независимо от уже реализованных. + +% TODO: Описание выбранных правил реализации / трансформации +% TODO: Описание подмножества SQL. + +\section{Реализация} + +Программа написана на языке C++ с использованием стандарта C++23. Этот язык +популярен в сфере разработки СУБД, являясь достаточно низкоуровневым и +производительным, но в то же время удобным для реализации больших систем. Для +сборки применяется CMake и его встроенные средства для зависимостей в виде +библиотек на C++. Окружение для разработки описывается декларативно и +воспроизводимо при помощи функционального доменно-специфичного языка \verb|nix| +для соответствующего пакетного менеджера. + +Для синтаксического анализа выбран генератор парсеров ANTLR4, ввиду простоты и +удобства в использовании. Асинхронное взаимодействие физических операторов +построено на Boost.Asio, по причине совместимости со встроенными в язык +корутинами и понятной модели асинхронности. Для тестирования корректности и +производительности используется Google Tests, Google Benchmarks и pytest. + +\subsection{Модуль синтаксического анализа} + +Для реализации парсера был выбран ANTLR4, ввиду его удобства и возможности +генерировать лексические и синтаксические анализаторы под практически любой язык +программирования. + +Грамматика PostgreSQL была взята из официального репозитория проекта +ANTLR4~\cite{Antlr4PostgreSQL}. Она была портирована автоматически из грамматики +для Bison~\cite{PostgresBisonGrammar} из официального репозитория Postgres, +поэтому является наиболее полной из существующих. + +ANTLR4 генерирует несколько файлов на C++, в том числе лексический и +синтаксический анализаторы, а также класс \verb|Visitor| для обхода дерева +разбора. Для интересующих конструкций SQL переопределены методы посещения узлов +грамматики. Например, предложение \WHERE{} преобразуется в логический оператор +фильтрации, список выражений после \SELECT{} преобразуется в проекцию, а список +таблиц после \FROM{} --- в дерево соединений. + +Результатом работы парсера является структура \texttt{ParsedQuery}. Она содержит +логический оператор верхнего уровня и необязательное требование к порядку строк +для \ORDERBY{}. Логические операторы и скалярные выражения представлены +алгебраическими типами на основе \texttt{std::variant}. В них входят операторы +таблицы, фильтрации, проекции, агрегации, соединения, ссылки на атрибуты, +константы и арифметические выражения~\refAlgo{lst:logical}. + +\begin{listing}[H] + \caption{Объявление типов для операторов реляционной алегебры и выражений.} + \label{lst:logical} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{C++} +using Operator = std::variant; + +using Expression = std::variant; + +struct Projection { + std::vector expressions; + std::shared_ptr source; + std::vector> aliases; + + bool operator==(const Projection& other) const; +}; + \end{minted} +\end{listing} + +Выражения хранятся в алгебраическом типе \verb|Expression|, реализованном +аналогично \verb|Operator|. + +Функция \verb|GetAST| внутри строит абстрактное синтаксическое дерево и обходит +его с помощью \verb|Visitor|, который и генерирует представление запроса в виде +дерева из \verb|Operator|. + +Дополнительно реализована визуализация дерева из операторов реляционной алгебры +в формате Graphviz с помощью функции \verb|GetDotRepresentation|. + +\subsection{Модуль оптимизации запросов} + +Модуль оптимизации запросов отвечает за преобразование логического дерева +операторов в физический план исполнения с минимальной оцененной стоимостью. +Пространство исследуемых эквивалентных планов эффективно хранится в структуре +Memo и расширяется с помощью добавления новых правил. Поиск выполняется сверху +вниз с использованием стоимостной модели, физических свойств и метода ветвей и +границ. + +\subsubsection{Структура Memo} + +Структура Memo хранит группы логически эквивалентных выражений. Выражения из +одной группы возвращают одинаковый результат. Например, $A \Join B$ и +$B \Join A$ являются эквивалентными. + +Дочерние узлы выражения ссылаются не на конкретные операторы, а на группы. +Благодаря этому одно выражение компактно представляет множество деревьев. +Объявления классов \verb|Memo| и \verb|Group| приведены в +листинге~\ref{lst:memo}. + +\begin{listing}[H] + \caption{Основные элементы структуры Memo.} + \label{lst:memo} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +class Memo { +public: + utils::NotNull AddGroup(LogicalOperator op); + utils::NotNull AddLogicalExprToGroup( + utils::NotNull group, LogicalOperator op); + utils::NotNull Populate(const Operator& op); + +private: + std::deque groups_; + std::unordered_map expr_index_; +}; + +class Group { +public: + utils::NotNull AddLogicalExpr(LogicalOperator op); + utils::NotNull AddPhysicalExpr( + PhysicalOperator op, bool is_enforcer = false); + + LogicalExprs GetLogicalExprs(); + PhysicalExprs GetPhysicalExprs(); +}; + \end{minted} +\end{listing} + +Метод \verb|Memo::Populate| рекурсивно обходит исходное логическое дерево и +создает первоначальные группы. Для предотвращения повторного добавления +выражений используется отображение \verb|expr_index_|. Ключ строится из типа +оператора, его параметров и идентификаторов дочерних групп. Таким образом, +одинаковые выражения добавляются в Memo только один раз. + +Каждая группа содержит логические и физические выражения. Логические выражения +описывают семантику запроса, а физические выражения определяют конкретный способ +исполнения. Например, логическому соединению могут соответствовать физические +операторы соединения вложенными циклами и хеш-соединения. + +\subsubsection{Правила оптимизации} + +Расширение пространства поиска выполняется с помощью правил. Реализовано два +типа правил: трансформационные и реализационные. Их интерфейсы приведены в +листинге~\ref{lst:optimizer-rules}. + +\begin{listing}[H] + \caption{Интерфейсы правил оптимизации.} + \label{lst:optimizer-rules} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +class TransformationRule { +public: + virtual bool IsApplicable(utils::NotNull expr) = 0; + utils::NotNull Apply( + utils::NotNull expr, Memo& memo); + +private: + virtual LogicalOperator ApplyImpl( + utils::NotNull expr, Memo& memo) = 0; +}; + +class ImplementationRule { +public: + virtual bool IsApplicable(utils::NotNull expr) = 0; + virtual utils::NotNull Apply( + utils::NotNull expr, Memo& memo) = 0; +}; + \end{minted} +\end{listing} + +Правило трансформации создает новое логическое выражение и добавляет его в +ту же группу Memo, что и исходное выражение. Следовательно, правило должно +сохранять семантику запроса. Правило реализации создает физическое выражение, +которое может быть передано модулю исполнения. + +Реализованы следующие трансформационные правила: +\begin{itemize} + \item коммутативность соединения; + \item ассоциативность соединения; + \item разбиение конъюнкции на последовательность фильтров; + \item объединение последовательных фильтров; + \item проталкивание фильтра через проекцию; + \item проталкивание фильтра во входы соединения; + \item преобразование выражения \verb|IN| в цепочку сравнений. +\end{itemize} + +Например, правило коммутативности преобразует выражение $A \Join B$ в +$B \Join A$. Для левого и правого внешнего соединения одновременно изменяется +тип соединения. Реализация правила приведена в +листинге~\ref{lst:join-commutativity}. + +\begin{listing}[H] + \caption{Правило коммутативности соединения.} + \label{lst:join-commutativity} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize, + linenos=false, xleftmargin=1.5em]{cpp} +LogicalOperator JoinCommutativity::ApplyImpl( + utils::NotNull expr, Memo&) { + auto join = std::get(expr->root_operator); + std::swap(join.lhs, join.rhs); + + if (join.type == JoinType::kLeft) { + join.type = JoinType::kRight; + } else if (join.type == JoinType::kRight) { + join.type = JoinType::kLeft; + } + return join; +} + \end{minted} +\end{listing} + +Правило проталкивания фильтра через соединение разбивает предикат на конъюнкты. +Каждый конъюнкт анализируется отдельно. Если он ссылается только на атрибуты +одного входа соединения, фильтр переносится ближе к соответствующей таблице. Это +позволяет уменьшить число кортежей до выполнения соединения. Для внешних +соединений учитывается направление: фильтр нельзя переносить в такой вход, для +которого это изменит семантику результата. + +Реализационные правила создают: +\begin{itemize} + \item последовательное сканирование таблицы; + \item фильтрацию; + \item проекцию; + \item агрегацию; + \item декартово произведение вложенными циклами; + \item соединение вложенными циклами; + \item хеш-соединение. +\end{itemize} + +Хеш-соединение применяется только для внутреннего эквисоединения двух атрибутов. +Для остальных условий доступно соединение вложенными циклами. Проверка +применимости правила приведена в листинге~\ref{lst:implement-hash-join}. + +\begin{listing}[H] + \caption{Проверка применимости хеш-соединения.} + \label{lst:implement-hash-join} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +bool ImplementHashJoin::IsApplicable( + utils::NotNull expr) { + if (!std::holds_alternative( + expr->root_operator)) { + return false; + } + + const auto& join = + std::get(expr->root_operator); + + if (join.type != JoinType::kInner) return false; + + const auto* bin = + std::get_if(&join.qual); + + return bin && bin->binop == BinaryOp::kEq + && std::holds_alternative(*bin->lhs) + && std::holds_alternative(*bin->rhs); +} + \end{minted} +\end{listing} + +Для каждого выражения хранится информация о ранее примененных правилах. Это +предотвращает повторное выполнение одного правила над одним выражением и +ограничивает образование циклов при заполнении Memo. + +\subsubsection{Оценка кардинальности} + +Стоимость физического плана зависит от количества обрабатываемых кортежей. +Для получения этой величины используется класс +\verb|CardinalityEstimates| из файла +\verb|src/stewkk/sql/logic/optimizer/cardinality.cpp|. + +Оценка кардинальности вычисляется для группы Memo и кэшируется. В текущей +версии используются следующие эвристики: +\begin{itemize} + \item для таблицы используется известное количество строк или значение по + умолчанию; + \item фильтрация и проекция сохраняют оценку кардинальности входа; + \item скалярная агрегация возвращает одну строку; + \item декартово произведение имеет кардинальность $N_l N_r$; + \item для соединения используется произведение кардинальностей входов и + коэффициента селективности. +\end{itemize} + +Для эквисоединения двух атрибутов коэффициент селективности оценивается как +\[ + S = \frac{1}{\max(1, N_l, N_r)}. +\] +Для конъюнкции условий коэффициенты селективности перемножаются. + +\subsubsection{Стоимостная модель} + +Стоимость плана вычисляется как сумма локальных стоимостей физических +операторов. Коэффициенты были подобраны с помощью бенчмарков отдельных +операторов, описанных в разделе тестирования. + +Для входных кардинальностей $N$, $N_l$ и $N_r$ используются следующие формулы: +\[ +\begin{aligned} + C_{\text{scan}} &= 100N, \\ + C_{\text{filter}} &= 100N, \\ + C_{\text{projection}} &= 22N, \\ + C_{\text{aggregation}} &= 510N, \\ + C_{\text{nested-loop join}} &= 70N_lN_r, \\ + C_{\text{cross join}} &= 104N_lN_r, \\ + C_{\text{hash join}} &= 69(N_l + N_r), \\ + C_{\text{sort}} &\approx 11N\log_2N. +\end{aligned} +\] + +Фрагмент реализации стоимостной модели приведен в +листинге~\ref{lst:optimizer-cost}. + +\begin{listing}[H] + \caption{Вычисление локальной стоимости физических операторов.} + \label{lst:optimizer-cost} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +int64_t CalcCost(PhysicalExpr* expr, + CardinalityEstimates& cardinality) { + return std::visit(utils::Overloaded{ + [&](const physical::SeqScan&) { + return 100 * cardinality.GetCardinality(expr->group); + }, + [&](const physical::NestedLoopJoin& j) { + return 70 * cardinality.GetCardinality(j.lhs) + * cardinality.GetCardinality(j.rhs); + }, + [&](const physical::HashJoin& j) { + return 69 * (cardinality.GetCardinality(j.lhs) + + cardinality.GetCardinality(j.rhs)); + }, + [&](const physical::Sort& s) { + auto n = cardinality.GetCardinality(s.input); + return 11 * n * std::bit_width( + static_cast(n)); + }, + ... + }, expr->root_operator); +} + \end{minted} +\end{listing} + +\subsubsection{Алгоритм поиска} + +Класс \verb|Optimizer| реализует алгоритм поиска. При создании объекта исходное +логическое дерево помещается в Memo. Поиск запускается для корневой группы и +требуемых физических свойств результата. + +Алгоритм использует стек отложенных задач. Это позволяет разделить процесс на +небольшие операции: исследование группы, применение правила, оптимизацию +физического выражения и обработку его дочерних узлов. + +Основной метод оптимизации группы приведен в +листинге~\ref{lst:optimize-group}. + +\begin{listing}[H] + \caption{Оптимизация группы Memo.} + \label{lst:optimize-group} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +void Optimizer::OptimizeGroup(Group* group, + PropertySet required, + Limit limit) { + WinnerKey key{group, required}; + if (winner_.contains(key)) return; + + if (IsExplored(group) + && limit + && LowerBoundCost(group) >= *limit) { + return; + } + + if (!IsExplored(group)) { + tasks_.emplace([=] { + OptimizeGroup(group, required, limit); + }); + tasks_.emplace([=] { + ExploreGroup(group, limit); + }); + return; + } + + for (auto expr : group->GetPhysicalExprs()) { + if (expr->is_enforcer) continue; + auto cost = local_cost_[expr.get()]; + if (!limit || cost < *limit) { + tasks_.emplace([=] { + OptimizeInputs(expr, required, {}, cost, limit); + }); + } + } + + AddEnforcers(group, required, limit); +} + \end{minted} +\end{listing} + +Если группа еще не исследована, в стек добавляются две задачи. Первая расширяет +группу с помощью правил, вторая повторно запускает ее оптимизацию после +расширения. Если группа уже исследована, для каждого физического выражения +вычисляется полная стоимость с учетом дочерних групп. + +Для каждой пары из группы и требуемого набора свойств хранится лучший найденный +вариант. Ключ такого варианта представлен структурой \verb|WinnerKey|: + +\begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +struct WinnerKey { + Group* group; + PropertySet required; +}; +\end{minted} + +Хранение победителя отдельно для каждого набора свойств необходимо, поскольку +самый дешевый неотсортированный план может отличаться от самого дешевого плана, +удовлетворяющего требованию \verb|ORDER BY|. + +Дочерние группы физического выражения оптимизируются последовательно. После +получения победителя дочерней группы его стоимость прибавляется к накопленной +стоимости. Если все дочерние группы обработаны, проверяются выходные свойства +плана. Затем найденный вариант при необходимости становится новым победителем. + +\subsubsection{Метод ветвей и границ} + +Полный перебор пространства планов быстро становится непрактичным при +увеличении количества соединяемых таблиц. Для сокращения перебора используется +метод ветвей и границ. + +Для каждой группы вычисляется нижняя граница стоимости. Она равна минимальной +сумме нижней оценки локального оператора и нижних оценок дочерних групп. +Например, для логического соединения выбирается минимум между стоимостью +хеш-соединения и стоимостью соединения вложенными циклами: +\[ + LB_{\text{join}} = + \min(69(N_l + N_r), 70N_lN_r). +\] + +Если нижняя граница не меньше стоимости уже известного решения, исследование +ветви прекращается. Фрагмент реализации приведен в +листинге~\ref{lst:lower-bound}. + +\begin{listing}[H] + \caption{Вычисление нижней границы стоимости группы.} + \label{lst:lower-bound} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +int64_t Optimizer::LowerBoundCost(Group* group) { + if (auto it = lower_bounds_.find(group); + it != lower_bounds_.end()) { + return it->second; + } + + int64_t best = std::numeric_limits::max(); + + for (auto expr : group->GetLogicalExprs()) { + int64_t cost = LowerBoundLocalCost(expr, cardinality_); + + for (auto child : GetChildren(expr)) { + cost += LowerBoundCost(child); + } + best = std::min(best, cost); + } + + lower_bounds_[group] = best; + return best; +} + \end{minted} +\end{listing} + +Метод \verb|OptimizeInputs| также уменьшает границу для очередного дочернего +узла. Из общей допустимой стоимости вычитаются уже накопленная стоимость и +нижние оценки еще не обработанных дочерних групп. Благодаря этому заведомо +неоптимальные ветви отбрасываются до построения полного плана. + +Для исследовательских задач предусмотрен метод \verb|OptimizeExhaustive|. Он +запускает тот же алгоритм без ограничения стоимости и используется при +проверке достижимости внешних физических планов. + +\subsubsection{Физические свойства} + +Некоторые требования к результату нельзя выразить только стоимостью. Например, +конструкция \verb|ORDER BY| требует от результата определенного порядка +кортежей. Для представления таких требований используется класс +\verb|PropertySet|. + +В текущей версии реализовано свойство сортировки \verb|SortProperty|. Порядок +считается удовлетворяющим требованию, если требуемые ключи являются префиксом +фактически полученного порядка. Например, сортировка по $(a, b)$ удовлетворяет +требованию сортировки по $a$. + +Некоторые операторы сохраняют порядок дочернего узла. Фильтрация и проекция +сохраняют свойства входа. Соединение вложенными циклами сохраняет свойства +левого входа. Хеш-соединение и агрегация не гарантируют сохранение порядка. + +Если подходящий план не найден, оптимизатор пытается добавить обеспечивающий +оператор. Для сортировки используется класс \verb|SortEnforcer|. Он проверяет +наличие требуемых атрибутов в схеме группы и создает физический оператор +сортировки. + +\begin{listing}[H] + \caption{Добавление оператора сортировки для обеспечения свойства.} + \label{lst:sort-enforcer} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +std::optional SortEnforcer::TryBuild( + utils::NotNull group, + const PropertySet& required, + SchemaCatalog& schema) const { + const auto* sort = required.Get(); + if (!sort) return std::nullopt; + + if (!ContainsRequiredKeys(schema.GetSchema(group), + sort->order)) { + return std::nullopt; + } + + return physical::Sort{group, sort->order}; +} + \end{minted} +\end{listing} + +\subsubsection{Формирование физического плана} + +После завершения поиска метод \verb|BuildOptimalPlan| рекурсивно восстанавливает +итоговое дерево физических операторов. Для каждой группы выбирается сохраненный +победитель с учетом требуемых свойств. Затем аналогичным образом +восстанавливаются планы дочерних групп. + +Результатом работы оптимизатора является объект \verb|PhysicalPlanNode|, который +не зависит от структуры Memo. Он может быть сериализован для анализа или передан +модулю исполнения запросов. + +Таким образом, модуль оптимизации разделяет представление пространства планов, +правила его расширения, стоимостную модель и алгоритм поиска. Такая организация +позволяет добавлять новые правила и физические операторы без изменения основной +логики оптимизатора. + +\subsection{Модуль физического хранения данных} + +Данные хранятся в одной директории, где каждому отношению соответствует +файл с CSV-таблицей. Название файла совпадает с названием отношения. +Первая строка CSV-файла содержит описание атрибутов и их типов, +последующие строки содержат кортежи отношения. + +Для чтения таблиц реализован класс +\verb|CsvDirSequentialScanner|~\refAlgo{lst:csv-dir-scanners}. Он +выполняет последовательное сканирование CSV-файла и передает прочитанные +кортежи модулю исполнения физических операторов. + +\begin{listing}[H] + \caption{Объявления классов сканирования CSV-таблиц.} + \label{lst:csv-dir-scanners} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{C++} +struct CsvDirSequentialScanner { + std::string dir; + + boost::asio::awaitable> operator()( + const std::string& table_name, + const std::string& output_table_name, + AttributesInfoChannel& attrs_chan, + TuplesChannel& tuples_chan) const; +}; + +struct CsvDirIndexedScanner { + std::string dir; + + boost::asio::awaitable> operator()( + const std::string& table_name, + const std::string& output_table_name, + const Expression& predicate, + AttributesInfoChannel& attrs_chan, + TuplesChannel& tuples_chan) const; +}; + \end{minted} +\end{listing} + +Дополнительно реализована поддержка индексов. Индекс представляет собой +отсортированный бинарный файл, содержащий целочисленные ключи и смещения +соответствующих строк в CSV-файле. Использование смещений позволяет +извлекать подходящие кортежи без полного сканирования отношения. + +Список доступных индексов задается в файле \verb|indexes.meta|, +расположенном в директории с данными. Каждая строка файла содержит +название отношения, название индексируемого атрибута, тип индекса и имя +бинарного файла~\refAlgo{lst:meta}. + +\begin{listing}[H] + \caption{Пример файла с метаданными о доступных индексах.} + \label{lst:meta} +\begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{text} +users id sorted users.id.sorted.idx +\end{minted} +\end{listing} + +Для чтения данных с использованием индекса реализован класс +\verb|CsvDirIndexedScanner|~\refAlgo{lst:csv-dir-scanners}. Если указанный в +метаданных бинарный файл отсутствует, индекс строится при первом обращении к +таблице. Для поиска диапазона подходящих ключей используется упорядоченность +записей индекса. + +Поддерживаются индексы только для целочисленных атрибутов. Индексное +сканирование применяется для предикатов сравнения атрибута с +целочисленной константой. Поддерживаются операции равенства, строгого и +нестрогого сравнения. После чтения найденных строк дополнительно +вычисляется исходный предикат, что обеспечивает корректную обработку +составных условий. + +\subsection{Модуль исполнения физических планов} + +За исполнение запросов отвечает класс \verb|Executor|~\refAlgo{lst:executor}. +Он получает физический план, запускает исполнение его операторов и формирует +результирующее отношение. Физический план представлен деревом объектов типа +\verb|PhysicalPlanNode|. Для каждого типа узла предусмотрена отдельная функция +исполнения. + +Поддерживается исполнение последовательного и индексного сканирования, +фильтрации, проекции, сортировки, агрегации, декартова произведения, соединения +вложенными циклами и хеш-соединения. Операторы могут обрабатывать дочерние узлы +плана независимо и передавать результаты вышестоящим операторам по мере их +готовности. + +\begin{listing}[H] + \caption{Основные элементы объявления класса Executor.} + \label{lst:executor} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{C++} +template +class Executor { +public: + using SequentialScan = std::function>( + const std::string& table_name, + const std::string& output_table_name, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan)>; + + using IndexScan = std::function>( + const std::string& table_name, + const std::string& output_table_name, + const Expression& predicate, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan)>; + + Executor(SequentialScan seq_scan, + IndexScan index_scan, + boost::asio::any_io_executor executor); + + boost::asio::awaitable> + Execute(const PhysicalPlanNode& op); + +private: + SequentialScan sequential_scan_; + IndexScan index_scan_; + ExpressionExecutor expression_executor_; +}; + \end{minted} +\end{listing} + +Класс параметризуется интерфейсами \verb|SequentialScan|, \verb|IndexScan| и +\verb|ExpressionExecutor|. Первые два интерфейса отделяют исполнение физического +плана от конкретного способа хранения данных. \verb|SequentialScan| выполняет +полное чтение таблицы, а \verb|IndexScan| получает дополнительный предикат и +извлекает строки с использованием индекса. Благодаря этому исполнитель не +зависит от формата CSV-файлов и устройства индексных структур. + +Параметр \verb|ExpressionExecutor| определяет способ вычисления скалярных +выражений. Реализованы стратегии для интерпретации выражений и для +JIT-компиляции. По умолчанию используется интерпретируемый вариант. + +Значения атрибутов представлены структурой \verb|Value|. Она содержит признак +\verb|NULL| и значение одного из поддерживаемых типов: целого числа, логического +значения или идентификатора строки. Исходное значение для строк сохраняется в +пул, а в кортеже хранится числовой идентификатор строки. Кортеж представлен +вектором значений, а отношение состоит из описания атрибутов и списка кортежей. + +Коммуникация между физическими операторами осуществляется с помощью асинхронных +каналов. Используются два типа каналов: \verb|AttributesInfoChannel| для +однократной передачи схемы отношения и \verb|TuplesChannel| для передачи данных. +Кортежи передаются векторами. Это уменьшает число операций синхронизации и +позволяет потоковым операторам начинать обработку до полного завершения дочерних +узлов плана. + +Для реализации каналов используется +\verb|boost::asio::experimental::concurrent_channel|. Данный класс обеспечивает +потокобезопасность, буферизацию, асинхронную передачу данных и совместимость с +корутинами. Такой подход позволяет единообразно организовать взаимодействие +между операторами независимо от конкретных алгоритмов для их реализации. + +\subsection{Руководство пользователя} + +Для сборки программы требуются CMake, компилятор C++ с поддержкой C++23, LLVM, +ANTLR4 и библиотеки Boost. Дополнительно требуется Python, pytest и Docker. В +репозитории присутствует файл \texttt{flake.nix}, описывающий необходимые +зависимости, поэтому в системе с пакетным менеджером Nix рекомендуется открыть +окружение при помощи команды \verb|nix develop|. + +Все команды далее выполняются из корневого каталога проекта. + +\subsubsection{Сборка программы} + +Для выполнения конфигурации и сборки следует запустить команды на листинге~\ref{lst:build}. + +\begin{listing}[H] + \caption{Конфигурация и сборка проекта.} + \label{lst:build} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{bash} +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -- -j 6 + \end{minted} +\end{listing} + +После успешной сборки исполняемый файл находится по пути +\texttt{build/bin/sql}. Для повторной сборки также можно использовать цель +\texttt{make build}. + +\subsubsection{Запуск программы} + +Пользователь должен создать отдельную директорию и поместить в нее CSV-файлы. +Имя каждого файла без расширения считается именем таблицы. В заголовке каждого +файла для столбца указывается имя и тип через двоеточие. Поддерживаются типы +\texttt{int} и \texttt{string}. Пустое SQL-значение записывается как +\texttt{NULL}. + +Для первого запуска можно использовать готовые тестовые данные в \verb|test/static/executor/test_data|. + +SQL-запрос передается программе через стандартный поток ввода. Простейший +пример запуска представлен на листинге~\ref{lst:run-example}. + +\begin{listing}[H] + \caption{Простейший пример запуска программы.} + \label{lst:run-example} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{bash} +echo 'SELECT users.id FROM users;' | \ + ./build/bin/sql --data-dir test/static/executor/test_data + \end{minted} +\end{listing} + +Программа выводит заголовок отношения, содержащий имена и типы столбцов, а +затем полученные строки. Если запрос не содержит \ORDERBY{}, строки вывода +сортируются лексикографически для воспроизводимости результата. + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{run-example.png} +\caption{Выполнение SQL-запроса над тестовыми данными}% +\label{fig:practice-run} +\end{figure} + +\subsubsection{Вывод логического и физического планов} + +Для диагностики предусмотрены параметры \texttt{--print-ast} и +\texttt{--print-plan}. Первый параметр выводит логическое дерево после +синтаксического анализа, второй --- выбранный физический план. +Пример использования представлен на листинге~\ref{lst:run-plan}. + +\begin{listing}[H] + \caption{Запуск программы с выводом логического дерева и физического плана.} + \label{lst:run-plan} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{bash} +echo 'SELECT users.id FROM users WHERE users.age > 18 \ +ORDER BY users.id;' | \ + ./build/bin/sql --data-dir test/static/executor/test_data \ + --print-ast --print-plan + \end{minted} +\end{listing} + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{run-with-prints.png} +\caption{Вывод логического дерева и физического плана}% +\label{fig:practice-plan} +\end{figure} + +Для проверки полноты набора правил оптимизации предусмотрен режим +\verb|--check-reachable|. В этом режиме программа получает SQL-запрос и +сериализованный физический план, после чего выполняет полный перебор +пространства поиска без отсечения по стоимости. Если заданный план может быть +получен с помощью реализованных правил, программа выводит сообщение +\verb|REACHABLE|. + +План передается в отдельном файле в формате S-expression. Например, содержимое +файла \verb|target.plan| может иметь вид, представленный на листинге~\ref{lst:target-plan}. + +\begin{listing}[H] + \caption{Пример содержимого файла с целевым физическим планом.} + \label{lst:target-plan} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +(PhysicalProjection (exprs (attr users id)) + (PhysicalFilter (> (attr users age) 18) + (SeqScan users))) + \end{minted} +\end{listing} + +Проверка выполняется командой на листинге~\ref{lst:check-reachable}. + +\begin{listing}[H] + \caption{Запуск программы в режиме проверки достижимости плана.} + \label{lst:check-reachable} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +echo 'SELECT users.id FROM users WHERE users.age > 18;' | \ + ./build/bin/sql \ + --data-dir test/static/executor/test_data \ + --check-reachable target.plan + \end{minted} +\end{listing} + +Если план недостижим, программа выводит сообщение \verb|NOT REACHABLE| и +описание первого обнаруженного расхождения. Параметр \verb|--data-dir| +необходим для загрузки схем таблиц. В частности, схема используется при +проверке достижимости планов с сортировкой. + +Для автоматизированной проверки достижимости планов используется Microsoft SQL +Server. Контейнер с СУБД запускается с помощью Docker Compose. Перед запуском +скриптов в директории research, следует выполнить команды на листинге~\ref{lst:docker-up}. + +\begin{listing}[H] + \caption{Запуск контейнера с Microsoft SQL Server.} + \label{lst:docker-up} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +cd research +docker compose up -d +cd .. + \end{minted} +\end{listing} + +Для запуска модульных тестов C++ следует выполнить команду на листинге~\ref{lst:ctest}. + +\begin{listing}[H] + \caption{Запуск модульных тестов C++.} + \label{lst:ctest} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +ctest --test-dir build --output-on-failure + \end{minted} +\end{listing} + +Модульные тесты проверяют синтаксический анализ запросов, вычисление выражений, +исполнение физических операторов, индексное сканирование, сериализацию планов, +структуру Memo, правила оптимизации и физические свойства. + +Для запуска Python-тестов преобразователя планов Microsoft SQL Server +необходимо предварительно запустить контейнер, после чего выполнить команды на листинге~\ref{lst:pytest-converter}. + +\begin{listing}[H] + \caption{Запуск тестов преобразователя планов Microsoft SQL Server.} + \label{lst:pytest-converter} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +cd research +pytest -q test_converter.py +cd .. + \end{minted} +\end{listing} + +Тесты инструментов Star Schema Benchmark запускаются командой на листинге~\ref{lst:pytest-ssb}. + +\begin{listing}[H] + \caption{Запуск тестов инструментов Star Schema Benchmark.} + \label{lst:pytest-ssb} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +pytest -q benchmarks/datasets/ssb + \end{minted} +\end{listing} + +Для поиска ошибок исполнения реализован дифференциальный фаззер. Он генерирует +случайные SQL-запросы, выполняет их в модельной СУБД и Microsoft SQL Server, +после чего сравнивает полученные результаты. Перед запуском необходимо поднять +контейнер с Microsoft SQL Server. + +Фаззер запускается из корневого каталога проекта командой на листинге~\ref{lst:diff-fuzz}. + +\begin{listing}[H] + \caption{Запуск дифференциального фаззера.} + \label{lst:diff-fuzz} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +python -m research.fuzz.diff_fuzz \ + --cli build/bin/sql \ + --data-dir test/static/executor/test_data + \end{minted} +\end{listing} + +Для проверки режима JIT-компиляции можно дополнительно передать параметр +\verb|--jit|. При обнаружении расхождения фаззер выводит начальное значение +генератора случайных чисел и текст запроса, что позволяет воспроизвести ошибку. + +Второй фаззер проверяет полноту набора правил оптимизатора. Он получает +физический план запроса из Microsoft SQL Server, преобразует его во внутренний +формат проекта и запускает программу в режиме \verb|--check-reachable|, как показано на листинге~\ref{lst:reach-fuzz}. + +\begin{listing}[H] + \caption{Запуск фаззера проверки полноты правил оптимизатора.} + \label{lst:reach-fuzz} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +python -m research.fuzz.reach_fuzz \ + --cli build/bin/sql \ + --data-dir test/static/executor/test_data + \end{minted} +\end{listing} + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{reach-fuzz.png} +\caption{Пример вывода фаззера проверки полноты правил оптимизатора}% +\label{fig:reach-fuzz} +\end{figure} + +Оба фаззера поддерживают параметр \verb|--start-seed|. Он задает начальное +значение генератора и используется для воспроизводимого повторного запуска, +как показано на листинге~\ref{lst:reach-fuzz-seed}. + +\begin{listing}[H] + \caption{Запуск фаззера с фиксированным начальным значением генератора.} + \label{lst:reach-fuzz-seed} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +python -m research.fuzz.reach_fuzz \ + --cli build/bin/sql \ + --data-dir test/static/executor/test_data \ + --start-seed 1000 + \end{minted} +\end{listing} + +\begin{longtable}{|p{4.2cm}|p{9.3cm}|} + \caption{Параметры командной строки программы.}\label{tbl:practice-cli}\\ + \hline + Параметр & Назначение \\ + \hline + \endfirsthead + \multicolumn{2}{l}{\small Продолжение таблицы~\ref{tbl:practice-cli}}\\ + \hline + Параметр & Назначение \\ + \hline + \endhead + \texttt{--data-dir DIR} & + Директория с CSV-файлами таблиц. Обязательна при обычном исполнении запроса. \\ + \hline + \texttt{--print-ast} & + Вывод логического дерева операторов после синтаксического анализа. \\ + \hline + \texttt{--print-plan} & + Вывод выбранного физического плана в формате S-expression. \\ + \hline + \texttt{--jit} & + Использование кэшируемой JIT-компиляции скалярных выражений. \\ + \hline + \texttt{--check-reachable FILE} & + Проверка достижимости физического плана, сериализованного в файле. \\ + \hline +\end{longtable} + +\section{Тестирование} + +Тестирование разработанной модельной СУБД выполнялось на нескольких уровнях. Для +проверки отдельных компонентов использовались модульные тесты. Корректность +выполнения запросов и полнота пространства поиска оптимизатора дополнительно +проверялись с помощью фаззинга. Для оценки производительности и калибровки +стоимостной модели были реализованы бенчмарки. Вспомогательные инструменты на +языке Python проверяются с помощью библиотеки \verb|pytest|. + +\subsection{Модульные тесты} + +Модульные тесты реализованы с использованием библиотеки GoogleTest. Они +проверяют синтаксический анализ SQL-запросов, исполнение физических операторов, +индексное сканирование, вычисление выражений, сериализацию физических планов, +структуру Memo, правила оптимизации и обработку свойств сортировки. + +Отдельные тесты проверяют выбор физического плана. Например, при различных +оценках кардинальности оптимизатор должен выбирать подходящий порядок соединения +таблиц. Также проверяется достижимость физических планов при полном переборе +пространства поиска. + +Пример теста синтаксического анализатора приведен в +листинге~\ref{lst:googletest}. + +\begin{listing}[H] + \caption{Пример модульного теста.} + \label{lst:googletest} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{cpp} +TEST(ParserTest, SelectSingleColumnFromSingleTable) { + std::stringstream s{"SELECT users.id FROM users;"}; + + Operator got = GetAST(s).value().op; + + ASSERT_THAT(got, VariantWith( + Projection{ + std::vector{{Attribute{"users", "id"}}}, + std::make_shared(Table{"users"}) + })); +} + \end{minted} +\end{listing} + +\subsection{Python-тесты} + +Для проверки вспомогательных инструментов используются тесты на языке Python, +запускаемые с помощью библиотеки \verb|pytest|. Они разделены на две группы. + +Первая группа проверяет преобразование физических планов Microsoft SQL Server из +формата ShowPlanXML во внутренний текстовый формат проекта. Рассматриваются +последовательное и индексное сканирование, фильтрация, сортировка, агрегация и +соединения. Часть тестов использует заранее подготовленные XML-фрагменты, часть +может быть запущена с подключением к экземпляру Microsoft SQL Server. + +Вторая группа относится к Star Schema Benchmark. Она проверяет преобразование +исходных таблиц SSB в CSV-формат модельной СУБД, обработку некорректных входных +данных и успешное выполнение стандартных аналитических запросов. + +\subsection{Фаззинг} + +Для проверки системы на большом количестве входных данных реализован генератор +случайных SQL-запросов. Он строит запросы из поддерживаемого подмножества языка: +проекции, фильтры, соединения, группировки, агрегатные функции, сортировки, +строковые выражения, \verb|IN| и \verb|BETWEEN|. Использование фиксированного +начального значения генератора позволяет воспроизводить найденные ошибки. + +% TODO: заменить на макросы \sqkkw + +Дифференциальный фаззер выполняет каждый сгенерированный запрос в модельной СУБД +и в Microsoft SQL Server. Результаты сравниваются как мультимножества строк. +Для запросов с \verb|ORDER BY| дополнительно проверяется корректность порядка +результатов. При обнаружении расхождения выводятся начальное значение +генератора, текст запроса и результаты обеих систем. + +Отдельный фаззер используется для исследования полноты набора правил +оптимизации. Для случайного запроса из Microsoft SQL Server извлекается +физический план, после чего план преобразуется во внутренний формат проекта. +Затем модельный оптимизатор выполняет полный перебор пространства поиска и +проверяет, достижим ли полученный план с помощью реализованных правил. Такой +подход позволяет находить отсутствующие преобразования и физические операторы. + +\subsection{Бенчмарки} + +Тесты производительности реализованы с помощью библиотеки Google Benchmark. +Для запросов предусмотрены два режима построения физического плана: +\verb|Naive| и \verb|Optimized|. В первом случае физический план строится +непосредственно по логическому дереву без применения оптимизатора. Во втором +случае используется стоимостная оптимизация. Сравнение этих режимов позволяет +оценить влияние выбранного физического плана на время выполнения запроса. + +Бенчмарки выполняются как для небольших синтетических тестовых запросов, так и +для набора аналитических запросов Star Schema Benchmark. Дополнительно +поддерживается сравнение интерпретируемого и JIT-компилируемого способов +вычисления выражений. + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{naive-vs-optimizer.png} +\caption{Сравнение времени выполнения запросов при наивном и оптимизированном + построении физического плана.} +\label{fig:benchmark-naive-optimized} +\end{figure} + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{optimizer-speedup.png} +\caption{Относительное ускорение выполнения запросов после применения оптимизатора.} +\label{fig:benchmark-speedup} +\end{figure} + +В таблице~\ref{tbl:ssb-benchmark} приведено среднее время выполнения трех +повторений запросов SSB для набора данных масштаба \texttt{sf001}. Ускорение +рассчитывается как отношение времени выполнения наивного плана ко времени +выполнения оптимизированного плана. + +\begin{table}[H] + \centering + \caption{Результаты выполнения запросов Star Schema Benchmark.} + \label{tbl:ssb-benchmark} + \begin{tabular}{|l|r|r|r|} + \hline + Запрос & \texttt{Naive}, мс & \texttt{Optimized}, мс & Ускорение \\ + \hline + \texttt{q1.1} & 8.11 & 6.51 & 1.25 \\ + \texttt{q1.2} & 8.20 & 6.59 & 1.24 \\ + \texttt{q1.3} & 8.00 & 6.58 & 1.22 \\ + \texttt{q2.1} & 8.80 & 9.33 & 0.94 \\ + \texttt{q2.2} & 8.84 & 6.18 & 1.43 \\ + \texttt{q2.3} & 8.68 & 5.80 & 1.50 \\ + \texttt{q3.1} & 35.67 & 5.90 & 6.04 \\ + \texttt{q3.2} & 35.88 & 5.58 & 6.43 \\ + \texttt{q3.3} & 38.05 & 5.86 & 6.49 \\ + \texttt{q3.4} & 37.12 & 5.77 & 6.43 \\ + \texttt{q4.1} & 9.06 & 12.21 & 0.74 \\ + \texttt{q4.2} & 9.05 & 11.12 & 0.81 \\ + \texttt{q4.3} & 9.28 & 6.31 & 1.47 \\ + \hline + \end{tabular} +\end{table} + +\subsection{Калибровка стоимостной модели} + +Для калибровки коэффициентов в стоимостных формулах реализованы отдельные +бенчмарки физических операторов. Измеряется время выполнения последовательного +сканирования, фильтрации, проекции, сортировки, агрегации, хеш-соединения, +соединения вложенными циклами и декартова произведения. + +При выполнении этих бенчмарков данные хранятся в оперативной памяти. Это +позволяет уменьшить влияние файловой системы и измерять преимущественно +стоимость самого физического оператора. Для каждого оператора выполняются замеры +на таблицах различного размера. + +Дополнительно формируются группы запусков с приблизительно одинаковой расчетной +стоимостью. Для этого размер входных отношений выбирается на основе стоимостной +формулы оператора. Сопоставление расчетной стоимости с фактическим временем +выполнения позволяет сравнить операторы между собой и скорректировать +коэффициенты модели. Благодаря этому оптимизатор принимает решения на основе +оценок, соответствующих характеристикам реализованного исполнителя. + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{scaling.png} +\caption{Зависимость времени выполнения физических операторов от размера входных + данных.} +\label{fig:operator-benchmark} +\end{figure} + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{calibration.png} +\caption{Сопоставление расчетной стоимости физических операторов с фактическим + временем выполнения.} +\label{fig:cost-model-calibration} +\end{figure} + +Стоимостные формулы физических операторов, полученные по результатам +калибровки, приведены в таблице~\ref{tbl:cost-model-coefficients}. + +\begin{table}[H] + \centering + \caption{Стоимостные формулы физических операторов.} + \label{tbl:cost-model-coefficients} + \small + \begin{tabular}{|p{0.40\textwidth}|l|} + \hline + Физический оператор & Формула стоимости \\ + \hline + Последовательное сканирование & $100\,n$ \\ + Фильтрация & $100\,n_{out}$ \\ + Проекция & $22\,n_{out}$ \\ + Сортировка & $11\,n\bigl(\lfloor\log_2 n\rfloor + 1\bigr)$ \\ + Агрегация & $510\,n$ \\ + Соединение вложенными циклами & $70\,n_l n_r$ \\ + Декартово произведение & $104\,n_l n_r$ \\ + Хеш-соединение & $100\,n_b w_b + 35\,n_p + 10\,n_o w_o$ \\ + \hline + \end{tabular} +\end{table} + +\anonsection{ЗАКЛЮЧЕНИЕ} + +В ходе практики разработана модельная СУБД и оптимизатор для нее. Программа +работает с CSV-файлами, поддерживает основные операторы реляционной алгебры и +формирует оптимальный физический план на основе стоимостной модели. + +Ключевой частью проекта является расширяемый оптимизатор, использующий структуру +Memo и правила преобразования. Реализован поиск планов с учетом физических +свойств, нижних оценок стоимости и метода ветвей и границ. + +Корректность основных компонентов проверена модульными тестами. Дополнительно +подготовлены бенчмарки физических операторов, оценен прирост производительности +относительно наивного плана и реализованы инструменты дифференциального +сравнения с промышленной СУБД Microsoft SQL Server. + +Цель курсовой работы была достигнута. Получена стабильная и расширяемая система, +демонстрирующая возможности оптимизатора. + +В заключение, несмотря на достигнутые положительные результаты, существует +потенциал для дальнейшего улучшения реализации оптимихатора. Это включает в себя +реализацию дедупликации групп в процессе поиска, добавление новых правил +трансформации и реализации и поддержку подзапросов. + +\renewcommand\refname{СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ} +{\catcode`"\active\def"{\relax} + \addcontentsline{toc}{section}{\protect\numberline{}\refname}% + \printbibliography +} + +\end{document} diff --git a/report/presentation-vkr.pdf b/report/presentation-vkr.pdf new file mode 100644 index 0000000..904060c Binary files /dev/null and b/report/presentation-vkr.pdf differ diff --git a/report/presentation-vkr.tex b/report/presentation-vkr.tex new file mode 100644 index 0000000..a2f1e76 --- /dev/null +++ b/report/presentation-vkr.tex @@ -0,0 +1,393 @@ +\documentclass{beamer} + +\usepackage[T1,T2A]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[english,russian]{babel} +\usepackage{tempora} + +\usepackage[auto]{contour} +\usepackage{amsmath} +\usepackage{array} +\usepackage{booktabs} +\usepackage{graphicx} +\usepackage{listings} +\usepackage{ragged2e} +\usepackage{tikz} +\usepackage{xcolor} +\usetikzlibrary{arrows.meta} + +\definecolor{iu9dept}{RGB}{40,40,60} +\definecolor{iu9shade}{RGB}{245,245,255} + +\newcommand{\iutitle}[1]{\contour[10]{iu9shade}{\textcolor{iu9dept}{#1}}} +\newcommand{\iutitleupd}[1]{\contour[10]{iu9shade!90!gray}{\textcolor{iu9dept}{#1}}} + +\newcommand{\sqlkw}[1]{\texttt{#1}} +\newcommand{\SELECT}{\sqlkw{SELECT}} +\newcommand{\FROM}{\sqlkw{FROM}} +\newcommand{\WHERE}{\sqlkw{WHERE}} +\newcommand{\JOIN}{\sqlkw{JOIN}} +\newcommand{\GROUPBY}{\sqlkw{GROUP BY}} +\newcommand{\ORDERBY}{\sqlkw{ORDER BY}} +\newcommand{\Sort}{\sqlkw{Sort}} +\newcommand{\HashJoin}{\sqlkw{HashJoin}} +\newcommand{\MergeJoin}{\sqlkw{MergeJoin}} +\newcommand{\NestedLoopJoin}{\sqlkw{NestedLoopJoin}} +\newcommand{\SeqScan}{\sqlkw{SeqScan}} +\newcommand{\IndexScan}{\sqlkw{IndexScan}} +\newcommand{\Sel}[2]{\sigma_{#1}(#2)} +\newcommand{\Projection}[2]{\pi_{#1}(#2)} +\newcommand{\Agg}[3]{\gamma_{#1;\,#2}(#3)} +\newcommand{\InnerJoin}[3]{#1 \bowtie_{#2} #3} +\newcommand{\CProd}[2]{#1 \times #2} + +\lstdefinestyle{sqlstyle}{ + basicstyle=\ttfamily\footnotesize, + columns=fullflexible, + breaklines=true, + keepspaces=true, + showstringspaces=false +} +\lstset{style=sqlstyle} + +\usetheme{IU9light} + +\title[Автоматическая оптимизация планов выполнения SQL-запросов]{Автоматическая оптимизация планов выполнения SQL-запросов} +\date[17.06.2026]{17.06.2026} +\author[Старовойтов А.И.]{Старовойтов А.И.} +\supervisor{Руководитель: Непейвода А.Н.} +\institute{\contourlength{0.05em}\iutitle{МГТУ им. Н.Э. Баумана}\\\vspace*{-2ex}\\ \iutitle{Кафедра <<Теоретическая информатика}\\ +\iutitleupd{\qquad\qquad\qquad\qquad и компьютерные технологии>>}} + +\setbeamertemplate{blocks}[rounded][shadow=true] + +\begin{document} + +\begin{frame}[plain] +\titlepage +\end{frame} + +\begin{frame}{Актуальность} +\begin{block}{Оптимизатор запросов} +Компонент СУБД, который преобразует декларативный SQL-запрос в физический план +исполнения. +\end{block} + +\begin{itemize} + \item Один SQL-запрос допускает множество эквивалентных планов. + \item Разные порядки соединений и физические операторы дают различную стоимость. + \item Поиск оптимального плана является NP-полной задачей в общем случае. + \item Выбор неудачного плана может увеличить время выполнения на порядки. +\end{itemize} +\end{frame} + +\begin{frame}{Цель и задачи} +\begin{block}{Цель} +Разработать и реализовать оптимизатор подмножества SQL-запросов на основе +архитектуры Cascades, а также создать каркас для его итеративного улучшения. +\end{block} + +\begin{itemize} + \item Формализовать поддерживаемое подмножество SQL и реляционной алгебры. + \item Реализовать Memo и алгоритм поиска физического плана. + \item Разработать правила трансформации и реализации операторов. + \item Реализовать стоимостную модель и метод ветвей и границ. + \item Сравнить пространство поиска с планами Microsoft SQL Server. +\end{itemize} +\end{frame} + +\begin{frame}{Эффект выбора плана} +\centering +\scriptsize +\resizebox{0.94\textwidth}{!}{% +\begin{tikzpicture}[ + node distance=0.55cm, + op/.style={rectangle, draw, fill=gray!10, rounded corners=2pt, + text width=2.45cm, minimum height=0.72cm, align=center}, + rel/.style={rectangle, draw, rounded corners=2pt, + text width=1.55cm, minimum height=0.54cm, align=center}, + edge/.style={->, thick} +] + \node[font=\bfseries] at (-3.1, 3.55) {Наивный план}; + \node[op] (nproj) at (-3.1, 2.70) {\(\pi_{\texttt{ename}}\)\\card=20\\cost=50\,050}; + \node[op] (nfilter2) at (-3.1, 1.25) {\(\sigma_{\texttt{dname}=\texttt{'Toy'}}\)\\card=20\\cost=50\,050}; + \node[op] (nfilter1) at (-3.1, -0.20) {\(\sigma_{\texttt{Emp.did}=\texttt{Dept.did}}\)\\card=10\,000\\cost=50\,050}; + \node[op] (ncross) at (-3.1, -1.90) {NestedLoop\\CrossJoin\\card=\(5\cdot10^6\)\\cost=50\,050}; + \node[rel] (nemp) at (-4.55, -3.25) {\texttt{Emp}\\card=10\,000}; + \node[rel] (ndept) at (-1.65, -3.25) {\texttt{Dept}\\card=500}; + + \draw[edge] (nemp) -- (ncross); + \draw[edge] (ndept) -- (ncross); + \draw[edge] (ncross) -- (nfilter1); + \draw[edge] (nfilter1) -- (nfilter2); + \draw[edge] (nfilter2) -- (nproj); + + \node[font=\bfseries] at (3.1, 3.25) {Оптимизированный план}; + \node[op] (oproj) at (3.1, 2.45) {\(\pi_{\texttt{ename}}\)\\card=20\\cost=26}; + \node[op] (ojoin) at (3.1, 0.85) {Index\\NestedLoopJoin\\card=20\\cost=26}; + \node[rel] (oemp) at (1.55, -0.55) {\texttt{Emp}\\card=10\,000}; + \node[op] (ofilter) at (4.65, -0.55) {\(\sigma_{\texttt{dname}=\texttt{'Toy'}}\)\\card=1\\cost=3}; + \node[rel] (odept) at (4.65, -1.75) {\texttt{Dept}\\card=500}; + + \draw[edge] (oemp) -- (ojoin); + \draw[edge] (ofilter) -- (ojoin); + \draw[edge] (ojoin) -- (oproj); + \draw[edge] (odept) -- (ofilter); +\end{tikzpicture} +} +\end{frame} + +\begin{frame}[fragile]{Поддерживаемое подмножество SQL} +\begin{lstlisting} +SELECT projection +FROM tables +JOIN ... ON predicate +WHERE predicate +GROUP BY attributes +ORDER BY attributes +\end{lstlisting} + +\begin{itemize} + \item Основа грамматики: диалект PostgreSQL. + \item Поддержаны \SELECT, \FROM, \WHERE, \JOIN, \GROUPBY, \ORDERBY. + \item Соединения: \sqlkw{INNER}, \sqlkw{LEFT}, \sqlkw{RIGHT}, \sqlkw{FULL}, \sqlkw{CROSS}. + \item Агрегации: \sqlkw{SUM}, \sqlkw{COUNT}, \sqlkw{COUNT(*)}. + \item Предикаты: сравнения, \sqlkw{AND}, \sqlkw{OR}, \sqlkw{NOT}, \sqlkw{BETWEEN}, \sqlkw{IN}, проверки \sqlkw{NULL}. +\end{itemize} +\end{frame} + +\begin{frame}{Cascades и Memo} +\begin{columns}[T,onlytextwidth] +\begin{column}{0.49\textwidth} +\begin{itemize} + \item Cascades хранит пространство поиска в структуре Memo. + \item Группа Memo объединяет логически эквивалентные выражения. + \item Групповое выражение содержит оператор и ссылки на дочерние группы. + \item Поиск управляется стеком задач. +\end{itemize} +\end{column} +\begin{column}{0.49\textwidth} +\centering +\includegraphics[width=\textwidth,height=0.62\textheight,keepaspectratio]{cascades.pdf} +\end{column} +\end{columns} +\end{frame} + +\begin{frame}{Процесс обработки запроса} +\centering +\resizebox{0.88\textwidth}{!}{% +\begin{tikzpicture}[ + scale=0.95, transform shape, + node/.style={rectangle, draw, rounded corners=2pt, + text width=3.0cm, minimum height=0.8cm, + align=center, font=\scriptsize}, + main/.style={node, fill=gray!8}, + io/.style={node, fill=gray!20}, + aux/.style={node, dashed}, + arrow/.style={-{Stealth[length=2.5mm]}, thick}, + auxarrow/.style={-{Stealth[length=2.5mm]}, thick, dashed} +] + \node[io] (sql) at (0, 0) {Генератор\\случайных\\запросов}; + \node[main] (parse) at (0, -2) {Разбор}; + \node[main] (optim) at (0, -4.5) {Оптимизация\\ и\\проверка\\ достижимости}; + \node[main] (exec) at (0, -6.65) {Исполнение}; + \node[io] (result) at (0, -8.2) {Результат}; + + \node[aux] (mssqlplan) at (5.0, -1.55) {MSSQL}; + \node[aux] (diff) at (5.0, -4.65) {Проверка\\корректности}; + \node[aux] (mssqlres) at (-5.0, -1.55) {MSSQL}; + \node[aux] (reach) at (-5.0, -4.5) {Конвертация\\в план\\модельной СУБД}; + + \draw[arrow] (sql) -- node[right, font=\scriptsize] {запрос} (parse); + \draw[arrow] (parse) -- node[right, font=\scriptsize] {реляционная алгебра} (optim); + \draw[arrow] (optim) -- node[right, font=\scriptsize] {план} (exec); + \draw[arrow] (exec) -- (result); + + \draw[auxarrow] (sql) -- node[above, font=\scriptsize] {запрос} (mssqlplan); + \draw[auxarrow] (mssqlplan) -- node[right, font=\scriptsize] {результат} (diff); + \draw[auxarrow] (result.east) -- ++(1.2,0) |- (diff.west); + + \draw[auxarrow] (sql) -- node[above, font=\scriptsize] {запрос} (mssqlres); + \draw[auxarrow] (mssqlres) -- node[left, font=\scriptsize] {план} (reach); + \draw[auxarrow] (reach.east) -- node[above, font=\scriptsize] {планы} (optim.west); +\end{tikzpicture} +} +\end{frame} + +\begin{frame}{Реляционная алгебра} +\begin{itemize} + \item \(\sigma_p(E)\) --- фильтрация кортежей по предикату \(p\). + \item \(\pi_{\bar a}(E)\) --- проекция выбранных атрибутов. + \item \(E_1 \times E_2\) --- декартово произведение отношений. + \item \(E_1 \Join_p E_2\) --- соединение отношений по предикату. + \item \(\gamma_{\bar g;\bar f}(E)\) --- группировка и вычисление агрегатов. + \item \(\tau_{\bar a}(E)\) --- сортировка результата. +\end{itemize} + +\begin{block}{Форма запроса} +\[ + \Projection{\bar a}{\Sel{p}{E_1 \times \ldots \times E_n}}, + \qquad + \Agg{\bar g}{\bar f}{\Sel{p}{E_1 \times \ldots \times E_n}} +\] +\end{block} +\end{frame} + +\begin{frame}{Правила оптимизации} +\footnotesize +\begin{columns}[T,onlytextwidth] +\begin{column}{0.54\textwidth} +\begin{block}{Трансформации} +\begin{itemize} + \item коммутативность и ассоциативность соединений; + \item разбиение, объединение и проталкивание фильтров; + \item проталкивание проекций через соединение; + \item преобразование \sqlkw{IN} в цепочку сравнений; + \item перенос фильтра в предикат соединения; + \item внешнее соединение во внутреннее; + \item проталкивание и перестановка агрегации. +\end{itemize} +\end{block} +\end{column} +\begin{column}{0.42\textwidth} +\begin{block}{Реализации} +\begin{itemize} + \item \SeqScan, \IndexScan; + \item \sqlkw{Filter}, \sqlkw{Projection}; + \item \NestedLoopJoin, \HashJoin, \MergeJoin; + \item \sqlkw{HashAggregate}; + \item \sqlkw{StreamAggregate}. +\end{itemize} +\end{block} +\end{column} +\end{columns} +\end{frame} + +\begin{frame}{Стоимостная модель} +\begin{block}{Отсечение ветвей} +Если для группы \(G\) и свойства \(P\) уже найден план стоимости \(C^*\), то +физическое выражение \(e\) можно не рассматривать при выполнении условия: +\[ + C_{local}(e) + \sum_{i=1}^{k} LB(G_i, P_i) \geq C^*. +\] +\end{block} + +\begin{center} +\scriptsize +\begin{tabular}{@{}ll@{}} +\toprule +Физический оператор & Формула стоимости \\ +\midrule +Последовательное сканирование & \(100\,n\) \\ +Проекция & \(22\,n_{out}\) \\ +Сортировка & \(11\,n(\lfloor\log_2 n\rfloor + 1)\) \\ +Агрегация & \(510\,n\) \\ +Потоковая агрегация & \(130\,n\) \\ +Соединение вложенными циклами & \(70\,n_l n_r\) \\ +Декартово произведение & \(104\,n_l n_r\) \\ +\bottomrule +\end{tabular} +\end{center} +\end{frame} + +\begin{frame}{Дифференциальный анализ планов} +\begin{block}{Основная идея} + Проверяем, достижим ли план, построенный промышленной СУБД, при текущем наборе + правил и условиях активации. Цель: найти недостающие правила и уточнить + правила активации. +\end{block} + +\begin{enumerate} + \item Генерируется случайный SQL-запрос из поддерживаемого подмножества. + \item Microsoft SQL Server строит физический план. + \item ShowPlanXML конвертируется во внутренний формат проекта. + \item Запускается оптимизатор с отключенным B\&B. + \item Структурно соотносится получившееся пространство поиска и эталонный план. +\end{enumerate} +\end{frame} + +\begin{frame}[fragile]{Пример найденного расхождения} +\begin{lstlisting} +SELECT markets.id AS c2619 +FROM markets CROSS JOIN users CROSS JOIN books +WHERE markets.region != 'AMERICA'; +\end{lstlisting} + +\begin{columns}[T,onlytextwidth] +\begin{column}{0.49\textwidth} +\centering +\includegraphics[width=\textwidth,height=0.42\textheight,keepaspectratio]{reach-fuzz-cross-mssql.pdf} + +\scriptsize План Microsoft SQL Server +\end{column} +\begin{column}{0.49\textwidth} +\centering +\includegraphics[width=\textwidth,height=0.42\textheight,keepaspectratio]{reach-fuzz-cross-ours.pdf} + +\scriptsize План модельной СУБД +\end{column} +\end{columns} +\end{frame} + +\begin{frame}{Результаты проверки пространства поиска} +\begin{center} +\small +\begin{tabular}{@{}lr@{}} +\toprule +Показатель & Значение \\ +\midrule +Количество сгенерированных запросов & 1000 \\ +Достижимый план промышленной СУБД & 251 \\ +Недостижимый план промышленной СУБД & 244 \\ +Пропущено & 505 \\ +Ошибки преобразования плана & 496 \\ +Ошибки преобразования из-за исключений & 9 \\ +Ошибки получения плана Microsoft SQL Server & 0 \\ +Среднее расстояние до ближайшего плана & 2.37 \\ +\bottomrule +\end{tabular} +\end{center} + + \begin{itemize} + \item 25\% от общего числа планов совпали. + \item 25\% требуют уточнения правил. + \item 50\% требуют расширения набора операторов. + \end{itemize} +\end{frame} + +\begin{frame}{Производительность} +\centering +\includegraphics[width=\textwidth,height=0.50\textheight,keepaspectratio]{optimizer-speedup.png} + +\raggedright +\begin{itemize} + \item Сравнивались режимы \texttt{Naive} и \texttt{Optimized}. + \item Использованы запросы Star Schema Benchmark. + \item На выбранном наборе тестов получено ускорение \(2.5\text{--}30\) раз. +\end{itemize} +\end{frame} + +\begin{frame}{Калибровка стоимостной модели} +\begin{columns}[T,onlytextwidth] +\begin{column}{0.49\textwidth} +\centering +\includegraphics[width=\textwidth,height=0.40\textheight,keepaspectratio]{calibration.png} + +\scriptsize Физические операторы +\end{column} +\begin{column}{0.49\textwidth} +\centering +\includegraphics[width=\textwidth,height=0.40\textheight,keepaspectratio]{cost-on-random-queries.png} + +\scriptsize Случайные запросы +\end{column} +\end{columns} + +\footnotesize +\begin{itemize} + \item Формулы стоимости откалиброваны по бенчмаркам физических операторов. + \item Общая стоимость планов согласована с реальным временем исполнения. +\end{itemize} +\end{frame} + +\end{document} diff --git a/report/reach-fuzz-cross-mssql.pdf b/report/reach-fuzz-cross-mssql.pdf new file mode 100644 index 0000000..c8d7a50 Binary files /dev/null and b/report/reach-fuzz-cross-mssql.pdf differ diff --git a/report/reach-fuzz-cross-ours.pdf b/report/reach-fuzz-cross-ours.pdf new file mode 100644 index 0000000..f13d705 Binary files /dev/null and b/report/reach-fuzz-cross-ours.pdf differ diff --git a/report/reach-fuzz.png b/report/reach-fuzz.png new file mode 100644 index 0000000..530de08 Binary files /dev/null and b/report/reach-fuzz.png differ diff --git a/report/run-example.png b/report/run-example.png new file mode 100644 index 0000000..3517d1e Binary files /dev/null and b/report/run-example.png differ diff --git a/report/run-with-prints.png b/report/run-with-prints.png new file mode 100644 index 0000000..d007a75 Binary files /dev/null and b/report/run-with-prints.png differ diff --git a/report/scaling.png b/report/scaling.png new file mode 100644 index 0000000..8f70bf5 Binary files /dev/null and b/report/scaling.png differ diff --git a/report/thesis.pdf b/report/thesis.pdf new file mode 100644 index 0000000..c2c815f Binary files /dev/null and b/report/thesis.pdf differ diff --git a/report/thesis.tex b/report/thesis.tex new file mode 100644 index 0000000..5c1fd68 --- /dev/null +++ b/report/thesis.tex @@ -0,0 +1,1477 @@ +% !TeX TXS-program:bibliography = txs:///biber +% chktex-file 1 +\documentclass[fontsize=14pt, russian]{scrartcl} +\input{header.tex} +\addbibresource{biblio.bib} + +\usepackage{svg} + +\begin{document} +\sloppy + +\def\figurename{Рисунок} + +\newcommand{\sqlkw}[1]{\texttt{#1}} +\newcommand{\SELECT}{\sqlkw{SELECT}} +\newcommand{\FROM}{\sqlkw{FROM}} +\newcommand{\WHERE}{\sqlkw{WHERE}} +\newcommand{\JOIN}{\sqlkw{JOIN}} +\newcommand{\GROUPBY}{\sqlkw{GROUP BY}} +\newcommand{\ORDERBY}{\sqlkw{ORDER BY}} + +\newcommand{\COUNT}{\sqlkw{COUNT}} +\newcommand{\SUM}{\sqlkw{SUM}} +\newcommand{\AVG}{\sqlkw{AVG}} +\newcommand{\MIN}{\sqlkw{MIN}} +\newcommand{\MAX}{\sqlkw{MAX}} + +\newcommand{\LogicalGet}{\sqlkw{LogicalGet}} +\newcommand{\SeqScan}{\sqlkw{SeqScan}} +\newcommand{\IndexScan}{\sqlkw{IndexScan}} +\newcommand{\NestedLoopJoin}{\sqlkw{NestedLoopJoin}} +\newcommand{\NestedLoopCrossJoin}{\sqlkw{NestedLoopCrossJoin}} +\newcommand{\HashJoin}{\sqlkw{HashJoin}} +\newcommand{\MergeJoin}{\sqlkw{MergeJoin}} +\newcommand{\Sort}{\sqlkw{Sort}} +\newcommand{\FilterOp}{\sqlkw{Filter}} +\newcommand{\ProjOp}{\sqlkw{Projection}} + +\newcommand{\SelOp}[1]{\sigma_{#1}} +\newcommand{\ProjectionOp}[1]{\pi_{#1}} + +\newcommand{\Sel}[2]{\sigma_{#1}(#2)} +\newcommand{\Projection}[2]{\pi_{#1}(#2)} +\newcommand{\Agg}[3]{\gamma_{#1;\,#2}(#3)} +\newcommand{\AggP}[3]{\gamma_{#1;\,#2}^{partial}(#3)} +\newcommand{\NJoin}[2]{#1 \bowtie #2} +\newcommand{\InnerJoin}[3]{#1 \bowtie_{#2} #3} +\newcommand{\CProd}[2]{#1 \times #2} +\newcommand{\LOJ}[3]{#1 \ltimes_{#2} #3} +\newcommand{\ROJ}[3]{#1 \rtimes_{#2} #3} +\newcommand{\FOJ}[3]{#1 \mathbin{\ltimes\!\!\!\rtimes}_{#2} #3} +\newcommand{\Tr}[1]{[\![ #1 ]\!]} +\newcommand{\Apply}[2]{#1 \mathbin{\mathcal{A}} #2} +\newcommand{\TransRule}[3]{% + \ensuremath{% + \begin{array}{c} + #1 \\[1pt] + \xRightarrow{\,#2\,} \\[1pt] + #3 + \end{array}% + }% +} + +\setlength{\tabcolsep}{3pt} +\setcounter{page}{2} +%---------------------------------------------------------------------------- +% ОТСЮДА --- СОБСТВЕННО ТЕКСТ +%---------------------------------------------------------------------------- + +\newpage +\renewcommand\contentsname{\hfill{\normalfont{СОДЕРЖАНИЕ}}\hfill} +\tableofcontents +\newpage +\anonsection{ВВЕДЕНИЕ} + +В эпоху стремительного роста объемов данных системы управления базами данных +(СУБД) являются важной частью информационных систем. По данным аналитических +исследований, объем мирового рынка СУБД ежегодно увеличивается, что обусловлено +как расширением круга решаемых задач, так и усложнением структуры обрабатываемых +данных и увеличением их объема. Реляционные базы данных, работающие с различными +диалектами языка SQL, продолжают занимать доминирующее положение в сегменте +обработки транзакций и аналитических запросов. + +Одним из определяющих факторов конкурентоспособности между различными +реализациями СУБД является качество оптимизатора запросов. Оптимизатор +представляет собой компонент, ответственный за преобразование декларативного +SQL-запроса в эффективный физический план исполнения. Именно от качества работы +оптимизатора в значительной степени зависит производительность всей системы: +выбор неоптимального плана исполнения может привести к увеличению времени +выполнения запроса в десятки раз относительно оптимального случая. + +Также оптимизатор запросов считается наиболее сложной составной частью любой +СУБД. Задача нахождения оптимального плана является NP-полной в общем случае, +что обуславливает необходимость применения эвристических методов и алгоритмов +ограниченного перебора. Современные коммерческие и открытые системы используют +различные архитектуры оптимизаторов, среди них наиболее популярны устоявшиеся и +проверенные временем архитектуры Cascades и Volcano. + +Архитектура Cascades используется в таких промышленных системах, как Microsoft +SQL Server, Apache Orca, а также в ряде исследовательских проектов, включая +Columbia и CockroachDB\@. Преимущества этой архитектуры заключаются в +расширяемости, модульности, а также понятном и эффективном алгоритме поиска, +допускающим параллельность исполнения. + +Целью данной работы является разработка и реализация оптимизатора подмножества +SQL-запросов на основе архитектуры Cascades, а также создание каркаса для его +итеративного улучшения путем сравнения с промышленными реализациями. + +Для достижения поставленной цели необходимо решить следующие задачи: +\begin{itemize} + \item изучить существующие архитектуры оптимизаторов запросов, включая + System~R, Volcano и Cascades; + \item формализовать подмножество SQL и соответствующее подмножество + реляционной алгебры; + \item реализовать структуру данных Memo и алгоритм поиска оптимального плана + на основе архитектуры Cascades; + \item реализовать набор правил трансформации и реализации для основных + операторов реляционной алгебры; + \item реализовать метод ветвей и границ для эффективного отсечения + неоптимальных планов; + \item реализовать метод дифференциального анализа физических планов для + автоматизированного поиска примеров неоптимальных планов. +\end{itemize} + +\section{Архитектура СУБД} + +Система управления базами данных представляет собой программный комплекс, +обеспечивающий хранение, извлечение и модификацию структурированных +данных. Несмотря на разнообразие существующих реализаций, архитектура +большинства реляционных СУБД включает ряд типовых компонентов, +взаимодействие которых обеспечивает выполнение пользовательских запросов. + +К основным компонентам СУБД относятся~\refImage{fig::dbms_arch}: + +\begin{itemize} + \item транспортный уровень, принимающий клиентские подключения и запросы; + \item синтаксический анализатор (парсер), выполняющий разбор текста +SQL-запроса и построение синтаксического дерева; + \item оптимизатор запросов, преобразующий логическое представление запроса в +эффективный физический план исполнения; + \item исполнитель, непосредственно реализующий физический план; + \item менеджер хранения данных, управляющий размещением данных на физических +носителях; + \item менеджер транзакций и менеджер блокировок, обеспечивающие гарантии +транзакций; + \item менеджер страниц памяти, отвечающий за кэширование страниц данных в +оперативной памяти. +\end{itemize} + +\begin{figure}[!htb]\centering +\usetikzlibrary{fit} +\begin{tikzpicture}[ + box/.style={rectangle, draw, minimum width=4.1cm, minimum height=1cm, + align=center, font=\small}, + wbox/.style={rectangle, draw, minimum width=9.1cm, minimum height=1cm, + align=center, font=\small}, + grp/.style={draw, dashed, inner sep=0.3cm}, + lbl/.style={font=\small}, +] + \node[box] (clust) at (-2.5, 0) {Кластерные\\соединения}; + \node[box] (client) at ( 2.5, 0) {Клиентские\\соединения}; + \node[grp, fit=(clust)(client)] (TG) {}; + \node[lbl, anchor=south west] at (TG.north west) {Транспорт}; + + \node[wbox] (parser) at (0, -2.8) {Синтаксический анализатор}; + \node[wbox, fill=gray!20] (optim) at (0, -4.05) {\textbf{Оптимизатор запросов}}; + \node[grp, fit=(parser)(optim)] (QPG) {}; + \node[lbl, anchor=south west] at (QPG.north west) {Обработчик запросов}; + + \node[box] (remote) at (-2.5, -6.85) {Удалённое\\исполнение}; + \node[box] (local) at ( 2.5, -6.85) {Локальное\\исполнение}; + \node[grp, fit=(remote)(local)] (EEG) {}; + \node[lbl, anchor=south west] at (EEG.north west) {Исполнитель}; + + \node[box] (txmgr) at (-2.5, -9.65) {Менеджер\\транзакций}; + \node[box] (lockmgr) at ( 2.5, -9.65) {Менеджер\\блокировок}; + \node[wbox] (access) at ( 0, -10.9) {Методы доступа}; + \node[box] (bufmgr) at (-2.5, -12.15) {Менеджер\\страниц}; + \node[box] (recmgr) at ( 2.5, -12.15) {Менеджер\\восстановления}; + \node[grp, fit=(txmgr)(lockmgr)(access)(bufmgr)(recmgr)] (SEG) {}; + \node[lbl, anchor=south west] at (SEG.north west) {Подсистема хранения}; + + \draw[<->, thick, >=stealth] + ([xshift=0.4cm]TG.south east) -- ([xshift=0.4cm]QPG.north east); + \draw[<->, thick, >=stealth] + ([xshift=0.4cm]QPG.south east) -- ([xshift=0.4cm]EEG.north east); + \draw[<->, thick, >=stealth] + ([xshift=0.4cm]EEG.south east) -- ([xshift=0.4cm]SEG.north east); + + \draw[thick] ([xshift=-0.5cm]clust.west) -- ([xshift=-0.5cm]remote.west); + \draw[->, thick, >=stealth] ([xshift=-0.5cm]clust.west) -- (clust.west); + \draw[->, thick, >=stealth] ([xshift=-0.5cm]remote.west) -- (remote.west); +\end{tikzpicture} + \caption{Архитектура реляционной системы управления базами данных}% + \label{fig::dbms_arch} +\end{figure} + +\subsection{Процесс исполнения SQL-запроса} + +Процесс исполнения SQL-запроса в реляционной СУБД проходит несколько +последовательных этапов. + +\begin{enumerate} + \item \emph{Синтаксический анализ} --- запроса проходит лексический и + синтаксический разбор. Результатом данного этапа является синтаксическое + дерево, узлы которого соответствуют конструкциям языка SQL: операторам + \SELECT, \FROM, \WHERE, \JOIN, \GROUPBY, \ORDERBY{} и другим. На этом же + этапе выполняется семантический анализ: проверяется существование + указанных таблиц и столбцов, разрешаются имена объектов и определяются + типы выражений. + + \item \emph{Оптимизация} --- синтаксическое дерево преобразуется в логический + план запроса, выраженный в терминах реляционной алгебры. Оптимизатор + исследует пространство эквивалентных логических планов и для каждого из + них рассматривает возможные физические реализации операторов. С помощью + модели стоимости оптимизатор оценивает затраты на выполнение каждого + плана и выбирает план с наименьшей оценочной стоимостью. Результатом + этого этапа является физический план исполнения. Под этим понимается + дерево физических операторов с указанием конкретных алгоритмов + соединения, методов доступа к данным и порядка операций. + + \item \emph{Исполнение физического плана} --- движок выполнения реализует + физический план, порождая потоки кортежей. Данные извлекаются из + хранилища, обрабатываются операторами плана и формируют результирующий + набор, возвращаемый пользователю. +\end{enumerate} + + +Сформулируем задачу, которую решает оптимизатор: по запросу, представленному в +форме дерева операторов реляционной алгебры, построить эквивалентный и +оптимальный, с точки зрения необходимых для исполнения ресурсов, план +исполнения. Итоговый план является деревом, вершины которого помечены +конкретными указаниями на алгоритмы, реализующими один или несколько операторов +реляционной алгебры. Эквивалентность означает, что итоговый физический план и +исходный запрос при исполнении на любом наборе данных порождают одинаковые +отношения с точностью до требуемых свойств, таких как сортировка. + +Запрос в форме реляционной алгебры также называют логическим планом, а операторы +реляционной алгебры --- логическими операторами. + +Для каждого логического оператора существует несколько возможных физических +реализаций. Например, оператор соединения \(\bowtie\) может быть реализован как +соединение вложенными циклами, хеш-соединение или соединение слиянием. +Аналогично, оператор доступа к данным может быть реализован как последовательное +сканирование таблицы или сканирование индекса. Еще одним важным примером +является эквивалентность сканирования индекса с предикатом и двух логических +операторов, примененных последовательно: доступ к данным и фильтрация по тому же +условию. + +\section{Реляционная алгебра} + +Реляционная алгебра представляет собой формальный язык для описания операций над +отношениями реляционной базы данных. В отличие от декларативного SQL, +реляционная алгебра является процедурной, так как она определяет конкретную +последовательность операций, необходимых для получения результата. + +Отношение определяется как конечное множество кортежей, каждый из которых +представляет собой упорядоченный набор значений атрибутов: + +\[ + R \subseteq \text{dom}(A_1) \times \text{dom}(A_2) \times \ldots \times \text{dom}(A_n). +\] + +\subsection{Основные операторы реляционной алгебры} + +Среди основных операторов реляционной алгебры выделяют следующие. + +\begin{enumerate} + \item \emph{Фильтрация} \(\Sel{p}{R}\) --- возвращает подмножество кортежей + отношения \(R\), удовлетворяющих предикату \(p\). Например, + \(\Sel{\text{age} > 30}{\text{Employee}}\) вернет все кортежи из + отношения Employee, для которых значение атрибута age больше 30. + \item \emph{Проекция} \(\Projection{A_1, \ldots, A_k}{R}\) --- формирует новое + отношение, содержащее только перечисленные атрибуты. + \item \emph{Декартово произведение} \(\CProd{R}{S}\) --- формирует отношение, + каждый кортеж которого является конкатенацией кортежа из \(R\) и кортежа + из \(S\), при этом результат содержит \(|\CProd{R}{S}| = |R| \cdot |S|\) + элементов. + \item \emph{Соединение} \(\InnerJoin{R}{p}{S}\) --- комбинирует кортежи двух + отношений на основании предиката \(p\). Внутреннее соединение + определяется как + \(\InnerJoin{R}{R.a = S.b}{S} = \Sel{R.a = S.b}{\CProd{R}{S}}\). + \item \emph{Объединение} \(R \cup S\) --- возвращает все кортежи, + принадлежащие хотя бы одному из двух совместимых по схеме отношений. + \item \emph{Разность} \(R - S\) --- выбирает кортежи, принадлежащие \(R\), но + отсутствующие в \(S\). + \item \emph{Агрегация} \(\Agg{G}{f}{R}\) --- разбивает отношение \(R\) на + непересекающиеся множества по набору атрибутов \(G\) и вычисляет + агрегированные значения функции \(f\) по каждому такому множеству. + Примеры функций: \COUNT, \SUM, \AVG, \MIN, \MAX. + \item \emph{Apply} \(\Apply{R}{S(t)}\) --- для каждого кортежа \(t\) + отношения \(R\) вычисляет параметризованное им отношение \(S(t)\) и + конкатенирует \(t\) с каждым кортежем результата: + \[ + \Apply{R}{S(t)} = \bigcup_{t \in R} \{t\} \times S(t). + \] + В отличие от соединения, правая сторона зависит от текущего кортежа + левой и используется для представления коррелированных подзапросов. +\end{enumerate} + +\subsection{Преобразование синтаксического дерева к реляционной алгебре}\label{sec:translation} + +Полученное после лексического и синтаксического разбора абстрактное +синтаксическое дерево преобразуется в выражение реляционной алгебры. Этот +процесс определяется структурной индукцией по дереву запроса. Введём оператор +конверсии \(\Tr{\cdot}\), сопоставляющий узлу синтаксического дерева его +алгебраическое представление. Тогда правила трансляции записываются в виде +правил вывода. Если для всех поддеревьев известны алгебраические представления, +то алгебраическое представление составной конструкции выражается через них. + +\begin{enumerate} + \item \emph{Базовый случай.} Для ссылки на таблицу \(T\) выполняется + \(\Tr{T} = T\). + \item \emph{Блок \texttt{SELECT-FROM-WHERE}.} Пусть \(\bar{T} = T_1, \ldots, + T_n\) --- ссылки на таблицы или вложенные подзапросы, \(p\) --- + предикат фильтрации, \(\bar{a} = a_1, \ldots, a_k\) --- список + выражений проекции. Тогда + \[ + \frac{\Tr{T_1} = E_1 \quad \cdots \quad \Tr{T_n} = E_n} + {\Tr{\SELECT\ \bar{a}\ \FROM\ \bar{T}\ \WHERE\ p} + = \Projection{\bar{a}}{\Sel{p}{E_1 \times \cdots \times E_n}}}. + \] + \item \emph{Группировка.} Пусть \(\bar{g} = g_1, \ldots, g_m\) --- список + группирующих атрибутов, \(\bar{f} = f_1, \ldots, f_l\) --- список + агрегирующих функций. Тогда + \[ + \frac{\Tr{T_1} = E_1 \quad \cdots \quad \Tr{T_n} = E_n} + {\Tr{\SELECT\ \bar{g}, \bar{f}\ \FROM\ \bar{T}\ \WHERE\ p\ \GROUPBY\ \bar{g}} + = \Agg{\bar{g}}{\bar{f}}{\Sel{p}{E_1 \times \cdots \times E_n}}}. + \] + \item \emph{Вложенные подзапросы.} Пусть \(Q\) --- внешний блок, а \(Q'(t)\) + --- коррелированный подзапрос, зависящий от кортежа \(t\) из + \(\Tr{Q}\). Тогда + \[ + \frac{\Tr{Q} = E \quad \Tr{Q'(t)} = E'(t)} + {\Tr{Q\ \text{с подзапросом}\ Q'} = \Apply{E}{E'(t)}}, + \] + где \(\mathcal{A}\) --- оператор Apply, который впоследствии может быть + удалён процедурой декорреляции. + \item \emph{Сортировка.} Конструкция \ORDERBY{} не выражается в виде + операторов реляционной алгебры. Вместо этого она задает требуемое + физическое свойство упорядоченности результата, учитываемое на этапе + выбора физического плана. +\end{enumerate} + +Приведём пример применения этих правил к SQL-запросу из +листинга~\ref{lst:correlated_subquery}. Трансляция этого запроса в реляционную +алгебру выполняется по следующим шагам: + +\begin{listing}[H] + \caption{Запрос с коррелированным подзапросом.} + \label{lst:correlated_subquery} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT e.name + FROM Employee e + WHERE e.salary > ( + SELECT AVG(salary) + FROM Employee + WHERE dept = e.dept + ) + \end{minted} +\end{listing} + +\begin{enumerate} + \item По базовому правилу оба вхождения таблицы остаются как есть + \(\Tr{\text{Employee}} = \text{Employee}\). + \item Внутренний подзапрос + \(Q'(e) = (\SELECT\ \AVG(\text{salary})\ \FROM\ \ldots \WHERE\ \ldots\)) + транслируется по правилу группировки, где \(\bar{g} = \varnothing\), + \(\bar{f} = \AVG(\text{salary})\): + \[ + \Tr{Q'(e)} = \Agg{\varnothing}{\AVG(\text{salary})}{\Sel{\text{dept} = e.\text{dept}}{\text{Employee}}}. + \] + Подзапрос коррелирован, так как зависит от атрибута \(e.\text{dept}\) + внешнего кортежа. + \item По правилу вложенных подзапросов результат \(\Tr{Q'(e)}\) присоединяется + к внешнему блоку оператором \(\mathcal{A}\), добавляя к каждому кортежу + столбец \(c\) с вычисленным значением средней зарплаты. + \item Оставшаяся часть внешнего запроса переводится по правилу + \texttt{SELECT-FROM-WHERE} с \(\bar{a} = (e.\text{name})\) и + \(p = (e.\text{salary} > c)\). +\end{enumerate} + +Итоговое алгебраическое выражение имеет вид +\[ + \Projection{e.\text{name}}{\Sel{e.\text{salary} > c}{\Apply{\text{Employee}\;e}{\Agg{\varnothing}{\text{AVG}(\text{salary}) \to c}{\Sel{\text{dept} = e.\text{dept}}{\text{Employee}}}}}}. +\] +Соответствующее дерево операторов изображено на +рисунке~\ref{fig:correlated_subquery_tree}. + +\begin{figure}[!htb]\centering +\begin{tikzpicture}[ + rel/.style={rectangle, draw, rounded corners=2pt, minimum width=2.2cm, + minimum height=0.8cm, align=center, font=\small}, + op/.style={rectangle, draw, fill=gray!12, minimum width=2.6cm, + minimum height=0.9cm, align=center, font=\small}, + level distance=1.15cm +] + \node[op] (proj) at (0, 6.5) {\(\Projection{e.\text{name}}{}\)}; + \node[op] (sel1) at (0, 5.2) {\(\Sel{e.\text{salary} > c}{}\)}; + \node[op] (apply) at (0, 3.9) {\(\mathcal{A}\)}; + \node[rel] (emp1) at (-2.8, 2.6) {\texttt{Employee} \(e\)}; + \node[op] (agg) at (2.8, 2.6) {\(\Agg{\varnothing}{\text{AVG}(\text{salary}) \to c}{}\)}; + \node[op] (sel2) at (2.8, 1.3) {\(\Sel{\text{dept} = e.\text{dept}}{}\)}; + \node[rel] (emp2) at (2.8, 0) {\texttt{Employee}}; + + \draw[->] (sel1) -- (proj); + \draw[->] (apply) -- (sel1); + \draw[->] (emp1) -- (apply); + \draw[->] (agg) -- (apply); + \draw[->] (sel2) -- (agg); + \draw[->] (emp2) -- (sel2); +\end{tikzpicture} +\caption{Дерево реляционной алгебры для запроса из +листинга~\ref{lst:correlated_subquery}.}% +\label{fig:correlated_subquery_tree} +\end{figure} + +Преобразование в форму реляционной алгебры необходимо по нескольким причинам. +Во-первых, это позволяет использовать формализованный набор правил +эквивалентности, который позволяют оптимизатору корректно преобразовывать план +запроса без изменения семантики. Во-вторых, алгебраическое представление +позволяет единообразно обрабатывать различные синтаксические формы SQL-запросов, +порождающие одинаковые логические планы. Наконец, разделение на логические и +физические операторы позволяет оптимизатору независимо исследовать порядок +операций и алгоритмы их реализации. + +\section{Поиск оптимального плана} + +Эквивалентность планов, исследуемых в процессе поиска оптимального, +обеспечивается применением алгебраических тождеств, например ассоциативности и +коммутативности соединений. Правила эквивалентности реляционной алгебры подробно +рассматриваются в разделе~\ref{sec:transformation_rules}. + +Оптимальность запроса принято оценивать с помощью функции стоимости +\(\mathrm{cost} \colon \mathcal{P}_L \to \mathbb{R}_{\ge 0}\)~\cite{Selinger1979}, +где \(\mathcal{P}_{L}\) --- множество эквивалентных физических планов. Это +отображение оценивает затраты на исполнение плана исходя из конкретных +реализаций алгоритмов, а также основываясь на эвристиках и статистике о данных в +отношениях. Другими словами, цель оптимизатора состоит в том, чтобы для входного +запроса найти план \(P^* \in \mathcal{P}_L\), минимизирующий \(\mathrm{cost}\): +\[ + P^* = \arg\min_{P \in \mathcal{P}_L} \mathrm{cost}(P). +\] + +Одной из подзадач в рамках построения оптимального плана является определение +порядка выполнения соединений. Благодаря правилам эквивалентости, таким как +ассоциативность и коммутативность соединения +(раздел~\ref{sec:transformation_rules}), одно и то же множество соединяемых +отношений допускает большое количество эквивалентных порядков вычисления. +Различные порядки имеют кардинально разные стоимости, ввиду особенностей +алгоритмов, реализующих соединение. Для \(n\) отношений количество таких +порядков выражается как +\(O(n! \cdot C_{n-1}) = O(\frac{(2n-2)!}{(n-1)!})\)~\cite{IntroToTheJoinOrderingProblem}, +где \(C_k\) --- \(k\)-е число Каталана. Таким образом, пространство поиска +растет экспоненциально с увеличением числа соединяемых таблиц. + +Поскольку полный перебор \(\mathcal{P}_L\) для достаточно больших \(n\) +невозможен, на практике речь идёт о поиске плана с приемлемо малой стоимостью в +определенном подмножестве пространства поиска. Именно эта задача и определяет +архитектуру современных оптимизаторов: расширяемое описание пространства поиска +через правила преобразования, эффективный алгоритм его исследования и +стоимостная модель, позволяющая отсекать заведомо неоптимальные ветви. + +Выбор плана исполнения запроса может оказывать огромное влияние на объем +требуемых вычислительных ресурсов. В качестве примера оценим количество чтений +страниц с диска для различных физических операторов. Пусть имеется три +отношения: таблица \texttt{customer} с \(|C| = 10^5\) строк, таблица +\texttt{orders} с \(|O| = 10^7\) строк и таблица \texttt{lineitem} с +\(|L| = 5 \cdot 10^7\) строк. Размер страницы --- \(8\) КБ. В одну страницу +помещается \(100\) строк \texttt{customer}, \(80\) строк \texttt{orders} и +\(50\) строк \texttt{lineitem}. Соответственно, общее число страниц составляет +\(P_C = 10^3\), \(P_O = 1{,}25 \cdot 10^5\), \(P_L = 10^6\). + +Для запроса из листинга~\ref{lst:join_query} +рассмотрим два альтернативных плана~\refImage{fig::join_plan_comparison}: +\begin{listing} + \caption{Соединение \texttt{customer}, \texttt{orders} и \texttt{lineitem}.} + \label{lst:join_query} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT * + FROM customer c + JOIN orders o ON c.c_custkey = o.o_custkey + JOIN lineitem l ON o.o_orderkey = l.l_orderkey + \end{minted} +\end{listing} + +\begin{figure}[!htb]\centering +\begin{tikzpicture}[ + rel/.style={rectangle, draw, rounded corners=2pt, minimum width=2.45cm, + minimum height=0.8cm, align=center, font=\small}, + op/.style={rectangle, draw, fill=gray!12, minimum width=4.25cm, + minimum height=1.2cm, align=center, font=\small} +] + \node[op] (p1j2) at (-4.4, 2.9) {Hash Join\\\scriptsize \(10^6\) чтений}; + \node[op] (p1j1) at (-6.5, 1.2) {Hash Join\\\scriptsize \(1{,}26 \cdot 10^5\) чтений}; + \node[rel] (p1l) at (-2.3, 1.2) {\texttt{lineitem}}; + \node[rel] (p1c) at (-7.8, -0.3) {\texttt{customer}}; + \node[rel] (p1o) at (-4.4, -0.3) {\texttt{orders}}; + + \draw[->] (p1j1) -- (p1j2); + \draw[->] (p1l) -- (p1j2); + \draw[->] (p1c) -- (p1j1); + \draw[->] (p1o) -- (p1j1); + \node[font=\small\bfseries] at (-4.4, 4.0) {План \(P_1\)}; + + \node[op] (p2j2) at (4.4, 2.9) {Nested Loop Join\\\scriptsize \(1{,}25 \cdot 10^{14}\) чтений}; + \node[op] (p2j1) at (2.3, 1.2) {Nested Loop Join\\\scriptsize \(1{,}25 \cdot 10^8\) чтений}; + \node[rel] (p2l) at (6.5, 1.2) {\texttt{lineitem}}; + \node[rel] (p2c) at (1.0, -0.3) {\texttt{customer}}; + \node[rel] (p2o) at (4.4, -0.3) {\texttt{orders}}; + + \draw[->] (p2j1) -- (p2j2); + \draw[->] (p2l) -- (p2j2); + \draw[->] (p2c) -- (p2j1); + \draw[->] (p2o) -- (p2j1); + \node[font=\small\bfseries] at (4.4, 4.0) {План \(P_2\)}; +\end{tikzpicture} +\caption{Сравнение планов для запроса из листинга~\ref{lst:join_query}.}% +\label{fig::join_plan_comparison} +\end{figure} + +\begin{itemize} + \item План \(P_1\): + \(\NJoin{(\NJoin{\texttt{customer}}{\texttt{orders}})}{\texttt{lineitem}}\) + с реализацией обоих соединений через Hash Join, для чего сначала + полностью считываются \(P_C + P_O = 1{,}26 \cdot 10^5\) страниц для + построения хеш-таблицы, после чего промежуточный результат размером + \(10^7\) строк соединяется с \texttt{lineitem}, требуя считывания ещё % + \(P_L = 10^6\) страниц. В сумме --- порядка \(1{,}126 \cdot 10^6\) + операций чтения с диска. + \item План \(P_2\): тот же запрос, но реализованный как соединение с помощью + вложенных циклов без использования индексов: для каждой строки + \texttt{customer} полностью читается таблица \texttt{orders}, для каждой + результирующей пары --- \texttt{lineitem}. Совокупный объём ввода-вывода + оценивается величиной + \(P_C \cdot P_O \cdot P_L \approx 1{,}25 \cdot 10^{14}\) операций + чтения, что на восемь порядков превышает \(P_1\). +\end{itemize} + +На еще одном примере запроса из листинга~\ref{lst:logical_transform_query} +рассмотрим влияние логических преобразований. Отношение \texttt{Emp} содержит +\(10{,}000\) кортежей, что эквивалентно \(1{,}000\) страницам. \texttt{Dept} +включает \(500\) записей и \(50\) страниц соответственно. Атрибут +\texttt{Emp.did} является внешним ключом к \texttt{Dept.did}, поэтому каждому +кортежу из \texttt{Dept} соответствует \(10{,}000 / 500 = 20\) из \texttt{Emp}. +Пусть на \texttt{Emp.did} и \texttt{Dept.dname} построены индексы, а в +промежуточную страницу помещается \(5\) кортежей. + +\begin{listing} + \caption{Пример запроса с проекцией, фильтрацией и соединением.} + \label{lst:logical_transform_query} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT DISTINCT ename + FROM Emp E JOIN Dept D ON E.did = D.did + WHERE D.dname = 'Toy' + \end{minted} +\end{listing} + +\begin{figure}[!htb]\centering +\begin{tikzpicture}[ + rel/.style={rectangle, draw, rounded corners=2pt, minimum width=2.0cm, + minimum height=0.8cm, align=center, font=\small}, + op/.style={rectangle, draw, fill=gray!12, minimum width=4.5cm, + minimum height=1.2cm, align=center, font=\small} +] + \node[font=\small\bfseries] at (-3.5, 8.8) {План \(P_1\)}; + \node[op] (p1proj) at (-3.5, 7.7) {\(\ProjectionOp{\texttt{ename}}\)}; + \node[op] (p1sig2) at (-3.5, 6.2) {\(\SelOp{\texttt{dname}=\texttt{'Toy'}}\)}; + \node[op] (p1sig1) at (-3.5, 4.7) {\(\SelOp{\texttt{Emp.did}=\texttt{Dept.did}}\)}; + \node[op] (p1cp) at (-3.5, 3.2) {\(\CProd{\texttt{Emp}}{\texttt{Dept}}\)\\\scriptsize \(50{,}050\) чтений}; + \node[rel] (p1emp) at (-5.2, 1.7) {\texttt{Emp}}; + \node[rel] (p1dept) at (-1.8, 1.7) {\texttt{Dept}}; + + \draw[->] (p1emp) -- (p1cp); + \draw[->] (p1dept) -- (p1cp); + \draw[->] (p1cp) -- (p1sig1); + \draw[->] (p1sig1) -- (p1sig2); + \draw[->] (p1sig2) -- (p1proj); + + \node[font=\small\bfseries] at (3.5, 8.8) {План \(P_2\)}; + \node[op] (p2proj) at (3.5, 7.7) {\(\ProjectionOp{\texttt{ename}}\)}; + \node[op] (p2join) at (3.5, 5.5) {Index NL Join\\\(\texttt{Emp.did}=\texttt{Dept.did}\)\\\scriptsize \(23\) чтения}; + \node[rel] (p2emp) at (1.6, 3.5) {\texttt{Emp}}; + \node[op] (p2sig) at (5.4, 3.5) {\(\SelOp{\texttt{dname}=\texttt{'Toy'}}\)\\\scriptsize \(3\) чтения}; + \node[rel] (p2dept) at (5.4, 1.8) {\texttt{Dept}}; + + \draw[->] (p2emp) -- (p2join); + \draw[->] (p2sig) -- (p2join); + \draw[->] (p2join) -- (p2proj); + \draw[->] (p2dept) -- (p2sig); +\end{tikzpicture} +\caption{Сравнение планов для запроса из + листинга~\ref{lst:logical_transform_query}.}% +\label{fig::logical_transform_plans} +\end{figure} + +Рассмотрим два плана~\refImage{fig::logical_transform_plans}: + +\begin{itemize} + \item План \(P_1\) соответствует прямому преобразованию запроса в реляционную + алгебру, выполненному по алгоритму из раздела~\ref{sec:translation}. + Если рассматривать стоимость исполнения такого плана в модели + итераторов, то каждый оператор запрашивает у дочернего следующий кортеж + через вызов \texttt{next()}, поэтому \(\sigma\) и~\(\pi\) работают в + режиме конвейера и не инициируют обращений к диску. Вся стоимость + ввода-вывода сосредоточена в операторе~\(\times\), реализованном через + вложенные циклы с \texttt{Dept} в роли внешней таблицы: \texttt{Dept} + читается один раз, а \texttt{Emp} перечитывается полностью для каждой + страницы~\texttt{Dept}: + \[ + 50 + 50 \times 1{,}000 = 50{,}050 \text{ чтений.} + \] + + \item План \(P_2\) является оптимизированным вариантом, где применено правило + проталкивания предиката и декартово произведение заменено на соединение + по предикату. Причем для доступа к таблице \texttt{Dept} применяется + сканирование по индексу, что дает всего \(3\) чтения с диска. Соединение + реализуется через вложенные циклы с использованием индекса, что в итоге дает: + \[ + 3 + (3 + 20) = 26 \text{ чтений.} + \] +\end{itemize} + +Оба приведенных примера наглядно демонстрируют, что оптимизатор отвечает за +принципиальную возможность исполнения сложных аналитических запросов за +приемлемое время. В первом случае разница между физическими планами составляет +восемь порядков: если \(P_1\) завершается за секунды, то наивный \(P_2\) +потребует десятков лет. Во втором случае даже единственное логическое +преобразование, в виде проталкивания предиката, снижает число чтений с диска в +\(1{,}900\) раз. Именно поэтому качественный оптимизатор является конкурентным +преимуществом и одной из самых важных частей СУБД. + +\section{Обзор архитектур оптимизаторов} + +Рассмотрим наиболее популярные архитектуры оптимизаторов. + +\subsection{System R} + +Оптимизатор System~R, разработанный в IBM Research в конце 1970-х +годов~\cite{Selinger1979}, стал первым стоимостным оптимизатором запросов и +заложил фундаментальные принципы, используемые во всех последующих системах. + +Архитектуру, аналогичную System~R, реализуют PostgreSQL и ранние версии IBM DB2. + +Оптимизатор, построенный по архитектуре System~R, разбивает запрос на блоки, +каждый из которых оптимизируется индивидуально. Блок запроса состоит из +конструкции \SELECT, конструкции \FROM, ссылающейся на одну или +несколько таблиц, и дерева предикатов \WHERE. Несколько блоков +появляется в случае запроса с вложенными подзапросами. + +Для каждого блока оптимизатор выполняет два основных действия. Во-первых, для +запросов к одному отношению выбирается наилучший метод доступа на основании +стоимостной модели. Во-вторых, для запросов с несколькими отношениями +определяется оптимальный порядок соединений методом восходящего динамического +программирования: сначала выбираются оптимальные планы доступа к отдельным +таблицам, затем перебираются двухтабличные соединения, трехтабличные и так +далее. + +Стоимость плана вычисляется по формуле +\(\text{COST} = \text{PageFetches} + w \cdot \text{RSICalls}\), учитывающей +ожидаемый объем операций ввода-вывода и потребление ресурсов процессора, где +\(w\) --- весовой коэффициент. + +Важным нововведением System~R стало понятие интересных порядков. Стоимость плана +зависит от непосредственных затрат на выполнение оператора и от +того, обеспечивает ли он упорядоченность результата, требуемую вышестоящими +операторами. Например, использование соединения слиянием вместо хеш-соединения +может быть выгодно, если результат должен быть отсортирован для последующей +операции \ORDERBY. + +К ограничениям архитектуры System~R относятся: + +\begin{itemize} + \item рассмотрение только левосторонних + деревьев соединений; + \item независимая оптимизация вложенных подзапросов; + \item невозможность гибкого расширения набора правил преобразования, что + необходимо при добавлении новых операторов языка SQL\@. +\end{itemize} + +Эти ограничения послужили мотивацией для создания расширяемых архитектур +оптимизаторов. + +\subsection{Volcano} + +Volcano --- расширяемый, основанный на правилах, стоимостной оптимизатор +запросов, разработанный Грефе в 1993 году~\cite{GraefeMcKenna1993}. Volcano ввел +ряд концепций, ставших основой для последующих расширяемых оптимизаторов: +физические свойства выражений, обеспечивающие правила, структуру данных Memo и +нисходящий алгоритм поиска на основе динамического программирования. + +Volcano разделяет поиск на две фазы: + +\begin{itemize} + \item \emph{фаза генерации} --- оптимизатор применяет правила трансформации + для порождения всех эквивалентных логических выражений. Применяется + рекурсивный нисходящий обход дерева: для каждого выражения сначала + исследуются дочерние выражения, затем применяются правила трансформации + к текущему выражению. Все порожденные выражения кэшируются в структуре + данных Memo для предотвращения дублирования. + + \item \emph{фаза стоимостного анализа} --- по завершении генерации всех + эквивалентных логических выражений оптимизатор ищет наилучший физический + план. Для каждого логического выражения применяются правила реализации, + порождающие физические операторы. Стоимость каждого физического плана + оценивается рекурсивно, и сохраняется наилучший найденный план. +\end{itemize} + +Поиск осуществляется нисходящим динамическим программированием с использованием +стратегии ветвей и границ: текущая верхняя граница стоимости передается при +рекурсивных вызовах и используется для отсечения заведомо неоптимальных планов. + +Основным недостаткам Volcano является полное раскрытие всех классов +эквивалентности на фазе генерации до начала стоимостного анализа, что приводит к +избыточным вычислениям. + +\section{Архитектура Cascades} + +Cascades --- это расширяемая архитектура оптимизатора запросов, являющаяся +логическим продолжением идеи Volcano~\cite{Graefe1995}. + +Основными объектами Cascades являются: + +\begin{itemize} + \item выражение --- дерево или фрагмент дерева, корнем которого является + логический либо физический оператор; + \item групповое выражение --- выражение, дочерние узлы которого являются + ссылками на группы Memo; + \item группа --- множество логически эквивалентных выражений, возвращающих + один и тот же результат; + \item правило трансформации --- правило, преобразующее логическое выражение в + логически эквивалентное выражение; + \item правило реализации --- правило, преобразующее логический оператор в + физический оператор; + \item физическое свойство --- требование относительно порядка, распределения, + разбиения или уникальности результата; + \item задача --- элементарная единица работы оптимизатора, например + исследование группы, применение правила или оптимизация выражения под + заданное свойство. +\end{itemize} + +Важным принципом данной архитектуры является то, что правила являются объектами +первого класса. Они определяются условиями применимости и собственно алгоритмом +применения. Предполагается, что система легко расширяется добавлением новых +правил, потому что все правила наследуются от общего интерфейса и имеют +одинаковую структуру. + +\subsection{Группы эквивалентности и выражения} + +Группа в Memo представляет собой множество выражений, которые эквивалентны с +точки зрения логического результата. Например, для отношений \(A\), \(B\) и +\(C\) выражения \( \NJoin{A}{(\NJoin{B}{C})} ; \qquad{} \NJoin{(\NJoin{A}{B})}{C} \) +при выполнении условий ассоциативности соединения принадлежат одной группе, +поскольку возвращают одинаковый результат. Причем физические реализации этих +выражений могут отличаться, а стоимость может зависеть от кардинальности +промежуточных результатов. + +Групповое выражение хранит оператор и ссылки на дочерние группы. Например, +выражение \(Join(G_1, G_2)\) не указывает конкретное дерево для левого и правого +входов, а ссылается на группы \(G_1\) и \(G_2\), каждая из которых может +содержать множество альтернатив. Поэтому одно групповое выражение кодирует +комбинацию многих конкретных деревьев, храня эту информацию компактно. + +\subsection{Структура Memo} + +Memo является основной структурой данных Cascades. Оно выполняет сразу +несколько функций: устраняет дублирующиеся выражения, хранит альтернативы, +фиксирует результаты оптимизации под разными физическими свойствами и обеспечивает +точку синхронизации для задач поиска. + +Группа в Memo включает следующую информацию: + +\begin{itemize} + \item уникальный идентификатор группы; + \item список логических выражений; + \item список физических выражений; + \item логические свойства группы; + \item статистику и оценку кардинальности; + \item состояние исследования группы. +\end{itemize} + +Выражение, в свою очередь, содержит сам оператор, массив идентификаторов +дочерних групп. Для физического выражения дополнительно могут храниться +предоставляемые физические свойства, например сортировка выходного потока, и +рассчитанная стоимость исполнения этого оператора. + +\begin{listing} + \caption{Пример запроса.} + \label{lst:query} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT * FROM A JOIN B ON p_1 JOIN C ON p_2; + \end{minted} +\end{listing} + +На рисунке~\ref{fig:memo-structure} показан пример Memo для запроса из +листинга~\ref{lst:query}. Например, группа \(G_5\) представляет результат +соединения трех отношений, в ней находятся два логически эквивалентных выражения +с разной ассоциацией соединений и четыре физические реализации для них. Входными +данными для операторов являются группы, поэтому общие подвыражения не +дублируются. + +\begin{figure}[H] + \centering + \begin{tikzpicture}[ + scale=0.85, transform shape, + group/.style={draw, rounded corners, text width=4.8cm, inner sep=5pt}, + arrow/.style={-{Stealth[length=2.5mm]}, thick}, + altarrow/.style={-{Stealth[length=2.5mm]}, thick, dashed} + ] + \node[group] (g5) at (6,0) { + \textbf{Группа G5}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Join(G4,G3)\\ + Join(G1,G6)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + HashJoin(G4,G3)\\ + NestedLoopJoin(G4,G3)\\ + HashJoin(G1,G6)\\ + NestedLoopJoin(G1,G6) + }; + \node[group] (g4) at (3,-6) { + \textbf{Группа G4}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Join(G1,G2)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + HashJoin(G1,G2)\\ + NestedLoopJoin(G1,G2) + }; + \node[group] (g6) at (9,-6) { + \textbf{Группа G6}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Join(G2,G3)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + HashJoin(G2,G3)\\ + NestedLoopJoin(G2,G3) + }; + \node[group] (g1) at (0,-12) { + \textbf{Группа G1}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Scan(A)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + SeqScan(A) + }; + \node[group] (g2) at (6,-12) { + \textbf{Группа G2}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Scan(B)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + SeqScan(B) + }; + \node[group] (g3) at (12,-12) { + \textbf{Группа G3}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Scan(C)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + SeqScan(C) + }; + + \draw[arrow] (g5) -- (g4); + \draw[arrow, rounded corners=5pt] + (g5.east) -- (15.5,0) -- (15.5,-12) -- (g3.east); + \draw[altarrow] (g5) -- (g6); + \draw[altarrow, rounded corners=5pt] + (g5.west) -- (-3.5,0) -- (-3.5,-12) -- (g1.west); + \draw[arrow] (g4) -- (g1); + \draw[arrow] (g4) -- (g2); + \draw[arrow] (g6) -- (g2); + \draw[arrow] (g6) -- (g3); + \end{tikzpicture} + \caption{Пример структуры Memo.}% + \label{fig:memo-structure} +\end{figure} + +При добавлении нового выражения Memo выполняет проверку на дубликаты. Если +выражение уже существует в некоторой группе, оно не добавляется повторно. Если +алгоритм в процессе работы обнаруживает эквивалентность двух ранее различных +групп, то они могут быть объединены. При этом требуется обновление ссылок и +состояния правил. + +\subsection{Алгоритм поиска} + +Cascades организует оптимизацию как выполнение набора задач. Задача может +исследовать группу, исследовать выражение, применить правило, реализовать +логический оператор, оптимизировать дочернюю группу под заданное физическое +свойство или построить вспомогательный оператор для обеспечения свойства. Такой +подход позволяет гибко управлять порядком работы и откладывать дорогостоящие +действия до тех пор, пока они не станут необходимыми, а также разбивать поиск на +несколько этапов и распараллеливать исполнение независимых веток. При этом +однопоточные реализации хранят задачи в обычной очереди. + +На рисунке~\ref{fig:cascades-tasks} показана схема взаимодействия задач в +оптимизаторе, а на +листингах~\ref{alg:cascades-search}-\ref{alg:cascades-search-3} приведен +псевдокод алгоритма поиска в архитектуре Cascades~\cite{DingNarasayya2024}. + +Алгоритм реализуется в виде управляемого стеком поиска в пространстве +эквивалентных планов: сначала запускается оптимизация корневой группы, +\textsc{OptimizeGroup} в свою очередь исследует группу, а затем оптимизирует +каждое ее логическое выражение. Исследование выполняется через +\textsc{ExploreGroup} и \textsc{ExploreExpression}, которые сохраняют в Memo +новые логические выражения и группы с помощью правил преобразования. После этого +\textsc{OptimizeExpression} применяет правила реализации, превращая логические +выражения в физические. Для каждого физического варианта +\textsc{ApplyImplementation} проверяет локальную стоимость и передает план в +\textsc{OptimizeInputs}, где последовательно оптимизируются дочерние группы, +накапливается полная стоимость и обновляется информация о лучшем плане. Параметр +\texttt{limit} используется для отсечения заведомо дорогих вариантов. + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{cascades.pdf} +\caption{Схема взаимодействия задач в оптимизаторе Cascades}% +\label{fig:cascades-tasks} +\end{figure} + +\section{Правила трансформации и реализации}\label{sec:transformation_rules} + +Правило в оптимизаторе Cascades состоит из образца, условия применимости и +алгоритма применения. Образец описывает форму логического выражения, к которому +может быть применено правило. Условие применимости проверяет дополнительные +требования: отсутствие внешних ссылок, тип соединения, сохранение семантики +\texttt{NULL}, совместимость свойств и другие ограничения. + +Формально правило записывается в виде +\[ + r : \TransRule{pattern(e)}{condition(e)}{substitute(e)}, +\] +где \(e\) --- исходное выражение, \(pattern\) --- структурный образец, +\(condition\) --- предикат применимости, \(substitute\) --- выражение-замена. +Для правил трансформации результатом применения является новое логическое +выражение в группе, а для правил реализации физическое выражение в той же +группе. + +\subsection{Правила трансформации} + +Правила трансформации задают эквивалентные преобразования логических выражений и +тем самым определяют пространство планов, исследуемое оптимизатором. Наиболее +важные из них представлены в таблице~\ref{tbl:transformation_rules}. + +\begin{Definition} +Null-отбрасывающим называется предикат, который при замене всех атрибутов +соответствующей стороны на \texttt{NULL} принимает значение, отличное от +\texttt{TRUE}. +\end{Definition} + +\begin{Definition} +Конъюнкты \(\theta_1 \land \theta_2\) называются перераспределяемыми, если их +можно разбить на две группы \(\theta'_1\) и \(\theta'_2\) такие, что атрибуты +\(\theta'_2\) принадлежат \(attrs(S) \cup attrs(T)\), а конъюнкты с атрибутами +из \(attrs(R)\) остаются в \(\theta'_1\). +\end{Definition} + +\begin{Definition} +Функция агрегации называется разделяемой, если она представима композицией +частичного агрегата и финального комбинатора. Например, \SUM{} раскладывается в +сумму частичных сумм, \AVG{} --- в пару \SUM{} и \COUNT{}. +\end{Definition} + +\subsection{Правила реализации} + +Правила реализации переводят логические операторы в физические. Используемые в +настоящей работе правила собраны в таблице~\ref{tbl:implementation_rules}. + +\subsection{Обеспечивающие операторы} + +Физические свойства в Cascades обрабатываются через требуемые и предоставляемые +свойства. Если родительский оператор требует вход, отсортированный по атрибуту +\(a\), а выбранный дочерний план такого порядка не предоставляет, оптимизатор +может добавить обеспечивающий оператор \Sort. + +Обеспечивающие операторы не изменяют логический результат, но меняют физические +свойства и увеличивают стоимость. Поэтому они рассматриваются наравне с другими +физическими альтернативами. Например, план с хеш-соединением и последующей +сортировкой может конкурировать с планом на основе соединения слиянием, которое +сохраняет порядок результата. + +\section{Метод ветвей и границ} + +Метод ветвей и границ используется для сокращения пространства поиска и, как +следствие, неасимптотического улучшения скорости работы алгоритма. В контексте +Cascades ветвями являются альтернативные выражения и комбинации физических +реализаций дочерних групп, а границей является текущая лучшая стоимость плана +для заданной группы и требуемых физических свойств. Если нижняя оценка стоимости +частично построенного плана уже превышает текущую лучшую, дальнейшее +исследование по этой ветке не имеет смысла. + +Пусть для группы \(G\) и требуемого свойства \(P\) уже найден план стоимости +\(C^*\). Рассматривается физическое выражение \(e\), имеющее локальную стоимость +\(C_{local}(e)\) и дочерние группы \(G_1, \ldots, G_k\). Если даже минимально +возможная оценка дочерних групп приводит к неравенству + +\[ + C_{local}(e) + \sum_{i=1}^{k} LB(G_i, P_i) \geq C^*, +\] + +то выражение \(e\) может быть отсечено. Здесь \(LB\) обозначает нижнюю границу +стоимости оптимизации дочерней группы под требуемым свойством \(P_i\). Чем +точнее нижняя граница, тем эффективнее отсечение. + +На практике в качестве \(LB(G_i, P_i)\) принимается лучшая стоимость, найденная +для пары \((G_i, P_i)\) на текущий момент работы алгоритма. Поскольку +оптимизация группы является рекурсивным процессом, к моменту отсечения для +\((G_i, P_i)\) уже могут быть найдены некоторые планы, и их минимальная +стоимость служит нижней границей. Если же группа \(G_i\) под свойством \(P_i\) +ещё не рассматривалась, используется тривиальная граница \(LB = 0\), или, при +условии наличия минимальной стоимости на кортеж, можно принимать +\(LB(G_i, P_i) = |G_i| \cdot \mathit{minCostPerRow}\). + +Требуемые свойства дочерних групп \(P_i\) определяются оператором выражения \(e\) +совместно с требованием родителя \(P\). Каждый физический оператор описывает, какие +свойства он ожидает от своих входов: например, оператор сортирующего слияния требует +упорядоченности обоих входов по соответствующим ключам, тогда как хеш-соединение не +предъявляет требований к порядку. Таким образом, при спуске по дереву поиска каждый +оператор транслирует глобальное требование \(P\) в конкретные требования \(P_1, +\ldots, P_k\) к своим дочерним группам. + +\subsection{Оценка кардинальности} + +Оценка кардинальности является процессом предсказания числа кортежей, +возвращаемых оператором. + +Для выборки используется селективность предиката \(sel(p)\), после чего +кардинальность результата оценивается как + +\[ + |\Sel{p}{R}| = |R| \cdot sel(p). +\] + +Для соединения на основе предиката равенства независимых атрибутов часто +применяется эвристика: + +\[ + |\InnerJoin{R}{R.a = S.b}{S}| \approx + \frac{|R| \cdot |S|}{\max(NDV(R.a), NDV(S.b))}, +\] + +где \(NDV\) --- число различных значений атрибута. Такая оценка удобна, но может +быть грубой, если данные имеют корреляции, сильный перекос или сложные +зависимости между атрибутами. Экспериментальные исследования показывают, что +ошибки кардинальности часто являются одной из основных причин выбора неудачных +планов~\cite{leis2015good}. + +Для повышения качества оценок используются гистограммы, статистики по +многоколоночным ключам, сведения об уникальности, функциональные зависимости, +ограничения на схему, а в современных исследованиях также модели машинного +обучения. + +\subsection{Оценка стоимости} + +Функция стоимости сопоставляет физическому плану численную оценку затрат на его +исполнение. Классическим считается представление стоимости в виде взвешенной +суммы операций чтения и записи страниц на диск, а также числа обработанных +кортежей и операций над ними~\cite{GraefeMcKenna1993,Graefe1995}: + +\[ + Cost(p) = w_{io} \cdot IO(p) + w_{cpu} \cdot CPU(p). +\] + +Коэффициенты \(w_{io}\), \(w_{cpu}\) зависят от конкретной аппаратной +конфигурации. + +Стоимость является аддитивной и вычисляется рекурсивно по дереву физического +плана: + +\[ + Cost(node) = LocalCost(node) + \sum_{child \in children(node)} Cost(child). +\] + +Локальная стоимость оператора определяется его типом и кардинальностью его +входов. Пусть \(|R|\) --- кардинальность отношения \(R\), \(b\) --- блочный +фактор (число кортежей на странице), тогда \(P(R) = \lceil|R|/b\rceil\) --- +число страниц, занимаемых \(R\); \(c_\theta\) --- стоимость вычисления +предиката \(\theta\) на одной паре кортежей; \(\sigma_\theta\) --- +селективность предиката \(\theta\) (доля кортежей входа, удовлетворяющих +\(\theta\)); \(M\) --- число страниц буфера, доступных оператору. Формулы +локальной стоимости физических операторов приведены в +таблице~\ref{tbl:local-cost}. + +\begin{longtable}{|l|c|c|} + \caption{Локальная стоимость физических операторов.}\label{tbl:local-cost}\\ + \hline + Оператор & \(IO(op)\) & \(CPU(op)\) \\ + \hline + \endfirsthead + \hline + \endfoot + + \(\SeqScan(T)\) & \(P(T)\) & \(|T|\) \\ \hline + \(\IndexScan(T, \theta)\) & \(\lceil \sigma_\theta \cdot P(T) \rceil\) & \(\sigma_\theta \cdot |T|\) \\ \hline + \(\FilterOp(R, \theta)\) & \(0\) & \(|R| \cdot c_\theta\) \\ \hline + \(\ProjOp(R)\) & \(0\) & \(|R|\) \\ \hline + \(\NestedLoopJoin(L, R, \theta)\) & \(P(L) \cdot (1 + P(R))\) & \(|L| \cdot |R| \cdot c_\theta\) \\ \hline + \(\NestedLoopCrossJoin(L, R)\) & \(P(L) \cdot (1 + P(R))\) & \(|L| \cdot |R|\) \\ \hline + \(\HashJoin(L, R, \theta)\) & \(P(L) + P(R)\) & \(|L| + |R| \cdot (1 + c_\theta)\) \\ \hline + \(\MergeJoin(L, R)\) & \(P(L) + P(R)\) & \(|L| + |R|\) \\ \hline + \(\Sort(R)\) & \(2 P(R) \cdot \lceil \log_M P(R) \rceil\) & \(|R| \cdot \log_2 |R|\) +\end{longtable} + +Для операторов \(\FilterOp\) и \(\ProjOp\) стоимость ввода-вывода равна \(0\), потому что это конвейерные операторы, которые не используют диск. + +\section{Дифференциальный анализ физических планов} + +Множество правил трансформации и реализации, используемых в +оптимизаторах запросов, изучено довольно хорошо и описано в +академической литературе. Вместе с тем условия активации правил +существенно различаются от системы к системе. Многие реализации +оптимизаторов упускают возможности для оптимизации из-за слишком +строгих условий активации правил или отсутствия определенных правил +в их наборе. + +Данная проблема особенно актуальна при разработке нового оптимизатора: после +реализации базового набора правил возникает вопрос о полноте и корректности +этого набора по сравнению с устоявшимися и проверенными временем промышленными +системами. Такие системы аккумулировали обратную связь от пользователей и +уточняли условия активации своих правил в течение многих лет. Не имея такого +преимущества сложно добиться отличного качества построенных планов. + +\subsection{Предлагаемый метод} + +Для поиска несоответствий между поведением реализуемой системы и внешней +эталонной системы предлагается применить дифференциальный анализ планов. + +Первым шагом нужно сформировать несколько наборов данных, состоящих из схем +таблиц и их наполнения. Разные данные позволяют всесторонне исследовать правила +активации, потому что они зависят от кардинальности отношений, наличия индексов +и т.п. + +Далее, по заданным схемам генерируются случайные SQL-запросы различной структуры +и сложности. Такой подход исключает тривиальные планы и приближает условия +работы оптимизатора к реальным нагрузкам при дальнейшем построении планов +запросов. + +Следующим шагом сгенерированные запросы транспилируются в диалекты SQL целевых +промышленных СУБД. В качестве эталонных систем рассматриваются PostgreSQL, +DuckDB, Umbra и Microsoft SQL Server. Эти системы являются зрелыми, имеют +документированный формат вывода планов и представляют разные архитектурные +подходы. Затем для получения физических планов исполняется аналог +\texttt{EXPLAIN ANALYZE}. + +Полученные физические планы промышленных СУБД конвертируются в формат +сериализованного плана разрабатываемого оптимизатора. Современные СУБД +предоставляют вывод планов в виде XML или JSON, что позволяет легко реализовать +их разбор. Для конвертации используется отображение между физическими +операторами разрабатываемой системы и операторами эталонной СУБД. Например, +\texttt{Hash Match} в SQL Server отображается в \HashJoin{} в разрабатываемой +системе, \texttt{Clustered Index Scan} --- в \IndexScan{} и так далее. +Восстановление предикатов, ключей соединения и выражений проекции выполняется +путем разбора соответствующих полей плана, причем не нужно поддерживать весь +синтаксис предикатов, так как генератор запросов и схемы данных ограничивают +множество допустимых выражений в планах. + +Корректность конвертации выполняется по построению, но дополнительно проверяется +путем исполнения исходного плана на внешней системе и сконвертированного плана +на разрабатываемой системе с последующим сравнением полученных результатов при +фиксированной схеме и различных вариантах сгенерированных для нее данных. + +Конвертированные планы сравниваются с планами, порождаемыми разрабатываемым +оптимизатором на исходных запросах, с целью определить, мог ли существующий +набор правил разрабатываемого оптимизатора породить план, эквивалентный плану +промышленной системы. Стоимостные модели и реализации операторов в разных СУБД +различаются, поэтому план, оптимальный для эталонной системы, не обязательно +будет оптимальным для разрабатываемой. По этоу причине сравнение направлено на +исследование полноты пространства поиска разрабатываемого оптимизатора, и не +предполагает сравнение эффективности планов. + +Для корректности структурного сравнения необходимо полное исследование +пространства эквивалентных планов. Определение требуемых изменений лежит на +разработчике системы, поэтому найденный запрос и примеры планов сохраняются для +последующего анализа. В дальнейшем, полученные после конвертации ``эталонные'' +планы можно использовать как тесты для оптимизатора, которые будут успешно +выполняться после уточнения соответвующих правил активации. + +\subsection{Поиск ближайшего плана} + +В случае невозможности построить ``эталонный'' план разрабатываемым +оптимизатором, предлагается искать ближайший план, т.е. план из пространства +поиска разрабатываемого оптимизатора, максимально близкий к плану промышленной +системы. При этом метрика расстояния между планами может быть определена как +расстояние редактирования деревьев. + +Анализ ближайшего плана может помочь упростить выявление трансформаций, которые +разрабатываемый план по какой-то причине не применил. + +\subsection{Область применения} + +Предлагаемый метод дифференциального анализа применим для сравнения как с +проприетарными системами, так и с СУБД с открытым исходным кодом, позволяя +уточнять условия применения правил. + +Систематическое применение этого подхода позволяет построить итеративный процесс +совершенствования оптимизатора: + +\begin{itemize} + \item генерация запросов; + \item запуск и сбор планов; + \item приведение к единому виду, сравнение и выявление расхождений; + \item добавление или корректировка правил; + \item повторное сравнение. +\end{itemize} + +Таким образом, дифференциальный анализ обеспечивает управляемое и измеримое +улучшение качества оптимизатора и позволяет новым или открытым оптимизаторам +быстрее достигать качества устоявшихся промышленных оптимизаторов. + +% {\color{red} TODO: описать как нормализовать и т.п.} + +\renewcommand\refname{СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ} +\clearpage +{\catcode`"\active\def"{\relax} + \addcontentsline{toc}{section}{\protect\numberline{}\refname}% + \printbibliography +} +\newpage + +\settocdepth{section} +\anonsection{ПРИЛОЖЕНИЕ А} +\vspace{-30pt} + +\begin{algorithm}[H] + \caption{Алгоритм поиска (часть 1).}% + \label{alg:cascades-search} + \begin{algorithmic}[1] + + \Procedure{Optimize}{$root$} + \State Push task $\Call{OptimizeGroup}{root.group, \bot}$ onto task stack + \While{task stack is not empty} + \State Pop and execute next task + \EndWhile + \State \Return $best\_plan[root.group]$ + \EndProcedure + + \Procedure{OptimizeGroup}{$group, limit$} + \If{$best\_cost[group]$ exists \textbf{and} ($limit = \bot$ \textbf{or} $best\_cost[group] < limit$)} + \State \Return + \EndIf + \If{$group$ is not explored} + \State Push task $\Call{OptimizeGroup}{group, limit}$ onto task stack + \State Push task $\Call{ExploreGroup}{group, limit}$ onto task stack + \State \Return + \EndIf + \For{each logical expression $expr \in group$} + \State Push task $\Call{OptimizeExpression}{expr, limit}$ onto task stack + \EndFor + \EndProcedure + + \Procedure{ExploreGroup}{$group, limit$} + \State Mark $group$ as explored + \For{each logical expression $expr \in group$} + \State Push task $\Call{ExploreExpression}{expr, limit}$ onto task stack + \EndFor + \EndProcedure + \end{algorithmic} +\end{algorithm} + +\begin{algorithm}[H] + \caption{Алгоритм поиска (часть 2).}% + \label{alg:cascades-search-2} + \begin{algorithmic}[1] + \Procedure{ExploreExpression}{$expr, limit$} + \For{each applicable transformation rule $r$} + \State Push task $\Call{ApplyTransformation}{r, expr, limit}$ onto task stack + \EndFor + \For{each child group $child \in \Call{Children}{expr}$} + \If{$child$ is not explored} + \State Push task $\Call{ExploreGroup}{child, limit}$ onto task stack + \EndIf + \EndFor + \EndProcedure + + \Procedure{OptimizeExpression}{$expr, limit$} + \For{each applicable implementation rule $r$} + \State Push task $\Call{ApplyImplementation}{r, expr, limit}$ onto task stack + \EndFor + \For{each child group $child \in \Call{Children}{expr}$} + \If{$child$ is not explored} + \State Push task $\Call{ExploreGroup}{child, limit}$ onto task stack + \EndIf + \EndFor + \EndProcedure + + \Procedure{ApplyTransformation}{$rule, expr, limit$} + \State $new\_expr \leftarrow \Call{Apply}{rule, expr}$ + \State Push task $\Call{ExploreExpression}{new\_expr, limit}$ onto task stack + \EndProcedure + + \Procedure{ApplyImplementation}{$rule, expr, limit$} + \State $phys \leftarrow \Call{Apply}{rule, expr}$ + \State $lc \leftarrow \Call{CalcLocalCost}{phys}$ + \If{$limit \neq \bot$ \textbf{and} $lc \geq limit$} + \State \Return + \EndIf + \State $child\_limit \leftarrow (limit = \bot) \mathbin{?} \bot \mathbin{:} limit - lc$ + \State Push task $\Call{OptimizeInputs}{phys, child\_limit, 0}$ onto task stack + \EndProcedure + \end{algorithmic} +\end{algorithm} + +\begin{algorithm}[H] + \caption{Алгоритм поиска (часть 3).}% + \label{alg:cascades-search-3} + \begin{algorithmic}[1] + \Procedure{OptimizeInputs}{$expr, limit, i$} + \State $children \leftarrow \Call{Children}{expr}$ + \If{$i \geq |children|$} + \State $total \leftarrow local\_cost[expr] + accum\_child\_cost[expr]$ + \If{$best\_cost[expr.group]$ does not exist \textbf{or} $total < best\_cost[expr.group]$} + \State $best\_cost[expr.group] \leftarrow total$ + \State $best\_plan[expr.group] \leftarrow expr$ + \EndIf + \State \Return + \EndIf + \State $child \leftarrow children[i]$ + \State Push task: \textbf{if} $best\_cost[child]$ exists \textbf{then} accumulate child cost \textbf{and} push $\Call{OptimizeInputs}{expr, \ldots, i+1}$ + \State Push task $\Call{OptimizeGroup}{child, limit}$ onto task stack + \EndProcedure + \end{algorithmic} +\end{algorithm} + +\newpage{} + +\begin{scriptsize} +\begin{longtable}{|c|p{2.6cm}|c|p{4.2cm}|} + \caption{Правила трансформации.}\label{tbl:transformation_rules}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endfirsthead + \multicolumn{4}{l}{\small Продолжение таблицы~\ref{tbl:transformation_rules}}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endhead + \hline + \endfoot + \hline + \endlastfoot + + 1 & Коммутативность соединения & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\text{внутреннее или полное внешнее соединение}}{\InnerJoin{S}{\theta}{R}}\) & + Перестановка входов соединения. Позволяет рассмотреть оба порядка вычисления. \\ + \hline + + 2 & Ассоциативность соединения & + \(\TransRule{\InnerJoin{(\InnerJoin{R}{\theta_1}{S})}{\theta_2}{T}}{\substack{\text{оба соединения внутренние;}\\ \theta_1 \land \theta_2 \text{ перераспределяемы}}}{\InnerJoin{R}{\theta'_1}{(\InnerJoin{S}{\theta'_2}{T})}}\) & + Изменяет группировку цепочки соединений. Открывает альтернативные порядки и уменьшает размер промежуточных результатов. \\ + \hline + + 3 & Проталкивание фильтра в левый вход & + \(\TransRule{\Sel{p}{\InnerJoin{R}{\theta}{S}}}{\substack{attrs(p) \subseteq attrs(R); \\ \text{внутреннее соединение или сохраняющий вход}}}{\InnerJoin{\Sel{p}{R}}{\theta}{S}}\) & + Применяет селективный предикат до соединения, уменьшая кардинальность входа. \\ + \hline + + 4 & Проталкивание фильтра в правый вход & + \(\TransRule{\Sel{p}{\InnerJoin{R}{\theta}{S}}}{\substack{attrs(p) \subseteq attrs(S); \\ \text{внутреннее соединение или сохраняющий вход}}}{\InnerJoin{R}{\theta}{\Sel{p}{S}}}\) & + Симметричный случай для правого входа. \\ + \hline + + 5 & Перенос фильтра в предикат соединения & + \(\TransRule{\Sel{p}{\InnerJoin{R}{\theta}{S}}}{\substack{\text{соединение внутреннее;} \\ attrs(p) \subseteq attrs(R) \cup attrs(S)}}{\InnerJoin{R}{\theta \land p}{S}}\) & + Объединяет фильтр и предикат соединения. Может позволить применить хеш-соединение или соединение слиянием. \\ + \hline + + 6 & Превращение декартова произведения в соединение & + \(\TransRule{\Sel{p}{\CProd{R}{S}}}{\top}{\InnerJoin{R}{p}{S}}\) & + Частный случай правила~5. \\ + \hline + + 7 & Поднятие фильтра над соединением & + \(\TransRule{\InnerJoin{\Sel{p}{R}}{\theta}{S}}{\substack{attrs(p) \subseteq attrs(R); \\ \text{внутреннее соединение или сохраняющая сторона}}}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + Переносит фильтр выше соединения для объединения с предикатами родительских операторов. \\ + \hline + + 8 & Разбиение фильтра на конъюнкты & + \(\TransRule{\Sel{p_1 \land \ldots \land p_n}{R}}{\top}{\Sel{p_1}{\ldots \Sel{p_n}{R} \ldots}}\) & + Позволяет обрабатывать каждый конъюнкт независимо. \\ + \hline + + 9 & Проталкивание проекции через соединение & + \(\TransRule{\Projection{L}{\InnerJoin{R}{\theta}{S}}}{\substack{L_R \cup J_R \subsetneq attrs(R) \\ \text{или}\ L_S \cup J_S \subsetneq attrs(S)}}{\Projection{L}{\InnerJoin{\Projection{L_R \cup J_R}{R}}{\theta}{\Projection{L_S \cup J_S}{S}}}}\) & + Удаляет неиспользуемые атрибуты до соединения, сокращая ширину кортежей. \\ + \hline + + 10 & Левое внешнее соединение во внутреннее & + \(\TransRule{\Sel{p}{\LOJ{R}{\theta}{S}}}{p\ \text{null-отбрасывающий по}\ attrs(S)}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + Открывает для подплана правила, ограниченные внутренним соединением. \\ + \hline + + 11 & Правое внешнее соединение во внутреннее & + \(\TransRule{\Sel{p}{\ROJ{R}{\theta}{S}}}{p\ \text{null-отбрасывающий по}\ attrs(R)}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + Симметричный случай. \\ + \hline + + 12 & Полное внешнее соединение во внутреннее & + \(\TransRule{\Sel{p}{\FOJ{R}{\theta}{S}}}{p\ \text{null-отбрасывающий по}\ attrs(R)\ \text{и}\ attrs(S)}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + Требует null-отбрасывания с обеих сторон. \\ + \hline + + 13 & Проталкивание агрегации ниже соединения & + \(\TransRule{\Agg{G}{F}{\InnerJoin{R}{R.k = S.k}{S}}}{\substack{F\text{ разделяема;}\\ \text{внутреннее соединение по равенству}}}{\Agg{G}{F'}{\InnerJoin{\AggP{G_R \cup \{R.k\}}{F_R}{R}}{R.k=S.k}{S}}}\) & + Снижает кардинальность входа частичной группировкой. \(G_R = G \cap attrs(R)\); \(F_R\) --- частичные агрегаты по атрибутам \(R\); \(F'\) --- финальные комбинаторы. \\ + \hline + + 14 & Перестановка агрегации и соединения & + \(\TransRule{\Agg{G}{F_R}{\InnerJoin{R}{R.k=S.k}{S}}}{\substack{\text{внутреннее соединение по равенству;}\\ F_R\subseteq attrs(R);\ R.k \in G; \\ S.k\text{ уникален и полон}}}{\InnerJoin{\Agg{G}{F_R}{R}}{R.k=S.k}{S}}\) & + Полностью переносит агрегацию до соединения. \(S.k\) уникален: соединение не дублирует строки \(R\). \(S.k\) полон относительно \(R.k\): соединение не теряет строки \(R\). \\ + \hline +\end{longtable} + +\newpage{} + +\begin{longtable}{|c|p{2.8cm}|c|p{4.5cm}|} + \caption{Правила реализации.}\label{tbl:implementation_rules}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endfirsthead + \multicolumn{4}{l}{\small Продолжение таблицы~\ref{tbl:implementation_rules}}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endhead + \hline + \endfoot + \hline + \endlastfoot + + 1 & Последовательное сканирование & + \(\TransRule{\LogicalGet}{\top}{\SeqScan}\) & + Применимо к любому отношению. \\ + \hline + + 2 & Сканирование по индексу & + \(\TransRule{\LogicalGet}{\text{есть совместимый индекс}}{\IndexScan}\) & + Индекс должен быть совместим с предикатом или требуемым порядком. \\ + \hline + + 3 & Соединение вложенными циклами & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\top}{\NestedLoopJoin}\) & + Применимо при любом виде предиката. Стоимость высока без доступа по индексу к внутреннему отношению. \\ + \hline + + 4 & Хеш-соединение & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\theta\ \text{содержит равенство по ключам}}{\HashJoin}\) & + Хеш-таблица строится по ключам равенства. \\ + \hline + + 5 & Соединение слиянием & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\text{входы упорядочены по ключам соединения}}{\MergeJoin}\) & + Требуемый порядок может быть обеспечен явным оператором \Sort{}. \\ + \hline + + 6 & Хеш-агрегация & + \(\TransRule{\Agg{G}{F}{R}}{\top}{\sqlkw{HashAggregate}}\) & + Не требует упорядоченности входа. Потребляет память пропорционально числу групп. \\ + \hline + + 7 & Потоковая агрегация & + \(\TransRule{\Agg{G}{F}{R}}{\text{вход упорядочен по } G}{\sqlkw{StreamAggregate}}\) & + Эффективна при уже отсортированном входе. Может быть выгодна, если требуемый порядок нужен и родительскому оператору. \\ + \hline +\end{longtable} +\end{scriptsize} + +\end{document} diff --git a/report/vkr.pdf b/report/vkr.pdf new file mode 100644 index 0000000..543b166 Binary files /dev/null and b/report/vkr.pdf differ diff --git a/report/vkr.tex b/report/vkr.tex new file mode 100644 index 0000000..40e371a --- /dev/null +++ b/report/vkr.tex @@ -0,0 +1,3305 @@ +% !TeX TXS-program:bibliography = txs:///biber +% chktex-file 1 +\documentclass[14pt, russian]{scrartcl} +\let\counterwithout\relax +\let\counterwithin\relax + +\usepackage{float} +\usepackage{xcolor} +\usepackage{extsizes} +\usepackage{subfig} +\usepackage[export]{adjustbox} +\usepackage{tocvsec2} % глубина разделов в оглавлении +\usepackage[subfigure]{tocloft} % сетка из картинок +\usepackage[newfloat]{minted} % можно менять место, где располагать объект +\captionsetup[listing]{position=top} % настрокйка подписи + +\usepackage{tikz} +\usetikzlibrary{automata,patterns, positioning, arrows,shadows,shapes,datavisualization} +\usetikzlibrary{decorations.pathreplacing} +\usetikzlibrary{arrows.meta,patterns.meta,graphs} + +% команды ниже просто добавляют перед текстам нечто +% \AtBeginEnvironment{figure}{\vspace{0.5cm}} +\AtBeginEnvironment{table}{\vspace{0.5cm}} +% \AtBeginEnvironment{listing}{\vspace{0.5cm}} +\AtBeginEnvironment{algorithm}{\vspace{0.5cm}} +% \AtBeginEnvironment{minted}{\vspace{-0.5cm}} + +\captionsetup[listing]{ + position=top, + skip=2pt, + aboveskip=0pt, + belowskip=2pt +} + +\AtBeginEnvironment{minted}{% + \setlength{\topsep}{0pt}% + \setlength{\partopsep}{0pt}% +} + +\usepackage{fancyvrb} +\usepackage{ulem,bm,mathrsfs,ifsym} %зачеркивания, особо жирный стиль и RSFS начертание +\usepackage{sectsty} % переопределение стилей подразделов + +% для полей и разметки страницы +\usepackage{pdflscape} %альбомные страницы +\usepackage{geometry} % настройка размеров страниц +\geometry{a4paper, tmargin=2cm, bmargin=2cm, lmargin=3cm, rmargin=1.5cm} % тоже самое, но лучше + +% математика +\usepackage{amsthm,amsfonts,amsmath,amssymb,amscd} % дополнения от AMS +\usepackage{mathtools} % добавляет окружение multlined +\usepackage[perpage]{footmisc} % нумперация сносок на каждой странице +\KOMAoptions{fontsize=14pt} % изменение параметров страницы + +\makeatletter % для команды с символом @ в имени не в стилевом пакете, а прямо в тексте документа +\def\showfontsize{\f@size{} point} +\makeatother + +% про стили и языки +\usepackage{cmap} % улучшает поиск кирилицы в pdf +\usepackage[T2A]{fontenc} % поддержка русских букв +\usepackage[utf8]{inputenc} % кодировка utf8 +\usepackage[english, main=russian]{babel} % языки: русский, английский +\usepackage{tempora} % шрифт times + +% оформление текста +\usepackage{indentfirst} % красная строка + +% таблицы +\usepackage{longtable} % длинные таблицы +\usepackage{multirow, makecell, array} % улучшенное форматирование таблиц +\usepackage{booktabs} % возможность оформления таблиц в классическом книжном стиле (при правильном использовании не противоречит ГОСТ) + +% общее форматирование +\usepackage{soulutf8} % переносоустойчивые подчёркивания и зачёркивания +\usepackage{icomma} % запятая в десятичных дробях + +% картинки +\usepackage{graphicx} % работа с графикой +\usepackage{wrapfig} % картинки могут «обтекаться» текстом + +% списки +\usepackage{enumitem} + +% подписи +\usepackage{caption} % помогает настраивать подписи к рисункам и таблицам +%% Использование: +%\begin{table}[h!]\ContinuedFloat - чтобы не переключать счетчик +%\captionsetup{labelformat=continued} %должен стоять до самого caption +%\caption{} +% либо ручками \caption*{Продолжение таблицы~\ref{...}.} :) + +% интервалы +\addto\captionsrussian{ + \renewcommand{\listingname}{Листинг} +} + +%счётчики +\usepackage[figure, table, section]{totalcount} % счётчик рисунков и таблиц +\DeclareTotalCounter{lstlisting} % считать листинги тоже +\usepackage{totcount} % создание счётчиков на основе последнего номера подсчитываемого элемента +\usepackage{totpages} % счётчик страниц, совместимый с hyperref (ссылается на номер последней страницы) + +% интервалы +% linespread-реализация ближе к реализации полуторного интервала в ворде. +% setspace реализация заточена под шрифты 10, 11, 12pt, под остальные кегли хуже, но всё же ближе к типографской классике. +\linespread{1.35} % Полуторный интервал (ГОСТ Р 7.0.11-2011, 5.3.6) +%\renewcommand{\@biblabel}[1]{#1} + +% гиперссылки +\usepackage{hyperref} + +% выравнивание и переносы +\sloppy % оберег от переполнений +\clubpenalty=10000 % запрет разрыва страницы после первой строки абзаца +\widowpenalty=10000 % запрет разрыва страницы после последней строки абзаца + +% какая-то магия +\makeatletter % малые заглавные, small caps shape +\let\@@scshape=\scshape +\renewcommand{\scshape}{% + \ifnum\strcmp{\f@series}{bx}=\z@ + \usefont{T1}{cmr}{bx}{sc}% + \else + \ifnum\strcmp{\f@shape}{it}=\z@ + \fontshape{scsl}\selectfont + \else + \@@scshape + \fi + \fi} +\makeatother + +% подписи +%\captionsetup{% +%singlelinecheck=off, % многострочные подписи, например у таблиц +%skip=2pt, % вертикальная отбивка между подписью и содержимым рисунка или таблицы определяется ключом +%justification=centering, % центрирование подписей, заданных командой \caption +%} + +% пакеты +\usepackage{ifthen} % добавляет ifthenelse +% инициализирование переменных, не трогать! (опять магия) +\newcounter{intvl} +\newcounter{otstup} +\newcounter{contnumeq} +\newcounter{contnumfig} +\newcounter{contnumtab} +\newcounter{pgnum} +\newcounter{bibliosel} +\newcounter{chapstyle} +\newcounter{headingdelim} +\newcounter{headingalign} +\newcounter{headingsize} +\newcounter{tabcap} +\newcounter{tablaba} +\newcounter{tabtita} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Область упрощённого управления оформлением %%% + +% интервал между заголовками и между заголовком и текстом +% заголовки отделяют от текста сверху и снизу тремя интервалами (ГОСТ Р 7.0.11-2011, 5.3.5) +\setcounter{intvl}{3} % коэффициент кратности к размеру шрифта + +% отступы у заголовков в тексте +\setcounter{otstup}{0} % 0 - без отступа; 1 - абзацный отступ + +% нумерация формул, таблиц и рисунков +\setcounter{contnumeq}{1} % нумерация формул: 0 - пораздельно, 1 - сквозная нумерация по всему документу +\setcounter{contnumfig}{1} % нумерация рисунков: 0 - пораздельно, 1 - сквозная нумерация по всему документу +\setcounter{contnumtab}{1} % нумерация таблиц: 0 - пораздельно, 1 - сквозная нумерация по всему документу + +% оглавление +\setcounter{pgnum}{0} % 0 - номера страниц никак не обозначены, 1 - стр. над номерами страниц + +% библиография +\setcounter{bibliosel}{1} % 0 - встроенная реализация с загрузкой файла через движок bibtex8, 1 - реализация пакетом biblatex через движок biber + +% текст и форматирование заголовков +\setcounter{chapstyle}{0} % 0 - разделы только под номером, 1 - разделы с названием "Глава" перед номером +\setcounter{headingdelim}{0} % 0 - номер отделен пропуском в 1em или \quad, 1 - номера разделов и приложений отделены точкой с пробелом, подразделы пропуском без точки, 2 - номера разделов, подразделов и приложений отделены точкой с пробелом + +% выравнивание заголовков в тексте +\setcounter{headingalign}{0} % 0 - по центру, 1 - по левому краю + +% размеры заголовков в тексте +\setcounter{headingsize}{0} % 0 - по ГОСТ, все всегда 14 пт; 1 - пропорционально изменяющийся размер в зависимости от базового шрифта + +% подпись таблиц +\setcounter{tabcap}{0} % 0 - по ГОСТ, номер таблицы и название разделены тире, выровнены по левому краю, при необходимости на нескольких строках, 1 - подпись таблицы не по ГОСТ, на двух и более строках, дальнейшие настройки: +% выравнивание первой строки, с подписью и номером +\setcounter{tablaba}{2} % 0 - по левому краю, 1 - по центру, 2 - по правому краю +% выравнивание строк с самим названием таблицы +\setcounter{tabtita}{1} % 0 - по левому краю, 1 - по центру, 2 - по правому краю + +% картинки +\DeclareCaptionLabelSeparator*{emdash}{~-- } % (ГОСТ 2.105, 4.3.1) +\captionsetup[figure]{labelsep=emdash,font=onehalfspacing,position=bottom} + +% таблицы +\ifthenelse{\equal{\thetabcap}{0}}{ + \newcommand{\tabcapalign}{\raggedright} % по левому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetablaba}{0} \AND \equal{\thetabcap}{1}}{ + \newcommand{\tabcapalign}{\raggedright} % по левому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetablaba}{1} \AND \equal{\thetabcap}{1}}{ + \newcommand{\tabcapalign}{\centering} % по центру страницы или аналога parbox +} + +\ifthenelse{\equal{\thetablaba}{2} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabcapalign}{\raggedleft} % по правому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetabtita}{0} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabtitalign}{\raggedright} % по левому краю страницы или аналога parbox +} + +\ifthenelse{\equal{\thetabtita}{1} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabtitalign}{\centering} % по центру страницы или аналога parbox +} + +\ifthenelse{\equal{\thetabtita}{2} \AND \equal{\thetabcap}{1}}{% + \newcommand{\tabtitalign}{\raggedleft} % по правому краю страницы или аналога parbox +} + +\DeclareCaptionFormat{tablenocaption}{\tabcapalign #1\strut} % наименование таблицы отсутствует +\ifthenelse{\equal{\thetabcap}{0}}{ + \DeclareCaptionFormat{tablecaption}{\tabcapalign #1#2#3} + \captionsetup[table]{labelsep=emdash} % тире как разделитель идентификатора с номером от наименования +}{ + \DeclareCaptionFormat{tablecaption}{\tabcapalign #1#2\par % идентификатор таблицы на отдельной строке + \tabtitalign{#3}} % наименование таблицы строкой ниже + \captionsetup[table]{labelsep=space} % пробельный разделитель идентификатора с номером от наименования +} +\captionsetup[table]{format=tablecaption, singlelinecheck=off, font=onehalfspacing, position=top, skip=-5pt} % многострочные наименования +\DeclareCaptionLabelFormat{continued}{Продолжение таблицы~#2} +\setlength{\belowcaptionskip}{.2cm} +\setlength{\intextsep}{0ex} + +% подписи подрисунков +\renewcommand{\thesubfigure}{\asbuk{subfigure}} % буквенные номера подрисунков +\captionsetup[subfigure]{font={normalsize}, % шрифт подписи названий подрисунков (не отличается от основного) + labelformat=brace, % формат обозначения подрисунка + justification=centering, % выключение подписей +} + +% гиперссылки +\definecolor{linkcolor}{rgb}{0.0,0,0} +\definecolor{citecolor}{rgb}{0,0.0,0} +\definecolor{urlcolor}{rgb}{0,0,0} +\hypersetup{ + linktocpage=true, % ссылки с номера страницы в оглавлении, списке таблиц и списке рисунков + plainpages=true, % что-то про нумерацию в арабской форме + colorlinks, % ссылки отображаются раскрашенным текстом + linkcolor={linkcolor}, % цвет ссылок типа ref, eqref и подобных + citecolor={citecolor}, % цвет ссылок-цитат + urlcolor={urlcolor}, % цвет гиперссылок + pdflang={ru}, +} +\urlstyle{same} + +% ещё что-то +\setlength{\parindent}{2.5em} % абзацный отступ. должен быть одинаковым по всему тексту (ГОСТ Р 7.0.11-2011, 5.3.7). + +% списки +% используем дефис для ненумерованных списков (ГОСТ 2.105-95, 4.1.7) +%\renewcommand{\labelitemi}{\normalfont\bfseries~{--}} +\renewcommand{\labelitemi}{\bfseries~{--}} +\setlist{nosep, % единый стиль для всех списков (пакет enumitem), без дополнительных интервалов + labelindent=\parindent,leftmargin=*% каждый пункт, подпункт и перечисление с абзацного отступа (ГОСТ 2.105-95, 4.1.8) +} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\usepackage{ragged2e} % выравнивание по горизонтали +\newcolumntype{L}[1]{>{\RaggedRight\arraybackslash}p{#1}} +\usepackage[explicit]{titlesec} % явное указание заголовка +\usepackage{placeins} % не даеёт объектам «утекать» за барьеры +\usepackage{xparse} % позволяет создавать более крутые команды, чем с \newcommand +\usepackage{csquotes} % многоязычное что-то +\usepackage{listingsutf8} % поддержка файлов с многобайтовыми кодировками +\usepackage{url} % пакеты расширений +\usepackage{algorithm, algorithmicx} % что-то св]занное с кодом +\usepackage[noend]{algpseudocode} % для норм форматирования кода +\usepackage{blkarray} % для работы с чем0то математическим +\usepackage{chngcntr} +\usepackage{tabularx} +\usepackage[backend=biber, + bibstyle=gost-numeric, + citestyle=gost-numeric, + sorting=none]{biblatex} +\setcounter{biburllcpenalty}{7000} +\setcounter{biburlucpenalty}{8000} +\newcommand*\template[1]{\text{<}#1\text{>}} +\addbibresource{biblio.bib} % добавление библиографии + +% нечто для нормального вида +\titleformat{name=\section,numberless}[block]{\normalfont\normalsize\centering}{}{0em}{#1} +\titleformat{\section}[block]{\normalfont\normalsize\bfseries\raggedright}{}{0em}{\thesection\hspace{0.25em}#1} +\titleformat{\subsection}[block]{\normalfont\normalsize\bfseries\raggedright}{}{0em}{\thesubsection\hspace{0.25em}#1} +\titleformat{name=\subsection,numberless}[block]{\normalfont\normalsize\bfseries\raggedright}{}{0em}{#1} +\titleformat{\subsubsection}[block]{\normalfont\normalsize\bfseries\raggedright}{}{0em}{\thesubsubsection\hspace{0.25em}#1} +\titleformat{name=\subsubsection,numberless}[block]{\normalfont\normalsize\bfseries\raggedright}{}{0em}{#1} + +\newcounter{subsubsubsection}[subsubsection] +\renewcommand{\thesubsubsubsection}{\thesubsubsection.\arabic{subsubsubsection}} +\setcounter{secnumdepth}{4} +\setcounter{tocdepth}{4} +\titleclass{\subsubsubsection}{straight}[\subsubsection] +\titleformat{\subsubsubsection}[block]{\normalfont\normalsize\bfseries\raggedright}{}{0em}{\thesubsubsubsection\hspace{0.25em}#1} +\titlespacing*{\subsubsubsection}{0pt}{3.25ex plus 1ex minus .2ex}{1.5ex plus .2ex} +\makeatletter +\newcommand*\l@subsubsubsection{\@dottedtocline{4}{0pt}{5em}} +\makeatother + +\let\Algorithm\algorithm % копирование одной команды в другую +\renewcommand\algorithm[1][]{\Algorithm[#1]\setstretch{1.5}} +%\renewcommand{\listingscaption}{Листинг} + +% работа с шрифтами, формулами +\usepackage{pifont} +\usepackage{calc} +\usepackage{suffix} +\usepackage{csquotes} +\DeclareQuoteStyle{russian} + {\guillemotleft}{\guillemotright}[0.025em] + {\quotedblbase}{\textquotedblleft} +\ExecuteQuoteOptions{style=russian} +\newcommand{\enq}[1]{\enquote{#1}} +\newcommand{\eng}[1]{\begin{english}#1\end{english}} + +% создание всяких разных счётчиков +\newcounter{cTheorem} +\newcounter{cDefinition} +\newcounter{cConsequent} +\newcounter{cExample} +\newcounter{cLemma} +\newcounter{cConjecture} +\newtheorem{Theorem}{Теорема}[cTheorem] +\newtheorem{Definition}{Определение}[cDefinition] +\newtheorem{Consequent}{Следствие}[cConsequent] +\newtheorem{Example}{Пример}[cExample] +\newtheorem{Lemma}{Лемма}[cLemma] +\newtheorem{Conjecture}{Гипотеза}[cConjecture] + +% счётчик - с арабской цифрой +\renewcommand{\theTheorem}{\arabic{Theorem}} +\renewcommand{\theDefinition}{\arabic{Definition}} +\renewcommand{\theConsequent}{\arabic{Consequent}} +\renewcommand{\theExample}{\arabic{Example}} +\renewcommand{\theLemma}{\arabic{Lemma}} +\renewcommand{\theConjecture}{\arabic{Conjecture}} +% \makeatletter +\NewDocumentCommand{\Newline}{}{\text{\\}} +\newcommand{\sequence}[2]{\ensuremath \left(#1,\ \dots,\ #2\right)} + +% переопределяем цвета +\definecolor{mygreen}{rgb}{0,0.6,0} +\definecolor{mygray}{rgb}{0.5,0.5,0.5} +\definecolor{mymauve}{rgb}{0.58,0,0.82} +\renewcommand{\listalgorithmname}{Список алгоритмов} +\floatname{algorithm}{Листинг} +\renewcommand{\lstlistingname}{Листинг} +\renewcommand{\thealgorithm}{\arabic{algorithm}} + +% работа с листингами, рисунками, параграфами +\newcommand{\refAlgo}[1]{(листинг \ref{#1})} +\newcommand{\refImage}[1]{(рисунок \ref{#1})} +\renewcommand{\theenumi}{\arabic{enumi}.} % меняем везде перечисления на цифра.цифра +\renewcommand{\labelenumi}{\arabic{enumi}.} % меняем везде перечисления на цифра.цифра +\renewcommand{\theenumii}{\arabic{enumii}} % меняем везде перечисления на цифра.цифра +\renewcommand{\labelenumii}{(\arabic{enumii})} % меняем везде перечисления на цифра.цифра +\renewcommand{\theenumiii}{\roman{enumiii}} % меняем везде перечисления на цифра.цифра +\renewcommand{\labelenumiii}{(\roman{enumiii})} % меняем везде перечисления на цифра.цифра +%\newfontfamily\AnkaCoder[Path=src/fonts/]{AnkaCoder-r.ttf} +\renewcommand{\labelitemi}{--} +\renewcommand{\labelitemii}{--} + +%\usepackage{courier} + +% оформление языков +\lstdefinelanguage{Refal}{ + alsodigit = {.,<,>}, + morekeywords = [1]{ENTRY}, + morekeywords = [2]{Go, Put, Get, Open, Close, Arg, Add, Sub, Mul, Div, Symb, Explode, Implode}, + %keyword4 + morekeywords = [3]{<,>}, + %keyword5 + morekeywords = [4]{e.,t.,s.}, + sensitive = true, + morecomment = [l]{*}, + morecomment = [s]{/*}{*/}, + commentstyle = \color{mygreen}, + morestring = [b]", + morestring = [b]', + stringstyle = \color{purple} +} + +\makeatletter % для норм обозначения @ +\def\p@subsection{} +\def\p@subsubsection{\thesection\,\thesubsection\,} +\makeatother + +% какая-то лютая магия +\newcommand{\prog}[1]{{\ttfamily\small#1}} +\lstset{ % + backgroundcolor=\color{white}, % выбор background, нужно \usepackage{color} or \usepackage{xcolor} + basicstyle=\ttfamily\footnotesize, + %basicstyle=\footnotesize\AnkaCoder, % размер букв для кода + breakatwhitespace=false, % sets if automatic breaks shoulbd only happen at whitespace + breaklines=true, % sets automatic line breaking + captionpos=top, % sets the caption-position to bottom + commentstyle=\color{mygreen}, % comment style + deletekeywords={...}, % if you want to delete keywords from the given language + escapeinside={\%*}{*)}, % if you want to add LaTeX within your code + extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 + inputencoding=utf8, + frame=single, % adds a frame around the code + keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) + keywordstyle=\bf, % keyword style + language=Refal, % the language of the code + morekeywords={<,>,$ENTRY,Go,Arg, Open, Close, e., s., t., Get, Put}, + numbers=left, % where to put the line-numbers; possible values are (none, left, right) + numbersep=5pt, % how far the line-numbers are from the code + xleftmargin=25pt, + xrightmargin=25pt, + numberstyle=\small\color{black}, % the style that is used for the line-numbers + rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here)) + showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' + showstringspaces=false, % underline spaces within strings only + showtabs=false, % show tabs within strings adding particular underscores + stepnumber=1, % the step between two line-numbers. If it's 1, each line will be numbered + stringstyle=\color{mymauve}, % string literal style + tabsize=8, % sets default tabsize to 8 spaces + title=\lstname % show the filename of files included with \lstinputlisting; also try caption instead of title +} +% какие-то переобъявления и новые команды +\newcommand{\anonsection}[1]{\cleardoublepage +\phantomsection % якроь в опр месте +\addcontentsline{toc}{section}{#1} +\section*{#1}\vspace*{2.5ex} % по госту положены 3 пустые строки после заголовка ненумеруемого раздела +} +\newcommand{\anonsubsection}[1]{\subsection*{#1}} +\newcommand{\anonsubsubsection}[1]{\subsubsection*{#1}} +\newcommand{\sectionbreak}{\clearpage} +\renewcommand{\sectionfont}{\normalsize} % Сбиваем стиль оглавления в стандартный +\renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}} % Точки в оглавлении напротив разделов + +\renewcommand{\cftsecfont}{\normalfont\large} % Переключение на times в содержании +\renewcommand{\cftsubsecfont}{\normalfont\large} % Переключение на times в содержании + +\setlength{\cftsecindent}{0pt}% Убираем отступы в содержании для \section +\setlength{\cftsubsecindent}{0pt}% Убираем отступы в содержании для \subsection +\setlength{\cftsubsubsecindent}{0pt}% Убираем отступы в содержании для \subsubsection + +% опять магия, я устала +\usepackage{caption} +%\captionsetup[table]{justification=raggedleft} +%\captionsetup[figure]{justification=centering,labelsep=endash} +\usepackage{amsmath} % \bar (матрицы и проч. ...) +\usepackage{amsfonts} % \mathbb (символ для множества действительных чисел и проч. ...) +\usepackage{mathtools} % \abs, \norm + \DeclarePairedDelimiter\abs{\lvert}{\rvert} % операция модуля + \DeclarePairedDelimiter\norm{\lVert}{\rVert} % операция нормы +\DeclareTextCommandDefault{\textvisiblespace}{% + \mbox{\kern.06em\vrule \@height.3ex}% + \vbox{\hrule \@width.3em}% + \hbox{\vrule \@height.3ex}} +\newsavebox{\spacebox} +\begin{lrbox}{\spacebox} +\verb*! ! +\end{lrbox} +\newcommand{\aspace}{\usebox{\spacebox}} +\DeclareTotalCounter{listing} + +% снова магия +\makeatletter +\renewcommand*{\p@subsubsection}{} +\makeatother + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\makeatletter +\AddToHook{begindocument/before}{\@ifpackageloaded{minted}{\removefromtoclist[float]{lol}}{}} +\makeatother +\newlength{\lblwd} + +% Поле титульного листа: метка + значение на подчёркивающей линии +\newcommand{\titlefield}[2]{% + \par\addvspace{1.6ex}% + \settowidth{\lblwd}{\small #1}% + \noindent\small #1\hspace{1em}% + \underline{\makebox[\dimexpr\linewidth-\lblwd-1em\relax][l]{\small #2}}% + \par +} +\begin{document} + +\sloppy % избегаем переполнения + +\def\figurename{Рисунок} % переименуем рис в рисунок + + +\newcommand{\sqlkw}[1]{\texttt{#1}} +\newcommand{\SELECT}{\sqlkw{SELECT}} +\newcommand{\FROM}{\sqlkw{FROM}} +\newcommand{\WHERE}{\sqlkw{WHERE}} +\newcommand{\JOIN}{\sqlkw{JOIN}} +\newcommand{\GROUPBY}{\sqlkw{GROUP BY}} +\newcommand{\ORDERBY}{\sqlkw{ORDER BY}} + +\newcommand{\COUNT}{\sqlkw{COUNT}} +\newcommand{\SUM}{\sqlkw{SUM}} +\newcommand{\AVG}{\sqlkw{AVG}} +\newcommand{\MIN}{\sqlkw{MIN}} +\newcommand{\MAX}{\sqlkw{MAX}} + +\newcommand{\LogicalGet}{\sqlkw{LogicalGet}} +\newcommand{\SeqScan}{\sqlkw{SeqScan}} +\newcommand{\IndexScan}{\sqlkw{IndexScan}} +\newcommand{\NestedLoopJoin}{\sqlkw{NestedLoopJoin}} +\newcommand{\NestedLoopCrossJoin}{\sqlkw{NestedLoopCrossJoin}} +\newcommand{\HashJoin}{\sqlkw{HashJoin}} +\newcommand{\MergeJoin}{\sqlkw{MergeJoin}} +\newcommand{\Sort}{\sqlkw{Sort}} +\newcommand{\FilterOp}{\sqlkw{Filter}} +\newcommand{\ProjOp}{\sqlkw{Projection}} + +\newcommand{\SelOp}[1]{\sigma_{#1}} +\newcommand{\ProjectionOp}[1]{\pi_{#1}} + +\newcommand{\Sel}[2]{\sigma_{#1}(#2)} +\newcommand{\Projection}[2]{\pi_{#1}(#2)} +\newcommand{\Agg}[3]{\gamma_{#1;\,#2}(#3)} +\newcommand{\AggP}[3]{\gamma_{#1;\,#2}^{partial}(#3)} +\newcommand{\NJoin}[2]{#1 \bowtie #2} +\newcommand{\InnerJoin}[3]{#1 \bowtie_{#2} #3} +\newcommand{\CProd}[2]{#1 \times #2} +\newcommand{\LOJ}[3]{#1 \ltimes_{#2} #3} +\newcommand{\ROJ}[3]{#1 \rtimes_{#2} #3} +\newcommand{\FOJ}[3]{#1 \mathbin{\ltimes\!\!\!\rtimes}_{#2} #3} +\newcommand{\Tr}[1]{[\![ #1 ]\!]} +\newcommand{\Apply}[2]{#1 \mathbin{\mathcal{A}} #2} +\newcommand{\TransRule}[3]{% + \ensuremath{% + \begin{array}{c} + #1 \\[1pt] + \xRightarrow{\,#2\,} \\[1pt] + #3 + \end{array}% + }% +} + +%---------------------------------- +% АННОТАЦИЯ (до титульного листа) +%---------------------------------- + +% начала магии title +\begin{titlepage} + \thispagestyle{empty} + \newpage + + \vspace*{-30pt} + \hspace{-45pt} + \begin{minipage}{0.17\textwidth} + \hspace*{-20pt}\centering + \includegraphics[width=1.3\textwidth]{emblem.png} + \end{minipage} + \begin{minipage}{0.82\textwidth}\small \textbf{ + \vspace*{-0.7ex} + \hspace*{-10pt}\centerline{Министерство науки и высшего образования Российской Федерации} + \vspace*{-0.7ex} + \centerline{Федеральное государственное автономное образовательное учреждение } + \vspace*{-0.7ex} + \centerline{высшего образования} + \vspace*{-0.7ex} + \centerline{<<Московский государственный технический университет} + \vspace*{-0.7ex} + \centerline{имени Н.Э. Баумана} + \vspace*{-0.7ex} + \centerline{(национальный исследовательский университет)>>} + \vspace*{-0.7ex} + \centerline{(МГТУ им. Н.Э. Баумана)}} + \end{minipage} + + \vspace{-2pt} + \hspace{-34.5pt}\rule{\textwidth}{2.5pt} + + \vspace*{-20.3pt} + \hspace{-34.5pt}\rule{\textwidth}{0.4pt} + +\titlefield{ФАКУЛЬТЕТ}{<<Информатика и системы управления>>} +\titlefield{КАФЕДРА}{<<Теоретическая информатика и компьютерные технологии>>} + + +\vspace{3em} +\begin{center} + {\Large\bfseries РАСЧЕТНО-ПОЯСНИТЕЛЬНАЯ ЗАПИСКА}\\[1.5ex] + {\Large\bfseries\itshape К ВЫПУСКНОЙ КВАЛИФИКАЦИОННОЙ РАБОТЕ НА ТЕМУ:}\\[1.5ex] + {\large\bfseries\itshape <<Автоматическая оптимизация планов выполнения SQL-запросов>>} +\end{center} + +\vspace{\fill} + + + \newlength{\ML} + \settowidth{\ML}{«\underline{\hspace{0.7cm}}» \underline{\hspace{2cm}}} + + \noindent Студент \underline{\text{ИУ9-81Б}} \hfill \underline{ \hspace{4cm}}\quad + \raisebox{0.42ex}{\underline{\parbox{4cm}{\centering Старовойтов А. И.}}} + + \vspace{-0.5ex} + \noindent\hspace{9ex}\scriptsize{(Группа)}\normalsize\hspace{170pt}\hspace{2ex}\scriptsize{(Подпись, дата)}\normalsize\hspace{30pt}\hspace{6ex}\scriptsize{(Фамилия И.О.)}\normalsize + + \bigskip + + \noindent Научный руководитель \hfill \underline{\hspace{4cm}}\quad + \raisebox{0.3ex}{\underline{\parbox{4cm}{\centering Непейвода А. Н.}}} + + \vspace{-0.5ex} + \noindent\hspace{13.5ex}\normalsize\hspace{170pt}\hspace{2ex}\scriptsize{(Подпись, дата)}\normalsize\hspace{30pt}\hspace{6ex}\scriptsize{(Фамилия И.О.)}\normalsize + + \bigskip + + \noindent Нормоконтролёр \hfill \underline{\hspace{4cm}}\quad + \raisebox{0.15ex}{\underline{\parbox{4cm}{\centering Алексеев С. М.}}} + + \vspace{-0.5ex} + \noindent\hspace{13.5ex}\normalsize\hspace{170pt}\hspace{2ex}\scriptsize{(Подпись, дата)}\normalsize\hspace{30pt}\hspace{6ex}\scriptsize{(Фамилия И.О.)}\normalsize + + \vfill + + %\vspace{\fill} + + + + \begin{center} + \textsl{2026} + \end{center} +\end{titlepage} +% конец магии title + +\setlength{\tabcolsep}{3pt} +\newpage +% \thispagestyle{empty} + +\section*{АННОТАЦИЯ} + +Работа посвящена разработке оптимизатора SQL-запросов для модельной реляционной +СУБД. Цель работы состоит в реализации оптимизатора на основе архитектуры +Cascades и создании основы для его дальнейшего улучшения за счет сопоставления +порождаемых физических планов с планами промышленных систем. + +В ходе исследования рассмотрены архитектуры оптимизаторов System~R, Volcano и +Cascades, формализовано поддерживаемое подмножество SQL и соответствующее ему +подмножество реляционной алгебры. При реализации использованы структура Memo, +правила логических преобразований и физических реализаций, стоимостная модель, +учет требуемых физических свойств, а также метод ветвей и границ для отсечения +неперспективных вариантов планов. Для оценки работы системы применены модульное +тестирование, генерация SQL-запросов, бенчмарки физических операторов и +дифференциальное сравнение планов с Microsoft SQL Server. + +В результате разработана модельная СУБД, работающая с CSV-файлами и +поддерживающая основные операторы реляционной алгебры, а также расширяемый +оптимизатор, способный строить физические планы с учетом стоимости и физических +свойств результата. Получены стоимостные формулы операторов, подтвержден прирост +производительности относительно наивного плана и подготовлены инструменты для +поиска случаев, в которых пространство поиска оптимизатора требует расширения. +Практическая значимость работы заключается в возможности использовать +реализованную систему как основу для экспериментального исследования методов +оптимизации запросов и итеративного развития набора правил оптимизатора. + +\newpage +\setcounter{page}{5} +%---------------------------------------------------------------------------- +% ОТСЮДА --- СОБСТВЕННО ТЕКСТ +%---------------------------------------------------------------------------- +\renewcommand\contentsname{\hfill{\normalfont{СОДЕРЖАНИЕ}}\hfill} %Оглавление +\tableofcontents +\newpage +\anonsection{ВВЕДЕНИЕ} + +В эпоху стремительного роста объемов данных системы управления базами данных +(СУБД) являются важной частью информационных систем. По данным аналитических +исследований~\cite{DBReport}, объем мирового рынка СУБД ежегодно увеличивается, +что обусловлено как расширением круга решаемых задач, так и усложнением +структуры обрабатываемых данных и увеличением их объема. Реляционные базы +данных, работающие с различными диалектами языка SQL, продолжают занимать +доминирующее положение в сегменте обработки транзакций и аналитических запросов. + +Одним из определяющих факторов конкурентоспособности между различными +реализациями СУБД является качество оптимизатора запросов. Оптимизатор +представляет собой компонент, ответственный за преобразование декларативного +SQL-запроса в эффективный физический план исполнения. Именно от качества работы +оптимизатора в значительной степени зависит производительность всей системы: +выбор неоптимального плана исполнения может привести к увеличению времени +выполнения запроса в десятки раз относительно оптимального случая.Также +оптимизатор запросов считается наиболее сложной составной частью любой СУБД. +Задача нахождения оптимального плана является NP-полной в общем случае, что +обуславливает необходимость применения эвристических методов и алгоритмов +ограниченного перебора. + +Архитектура оптимизаторов Cascades используется в таких промышленных системах, +как Microsoft SQL Server, Apache Orca, а также в ряде исследовательских +проектов, включая Columbia и CockroachDB\@. Преимущества этой архитектуры +заключаются в расширяемости, модульности, а также понятном и эффективном +алгоритме поиска, допускающем параллельность исполнения. + +Целью данной работы является разработка и реализация оптимизатора подмножества +SQL-запросов на основе архитектуры Cascades, а также создание каркаса для его +итеративного улучшения путем сравнения с промышленными реализациями. + +Для достижения поставленной цели прежде всего требуется изучить архитектуру +оптимизаторов Cascades и на ее основе проработать собственную. + +После выбора архитектурной основы необходимо формализовать поддерживаемое +подмножество SQL и соответствующее ему подмножество реляционной алгебры. Эта +задача определяет границы рассматриваемых запросов, их семантику и внутреннее +логическое представление, используемое на этапе оптимизации. + +Следующим шагом является реализация структуры данных Memo и алгоритма поиска +оптимального физического плана на основе архитектуры Cascades. В рамках этой +задачи требуется обеспечить хранение эквивалентных логических выражений, +повторное использование найденных вариантов планов и выбор плана с минимальной +оценочной стоимостью. + +Чтобы сформировать пространство поиска оптимизатора, требуется разработать набор +правил трансформации и реализации для основных операторов реляционной алгебры. +Правила трансформации должны порождать логически эквивалентные планы, а правила +реализации сопоставлять логическим операторам допустимые физические операторы. + +Для оптимизации алгоритма поиска нужно реализовать метод ветвей и границ. Этот +метод должен использовать оценки стоимости для отсечения заведомо неоптимальных +планов и уменьшения объема пространства поиска, исследуемого оптимизатором. + +Для последующего развития системы также требуется реализовать метод +дифференциального анализа физических планов. Он должен обеспечивать +автоматизированный поиск примеров неоптимальных планов путем сравнения планов +разрабатываемого оптимизатора с планами промышленных СУБД, а найденные различия +должны использоваться для уточнения правил оптимизации. + +\section{Исследовательская часть} + +Система управления базами данных представляет собой программный комплекс, +обеспечивающий хранение, извлечение и модификацию структурированных данных. +Несмотря на разнообразие существующих реализаций, архитектура большинства +реляционных СУБД включает ряд типовых компонентов, взаимодействие которых +обеспечивает выполнение пользовательских запросов~\cite{petrov2019database}. + +К основным компонентам СУБД относятся~\refImage{fig::dbms_arch}: + +\begin{itemize} + \item транспортный уровень, принимающий клиентские подключения и запросы; + \item синтаксический анализатор (парсер), выполняющий разбор текста +SQL-запроса и построение синтаксического дерева; + \item оптимизатор запросов, преобразующий логическое представление запроса в +эффективный физический план исполнения; + \item исполнитель, непосредственно реализующий физический план; + \item менеджер хранения данных, управляющий размещением данных на физических +носителях; + \item менеджер транзакций и менеджер блокировок, обеспечивающие гарантии +транзакций; + \item менеджер страниц памяти, отвечающий за кэширование страниц данных в +оперативной памяти. +\end{itemize} + +\begin{figure}[!htb]\centering +\usetikzlibrary{fit} +\begin{tikzpicture}[ + box/.style={rectangle, draw, minimum width=4.1cm, minimum height=1cm, + align=center, font=\small}, + wbox/.style={rectangle, draw, minimum width=9.1cm, minimum height=1cm, + align=center, font=\small}, + grp/.style={draw, dashed, inner sep=0.3cm}, + lbl/.style={font=\small}, +] + \node[box] (clust) at (-2.5, 0) {Кластерные\\соединения}; + \node[box] (client) at ( 2.5, 0) {Клиентские\\соединения}; + \node[grp, fit=(clust)(client)] (TG) {}; + \node[lbl, anchor=south west] at (TG.north west) {Транспорт}; + + \node[wbox] (parser) at (0, -2.8) {Синтаксический анализатор}; + \node[wbox, fill=gray!20] (optim) at (0, -4.05) {\textbf{Оптимизатор запросов}}; + \node[grp, fit=(parser)(optim)] (QPG) {}; + \node[lbl, anchor=south west] at (QPG.north west) {Обработчик запросов}; + + \node[box] (remote) at (-2.5, -6.85) {Удалённое\\исполнение}; + \node[box] (local) at ( 2.5, -6.85) {Локальное\\исполнение}; + \node[grp, fit=(remote)(local)] (EEG) {}; + \node[lbl, anchor=south west] at (EEG.north west) {Исполнитель}; + + \node[box] (txmgr) at (-2.5, -9.65) {Менеджер\\транзакций}; + \node[box] (lockmgr) at ( 2.5, -9.65) {Менеджер\\блокировок}; + \node[wbox] (access) at ( 0, -10.9) {Методы доступа}; + \node[box] (bufmgr) at (-2.5, -12.15) {Менеджер\\страниц}; + \node[box] (recmgr) at ( 2.5, -12.15) {Менеджер\\восстановления}; + \node[grp, fit=(txmgr)(lockmgr)(access)(bufmgr)(recmgr)] (SEG) {}; + \node[lbl, anchor=south west] at (SEG.north west) {Подсистема хранения}; + + \draw[<->, thick, >=stealth] + ([xshift=0.4cm]TG.south east) -- ([xshift=0.4cm]QPG.north east); + \draw[<->, thick, >=stealth] + ([xshift=0.4cm]QPG.south east) -- ([xshift=0.4cm]EEG.north east); + \draw[<->, thick, >=stealth] + ([xshift=0.4cm]EEG.south east) -- ([xshift=0.4cm]SEG.north east); + + \draw[thick] ([xshift=-0.5cm]clust.west) -- ([xshift=-0.5cm]remote.west); + \draw[->, thick, >=stealth] ([xshift=-0.5cm]clust.west) -- (clust.west); + \draw[->, thick, >=stealth] ([xshift=-0.5cm]remote.west) -- (remote.west); +\end{tikzpicture} + \caption{Архитектура реляционной системы управления базами данных}% + \label{fig::dbms_arch} +\end{figure} + +\subsection{Процесс исполнения SQL-запроса} + +Процесс исполнения SQL-запроса в реляционной СУБД проходит несколько +последовательных этапов. + +\emph{Синтаксический анализ} --- запрос проходит лексический и синтаксический +разбор. Результатом данного этапа является синтаксическое дерево, узлы которого +соответствуют конструкциям языка SQL: операторам \SELECT, \FROM, \WHERE, \JOIN, +\GROUPBY, \ORDERBY{} и другим. На этом же этапе выполняется семантический +анализ: проверяется существование указанных таблиц и столбцов, разрешаются имена +объектов и определяются типы выражений. + +\emph{Оптимизация} --- синтаксическое дерево преобразуется в логический план +запроса, выраженный в терминах реляционной алгебры. Оптимизатор исследует +пространство эквивалентных логических планов и для каждого из них рассматривает +возможные физические реализации операторов. С помощью модели стоимости +оптимизатор оценивает затраты на выполнение каждого плана и выбирает план с +наименьшей оценочной стоимостью. Результатом этого этапа является физический +план исполнения. Под этим понимается дерево физических операторов с указанием +конкретных алгоритмов соединения, методов доступа к данным и порядка операций. + +\emph{Исполнение физического плана} --- движок выполнения реализует физический +план, порождая потоки кортежей. Данные извлекаются из хранилища, обрабатываются +операторами плана и формируют результирующий набор, возвращаемый пользователю. + +Сформулируем задачу, которую решает оптимизатор: по запросу, представленному в +форме дерева операторов реляционной алгебры, построить эквивалентный и +оптимальный, с точки зрения необходимых для исполнения ресурсов, план +исполнения. Итоговый план является деревом, вершины которого помечены +конкретными указаниями на алгоритмы, реализующими один или несколько операторов +реляционной алгебры. Эквивалентность означает, что итоговый физический план и +исходный запрос при исполнении на любом наборе данных порождают одинаковые +отношения с точностью до требуемых свойств, таких как сортировка. + +Запрос в форме реляционной алгебры также называют логическим планом, а операторы +реляционной алгебры --- логическими операторами. + +Для каждого логического оператора существует несколько возможных физических +реализаций. Например, оператор соединения \(\bowtie\) может быть реализован как +соединение вложенными циклами, хеш-соединение или соединение слиянием. +Аналогично, оператор доступа к данным может быть реализован как последовательное +сканирование таблицы или сканирование индекса. Еще одним важным примером +является эквивалентность сканирования индекса с предикатом и двух логических +операторов, примененных последовательно: доступ к данным и фильтрация по тому же +условию. + +\subsection{Реляционная алгебра} + +Реляционная алгебра представляет собой формальный язык для описания операций над +отношениями реляционной базы данных. В отличие от декларативного SQL, +реляционная алгебра является процедурной, так как она определяет конкретную +последовательность операций, необходимых для получения результата. + +Отношение определяется как конечное множество кортежей, каждый из которых +представляет собой упорядоченный набор значений атрибутов: + +\[ + R \subseteq \text{dom}(A_1) \times \text{dom}(A_2) \times \ldots \times \text{dom}(A_n). +\] + +Среди основных операторов реляционной алгебры выделяют +следующие~\cite{silberschatz2020database}. + +\emph{Фильтрация} \(\Sel{p}{R}\) --- возвращает подмножество кортежей отношения +\(R\), удовлетворяющих предикату \(p\). Например, +\(\Sel{\text{age} > 30}{\text{Employee}}\) вернет все кортежи из отношения +Employee, для которых значение атрибута age больше 30. + +\emph{Проекция} \(\Projection{A_1, \ldots, A_k}{R}\) --- формирует новое +отношение, содержащее только перечисленные атрибуты. + +\emph{Декартово произведение} \(\CProd{R}{S}\) --- формирует отношение, каждый +кортеж которого является конкатенацией кортежа из \(R\) и кортежа из \(S\), при +этом результат содержит \(|\CProd{R}{S}| = |R| \cdot |S|\) элементов. + +\emph{Соединение} \(\InnerJoin{R}{p}{S}\) --- комбинирует кортежи двух отношений +на основании предиката \(p\). Внутреннее соединение определяется как +\(\InnerJoin{R}{R.a = S.b}{S} = \Sel{R.a = S.b}{\CProd{R}{S}}\). + +\emph{Объединение} \(R \cup S\) --- возвращает все кортежи, принадлежащие хотя +бы одному из двух совместимых по схеме отношений. + +\emph{Разность} \(R - S\) --- выбирает кортежи, принадлежащие \(R\), но +отсутствующие в \(S\). + +\emph{Агрегация} \(\Agg{G}{f}{R}\) --- разбивает отношение \(R\) на +непересекающиеся множества по набору атрибутов \(G\) и вычисляет агрегированные +значения функции \(f\) по каждому такому множеству. Примеры функций: \COUNT, +\SUM, \AVG, \MIN, \MAX. + +\emph{Apply} \(\Apply{R}{S(t)}\) --- для каждого кортежа \(t\) отношения \(R\) +вычисляет параметризованное им отношение \(S(t)\) и конкатенирует \(t\) с каждым +кортежем результата: + \[ + \Apply{R}{S(t)} = \bigcup_{t \in R} \{t\} \times S(t). + \] +\noindent{} В отличие от соединения, правая сторона зависит от текущего кортежа +левой и используется для представления коррелированных подзапросов. + +\subsection{Преобразование синтаксического дерева в реляционную алгебру}\label{sec:translation} + +Полученное после лексического и синтаксического разбора абстрактное +синтаксическое дерево преобразуется в выражение в форме реляционной алгебры. +Этот процесс определяется структурной индукцией по дереву запроса. Введём +оператор конверсии \(\Tr{\cdot}\), сопоставляющий узлу синтаксического дерева +его алгебраическое представление. Тогда правила трансляции записываются в виде +правил вывода. Если для всех поддеревьев известны алгебраические представления, +то алгебраическое представление составной конструкции выражается через них. + +\emph{Базовый случай.} Для ссылки на таблицу \(T\) выполняется \(\Tr{T} = T\). + +\emph{Блок \texttt{SELECT-FROM-WHERE}.} Пусть \(\bar{T} = T_1, \ldots, T_n\) --- +ссылки на таблицы или вложенные подзапросы, \(p\) --- предикат фильтрации, +\(\bar{a} = a_1, \ldots, a_k\) --- список выражений проекции. Тогда + \[ + \frac{\Tr{T_1} = E_1 \quad \cdots \quad \Tr{T_n} = E_n} + {\Tr{\SELECT\ \bar{a}\ \FROM\ \bar{T}\ \WHERE\ p} + = \Projection{\bar{a}}{\Sel{p}{E_1 \times \cdots \times E_n}}}. + \] + +\emph{Группировка.} Пусть \(\bar{g} = g_1, \ldots, g_m\) --- список +группирующих атрибутов, \(\bar{f} = f_1, \ldots, f_l\) --- список +агрегирующих функций. Тогда +\[ + \frac{\Tr{T_1} = E_1 \quad \cdots \quad \Tr{T_n} = E_n} + {\Tr{\SELECT\ \bar{g}, \bar{f}\ \FROM\ \bar{T}\ \WHERE\ p\ \GROUPBY\ \bar{g}} + = \Agg{\bar{g}}{\bar{f}}{\Sel{p}{E_1 \times \cdots \times E_n}}}. +\] + +\emph{Вложенные подзапросы.} Пусть \(Q\) --- внешний блок, а \(Q'(t)\) +--- коррелированный подзапрос, зависящий от кортежа \(t\) из +\(\Tr{Q}\). Тогда +\[ + \frac{\Tr{Q} = E \quad \Tr{Q'(t)} = E'(t)} + {\Tr{Q\ \text{с подзапросом}\ Q'} = \Apply{E}{E'(t)}}, +\] +\noindent{} где \(\mathcal{A}\) --- оператор Apply, который впоследствии может быть +удалён процедурой декорреляции. + +\emph{Сортировка.} Конструкция \ORDERBY{} не выражается в виде +операторов реляционной алгебры. Вместо этого она задает требуемое +физическое свойство упорядоченности результата, учитываемое на этапе +выбора физического плана. + +Приведём пример применения этих правил к SQL-запросу из +листинга~\ref{lst:correlated_subquery}. Трансляция этого запроса в реляционную +алгебру выполняется по следующим шагам. По базовому правилу оба вхождения +таблицы остаются как есть \(\Tr{\text{Employee}} = \text{Employee}\). + +\begin{listing}[H] + \caption{Запрос с коррелированным подзапросом.} + \label{lst:correlated_subquery} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT e.name + FROM Employee e + WHERE e.salary > ( + SELECT AVG(salary) + FROM Employee + WHERE dept = e.dept + ) + \end{minted} +\end{listing} + + +Внутренний подзапрос +\(Q'(e) = (\SELECT\ \AVG(\text{salary})\ \FROM\ \ldots \WHERE\ \ldots\)) +транслируется по правилу группировки, где \(\bar{g} = \varnothing\), +\(\bar{f} = \AVG(\text{salary})\): +\[ + \Tr{Q'(e)} = \Agg{\varnothing}{\AVG(\text{salary})}{\Sel{\text{dept} = e.\text{dept}}{\text{Employee}}}. +\] +\noindent{} Подзапрос коррелирован, так как зависит от атрибута +\(e.\text{dept}\) внешнего кортежа. + +По правилу вложенных подзапросов результат \(\Tr{Q'(e)}\) присоединяется к +внешнему блоку оператором \(\mathcal{A}\), добавляя к каждому кортежу столбец +\(c\) с вычисленным значением средней зарплаты. + +Оставшаяся часть внешнего запроса переводится по правилу +\texttt{SELECT-FROM-WHERE} с \(\bar{a} = (e.\text{name})\) и +\(p = (e.\text{salary} > c)\). + +Итоговое алгебраическое выражение имеет вид +\[ + \Projection{e.\text{name}}{\Sel{e.\text{salary} > c}{\Apply{\text{Employee}\;e}{\Agg{\varnothing}{\text{AVG}(\text{salary}) \to c}{\Sel{\text{dept} = e.\text{dept}}{\text{Employee}}}}}}. +\] +\noindent{} Соответствующее дерево операторов изображено на +рисунке~\ref{fig:correlated_subquery_tree}. + +\begin{figure}[!htb]\centering +\begin{tikzpicture}[ + rel/.style={rectangle, draw, rounded corners=2pt, minimum width=2.2cm, + minimum height=0.8cm, align=center, font=\small}, + op/.style={rectangle, draw, fill=gray!12, minimum width=2.6cm, + minimum height=0.9cm, align=center, font=\small}, + level distance=1.15cm +] + \node[op] (proj) at (0, 6.5) {\(\Projection{e.\text{name}}{}\)}; + \node[op] (sel1) at (0, 5.2) {\(\Sel{e.\text{salary} > c}{}\)}; + \node[op] (apply) at (0, 3.9) {\(\mathcal{A}\)}; + \node[rel] (emp1) at (-2.8, 2.6) {\texttt{Employee} \(e\)}; + \node[op] (agg) at (2.8, 2.6) {\(\Agg{\varnothing}{\text{AVG}(\text{salary}) \to c}{}\)}; + \node[op] (sel2) at (2.8, 1.3) {\(\Sel{\text{dept} = e.\text{dept}}{}\)}; + \node[rel] (emp2) at (2.8, 0) {\texttt{Employee}}; + + \draw[->] (sel1) -- (proj); + \draw[->] (apply) -- (sel1); + \draw[->] (emp1) -- (apply); + \draw[->] (agg) -- (apply); + \draw[->] (sel2) -- (agg); + \draw[->] (emp2) -- (sel2); +\end{tikzpicture} +\caption{Дерево реляционной алгебры для запроса из +листинга~\ref{lst:correlated_subquery}.}% +\label{fig:correlated_subquery_tree} +\end{figure} + +Преобразование в форму реляционной алгебры необходимо по нескольким причинам. +Во-первых, это позволяет использовать формализованный набор правил +эквивалентности, который позволяет оптимизатору корректно преобразовывать план +запроса без изменения семантики. Во-вторых, алгебраическое представление +позволяет единообразно обрабатывать различные синтаксические формы SQL-запросов, +порождающие одинаковые логические планы. Наконец, разделение на логические и +физические операторы позволяет оптимизатору независимо исследовать порядок +операций и алгоритмы их реализации. + +\subsection{Поиск оптимального плана} + +Эквивалентность планов, исследуемых в процессе поиска оптимального, +обеспечивается применением алгебраических тождеств, например ассоциативности и +коммутативности соединений. Правила эквивалентности реляционной алгебры подробно +рассматриваются в разделе~\ref{sec:transformation_rules}. + +Оптимальность запроса принято оценивать с помощью функции стоимости +\(\mathrm{cost} \colon \mathcal{P}_L \to \mathbb{R}_{\ge 0}\)~\cite{Selinger1979}, +где \(\mathcal{P}_{L}\) --- множество эквивалентных физических планов. Это +отображение оценивает затраты на исполнение плана исходя из конкретных +реализаций алгоритмов, а также основываясь на эвристиках и статистике о данных в +отношениях. Другими словами, цель оптимизатора состоит в том, чтобы для входного +запроса найти план \(P^* \in \mathcal{P}_L\), минимизирующий \(\mathrm{cost}\): +\[ + P^* = \arg\min_{P \in \mathcal{P}_L} \mathrm{cost}(P). +\] + +Одной из подзадач в рамках построения оптимального плана является определение +порядка выполнения соединений. Благодаря правилам эквивалентности, таким как +ассоциативность и коммутативность соединения +(раздел~\ref{sec:transformation_rules}), одно и то же множество соединяемых +отношений допускает большое количество эквивалентных порядков вычисления. +Различные порядки имеют кардинально разные стоимости, ввиду особенностей +алгоритмов, реализующих соединение. Для \(n\) отношений количество таких +порядков выражается как +\(O(n! \cdot C_{n-1}) = O(\frac{(2n-2)!}{(n-1)!})\)~\cite{IntroToTheJoinOrderingProblem}, +где \(C_k\) --- \(k\)-е число Каталана. Таким образом, пространство поиска +растет экспоненциально с увеличением числа соединяемых таблиц. + +Поскольку полный перебор \(\mathcal{P}_L\) для достаточно больших \(n\) +невозможен, на практике речь идёт о поиске плана с приемлемо малой стоимостью в +определенном подмножестве пространства поиска. Именно эта задача и определяет +архитектуру современных оптимизаторов: расширяемое описание пространства поиска +через правила преобразования, эффективный алгоритм его исследования и +стоимостная модель, позволяющая отсекать заведомо неоптимальные ветви. + +Выбор плана исполнения запроса может оказывать огромное влияние на объем +требуемых вычислительных ресурсов. В качестве примера оценим количество чтений +страниц с диска для различных физических операторов. Пусть имеется три +отношения: таблица \texttt{customer} с \(|C| = 10^5\) строк, таблица +\texttt{orders} с \(|O| = 10^7\) строк и таблица \texttt{lineitem} с +\(|L| = 5 \cdot 10^7\) строк. Размер страницы --- \(8\) КБ. В одну страницу +помещается \(100\) строк \texttt{customer}, \(80\) строк \texttt{orders} и +\(50\) строк \texttt{lineitem}. Соответственно, общее число страниц составляет +\(P_C = 10^3\), \(P_O = 1{,}25 \cdot 10^5\), \(P_L = 10^6\). + +Для запроса из листинга~\ref{lst:join_query} +рассмотрим два альтернативных плана~\refImage{fig::join_plan_comparison}: +\begin{listing} + \caption{Соединение \texttt{customer}, \texttt{orders} и \texttt{lineitem}.} + \label{lst:join_query} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT * + FROM customer c + JOIN orders o ON c.c_custkey = o.o_custkey + JOIN lineitem l ON o.o_orderkey = l.l_orderkey + \end{minted} +\end{listing} + +\begin{figure}[!htb]\centering +\begin{tikzpicture}[ + rel/.style={rectangle, draw, rounded corners=2pt, minimum width=2.45cm, + minimum height=0.8cm, align=center, font=\small}, + op/.style={rectangle, draw, fill=gray!12, minimum width=4.25cm, + minimum height=1.2cm, align=center, font=\small} +] + \node[op] (p1j2) at (-4.4, 2.9) {Hash Join\\\footnotesize \(10^6\) чтений}; + \node[op] (p1j1) at (-6.5, 1.2) {Hash Join\\\footnotesize \(1{,}26 \cdot 10^5\) чтений}; + \node[rel] (p1l) at (-2.3, 1.2) {\texttt{lineitem}}; + \node[rel] (p1c) at (-7.8, -0.3) {\texttt{customer}}; + \node[rel] (p1o) at (-4.4, -0.3) {\texttt{orders}}; + + \draw[->] (p1j1) -- (p1j2); + \draw[->] (p1l) -- (p1j2); + \draw[->] (p1c) -- (p1j1); + \draw[->] (p1o) -- (p1j1); + \node[font=\small\bfseries] at (-4.4, 4.0) {План \(P_1\)}; + + \node[op] (p2j2) at (4.4, 2.9) {Nested Loop Join\\\footnotesize \(1{,}25 \cdot 10^{14}\) чтений}; + \node[op] (p2j1) at (2.3, 1.2) {Nested Loop Join\\\footnotesize \(1{,}25 \cdot 10^8\) чтений}; + \node[rel] (p2l) at (6.5, 1.2) {\texttt{lineitem}}; + \node[rel] (p2c) at (1.0, -0.3) {\texttt{customer}}; + \node[rel] (p2o) at (4.4, -0.3) {\texttt{orders}}; + + \draw[->] (p2j1) -- (p2j2); + \draw[->] (p2l) -- (p2j2); + \draw[->] (p2c) -- (p2j1); + \draw[->] (p2o) -- (p2j1); + \node[font=\small\bfseries] at (4.4, 4.0) {План \(P_2\)}; +\end{tikzpicture} +\caption{Сравнение планов для запроса из листинга~\ref{lst:join_query}.}% +\label{fig::join_plan_comparison} +\end{figure} + +План \(P_1\): +\(\NJoin{(\NJoin{\texttt{customer}}{\texttt{orders}})}{\texttt{lineitem}}\) +с реализацией обоих соединений через Hash Join, для чего сначала +полностью считываются \(P_C + P_O = 1{,}26 \cdot 10^5\) страниц для +построения хеш-таблицы, после чего промежуточный результат размером +\(10^7\) строк соединяется с \texttt{lineitem}, требуя считывания ещё % +\(P_L = 10^6\) страниц. В сумме --- порядка \(1{,}126 \cdot 10^6\) +операций чтения с диска. + +План \(P_2\): тот же запрос, но реализованный как соединение с помощью +вложенных циклов без использования индексов: для каждой строки +\texttt{customer} полностью читается таблица \texttt{orders}, для каждой +результирующей пары --- \texttt{lineitem}. Совокупный объём ввода-вывода +оценивается величиной +\(P_C \cdot P_O \cdot P_L \approx 1{,}25 \cdot 10^{14}\) операций +чтения, что на восемь порядков превышает \(P_1\). + +На еще одном примере запроса из листинга~\ref{lst:logical_transform_query} +рассмотрим влияние логических преобразований. Отношение \texttt{Emp} содержит +\(10{,}000\) кортежей, что эквивалентно \(1{,}000\) страницам. \texttt{Dept} +включает \(500\) записей и \(50\) страниц соответственно. Атрибут +\texttt{Emp.did} является внешним ключом к \texttt{Dept.did}, поэтому каждому +кортежу из \texttt{Dept} соответствует \(10{,}000 / 500 = 20\) из \texttt{Emp}. +Пусть на \texttt{Emp.did} и \texttt{Dept.dname} построены индексы, а в +промежуточную страницу помещается \(5\) кортежей. + +\begin{listing} + \caption{Пример запроса с проекцией, фильтрацией и соединением.} + \label{lst:logical_transform_query} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT DISTINCT ename + FROM Emp E JOIN Dept D ON E.did = D.did + WHERE D.dname = 'Toy' + \end{minted} +\end{listing} + +\begin{figure}[!htb]\centering +\begin{tikzpicture}[ + rel/.style={rectangle, draw, rounded corners=2pt, minimum width=2.0cm, + minimum height=0.8cm, align=center, font=\small}, + op/.style={rectangle, draw, fill=gray!12, minimum width=4.5cm, + minimum height=1.2cm, align=center, font=\small} +] + \node[font=\small\bfseries] at (-3.5, 8.8) {План \(P_1\)}; + \node[op] (p1proj) at (-3.5, 7.7) {\(\ProjectionOp{\texttt{ename}}\)}; + \node[op] (p1sig2) at (-3.5, 6.2) {\(\SelOp{\texttt{dname}=\texttt{'Toy'}}\)}; + \node[op] (p1sig1) at (-3.5, 4.7) {\(\SelOp{\texttt{Emp.did}=\texttt{Dept.did}}\)}; + \node[op] (p1cp) at (-3.5, 3.2) {\(\CProd{\texttt{Emp}}{\texttt{Dept}}\)\\\footnotesize \(50{,}050\) чтений}; + \node[rel] (p1emp) at (-5.2, 1.7) {\texttt{Emp}}; + \node[rel] (p1dept) at (-1.8, 1.7) {\texttt{Dept}}; + + \draw[->] (p1emp) -- (p1cp); + \draw[->] (p1dept) -- (p1cp); + \draw[->] (p1cp) -- (p1sig1); + \draw[->] (p1sig1) -- (p1sig2); + \draw[->] (p1sig2) -- (p1proj); + + \node[font=\small\bfseries] at (3.5, 8.8) {План \(P_2\)}; + \node[op] (p2proj) at (3.5, 7.7) {\(\ProjectionOp{\texttt{ename}}\)}; + \node[op] (p2join) at (3.5, 5.5) {Index NL Join\\\(\texttt{Emp.did}=\texttt{Dept.did}\)\\\footnotesize \(23\) чтения}; + \node[rel] (p2emp) at (1.6, 3.5) {\texttt{Emp}}; + \node[op] (p2sig) at (5.4, 3.5) {\(\SelOp{\texttt{dname}=\texttt{'Toy'}}\)\\\footnotesize \(3\) чтения}; + \node[rel] (p2dept) at (5.4, 1.8) {\texttt{Dept}}; + + \draw[->] (p2emp) -- (p2join); + \draw[->] (p2sig) -- (p2join); + \draw[->] (p2join) -- (p2proj); + \draw[->] (p2dept) -- (p2sig); +\end{tikzpicture} +\caption{Сравнение планов для запроса из + листинга~\ref{lst:logical_transform_query}.}% +\label{fig::logical_transform_plans} +\end{figure} + +Рассмотрим два плана~\refImage{fig::logical_transform_plans}. План \(P_1\) +соответствует прямому преобразованию запроса в реляционную алгебру, выполненному +по алгоритму из раздела~\ref{sec:translation}. Если рассматривать стоимость +исполнения такого плана в модели итераторов, то каждый оператор запрашивает у +дочернего следующий кортеж через вызов \texttt{next()}, поэтому \(\sigma\) +и~\(\pi\) работают в режиме конвейера и не инициируют обращений к диску. Вся +стоимость ввода-вывода сосредоточена в операторе~\(\times\), реализованном через +вложенные циклы с \texttt{Dept} в роли внешней таблицы: \texttt{Dept} читается +один раз, а \texttt{Emp} перечитывается полностью для каждой +страницы~\texttt{Dept}: +\[ + 50 + 50 \times 1{,}000 = 50{,}050 \text{ чтений.} +\] + +План \(P_2\) является оптимизированным вариантом, где применено правило +проталкивания предиката и декартово произведение заменено на соединение по +предикату. Причем для доступа к таблице \texttt{Dept} применяется сканирование +по индексу, что дает всего \(3\) чтения с диска. Соединение реализуется через +вложенные циклы с использованием индекса, что в итоге дает: +\[ + 3 + (3 + 20) = 26 \text{ чтений.} +\] + +Оба приведенных примера наглядно демонстрируют, что оптимизатор отвечает за +принципиальную возможность исполнения сложных аналитических запросов за +приемлемое время. В первом случае разница между физическими планами составляет +восемь порядков: если \(P_1\) завершается за секунды, то наивный \(P_2\) +потребует десятков лет. Во втором случае даже единственное логическое +преобразование, в виде проталкивания предиката, снижает число чтений с диска в +\(1{,}900\) раз. Именно поэтому качественный оптимизатор является конкурентным +преимуществом и одной из самых важных частей СУБД. + +\subsection{Обзор архитектур оптимизаторов} + +System~R, разработанный в IBM Research в конце 1970-х годов~\cite{Selinger1979}, +стал первым стоимостным оптимизатором. Его основная идея состоит в восходящем +динамическом программировании. Оптимизатор сначала выбирает лучшие методы +доступа к отдельным отношениям, затем строит лучшие планы для соединений из +двух, трех и большего числа отношений. Преимущество System~R заключается в +простоте и предсказуемости поиска, а также в учете стоимости ввода-вывода, +процессорных затрат и физических свойств операторов. Недостатки связаны с +ограниченной расширяемостью, ориентацией на левосторонние деревья соединений и +независимой оптимизацией вложенных подзапросов. + +Volcano~\cite{GraefeMcKenna1993,graefe2002volcano,Begoli2018Calcite} развивает +стоимостный подход System~R, но делает оптимизатор расширяемым за счет правил и +структуры Memo. В отличие от System~R, пространство эквивалентных выражений +задается не жестко встроенным алгоритмом, а набором правил трансформации и +реализации. Поиск выполняется нисходящим динамическим программированием и может +учитывать требуемые физические свойства. Преимущество Volcano состоит в +модульности и возможности добавлять новые преобразования и операторы без +переписывания всего оптимизатора. Основной недостаток заключается в разделении +поиска на фазу генерации и фазу стоимостного анализа: предварительное раскрытие +всех классов эквивалентности может приводить к избыточной работе. + +\subsection{Архитектура Cascades} + +Cascades~\cite{Graefe1995,XuColumbia1998} является развитием Volcano и +сохраняет его ключевые идеи: правила, Memo, физические свойства и стоимостный +поиск. Главное отличие Cascades состоит в том, что генерация альтернатив и +стоимостная оптимизация не разделяются на две независимые фазы, а чередуются +через систему задач. Благодаря этому оптимизатор может порождать только те +альтернативы, которые нужны для текущего поиска, раньше применять отсечения по +стоимости и удобнее распараллеливать независимые ветви. Недостатком Cascades +является более сложная реализация, так как требуется управлять состоянием групп, +задачами поиска, правилами и физическими свойствами. + +В следующих разделах подробно рассматривается основные элементы архитектуры +оптимизаторов Cascades. + +\subsubsection{Группы эквивалентности и выражения} + +Группа представляет собой множество выражений, которые эквивалентны с +точки зрения логического результата. Например, для отношений \(A\), \(B\) и +\(C\) выражения \( \NJoin{A}{(\NJoin{B}{C})} ; \qquad{} \NJoin{(\NJoin{A}{B})}{C} \) +при выполнении условий ассоциативности соединения принадлежат одной группе, +поскольку возвращают одинаковый результат. Причем физические реализации этих +выражений могут отличаться, а стоимость может зависеть от кардинальности +промежуточных результатов. + +Групповое выражение хранит оператор и ссылки на дочерние группы. Например, +выражение \(Join(G_1, G_2)\) не указывает конкретное дерево для левого и правого +входов, а ссылается на группы \(G_1\) и \(G_2\), каждая из которых может +содержать множество альтернатив. Поэтому одно групповое выражение кодирует +комбинацию многих конкретных деревьев, храня эту информацию компактно. + +Memo является основной структурой данных для хранения групп эквивалентных +выражений в Cascades. Она выполняет функции устранения дублирующихся выражений, +хранит альтернативы, фиксирует результаты оптимизации под разными физическими +свойствами и является точкой синхронизации для задач поиска. + +\begin{listing} + \caption{Пример запроса.} + \label{lst:query} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} + SELECT * FROM A JOIN B ON p_1 JOIN C ON p_2; + \end{minted} +\end{listing} + +На рисунке~\ref{fig:memo-structure} показан пример Memo для запроса из +листинга~\ref{lst:query}. Например, группа \(G_5\) представляет результат +соединения трех отношений, в ней находятся два логически эквивалентных выражения +с разной ассоциацией соединений и четыре физические реализации для них. Входными +данными для операторов являются группы, поэтому общие подвыражения не +дублируются. + +При добавлении нового выражения Memo выполняет проверку на дубликаты. Если +выражение уже существует в некоторой группе, оно не добавляется повторно. В +случае, когда алгоритм в процессе работы обнаруживает эквивалентность двух ранее +различных групп, они эти группы объединяются в одну. При этом требуется +обновление ссылок и состояния правил. + +\subsubsection{Алгоритм поиска} + +Cascades организует оптимизацию как выполнение набора задач. Задача может +исследовать группу, исследовать выражение, применить правило, реализовать +логический оператор, оптимизировать дочернюю группу под заданное физическое +свойство или построить вспомогательный оператор для обеспечения свойства. Такой +подход позволяет гибко управлять порядком работы и откладывать дорогостоящие +действия до тех пор, пока они не станут необходимыми, а также разбивать поиск на +несколько этапов и распараллеливать исполнение независимых веток. При этом +однопоточные реализации хранят задачи в обычной очереди. + +На рисунке~\ref{fig:cascades-tasks} показана схема взаимодействия задач в +оптимизаторе, а на +листингах~\ref{alg:cascades-search}-\ref{alg:cascades-search-2} приведен +псевдокод алгоритма поиска в архитектуре Cascades~\cite{DingNarasayya2024}. + +Алгоритм реализуется в виде управляемого стеком поиска в пространстве +эквивалентных планов: сначала запускается оптимизация корневой группы, +\textsc{OptimizeGroup} в свою очередь исследует группу, а затем оптимизирует +каждое ее логическое выражение. Исследование выполняется через +\textsc{ExploreGroup} и \textsc{ExploreExpression}, которые сохраняют в Memo +новые логические выражения и группы с помощью правил преобразования. После этого +\textsc{OptimizeExpression} применяет правила реализации, превращая логические +выражения в физические. Для каждого физического варианта +\textsc{ApplyImplementation} проверяет локальную стоимость и передает план в +\textsc{OptimizeInputs}, где последовательно оптимизируются дочерние группы, +накапливается полная стоимость и обновляется информация о лучшем плане. Параметр +\texttt{limit} используется для отсечения заведомо дорогих вариантов. + +\subsubsection{Правила трансформации и реализации}\label{sec:transformation_rules} + +Правило в оптимизаторе Cascades состоит из образца, условия применимости и +алгоритма применения. Образец описывает форму логического выражения, к которому +может быть применено правило. Условие применимости проверяет дополнительные +требования: отсутствие внешних ссылок, тип соединения, сохранение семантики +\texttt{NULL}, совместимость свойств и другие ограничения. + +Формально правило записывается в виде +\[ + r : \TransRule{pattern(e)}{condition(e)}{substitute(e)}, +\] +\noindent{} где \(e\) --- исходное выражение, \(pattern\) --- структурный образец, +\(condition\) --- предикат применимости, \(substitute\) --- выражение-замена. +Для правил трансформации результатом применения является новое логическое +выражение в группе, а для правил реализации физическое выражение в той же +группе. + +Правила трансформации задают эквивалентные преобразования логических выражений и +тем самым определяют пространство планов, исследуемое оптимизатором. Наиболее +важные из них представлены в таблице~\ref{tbl:transformation_rules}. + +Правила реализации переводят логические операторы в физические. Используемые в +настоящей работе правила собраны в таблице~\ref{tbl:implementation_rules}. + +Физические свойства в Cascades обрабатываются через требуемые и предоставляемые +свойства. Если родительский оператор требует вход, отсортированный по атрибуту +\(a\), а выбранный дочерний план такого порядка не предоставляет, оптимизатор +может добавить обеспечивающий оператор \Sort. + +Обеспечивающие операторы не изменяют логический результат, но меняют физические +свойства и увеличивают стоимость. Поэтому они рассматриваются наравне с другими +физическими альтернативами. Например, план с хеш-соединением и последующей +сортировкой может конкурировать с планом на основе соединения слиянием, которое +сохраняет порядок результата. + +\subsubsection{Оценка кардинальности} + +Оценка кардинальности является процессом предсказания числа кортежей, +возвращаемых оператором. + +Для выборки используется селективность предиката \(sel(p)\), после чего +кардинальность результата оценивается как + +\[ + |\Sel{p}{R}| = |R| \cdot sel(p). +\] + +Для соединения на основе предиката равенства независимых атрибутов часто +применяется эвристика: + +\[ + |\InnerJoin{R}{R.a = S.b}{S}| \approx + \frac{|R| \cdot |S|}{\max(NDV(R.a), NDV(S.b))}, +\] + +\noindent{}где \(NDV\) --- число различных значений атрибута. Такая оценка удобна, но может +быть грубой, если данные имеют корреляции, сильный перекос или сложные +зависимости между атрибутами. Экспериментальные исследования показывают, что +ошибки кардинальности часто являются одной из основных причин выбора неудачных +планов~\cite{leis2015good}. + +Для повышения качества оценок используются гистограммы, статистики по +многоколоночным ключам, сведения об уникальности, функциональные зависимости, +ограничения на схему, а в современных исследованиях также модели машинного +обучения. + +\subsubsection{Оценка стоимости} + +Функция стоимости сопоставляет физическому плану численную оценку затрат на его +исполнение. Классическим считается представление стоимости в виде взвешенной +суммы операций чтения и записи страниц на диск, а также числа обработанных +кортежей и операций над ними~\cite{GraefeMcKenna1993,Graefe1995}: + +\[ + Cost(p) = w_{io} \cdot IO(p) + w_{cpu} \cdot CPU(p). +\] + +\noindent{}Коэффициенты \(w_{io}\), \(w_{cpu}\) зависят от конкретной аппаратной +конфигурации. + +Стоимость является аддитивной и вычисляется рекурсивно по дереву физического +плана: + +\[ + Cost(node) = LocalCost(node) + \sum_{child \in children(node)} Cost(child). +\] + +Локальная стоимость оператора определяется его типом, кардинальностью входов и +физическими свойствами промежуточного результата. Для конвейерных операторов +стоимость ввода-вывода обычно отсутствует, а для операторов доступа к данным, +сортировки и соединений учитывается объем чтения, записи и обработки входных +кортежей. Конкретные формулы стоимостной модели приведены в +таблице~\ref{tbl:cost-model-coefficients}. + +На оценке стоимости исполнения также основывается метод ветвей и границ. Он +используется для сокращения пространства поиска и, как следствие, +неасимптотического улучшения скорости работы алгоритма. В контексте Cascades +ветвями являются альтернативные выражения и комбинации физических реализаций +дочерних групп, а границей является текущая лучшая стоимость плана для заданной +группы и требуемых физических свойств. Если нижняя оценка стоимости частично +построенного плана уже превышает текущую лучшую, дальнейшее исследование по этой +ветке не имеет смысла. + + +\section{Конструкторская часть} + +В рамках данной работы был разработан оптимизатор запросов для модельной +реляционной СУБД. + +Структурно реализация модельной СУБД состоит из следующих модулей: +\begin{itemize} + \item модуль синтаксического анализа SQL-запросов; + \item модуль построения логического представления запроса; + \item модуль стоимостной оптимизации на основе структуры Memo; + \item подсистема физического хранения на основе статических CSV-таблиц; + \item модуль исполнения физических операторов. +\end{itemize} + +Основой для проектирования оптимизатора была выбрана архитектура Cascades. В ней +пространство допустимых планов формируется с помощью набора правил +преобразования и хранится в структуре Memo. Эта структура объединяет логически +эквивалентные выражения в группы, что позволяет избежать повторного рассмотрения +одинаковых поддеревьев. Поиск оптимального физического плана выполняется с учетом +стоимостной модели и требуемых свойств результата, например порядка сортировки. + +Данная архитектура была выбрана по причине ее модульности и расширяемости. +Добавление новых логических преобразований и физических операторов выполняется +путем реализации отдельных правил и не требует изменения основного алгоритма +поиска. Это также удобно для итеративного процесса разработки, предполагающего +постепенное улучшение качества итоговых планов путем уточнения и расширения +пространства поиска. + +На рисунке~\ref{fig:project-pipeline} представлена диаграмма с процессом +обработки SQL-запроса запроса модельной СУБД. + +\begin{figure}[H] +\centering +\begin{adjustbox}{max width=\textwidth} +\begin{tikzpicture}[ + scale=0.95, transform shape, + node/.style={rectangle, draw, rounded corners=2pt, + text width=3.0cm, minimum height=0.8cm, + align=center, font=\scriptsize}, + main/.style={node, fill=gray!8}, + io/.style={node, fill=gray!20}, + aux/.style={node, dashed}, + arrow/.style={-{Stealth[length=2.5mm]}, thick}, + auxarrow/.style={-{Stealth[length=2.5mm]}, thick, dashed} +] + \node[io] (sql) at (0, 0) {Генератор\\случайных\\запросов}; + \node[main] (parse) at (0, -2) {Разбор}; + \node[main] (optim) at (0, -4.5) {Оптимизация\\ и\\проверка\\ достижимости}; + \node[main] (exec) at (0, -6.65) {Исполнение}; + \node[io] (result) at (0, -8.2) {Результат}; + + \node[aux] (mssqlplan) at (5.0, -1.55) {MSSQL}; + \node[aux] (diff) at (5.0, -4.65) {Проверка\\корректности}; + \node[aux] (mssqlres) at (-5.0, -1.55) {MSSQL}; + \node[aux] (reach) at (-5.0, -4.5) {Конвертация\\в план\\модельной СУБД}; + + \draw[arrow] (sql) -- node[right, font=\scriptsize] {запрос} (parse); + \draw[arrow] (parse) -- node[right, font=\scriptsize] {реляционная алгебра} (optim); + \draw[arrow] (optim) -- node[right, font=\scriptsize] {план} (exec); + \draw[arrow] (exec) -- (result); + + \draw[auxarrow] (sql) -- node[above, font=\scriptsize] {запрос} (mssqlplan); + \draw[auxarrow] (mssqlplan) -- node[right, font=\scriptsize] {результат} (diff); + \draw[auxarrow] (result.east) -- ++(1.2,0) |- (diff.west); + + \draw[auxarrow] (sql) -- node[above, font=\scriptsize] {запрос} (mssqlres); + \draw[auxarrow] (mssqlres) -- node[left, font=\scriptsize] {план} (reach); + \draw[auxarrow] (reach.east) -- node[above, font=\scriptsize] {планы} (optim.west); +\end{tikzpicture} +\end{adjustbox} +\caption{Процесс обработки запроса в модельной СУБД.}% +\label{fig:project-pipeline} +\end{figure} + +\subsection{Модули синтаксического анализа и построения логического представления} + +Модуль синтаксического анализа SQL-запросов отвечает за лексический и +синтаксический разбор поступающих на вход SQL-запросов. В качестве основы для +грамматики выбран диалект PostgreSQL~\cite{PostgresDocs}, так как он широко +используется на практике и имеет открытый исходный код. Использование готовой +грамматики из официального репозитория~\cite{PostgresBisonGrammar} гарантирует +совместимость. + +В рамках работы поддерживается подмножество диалекта PostgreSQL, достаточное для +исследования оптимизации запросов на чтение данных. В него входят операторы +\SELECT{} с предложениями \FROM, \WHERE, \GROUPBY{} и \ORDERBY{}, выбор всех +столбцов или списка выражений, псевдонимы, соединения \JOIN, \sqlkw{INNER JOIN}, +\sqlkw{LEFT JOIN}, \sqlkw{RIGHT JOIN}, \sqlkw{FULL JOIN} с условием \sqlkw{ON}, +а также \sqlkw{CROSS JOIN}. Для группировки поддерживаются обычные выражения в +\GROUPBY{} и агрегатные функции \SUM{}, \COUNT{} и \sqlkw{COUNT(*)}. Сортировка +результата задается через \ORDERBY{} по ссылкам на столбцы в прямом или обратном +порядке. + +Поддерживаемые скалярные выражения включают ссылки на столбцы, целочисленные +константы, строковые литералы в одинарных кавычках, значения \sqlkw{NULL}, +\sqlkw{TRUE}, \sqlkw{FALSE} и \sqlkw{UNKNOWN}, арифметические операции сложения, +вычитания, умножения, деления, остатка от деления и возведения в степень, а +также унарный минус. В предикатах поддерживаются операции сравнения, логические +связки \sqlkw{AND}, \sqlkw{OR} и \sqlkw{NOT}, проверки \sqlkw{IS NULL}, +\sqlkw{IS TRUE}, \sqlkw{IS FALSE}, \sqlkw{IS UNKNOWN}, условия \sqlkw{BETWEEN} и +\sqlkw{IN} со списком значений. Такое подмножество покрывает основные +конструкции, влияющие на построение логического плана: проекцию, фильтрацию, +соединение, агрегацию и требование сортировки результата. + +Модуль построения логического представления запроса отвечает за преобразование +результата синтаксического анализа во внутреннее представление, пригодное для +оптимизации. В качестве такого представления решено использовать дерево +операторов реляционной алгебры. Этот подход является общепринятым и позволяет +отделить особенности синтаксиса SQL от последующих этапов обработки запроса, а +также выполнять оптимизацию над ограниченным набором формализованных операций: +фильтрацией, проекцией, соединением, агрегацией и сортировкой. + +\subsection{Модули физического хранения и исполнения} + +Подсистема физического хранения отвечает за предоставление модулю исполнения +табличных данных. Для хранения выбраны статические таблицы в формате CSV. +Использование простого текстового формата позволяет сосредоточиться на +проектировании оптимизатора и не усложнять прототип реализацией транзакций, +индексов, буферного кэша и управления дисковыми страницами. Кроме того, +CSV-файлы удобно создавать, изменять и использовать при проведении тестов. + +Данные хранятся в одной директории, где каждому отношению соответствует +файл с CSV-таблицей. Название файла совпадает с названием отношения. +Первая строка CSV-файла содержит описание атрибутов и их типов, +последующие строки содержат кортежи отношения. + +Дополнительно предусмотрена поддержка индексов. Индекс представляет собой +отсортированный бинарный файл, содержащий целочисленные ключи и смещения +соответствующих строк в CSV-файле. Использование смещений позволяет +извлекать подходящие кортежи без полного сканирования отношения. + +Модуль исполнения физических операторов отвечает за выполнение выбранного +оптимизатором плана и формирование результата запроса. Физический план решено +представлять в виде дерева операторов с единым интерфейсом взаимодействия. +Каждый оператор получает данные от дочерних узлов, выполняет определенное +преобразование и передает результат вышестоящему оператору. Такой подход +обеспечивает модульность подсистемы исполнения и позволяет добавлять новые +физические операторы независимо от уже реализованных. + +\subsection{Модуль стоимостной оптимизации планов} + +Модуль стоимостной оптимизации планов принимает на вход логическое представление +запроса и строит физический план исполнения с минимальной оцененной стоимостью. +В качестве основы выбран подход Cascades. Исходное дерево логических операторов +помещается в структуру Memo, после чего оптимизатор постепенно расширяет +пространство поиска с помощью правил преобразования и правил реализации. Такой +подход позволяет не хранить каждое дерево плана отдельно, а компактно +представлять множество эквивалентных вариантов через группы Memo. + +Каждая группа Memo соответствует множеству логически эквивалентных выражений, +то есть выражений, возвращающих один и тот же результат. Логические выражения +содержат ссылки на дочерние группы, поэтому один узел может представлять сразу +несколько вариантов построения поддерева. В процессе оптимизации в группы +добавляются как новые логические выражения, полученные правилами +трансформации, так и физические выражения, полученные правилами реализации. + +Правило трансформации создает новое логическое выражение и добавляет его в +ту же группу Memo, что и исходное выражение. Следовательно, правило должно +сохранять семантику запроса. Правило реализации создает физическое выражение, +которое может быть передано модулю исполнения. + +Реализованы следующие трансформационные правила: +\begin{itemize} + \item коммутативность соединения; + \item ассоциативность соединения; + \item разбиение фильтра на конъюнкты; + \item объединение последовательных фильтров; + \item проталкивание фильтра через проекцию; + \item проталкивание фильтра во входы соединения; + \item преобразование выражения \verb|IN| в цепочку сравнений; + \item перенос фильтра в предикат соединения; + \item превращение декартова произведения в соединение; + \item поднятие фильтра над соединением; + \item проталкивание проекции через соединение; + \item преобразование внешнего соединения во внутреннее; + \item проталкивание агрегации ниже соединения; + \item перестановка агрегации и соединения. +\end{itemize} + +И следующие правила реализации: +\begin{itemize} + \item последовательное сканирование таблицы; + \item фильтрация; + \item сканирование по индексу; + \item проекция; + \item соединение вложенными циклами; + \item декартово произведение вложенными циклами; + \item хеш-соединение; + \item соединение слиянием; + \item хэш-агрегация; + \item потоковая агрегация. +\end{itemize} + +Данный набор правил покрывает все основные алгоритмы, реализующие операторы +реляционной алгебры, и включает самые важные правила трансформации. Также более +полное покрытие правилами реализации хорошо подходит для демонстрации +возможностей дифференциального сравнения планов для последующего расширения +покрытия правилами трансформации. + +Выбор лучшего плана выполняется на основе стоимостной модели. Пусть для группы +\(G\) и требуемого свойства \(P\) уже найден план стоимости \(C^*\). +Рассматривается физическое выражение \(e\), имеющее локальную стоимость +\(C_{local}(e)\) и дочерние группы \(G_1, \ldots, G_k\). Если даже минимально +возможная оценка дочерних групп приводит к неравенству + +\[ + C_{local}(e) + \sum_{i=1}^{k} LB(G_i, P_i) \geq C^*, +\] + +\noindent{}то выражение \(e\) может быть отсечено. Здесь \(LB\) обозначает +нижнюю границу стоимости оптимизации дочерней группы под требуемым свойством +\(P_i\). Чем точнее нижняя граница, тем эффективнее отсечение. При этом \(C^{*}\) +должна быть вычислена в том же запуске оптимизатора, а значит использует оценки +дочерних групп не хуже чем \(LB(G_{i}, P_{i}), i=1, \ldots{} ,k\). + +В качестве \(LB(G_i, P_i)\) принимается лучшая стоимость, найденная для пары +\((G_i, P_i)\) на текущий момент работы алгоритма. Поскольку оптимизация группы +является рекурсивным процессом, к моменту отсечения для \((G_i, P_i)\) уже могут +быть найдены некоторые планы, и их минимальная стоимость служит нижней границей. +Если же группа \(G_i\) под свойством \(P_i\) ещё не рассматривалась, +используется минимальная из стоимостей физических операторов, которые реализуют +логический оператор из группы \(G_{i}\). + +Требуемые свойства дочерних групп \(P_i\) определяются оператором выражения +\(e\) совместно с требованием родителя \(P\). Каждый физический оператор +описывает, какие свойства он ожидает от своих входов: например, оператор +сортирующего слияния требует упорядоченности обоих входов по соответствующим +ключам, тогда как хеш-соединение не предъявляет требований к порядку. Таким +образом, при спуске по дереву поиска каждый оператор транслирует глобальное +требование \(P\) в конкретные требования \(P_1, \ldots, P_k\) к своим дочерним +группам. + +Стоимость физического оператора рассчитывается с учетом оценок кардинальности +входных и выходных групп. Формулы для вычисления стоимости откалиброваны по +результатам бенчмарков и представлены в +таблице~\ref{tbl:cost-model-coefficients}. Кардинальность базовых отношений +берется из описания входных таблиц, а для фильтров, соединений, агрегаций и +проекций оценивается эвристически. + +Для оценки кардинальности промежуточных результатов используется коэффициент +селективности \(sel(p)\), показывающий долю кортежей, удовлетворяющих предикату +\(p\). Используемые формулы приведены в таблице~\ref{tbl:selectivity-model}. + +Такие формулы выбраны как простая эвристическая модель, не требующая гистограмм, +сведений о количестве различных значений и статистик корреляции между +атрибутами. Равенство атрибута константе считается достаточно селективным, +поэтому ему сопоставляется коэффициент \(0{,}10\). Диапазонные и неизвестные +условия оцениваются нейтральным коэффициентом \(0{,}50\), так как без статистики +нельзя надежно определить, какую часть отношения они оставят. Для \sqlkw{IN} +используется сумма селективностей отдельных равенств с верхней границей +\(0{,}50\), чтобы длинный список значений не делал оценку слишком оптимистичной. + +Формулы для \sqlkw{AND}, \sqlkw{OR} и \sqlkw{NOT} основаны на предположении +независимости предикатов. Для эквисоединения двух атрибутов используется оценка +\(1 / \max(n_l, n_r)\), соответствующая типичному случаю соединения по ключу или +внешнему ключу, размер результата в таком случае оказывается сопоставим с +размером большего входа, а не с полным декартовым произведением. Для неизвестных +условий соединения применяется селективность \(1\), чтобы не занижать стоимость +плана при отсутствии надежной информации. + +Также учитываются физические свойства результата. В данной работе таким +свойством является порядок строк, требуемый предложением \ORDERBY{}. Если +выбранный план не обеспечивает требуемую сортировку, оптимизатор может добавить +обеспечивающий физический оператор сортировки и сравнить его стоимость с другими +альтернативами. Результатом работы модуля является дерево физических операторов, +которое передается в модуль исполнения. + +\subsection{Дифференциальный анализ физических планов} + +Множество правил трансформации и реализации, используемых в +оптимизаторах запросов, изучено довольно хорошо и описано в +академической литературе. Вместе с тем условия активации правил +существенно различаются от системы к системе. Многие реализации +оптимизаторов упускают возможности для оптимизации из-за слишком +строгих условий активации правил или отсутствия определенных правил +в их наборе. + +Данная проблема особенно актуальна при разработке нового оптимизатора: после +реализации базового набора правил возникает вопрос о полноте и корректности +этого набора по сравнению с устоявшимися и проверенными временем промышленными +системами. Такие системы аккумулировали обратную связь от пользователей и +уточняли условия активации своих правил в течение многих лет. Не имея такого +преимущества сложно добиться отличного качества построенных планов. + +Решением может быть поиск несоответствий между поведением реализуемой системы и +внешней эталонной системы путем применения дифференциального анализа планов. + +Первым шагом нужно сформировать несколько наборов данных, состоящих из схем +таблиц и их наполнения. Разные данные позволяют всесторонне исследовать правила +активации, потому что они зависят от кардинальности отношений, наличия индексов +и т.п. + +Далее, по заданным схемам генерируются случайные SQL-запросы различной структуры +и сложности. Такой подход исключает тривиальные планы и приближает условия +работы оптимизатора к реальным нагрузкам при дальнейшем построении планов +запросов. + +Следующим шагом сгенерированные запросы транспилируются в диалекты SQL целевых +промышленных СУБД. В качестве эталона рассматривается Microsoft SQL Server. Эта +система является зрелой и имеет машиночитаемый формат вывода планов. Затем для +получения физических планов исполняется аналог \texttt{EXPLAIN ANALYZE}. + +Полученные физические планы промышленных СУБД конвертируются в формат +сериализованного плана разрабатываемого оптимизатора. Современные СУБД +предоставляют вывод планов в виде XML или JSON, что позволяет легко реализовать +их разбор. Для конвертации используется отображение между физическими +операторами разрабатываемой системы и операторами эталонной СУБД. Например, +\texttt{Hash Match} в SQL Server отображается в \HashJoin{} в разрабатываемой +системе, \texttt{Clustered Index Scan} --- в \IndexScan{} и так далее. +Восстановление предикатов, ключей соединения и выражений проекции выполняется +путем разбора соответствующих полей плана, причем не нужно поддерживать весь +синтаксис предикатов, так как генератор запросов и схемы данных ограничивают +множество допустимых выражений в планах. + +Для сериализации физических планов было решено использовать s-выражения, похожие +на выражения языка LISP. Этот формат прост с точки зрения реализации парсера, а +также удобен для чтения и изменения человеком. Пример сериализованного плана для +запроса \texttt{SELECT * FROM users WHERE users.age BETWEEN 18 AND 30} приведен +на листинге~\ref{lst:serialized-plan}. + +\begin{listing}[H] + \caption{Пример сериализованного плана.} + \label{lst:serialized-plan} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{text} +(PhysicalFilter + (and (>= (attr users age) 18) + (<= (attr users age) 30)) + (SeqScan users)) + \end{minted} +\end{listing} + +Корректность конвертации выполняется по построению, но дополнительно проверяется +путем исполнения исходного плана на внешней системе и сконвертированного плана +на разрабатываемой системе с последующим сравнением полученных результатов при +фиксированной схеме и различных вариантах сгенерированных для нее данных. + +Конвертированные планы сравниваются с планами, порождаемыми разрабатываемым +оптимизатором на исходных запросах, с целью определить, мог ли существующий +набор правил разрабатываемого оптимизатора породить план, эквивалентный плану +промышленной системы. Стоимостные модели и реализации операторов в разных СУБД +различаются, поэтому план, оптимальный для эталонной системы, не обязательно +будет оптимальным для разрабатываемой. По этой причине сравнение направлено на +исследование полноты пространства поиска разрабатываемого оптимизатора, и не +предполагает сравнение эффективности планов. + +Для корректности структурного сравнения необходимо полное исследование +пространства эквивалентных планов. Определение требуемых изменений лежит на +разработчике системы, поэтому найденный запрос и примеры планов сохраняются для +последующего анализа. В дальнейшем, полученные после конвертации ``эталонные'' +планы можно использовать как тесты для оптимизатора, которые будут успешно +выполняться после уточнения соответствующих правил активации. + +В случае невозможности построить ``эталонный'' план разрабатываемым +оптимизатором, предлагается искать ближайший план, т.е. план из пространства +поиска разрабатываемого оптимизатора, максимально близкий к плану промышленной +системы. При этом метрика расстояния между планами может быть определена как +расстояние редактирования деревьев. + +Анализ ближайшего плана может помочь упростить выявление трансформаций, которые +разрабатываемый план по какой-то причине не применил. + +Предлагаемый метод дифференциального анализа применим для сравнения как с +проприетарными системами, так и с СУБД с открытым исходным кодом, позволяя +уточнять условия применения правил. + +Систематическое применение этого подхода позволяет построить итеративный процесс +совершенствования оптимизатора: + +\begin{itemize} + \item генерация запросов; + \item запуск и сбор планов; + \item приведение к единому виду, сравнение и выявление расхождений; + \item добавление или корректировка правил; + \item повторное сравнение. +\end{itemize} + +Таким образом, дифференциальный анализ обеспечивает управляемое и измеримое +улучшение качества оптимизатора и позволяет новым или открытым оптимизаторам +быстрее достигать качества устоявшихся промышленных оптимизаторов. + +% {\color{red} TODO: описать как нормализовать и т.п.} + + + +\section{Технологическая часть} + +Программа написана на языке C++ с использованием стандарта C++23. Этот язык +популярен в сфере разработки СУБД, являясь достаточно низкоуровневым и +производительным, но в то же время удобным для реализации больших систем. Для +сборки применяется CMake и его встроенные средства для зависимостей в виде +библиотек на C++. Окружение для разработки описывается декларативно и +воспроизводимо при помощи функционального доменно-специфичного языка \verb|nix| +для соответствующего пакетного менеджера. + +Для синтаксического анализа выбран генератор парсеров ANTLR4, ввиду простоты и +удобства в использовании. Асинхронное взаимодействие физических операторов +построено на Boost.Asio, по причине совместимости со встроенными в язык +корутинами и понятной модели асинхронности. Для тестирования корректности и +производительности используется Google Tests, Google Benchmarks и pytest. + +\subsection{Модуль синтаксического анализа} + +Для реализации парсера был выбран ANTLR4, ввиду его удобства и возможности +генерировать лексические и синтаксические анализаторы под практически любой язык +программирования. + +Грамматика PostgreSQL была взята из официального репозитория проекта +ANTLR4~\cite{Antlr4PostgreSQL}. Она была портирована автоматически из грамматики +для Bison~\cite{PostgresBisonGrammar} из официального репозитория Postgres, +поэтому является наиболее полной из существующих. + +ANTLR4 генерирует несколько файлов на C++, в том числе лексический и +синтаксический анализаторы, а также класс \verb|Visitor| для обхода дерева +разбора. Для интересующих конструкций SQL переопределены методы посещения узлов +грамматики. Например, предложение \WHERE{} преобразуется в логический оператор +фильтрации, список выражений после \SELECT{} преобразуется в проекцию, а список +таблиц после \FROM{} --- в дерево соединений. + +Результатом работы парсера является структура \texttt{ParsedQuery}. Она содержит +логический оператор верхнего уровня и необязательное требование к порядку строк +для \ORDERBY{}. Логические операторы и скалярные выражения представлены +алгебраическими типами на основе \texttt{std::variant}. В них входят операторы +таблицы, фильтрации, проекции, агрегации, соединения, ссылки на атрибуты, +константы и арифметические выражения~\refAlgo{lst:logical}. + +\begin{listing}[H] + \caption{Объявление типов для операторов реляционной алгебры и выражений.} + \label{lst:logical} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{C++} +using Operator = std::variant; + +using Expression = std::variant; + +struct Projection { + std::vector expressions; + std::shared_ptr source; + std::vector> aliases; + + bool operator==(const Projection& other) const; +}; + \end{minted} +\end{listing} + +Выражения хранятся в алгебраическом типе \verb|Expression|, реализованном +аналогично \verb|Operator|. + +Функция \verb|GetAST| внутри строит абстрактное синтаксическое дерево и обходит +его с помощью \verb|Visitor|, который и генерирует представление запроса в виде +дерева из \verb|Operator|. + +Дополнительно реализована визуализация дерева из операторов реляционной алгебры +в формате Graphviz с помощью функции \verb|GetDotRepresentation|. + +\subsection{Модуль оптимизации запросов} + +Модуль оптимизации запросов отвечает за преобразование логического дерева +операторов в физический план исполнения с минимальной оцененной стоимостью. +Пространство исследуемых эквивалентных планов эффективно хранится в структуре +Memo и расширяется с помощью добавления новых правил. Поиск выполняется сверху +вниз с использованием стоимостной модели, физических свойств и метода ветвей и +границ. + +Центральное место в оптимизаторе занимает структура Memo, которая хранит группы +логически эквивалентных выражений. Выражения из одной группы возвращают +одинаковый результат. Например, $A \Join B$ и $B \Join A$ являются +эквивалентными. + +Дочерние узлы выражения ссылаются не на конкретные операторы, а на группы. +Благодаря этому одно выражение компактно представляет множество деревьев. +Объявления классов \verb|Memo| и \verb|Group| приведены в +листинге~\ref{lst:memo}. + +\begin{listing}[H] + \caption{Основные элементы структуры Memo.} + \label{lst:memo} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +class Memo { +public: + utils::NotNull AddGroup(LogicalOperator op); + utils::NotNull AddLogicalExprToGroup( + utils::NotNull group, LogicalOperator op); + utils::NotNull Populate(const Operator& op); + +private: + std::deque groups_; + std::unordered_map expr_index_; +}; + +class Group { +public: + utils::NotNull AddLogicalExpr(LogicalOperator op); + utils::NotNull AddPhysicalExpr( + PhysicalOperator op, bool is_enforcer = false); + + LogicalExprs GetLogicalExprs(); + PhysicalExprs GetPhysicalExprs(); +}; + \end{minted} +\end{listing} + +Метод \verb|Memo::Populate| рекурсивно обходит исходное логическое дерево и +создает первоначальные группы. Для предотвращения повторного добавления +выражений используется отображение \verb|expr_index_|. Ключ строится из типа +оператора, его параметров и идентификаторов дочерних групп. Таким образом, +одинаковые выражения добавляются в Memo только один раз. + +Каждая группа содержит логические и физические выражения. Логические выражения +описывают семантику запроса, а физические выражения определяют конкретный способ +исполнения. Например, логическому соединению могут соответствовать физические +операторы соединения вложенными циклами и хеш-соединения. + +\subsubsection{Правила трансформации и реализации} + +Расширение пространства поиска выполняется с помощью правил. Реализовано два +типа правил: трансформационные и реализационные. % Их интерфейсы приведены в +% листинге~\ref{lst:optimizer-rules}. +Правила реализуют единый интерфейс, состоящий из условия применимости и метода +для применения правила. Условие применимости выполняет быструю структурную +проверку по дереву выражений в Memo. Метод применения правила порождает +необходимые новые группы и результирующее логическое или физическое выражение. В +качестве примера в листинге~\ref{lst:join-commutativity} приведена реализация +правила коммутативности соединений. + +% \begin{listing}[H] +% \caption{Интерфейсы правил оптимизации.} +% \label{lst:optimizer-rules} +% \begin{minted}[style=bw, breaklines, frame=single, +% fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +% class TransformationRule { +% public: +% virtual bool IsApplicable(utils::NotNull expr) = 0; +% utils::NotNull Apply( +% utils::NotNull expr, Memo& memo); +% private: +% virtual LogicalOperator ApplyImpl( +% utils::NotNull expr, Memo& memo) = 0; +% }; +% class ImplementationRule { +% public: +% virtual bool IsApplicable(utils::NotNull expr) = 0; +% virtual utils::NotNull Apply( +% utils::NotNull expr, Memo& memo) = 0; +% }; +% \end{minted} +% \end{listing} + +Для каждого выражения хранится информация о ранее примененных правилах. Это +предотвращает повторное выполнение одного правила над одним выражением и +ограничивает образование циклов при заполнении Memo. + +\subsubsection{Алгоритм поиска} + +Стоимость физического плана зависит от количества обрабатываемых кортежей. Для +получения этой величины используется класс \verb|CardinalityEstimates|. Оценка +кардинальности вычисляется для группы Memo и кэшируется. + +Стоимость плана вычисляется как сумма локальных стоимостей физических +операторов. Фрагмент реализации стоимостной модели приведен в +листинге~\ref{lst:optimizer-cost}. + +Класс \verb|Optimizer| реализует алгоритм поиска. При создании объекта исходное +логическое дерево помещается в Memo. Поиск запускается для корневой группы и +требуемых физических свойств результата. + +Алгоритм использует стек отложенных задач. Это позволяет разделить процесс на +небольшие операции: исследование группы, применение правила, оптимизацию +физического выражения и обработку его дочерних узлов. Основной метод оптимизации +группы приведен в листинге~\ref{lst:optimize-group}. + +Если группа еще не исследована, в стек добавляются две задачи. Первая расширяет +группу с помощью правил, вторая повторно запускает ее оптимизацию после +расширения. Если группа уже исследована, для каждого физического выражения +вычисляется полная стоимость с учетом дочерних групп. + +Для каждой пары из группы и требуемого набора свойств хранится лучший найденный +вариант. Ключ такого варианта представлен структурой +\verb|WinnerKey|~\refAlgo{lst:winner-key}. Хранение победителя отдельно для каждого +набора свойств необходимо, поскольку самый дешевый неотсортированный план может +отличаться от самого дешевого плана, удовлетворяющего требованию +\verb|ORDER BY|. + +\begin{listing}[H] + \caption{Ключ для хранения лучшего плана по группе и свойствам.} + \label{lst:winner-key} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +struct WinnerKey { + Group* group; + PropertySet required; +}; +\end{minted} +\end{listing} + +Дочерние группы физического выражения оптимизируются последовательно. После +получения победителя дочерней группы его стоимость прибавляется к накопленной +стоимости. Если все дочерние группы обработаны, проверяются выходные свойства +плана. Затем найденный вариант может стать новым лучшим планом. + +\subsubsection{Метод ветвей и границ} + +Полный перебор пространства планов быстро становится непрактичным при +увеличении количества соединяемых таблиц. Для сокращения перебора используется +метод ветвей и границ. + +Для каждой группы вычисляется нижняя граница стоимости. Она равна минимальной +сумме нижней оценки локального оператора и нижних оценок дочерних групп. +Например, для логического соединения выбирается минимум между стоимостью +хеш-соединения и стоимостью соединения вложенными циклами: +\[ + LB_{\text{join}} = + \min(69(N_l + N_r), 70N_lN_r). +\] + +\noindent{}Если нижняя граница не меньше стоимости уже известного решения, +исследование ветви прекращается. Фрагмент реализации приведен в +листинге~\ref{lst:lower-bound}. + +\begin{listing}[H] + \caption{Вычисление нижней границы стоимости группы.} + \label{lst:lower-bound} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +int64_t Optimizer::LowerBoundCost(Group* group) { + if (auto it = lower_bounds_.find(group); + it != lower_bounds_.end()) { + return it->second; + } + int64_t best = std::numeric_limits::max(); + for (auto expr : group->GetLogicalExprs()) { + int64_t cost = LowerBoundLocalCost(expr, cardinality_); + for (auto child : GetChildren(expr)) { + cost += LowerBoundCost(child); + } + best = std::min(best, cost); + } + lower_bounds_[group] = best; + return best; +} + \end{minted} +\end{listing} + +Метод \verb|OptimizeInputs| также уменьшает границу для очередного дочернего +узла. Из общей допустимой стоимости вычитаются уже накопленная стоимость и +нижние оценки еще не обработанных дочерних групп. Благодаря этому заведомо +неоптимальные ветви отбрасываются до построения полного плана. + +% Для исследовательских задач предусмотрен метод \verb|OptimizeExhaustive|. Он +% запускает тот же алгоритм без ограничения стоимости и используется при проверке +% достижимости внешних физических планов. + +\subsubsection{Физические свойства} + +Некоторые требования к результату нельзя выразить только стоимостью. Например, +конструкция \verb|ORDER BY| требует от результата определенного порядка +кортежей. Для представления таких требований используется класс +\verb|PropertySet|. + +Например, порядок сортировки считается удовлетворяющим требованию, если +требуемые ключи являются префиксом фактически полученного порядка. Таким образом +сортировка по $(a, b)$ удовлетворяет требованию сортировки по $a$. + +Некоторые операторы сохраняют порядок дочернего узла. Фильтрация и проекция +сохраняют свойства входа. Соединение вложенными циклами сохраняет свойства +левого входа. Хеш-соединение и агрегация не гарантируют сохранение порядка. + +Если подходящий план не найден, оптимизатор пытается добавить обеспечивающий +оператор. Для сортировки используется класс \verb|SortEnforcer|. Он проверяет +наличие требуемых атрибутов в схеме группы и создает физический оператор +сортировки. + +\begin{listing}[H] + \caption{Добавление оператора сортировки для обеспечения свойства.} + \label{lst:sort-enforcer} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +std::optional SortEnforcer::TryBuild( + utils::NotNull group, + const PropertySet& required, + SchemaCatalog& schema) const { + const auto* sort = required.Get(); + if (!sort) return std::nullopt; + if (!ContainsRequiredKeys(schema.GetSchema(group), + sort->order)) { + return std::nullopt; + } + return physical::Sort{group, sort->order}; +} + \end{minted} +\end{listing} + +\subsubsection{Формирование физического плана} + +После завершения поиска метод \verb|BuildOptimalPlan| рекурсивно восстанавливает +итоговое дерево физических операторов. Для каждой группы выбирается сохраненный +лучший план с учетом требуемых свойств. Затем аналогичным образом +восстанавливаются планы дочерних групп. + +Результатом работы оптимизатора является объект \verb|PhysicalPlanNode|, который +не зависит от структуры Memo. Он может быть сериализован для анализа или передан +модулю исполнения запросов. + +Таким образом, модуль оптимизации разделяет представление пространства планов, +правила его расширения, стоимостную модель и алгоритм поиска. Такая организация +позволяет добавлять новые правила и физические операторы без изменения основной +логики оптимизатора. + +\subsection{Модуль физического хранения данных} + +Для чтения таблиц реализован класс \verb|CsvDirSequentialScanner|. Он выполняет +последовательное сканирование CSV-файла и передает прочитанные кортежи модулю +исполнения физических операторов. + +Список доступных индексов задается в файле \verb|indexes.meta|, +расположенном в директории с данными. Каждая строка файла содержит +название отношения, название индексируемого атрибута, тип индекса и имя +бинарного файла~\refAlgo{lst:meta}. + +\begin{listing}[H] + \caption{Пример файла с метаданными о доступных индексах.} + \label{lst:meta} +\begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{text} +users id sorted users.id.sorted.idx +\end{minted} +\end{listing} + +Для чтения данных с использованием индекса реализован класс +\verb|CsvDirIndexedScanner|. Если указанный в метаданных бинарный файл +отсутствует, индекс строится при первом обращении к таблице. Для поиска +диапазона подходящих ключей используется упорядоченность записей индекса. + +Поддерживаются индексы только для целочисленных атрибутов. Индексное +сканирование применяется для предикатов сравнения атрибута с целочисленной +константой. Из возможных операций доступно равенство, строгое и нестрогое +сравнение. После чтения найденных строк дополнительно вычисляется исходный +предикат, что обеспечивает корректную обработку составных условий. + +\subsection{Модуль исполнения физических планов} + +За исполнение запросов отвечает класс \verb|Executor|~\refAlgo{lst:executor}. Он +получает физический план, запускает исполнение его операторов и формирует +результирующее отношение. Физический план представлен деревом объектов типа +\verb|PhysicalPlanNode|. Для каждого типа узла предусмотрена отдельная функция +исполнения реализующая единый интерфейс и исполняющаяся независимо. + +\begin{listing}[H] + \caption{Основные элементы объявления класса Executor.} + \label{lst:executor} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{C++} +template +class Executor { +public: + Executor(SequentialScan seq_scan, + IndexScan index_scan, + boost::asio::any_io_executor executor); + boost::asio::awaitable> + Execute(const PhysicalPlanNode& op); +private: + SequentialScan sequential_scan_; + IndexScan index_scan_; + ExpressionExecutor expression_executor_; +}; + \end{minted} +\end{listing} + +Класс параметризуется интерфейсами \verb|SequentialScan|, \verb|IndexScan| и +\verb|ExpressionExecutor|. Первые два интерфейса отделяют исполнение физического +плана от конкретного способа хранения данных. \verb|SequentialScan| выполняет +полное чтение таблицы, а \verb|IndexScan| получает дополнительный предикат и +извлекает строки с использованием индекса. Благодаря этому исполнитель не +зависит от формата CSV-файлов и устройства индексных структур. + +Параметр \verb|ExpressionExecutor| определяет способ вычисления скалярных +выражений. Реализованы стратегии для интерпретации выражений и для +JIT-компиляции. По умолчанию используется интерпретируемый вариант. + +Значения атрибутов представлены структурой \verb|Value|. Она содержит признак +\verb|NULL| и значение одного из поддерживаемых типов: целого числа, логического +значения или идентификатора строки. Исходное значение для строк сохраняется в +пул, а в кортеже хранится числовой идентификатор строки. Кортеж представлен +вектором значений, а отношение состоит из описания атрибутов и списка кортежей. + +Коммуникация между физическими операторами осуществляется с помощью асинхронных +каналов. Используются два типа каналов: \verb|AttributesInfoChannel| для +однократной передачи схемы отношения и \verb|TuplesChannel| для передачи данных. +Кортежи передаются векторами. Это уменьшает число операций синхронизации и +позволяет потоковым операторам начинать обработку до полного завершения дочерних +узлов плана. + +Для реализации каналов используется +boost::asio::experimental::concurrent\_\-channel. Данный класс обеспечивает +потокобезопасность, буферизацию, асинхронную передачу данных и совместимость с +корутинами. Такой подход позволяет единообразно организовать взаимодействие +между операторами независимо от конкретных алгоритмов для их реализации. + +\subsection{Руководство пользователя} + +Для сборки программы требуются CMake, компилятор C++ с поддержкой C++23, LLVM, +ANTLR4 и библиотеки Boost. Дополнительно требуется Python, pytest и Docker. В +репозитории присутствует файл \texttt{flake.nix}, описывающий необходимые +зависимости, поэтому в системе с пакетным менеджером Nix рекомендуется открыть +окружение при помощи команды \verb|nix develop|. + +Все команды далее выполняются из корневого каталога проекта. + +Для выполнения конфигурации и сборки следует запустить команды на +листинге~\ref{lst:build}. + +\begin{listing}[H] + \caption{Конфигурация и сборка проекта.} + \label{lst:build} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{bash} +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -- -j 6 + \end{minted} +\end{listing} + +После успешной сборки исполняемый файл находится по пути +\texttt{build/bin/sql}. Для повторной сборки также можно использовать цель +\texttt{make build}. + +Пользователь должен создать отдельную директорию и поместить в нее CSV-файлы. +Имя каждого файла без расширения считается именем таблицы. В заголовке каждого +файла для столбца указывается имя и тип через двоеточие. Поддерживаются типы +\texttt{int} и \texttt{string}. Пустое SQL-значение записывается как +\texttt{NULL}. Для первого запуска можно использовать готовые тестовые данные в +\verb|test/static/executor/test_data|. SQL-запрос передается программе через +стандартный поток ввода. Простейший пример запуска представлен на +листинге~\ref{lst:run-example} и на рисунке~\ref{fig:practice-run}. + +\begin{listing}[H] + \caption{Примеры команд для запуска программы.} + \label{lst:run-example} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{bash} +echo 'SELECT users.id FROM users;' | \ + ./build/bin/sql --data-dir test/static/executor/test_data + +echo 'SELECT users.id FROM users WHERE users.age > 18 \ +ORDER BY users.id;' | \ + ./build/bin/sql --data-dir test/static/executor/test_data \ + --print-ast --print-plan + \end{minted} +\end{listing} + +Программа выводит заголовок отношения, содержащий имена и типы столбцов, а +затем полученные строки. Если запрос не содержит \ORDERBY{}, строки вывода +сортируются лексикографически для воспроизводимости результата. + +Для диагностики предусмотрены параметры \texttt{--print-ast} и +\texttt{--print-plan}. Первый параметр выводит логическое дерево после +синтаксического анализа, второй --- выбранный физический план. +Пример использования представлен на листинге~\ref{lst:run-example}. + +% \begin{figure}[H] +% \centering +% \includegraphics[width=0.8\textwidth]{run-with-prints.png} +% \caption{Вывод логического дерева и физического плана}% +% \label{fig:practice-plan} +% \end{figure} + +% Для проверки полноты набора правил оптимизации предусмотрен режим +% \verb|--check-reachable|. В этом режиме программа получает SQL-запрос и +% сериализованный физический план, после чего выполняет полный перебор +% пространства поиска без отсечения по стоимости. Если заданный план может быть +% получен с помощью реализованных правил, программа выводит сообщение +% \verb|REACHABLE|. + +% План передается в отдельном файле в формате S-expression. Например, содержимое +% файла \verb|target.plan| может иметь вид, представленный на листинге~\ref{lst:target-plan}. + +% \begin{listing}[H] +% \caption{Пример содержимого файла с целевым физическим планом.} +% \label{lst:target-plan} +% \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +% (PhysicalProjection (exprs (attr users id)) +% (PhysicalFilter (> (attr users age) 18) +% (SeqScan users))) +% \end{minted} +% \end{listing} + +% Проверка выполняется командой на листинге~\ref{lst:check-reachable}. + +% \begin{listing}[H] +% \caption{Запуск программы в режиме проверки достижимости плана.} +% \label{lst:check-reachable} +% \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +% echo 'SELECT users.id FROM users WHERE users.age > 18;' | \ +% ./build/bin/sql \ +% --data-dir test/static/executor/test_data \ +% --check-reachable target.plan +% \end{minted} +% \end{listing} + +Если план недостижим, программа выводит сообщение \verb|NOT REACHABLE| и +описание первого обнаруженного расхождения. Параметр \verb|--data-dir| +необходим для загрузки схем таблиц. В частности, схема используется при +проверке достижимости планов с сортировкой. + +% Модульные тесты проверяют синтаксический анализ запросов, вычисление выражений, +% исполнение физических операторов, индексное сканирование, сериализацию планов, +% структуру Memo, правила оптимизации и физические свойства. + +% Для запуска Python-тестов преобразователя планов Microsoft SQL Server +% необходимо предварительно запустить контейнер, после чего выполнить команды на листинге~\ref{lst:pytest-converter}. + +% \begin{listing}[H] +% \caption{Запуск тестов преобразователя планов Microsoft SQL Server.} +% \label{lst:pytest-converter} +% \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +% cd research +% pytest -q test_converter.py +% cd .. +% \end{minted} +% \end{listing} + +% Тесты инструментов Star Schema Benchmark запускаются командой на листинге~\ref{lst:pytest-ssb}. + +% \begin{listing}[H] +% \caption{Запуск тестов инструментов Star Schema Benchmark.} +% \label{lst:pytest-ssb} +% \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +% pytest -q benchmarks/datasets/ssb +% \end{minted} +% \end{listing} + +Для поиска ошибок исполнения реализован дифференциальный фаззер. Он генерирует +случайные SQL-запросы, выполняет их в модельной СУБД и Microsoft SQL Server, +после чего сравнивает полученные результаты. Перед запуском необходимо поднять +контейнер с Microsoft SQL Server. Фаззер запускается из корневого каталога +проекта командой на листинге~\ref{lst:reach-fuzz}. + +Второй фаззер проверяет полноту набора правил оптимизатора. Он получает +физический план запроса из Microsoft SQL Server, преобразует его во внутренний +формат проекта и запускает программу в режиме \verb|--check-reachable|, как +показано на листинге~\ref{lst:reach-fuzz}. + +% \begin{figure}[H] +% \centering +% \includegraphics[width=0.8\textwidth]{reach-fuzz.png} +% \caption{Пример вывода фаззера проверки полноты правил оптимизатора}% +% \label{fig:reach-fuzz} +% \end{figure} + +% \begin{longtable}{|p{4.2cm}|p{9.3cm}|} +% \caption{Параметры командной строки программы.}\label{tbl:practice-cli}\\ +% \hline +% Параметр & Назначение \\ +% \hline +% \endfirsthead +% \multicolumn{2}{l}{\small Продолжение таблицы~\ref{tbl:practice-cli}}\\ +% \hline +% Параметр & Назначение \\ +% \hline +% \endhead +% \texttt{--data-dir DIR} & +% Директория с CSV-файлами таблиц. Обязательна при обычном исполнении запроса. \\ +% \hline +% \texttt{--print-ast} & +% Вывод логического дерева операторов после синтаксического анализа. \\ +% \hline +% \texttt{--print-plan} & +% Вывод выбранного физического плана в формате S-expression. \\ +% \hline +% \texttt{--jit} & +% Использование кэшируемой JIT-компиляции скалярных выражений. \\ +% \hline +% \texttt{--check-reachable FILE} & +% Проверка достижимости физического плана, сериализованного в файле. \\ +% \hline +% \end{longtable} + +\section{Аналитическая часть} + +Тестирование разработанной модельной СУБД выполнялось на нескольких уровнях. Для +проверки отдельных компонентов использовались модульные тесты. Корректность +выполнения запросов и полнота пространства поиска оптимизатора дополнительно +проверялись с помощью фаззинга. Для оценки производительности и калибровки +стоимостной модели были реализованы бенчмарки. Вспомогательные инструменты на +языке Python проверяются с помощью библиотеки \verb|pytest|. + +Модульные тесты реализованы с использованием библиотеки GoogleTest. Они +проверяют синтаксический анализ SQL-запросов, исполнение физических операторов, +индексное сканирование, вычисление выражений, сериализацию физических планов, +структуру Memo, правила оптимизации и обработку свойств сортировки. Пример теста +синтаксического анализатора приведен в листинге~\ref{lst:googletest}. Также +проверяется выбор физического плана. Например, при различных оценках +кардинальности оптимизатор должен выбирать подходящий порядок соединения таблиц. + +\begin{listing}[H] + \caption{Пример модульного теста.} + \label{lst:googletest} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{cpp} +TEST(ParserTest, SelectSingleColumnFromSingleTable) { + std::stringstream s{"SELECT users.id FROM users;"}; + + Operator got = GetAST(s).value().op; + + ASSERT_THAT(got, VariantWith( + Projection{ + std::vector{{Attribute{"users", "id"}}}, + std::make_shared(Table{"users"}) + })); +} + \end{minted} +\end{listing} + +Для проверки вспомогательных инструментов используются тесты на языке Python. +Они разделены на две группы. Первая группа проверяет преобразование физических +планов Microsoft SQL Server из формата ShowPlanXML во внутренний текстовый +формат проекта. Рассматриваются последовательное и индексное сканирование, +фильтрация, сортировка, агрегация и соединения. Часть тестов использует заранее +подготовленные XML-фрагменты, часть может быть запущена с подключением к +экземпляру Microsoft SQL Server. + +Вторая группа относится к стандартному Star Schema Benchmark. Она проверяет +преобразование исходных таблиц SSB в CSV-формат модельной СУБД, обработку +некорректных входных данных и успешное выполнение стандартных аналитических +запросов. + +\subsection{Дифференциальный анализ и тестирование производительности} + +Для проверки системы на большом количестве входных данных реализован генератор +случайных SQL-запросов. Он строит запросы из поддерживаемого подмножества языка: +проекции, фильтры, соединения, группировки, агрегатные функции, сортировки, +строковые выражения, \verb|IN| и \verb|BETWEEN|. + +% TODO: заменить на макросы \sqkkw + +Дифференциальный фаззер выполняет каждый сгенерированный запрос в модельной СУБД +и в Microsoft SQL Server. Результаты сравниваются как мультимножества строк. Для +запросов с \verb|ORDER BY| дополнительно проверяется корректность порядка +результатов. + +Отдельный фаззер используется для исследования полноты набора правил +оптимизации. Для случайного запроса из Microsoft SQL Server извлекается +физический план, после чего план преобразуется во внутренний формат проекта. +Затем модельный оптимизатор выполняет полный перебор пространства поиска и +проверяет, достижим ли полученный план с помощью реализованных правил. Такой +подход позволяет находить отсутствующие преобразования и физические операторы. + +Результаты запуска фаззера достижимости на 1000 случайных запросов приведены в +таблице~\ref{tbl:reachability-fuzz-results}. Около $50\%$ планов запросов были +успешно приведены к единому формату, из них также около $50\%$ запросов +оказались без расхождений. Среднее расстояние до ближайшего плана составило +2.37, что указывает на локальный характер большинства расхождений. Учитывая +ограниченный набор реализованных правил, полученный результат представляется +отличным. При этом $25\%$ планов с расхождениями из общего числа запросов +подтверждают, что промышленные системы имеют гораздо более богатый набор +трансформаций. Ошибки конвертации объясняются отсутствием в разрабатываемой СУБД +некоторых физических операторов, которые реализованы в промышленной системе. + +\begin{table}[H] + \centering + \caption{Результаты фаззинга достижимости планов.} + \label{tbl:reachability-fuzz-results} + \begin{tabular}{|l|r|} + \hline + Показатель & Значение \\ + \hline + Количество сгенерированных запросов & 1000 \\ + Достижимый план промышленной СУБД & 251 \\ + Недостижимый план промышленной СУБД & 244 \\ + Пропущено & 505 \\ + Ошибки преобразования плана & 496 \\ + Ошибки преобразования из-за исключений & 9 \\ + Ошибки получения плана Microsoft SQL Server & 0 \\ + Среднее расстояние до ближайшего плана & 2.37 \\ + \hline + \end{tabular} +\end{table} + +Один из найденных фаззером примеров показывает отсутствие преобразований для +декартова произведения. Запрос из листинга~\ref{lst:reach-fuzz-cross-query} +содержит фильтр только по таблице \verb|markets|, но исходное логическое дерево +содержит этот фильтр над цепочкой операторов \verb|CrossJoin|. + +\begin{listing}[H] + \caption{Запрос, выявивший недостижимый план с \texttt{CrossJoin}.} + \label{lst:reach-fuzz-cross-query} + \begin{minted}[style=bw, breaklines, frame=single, fontsize = \footnotesize, linenos=false, xleftmargin = 1.5em]{SQL} +SELECT markets.id AS c2619 +FROM markets CROSS JOIN users CROSS JOIN books +WHERE markets.region != 'AMERICA'; + \end{minted} +\end{listing} + +Microsoft SQL Server для этого запроса построил план, в котором фильтр был +протолкнут непосредственно к чтению таблицы \verb|markets|, а порядок декартовых +соединений был изменен. После преобразования во внутренний формат проекта +получается план на рисунке~\ref{fig:reach-fuzz-cross-mssql}. + +\begin{figure}[H] +\centering +\includegraphics[width=0.72\textwidth]{reach-fuzz-cross-mssql.pdf} +\caption{Недостижимый физический план Microsoft SQL Server после конвертации для + запроса из листинга~\ref{lst:reach-fuzz-cross-query}} +\label{fig:reach-fuzz-cross-mssql} +\end{figure} + +Модельная СУБД для того же запроса строит план, представленный на +рисунке~\ref{fig:reach-fuzz-cross-ours}. В нем фильтр по таблице \verb|markets| +используется как предикат внутреннего соединения, а не как отдельный оператор +\verb|PhysicalFilter| над чтением этой таблицы. + +Оба плана вычисляют один и тот же результат, однако план Microsoft SQL Server +недостижим в текущем пространстве поиска. В реализованном оптимизаторе правило +проталкивания фильтра через соединение работает для логического оператора +\verb|Join|, но не для отдельного оператора \verb|CrossJoin|. Кроме того, +правила коммутативности и ассоциативности реализованы для обычных соединений, +поэтому оптимизатор не строит все эквивалентные порядки декартовых соединений. +Данный пример показывает, что для покрытия планов промышленной СУБД требуется +добавить преобразования вида +\(\Sel{p_A}{\CProd{A}{B}} \rightarrow \CProd{\Sel{p_A}{A}}{B}\), а также +правила перестановки и перегруппировки цепочек \verb|CrossJoin|. + + +\begin{figure}[H] +\centering +\includegraphics[width=0.5\textwidth]{reach-fuzz-cross-ours.pdf} +\caption{Физический план, построенный модельной СУБД для запроса из + листинга~\ref{lst:reach-fuzz-cross-query}.} +\label{fig:reach-fuzz-cross-ours} +\end{figure} + +Тесты производительности реализованы с помощью библиотеки Google Benchmark. Для +запросов предусмотрены два режима построения физического плана: \verb|Naive| и +\verb|Optimized|. В первом случае физический план строится непосредственно по +логическому дереву без применения оптимизатора. Во втором случае используется +стоимостная оптимизация. Сравнение этих режимов позволяет оценить влияние +выбранного физического плана на время выполнения запроса. + +Бенчмарки выполняются как для небольших синтетических тестовых запросов, так и +для набора аналитических запросов Star Schema Benchmark. Дополнительно +поддерживается сравнение интерпретируемого и JIT-компилируемого способов +вычисления выражений. + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{naive-vs-optimizer.png} +\caption{Сравнение времени выполнения запросов при наивном и оптимизированном + построении физического плана.} +\label{fig:benchmark-naive-optimized} +\end{figure} + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{optimizer-speedup.png} +\caption{Относительное ускорение выполнения запросов после применения оптимизатора.} +\label{fig:benchmark-speedup} +\end{figure} + +Star Schema Benchmark представляет собой стандартный набор аналитических +запросов и таблиц для оценки производительности СУБД. В схеме этого набора +данных одна крупная таблица фактов связывается с несколькими таблицами +измерений, а запросы содержат типовые для аналитического типа нагрузки операции +фильтрации, соединения и агрегации. Поэтому данный бенчмарк позволяет проверить, +насколько эффективно оптимизатор выбирает порядок соединений и физические +операторы, при этом не требуя поддержки полной грамматики SQL. + +В таблице~\ref{tbl:ssb-benchmark} и на +рисунке~\ref{fig:benchmark-naive-optimized} приведено среднее время выполнения +трех повторений запросов SSB. + +На рисунке~\ref{fig:benchmark-speedup} приведено относительное ускорение +запросов на том же наборе данных. На выбранном наборе тестов достигается +ускорение в $2.5 \text{---} 30$ раз, что показывает адекватность оптимизаций для +данного тестового набора. + +\begin{table}[H] + \centering + \caption{Результаты выполнения запросов Star Schema Benchmark.} + \label{tbl:ssb-benchmark} + \begin{tabular}{|l|r|r|r|} + \hline + Запрос & \texttt{Naive}, мс & \texttt{Optimized}, мс & Ускорение \\ + \hline + \texttt{q1.1} & 172.82 & 61.51 & 2.81 \\ + \texttt{q1.2} & 167.64 & 63.25 & 2.65 \\ + \texttt{q1.3} & 166.89 & 64.17 & 2.60 \\ + \texttt{q2.1} & 301.34 & 65.05 & 4.63 \\ + \texttt{q2.2} & 300.50 & 58.72 & 5.12 \\ + \texttt{q2.3} & 303.55 & 58.67 & 5.17 \\ + \texttt{q3.1} & 1629.95 & 63.39 & 25.71 \\ + \texttt{q3.2} & 1590.02 & 56.05 & 28.37 \\ + \texttt{q3.3} & 1583.15 & 54.40 & 29.10 \\ + \texttt{q3.4} & 1587.09 & 54.16 & 29.30 \\ + \texttt{q4.1} & 351.49 & 161.92 & 2.17 \\ + \texttt{q4.2} & 354.18 & 163.33 & 2.17 \\ + \texttt{q4.3} & 350.95 & 59.86 & 5.86 \\ + \hline + \end{tabular} +\end{table} + +Дополнительно была собрана статистика по правилам, которые участвовали в +создании оптимальных планов на 1000 случайных запросах +(таблица~\ref{tbl:rule-lineage-random}). Это реализовано через восстановление +цепочки правил, через которую были получены выражения, вошедшие в итоговый план. +Это позволяет оценить, какие правила чаще входят в оптимальные планы, что может +быть полезно для определения подмножеств правил для многоэтапной оптимизации. + +\begin{table}[H] + \centering + \scriptsize + \setlength{\tabcolsep}{3pt} + \caption{Правила в происхождении выбранных планов на 1000 случайных запросах.} + \label{tbl:rule-lineage-random} + \begin{tabular}{|p{2.6cm}|p{5.0cm}|r|r|r|r|} + \hline + Тип & Правило & Всего & Запросов & Доля, \% & Максимум \\ + \hline + Трансформация & \texttt{JoinCommutativity} & 594 & 517 & 51.70 & 4 \\ + Трансформация & \texttt{JoinAssociativity} & 75 & 29 & 2.90 & 4 \\ + Трансформация & \texttt{FilterSplit} & 70 & 42 & 4.20 & 5 \\ + Трансформация & \texttt{FilterMerge} & 7 & 7 & 0.70 & 1 \\ + Трансформация & \makecell[l]{\texttt{FilterPushdown}\\\texttt{ThroughJoin}} & 469 & 191 & 19.10 & 5 \\ + Трансформация & \texttt{InToOrChain} & 158 & 153 & 15.30 & 3 \\ + Трансформация & \texttt{FilterToJoinPredicate} & 27 & 23 & 2.30 & 2 \\ + Трансформация & \texttt{CrossJoinToJoin} & 82 & 82 & 8.20 & 1 \\ + Трансформация & \texttt{FilterLiftThroughJoin} & 40 & 19 & 1.90 & 4 \\ + Трансформация & \makecell[l]{\texttt{ProjectionPushdown}\\\texttt{ThroughJoin}} & 106 & 30 & 3.00 & 7 \\ + Трансформация & \texttt{OuterJoinToInner} & 101 & 91 & 9.10 & 2 \\ + Реализация & \texttt{ImplementTable} & 1977 & 1000 & 100.00 & 3 \\ + Реализация & \texttt{ImplementFilter} & 531 & 487 & 48.70 & 2 \\ + Реализация & \texttt{ImplementProjection} & 1038 & 1000 & 100.00 & 4 \\ + Реализация & \texttt{ImplementJoin} & 156 & 153 & 15.30 & 2 \\ + Реализация & \texttt{ImplementCrossJoin} & 157 & 145 & 14.50 & 2 \\ + Реализация & \texttt{ImplementHashJoin} & 171 & 162 & 16.20 & 2 \\ + Реализация & \texttt{ImplementMergeJoin} & 493 & 394 & 39.40 & 2 \\ + Реализация & \texttt{ImplementAggregation} & 319 & 319 & 31.90 & 1 \\ + Обеспечение свойства & \texttt{SortEnforcer} & 1494 & 706 & 70.60 & 6 \\ + \hline + \end{tabular} +\end{table} + +В таблице~\ref{tbl:rule-lineage-random} столбец <<Всего>> обозначает суммарное +число уникальных выражений в происхождении выбранных планов, полученных данным +правилом. Столбец <<Запросов>> показывает, в скольких запросах правило +встретилось хотя бы один раз, а <<Максимум>>~--- максимальное число таких +выражений для одного запроса. Наиболее часто среди логических преобразований +встречается коммутативность соединения, что показывает важность выбора +правильного порядка соединений. Правило проталкивания фильтра через соединение +встречается реже, но его появление в $19.10\%$ запросов показывает, что оно +реально участвует в построении выбранных планов, а не только расширяет +пространство поиска. Высокая частота \texttt{SortEnforcer} объясняется +генерацией запросов с \texttt{ORDER BY} и выбором физических операторов, +требующих отсортированный вход, например \texttt{MergeJoin} и потоковой +агрегации. Часть правил не встретилась ни в одном из планов, но это не является +признаком того, что их можно удалить, так как на других данных или при другой +конфигурации генератора запросов эти правила могут дать более оптимальные планы. + +\subsection{Калибровка стоимостной модели} + +Для калибровки коэффициентов в стоимостных формулах реализованы отдельные +бенчмарки физических операторов. +Измеряется время выполнения последовательного сканирования, фильтрации, +проекции, сортировки, агрегации, хеш-соединения, соединения вложенными циклами и +декартова произведения~\refImage{fig:operator-benchmark}. + +\begin{figure}[H] +\centering +\includegraphics[width=0.7\textwidth]{scaling.png} +\caption{Зависимость времени выполнения физических операторов от размера входных + данных.} +\label{fig:operator-benchmark} +\end{figure} + +При выполнении этих бенчмарков данные хранятся в оперативной памяти. Это +позволяет уменьшить влияние файловой системы и измерять преимущественно +стоимость самого физического оператора. Для каждого оператора выполняются замеры +на таблицах различного размера. + +Дополнительно формируются группы запусков с приблизительно одинаковой расчетной +стоимостью. Для этого размер входных отношений выбирается на основе стоимостной +формулы оператора. Сопоставление расчетной стоимости с фактическим временем +выполнения позволяет сравнить операторы между собой и скорректировать +коэффициенты модели~\refImage{fig:cost-model-calibration}. Благодаря этому +оптимизатор принимает решения на основе оценок, соответствующих характеристикам +реализованного исполнителя. + +Выведенные коэффициенты дополнительно валидируются на случайных запросах путем +сравнения полной стоимости плана и времени его +исполнения~\refImage{fig:cost-on-random-queries}. + +\anonsection{ЗАКЛЮЧЕНИЕ} + +В ходе работы была изучена архитектура оптимизаторов Cascades и на ее основе +спроектирован собственный оптимизатор для модельной реляционной СУБД. + +Было формализовано поддерживаемое подмножество SQL и соответствующее ему +представление в терминах реляционной алгебры. Реализованная система поддерживает +запросы на чтение данных с основными операторами диалекта PostgreSQL. + +Был реализован алгоритм поиска оптимального физического плана и структура Memo +для эффективного хранения групп логически эквивалентных выражений. В результате +система выбирает физический план с минимальной оценочной стоимостью с учетом +требуемых свойств результата. + +Для формирования пространства поиска был разработан набор правил трансформации и +реализации для основных операторов реляционной алгебры. Реализованные правила +позволяют изменять порядок и группировку соединений, преобразовывать предикаты и +выражения, а также сопоставлять логическим операторам физические реализации. + +Для ускорения поиска был реализован метод ветвей и границ. Оптимизатор +использует текущую лучшую стоимость и нижние оценки стоимости поддеревьев для +отсечения заведомо неоптимальных вариантов, что уменьшает объем исследуемого +пространства планов без ухудшения качества результирующих планов. + +Для последующего развития оптимизатора был реализован метод дифференциального +анализа физических планов. Подготовлены инструменты генерации запросов, +сравнения результатов исполнения и сопоставления планов с промышленной СУБД +Microsoft SQL Server. + +Тем самым цель работы была достигнута. Получена работоспособная и расширяемая +система, которую можно использовать как основу для дальнейшего уточнения правил +оптимизации и увеличения пространства исследуемых планов исполнения запросов. + +\renewcommand\refname{СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ} +\clearpage +{\catcode`"\active\def"{\relax} + \addcontentsline{toc}{section}{\refname}% + \printbibliography +} +\newpage + +\settocdepth{section} +\anonsection{ПРИЛОЖЕНИЕ А} +\vspace{-30pt} + +\begin{figure}[H] +\centering +\includegraphics[width=0.7\textwidth]{cascades.pdf} +\caption{Схема взаимодействия задач в оптимизаторе Cascades}% +\label{fig:cascades-tasks} +\end{figure} + +\begin{figure}[H] + \centering + \begin{tikzpicture}[ + scale=0.85, transform shape, + group/.style={draw, rounded corners, text width=4.8cm, inner sep=5pt}, + arrow/.style={-{Stealth[length=2.5mm]}, thick}, + altarrow/.style={-{Stealth[length=2.5mm]}, thick, dashed} + ] + \node[group] (g5) at (6,0) { + \textbf{Группа G5}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Join(G4,G3)\\ + Join(G1,G6)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + HashJoin(G4,G3)\\ + NestedLoopJoin(G4,G3)\\ + HashJoin(G1,G6)\\ + NestedLoopJoin(G1,G6) + }; + \node[group] (g4) at (3,-5.4) { + \textbf{Группа G4}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Join(G1,G2)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + HashJoin(G1,G2)\\ + NestedLoopJoin(G1,G2) + }; + \node[group] (g6) at (9,-5.4) { + \textbf{Группа G6}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Join(G2,G3)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + HashJoin(G2,G3)\\ + NestedLoopJoin(G2,G3) + }; + \node[group] (g1) at (0,-9.8) { + \textbf{Группа G1}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Scan(A)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + SeqScan(A) + }; + \node[group] (g2) at (6,-9.8) { + \textbf{Группа G2}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Scan(B)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + SeqScan(B) + }; + \node[group] (g3) at (12,-9.8) { + \textbf{Группа G3}\\[2pt] + \hrule\vspace{2pt} + \textit{Логические:}\\ + Scan(C)\\[2pt] + \hrule\vspace{2pt} + \textit{Физические:}\\ + SeqScan(C) + }; + + \draw[arrow] (g5) -- (g4); + \draw[arrow, rounded corners=5pt] + (g5.east) -- (15.5,0) -- (15.5,-9.8) -- (g3.east); + \draw[altarrow] (g5) -- (g6); + \draw[altarrow, rounded corners=5pt] + (g5.west) -- (-3.5,0) -- (-3.5,-9.8) -- (g1.west); + \draw[arrow] (g4) -- (g1); + \draw[arrow] (g4) -- (g2); + \draw[arrow] (g6) -- (g2); + \draw[arrow] (g6) -- (g3); + \end{tikzpicture} + \caption{Пример структуры Memo.}% + \label{fig:memo-structure} +\end{figure} + +\begin{table}[H] + \centering + \caption{Формулы оценки селективности предикатов.} + \label{tbl:selectivity-model} + \small + \begin{tabular}{|p{0.42\textwidth}|p{0.42\textwidth}|} + \hline + Вид предиката & Формула селективности \\ + \hline + \(a = c\), где \(a\) --- атрибут, \(c\) --- константа + & \(sel(a = c) = 0{,}10\) \\ + \hline + \(a < b\), \(a > b\), \(a \le b\), \(a \ge b\) + & \(sel(p) = 0{,}50\) \\ + \hline + \(p_1 \land p_2\) + & \(sel(p_1 \land p_2) = sel(p_1) \cdot sel(p_2)\) \\ + \hline + \(p_1 \lor p_2\) + & \(sel(p_1 \lor p_2) = + sel(p_1) + sel(p_2) - sel(p_1) \cdot sel(p_2)\) \\ + \hline + \(\lnot p\) + & \(sel(\lnot p) = 1 - sel(p)\) \\ + \hline + \(a \in \{c_1, \ldots, c_k\}\) + & \(sel(p) = \min(0{,}50,\; 0{,}10k)\) \\ + \hline + \(a \notin \{c_1, \ldots, c_k\}\) + & \(sel(p) = 1 - \min(0{,}50,\; 0{,}10k)\) \\ + \hline + \(a = b\) в условии соединения, где \(a\) и \(b\) --- атрибуты разных входов + & \(sel(a = b) = \frac{1}{\max(1,\; \max(n_l, n_r))}\) \\ + \hline + \(\theta_1 \land \theta_2\) в условии соединения + & \(sel(\theta_1 \land \theta_2) = + sel(\theta_1) \cdot sel(\theta_2)\) \\ + \hline + Иной или неизвестный предикат фильтрации + & \(sel(p) = 0{,}50\) \\ + \hline + Иной или неизвестный предикат соединения + & \(sel(\theta) = 1\) \\ + \hline + \end{tabular} +\end{table} + + +\begin{algorithm}[H] + \caption{Алгоритм поиска (часть 1).}% + \label{alg:cascades-search} + \footnotesize + \begin{algorithmic}[1] + \Procedure{Optimize}{$root, required$} + \State Push task $\Call{OptimizeGroup}{root.group, required, +\infty}$ + \While{task stack is not empty} + \State Pop and execute top task + \EndWhile + \State \Return $\Call{BuildPlan}{root.group, required}$ + \EndProcedure + + \Procedure{ExploreGroup}{$group, limit$} + \State Mark $group$ as explored + \For{each logical expression $expr \in group$} + \State Push task $\Call{ExploreExpression}{expr, limit}$ + \EndFor + \EndProcedure + + \Procedure{ExploreExpression}{$expr, limit$} + \State $\Call{TryRules}{expr, limit}$ + \If{$expr$ is already explored} + \State \Return + \EndIf + \State Mark $expr$ as explored + \For{each $child \in \Call{Children}{expr}$} + \State Register $expr$ as parent of $child$ + \If{$child$ is not explored} + \State Push task $\Call{ExploreGroup}{child, limit}$ + \EndIf + \EndFor + \EndProcedure + + \Procedure{TryRules}{$expr, limit$} + \For{each applicable transformation rule $r$} + \State Push task $\Call{ApplyTransformation}{r, expr, limit}$ + \EndFor + \For{each applicable implementation rule $r$} + \State Push task $\Call{ApplyImplementation}{r, expr}$ + \EndFor + \EndProcedure + \end{algorithmic} +\end{algorithm} + +\begin{algorithm}[H] + \caption{Алгоритм поиска (часть 2).}% + \label{alg:cascades-search-2} + \footnotesize + \begin{algorithmic}[1] + \Procedure{OptimizeGroup}{$group, required, limit$} + \If{$winner[group, required]$ exists \textbf{or} $group$ is explored \textbf{and} $\Call{LowerBound}{group} \geq limit$} + \State \Return + \EndIf + \If{$group$ is not explored} + \State Push task $\Call{OptimizeGroup}{group, required, limit}$ + \State Push task $\Call{ExploreGroup}{group, limit}$ + \State \Return + \EndIf + \For{each physical expression $phys \in group$} + \If{$phys$ is not enforcer \textbf{and} $local\_cost[phys] < limit$} + \State Push task $\Call{OptimizeInputs}{phys, required, \varnothing, local\_cost[phys], limit, 0}$ + \EndIf + \EndFor + \For{each enforcer applicable to $(group, required)$} + \State Add enforcer expression $phys$ and compute $local\_cost[phys]$ + \If{$local\_cost[phys] < limit$} + \State Push task $\Call{OptimizeInputs}{phys, required, \varnothing, local\_cost[phys], limit, 0}$ + \EndIf + \EndFor + \Procedure{OptimizeInputs}{$phys, required, delivered, accum, limit, i$} + \If{$winner[phys.group, required]$ exists} + \State $limit \leftarrow \min(limit,\; winner[phys.group, required].cost)$ + \EndIf + \If{$accum \geq limit$} + \State \Return + \EndIf + \If{$i = |children|$} + \State $props \leftarrow \Call{DeriveOutputProps}{phys, delivered}$ + \State Update winner and \Return + \EndIf + \State Calc child limit and requirements + \State Push task $\Call{OptimizeGroup}{child, child\_required, child\_limit}$ + \EndProcedure + \EndProcedure + + \end{algorithmic} +\end{algorithm} + +\begin{Definition} +Null-отбрасывающим называется предикат, который при замене всех атрибутов +соответствующей стороны на \texttt{NULL} принимает значение, отличное от +\texttt{TRUE}. +\end{Definition} + +\begin{Definition} +Конъюнкты \(\theta_1 \land \theta_2\) называются перераспределяемыми, если их +можно разбить на две группы \(\theta'_1\) и \(\theta'_2\) такие, что атрибуты +\(\theta'_2\) принадлежат \(attrs(S) \cup attrs(T)\), а конъюнкты с атрибутами +из \(attrs(R)\) остаются в \(\theta'_1\). +\end{Definition} + +\begin{Definition} +Функция агрегации называется разделяемой, если она представима композицией +частичного агрегата и финального комбинатора. Например, \SUM{} раскладывается в +сумму частичных сумм, \AVG{} --- в пару \SUM{} и \COUNT{}. +\end{Definition} + + +\begin{footnotesize} +\begin{longtable}{|c|L{3.1cm}|c|L{3.7cm}|} + \caption{Правила трансформации.}\label{tbl:transformation_rules}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endfirsthead + \multicolumn{4}{l}{\small Продолжение таблицы~\ref{tbl:transformation_rules}}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endhead + \hline + \endfoot + \hline + \endlastfoot + + 1 & Коммутативность соединения & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\text{внутреннее или полное внешнее соединение}}{\InnerJoin{S}{\theta}{R}}\) & + Перестановка входов соединения. Позволяет рассмотреть оба порядка вычисления. \\ + \hline + + 2 & Ассоциативность соединения & + \(\TransRule{\InnerJoin{(\InnerJoin{R}{\theta_1}{S})}{\theta_2}{T}}{\substack{\text{оба соединения внутренние;}\\ \theta_1 \land \theta_2 \text{ перераспределяемы}}}{\InnerJoin{R}{\theta'_1}{(\InnerJoin{S}{\theta'_2}{T})}}\) & + Изменяет группировку цепочки соединений. Открывает альтернативные порядки и уменьшает размер промежуточных результатов. \\ + \hline + + 3 & Проталкивание фильтра в левый вход & + \(\TransRule{\Sel{p}{\InnerJoin{R}{\theta}{S}}}{\substack{attrs(p) \subseteq attrs(R); \\ \text{внутреннее соединение или сохраняющий вход}}}{\InnerJoin{\Sel{p}{R}}{\theta}{S}}\) & + Применяет селективный предикат до соединения, уменьшая кардинальность входа. \\ + \hline + + 4 & Проталкивание фильтра в правый вход & + \(\TransRule{\Sel{p}{\InnerJoin{R}{\theta}{S}}}{\substack{attrs(p) \subseteq attrs(S); \\ \text{внутреннее соединение или сохраняющий вход}}}{\InnerJoin{R}{\theta}{\Sel{p}{S}}}\) & + Симметричный случай для правого входа. \\ + \hline + + 5 & Перенос фильтра в предикат соединения & + \(\TransRule{\Sel{p}{\InnerJoin{R}{\theta}{S}}}{\substack{\text{соединение внутреннее;} \\ attrs(p) \subseteq attrs(R) \cup attrs(S)}}{\InnerJoin{R}{\theta \land p}{S}}\) & + Объединяет фильтр и предикат соединения. Может позволить применить хеш-соединение или соединение слиянием. \\ + \hline + + 6 & Превращение декартова произведения в соединение & + \(\TransRule{\Sel{p}{\CProd{R}{S}}}{\top}{\InnerJoin{R}{p}{S}}\) & + Частный случай правила~5. \\ + \hline + + 7 & Поднятие фильтра над соединением & + \(\TransRule{\InnerJoin{\Sel{p}{R}}{\theta}{S}}{\substack{attrs(p) \subseteq attrs(R); \\ \text{внутреннее соединение или сохраняющая сторона}}}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + Переносит фильтр выше соединения для объединения с предикатами родительских операторов. \\ + \hline + + 8 & Разбиение фильтра на конъюнкты & + \(\TransRule{\Sel{p_1 \land \ldots \land p_n}{R}}{\top}{\Sel{p_1}{\ldots \Sel{p_n}{R} \ldots}}\) & + Позволяет обрабатывать каждый конъюнкт независимо. \\ + \hline + + 9 & Проталкивание проекции через соединение & + \(\TransRule{\Projection{L}{\InnerJoin{R}{\theta}{S}}}{\substack{L_R \cup J_R \subsetneq attrs(R) \\ \text{или}\ L_S \cup J_S \subsetneq attrs(S)}}{\Projection{L}{\InnerJoin{\Projection{L_R \cup J_R}{R}}{\theta}{\Projection{L_S \cup J_S}{S}}}}\) & + Удаляет неиспользуемые атрибуты до соединения, сокращая ширину кортежей. \\ + \hline + + 10 & Левое внешнее соединение во внутреннее & + \(\TransRule{\Sel{p}{\LOJ{R}{\theta}{S}}}{p\ \text{null-отбрасывающий по}\ attrs(S)}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + Открывает для подплана правила, ограниченные внутренним соединением. \\ + \hline + + % 11 & Правое внешнее соединение во внутреннее & + % \(\TransRule{\Sel{p}{\ROJ{R}{\theta}{S}}}{p\ \text{null-отбрасывающий по}\ attrs(R)}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + % Симметричный случай. \\ + % \hline + + 11 & Полное внешнее соединение во внутреннее & + \(\TransRule{\Sel{p}{\FOJ{R}{\theta}{S}}}{p\ \text{null-отбрасывающий по}\ attrs(R)\ \text{и}\ attrs(S)}{\Sel{p}{\InnerJoin{R}{\theta}{S}}}\) & + Требует null-отбрасывания с обеих сторон. \\ + \hline + + 12 & Проталкивание агрегации ниже соединения & + \(\TransRule{\Agg{G}{F}{\InnerJoin{R}{R.k = S.k}{S}}}{\substack{F\text{ разделяема;}\\ \text{внутреннее соединение по равенству}}}{\Agg{G}{F'}{\InnerJoin{\AggP{G_R \cup \{R.k\}}{F_R}{R}}{R.k=S.k}{S}}}\) & + Снижает кардинальность входа частичной группировкой. \(G_R = G \cap attrs(R)\); \(F_R\) --- частичные агрегаты по атрибутам \(R\); \(F'\) --- финальные комбинаторы. \\ + \hline + + 13 & Перестановка агрегации и соединения & + \(\TransRule{\Agg{G}{F_R}{\InnerJoin{R}{R.k=S.k}{S}}}{\substack{\text{внутреннее соединение по равенству;}\\ F_R\subseteq attrs(R);\ R.k \in G; \\ S.k\text{ уникален и полон}}}{\InnerJoin{\Agg{G}{F_R}{R}}{R.k=S.k}{S}}\) & + Полностью переносит агрегацию до соединения. \(S.k\) уникален: соединение не дублирует строки \(R\). \(S.k\) полон относительно \(R.k\): соединение не теряет строки \(R\). \\ + \hline +\end{longtable} + +\begin{table}[H] + \centering + \caption{Стоимостные формулы физических операторов.} + \label{tbl:cost-model-coefficients} + \small + \begin{tabular}{|p{0.40\textwidth}|l|} + \hline + Физический оператор & Формула стоимости \\ + \hline + Последовательное сканирование & $100\,n$ \\ + Фильтрация & $100\,n_{out}$ \\ + Проекция & $22\,n_{out}$ \\ + Сортировка & $11\,n\bigl(\lfloor\log_2 n\rfloor + 1\bigr)$ \\ + Агрегация & $510\,n$ \\ + Потоковая агрегация & $130\,n$ \\ + Соединение вложенными циклами & $70\,n_l n_r$ \\ + Соединение слиянием & $18\,n_l + n_l\,+ 10\,n_o w_o$ \\ + Декартово произведение & $104\,n_l n_r$ \\ + Хеш-соединение & $100\,n_b w_b + 35\,n_p + 10\,n_o w_o$ \\ + \hline + \end{tabular} +\end{table} + +\begin{longtable}{|c|L{3.4cm}|c|L{3.9cm}|} + \caption{Правила реализации.}\label{tbl:implementation_rules}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endfirsthead + \multicolumn{4}{l}{\small Продолжение таблицы~\ref{tbl:implementation_rules}}\\ + \hline + № & Название & Правило & Комментарий \\ + \hline + \endhead + \hline + \endfoot + \hline + \endlastfoot + + 1 & Последовательное сканирование & + \(\TransRule{\LogicalGet}{\top}{\SeqScan}\) & + Применимо к любому отношению. \\ + \hline + + 2 & Сканирование по индексу & + \(\TransRule{\LogicalGet}{\text{есть совместимый индекс}}{\IndexScan}\) & + Индекс должен быть совместим с предикатом или требуемым порядком. \\ + \hline + + 3 & Соединение вложенными циклами & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\top}{\NestedLoopJoin}\) & + Применимо при любом виде предиката. Стоимость высока без доступа по индексу к внутреннему отношению. \\ + \hline + + 4 & Хеш-соединение & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\theta\ \text{содержит равенство по ключам}}{\HashJoin}\) & + Хеш-таблица строится по ключам равенства. \\ + \hline + + 5 & Соединение слиянием & + \(\TransRule{\InnerJoin{R}{\theta}{S}}{\text{входы упорядочены по ключам соединения}}{\MergeJoin}\) & + Требуемый порядок может быть обеспечен явным оператором \Sort{}. \\ + \hline + + 6 & Хеш-агрегация & + \(\TransRule{\Agg{G}{F}{R}}{\top}{\sqlkw{HashAggregate}}\) & + Не требует упорядоченности входа. Потребляет память пропорционально числу групп. \\ + \hline + + 7 & Потоковая агрегация & + \(\TransRule{\Agg{G}{F}{R}}{\text{вход упорядочен по } G}{\sqlkw{StreamAggregate}}\) & + Эффективна при уже отсортированном входе. Может быть выгодна, если требуемый порядок нужен и родительскому оператору. \\ + \hline +\end{longtable} +\end{footnotesize} + +\begin{listing}[H] + \caption{Вычисление локальной стоимости физических операторов.} + \label{lst:optimizer-cost} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +int64_t CalcCost(PhysicalExpr* expr, CardinalityEstimates& cardinality) { + return std::visit(utils::Overloaded{ + [&](const physical::NestedLoopJoin& j) { + return 70 * cardinality.GetCardinality(j.lhs) + * cardinality.GetCardinality(j.rhs);}, + [&](const physical::HashJoin& j) { + return 69 * (cardinality.GetCardinality(j.lhs) + + cardinality.GetCardinality(j.rhs));}, ... + \end{minted} +\end{listing} + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{calibration.png} +\caption{Сопоставление расчетной стоимости физических операторов с фактическим + временем выполнения.} +\label{fig:cost-model-calibration} +\end{figure} + +\begin{listing}[H] + \caption{Правило коммутативности соединения.} + \label{lst:join-commutativity} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize, + linenos=false, xleftmargin=1.5em]{cpp} +LogicalOperator JoinCommutativity::ApplyImpl(utils::NotNull expr, Memo&) { + auto join = std::get(expr->root_operator); + std::swap(join.lhs, join.rhs); + if (join.type == JoinType::kLeft) { + join.type = JoinType::kRight; + } else if (join.type == JoinType::kRight) { + join.type = JoinType::kLeft; + } + return join; +} +bool ImplementHashJoin::IsApplicable(utils::NotNull expr) { + if (!std::holds_alternative(expr->root_operator)) + return false; + const auto& join = std::get(expr->root_operator); + if (join.type != JoinType::kInner) return false; + const auto* bin = std::get_if(&join.qual); + return bin && bin->binop == BinaryOp::kEq && std::holds_alternative(*bin->lhs) && std::holds_alternative(*bin->rhs); +} + \end{minted} +\end{listing} + +\begin{figure}[H] +\centering +\includegraphics[width=0.8\textwidth]{run-example.png} +\caption{Выполнение SQL-запроса над тестовыми данными}% +\label{fig:practice-run} +\end{figure} + +\begin{listing}[H] + \caption{Оптимизация группы Memo.} + \label{lst:optimize-group} + \begin{minted}[style=bw, breaklines, frame=single, + fontsize=\footnotesize, linenos=false, xleftmargin=1.5em]{cpp} +void Optimizer::OptimizeGroup(Group* group, PropertySet required, + Limit limit) { + WinnerKey key{group, required}; + if (winner_.contains(key)) return; + if (IsExplored(group) && limit && LowerBoundCost(group) >= *limit) return; + if (!IsExplored(group)) { + tasks_.emplace([=] { + OptimizeGroup(group, required, limit); + }); + tasks_.emplace([=] { + ExploreGroup(group, limit); + }); + return; + } + for (auto expr : group->GetPhysicalExprs()) { + if (expr->is_enforcer) continue; + auto cost = local_cost_[expr.get()]; + if (!limit || cost < *limit) { + tasks_.emplace([=] { + OptimizeInputs(expr, required, {}, cost, limit); + }); + } + } + AddEnforcers(group, required, limit); +} + \end{minted} +\end{listing} + +\begin{figure}[H] +\centering +\includegraphics[width=0.7\textwidth]{cost-on-random-queries.png} +\caption{Сравнение скорости выполнения и стоимости на случайных запросах.}% +\label{fig:cost-on-random-queries} +\end{figure} + +\begin{listing}[H] + \caption{Запуск фаззеров.} + \label{lst:reach-fuzz} + \begin{minted}[style=bw, breaklines, frame=single, fontsize=\footnotesize]{text} +docker compose up -d +python -m research.fuzz.reach_fuzz \ + --cli build/bin/sql \ + --data-dir test/static/executor/test_data +python -m research.fuzz.diff_fuzz \ + --cli build/bin/sql \ + --data-dir test/static/executor/test_data + \end{minted} +\end{listing} + +\end{document} diff --git a/research/benchmark-results/operator-cost-matched.json b/research/benchmark-results/operator-cost-matched.json new file mode 100644 index 0000000..748c8e3 --- /dev/null +++ b/research/benchmark-results/operator-cost-matched.json @@ -0,0 +1,6408 @@ +{ + "context": { + "date": "2026-06-14T20:48:21+03:00", + "host_name": "nixos", + "executable": "./build-release/bin/benchmarks", + "num_cpus": 8, + "mhz_per_cpu": 4200, + "cpu_scaling_enabled": true, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 49152, + "num_sharing": 2 + }, + { + "type": "Instruction", + "level": 1, + "size": 32768, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 2, + "size": 1310720, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 3, + "size": 8388608, + "num_sharing": 8 + } + ], + "load_avg": [1.43262,1.46533,1.78467], + "library_version": "v1.9.0", + "library_build_type": "release", + "json_schema_version": 1 + }, + "benchmarks": [ + { + "name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2214, + "real_time": 3.0911023938454612e+05, + "cpu_time": 3.0619232971996389e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 6.4000000000000000e+03, + "plan_cost": 6.4000000000000000e+05, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2214, + "real_time": 3.1765129223070038e+05, + "cpu_time": 3.1451108536585368e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 6.4000000000000000e+03, + "plan_cost": 6.4000000000000000e+05, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2214, + "real_time": 3.5457537533911917e+05, + "cpu_time": 3.4965761698283651e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 6.4000000000000000e+03, + "plan_cost": 6.4000000000000000e+05, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_mean", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.2711230231812195e+05, + "cpu_time": 3.2345367735621799e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 6.4000000000000000e+03, + "plan_cost": 6.4000000000000000e+05, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_median", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1765129223070032e+05, + "cpu_time": 3.1451108536585368e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 6.4000000000000000e+03, + "plan_cost": 6.4000000000000000e+05, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_stddev", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4164078313638052e+04, + "cpu_time": 2.3071308216930705e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_cv", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 7.3870894314876923e-02, + "cpu_time": 7.1328013351112357e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1760, + "real_time": 4.1470032556803327e+05, + "cpu_time": 4.1158367443181819e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 3.3280000000000000e+03, + "plan_cost": 1.2800000000000000e+06, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1760, + "real_time": 4.1930176022850157e+05, + "cpu_time": 4.1661144374999992e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 3.3280000000000000e+03, + "plan_cost": 1.2800000000000000e+06, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1760, + "real_time": 4.3658621818229387e+05, + "cpu_time": 4.3417356250000041e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 3.3280000000000000e+03, + "plan_cost": 1.2800000000000000e+06, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time_mean", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.2352943465960957e+05, + "cpu_time": 4.2078956022727280e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 3.3280000000000000e+03, + "plan_cost": 1.2800000000000000e+06, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time_median", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.1930176022850157e+05, + "cpu_time": 4.1661144374999986e+05, + "time_unit": "ns", + "model_cost": 6.4000000000000000e+05, + "output_rows": 3.3280000000000000e+03, + "plan_cost": 1.2800000000000000e+06, + "rows": 6.4000000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time_stddev", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1539193958084354e+04, + "cpu_time": 1.1860364821811641e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time_cv", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Filter/6400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.7245317594887825e-02, + "cpu_time": 2.8185976894022118e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 226, + "real_time": 3.0195558805306354e+06, + "cpu_time": 3.0009444867256628e+06, + "time_unit": "ns", + "model_cost": 6.4000200000000000e+05, + "output_rows": 2.9091000000000000e+04, + "plan_cost": 3.5491020000000000e+06, + "rows": 2.9091000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 226, + "real_time": 2.9964330132742389e+06, + "cpu_time": 2.9785224601769918e+06, + "time_unit": "ns", + "model_cost": 6.4000200000000000e+05, + "output_rows": 2.9091000000000000e+04, + "plan_cost": 3.5491020000000000e+06, + "rows": 2.9091000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 226, + "real_time": 2.8924311946842363e+06, + "cpu_time": 2.8764148097345126e+06, + "time_unit": "ns", + "model_cost": 6.4000200000000000e+05, + "output_rows": 2.9091000000000000e+04, + "plan_cost": 3.5491020000000000e+06, + "rows": 2.9091000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time_mean", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.9694733628297024e+06, + "cpu_time": 2.9519605855457224e+06, + "time_unit": "ns", + "model_cost": 6.4000200000000000e+05, + "output_rows": 2.9091000000000000e+04, + "plan_cost": 3.5491020000000000e+06, + "rows": 2.9091000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time_median", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.9964330132742389e+06, + "cpu_time": 2.9785224601769918e+06, + "time_unit": "ns", + "model_cost": 6.4000200000000000e+05, + "output_rows": 2.9091000000000000e+04, + "plan_cost": 3.5491020000000000e+06, + "rows": 2.9091000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time_stddev", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.7714758376275582e+04, + "cpu_time": 6.6378159064747015e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time_cv", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Projection/29091/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.2803625458942690e-02, + "cpu_time": 2.2486126471257015e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 907, + "real_time": 7.0784833847735438e+05, + "cpu_time": 7.0478191951488447e+05, + "time_unit": "ns", + "model_cost": 6.4006800000000000e+05, + "output_rows": 4.4760000000000000e+03, + "plan_cost": 1.0876680000000000e+06, + "rows": 4.4760000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 907, + "real_time": 7.1027558875225135e+05, + "cpu_time": 7.0695717530319712e+05, + "time_unit": "ns", + "model_cost": 6.4006800000000000e+05, + "output_rows": 4.4760000000000000e+03, + "plan_cost": 1.0876680000000000e+06, + "rows": 4.4760000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 907, + "real_time": 7.0588639911670797e+05, + "cpu_time": 7.0260624255788233e+05, + "time_unit": "ns", + "model_cost": 6.4006800000000000e+05, + "output_rows": 4.4760000000000000e+03, + "plan_cost": 1.0876680000000000e+06, + "rows": 4.4760000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time_mean", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.0800344211543782e+05, + "cpu_time": 7.0478177912532119e+05, + "time_unit": "ns", + "model_cost": 6.4006800000000000e+05, + "output_rows": 4.4760000000000000e+03, + "plan_cost": 1.0876680000000000e+06, + "rows": 4.4760000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time_median", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.0784833847735438e+05, + "cpu_time": 7.0478191951488436e+05, + "time_unit": "ns", + "model_cost": 6.4006800000000000e+05, + "output_rows": 4.4760000000000000e+03, + "plan_cost": 1.0876680000000000e+06, + "rows": 4.4760000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time_stddev", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.1987017232992253e+03, + "cpu_time": 2.1754663760714229e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time_cv", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Sort/4476/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.1054958104860925e-03, + "cpu_time": 3.0867233525408593e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1946, + "real_time": 3.4933276772805076e+05, + "cpu_time": 3.4805591058581707e+05, + "time_unit": "ns", + "model_cost": 6.4005000000000000e+05, + "output_rows": 1.2550000000000000e+03, + "plan_cost": 7.6555000000000000e+05, + "rows": 1.2550000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1946, + "real_time": 3.5104961510741868e+05, + "cpu_time": 3.4970038848920772e+05, + "time_unit": "ns", + "model_cost": 6.4005000000000000e+05, + "output_rows": 1.2550000000000000e+03, + "plan_cost": 7.6555000000000000e+05, + "rows": 1.2550000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1946, + "real_time": 3.5066883864314586e+05, + "cpu_time": 3.4928844039054483e+05, + "time_unit": "ns", + "model_cost": 6.4005000000000000e+05, + "output_rows": 1.2550000000000000e+03, + "plan_cost": 7.6555000000000000e+05, + "rows": 1.2550000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_mean", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5035040715953842e+05, + "cpu_time": 3.4901491315518977e+05, + "time_unit": "ns", + "model_cost": 6.4005000000000000e+05, + "output_rows": 1.2550000000000000e+03, + "plan_cost": 7.6555000000000000e+05, + "rows": 1.2550000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_median", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5066883864314581e+05, + "cpu_time": 3.4928844039054483e+05, + "time_unit": "ns", + "model_cost": 6.4005000000000000e+05, + "output_rows": 1.2550000000000000e+03, + "plan_cost": 7.6555000000000000e+05, + "rows": 1.2550000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_stddev", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.0163195835342015e+02, + "cpu_time": 8.5568087220638881e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_cv", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.5735148009770848e-03, + "cpu_time": 2.4517028927812877e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2052, + "real_time": 3.1039785721233365e+05, + "cpu_time": 3.0903469249512703e+05, + "time_unit": "ns", + "lhs_rows": 1.6200000000000000e+03, + "model_cost": 6.3990000000000000e+05, + "output_rows": 1.6200000000000000e+03, + "plan_cost": 9.6390000000000000e+05, + "rhs_rows": 1.6200000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2052, + "real_time": 3.1106529385987530e+05, + "cpu_time": 3.0980652241715358e+05, + "time_unit": "ns", + "lhs_rows": 1.6200000000000000e+03, + "model_cost": 6.3990000000000000e+05, + "output_rows": 1.6200000000000000e+03, + "plan_cost": 9.6390000000000000e+05, + "rhs_rows": 1.6200000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2052, + "real_time": 3.0510703606291499e+05, + "cpu_time": 3.0402607846003963e+05, + "time_unit": "ns", + "lhs_rows": 1.6200000000000000e+03, + "model_cost": 6.3990000000000000e+05, + "output_rows": 1.6200000000000000e+03, + "plan_cost": 9.6390000000000000e+05, + "rhs_rows": 1.6200000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_mean", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0885672904504131e+05, + "cpu_time": 3.0762243112410681e+05, + "time_unit": "ns", + "lhs_rows": 1.6200000000000000e+03, + "model_cost": 6.3990000000000000e+05, + "output_rows": 1.6200000000000000e+03, + "plan_cost": 9.6390000000000000e+05, + "rhs_rows": 1.6200000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_median", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1039785721233365e+05, + "cpu_time": 3.0903469249512703e+05, + "time_unit": "ns", + "lhs_rows": 1.6200000000000000e+03, + "model_cost": 6.3990000000000000e+05, + "output_rows": 1.6200000000000000e+03, + "plan_cost": 9.6390000000000000e+05, + "rhs_rows": 1.6200000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_stddev", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.2644319589770948e+03, + "cpu_time": 3.1383506369408151e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_cv", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0569405332596900e-02, + "cpu_time": 1.0201956422594825e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1056, + "real_time": 6.6501874526679656e+05, + "cpu_time": 6.6166925946969760e+05, + "time_unit": "ns", + "lhs_rows": 9.6000000000000000e+01, + "model_cost": 6.4512000000000000e+05, + "output_rows": 9.6000000000000000e+01, + "plan_cost": 6.6432000000000000e+05, + "rhs_rows": 9.6000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1056, + "real_time": 6.6649900568129611e+05, + "cpu_time": 6.6314359090909176e+05, + "time_unit": "ns", + "lhs_rows": 9.6000000000000000e+01, + "model_cost": 6.4512000000000000e+05, + "output_rows": 9.6000000000000000e+01, + "plan_cost": 6.6432000000000000e+05, + "rhs_rows": 9.6000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1056, + "real_time": 6.6576830681697174e+05, + "cpu_time": 6.6256839204545307e+05, + "time_unit": "ns", + "lhs_rows": 9.6000000000000000e+01, + "model_cost": 6.4512000000000000e+05, + "output_rows": 9.6000000000000000e+01, + "plan_cost": 6.6432000000000000e+05, + "rhs_rows": 9.6000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_mean", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6576201925502135e+05, + "cpu_time": 6.6246041414141410e+05, + "time_unit": "ns", + "lhs_rows": 9.6000000000000000e+01, + "model_cost": 6.4512000000000000e+05, + "output_rows": 9.6000000000000000e+01, + "plan_cost": 6.6432000000000000e+05, + "rhs_rows": 9.6000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_median", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6576830681697174e+05, + "cpu_time": 6.6256839204545307e+05, + "time_unit": "ns", + "lhs_rows": 9.6000000000000000e+01, + "model_cost": 6.4512000000000000e+05, + "output_rows": 9.6000000000000000e+01, + "plan_cost": 6.6432000000000000e+05, + "rhs_rows": 9.6000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_stddev", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.4015023729405777e+02, + "cpu_time": 7.4307315863388430e+02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_cv", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1117339467971992e-03, + "cpu_time": 1.1216868853921613e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1795, + "real_time": 3.8999236490163032e+05, + "cpu_time": 3.8347962674094742e+05, + "time_unit": "ns", + "lhs_rows": 7.8000000000000000e+01, + "model_cost": 6.3273600000000000e+05, + "output_rows": 6.0840000000000000e+03, + "plan_cost": 6.4833600000000000e+05, + "rhs_rows": 7.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1795, + "real_time": 3.9009576936010504e+05, + "cpu_time": 3.8747032423398481e+05, + "time_unit": "ns", + "lhs_rows": 7.8000000000000000e+01, + "model_cost": 6.3273600000000000e+05, + "output_rows": 6.0840000000000000e+03, + "plan_cost": 6.4833600000000000e+05, + "rhs_rows": 7.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1795, + "real_time": 3.8952511754879227e+05, + "cpu_time": 3.8699354038997233e+05, + "time_unit": "ns", + "lhs_rows": 7.8000000000000000e+01, + "model_cost": 6.3273600000000000e+05, + "output_rows": 6.0840000000000000e+03, + "plan_cost": 6.4833600000000000e+05, + "rhs_rows": 7.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_mean", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.8987108393684245e+05, + "cpu_time": 3.8598116378830146e+05, + "time_unit": "ns", + "lhs_rows": 7.8000000000000000e+01, + "model_cost": 6.3273600000000000e+05, + "output_rows": 6.0840000000000000e+03, + "plan_cost": 6.4833600000000000e+05, + "rhs_rows": 7.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_median", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.8999236490163038e+05, + "cpu_time": 3.8699354038997233e+05, + "time_unit": "ns", + "lhs_rows": 7.8000000000000000e+01, + "model_cost": 6.3273600000000000e+05, + "output_rows": 6.0840000000000000e+03, + "plan_cost": 6.4833600000000000e+05, + "rhs_rows": 7.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_stddev", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0404387311849194e+02, + "cpu_time": 2.1794715891542296e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_cv", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 7.7985745967183815e-04, + "cpu_time": 5.6465749980214091e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1985, + "real_time": 3.5433331838938274e+05, + "cpu_time": 3.5242765188916784e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 1.0000000000000000e+04, + "plan_cost": 1.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1985, + "real_time": 3.5468370176298119e+05, + "cpu_time": 3.5280560554156185e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 1.0000000000000000e+04, + "plan_cost": 1.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1985, + "real_time": 3.5680529622219433e+05, + "cpu_time": 3.5481444382871594e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 1.0000000000000000e+04, + "plan_cost": 1.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_mean", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5527410545818607e+05, + "cpu_time": 3.5334923375314852e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 1.0000000000000000e+04, + "plan_cost": 1.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_median", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5468370176298119e+05, + "cpu_time": 3.5280560554156179e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 1.0000000000000000e+04, + "plan_cost": 1.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_stddev", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3375727995017780e+03, + "cpu_time": 1.2829039968112327e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_cv", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.7649037150533434e-03, + "cpu_time": 3.6306969826556236e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1002, + "real_time": 6.2769447704667330e+05, + "cpu_time": 6.2485120758483198e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 5.1200000000000000e+03, + "plan_cost": 2.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1002, + "real_time": 6.3063119460765040e+05, + "cpu_time": 6.2753678642714547e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 5.1200000000000000e+03, + "plan_cost": 2.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1002, + "real_time": 6.3242097704658192e+05, + "cpu_time": 6.2945885828343406e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 5.1200000000000000e+03, + "plan_cost": 2.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time_mean", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3024888290030195e+05, + "cpu_time": 6.2728228409847047e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 5.1200000000000000e+03, + "plan_cost": 2.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time_median", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3063119460765051e+05, + "cpu_time": 6.2753678642714547e+05, + "time_unit": "ns", + "model_cost": 1.0000000000000000e+06, + "output_rows": 5.1200000000000000e+03, + "plan_cost": 2.0000000000000000e+06, + "rows": 1.0000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time_stddev", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3863302879858070e+03, + "cpu_time": 2.3143443600135965e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time_cv", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Filter/10000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.7863300558412839e-03, + "cpu_time": 3.6894782758607162e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 122, + "real_time": 4.9816019098395118e+06, + "cpu_time": 4.9544085327869086e+06, + "time_unit": "ns", + "model_cost": 1.0000100000000000e+06, + "output_rows": 4.5455000000000000e+04, + "plan_cost": 5.5455100000000000e+06, + "rows": 4.5455000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 122, + "real_time": 5.0094015983691467e+06, + "cpu_time": 4.9823941229508277e+06, + "time_unit": "ns", + "model_cost": 1.0000100000000000e+06, + "output_rows": 4.5455000000000000e+04, + "plan_cost": 5.5455100000000000e+06, + "rows": 4.5455000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 122, + "real_time": 4.9544221639407026e+06, + "cpu_time": 4.9275025983606549e+06, + "time_unit": "ns", + "model_cost": 1.0000100000000000e+06, + "output_rows": 4.5455000000000000e+04, + "plan_cost": 5.5455100000000000e+06, + "rows": 4.5455000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time_mean", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.9818085573831210e+06, + "cpu_time": 4.9547684180327971e+06, + "time_unit": "ns", + "model_cost": 1.0000100000000000e+06, + "output_rows": 4.5455000000000000e+04, + "plan_cost": 5.5455100000000000e+06, + "rows": 4.5455000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time_median", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.9816019098395118e+06, + "cpu_time": 4.9544085327869086e+06, + "time_unit": "ns", + "model_cost": 1.0000100000000000e+06, + "output_rows": 4.5455000000000000e+04, + "plan_cost": 5.5455100000000000e+06, + "rows": 4.5455000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time_stddev", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7490299742283547e+04, + "cpu_time": 2.7447531874389522e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time_cv", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Projection/45455/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.5181365212323306e-03, + "cpu_time": 5.5396195258076422e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 594, + "real_time": 1.1378401750855474e+06, + "cpu_time": 1.1335161094276085e+06, + "time_unit": "ns", + "model_cost": 9.9999900000000000e+05, + "output_rows": 6.9930000000000000e+03, + "plan_cost": 1.6992990000000000e+06, + "rows": 6.9930000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 594, + "real_time": 1.1455248249109199e+06, + "cpu_time": 1.1408682929292931e+06, + "time_unit": "ns", + "model_cost": 9.9999900000000000e+05, + "output_rows": 6.9930000000000000e+03, + "plan_cost": 1.6992990000000000e+06, + "rows": 6.9930000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 594, + "real_time": 1.1423288232304202e+06, + "cpu_time": 1.1374496414141406e+06, + "time_unit": "ns", + "model_cost": 9.9999900000000000e+05, + "output_rows": 6.9930000000000000e+03, + "plan_cost": 1.6992990000000000e+06, + "rows": 6.9930000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time_mean", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1418979410756293e+06, + "cpu_time": 1.1372780145903474e+06, + "time_unit": "ns", + "model_cost": 9.9999900000000000e+05, + "output_rows": 6.9930000000000000e+03, + "plan_cost": 1.6992990000000000e+06, + "rows": 6.9930000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time_median", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1423288232304205e+06, + "cpu_time": 1.1374496414141406e+06, + "time_unit": "ns", + "model_cost": 9.9999900000000000e+05, + "output_rows": 6.9930000000000000e+03, + "plan_cost": 1.6992990000000000e+06, + "rows": 6.9930000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time_stddev", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.8604022210250073e+03, + "cpu_time": 3.6790953216950588e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time_cv", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Sort/6993/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.3806893612476773e-03, + "cpu_time": 3.2350008304875963e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1343, + "real_time": 5.3669842516653473e+05, + "cpu_time": 5.3436887714072736e+05, + "time_unit": "ns", + "model_cost": 1.0001100000000000e+06, + "output_rows": 1.9610000000000000e+03, + "plan_cost": 1.1962100000000000e+06, + "rows": 1.9610000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1343, + "real_time": 5.3255765003641346e+05, + "cpu_time": 5.3023443112435006e+05, + "time_unit": "ns", + "model_cost": 1.0001100000000000e+06, + "output_rows": 1.9610000000000000e+03, + "plan_cost": 1.1962100000000000e+06, + "rows": 1.9610000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1343, + "real_time": 5.3225933134797914e+05, + "cpu_time": 5.2984623454951541e+05, + "time_unit": "ns", + "model_cost": 1.0001100000000000e+06, + "output_rows": 1.9610000000000000e+03, + "plan_cost": 1.1962100000000000e+06, + "rows": 1.9610000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_mean", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.3383846885030915e+05, + "cpu_time": 5.3148318093819753e+05, + "time_unit": "ns", + "model_cost": 1.0001100000000000e+06, + "output_rows": 1.9610000000000000e+03, + "plan_cost": 1.1962100000000000e+06, + "rows": 1.9610000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_median", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.3255765003641346e+05, + "cpu_time": 5.3023443112435006e+05, + "time_unit": "ns", + "model_cost": 1.0001100000000000e+06, + "output_rows": 1.9610000000000000e+03, + "plan_cost": 1.1962100000000000e+06, + "rows": 1.9610000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_stddev", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4812821500415994e+03, + "cpu_time": 2.5066124700974638e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_cv", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6480017736177104e-03, + "cpu_time": 4.7162592533458560e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1432, + "real_time": 4.8710068435790343e+05, + "cpu_time": 4.8449705377095164e+05, + "time_unit": "ns", + "lhs_rows": 2.5320000000000000e+03, + "model_cost": 1.0001400000000000e+06, + "output_rows": 2.5320000000000000e+03, + "plan_cost": 1.5065400000000000e+06, + "rhs_rows": 2.5320000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1432, + "real_time": 4.8789104399556329e+05, + "cpu_time": 4.8550960265363037e+05, + "time_unit": "ns", + "lhs_rows": 2.5320000000000000e+03, + "model_cost": 1.0001400000000000e+06, + "output_rows": 2.5320000000000000e+03, + "plan_cost": 1.5065400000000000e+06, + "rhs_rows": 2.5320000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1432, + "real_time": 4.8274565014197421e+05, + "cpu_time": 4.8046665083798999e+05, + "time_unit": "ns", + "lhs_rows": 2.5320000000000000e+03, + "model_cost": 1.0001400000000000e+06, + "output_rows": 2.5320000000000000e+03, + "plan_cost": 1.5065400000000000e+06, + "rhs_rows": 2.5320000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_mean", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.8591245949848025e+05, + "cpu_time": 4.8349110242085723e+05, + "time_unit": "ns", + "lhs_rows": 2.5320000000000000e+03, + "model_cost": 1.0001400000000000e+06, + "output_rows": 2.5320000000000000e+03, + "plan_cost": 1.5065400000000000e+06, + "rhs_rows": 2.5320000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_median", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.8710068435790343e+05, + "cpu_time": 4.8449705377095164e+05, + "time_unit": "ns", + "lhs_rows": 2.5320000000000000e+03, + "model_cost": 1.0001400000000000e+06, + "output_rows": 2.5320000000000000e+03, + "plan_cost": 1.5065400000000000e+06, + "rhs_rows": 2.5320000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_stddev", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7708623593766188e+03, + "cpu_time": 2.6677320597961912e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_cv", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.7023900194625174e-03, + "cpu_time": 5.5176445780258645e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 693, + "real_time": 1.0438258802304232e+06, + "cpu_time": 1.0267886406926359e+06, + "time_unit": "ns", + "lhs_rows": 1.2000000000000000e+02, + "model_cost": 1.0080000000000000e+06, + "output_rows": 1.2000000000000000e+02, + "plan_cost": 1.0320000000000000e+06, + "rhs_rows": 1.2000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 693, + "real_time": 1.0075375454556257e+06, + "cpu_time": 1.0020099538239490e+06, + "time_unit": "ns", + "lhs_rows": 1.2000000000000000e+02, + "model_cost": 1.0080000000000000e+06, + "output_rows": 1.2000000000000000e+02, + "plan_cost": 1.0320000000000000e+06, + "rhs_rows": 1.2000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 693, + "real_time": 1.0043929090908518e+06, + "cpu_time": 9.9924881818182161e+05, + "time_unit": "ns", + "lhs_rows": 1.2000000000000000e+02, + "model_cost": 1.0080000000000000e+06, + "output_rows": 1.2000000000000000e+02, + "plan_cost": 1.0320000000000000e+06, + "rhs_rows": 1.2000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_mean", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0185854449256334e+06, + "cpu_time": 1.0093491375661356e+06, + "time_unit": "ns", + "lhs_rows": 1.2000000000000000e+02, + "model_cost": 1.0080000000000000e+06, + "output_rows": 1.2000000000000000e+02, + "plan_cost": 1.0320000000000000e+06, + "rhs_rows": 1.2000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_median", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0075375454556257e+06, + "cpu_time": 1.0020099538239491e+06, + "time_unit": "ns", + "lhs_rows": 1.2000000000000000e+02, + "model_cost": 1.0080000000000000e+06, + "output_rows": 1.2000000000000000e+02, + "plan_cost": 1.0320000000000000e+06, + "rhs_rows": 1.2000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_stddev", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.1915334020921273e+04, + "cpu_time": 1.5166020225600876e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_cv", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.1515459630901460e-02, + "cpu_time": 1.5025544344517911e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1163, + "real_time": 5.8840910833990015e+05, + "cpu_time": 5.8406956405847345e+05, + "time_unit": "ns", + "lhs_rows": 9.8000000000000000e+01, + "model_cost": 9.9881600000000000e+05, + "output_rows": 9.6040000000000000e+03, + "plan_cost": 1.0184160000000000e+06, + "rhs_rows": 9.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1163, + "real_time": 5.9627645485679095e+05, + "cpu_time": 5.9222581169389328e+05, + "time_unit": "ns", + "lhs_rows": 9.8000000000000000e+01, + "model_cost": 9.9881600000000000e+05, + "output_rows": 9.6040000000000000e+03, + "plan_cost": 1.0184160000000000e+06, + "rhs_rows": 9.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1163, + "real_time": 5.6384729234676284e+05, + "cpu_time": 5.6077827858985390e+05, + "time_unit": "ns", + "lhs_rows": 9.8000000000000000e+01, + "model_cost": 9.9881600000000000e+05, + "output_rows": 9.6040000000000000e+03, + "plan_cost": 1.0184160000000000e+06, + "rhs_rows": 9.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_mean", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8284428518115135e+05, + "cpu_time": 5.7902455144740688e+05, + "time_unit": "ns", + "lhs_rows": 9.8000000000000000e+01, + "model_cost": 9.9881600000000000e+05, + "output_rows": 9.6040000000000000e+03, + "plan_cost": 1.0184160000000000e+06, + "rhs_rows": 9.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_median", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8840910833990027e+05, + "cpu_time": 5.8406956405847357e+05, + "time_unit": "ns", + "lhs_rows": 9.8000000000000000e+01, + "model_cost": 9.9881600000000000e+05, + "output_rows": 9.6040000000000000e+03, + "plan_cost": 1.0184160000000000e+06, + "rhs_rows": 9.8000000000000000e+01 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_stddev", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6915616685965517e+04, + "cpu_time": 1.6319495971594204e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_cv", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.9022531602429701e-02, + "cpu_time": 2.8184462870874505e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 568, + "real_time": 1.1194045651394282e+06, + "cpu_time": 1.1136787183098528e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 3.2400000000000000e+04, + "plan_cost": 3.2400000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 568, + "real_time": 1.1225315721839906e+06, + "cpu_time": 1.1169980123239360e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 3.2400000000000000e+04, + "plan_cost": 3.2400000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 568, + "real_time": 1.1623374207758873e+06, + "cpu_time": 1.1553145580985874e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 3.2400000000000000e+04, + "plan_cost": 3.2400000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_mean", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1347578526997687e+06, + "cpu_time": 1.1286637629107919e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 3.2400000000000000e+04, + "plan_cost": 3.2400000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_median", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1225315721839906e+06, + "cpu_time": 1.1169980123239357e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 3.2400000000000000e+04, + "plan_cost": 3.2400000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_stddev", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3935725907369171e+04, + "cpu_time": 2.3139859362031868e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_cv", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.1093245444763646e-02, + "cpu_time": 2.0501995476805975e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 331, + "real_time": 2.1149336495450796e+06, + "cpu_time": 2.1047326525679757e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.4800000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 331, + "real_time": 2.2388615045388578e+06, + "cpu_time": 2.2274217824773476e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.4800000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 331, + "real_time": 2.1009314048327850e+06, + "cpu_time": 2.0896825468278064e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.4800000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time_mean", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.1515755196389076e+06, + "cpu_time": 2.1406123272910430e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.4800000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time_median", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.1149336495450796e+06, + "cpu_time": 2.1047326525679757e+06, + "time_unit": "ns", + "model_cost": 3.2400000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.4800000000000000e+06, + "rows": 3.2400000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time_stddev", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.5915400836785033e+04, + "cpu_time": 7.5554864522459175e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time_cv", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Filter/32400/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.5283632921016730e-02, + "cpu_time": 3.5295912089824445e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 36, + "real_time": 1.9277200138882712e+07, + "cpu_time": 1.9131104916666549e+07, + "time_unit": "ns", + "model_cost": 3.2400060000000000e+06, + "output_rows": 1.4727300000000000e+05, + "plan_cost": 1.7967306000000000e+07, + "rows": 1.4727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 36, + "real_time": 1.9715131944495723e+07, + "cpu_time": 1.9566701166666750e+07, + "time_unit": "ns", + "model_cost": 3.2400060000000000e+06, + "output_rows": 1.4727300000000000e+05, + "plan_cost": 1.7967306000000000e+07, + "rows": 1.4727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 36, + "real_time": 1.9886034555485014e+07, + "cpu_time": 1.9739094666666701e+07, + "time_unit": "ns", + "model_cost": 3.2400060000000000e+06, + "output_rows": 1.4727300000000000e+05, + "plan_cost": 1.7967306000000000e+07, + "rows": 1.4727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time_mean", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9626122212954484e+07, + "cpu_time": 1.9478966916666668e+07, + "time_unit": "ns", + "model_cost": 3.2400060000000000e+06, + "output_rows": 1.4727300000000000e+05, + "plan_cost": 1.7967306000000000e+07, + "rows": 1.4727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time_median", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9715131944495723e+07, + "cpu_time": 1.9566701166666750e+07, + "time_unit": "ns", + "model_cost": 3.2400060000000000e+06, + "output_rows": 1.4727300000000000e+05, + "plan_cost": 1.7967306000000000e+07, + "rows": 1.4727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time_stddev", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1402529506662284e+05, + "cpu_time": 3.1334622702948493e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time_cv", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Projection/147273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.6000373973996063e-02, + "cpu_time": 1.6086388378296308e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 171, + "real_time": 3.9185205029130694e+06, + "cpu_time": 3.9026283274853542e+06, + "time_unit": "ns", + "model_cost": 3.2399400000000000e+06, + "output_rows": 1.9636000000000000e+04, + "plan_cost": 5.2035400000000000e+06, + "rows": 1.9636000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 171, + "real_time": 3.9332606257412620e+06, + "cpu_time": 3.9149300350876790e+06, + "time_unit": "ns", + "model_cost": 3.2399400000000000e+06, + "output_rows": 1.9636000000000000e+04, + "plan_cost": 5.2035400000000000e+06, + "rows": 1.9636000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 171, + "real_time": 3.8436262690105098e+06, + "cpu_time": 3.8296677251461945e+06, + "time_unit": "ns", + "model_cost": 3.2399400000000000e+06, + "output_rows": 1.9636000000000000e+04, + "plan_cost": 5.2035400000000000e+06, + "rows": 1.9636000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time_mean", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.8984691325549465e+06, + "cpu_time": 3.8824086959064086e+06, + "time_unit": "ns", + "model_cost": 3.2399400000000000e+06, + "output_rows": 1.9636000000000000e+04, + "plan_cost": 5.2035400000000000e+06, + "rows": 1.9636000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time_median", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.9185205029130690e+06, + "cpu_time": 3.9026283274853542e+06, + "time_unit": "ns", + "model_cost": 3.2399400000000000e+06, + "output_rows": 1.9636000000000000e+04, + "plan_cost": 5.2035400000000000e+06, + "rows": 1.9636000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time_stddev", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.8063734421756788e+04, + "cpu_time": 4.6087313873067236e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time_cv", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Sort/19636/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.2328873921404419e-02, + "cpu_time": 1.1870804308073350e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 401, + "real_time": 1.8448469077281659e+06, + "cpu_time": 1.8365411645885147e+06, + "time_unit": "ns", + "model_cost": 3.2400300000000000e+06, + "output_rows": 6.3530000000000000e+03, + "plan_cost": 3.8753300000000000e+06, + "rows": 6.3530000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 401, + "real_time": 1.8234426733172422e+06, + "cpu_time": 1.8135941346633283e+06, + "time_unit": "ns", + "model_cost": 3.2400300000000000e+06, + "output_rows": 6.3530000000000000e+03, + "plan_cost": 3.8753300000000000e+06, + "rows": 6.3530000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 401, + "real_time": 1.8907349476376793e+06, + "cpu_time": 1.8810223291770415e+06, + "time_unit": "ns", + "model_cost": 3.2400300000000000e+06, + "output_rows": 6.3530000000000000e+03, + "plan_cost": 3.8753300000000000e+06, + "rows": 6.3530000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_mean", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8530081762276955e+06, + "cpu_time": 1.8437192094762949e+06, + "time_unit": "ns", + "model_cost": 3.2400300000000000e+06, + "output_rows": 6.3530000000000000e+03, + "plan_cost": 3.8753300000000000e+06, + "rows": 6.3530000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_median", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8448469077281661e+06, + "cpu_time": 1.8365411645885147e+06, + "time_unit": "ns", + "model_cost": 3.2400300000000000e+06, + "output_rows": 6.3530000000000000e+03, + "plan_cost": 3.8753300000000000e+06, + "rows": 6.3530000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_stddev", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.4380478086360250e+04, + "cpu_time": 3.4282409485806726e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_cv", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.8553872847097256e-02, + "cpu_time": 1.8594159734086940e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 420, + "real_time": 1.6105874952440250e+06, + "cpu_time": 1.6015009047619074e+06, + "time_unit": "ns", + "lhs_rows": 8.2030000000000000e+03, + "model_cost": 3.2401850000000000e+06, + "output_rows": 8.2030000000000000e+03, + "plan_cost": 4.8807850000000000e+06, + "rhs_rows": 8.2030000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 420, + "real_time": 1.6070290547629285e+06, + "cpu_time": 1.5983681214285705e+06, + "time_unit": "ns", + "lhs_rows": 8.2030000000000000e+03, + "model_cost": 3.2401850000000000e+06, + "output_rows": 8.2030000000000000e+03, + "plan_cost": 4.8807850000000000e+06, + "rhs_rows": 8.2030000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 420, + "real_time": 1.5920918047586817e+06, + "cpu_time": 1.5839235309523793e+06, + "time_unit": "ns", + "lhs_rows": 8.2030000000000000e+03, + "model_cost": 3.2401850000000000e+06, + "output_rows": 8.2030000000000000e+03, + "plan_cost": 4.8807850000000000e+06, + "rhs_rows": 8.2030000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_mean", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6032361182552117e+06, + "cpu_time": 1.5945975190476188e+06, + "time_unit": "ns", + "lhs_rows": 8.2030000000000000e+03, + "model_cost": 3.2401850000000000e+06, + "output_rows": 8.2030000000000000e+03, + "plan_cost": 4.8807850000000000e+06, + "rhs_rows": 8.2030000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_median", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6070290547629285e+06, + "cpu_time": 1.5983681214285705e+06, + "time_unit": "ns", + "lhs_rows": 8.2030000000000000e+03, + "model_cost": 3.2401850000000000e+06, + "output_rows": 8.2030000000000000e+03, + "plan_cost": 4.8807850000000000e+06, + "rhs_rows": 8.2030000000000000e+03 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_stddev", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.8138889919282901e+03, + "cpu_time": 9.3757185989758382e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_cv", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.1212998386093393e-03, + "cpu_time": 5.8796771517464373e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 241, + "real_time": 2.8838505643146755e+06, + "cpu_time": 2.8719157136929454e+06, + "time_unit": "ns", + "lhs_rows": 2.1500000000000000e+02, + "model_cost": 3.2357500000000000e+06, + "output_rows": 2.1500000000000000e+02, + "plan_cost": 3.2787500000000000e+06, + "rhs_rows": 2.1500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 241, + "real_time": 2.9244994232412940e+06, + "cpu_time": 2.9107990705394018e+06, + "time_unit": "ns", + "lhs_rows": 2.1500000000000000e+02, + "model_cost": 3.2357500000000000e+06, + "output_rows": 2.1500000000000000e+02, + "plan_cost": 3.2787500000000000e+06, + "rhs_rows": 2.1500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 241, + "real_time": 2.9240677593338946e+06, + "cpu_time": 2.9106966639004154e+06, + "time_unit": "ns", + "lhs_rows": 2.1500000000000000e+02, + "model_cost": 3.2357500000000000e+06, + "output_rows": 2.1500000000000000e+02, + "plan_cost": 3.2787500000000000e+06, + "rhs_rows": 2.1500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_mean", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.9108059156299545e+06, + "cpu_time": 2.8978038160442538e+06, + "time_unit": "ns", + "lhs_rows": 2.1500000000000000e+02, + "model_cost": 3.2357500000000000e+06, + "output_rows": 2.1500000000000000e+02, + "plan_cost": 3.2787500000000000e+06, + "rhs_rows": 2.1500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_median", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.9240677593338937e+06, + "cpu_time": 2.9106966639004149e+06, + "time_unit": "ns", + "lhs_rows": 2.1500000000000000e+02, + "model_cost": 3.2357500000000000e+06, + "output_rows": 2.1500000000000000e+02, + "plan_cost": 3.2787500000000000e+06, + "rhs_rows": 2.1500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_stddev", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3345016745122204e+04, + "cpu_time": 2.2419812762276619e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_cv", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.0201213759282519e-03, + "cpu_time": 7.7368290559026009e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 354, + "real_time": 1.9130518587580242e+06, + "cpu_time": 1.8946784887005747e+06, + "time_unit": "ns", + "lhs_rows": 1.7700000000000000e+02, + "model_cost": 3.2582160000000000e+06, + "output_rows": 3.1329000000000000e+04, + "plan_cost": 3.2936160000000000e+06, + "rhs_rows": 1.7700000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 354, + "real_time": 2.0192119717530708e+06, + "cpu_time": 1.9987998785310758e+06, + "time_unit": "ns", + "lhs_rows": 1.7700000000000000e+02, + "model_cost": 3.2582160000000000e+06, + "output_rows": 3.1329000000000000e+04, + "plan_cost": 3.2936160000000000e+06, + "rhs_rows": 1.7700000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 354, + "real_time": 1.8982003785324174e+06, + "cpu_time": 1.8807306101694885e+06, + "time_unit": "ns", + "lhs_rows": 1.7700000000000000e+02, + "model_cost": 3.2582160000000000e+06, + "output_rows": 3.1329000000000000e+04, + "plan_cost": 3.2936160000000000e+06, + "rhs_rows": 1.7700000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_mean", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9434880696811711e+06, + "cpu_time": 1.9247363258003795e+06, + "time_unit": "ns", + "lhs_rows": 1.7700000000000000e+02, + "model_cost": 3.2582160000000000e+06, + "output_rows": 3.1329000000000000e+04, + "plan_cost": 3.2936160000000000e+06, + "rhs_rows": 1.7700000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_median", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9130518587580242e+06, + "cpu_time": 1.8946784887005745e+06, + "time_unit": "ns", + "lhs_rows": 1.7700000000000000e+02, + "model_cost": 3.2582160000000000e+06, + "output_rows": 3.1329000000000000e+04, + "plan_cost": 3.2936160000000000e+06, + "rhs_rows": 1.7700000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_stddev", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.5997906216558025e+04, + "cpu_time": 6.4518936841824965e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_cv", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.3958482815582697e-02, + "cpu_time": 3.3520922308667662e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 191, + "real_time": 3.4445205549706453e+06, + "cpu_time": 3.4124743193717292e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 6.7600000000000000e+04, + "plan_cost": 6.7600000000000000e+06, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 191, + "real_time": 3.3868414764355016e+06, + "cpu_time": 3.3565554031413412e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 6.7600000000000000e+04, + "plan_cost": 6.7600000000000000e+06, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 191, + "real_time": 3.3169767434557313e+06, + "cpu_time": 3.2917669476439739e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 6.7600000000000000e+04, + "plan_cost": 6.7600000000000000e+06, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_mean", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.3827795916206255e+06, + "cpu_time": 3.3535988900523479e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 6.7600000000000000e+04, + "plan_cost": 6.7600000000000000e+06, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_median", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.3868414764355007e+06, + "cpu_time": 3.3565554031413407e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 6.7600000000000000e+04, + "plan_cost": 6.7600000000000000e+06, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_stddev", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3868851133623881e+04, + "cpu_time": 6.0407972359519510e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_cv", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.8880583083754957e-02, + "cpu_time": 1.8012879399115191e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 116, + "real_time": 4.4690427068793541e+06, + "cpu_time": 4.4439296293103714e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 3.3808000000000000e+04, + "plan_cost": 1.3520000000000000e+07, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 116, + "real_time": 4.7122111379300393e+06, + "cpu_time": 4.6769992327585993e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 3.3808000000000000e+04, + "plan_cost": 1.3520000000000000e+07, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 116, + "real_time": 4.8310390775754815e+06, + "cpu_time": 4.7991511379310796e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 3.3808000000000000e+04, + "plan_cost": 1.3520000000000000e+07, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time_mean", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.6707643074616250e+06, + "cpu_time": 4.6400266666666837e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 3.3808000000000000e+04, + "plan_cost": 1.3520000000000000e+07, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time_median", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.7122111379300402e+06, + "cpu_time": 4.6769992327585993e+06, + "time_unit": "ns", + "model_cost": 6.7600000000000000e+06, + "output_rows": 3.3808000000000000e+04, + "plan_cost": 1.3520000000000000e+07, + "rows": 6.7600000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time_stddev", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8452296040383805e+05, + "cpu_time": 1.8047384306113768e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time_cv", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Filter/67600/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.9505945549224028e-02, + "cpu_time": 3.8895001263168826e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 17, + "real_time": 4.1575665764599651e+07, + "cpu_time": 4.1278783705882251e+07, + "time_unit": "ns", + "model_cost": 6.7600060000000000e+06, + "output_rows": 3.0727300000000000e+05, + "plan_cost": 3.7487306000000000e+07, + "rows": 3.0727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 17, + "real_time": 4.1320727058679469e+07, + "cpu_time": 4.0926270058823258e+07, + "time_unit": "ns", + "model_cost": 6.7600060000000000e+06, + "output_rows": 3.0727300000000000e+05, + "plan_cost": 3.7487306000000000e+07, + "rows": 3.0727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 17, + "real_time": 4.1540129411741339e+07, + "cpu_time": 4.1133353705882147e+07, + "time_unit": "ns", + "model_cost": 6.7600060000000000e+06, + "output_rows": 3.0727300000000000e+05, + "plan_cost": 3.7487306000000000e+07, + "rows": 3.0727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time_mean", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.1478840745006822e+07, + "cpu_time": 4.1112802490195885e+07, + "time_unit": "ns", + "model_cost": 6.7600060000000000e+06, + "output_rows": 3.0727300000000000e+05, + "plan_cost": 3.7487306000000000e+07, + "rows": 3.0727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time_median", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.1540129411741339e+07, + "cpu_time": 4.1133353705882154e+07, + "time_unit": "ns", + "model_cost": 6.7600060000000000e+06, + "output_rows": 3.0727300000000000e+05, + "plan_cost": 3.7487306000000000e+07, + "rows": 3.0727300000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time_stddev", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3807846119852070e+05, + "cpu_time": 1.7715313203725443e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time_cv", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Projection/307273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.3288891087232819e-03, + "cpu_time": 4.3089529612947179e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 69, + "real_time": 1.0315979376806038e+07, + "cpu_time": 1.0245213855072534e+07, + "time_unit": "ns", + "model_cost": 6.7599840000000000e+06, + "output_rows": 3.8409000000000000e+04, + "plan_cost": 1.0600884000000000e+07, + "rows": 3.8409000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 69, + "real_time": 1.0310139942031011e+07, + "cpu_time": 1.0246672159420282e+07, + "time_unit": "ns", + "model_cost": 6.7599840000000000e+06, + "output_rows": 3.8409000000000000e+04, + "plan_cost": 1.0600884000000000e+07, + "rows": 3.8409000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 69, + "real_time": 9.7521267826263998e+06, + "cpu_time": 9.6988003478260934e+06, + "time_unit": "ns", + "model_cost": 6.7599840000000000e+06, + "output_rows": 3.8409000000000000e+04, + "plan_cost": 1.0600884000000000e+07, + "rows": 3.8409000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time_mean", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0126082033821149e+07, + "cpu_time": 1.0063562120772971e+07, + "time_unit": "ns", + "model_cost": 6.7599840000000000e+06, + "output_rows": 3.8409000000000000e+04, + "plan_cost": 1.0600884000000000e+07, + "rows": 3.8409000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time_median", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0310139942031009e+07, + "cpu_time": 1.0245213855072534e+07, + "time_unit": "ns", + "model_cost": 6.7599840000000000e+06, + "output_rows": 3.8409000000000000e+04, + "plan_cost": 1.0600884000000000e+07, + "rows": 3.8409000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time_stddev", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.2386790852402127e+05, + "cpu_time": 3.1589380322411103e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time_cv", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Sort/38409/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.1983535926560870e-02, + "cpu_time": 3.1389859716973417e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 170, + "real_time": 4.2038333235241761e+06, + "cpu_time": 4.1811743294117907e+06, + "time_unit": "ns", + "model_cost": 6.7600500000000000e+06, + "output_rows": 1.3255000000000000e+04, + "plan_cost": 8.0855500000000000e+06, + "rows": 1.3255000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 170, + "real_time": 4.2756962058774661e+06, + "cpu_time": 4.2518384647059003e+06, + "time_unit": "ns", + "model_cost": 6.7600500000000000e+06, + "output_rows": 1.3255000000000000e+04, + "plan_cost": 8.0855500000000000e+06, + "rows": 1.3255000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 170, + "real_time": 4.2982202117641196e+06, + "cpu_time": 4.2745971529411720e+06, + "time_unit": "ns", + "model_cost": 6.7600500000000000e+06, + "output_rows": 1.3255000000000000e+04, + "plan_cost": 8.0855500000000000e+06, + "rows": 1.3255000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_mean", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.2592499137219209e+06, + "cpu_time": 4.2358699823529543e+06, + "time_unit": "ns", + "model_cost": 6.7600500000000000e+06, + "output_rows": 1.3255000000000000e+04, + "plan_cost": 8.0855500000000000e+06, + "rows": 1.3255000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_median", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.2756962058774661e+06, + "cpu_time": 4.2518384647059003e+06, + "time_unit": "ns", + "model_cost": 6.7600500000000000e+06, + "output_rows": 1.3255000000000000e+04, + "plan_cost": 8.0855500000000000e+06, + "rows": 1.3255000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_stddev", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.9295857454287507e+04, + "cpu_time": 4.8715503800550687e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_cv", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1573835405964853e-02, + "cpu_time": 1.1500708001781029e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 188, + "real_time": 3.8816334202025211e+06, + "cpu_time": 3.8509602553191409e+06, + "time_unit": "ns", + "lhs_rows": 1.7114000000000000e+04, + "model_cost": 6.7600300000000000e+06, + "output_rows": 1.7114000000000000e+04, + "plan_cost": 1.0182830000000000e+07, + "rhs_rows": 1.7114000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 188, + "real_time": 3.7382285106317834e+06, + "cpu_time": 3.7129705691489545e+06, + "time_unit": "ns", + "lhs_rows": 1.7114000000000000e+04, + "model_cost": 6.7600300000000000e+06, + "output_rows": 1.7114000000000000e+04, + "plan_cost": 1.0182830000000000e+07, + "rhs_rows": 1.7114000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 188, + "real_time": 3.6913151489375676e+06, + "cpu_time": 3.6675774893617225e+06, + "time_unit": "ns", + "lhs_rows": 1.7114000000000000e+04, + "model_cost": 6.7600300000000000e+06, + "output_rows": 1.7114000000000000e+04, + "plan_cost": 1.0182830000000000e+07, + "rhs_rows": 1.7114000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_mean", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7703923599239569e+06, + "cpu_time": 3.7438361046099388e+06, + "time_unit": "ns", + "lhs_rows": 1.7114000000000000e+04, + "model_cost": 6.7600300000000000e+06, + "output_rows": 1.7114000000000000e+04, + "plan_cost": 1.0182830000000000e+07, + "rhs_rows": 1.7114000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_median", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7382285106317829e+06, + "cpu_time": 3.7129705691489540e+06, + "time_unit": "ns", + "lhs_rows": 1.7114000000000000e+04, + "model_cost": 6.7600300000000000e+06, + "output_rows": 1.7114000000000000e+04, + "plan_cost": 1.0182830000000000e+07, + "rhs_rows": 1.7114000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_stddev", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.9152135606278447e+04, + "cpu_time": 9.5508223059701733e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_cv", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.6297564322530671e-02, + "cpu_time": 2.5510791709631345e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 120, + "real_time": 5.8697186833342128e+06, + "cpu_time": 5.8452958499999614e+06, + "time_unit": "ns", + "lhs_rows": 3.1100000000000000e+02, + "model_cost": 6.7704700000000000e+06, + "output_rows": 3.1100000000000000e+02, + "plan_cost": 6.8326700000000000e+06, + "rhs_rows": 3.1100000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 120, + "real_time": 5.7186517333320808e+06, + "cpu_time": 5.6974987916666698e+06, + "time_unit": "ns", + "lhs_rows": 3.1100000000000000e+02, + "model_cost": 6.7704700000000000e+06, + "output_rows": 3.1100000000000000e+02, + "plan_cost": 6.8326700000000000e+06, + "rhs_rows": 3.1100000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 120, + "real_time": 5.7306100416705403e+06, + "cpu_time": 5.7089901000000043e+06, + "time_unit": "ns", + "lhs_rows": 3.1100000000000000e+02, + "model_cost": 6.7704700000000000e+06, + "output_rows": 3.1100000000000000e+02, + "plan_cost": 6.8326700000000000e+06, + "rhs_rows": 3.1100000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_mean", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.7729934861122780e+06, + "cpu_time": 5.7505949138888782e+06, + "time_unit": "ns", + "lhs_rows": 3.1100000000000000e+02, + "model_cost": 6.7704700000000000e+06, + "output_rows": 3.1100000000000000e+02, + "plan_cost": 6.8326700000000000e+06, + "rhs_rows": 3.1100000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_median", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.7306100416705394e+06, + "cpu_time": 5.7089901000000043e+06, + "time_unit": "ns", + "lhs_rows": 3.1100000000000000e+02, + "model_cost": 6.7704700000000000e+06, + "output_rows": 3.1100000000000000e+02, + "plan_cost": 6.8326700000000000e+06, + "rhs_rows": 3.1100000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_stddev", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3979599414396522e+04, + "cpu_time": 8.2214433143141883e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_cv", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.4546976298591167e-02, + "cpu_time": 1.4296683103964947e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 137, + "real_time": 5.0641394598536147e+06, + "cpu_time": 5.0045665474452516e+06, + "time_unit": "ns", + "lhs_rows": 2.5500000000000000e+02, + "model_cost": 6.7626000000000000e+06, + "output_rows": 6.5025000000000000e+04, + "plan_cost": 6.8136000000000000e+06, + "rhs_rows": 2.5500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 137, + "real_time": 5.1911513576663034e+06, + "cpu_time": 5.1264440948905954e+06, + "time_unit": "ns", + "lhs_rows": 2.5500000000000000e+02, + "model_cost": 6.7626000000000000e+06, + "output_rows": 6.5025000000000000e+04, + "plan_cost": 6.8136000000000000e+06, + "rhs_rows": 2.5500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 137, + "real_time": 5.1045589270140296e+06, + "cpu_time": 5.0391962408758793e+06, + "time_unit": "ns", + "lhs_rows": 2.5500000000000000e+02, + "model_cost": 6.7626000000000000e+06, + "output_rows": 6.5025000000000000e+04, + "plan_cost": 6.8136000000000000e+06, + "rhs_rows": 2.5500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_mean", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.1199499148446489e+06, + "cpu_time": 5.0567356277372418e+06, + "time_unit": "ns", + "lhs_rows": 2.5500000000000000e+02, + "model_cost": 6.7626000000000000e+06, + "output_rows": 6.5025000000000000e+04, + "plan_cost": 6.8136000000000000e+06, + "rhs_rows": 2.5500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_median", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.1045589270140296e+06, + "cpu_time": 5.0391962408758802e+06, + "time_unit": "ns", + "lhs_rows": 2.5500000000000000e+02, + "model_cost": 6.7626000000000000e+06, + "output_rows": 6.5025000000000000e+04, + "plan_cost": 6.8136000000000000e+06, + "rhs_rows": 2.5500000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_stddev", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.4889655772713224e+04, + "cpu_time": 6.2803317678504209e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_cv", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.2673884872305851e-02, + "cpu_time": 1.2419735240659013e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 70, + "real_time": 7.2192994714506706e+06, + "cpu_time": 7.1536679142858312e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 1.2250000000000000e+05, + "plan_cost": 1.2250000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 70, + "real_time": 6.9894773285860932e+06, + "cpu_time": 6.9234417000000039e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 1.2250000000000000e+05, + "plan_cost": 1.2250000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 70, + "real_time": 6.7752621428683465e+06, + "cpu_time": 6.7208630428571124e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 1.2250000000000000e+05, + "plan_cost": 1.2250000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_mean", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.9946796476350361e+06, + "cpu_time": 6.9326575523809819e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 1.2250000000000000e+05, + "plan_cost": 1.2250000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_median", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.9894773285860932e+06, + "cpu_time": 6.9234417000000039e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 1.2250000000000000e+05, + "plan_cost": 1.2250000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_stddev", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.2206437216772154e+05, + "cpu_time": 2.1654956276668992e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_cv", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.1747611521108547e-02, + "cpu_time": 3.1236154552638649e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 77, + "real_time": 8.8145816103837229e+06, + "cpu_time": 8.7519335194805134e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 6.1440000000000000e+04, + "plan_cost": 2.4500000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 77, + "real_time": 8.9319949869864769e+06, + "cpu_time": 8.8616890259738490e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 6.1440000000000000e+04, + "plan_cost": 2.4500000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 77, + "real_time": 8.8010108701347262e+06, + "cpu_time": 8.7357694545454625e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 6.1440000000000000e+04, + "plan_cost": 2.4500000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time_mean", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.8491958225016426e+06, + "cpu_time": 8.7831306666666064e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 6.1440000000000000e+04, + "plan_cost": 2.4500000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time_median", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.8145816103837211e+06, + "cpu_time": 8.7519335194805134e+06, + "time_unit": "ns", + "model_cost": 1.2250000000000000e+07, + "output_rows": 6.1440000000000000e+04, + "plan_cost": 2.4500000000000000e+07, + "rows": 1.2250000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time_stddev", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.2026505380105955e+04, + "cpu_time": 6.8511904889518584e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time_cv", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Filter/122500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.1393277790234603e-03, + "cpu_time": 7.8003968618538576e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 10, + "real_time": 7.4892891000126839e+07, + "cpu_time": 7.4148114099999413e+07, + "time_unit": "ns", + "model_cost": 1.2249996000000000e+07, + "output_rows": 5.5681800000000000e+05, + "plan_cost": 6.7931796000000000e+07, + "rows": 5.5681800000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 10, + "real_time": 7.3184035300073445e+07, + "cpu_time": 7.2626437299999222e+07, + "time_unit": "ns", + "model_cost": 1.2249996000000000e+07, + "output_rows": 5.5681800000000000e+05, + "plan_cost": 6.7931796000000000e+07, + "rows": 5.5681800000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 10, + "real_time": 7.5988647299891457e+07, + "cpu_time": 7.5301902100000009e+07, + "time_unit": "ns", + "model_cost": 1.2249996000000000e+07, + "output_rows": 5.5681800000000000e+05, + "plan_cost": 6.7931796000000000e+07, + "rows": 5.5681800000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time_mean", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.4688524533363909e+07, + "cpu_time": 7.4025484499999538e+07, + "time_unit": "ns", + "model_cost": 1.2249996000000000e+07, + "output_rows": 5.5681800000000000e+05, + "plan_cost": 6.7931796000000000e+07, + "rows": 5.5681800000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time_median", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.4892891000126824e+07, + "cpu_time": 7.4148114099999413e+07, + "time_unit": "ns", + "model_cost": 1.2249996000000000e+07, + "output_rows": 5.5681800000000000e+05, + "plan_cost": 6.7931796000000000e+07, + "rows": 5.5681800000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time_stddev", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4134307046807287e+06, + "cpu_time": 1.3419413132124788e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time_cv", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Projection/556818/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.8924335612619300e-02, + "cpu_time": 1.8128099022607369e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 35, + "real_time": 2.0001183600002799e+07, + "cpu_time": 1.9830396628571276e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 35, + "real_time": 2.0777739771438062e+07, + "cpu_time": 2.0568345514285672e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 35, + "real_time": 2.0977178314309899e+07, + "cpu_time": 2.0757413742857367e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time_mean", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0585367228583585e+07, + "cpu_time": 2.0385385295238104e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time_median", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0777739771438066e+07, + "cpu_time": 2.0568345514285672e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time_stddev", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.1565183702010143e+05, + "cpu_time": 4.8984284608480684e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time_cv", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.5049435907273914e-02, + "cpu_time": 2.4029118851103148e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 63, + "real_time": 1.0619408396797510e+07, + "cpu_time": 1.0498691063491926e+07, + "time_unit": "ns", + "model_cost": 1.2250200000000000e+07, + "output_rows": 2.4020000000000000e+04, + "plan_cost": 1.4652200000000000e+07, + "rows": 2.4020000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 63, + "real_time": 9.9457307142507676e+06, + "cpu_time": 9.8654796349206120e+06, + "time_unit": "ns", + "model_cost": 1.2250200000000000e+07, + "output_rows": 2.4020000000000000e+04, + "plan_cost": 1.4652200000000000e+07, + "rows": 2.4020000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 63, + "real_time": 9.7143659047668781e+06, + "cpu_time": 9.6142546190477908e+06, + "time_unit": "ns", + "model_cost": 1.2250200000000000e+07, + "output_rows": 2.4020000000000000e+04, + "plan_cost": 1.4652200000000000e+07, + "rows": 2.4020000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_mean", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0093168338605052e+07, + "cpu_time": 9.9928084391534422e+06, + "time_unit": "ns", + "model_cost": 1.2250200000000000e+07, + "output_rows": 2.4020000000000000e+04, + "plan_cost": 1.4652200000000000e+07, + "rows": 2.4020000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_median", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.9457307142507657e+06, + "cpu_time": 9.8654796349206120e+06, + "time_unit": "ns", + "model_cost": 1.2250200000000000e+07, + "output_rows": 2.4020000000000000e+04, + "plan_cost": 1.4652200000000000e+07, + "rows": 2.4020000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_stddev", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.7019024649781379e+05, + "cpu_time": 4.5575917363863421e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_cv", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6584999944903074e-02, + "cpu_time": 4.5608717150315420e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 94, + "real_time": 7.1130791808521263e+06, + "cpu_time": 7.0478910851062862e+06, + "time_unit": "ns", + "lhs_rows": 3.1013000000000000e+04, + "model_cost": 1.2250135000000000e+07, + "output_rows": 3.1013000000000000e+04, + "plan_cost": 1.8452735000000000e+07, + "rhs_rows": 3.1013000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 94, + "real_time": 7.1419395319142193e+06, + "cpu_time": 7.0774271170211509e+06, + "time_unit": "ns", + "lhs_rows": 3.1013000000000000e+04, + "model_cost": 1.2250135000000000e+07, + "output_rows": 3.1013000000000000e+04, + "plan_cost": 1.8452735000000000e+07, + "rhs_rows": 3.1013000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 94, + "real_time": 7.2009828191438187e+06, + "cpu_time": 7.1336051063829949e+06, + "time_unit": "ns", + "lhs_rows": 3.1013000000000000e+04, + "model_cost": 1.2250135000000000e+07, + "output_rows": 3.1013000000000000e+04, + "plan_cost": 1.8452735000000000e+07, + "rhs_rows": 3.1013000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_mean", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.1520005106367208e+06, + "cpu_time": 7.0863077695034770e+06, + "time_unit": "ns", + "lhs_rows": 3.1013000000000000e+04, + "model_cost": 1.2250135000000000e+07, + "output_rows": 3.1013000000000000e+04, + "plan_cost": 1.8452735000000000e+07, + "rhs_rows": 3.1013000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_median", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.1419395319142183e+06, + "cpu_time": 7.0774271170211509e+06, + "time_unit": "ns", + "lhs_rows": 3.1013000000000000e+04, + "model_cost": 1.2250135000000000e+07, + "output_rows": 3.1013000000000000e+04, + "plan_cost": 1.8452735000000000e+07, + "rhs_rows": 3.1013000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_stddev", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.4807140902797313e+04, + "cpu_time": 4.3541622067070799e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_cv", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.2649801039804838e-03, + "cpu_time": 6.1444723378309704e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 62, + "real_time": 1.1065283451605337e+07, + "cpu_time": 1.0986525161290308e+07, + "time_unit": "ns", + "lhs_rows": 4.1800000000000000e+02, + "model_cost": 1.2230680000000000e+07, + "output_rows": 4.1800000000000000e+02, + "plan_cost": 1.2314280000000000e+07, + "rhs_rows": 4.1800000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 62, + "real_time": 1.0932178451595621e+07, + "cpu_time": 1.0886830306451466e+07, + "time_unit": "ns", + "lhs_rows": 4.1800000000000000e+02, + "model_cost": 1.2230680000000000e+07, + "output_rows": 4.1800000000000000e+02, + "plan_cost": 1.2314280000000000e+07, + "rhs_rows": 4.1800000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 62, + "real_time": 1.0839656725781217e+07, + "cpu_time": 1.0791560499999864e+07, + "time_unit": "ns", + "lhs_rows": 4.1800000000000000e+02, + "model_cost": 1.2230680000000000e+07, + "output_rows": 4.1800000000000000e+02, + "plan_cost": 1.2314280000000000e+07, + "rhs_rows": 4.1800000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_mean", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0945706209660726e+07, + "cpu_time": 1.0888305322580546e+07, + "time_unit": "ns", + "lhs_rows": 4.1800000000000000e+02, + "model_cost": 1.2230680000000000e+07, + "output_rows": 4.1800000000000000e+02, + "plan_cost": 1.2314280000000000e+07, + "rhs_rows": 4.1800000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_median", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0932178451595621e+07, + "cpu_time": 1.0886830306451466e+07, + "time_unit": "ns", + "lhs_rows": 4.1800000000000000e+02, + "model_cost": 1.2230680000000000e+07, + "output_rows": 4.1800000000000000e+02, + "plan_cost": 1.2314280000000000e+07, + "rhs_rows": 4.1800000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_stddev", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1342003804535972e+05, + "cpu_time": 9.7490699774232999e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_cv", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0362057584302302e-02, + "cpu_time": 8.9537073847528320e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 72, + "real_time": 9.3709239027804062e+06, + "cpu_time": 9.2494039583333656e+06, + "time_unit": "ns", + "lhs_rows": 3.4300000000000000e+02, + "model_cost": 1.2235496000000000e+07, + "output_rows": 1.1764900000000000e+05, + "plan_cost": 1.2304096000000000e+07, + "rhs_rows": 3.4300000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 72, + "real_time": 9.5691700000012740e+06, + "cpu_time": 9.4526028194444999e+06, + "time_unit": "ns", + "lhs_rows": 3.4300000000000000e+02, + "model_cost": 1.2235496000000000e+07, + "output_rows": 1.1764900000000000e+05, + "plan_cost": 1.2304096000000000e+07, + "rhs_rows": 3.4300000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 72, + "real_time": 1.1897035638867237e+07, + "cpu_time": 1.1714046486111112e+07, + "time_unit": "ns", + "lhs_rows": 3.4300000000000000e+02, + "model_cost": 1.2235496000000000e+07, + "output_rows": 1.1764900000000000e+05, + "plan_cost": 1.2304096000000000e+07, + "rhs_rows": 3.4300000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_mean", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0279043180549638e+07, + "cpu_time": 1.0138684421296323e+07, + "time_unit": "ns", + "lhs_rows": 3.4300000000000000e+02, + "model_cost": 1.2235496000000000e+07, + "output_rows": 1.1764900000000000e+05, + "plan_cost": 1.2304096000000000e+07, + "rhs_rows": 3.4300000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_median", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.5691700000012740e+06, + "cpu_time": 9.4526028194444999e+06, + "time_unit": "ns", + "lhs_rows": 3.4300000000000000e+02, + "model_cost": 1.2235496000000000e+07, + "output_rows": 1.1764900000000000e+05, + "plan_cost": 1.2304096000000000e+07, + "rhs_rows": 3.4300000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_stddev", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4047241989605229e+06, + "cpu_time": 1.3680813830808310e+06, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_cv", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.3665904250879993e-01, + "cpu_time": 1.3493677544664215e-01, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 60, + "real_time": 1.2445488150054492e+07, + "cpu_time": 1.2357205483333427e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 2.6010000000000000e+05, + "plan_cost": 2.6010000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 60, + "real_time": 1.2900366483336257e+07, + "cpu_time": 1.2805118766666604e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 2.6010000000000000e+05, + "plan_cost": 2.6010000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 60, + "real_time": 1.4664686099968094e+07, + "cpu_time": 1.4513248916666536e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 2.6010000000000000e+05, + "plan_cost": 2.6010000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_mean", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3336846911119616e+07, + "cpu_time": 1.3225191055555522e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 2.6010000000000000e+05, + "plan_cost": 2.6010000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_median", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2900366483336257e+07, + "cpu_time": 1.2805118766666604e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 2.6010000000000000e+05, + "plan_cost": 2.6010000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_stddev", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1722185197750556e+06, + "cpu_time": 1.1377505735107160e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_cv", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.7893227506249380e-02, + "cpu_time": 8.6029046289866615e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 39, + "real_time": 1.8564079384631418e+07, + "cpu_time": 1.8476515435897529e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 1.3005200000000000e+05, + "plan_cost": 5.2020000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 39, + "real_time": 1.8406469512858447e+07, + "cpu_time": 1.8304321282051262e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 1.3005200000000000e+05, + "plan_cost": 5.2020000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 39, + "real_time": 1.8811837948678892e+07, + "cpu_time": 1.8689153512820322e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 1.3005200000000000e+05, + "plan_cost": 5.2020000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time_mean", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8594128948722918e+07, + "cpu_time": 1.8489996743589703e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 1.3005200000000000e+05, + "plan_cost": 5.2020000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time_median", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8564079384631418e+07, + "cpu_time": 1.8476515435897529e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 1.3005200000000000e+05, + "plan_cost": 5.2020000000000000e+07, + "rows": 2.6010000000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time_stddev", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0434804725356543e+05, + "cpu_time": 1.9276999430002703e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time_cv", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Filter/260100/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0989923099764263e-02, + "cpu_time": 1.0425637006499660e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 3, + "real_time": 1.8557444133330140e+08, + "cpu_time": 1.7944237166666710e+08, + "time_unit": "ns", + "model_cost": 2.6010006000000000e+07, + "output_rows": 1.1822730000000000e+06, + "plan_cost": 1.4423730600000000e+08, + "rows": 1.1822730000000000e+06 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 3, + "real_time": 1.7689592599951234e+08, + "cpu_time": 1.7443871366666979e+08, + "time_unit": "ns", + "model_cost": 2.6010006000000000e+07, + "output_rows": 1.1822730000000000e+06, + "plan_cost": 1.4423730600000000e+08, + "rows": 1.1822730000000000e+06 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 3, + "real_time": 1.9234867366564381e+08, + "cpu_time": 1.8960307399999955e+08, + "time_unit": "ns", + "model_cost": 2.6010006000000000e+07, + "output_rows": 1.1822730000000000e+06, + "plan_cost": 1.4423730600000000e+08, + "rows": 1.1822730000000000e+06 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_mean", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8493968033281913e+08, + "cpu_time": 1.8116138644444546e+08, + "time_unit": "ns", + "model_cost": 2.6010006000000000e+07, + "output_rows": 1.1822730000000000e+06, + "plan_cost": 1.4423730600000000e+08, + "rows": 1.1822730000000000e+06 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_median", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8557444133330140e+08, + "cpu_time": 1.7944237166666710e+08, + "time_unit": "ns", + "model_cost": 2.6010006000000000e+07, + "output_rows": 1.1822730000000000e+06, + "plan_cost": 1.4423730600000000e+08, + "rows": 1.1822730000000000e+06 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_stddev", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.7459049667607406e+06, + "cpu_time": 7.7269473231313974e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_cv", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.1883412758263336e-02, + "cpu_time": 4.2652286311028673e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 8, + "real_time": 9.8504922500069365e+07, + "cpu_time": 9.7412332624999821e+07, + "time_unit": "ns", + "model_cost": 2.6010072000000000e+07, + "output_rows": 1.3136400000000000e+05, + "plan_cost": 3.9146472000000000e+07, + "rows": 1.3136400000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 8, + "real_time": 8.6036432750006497e+07, + "cpu_time": 8.5027458124999449e+07, + "time_unit": "ns", + "model_cost": 2.6010072000000000e+07, + "output_rows": 1.3136400000000000e+05, + "plan_cost": 3.9146472000000000e+07, + "rows": 1.3136400000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 8, + "real_time": 8.0885440124802694e+07, + "cpu_time": 7.9132876749998361e+07, + "time_unit": "ns", + "model_cost": 2.6010072000000000e+07, + "output_rows": 1.3136400000000000e+05, + "plan_cost": 3.9146472000000000e+07, + "rows": 1.3136400000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time_mean", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.8475598458292857e+07, + "cpu_time": 8.7190889166665882e+07, + "time_unit": "ns", + "model_cost": 2.6010072000000000e+07, + "output_rows": 1.3136400000000000e+05, + "plan_cost": 3.9146472000000000e+07, + "rows": 1.3136400000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time_median", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.6036432750006497e+07, + "cpu_time": 8.5027458124999449e+07, + "time_unit": "ns", + "model_cost": 2.6010072000000000e+07, + "output_rows": 1.3136400000000000e+05, + "plan_cost": 3.9146472000000000e+07, + "rows": 1.3136400000000000e+05 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time_stddev", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.0594528977993187e+06, + "cpu_time": 9.3297884314473588e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time_cv", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Sort/131364/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0239493211306075e-01, + "cpu_time": 1.0700416661210344e-01, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 20, + "real_time": 3.1578403249841355e+07, + "cpu_time": 3.1374204650000341e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 5.1000000000000000e+04, + "plan_cost": 3.1110000000000000e+07, + "rows": 5.1000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 20, + "real_time": 3.1751288499981456e+07, + "cpu_time": 3.1313289349999707e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 5.1000000000000000e+04, + "plan_cost": 3.1110000000000000e+07, + "rows": 5.1000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 20, + "real_time": 3.0507513350130469e+07, + "cpu_time": 3.0061373449999709e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 5.1000000000000000e+04, + "plan_cost": 3.1110000000000000e+07, + "rows": 5.1000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_mean", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1279068366651092e+07, + "cpu_time": 3.0916289149999917e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 5.1000000000000000e+04, + "plan_cost": 3.1110000000000000e+07, + "rows": 5.1000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_median", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1578403249841359e+07, + "cpu_time": 3.1313289349999707e+07, + "time_unit": "ns", + "model_cost": 2.6010000000000000e+07, + "output_rows": 5.1000000000000000e+04, + "plan_cost": 3.1110000000000000e+07, + "rows": 5.1000000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_stddev", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.7375454363427730e+05, + "cpu_time": 7.4100493184850633e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_cv", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.1540109051093621e-02, + "cpu_time": 2.3968107176553188e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 51, + "real_time": 1.5567576823563213e+07, + "cpu_time": 1.5447459196078412e+07, + "time_unit": "ns", + "lhs_rows": 6.5848000000000000e+04, + "model_cost": 2.6009960000000000e+07, + "output_rows": 6.5848000000000000e+04, + "plan_cost": 3.9179560000000000e+07, + "rhs_rows": 6.5848000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 51, + "real_time": 1.3858204745095767e+07, + "cpu_time": 1.3798366549019760e+07, + "time_unit": "ns", + "lhs_rows": 6.5848000000000000e+04, + "model_cost": 2.6009960000000000e+07, + "output_rows": 6.5848000000000000e+04, + "plan_cost": 3.9179560000000000e+07, + "rhs_rows": 6.5848000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 51, + "real_time": 1.3834395725508312e+07, + "cpu_time": 1.3780844490195885e+07, + "time_unit": "ns", + "lhs_rows": 6.5848000000000000e+04, + "model_cost": 2.6009960000000000e+07, + "output_rows": 6.5848000000000000e+04, + "plan_cost": 3.9179560000000000e+07, + "rhs_rows": 6.5848000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_mean", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4420059098055765e+07, + "cpu_time": 1.4342223411764687e+07, + "time_unit": "ns", + "lhs_rows": 6.5848000000000000e+04, + "model_cost": 2.6009960000000000e+07, + "output_rows": 6.5848000000000000e+04, + "plan_cost": 3.9179560000000000e+07, + "rhs_rows": 6.5848000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_median", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3858204745095767e+07, + "cpu_time": 1.3798366549019760e+07, + "time_unit": "ns", + "lhs_rows": 6.5848000000000000e+04, + "model_cost": 2.6009960000000000e+07, + "output_rows": 6.5848000000000000e+04, + "plan_cost": 3.9179560000000000e+07, + "rhs_rows": 6.5848000000000000e+04 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_stddev", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.9385080123668956e+05, + "cpu_time": 9.5720236096242710e+05, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_cv", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.8921409716738893e-02, + "cpu_time": 6.6740165278505556e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 34, + "real_time": 2.0726778235364370e+07, + "cpu_time": 2.0689765823529255e+07, + "time_unit": "ns", + "lhs_rows": 6.1000000000000000e+02, + "model_cost": 2.6047000000000000e+07, + "output_rows": 6.1000000000000000e+02, + "plan_cost": 2.6169000000000000e+07, + "rhs_rows": 6.1000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 34, + "real_time": 2.0663114088272456e+07, + "cpu_time": 2.0627233588235203e+07, + "time_unit": "ns", + "lhs_rows": 6.1000000000000000e+02, + "model_cost": 2.6047000000000000e+07, + "output_rows": 6.1000000000000000e+02, + "plan_cost": 2.6169000000000000e+07, + "rhs_rows": 6.1000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 34, + "real_time": 2.0601398117672514e+07, + "cpu_time": 2.0567957235294312e+07, + "time_unit": "ns", + "lhs_rows": 6.1000000000000000e+02, + "model_cost": 2.6047000000000000e+07, + "output_rows": 6.1000000000000000e+02, + "plan_cost": 2.6169000000000000e+07, + "rhs_rows": 6.1000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_mean", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0663763480436448e+07, + "cpu_time": 2.0628318882352922e+07, + "time_unit": "ns", + "lhs_rows": 6.1000000000000000e+02, + "model_cost": 2.6047000000000000e+07, + "output_rows": 6.1000000000000000e+02, + "plan_cost": 2.6169000000000000e+07, + "rhs_rows": 6.1000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_median", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0663114088272456e+07, + "cpu_time": 2.0627233588235203e+07, + "time_unit": "ns", + "lhs_rows": 6.1000000000000000e+02, + "model_cost": 2.6047000000000000e+07, + "output_rows": 6.1000000000000000e+02, + "plan_cost": 2.6169000000000000e+07, + "rhs_rows": 6.1000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_stddev", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.2692581385378056e+04, + "cpu_time": 6.0911546028532845e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_cv", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.0339382003057029e-03, + "cpu_time": 2.9528119269399767e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 41, + "real_time": 1.6635666585299497e+07, + "cpu_time": 1.6552195024390273e+07, + "time_unit": "ns", + "lhs_rows": 5.0000000000000000e+02, + "model_cost": 2.6000000000000000e+07, + "output_rows": 2.5000000000000000e+05, + "plan_cost": 2.6100000000000000e+07, + "rhs_rows": 5.0000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 41, + "real_time": 1.6714775658595210e+07, + "cpu_time": 1.6627896902439220e+07, + "time_unit": "ns", + "lhs_rows": 5.0000000000000000e+02, + "model_cost": 2.6000000000000000e+07, + "output_rows": 2.5000000000000000e+05, + "plan_cost": 2.6100000000000000e+07, + "rhs_rows": 5.0000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 41, + "real_time": 1.6558810463388132e+07, + "cpu_time": 1.6473782512195306e+07, + "time_unit": "ns", + "lhs_rows": 5.0000000000000000e+02, + "model_cost": 2.6000000000000000e+07, + "output_rows": 2.5000000000000000e+05, + "plan_cost": 2.6100000000000000e+07, + "rhs_rows": 5.0000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_mean", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6636417569094280e+07, + "cpu_time": 1.6551291479674930e+07, + "time_unit": "ns", + "lhs_rows": 5.0000000000000000e+02, + "model_cost": 2.6000000000000000e+07, + "output_rows": 2.5000000000000000e+05, + "plan_cost": 2.6100000000000000e+07, + "rhs_rows": 5.0000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_median", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6635666585299497e+07, + "cpu_time": 1.6552195024390271e+07, + "time_unit": "ns", + "lhs_rows": 5.0000000000000000e+02, + "model_cost": 2.6000000000000000e+07, + "output_rows": 2.5000000000000000e+05, + "plan_cost": 2.6100000000000000e+07, + "rhs_rows": 5.0000000000000000e+02 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_stddev", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.7985309587918309e+04, + "cpu_time": 7.7061168008186316e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_cv", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6876263633098981e-03, + "cpu_time": 4.6559006046638617e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + } + ] +} diff --git a/research/benchmark-results/operator-cost.json b/research/benchmark-results/operator-cost.json new file mode 100644 index 0000000..f574feb --- /dev/null +++ b/research/benchmark-results/operator-cost.json @@ -0,0 +1,8495 @@ +{ + "context": { + "date": "2026-06-14T20:45:51+03:00", + "host_name": "nixos", + "executable": "./build-release/bin/benchmarks", + "num_cpus": 8, + "mhz_per_cpu": 4200, + "cpu_scaling_enabled": true, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 49152, + "num_sharing": 2 + }, + { + "type": "Instruction", + "level": 1, + "size": 32768, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 2, + "size": 1310720, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 3, + "size": 8388608, + "num_sharing": 8 + } + ], + "load_avg": [1.35254,1.46924,1.84277], + "library_version": "v1.9.0", + "library_build_type": "release", + "json_schema_version": 1 + }, + "benchmarks": [ + { + "name": "OperatorCost/SeqScan/1024/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 18183, + "real_time": 3.9952357201905637e+04, + "cpu_time": 3.9780922180058289e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.0240000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/1024/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 18183, + "real_time": 3.9745107738011175e+04, + "cpu_time": 3.9613663476874004e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.0240000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/1024/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 18183, + "real_time": 3.9485971126900746e+04, + "cpu_time": 3.9359944178628393e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.0240000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/1024/real_time_mean", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.9727812022272519e+04, + "cpu_time": 3.9584843278520224e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.0240000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/1024/real_time_median", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.9745107738011175e+04, + "cpu_time": 3.9613663476873997e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.0240000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/1024/real_time_stddev", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3367359516357740e+02, + "cpu_time": 2.1196361078531322e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/1024/real_time_cv", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.8818641971164548e-03, + "cpu_time": 5.3546659082096262e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/2048/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 9450, + "real_time": 7.0023080740597041e+04, + "cpu_time": 6.9646964021164022e+04, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.0480000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/2048/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 9450, + "real_time": 6.7391017036960286e+04, + "cpu_time": 6.7122223174603205e+04, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.0480000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/2048/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 9450, + "real_time": 6.7115533862431737e+04, + "cpu_time": 6.6909787513227493e+04, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.0480000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/2048/real_time_mean", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.8176543879996345e+04, + "cpu_time": 6.7892991569664897e+04, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.0480000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/2048/real_time_median", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.7391017036960300e+04, + "cpu_time": 6.7122223174603205e+04, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.0480000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/2048/real_time_stddev", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6050690103636371e+03, + "cpu_time": 1.5226939114672762e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/2048/real_time_cv", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.3542833341462174e-02, + "cpu_time": 2.2427851185565187e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/4096/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 4351, + "real_time": 1.5550668673872287e+05, + "cpu_time": 1.5401735738910592e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/4096/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 4351, + "real_time": 1.6562274557530464e+05, + "cpu_time": 1.6383219903470456e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/4096/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 4351, + "real_time": 1.6600103562343947e+05, + "cpu_time": 1.6446725580326372e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/4096/real_time_mean", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6237682264582231e+05, + "cpu_time": 1.6077227074235803e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/4096/real_time_median", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6562274557530464e+05, + "cpu_time": 1.6383219903470456e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/4096/real_time_stddev", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9527179822873295e+03, + "cpu_time": 5.8585377938772517e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/4096/real_time_cv", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.6659899395072214e-02, + "cpu_time": 3.6439976662802250e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/8192/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1899, + "real_time": 3.5926793259711046e+05, + "cpu_time": 3.5606712954186404e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/8192/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1899, + "real_time": 3.6069299736609607e+05, + "cpu_time": 3.5788115007898922e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/8192/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1899, + "real_time": 3.6195649341763486e+05, + "cpu_time": 3.5909368193786184e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/8192/real_time_mean", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6063914112694707e+05, + "cpu_time": 3.5768065385290497e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/8192/real_time_median", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6069299736609607e+05, + "cpu_time": 3.5788115007898927e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/SeqScan/8192/real_time_stddev", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3450892878593183e+03, + "cpu_time": 1.5232051417843304e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/8192/real_time_cv", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.7297373869516824e-03, + "cpu_time": 4.2585617236395000e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/16384/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 821, + "real_time": 8.2887772472626925e+05, + "cpu_time": 8.2099631912301993e+05, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.6384000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/16384/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 821, + "real_time": 7.8550826065964322e+05, + "cpu_time": 7.7955554445797752e+05, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.6384000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/16384/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 821, + "real_time": 7.6124248721138551e+05, + "cpu_time": 7.5523271254567569e+05, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.6384000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/16384/real_time_mean", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.9187615753243258e+05, + "cpu_time": 7.8526152537555760e+05, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.6384000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/16384/real_time_median", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.8550826065964310e+05, + "cpu_time": 7.7955554445797752e+05, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.6384000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/16384/real_time_stddev", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.4264324323715009e+04, + "cpu_time": 3.3251039851237554e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/16384/real_time_cv", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.3269801720620255e-02, + "cpu_time": 4.2343905535592079e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/32768/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 345, + "real_time": 1.8477772260877723e+06, + "cpu_time": 1.8285485449275328e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.2768000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/32768/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 345, + "real_time": 1.8813254608624224e+06, + "cpu_time": 1.8631300724637695e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.2768000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/32768/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 345, + "real_time": 1.8596911652201780e+06, + "cpu_time": 1.8389719797101459e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.2768000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/32768/real_time_mean", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8629312840567909e+06, + "cpu_time": 1.8435501990338161e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.2768000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/32768/real_time_median", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8596911652201780e+06, + "cpu_time": 1.8389719797101461e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.2768000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/32768/real_time_stddev", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.7007198231345978e+04, + "cpu_time": 1.7739520309685988e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/32768/real_time_cv", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 9.1292676100808452e-03, + "cpu_time": 9.6224774996542389e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/65536/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 134, + "real_time": 4.6026329029917913e+06, + "cpu_time": 4.5590463507462721e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/65536/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 134, + "real_time": 4.6028150373148043e+06, + "cpu_time": 4.5582589179104539e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/65536/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 134, + "real_time": 4.5782678208701350e+06, + "cpu_time": 4.5370564477611985e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/65536/real_time_mean", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.5945719203922432e+06, + "cpu_time": 4.5514539054726409e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/65536/real_time_median", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.6026329029917913e+06, + "cpu_time": 4.5582589179104539e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/SeqScan/65536/real_time_stddev", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4120058043519828e+04, + "cpu_time": 1.2474778717056504e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/65536/real_time_cv", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.0732042697710965e-03, + "cpu_time": 2.7408338030309189e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/131072/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 66, + "real_time": 1.0166986939414831e+07, + "cpu_time": 1.0076584590909090e+07, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.3107200000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/131072/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 66, + "real_time": 1.0271312575710136e+07, + "cpu_time": 1.0181786378787894e+07, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.3107200000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/131072/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 66, + "real_time": 9.7216206969747115e+06, + "cpu_time": 9.6414619696969911e+06, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.3107200000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/131072/real_time_mean", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0053306737366559e+07, + "cpu_time": 9.9666109797979910e+06, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.3107200000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/131072/real_time_median", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0166986939414831e+07, + "cpu_time": 1.0076584590909090e+07, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.3107200000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/131072/real_time_stddev", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.9194636774624448e+05, + "cpu_time": 2.8645813502233382e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/131072/real_time_cv", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.9039834889462369e-02, + "cpu_time": 2.8741779487829466e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/262144/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 34, + "real_time": 2.0544696499949653e+07, + "cpu_time": 2.0386411441176478e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.6214400000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/262144/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 34, + "real_time": 2.0518101764644276e+07, + "cpu_time": 2.0362821470588300e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.6214400000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/262144/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 34, + "real_time": 2.0957574529401269e+07, + "cpu_time": 2.0790742676470667e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.6214400000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/262144/real_time_mean", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0673457597998399e+07, + "cpu_time": 2.0513325196078483e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.6214400000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/262144/real_time_median", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0544696499949653e+07, + "cpu_time": 2.0386411441176478e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.6214400000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/SeqScan/262144/real_time_stddev", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4641153183035008e+05, + "cpu_time": 2.4053994574412901e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/SeqScan/262144/real_time_cv", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "OperatorCost/SeqScan/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1919222058636561e-02, + "cpu_time": 1.1726033855794031e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/1024/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 9603, + "real_time": 6.8690185462695677e+04, + "cpu_time": 6.8472288347391412e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 2.0480000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Filter/1024/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 9603, + "real_time": 7.0325538477518334e+04, + "cpu_time": 7.0109755597209398e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 2.0480000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Filter/1024/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 9603, + "real_time": 7.3417058002968915e+04, + "cpu_time": 7.3112707174841315e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 2.0480000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Filter/1024/real_time_mean", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.0810927314394299e+04, + "cpu_time": 7.0564917039814041e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 2.0480000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Filter/1024/real_time_median", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.0325538477518334e+04, + "cpu_time": 7.0109755597209383e+04, + "time_unit": "ns", + "model_cost": 1.0240000000000000e+05, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 2.0480000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Filter/1024/real_time_stddev", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4005275972631484e+03, + "cpu_time": 2.3534550511244838e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/1024/real_time_cv", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.3900524796194473e-02, + "cpu_time": 3.3351630666505580e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/2048/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 5115, + "real_time": 1.3426594330379233e+05, + "cpu_time": 1.3370913372434006e+05, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Filter/2048/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 5115, + "real_time": 1.3586024965754987e+05, + "cpu_time": 1.3514681153470199e+05, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Filter/2048/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 5115, + "real_time": 1.3516407448679840e+05, + "cpu_time": 1.3457058123167208e+05, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Filter/2048/real_time_mean", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3509675581604688e+05, + "cpu_time": 1.3447550883023805e+05, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Filter/2048/real_time_median", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3516407448679840e+05, + "cpu_time": 1.3457058123167208e+05, + "time_unit": "ns", + "model_cost": 2.0480000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 4.0960000000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Filter/2048/real_time_stddev", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.9928220296819643e+02, + "cpu_time": 7.2353883291625959e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/2048/real_time_cv", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.9163685918300719e-03, + "cpu_time": 5.3804506055422727e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/4096/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2665, + "real_time": 2.6182521350818727e+05, + "cpu_time": 2.6086632720450283e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Filter/4096/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2665, + "real_time": 2.6336371181894216e+05, + "cpu_time": 2.6220672307692328e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Filter/4096/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2665, + "real_time": 2.6097398686733170e+05, + "cpu_time": 2.5940393733583513e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Filter/4096/real_time_mean", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.6205430406482038e+05, + "cpu_time": 2.6082566253908709e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Filter/4096/real_time_median", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.6182521350818730e+05, + "cpu_time": 2.6086632720450286e+05, + "time_unit": "ns", + "model_cost": 4.0960000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 8.1920000000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Filter/4096/real_time_stddev", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2112217792196116e+03, + "cpu_time": 1.4018352930624114e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/4096/real_time_cv", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6220258947550436e-03, + "cpu_time": 5.3746064686113239e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/8192/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1339, + "real_time": 5.0586725392064487e+05, + "cpu_time": 5.0367560418222670e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 1.6384000000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Filter/8192/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1339, + "real_time": 5.2297352800632012e+05, + "cpu_time": 5.2033982001493528e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 1.6384000000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Filter/8192/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1339, + "real_time": 4.9771732935041009e+05, + "cpu_time": 4.9551648693054519e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 1.6384000000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Filter/8192/real_time_mean", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.0885270375912497e+05, + "cpu_time": 5.0651063704256900e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 1.6384000000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Filter/8192/real_time_median", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.0586725392064481e+05, + "cpu_time": 5.0367560418222664e+05, + "time_unit": "ns", + "model_cost": 8.1920000000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 1.6384000000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Filter/8192/real_time_stddev", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2890057241544024e+04, + "cpu_time": 1.2652174708094788e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/8192/real_time_cv", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.5331608039653404e-02, + "cpu_time": 2.4979089840972978e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/16384/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 679, + "real_time": 1.0050831708443469e+06, + "cpu_time": 1.0005117893961695e+06, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 3.2768000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Filter/16384/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 679, + "real_time": 1.0103071664176423e+06, + "cpu_time": 1.0057585198821783e+06, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 3.2768000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Filter/16384/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 679, + "real_time": 1.0162211723091761e+06, + "cpu_time": 1.0113145257731929e+06, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 3.2768000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Filter/16384/real_time_mean", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0105371698570551e+06, + "cpu_time": 1.0058616116838468e+06, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 3.2768000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Filter/16384/real_time_median", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0103071664176424e+06, + "cpu_time": 1.0057585198821783e+06, + "time_unit": "ns", + "model_cost": 1.6384000000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 3.2768000000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Filter/16384/real_time_stddev", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.5725618295511194e+03, + "cpu_time": 5.4021060011550790e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/16384/real_time_cv", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.5144550797071451e-03, + "cpu_time": 5.3706254800914097e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/32768/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 321, + "real_time": 2.0942968909604754e+06, + "cpu_time": 2.0836751277258459e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Filter/32768/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 321, + "real_time": 2.1086145015517753e+06, + "cpu_time": 2.0970285794392612e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Filter/32768/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 321, + "real_time": 2.0808537071652352e+06, + "cpu_time": 2.0711384267912786e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Filter/32768/real_time_mean", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0945883665591618e+06, + "cpu_time": 2.0839473779854618e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Filter/32768/real_time_median", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0942968909604752e+06, + "cpu_time": 2.0836751277258454e+06, + "time_unit": "ns", + "model_cost": 3.2768000000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 6.5536000000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Filter/32768/real_time_stddev", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3882692273514394e+04, + "cpu_time": 1.2947223300257732e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/32768/real_time_cv", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.6278856958992263e-03, + "cpu_time": 6.2128360039367826e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/65536/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 158, + "real_time": 4.3984668860687204e+06, + "cpu_time": 4.3722477658227980e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.3107200000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Filter/65536/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 158, + "real_time": 4.4091168734275578e+06, + "cpu_time": 4.3848112658228232e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.3107200000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Filter/65536/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 158, + "real_time": 4.3909908037929637e+06, + "cpu_time": 4.3653056582278619e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.3107200000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Filter/65536/real_time_mean", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3995248544297470e+06, + "cpu_time": 4.3741215632911613e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.3107200000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Filter/65536/real_time_median", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3984668860687213e+06, + "cpu_time": 4.3722477658227989e+06, + "time_unit": "ns", + "model_cost": 6.5536000000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.3107200000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Filter/65536/real_time_stddev", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.1092300934433351e+03, + "cpu_time": 9.8868862454544724e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/65536/real_time_cv", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.0705031554195063e-03, + "cpu_time": 2.2603135515089382e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/131072/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 76, + "real_time": 9.0810142236908805e+06, + "cpu_time": 9.0268509736842681e+06, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 2.6214400000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Filter/131072/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 76, + "real_time": 9.2972678421095852e+06, + "cpu_time": 9.2514884342104495e+06, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 2.6214400000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Filter/131072/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 76, + "real_time": 8.8838116315807719e+06, + "cpu_time": 8.8443053289473802e+06, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 2.6214400000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Filter/131072/real_time_mean", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.0873645657937434e+06, + "cpu_time": 9.0408815789473653e+06, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 2.6214400000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Filter/131072/real_time_median", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.0810142236908786e+06, + "cpu_time": 9.0268509736842681e+06, + "time_unit": "ns", + "model_cost": 1.3107200000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 2.6214400000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Filter/131072/real_time_stddev", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0680124428986004e+05, + "cpu_time": 2.0395382741191427e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/131072/real_time_cv", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.2757009779082942e-02, + "cpu_time": 2.2559064138926679e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/262144/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 38, + "real_time": 1.8975293473646928e+07, + "cpu_time": 1.8872990342105303e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 5.2428800000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Filter/262144/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 38, + "real_time": 1.8865303684211347e+07, + "cpu_time": 1.8754480631579034e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 5.2428800000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Filter/262144/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 38, + "real_time": 1.9274510078934040e+07, + "cpu_time": 1.9165611342105132e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 5.2428800000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Filter/262144/real_time_mean", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9038369078930769e+07, + "cpu_time": 1.8931027438596491e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 5.2428800000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Filter/262144/real_time_median", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8975293473646928e+07, + "cpu_time": 1.8872990342105303e+07, + "time_unit": "ns", + "model_cost": 2.6214400000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 5.2428800000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Filter/262144/real_time_stddev", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.1176960914370150e+05, + "cpu_time": 2.1162075444352781e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Filter/262144/real_time_cv", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Filter/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1123306217340907e-02, + "cpu_time": 1.1178513957043685e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/1024/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 7156, + "real_time": 9.8666793180478402e+04, + "cpu_time": 9.8323235885970571e+04, + "time_unit": "ns", + "model_cost": 2.2528000000000000e+04, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.2492800000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Projection/1024/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 7156, + "real_time": 9.7837508943523499e+04, + "cpu_time": 9.7484671883733739e+04, + "time_unit": "ns", + "model_cost": 2.2528000000000000e+04, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.2492800000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Projection/1024/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 7156, + "real_time": 9.8023990357427319e+04, + "cpu_time": 9.7688416992733124e+04, + "time_unit": "ns", + "model_cost": 2.2528000000000000e+04, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.2492800000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Projection/1024/real_time_mean", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.8176097493809764e+04, + "cpu_time": 9.7832108254145787e+04, + "time_unit": "ns", + "model_cost": 2.2528000000000000e+04, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.2492800000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Projection/1024/real_time_median", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.8023990357427334e+04, + "cpu_time": 9.7688416992733139e+04, + "time_unit": "ns", + "model_cost": 2.2528000000000000e+04, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.2492800000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Projection/1024/real_time_stddev", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3506381384660466e+02, + "cpu_time": 4.3735886914609813e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/1024/real_time_cv", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.4314637162475978e-03, + "cpu_time": 4.4705043870662394e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/2048/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 3730, + "real_time": 1.8324566300343236e+05, + "cpu_time": 1.8256576621983893e+05, + "time_unit": "ns", + "model_cost": 4.5056000000000000e+04, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.4985600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Projection/2048/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 3730, + "real_time": 1.8439168391404231e+05, + "cpu_time": 1.8382601554959849e+05, + "time_unit": "ns", + "model_cost": 4.5056000000000000e+04, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.4985600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Projection/2048/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 3730, + "real_time": 1.8079054289509606e+05, + "cpu_time": 1.8023151447721187e+05, + "time_unit": "ns", + "model_cost": 4.5056000000000000e+04, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.4985600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Projection/2048/real_time_mean", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8280929660419023e+05, + "cpu_time": 1.8220776541554971e+05, + "time_unit": "ns", + "model_cost": 4.5056000000000000e+04, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.4985600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Projection/2048/real_time_median", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8324566300343233e+05, + "cpu_time": 1.8256576621983890e+05, + "time_unit": "ns", + "model_cost": 4.5056000000000000e+04, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.4985600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Projection/2048/real_time_stddev", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8398005015174278e+03, + "cpu_time": 1.8237962939309716e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/2048/real_time_cv", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0064042341899462e-02, + "cpu_time": 1.0009432308066314e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/4096/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1957, + "real_time": 3.5603891773163405e+05, + "cpu_time": 3.5477958610117622e+05, + "time_unit": "ns", + "model_cost": 9.0112000000000000e+04, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.9971200000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Projection/4096/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1957, + "real_time": 3.5593747572827508e+05, + "cpu_time": 3.5454490802248259e+05, + "time_unit": "ns", + "model_cost": 9.0112000000000000e+04, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.9971200000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Projection/4096/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1957, + "real_time": 3.5629890189032536e+05, + "cpu_time": 3.5491928615227150e+05, + "time_unit": "ns", + "model_cost": 9.0112000000000000e+04, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.9971200000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Projection/4096/real_time_mean", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5609176511674485e+05, + "cpu_time": 3.5474792675864347e+05, + "time_unit": "ns", + "model_cost": 9.0112000000000000e+04, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.9971200000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Projection/4096/real_time_median", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5603891773163411e+05, + "cpu_time": 3.5477958610117628e+05, + "time_unit": "ns", + "model_cost": 9.0112000000000000e+04, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.9971200000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Projection/4096/real_time_stddev", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8641848696415431e+02, + "cpu_time": 1.8918636712796342e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/4096/real_time_cv", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.2351249095310289e-04, + "cpu_time": 5.3329802053128943e-04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/8192/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 980, + "real_time": 7.0419572448929329e+05, + "cpu_time": 7.0125159489795496e+05, + "time_unit": "ns", + "model_cost": 1.8022400000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 9.9942400000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Projection/8192/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 980, + "real_time": 7.1417365816531098e+05, + "cpu_time": 7.1118539693877380e+05, + "time_unit": "ns", + "model_cost": 1.8022400000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 9.9942400000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Projection/8192/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 980, + "real_time": 7.1909350918591663e+05, + "cpu_time": 7.1576403673469892e+05, + "time_unit": "ns", + "model_cost": 1.8022400000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 9.9942400000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Projection/8192/real_time_mean", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.1248763061350689e+05, + "cpu_time": 7.0940034285714244e+05, + "time_unit": "ns", + "model_cost": 1.8022400000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 9.9942400000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Projection/8192/real_time_median", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.1417365816531098e+05, + "cpu_time": 7.1118539693877380e+05, + "time_unit": "ns", + "model_cost": 1.8022400000000000e+05, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 9.9942400000000000e+05, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Projection/8192/real_time_stddev", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.5906530612250826e+03, + "cpu_time": 7.4190670284128673e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/8192/real_time_cv", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0653733110691260e-02, + "cpu_time": 1.0458223065599650e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/16384/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 369, + "real_time": 1.5983485501314146e+06, + "cpu_time": 1.5684159539295367e+06, + "time_unit": "ns", + "model_cost": 3.6044800000000000e+05, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.9988480000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Projection/16384/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 369, + "real_time": 1.5161208970149471e+06, + "cpu_time": 1.5049820189701822e+06, + "time_unit": "ns", + "model_cost": 3.6044800000000000e+05, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.9988480000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Projection/16384/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 369, + "real_time": 1.4712186260095250e+06, + "cpu_time": 1.4641545392953865e+06, + "time_unit": "ns", + "model_cost": 3.6044800000000000e+05, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.9988480000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Projection/16384/real_time_mean", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5285626910519621e+06, + "cpu_time": 1.5125175040650351e+06, + "time_unit": "ns", + "model_cost": 3.6044800000000000e+05, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.9988480000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Projection/16384/real_time_median", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5161208970149474e+06, + "cpu_time": 1.5049820189701824e+06, + "time_unit": "ns", + "model_cost": 3.6044800000000000e+05, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.9988480000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Projection/16384/real_time_stddev", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.4471723111402913e+04, + "cpu_time": 5.2537589372649505e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/16384/real_time_cv", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.2178003878292514e-02, + "cpu_time": 3.4735194291272468e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/32768/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 235, + "real_time": 2.9960241957482179e+06, + "cpu_time": 2.9818136382978819e+06, + "time_unit": "ns", + "model_cost": 7.2089600000000000e+05, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.9976960000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Projection/32768/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 235, + "real_time": 3.0641198255173722e+06, + "cpu_time": 3.0462217361701960e+06, + "time_unit": "ns", + "model_cost": 7.2089600000000000e+05, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.9976960000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Projection/32768/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 235, + "real_time": 3.1016805148999295e+06, + "cpu_time": 3.0833265872340365e+06, + "time_unit": "ns", + "model_cost": 7.2089600000000000e+05, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.9976960000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Projection/32768/real_time_mean", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0539415120551731e+06, + "cpu_time": 3.0371206539007048e+06, + "time_unit": "ns", + "model_cost": 7.2089600000000000e+05, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.9976960000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Projection/32768/real_time_median", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0641198255173718e+06, + "cpu_time": 3.0462217361701964e+06, + "time_unit": "ns", + "model_cost": 7.2089600000000000e+05, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 3.9976960000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Projection/32768/real_time_stddev", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.3558500659321289e+04, + "cpu_time": 5.1364793139702873e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/32768/real_time_cv", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.7537500455691014e-02, + "cpu_time": 1.6912332104334696e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/65536/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 106, + "real_time": 6.3347297264216365e+06, + "cpu_time": 6.2942219433962200e+06, + "time_unit": "ns", + "model_cost": 1.4417920000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 7.9953920000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Projection/65536/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 106, + "real_time": 6.3714250000147047e+06, + "cpu_time": 6.3321674433962852e+06, + "time_unit": "ns", + "model_cost": 1.4417920000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 7.9953920000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Projection/65536/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 106, + "real_time": 6.2802631320733307e+06, + "cpu_time": 6.2449743396226317e+06, + "time_unit": "ns", + "model_cost": 1.4417920000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 7.9953920000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Projection/65536/real_time_mean", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3288059528365554e+06, + "cpu_time": 6.2904545754717113e+06, + "time_unit": "ns", + "model_cost": 1.4417920000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 7.9953920000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Projection/65536/real_time_median", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3347297264216356e+06, + "cpu_time": 6.2942219433962209e+06, + "time_unit": "ns", + "model_cost": 1.4417920000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 7.9953920000000000e+06, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Projection/65536/real_time_stddev", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.5868724222071141e+04, + "cpu_time": 4.3718464431275053e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/65536/real_time_cv", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 7.2476110918700048e-03, + "cpu_time": 6.9499690215943828e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/131072/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 51, + "real_time": 1.3400773176459447e+07, + "cpu_time": 1.3305886568627372e+07, + "time_unit": "ns", + "model_cost": 2.8835840000000000e+06, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.5990784000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Projection/131072/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 51, + "real_time": 1.3479431196031453e+07, + "cpu_time": 1.3354870313725533e+07, + "time_unit": "ns", + "model_cost": 2.8835840000000000e+06, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.5990784000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Projection/131072/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 51, + "real_time": 1.2959331568637544e+07, + "cpu_time": 1.2850603274509743e+07, + "time_unit": "ns", + "model_cost": 2.8835840000000000e+06, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.5990784000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Projection/131072/real_time_mean", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3279845313709481e+07, + "cpu_time": 1.3170453385620883e+07, + "time_unit": "ns", + "model_cost": 2.8835840000000000e+06, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.5990784000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Projection/131072/real_time_median", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3400773176459445e+07, + "cpu_time": 1.3305886568627371e+07, + "time_unit": "ns", + "model_cost": 2.8835840000000000e+06, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 1.5990784000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Projection/131072/real_time_stddev", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.8034544154620782e+05, + "cpu_time": 2.7807898519011628e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/131072/real_time_cv", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.1110595411589057e-02, + "cpu_time": 2.1113850605455609e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/262144/real_time", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 22, + "real_time": 3.1345011454587534e+07, + "cpu_time": 3.1017350727272812e+07, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 3.1981568000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Projection/262144/real_time", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 22, + "real_time": 3.1134449136350833e+07, + "cpu_time": 3.0907373090909053e+07, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 3.1981568000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Projection/262144/real_time", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 22, + "real_time": 3.3140141681790486e+07, + "cpu_time": 3.2887491727272466e+07, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 3.1981568000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Projection/262144/real_time_mean", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1873200757576283e+07, + "cpu_time": 3.1604071848484769e+07, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 3.1981568000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Projection/262144/real_time_median", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1345011454587534e+07, + "cpu_time": 3.1017350727272809e+07, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 3.1981568000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Projection/262144/real_time_stddev", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1022425329989677e+06, + "cpu_time": 1.1128336394430066e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Projection/262144/real_time_cv", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Projection/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.4582109948181589e-02, + "cpu_time": 3.5211717172968025e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/1024/real_time", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 5641, + "real_time": 1.2108951356129415e+05, + "cpu_time": 1.2058807924126850e+05, + "time_unit": "ns", + "model_cost": 1.2390400000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 2.2630400000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Sort/1024/real_time", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 5641, + "real_time": 1.2039773586234968e+05, + "cpu_time": 1.1997666158482579e+05, + "time_unit": "ns", + "model_cost": 1.2390400000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 2.2630400000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Sort/1024/real_time", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 5641, + "real_time": 1.2066585374989979e+05, + "cpu_time": 1.1989094788158065e+05, + "time_unit": "ns", + "model_cost": 1.2390400000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 2.2630400000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Sort/1024/real_time_mean", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2071770105784787e+05, + "cpu_time": 1.2015189623589163e+05, + "time_unit": "ns", + "model_cost": 1.2390400000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 2.2630400000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Sort/1024/real_time_median", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2066585374989978e+05, + "cpu_time": 1.1997666158482579e+05, + "time_unit": "ns", + "model_cost": 1.2390400000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 2.2630400000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Sort/1024/real_time_stddev", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.4879106022315710e+02, + "cpu_time": 3.8016893662041053e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/1024/real_time_cv", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.8893116516194800e-03, + "cpu_time": 3.1640693865873999e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/2048/real_time", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2430, + "real_time": 2.7988660987658205e+05, + "cpu_time": 2.7869887119341525e+05, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 4.7513600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Sort/2048/real_time", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2430, + "real_time": 2.7806842551432998e+05, + "cpu_time": 2.7725973539094441e+05, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 4.7513600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Sort/2048/real_time", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2430, + "real_time": 2.8030595761418890e+05, + "cpu_time": 2.7880494156378834e+05, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 4.7513600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Sort/2048/real_time_mean", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7942033100170025e+05, + "cpu_time": 2.7825451604938268e+05, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 4.7513600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Sort/2048/real_time_median", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7988660987658199e+05, + "cpu_time": 2.7869887119341525e+05, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 4.7513600000000000e+05, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Sort/2048/real_time_stddev", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1894113949218515e+03, + "cpu_time": 8.6313622891507202e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/2048/real_time_cv", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.2567102782317366e-03, + "cpu_time": 3.1019666497052959e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/4096/real_time", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1107, + "real_time": 6.2356169738173392e+05, + "cpu_time": 6.2107064498644660e+05, + "time_unit": "ns", + "model_cost": 5.8572800000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 9.9532800000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Sort/4096/real_time", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1107, + "real_time": 6.2127104155452061e+05, + "cpu_time": 6.1878930352304166e+05, + "time_unit": "ns", + "model_cost": 5.8572800000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 9.9532800000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Sort/4096/real_time", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1107, + "real_time": 6.1642583739809087e+05, + "cpu_time": 6.1392024028907670e+05, + "time_unit": "ns", + "model_cost": 5.8572800000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 9.9532800000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Sort/4096/real_time_mean", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.2041952544478176e+05, + "cpu_time": 6.1792672959952161e+05, + "time_unit": "ns", + "model_cost": 5.8572800000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 9.9532800000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Sort/4096/real_time_median", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.2127104155452061e+05, + "cpu_time": 6.1878930352304166e+05, + "time_unit": "ns", + "model_cost": 5.8572800000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 9.9532800000000000e+05, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Sort/4096/real_time_stddev", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6433410752253890e+03, + "cpu_time": 3.6524097749773009e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/4096/real_time_cv", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.8723830018300284e-03, + "cpu_time": 5.9107489610368989e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/8192/real_time", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 489, + "real_time": 1.3808442924355236e+06, + "cpu_time": 1.3746132965235079e+06, + "time_unit": "ns", + "model_cost": 1.2615680000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 2.0807680000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Sort/8192/real_time", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 489, + "real_time": 1.3835909897729955e+06, + "cpu_time": 1.3779464539877325e+06, + "time_unit": "ns", + "model_cost": 1.2615680000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 2.0807680000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Sort/8192/real_time", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 489, + "real_time": 1.4144051472385863e+06, + "cpu_time": 1.4078482556237341e+06, + "time_unit": "ns", + "model_cost": 1.2615680000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 2.0807680000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Sort/8192/real_time_mean", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3929468098157020e+06, + "cpu_time": 1.3868026687116581e+06, + "time_unit": "ns", + "model_cost": 1.2615680000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 2.0807680000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Sort/8192/real_time_median", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.3835909897729957e+06, + "cpu_time": 1.3779464539877325e+06, + "time_unit": "ns", + "model_cost": 1.2615680000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 2.0807680000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Sort/8192/real_time_stddev", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8634142595817015e+04, + "cpu_time": 1.8302049913216953e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/8192/real_time_cv", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.3377497593237220e-02, + "cpu_time": 1.3197299317443331e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/16384/real_time", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 220, + "real_time": 3.2022223227282995e+06, + "cpu_time": 3.1863563272727388e+06, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 4.3417600000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Sort/16384/real_time", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 220, + "real_time": 3.3020563000139766e+06, + "cpu_time": 3.2827890636363449e+06, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 4.3417600000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Sort/16384/real_time", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 220, + "real_time": 3.1589662227402600e+06, + "cpu_time": 3.1439646545454604e+06, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 4.3417600000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Sort/16384/real_time_mean", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.2210816151608452e+06, + "cpu_time": 3.2043700151515142e+06, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 4.3417600000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Sort/16384/real_time_median", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.2022223227282991e+06, + "cpu_time": 3.1863563272727388e+06, + "time_unit": "ns", + "model_cost": 2.7033600000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 4.3417600000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Sort/16384/real_time_stddev", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.3385606469129867e+04, + "cpu_time": 7.1143684562345370e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/16384/real_time_cv", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.2782908115001412e-02, + "cpu_time": 2.2202081602920452e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/32768/real_time", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 87, + "real_time": 7.9430395402094536e+06, + "cpu_time": 7.8728290574711468e+06, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 9.0439680000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Sort/32768/real_time", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 87, + "real_time": 8.1718665632220572e+06, + "cpu_time": 8.0973900919541288e+06, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 9.0439680000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Sort/32768/real_time", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 87, + "real_time": 8.0255463793131141e+06, + "cpu_time": 7.9662590574712818e+06, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 9.0439680000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Sort/32768/real_time_mean", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.0468174942482086e+06, + "cpu_time": 7.9788260689655179e+06, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 9.0439680000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Sort/32768/real_time_median", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.0255463793131141e+06, + "cpu_time": 7.9662590574712828e+06, + "time_unit": "ns", + "model_cost": 5.7671680000000000e+06, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 9.0439680000000000e+06, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Sort/32768/real_time_stddev", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1588700040610986e+05, + "cpu_time": 1.1280674574440201e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/32768/real_time_cv", + "family_index": 32, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.4401594231377168e-02, + "cpu_time": 1.4138263545206944e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/65536/real_time", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 33, + "real_time": 2.0550580363664210e+07, + "cpu_time": 2.0332200090909295e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Sort/65536/real_time", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 33, + "real_time": 2.0616162969721094e+07, + "cpu_time": 2.0433771969697054e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Sort/65536/real_time", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 33, + "real_time": 2.0273725696990203e+07, + "cpu_time": 2.0103996454545062e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Sort/65536/real_time_mean", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0480156343458503e+07, + "cpu_time": 2.0289989505050469e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Sort/65536/real_time_median", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0550580363664214e+07, + "cpu_time": 2.0332200090909291e+07, + "time_unit": "ns", + "model_cost": 1.2255232000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 1.8808832000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Sort/65536/real_time_stddev", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8175664611046438e+05, + "cpu_time": 1.6889130459357981e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/65536/real_time_cv", + "family_index": 33, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.8747684862532147e-03, + "cpu_time": 8.3238734328344704e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/131072/real_time", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 10, + "real_time": 5.5530760100009501e+07, + "cpu_time": 5.4966198900000051e+07, + "time_unit": "ns", + "model_cost": 2.5952256000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 3.9059456000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Sort/131072/real_time", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 10, + "real_time": 5.5356484100047968e+07, + "cpu_time": 5.4701726599999748e+07, + "time_unit": "ns", + "model_cost": 2.5952256000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 3.9059456000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Sort/131072/real_time", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 10, + "real_time": 5.5279565599994384e+07, + "cpu_time": 5.4693666000000015e+07, + "time_unit": "ns", + "model_cost": 2.5952256000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 3.9059456000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Sort/131072/real_time_mean", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.5388936600017279e+07, + "cpu_time": 5.4787197166666597e+07, + "time_unit": "ns", + "model_cost": 2.5952256000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 3.9059456000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Sort/131072/real_time_median", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.5356484100047968e+07, + "cpu_time": 5.4701726599999748e+07, + "time_unit": "ns", + "model_cost": 2.5952256000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 3.9059456000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Sort/131072/real_time_stddev", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2870331299452034e+05, + "cpu_time": 1.5507243056115572e+05, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/131072/real_time_cv", + "family_index": 34, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.3236285239402877e-03, + "cpu_time": 2.8304501522392949e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/262144/real_time", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 4, + "real_time": 1.6194054500010678e+08, + "cpu_time": 1.6049572324999774e+08, + "time_unit": "ns", + "model_cost": 5.4788096000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 8.1002496000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Sort/262144/real_time", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 4, + "real_time": 1.6892709750027278e+08, + "cpu_time": 1.6732419474999815e+08, + "time_unit": "ns", + "model_cost": 5.4788096000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 8.1002496000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Sort/262144/real_time", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 4, + "real_time": 1.7597630074942571e+08, + "cpu_time": 1.7445982250000113e+08, + "time_unit": "ns", + "model_cost": 5.4788096000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 8.1002496000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Sort/262144/real_time_mean", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6894798108326843e+08, + "cpu_time": 1.6742658016666567e+08, + "time_unit": "ns", + "model_cost": 5.4788096000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 8.1002496000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Sort/262144/real_time_median", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6892709750027278e+08, + "cpu_time": 1.6732419474999815e+08, + "time_unit": "ns", + "model_cost": 5.4788096000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 8.1002496000000000e+07, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Sort/262144/real_time_stddev", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.0179011788904993e+06, + "cpu_time": 6.9826126232336499e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Sort/262144/real_time_cv", + "family_index": 35, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Sort/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.1538828306161450e-02, + "cpu_time": 4.1705520212398602e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/1024/real_time", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2491, + "real_time": 2.8827810517762712e+05, + "cpu_time": 2.8623571497390448e+05, + "time_unit": "ns", + "model_cost": 5.2224000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.2464000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/1024/real_time", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2491, + "real_time": 2.8888266358885070e+05, + "cpu_time": 2.8733583982335881e+05, + "time_unit": "ns", + "model_cost": 5.2224000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.2464000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/1024/real_time", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2491, + "real_time": 2.8930293054901809e+05, + "cpu_time": 2.8761188036933448e+05, + "time_unit": "ns", + "model_cost": 5.2224000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.2464000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/1024/real_time_mean", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.8882123310516530e+05, + "cpu_time": 2.8706114505553257e+05, + "time_unit": "ns", + "model_cost": 5.2224000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.2464000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/1024/real_time_median", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.8888266358885070e+05, + "cpu_time": 2.8733583982335881e+05, + "time_unit": "ns", + "model_cost": 5.2224000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.2464000000000000e+05, + "rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/1024/real_time_stddev", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.1516700080929650e+02, + "cpu_time": 7.2804581622971875e+02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/1024/real_time_cv", + "family_index": 36, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.7836881148613969e-03, + "cpu_time": 2.5362046684823084e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/2048/real_time", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1163, + "real_time": 5.6094689080133312e+05, + "cpu_time": 5.5830061822872597e+05, + "time_unit": "ns", + "model_cost": 1.0444800000000000e+06, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2492800000000000e+06, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/2048/real_time", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1163, + "real_time": 5.5871517282724660e+05, + "cpu_time": 5.5545842734307877e+05, + "time_unit": "ns", + "model_cost": 1.0444800000000000e+06, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2492800000000000e+06, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/2048/real_time", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1163, + "real_time": 5.6419359415198804e+05, + "cpu_time": 5.6073709802236105e+05, + "time_unit": "ns", + "model_cost": 1.0444800000000000e+06, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2492800000000000e+06, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/2048/real_time_mean", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.6128521926018933e+05, + "cpu_time": 5.5816538119805523e+05, + "time_unit": "ns", + "model_cost": 1.0444800000000000e+06, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2492800000000000e+06, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/2048/real_time_median", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.6094689080133312e+05, + "cpu_time": 5.5830061822872597e+05, + "time_unit": "ns", + "model_cost": 1.0444800000000000e+06, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2492800000000000e+06, + "rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/2048/real_time_stddev", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7548365945152127e+03, + "cpu_time": 2.6419325929955298e+03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/2048/real_time_cv", + "family_index": 37, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.9080868335465311e-03, + "cpu_time": 4.7332433755114698e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/4096/real_time", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 606, + "real_time": 1.1421539504958929e+06, + "cpu_time": 1.1356451089108931e+06, + "time_unit": "ns", + "model_cost": 2.0889600000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4985600000000000e+06, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/4096/real_time", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 606, + "real_time": 1.0979971122093173e+06, + "cpu_time": 1.0926359702970276e+06, + "time_unit": "ns", + "model_cost": 2.0889600000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4985600000000000e+06, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/4096/real_time", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 606, + "real_time": 1.1072508399346927e+06, + "cpu_time": 1.1017405594059452e+06, + "time_unit": "ns", + "model_cost": 2.0889600000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4985600000000000e+06, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/4096/real_time_mean", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1158006342133011e+06, + "cpu_time": 1.1100072128712886e+06, + "time_unit": "ns", + "model_cost": 2.0889600000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4985600000000000e+06, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/4096/real_time_median", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1072508399346930e+06, + "cpu_time": 1.1017405594059455e+06, + "time_unit": "ns", + "model_cost": 2.0889600000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4985600000000000e+06, + "rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/4096/real_time_stddev", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3286923981526983e+04, + "cpu_time": 2.2664943651246300e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/4096/real_time_cv", + "family_index": 38, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.0870147647787909e-02, + "cpu_time": 2.0418735471653574e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/8192/real_time", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 281, + "real_time": 2.4236838825510400e+06, + "cpu_time": 2.4122288220640286e+06, + "time_unit": "ns", + "model_cost": 4.1779200000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.9971200000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/8192/real_time", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 281, + "real_time": 2.5777469145849156e+06, + "cpu_time": 2.5646061530249054e+06, + "time_unit": "ns", + "model_cost": 4.1779200000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.9971200000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/8192/real_time", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 281, + "real_time": 2.4714996156637357e+06, + "cpu_time": 2.4598212135231178e+06, + "time_unit": "ns", + "model_cost": 4.1779200000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.9971200000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/8192/real_time_mean", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4909768042665636e+06, + "cpu_time": 2.4788853962040176e+06, + "time_unit": "ns", + "model_cost": 4.1779200000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.9971200000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/8192/real_time_median", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4714996156637357e+06, + "cpu_time": 2.4598212135231178e+06, + "time_unit": "ns", + "model_cost": 4.1779200000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.9971200000000000e+06, + "rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/Aggregation/8192/real_time_stddev", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.8856674522649773e+04, + "cpu_time": 7.7957007662285701e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/8192/real_time_cv", + "family_index": 39, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.1656928473835437e-02, + "cpu_time": 3.1448411363293891e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/16384/real_time", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 116, + "real_time": 5.5622124051798647e+06, + "cpu_time": 5.5310783706896761e+06, + "time_unit": "ns", + "model_cost": 8.3558400000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.9942400000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/16384/real_time", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 116, + "real_time": 5.4617910948230736e+06, + "cpu_time": 5.4333963189655282e+06, + "time_unit": "ns", + "model_cost": 8.3558400000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.9942400000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/16384/real_time", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 116, + "real_time": 5.5997759999909569e+06, + "cpu_time": 5.5645316206897097e+06, + "time_unit": "ns", + "model_cost": 8.3558400000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.9942400000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/16384/real_time_mean", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.5412598333312990e+06, + "cpu_time": 5.5096687701149704e+06, + "time_unit": "ns", + "model_cost": 8.3558400000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.9942400000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/16384/real_time_median", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.5622124051798629e+06, + "cpu_time": 5.5310783706896761e+06, + "time_unit": "ns", + "model_cost": 8.3558400000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.9942400000000000e+06, + "rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/16384/real_time_stddev", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.1338742726841549e+04, + "cpu_time": 6.8138792821351803e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/16384/real_time_cv", + "family_index": 40, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.2874101715593814e-02, + "cpu_time": 1.2367130523516014e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/32768/real_time", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 42, + "real_time": 1.6470797547559153e+07, + "cpu_time": 1.6335334952380940e+07, + "time_unit": "ns", + "model_cost": 1.6711680000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9988480000000000e+07, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/32768/real_time", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 42, + "real_time": 1.6591870738054803e+07, + "cpu_time": 1.6467307166666482e+07, + "time_unit": "ns", + "model_cost": 1.6711680000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9988480000000000e+07, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/32768/real_time", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 42, + "real_time": 1.6630647428558275e+07, + "cpu_time": 1.6503511190476179e+07, + "time_unit": "ns", + "model_cost": 1.6711680000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9988480000000000e+07, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/32768/real_time_mean", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6564438571390741e+07, + "cpu_time": 1.6435384436507866e+07, + "time_unit": "ns", + "model_cost": 1.6711680000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9988480000000000e+07, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/32768/real_time_median", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6591870738054803e+07, + "cpu_time": 1.6467307166666484e+07, + "time_unit": "ns", + "model_cost": 1.6711680000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9988480000000000e+07, + "rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/32768/real_time_stddev", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3380986680853865e+04, + "cpu_time": 8.8516141412322657e+04, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/32768/real_time_cv", + "family_index": 41, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.0337345465402772e-03, + "cpu_time": 5.3857055643737814e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/65536/real_time", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 12, + "real_time": 5.0072440500116497e+07, + "cpu_time": 4.9631209750000238e+07, + "time_unit": "ns", + "model_cost": 3.3423360000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.9976960000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/65536/real_time", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 12, + "real_time": 5.1318516083483696e+07, + "cpu_time": 5.0895633166666932e+07, + "time_unit": "ns", + "model_cost": 3.3423360000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.9976960000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/65536/real_time", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 12, + "real_time": 4.6274398416547529e+07, + "cpu_time": 4.5726248249999672e+07, + "time_unit": "ns", + "model_cost": 3.3423360000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.9976960000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/65536/real_time_mean", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.9221785000049233e+07, + "cpu_time": 4.8751030388888948e+07, + "time_unit": "ns", + "model_cost": 3.3423360000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.9976960000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/65536/real_time_median", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.0072440500116497e+07, + "cpu_time": 4.9631209750000238e+07, + "time_unit": "ns", + "model_cost": 3.3423360000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.9976960000000000e+07, + "rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/Aggregation/65536/real_time_stddev", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.6274496844509300e+06, + "cpu_time": 2.6947489465554571e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/65536/real_time_cv", + "family_index": 42, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.3379813114219681e-02, + "cpu_time": 5.5275733149829559e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/131072/real_time", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 6, + "real_time": 1.2124658716675185e+08, + "cpu_time": 1.2044766766666727e+08, + "time_unit": "ns", + "model_cost": 6.6846720000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 7.9953920000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/131072/real_time", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 6, + "real_time": 1.1750262633358943e+08, + "cpu_time": 1.1657403650000001e+08, + "time_unit": "ns", + "model_cost": 6.6846720000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 7.9953920000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/131072/real_time", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/131072/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 6, + "real_time": 1.1702268499963491e+08, + "cpu_time": 1.1634220916666748e+08, + "time_unit": "ns", + "model_cost": 6.6846720000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 7.9953920000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/131072/real_time_mean", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1859063283332539e+08, + "cpu_time": 1.1778797111111158e+08, + "time_unit": "ns", + "model_cost": 6.6846720000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 7.9953920000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/131072/real_time_median", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1750262633358943e+08, + "cpu_time": 1.1657403650000001e+08, + "time_unit": "ns", + "model_cost": 6.6846720000000000e+07, + "output_rows": 1.3107200000000000e+05, + "plan_cost": 7.9953920000000000e+07, + "rows": 1.3107200000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/131072/real_time_stddev", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3126080487077404e+06, + "cpu_time": 2.3062795372307478e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/131072/real_time_cv", + "family_index": 43, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/131072/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.9500764887206756e-02, + "cpu_time": 1.9579924125318292e-02, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/262144/real_time", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2, + "real_time": 2.9701074250078821e+08, + "cpu_time": 2.9526163999999964e+08, + "time_unit": "ns", + "model_cost": 1.3369344000000000e+08, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 1.5990784000000000e+08, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/262144/real_time", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2, + "real_time": 3.0190800499985927e+08, + "cpu_time": 2.9990138250000340e+08, + "time_unit": "ns", + "model_cost": 1.3369344000000000e+08, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 1.5990784000000000e+08, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/262144/real_time", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/262144/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2, + "real_time": 3.0017835950093287e+08, + "cpu_time": 2.9699710800000644e+08, + "time_unit": "ns", + "model_cost": 1.3369344000000000e+08, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 1.5990784000000000e+08, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/262144/real_time_mean", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.9969903566719341e+08, + "cpu_time": 2.9738671016666985e+08, + "time_unit": "ns", + "model_cost": 1.3369344000000000e+08, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 1.5990784000000000e+08, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/262144/real_time_median", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0017835950093287e+08, + "cpu_time": 2.9699710800000644e+08, + "time_unit": "ns", + "model_cost": 1.3369344000000000e+08, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 1.5990784000000000e+08, + "rows": 2.6214400000000000e+05 + }, + { + "name": "OperatorCost/Aggregation/262144/real_time_stddev", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4835676957591916e+06, + "cpu_time": 2.3442792075138032e+06, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/Aggregation/262144/real_time_cv", + "family_index": 44, + "per_family_instance_index": 0, + "run_name": "OperatorCost/Aggregation/262144/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.2868724960367159e-03, + "cpu_time": 7.8829319783656655e-03, + "time_unit": "ns", + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/1024/1024/real_time", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 3567, + "real_time": 2.0146374376250996e+05, + "cpu_time": 2.0073131959629941e+05, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 4.0448000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.0928000000000000e+05, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/1024/1024/real_time", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 3567, + "real_time": 2.0650068488923580e+05, + "cpu_time": 2.0434347687131850e+05, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 4.0448000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.0928000000000000e+05, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/1024/1024/real_time", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 3567, + "real_time": 2.0361949509364122e+05, + "cpu_time": 2.0294448444070792e+05, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 4.0448000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.0928000000000000e+05, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/1024/1024/real_time_mean", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0386130791512897e+05, + "cpu_time": 2.0267309363610856e+05, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 4.0448000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.0928000000000000e+05, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/1024/1024/real_time_median", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0361949509364122e+05, + "cpu_time": 2.0294448444070792e+05, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 4.0448000000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 6.0928000000000000e+05, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/1024/1024/real_time_stddev", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.5271622541861511e+03, + "cpu_time": 1.8213071601146337e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/1024/1024/real_time_cv", + "family_index": 45, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.2396478174456984e-02, + "cpu_time": 8.9864279833055567e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/2048/2048/real_time", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/2048/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1782, + "real_time": 3.8912884960722306e+05, + "cpu_time": 3.8735831369248242e+05, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 8.0896000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2185600000000000e+06, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/2048/2048/real_time", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/2048/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1782, + "real_time": 3.8921161840631138e+05, + "cpu_time": 3.8687223793490685e+05, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 8.0896000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2185600000000000e+06, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/2048/2048/real_time", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/2048/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1782, + "real_time": 3.8661358136832394e+05, + "cpu_time": 3.8472813692480652e+05, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 8.0896000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2185600000000000e+06, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/2048/2048/real_time_mean", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.8831801646061940e+05, + "cpu_time": 3.8631956285073195e+05, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 8.0896000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2185600000000000e+06, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/2048/2048/real_time_median", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.8912884960722300e+05, + "cpu_time": 3.8687223793490691e+05, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 8.0896000000000000e+05, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 1.2185600000000000e+06, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/2048/2048/real_time_stddev", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4766641142950914e+03, + "cpu_time": 1.3994801780613327e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/2048/2048/real_time_cv", + "family_index": 46, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.8027185237356734e-03, + "cpu_time": 3.6225972294394808e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/4096/4096/real_time", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/4096/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 897, + "real_time": 7.7220005462761945e+05, + "cpu_time": 7.6321313266443566e+05, + "time_unit": "ns", + "lhs_rows": 4.0960000000000000e+03, + "model_cost": 1.6179200000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4371200000000000e+06, + "rhs_rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/4096/4096/real_time", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/4096/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 897, + "real_time": 8.2078781048108626e+05, + "cpu_time": 8.1381326644370065e+05, + "time_unit": "ns", + "lhs_rows": 4.0960000000000000e+03, + "model_cost": 1.6179200000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4371200000000000e+06, + "rhs_rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/4096/4096/real_time", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/4096/4096/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 897, + "real_time": 7.8572890078154451e+05, + "cpu_time": 7.7831858751394250e+05, + "time_unit": "ns", + "lhs_rows": 4.0960000000000000e+03, + "model_cost": 1.6179200000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4371200000000000e+06, + "rhs_rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/4096/4096/real_time_mean", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/4096/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.9290558863008337e+05, + "cpu_time": 7.8511499554069294e+05, + "time_unit": "ns", + "lhs_rows": 4.0960000000000000e+03, + "model_cost": 1.6179200000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4371200000000000e+06, + "rhs_rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/4096/4096/real_time_median", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/4096/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.8572890078154451e+05, + "cpu_time": 7.7831858751394262e+05, + "time_unit": "ns", + "lhs_rows": 4.0960000000000000e+03, + "model_cost": 1.6179200000000000e+06, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 2.4371200000000000e+06, + "rhs_rows": 4.0960000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/4096/4096/real_time_stddev", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/4096/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.5076306368273723e+04, + "cpu_time": 2.5975695489619106e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/4096/4096/real_time_cv", + "family_index": 47, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/4096/4096/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.1625841371100041e-02, + "cpu_time": 3.3085211258422301e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/8192/8192/real_time", + "family_index": 48, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/8192/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 430, + "real_time": 1.5729009674413288e+06, + "cpu_time": 1.5633765255813946e+06, + "time_unit": "ns", + "lhs_rows": 8.1920000000000000e+03, + "model_cost": 3.2358400000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.8742400000000000e+06, + "rhs_rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/8192/8192/real_time", + "family_index": 48, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/8192/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 430, + "real_time": 1.5958809558194547e+06, + "cpu_time": 1.5842298325581364e+06, + "time_unit": "ns", + "lhs_rows": 8.1920000000000000e+03, + "model_cost": 3.2358400000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.8742400000000000e+06, + "rhs_rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/8192/8192/real_time", + "family_index": 48, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/8192/8192/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 430, + "real_time": 1.5179995790720906e+06, + "cpu_time": 1.5076555279069894e+06, + "time_unit": "ns", + "lhs_rows": 8.1920000000000000e+03, + "model_cost": 3.2358400000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.8742400000000000e+06, + "rhs_rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/8192/8192/real_time_mean", + "family_index": 48, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/8192/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5622605007776245e+06, + "cpu_time": 1.5517539620155066e+06, + "time_unit": "ns", + "lhs_rows": 8.1920000000000000e+03, + "model_cost": 3.2358400000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.8742400000000000e+06, + "rhs_rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/8192/8192/real_time_median", + "family_index": 48, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/8192/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5729009674413288e+06, + "cpu_time": 1.5633765255813943e+06, + "time_unit": "ns", + "lhs_rows": 8.1920000000000000e+03, + "model_cost": 3.2358400000000000e+06, + "output_rows": 8.1920000000000000e+03, + "plan_cost": 4.8742400000000000e+06, + "rhs_rows": 8.1920000000000000e+03 + }, + { + "name": "OperatorCost/HashJoin/8192/8192/real_time_stddev", + "family_index": 48, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/8192/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.0016144980897938e+04, + "cpu_time": 3.9588117170229765e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/8192/8192/real_time_cv", + "family_index": 48, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/8192/8192/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.5614258928635562e-02, + "cpu_time": 2.5511851839456857e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/16384/16384/real_time", + "family_index": 49, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/16384/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 215, + "real_time": 3.3740766604666510e+06, + "cpu_time": 3.3407089534883853e+06, + "time_unit": "ns", + "lhs_rows": 1.6384000000000000e+04, + "model_cost": 6.4716800000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.7484800000000000e+06, + "rhs_rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/16384/16384/real_time", + "family_index": 49, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/16384/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 215, + "real_time": 3.4412175488352319e+06, + "cpu_time": 3.4016907720930316e+06, + "time_unit": "ns", + "lhs_rows": 1.6384000000000000e+04, + "model_cost": 6.4716800000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.7484800000000000e+06, + "rhs_rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/16384/16384/real_time", + "family_index": 49, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/16384/16384/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 215, + "real_time": 3.4444020278981975e+06, + "cpu_time": 3.3997880790697397e+06, + "time_unit": "ns", + "lhs_rows": 1.6384000000000000e+04, + "model_cost": 6.4716800000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.7484800000000000e+06, + "rhs_rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/16384/16384/real_time_mean", + "family_index": 49, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/16384/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.4198987457333598e+06, + "cpu_time": 3.3807292682170519e+06, + "time_unit": "ns", + "lhs_rows": 1.6384000000000000e+04, + "model_cost": 6.4716800000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.7484800000000000e+06, + "rhs_rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/16384/16384/real_time_median", + "family_index": 49, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/16384/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.4412175488352315e+06, + "cpu_time": 3.3997880790697397e+06, + "time_unit": "ns", + "lhs_rows": 1.6384000000000000e+04, + "model_cost": 6.4716800000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 9.7484800000000000e+06, + "rhs_rows": 1.6384000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/16384/16384/real_time_stddev", + "family_index": 49, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/16384/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.9715020462852139e+04, + "cpu_time": 3.4671663551382757e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/16384/16384/real_time_cv", + "family_index": 49, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/16384/16384/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1612922900831584e-02, + "cpu_time": 1.0255675861814305e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/32768/32768/real_time", + "family_index": 50, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/32768/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 85, + "real_time": 8.0679135293929214e+06, + "cpu_time": 7.9813523882353026e+06, + "time_unit": "ns", + "lhs_rows": 3.2768000000000000e+04, + "model_cost": 1.2943360000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9496960000000000e+07, + "rhs_rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/32768/32768/real_time", + "family_index": 50, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/32768/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 85, + "real_time": 8.4998512941049691e+06, + "cpu_time": 8.3723659411764871e+06, + "time_unit": "ns", + "lhs_rows": 3.2768000000000000e+04, + "model_cost": 1.2943360000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9496960000000000e+07, + "rhs_rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/32768/32768/real_time", + "family_index": 50, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/32768/32768/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 85, + "real_time": 7.7428119529298898e+06, + "cpu_time": 7.6226928705881061e+06, + "time_unit": "ns", + "lhs_rows": 3.2768000000000000e+04, + "model_cost": 1.2943360000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9496960000000000e+07, + "rhs_rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/32768/32768/real_time_mean", + "family_index": 50, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/32768/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.1035255921425922e+06, + "cpu_time": 7.9921370666666320e+06, + "time_unit": "ns", + "lhs_rows": 3.2768000000000000e+04, + "model_cost": 1.2943360000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9496960000000000e+07, + "rhs_rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/32768/32768/real_time_median", + "family_index": 50, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/32768/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.0679135293929204e+06, + "cpu_time": 7.9813523882353036e+06, + "time_unit": "ns", + "lhs_rows": 3.2768000000000000e+04, + "model_cost": 1.2943360000000000e+07, + "output_rows": 3.2768000000000000e+04, + "plan_cost": 1.9496960000000000e+07, + "rhs_rows": 3.2768000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/32768/32768/real_time_stddev", + "family_index": 50, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/32768/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7977401870278240e+05, + "cpu_time": 3.7495287724993145e+05, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/32768/32768/real_time_cv", + "family_index": 50, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/32768/32768/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6865282818508283e-02, + "cpu_time": 4.6915221063183930e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/65536/65536/real_time", + "family_index": 51, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/65536/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 43, + "real_time": 1.6538087651201220e+07, + "cpu_time": 1.6414523976743964e+07, + "time_unit": "ns", + "lhs_rows": 6.5536000000000000e+04, + "model_cost": 2.5886720000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.8993920000000000e+07, + "rhs_rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/65536/65536/real_time", + "family_index": 51, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/65536/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 43, + "real_time": 1.7416210860426154e+07, + "cpu_time": 1.7231467302325707e+07, + "time_unit": "ns", + "lhs_rows": 6.5536000000000000e+04, + "model_cost": 2.5886720000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.8993920000000000e+07, + "rhs_rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/65536/65536/real_time", + "family_index": 51, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/65536/65536/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 43, + "real_time": 2.0278357162761644e+07, + "cpu_time": 2.0049053000000212e+07, + "time_unit": "ns", + "lhs_rows": 6.5536000000000000e+04, + "model_cost": 2.5886720000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.8993920000000000e+07, + "rhs_rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/65536/65536/real_time_mean", + "family_index": 51, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/65536/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8077551891463004e+07, + "cpu_time": 1.7898348093023293e+07, + "time_unit": "ns", + "lhs_rows": 6.5536000000000000e+04, + "model_cost": 2.5886720000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.8993920000000000e+07, + "rhs_rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/65536/65536/real_time_median", + "family_index": 51, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/65536/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.7416210860426154e+07, + "cpu_time": 1.7231467302325703e+07, + "time_unit": "ns", + "lhs_rows": 6.5536000000000000e+04, + "model_cost": 2.5886720000000000e+07, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 3.8993920000000000e+07, + "rhs_rows": 6.5536000000000000e+04 + }, + { + "name": "OperatorCost/HashJoin/65536/65536/real_time_stddev", + "family_index": 51, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/65536/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9558714104656174e+06, + "cpu_time": 1.9068292521812392e+06, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/HashJoin/65536/65536/real_time_cv", + "family_index": 51, + "per_family_instance_index": 0, + "run_name": "OperatorCost/HashJoin/65536/65536/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0819337829638669e-01, + "cpu_time": 1.0653660562812016e-01, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "family_index": 52, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1944, + "real_time": 3.6657388374559011e+05, + "cpu_time": 3.6462056635802705e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 2.8672000000000000e+05, + "output_rows": 6.4000000000000000e+01, + "plan_cost": 2.9952000000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "family_index": 52, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1944, + "real_time": 3.7501044444407278e+05, + "cpu_time": 3.7154841563785839e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 2.8672000000000000e+05, + "output_rows": 6.4000000000000000e+01, + "plan_cost": 2.9952000000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "family_index": 52, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1944, + "real_time": 3.9455854886999773e+05, + "cpu_time": 3.9103051543209713e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 2.8672000000000000e+05, + "output_rows": 6.4000000000000000e+01, + "plan_cost": 2.9952000000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopJoin/64/64/real_time_mean", + "family_index": 52, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7871429235322011e+05, + "cpu_time": 3.7573316580932750e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 2.8672000000000000e+05, + "output_rows": 6.4000000000000000e+01, + "plan_cost": 2.9952000000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopJoin/64/64/real_time_median", + "family_index": 52, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7501044444407272e+05, + "cpu_time": 3.7154841563785839e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 2.8672000000000000e+05, + "output_rows": 6.4000000000000000e+01, + "plan_cost": 2.9952000000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopJoin/64/64/real_time_stddev", + "family_index": 52, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4355286048416290e+04, + "cpu_time": 1.3693263052392054e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/64/64/real_time_cv", + "family_index": 52, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.7905318965430986e-02, + "cpu_time": 3.6444115927048472e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "family_index": 53, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 564, + "real_time": 1.2504466258888117e+06, + "cpu_time": 1.2393362429078142e+06, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.1468800000000000e+06, + "output_rows": 1.2800000000000000e+02, + "plan_cost": 1.1724800000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "family_index": 53, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 564, + "real_time": 1.1502129361702723e+06, + "cpu_time": 1.1443939024822719e+06, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.1468800000000000e+06, + "output_rows": 1.2800000000000000e+02, + "plan_cost": 1.1724800000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "family_index": 53, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 564, + "real_time": 1.1442143616974226e+06, + "cpu_time": 1.1384036046099332e+06, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.1468800000000000e+06, + "output_rows": 1.2800000000000000e+02, + "plan_cost": 1.1724800000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/128/128/real_time_mean", + "family_index": 53, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1816246412521689e+06, + "cpu_time": 1.1740445833333398e+06, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.1468800000000000e+06, + "output_rows": 1.2800000000000000e+02, + "plan_cost": 1.1724800000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/128/128/real_time_median", + "family_index": 53, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1502129361702723e+06, + "cpu_time": 1.1443939024822717e+06, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.1468800000000000e+06, + "output_rows": 1.2800000000000000e+02, + "plan_cost": 1.1724800000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/128/128/real_time_stddev", + "family_index": 53, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9677004791804160e+04, + "cpu_time": 5.6623506820418137e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/128/128/real_time_cv", + "family_index": 53, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.0504197956268398e-02, + "cpu_time": 4.8229434916051528e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "family_index": 54, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 169, + "real_time": 4.1010925562131996e+06, + "cpu_time": 4.0723679940828541e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 4.5875200000000000e+06, + "output_rows": 2.5600000000000000e+02, + "plan_cost": 4.6387200000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "family_index": 54, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 169, + "real_time": 4.1692943491156008e+06, + "cpu_time": 4.1367916745562083e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 4.5875200000000000e+06, + "output_rows": 2.5600000000000000e+02, + "plan_cost": 4.6387200000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "family_index": 54, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 169, + "real_time": 4.1148842544408212e+06, + "cpu_time": 4.0873642307692380e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 4.5875200000000000e+06, + "output_rows": 2.5600000000000000e+02, + "plan_cost": 4.6387200000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/256/256/real_time_mean", + "family_index": 54, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.1284237199232071e+06, + "cpu_time": 4.0988412998027666e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 4.5875200000000000e+06, + "output_rows": 2.5600000000000000e+02, + "plan_cost": 4.6387200000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/256/256/real_time_median", + "family_index": 54, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.1148842544408212e+06, + "cpu_time": 4.0873642307692380e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 4.5875200000000000e+06, + "output_rows": 2.5600000000000000e+02, + "plan_cost": 4.6387200000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/256/256/real_time_stddev", + "family_index": 54, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6060490608731881e+04, + "cpu_time": 3.3710458119615381e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/256/256/real_time_cv", + "family_index": 54, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.7346873904218924e-03, + "cpu_time": 8.2243872484737326e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "family_index": 55, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 44, + "real_time": 1.5716153659096479e+07, + "cpu_time": 1.5615596909090938e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 1.8350080000000000e+07, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 1.8452480000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "family_index": 55, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 44, + "real_time": 1.5601414704509474e+07, + "cpu_time": 1.5520218500000091e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 1.8350080000000000e+07, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 1.8452480000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "family_index": 55, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 44, + "real_time": 1.5197989022701884e+07, + "cpu_time": 1.5102290250000166e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 1.8350080000000000e+07, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 1.8452480000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/512/512/real_time_mean", + "family_index": 55, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5505185795435945e+07, + "cpu_time": 1.5412701886363730e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 1.8350080000000000e+07, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 1.8452480000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/512/512/real_time_median", + "family_index": 55, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5601414704509474e+07, + "cpu_time": 1.5520218500000089e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 1.8350080000000000e+07, + "output_rows": 5.1200000000000000e+02, + "plan_cost": 1.8452480000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopJoin/512/512/real_time_stddev", + "family_index": 55, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7215556178150635e+05, + "cpu_time": 2.7302160761126026e+05, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/512/512/real_time_cv", + "family_index": 55, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.7552550828614844e-02, + "cpu_time": 1.7714065296547003e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "family_index": 56, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 12, + "real_time": 5.7824528999844916e+07, + "cpu_time": 5.7638922333332233e+07, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 7.3400320000000000e+07, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 7.3605120000000000e+07, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "family_index": 56, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 12, + "real_time": 5.8432866166792035e+07, + "cpu_time": 5.8187267416667990e+07, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 7.3400320000000000e+07, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 7.3605120000000000e+07, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "family_index": 56, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 12, + "real_time": 5.8150967583060265e+07, + "cpu_time": 5.7926297749998428e+07, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 7.3400320000000000e+07, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 7.3605120000000000e+07, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/1024/1024/real_time_mean", + "family_index": 56, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8136120916565739e+07, + "cpu_time": 5.7917495833332874e+07, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 7.3400320000000000e+07, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 7.3605120000000000e+07, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/1024/1024/real_time_median", + "family_index": 56, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8150967583060265e+07, + "cpu_time": 5.7926297749998428e+07, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 7.3400320000000000e+07, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 7.3605120000000000e+07, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/1024/1024/real_time_stddev", + "family_index": 56, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0444021548194246e+05, + "cpu_time": 2.7427848604579753e+05, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/1024/1024/real_time_cv", + "family_index": 56, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.2366792053233297e-03, + "cpu_time": 4.7356758454315598e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "family_index": 57, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 3, + "real_time": 2.3798401633272684e+08, + "cpu_time": 2.3706457999999961e+08, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 2.9360128000000000e+08, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.9401088000000000e+08, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "family_index": 57, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 3, + "real_time": 2.3061634099940419e+08, + "cpu_time": 2.2979345166666576e+08, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 2.9360128000000000e+08, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.9401088000000000e+08, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "family_index": 57, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 3, + "real_time": 2.3167426366611230e+08, + "cpu_time": 2.3087696966666725e+08, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 2.9360128000000000e+08, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.9401088000000000e+08, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/2048/2048/real_time_mean", + "family_index": 57, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3342487366608110e+08, + "cpu_time": 2.3257833377777752e+08, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 2.9360128000000000e+08, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.9401088000000000e+08, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/2048/2048/real_time_median", + "family_index": 57, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3167426366611230e+08, + "cpu_time": 2.3087696966666725e+08, + "time_unit": "ns", + "lhs_rows": 2.0480000000000000e+03, + "model_cost": 2.9360128000000000e+08, + "output_rows": 2.0480000000000000e+03, + "plan_cost": 2.9401088000000000e+08, + "rhs_rows": 2.0480000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopJoin/2048/2048/real_time_stddev", + "family_index": 57, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.9836084751526252e+06, + "cpu_time": 3.9227932253551562e+06, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopJoin/2048/2048/real_time_cv", + "family_index": 57, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopJoin/2048/2048/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.7065912525034737e-02, + "cpu_time": 1.6866546258359913e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "family_index": 58, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 5353, + "real_time": 1.2898380907888920e+05, + "cpu_time": 1.2792908051560172e+05, + "time_unit": "ns", + "lhs_rows": 3.2000000000000000e+01, + "model_cost": 1.0649600000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.1289600000000000e+05, + "rhs_rows": 3.2000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "family_index": 58, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 5353, + "real_time": 1.2603125481055504e+05, + "cpu_time": 1.2502512478983718e+05, + "time_unit": "ns", + "lhs_rows": 3.2000000000000000e+01, + "model_cost": 1.0649600000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.1289600000000000e+05, + "rhs_rows": 3.2000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "family_index": 58, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 5353, + "real_time": 1.2263131029293431e+05, + "cpu_time": 1.2186104558191706e+05, + "time_unit": "ns", + "lhs_rows": 3.2000000000000000e+01, + "model_cost": 1.0649600000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.1289600000000000e+05, + "rhs_rows": 3.2000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time_mean", + "family_index": 58, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2588212472745951e+05, + "cpu_time": 1.2493841696245196e+05, + "time_unit": "ns", + "lhs_rows": 3.2000000000000000e+01, + "model_cost": 1.0649600000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.1289600000000000e+05, + "rhs_rows": 3.2000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time_median", + "family_index": 58, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2603125481055502e+05, + "cpu_time": 1.2502512478983717e+05, + "time_unit": "ns", + "lhs_rows": 3.2000000000000000e+01, + "model_cost": 1.0649600000000000e+05, + "output_rows": 1.0240000000000000e+03, + "plan_cost": 1.1289600000000000e+05, + "rhs_rows": 3.2000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time_stddev", + "family_index": 58, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.1788740211984400e+03, + "cpu_time": 3.0349465686572944e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time_cv", + "family_index": 58, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/32/32/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.5252783332668127e-02, + "cpu_time": 2.4291540123878741e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "family_index": 59, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2559, + "real_time": 2.7295744626842841e+05, + "cpu_time": 2.7121314732316602e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 4.2598400000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.3878400000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "family_index": 59, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2559, + "real_time": 2.7748443845134013e+05, + "cpu_time": 2.7559722782336967e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 4.2598400000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.3878400000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "family_index": 59, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2559, + "real_time": 2.8844898866717721e+05, + "cpu_time": 2.8623549159827927e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 4.2598400000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.3878400000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time_mean", + "family_index": 59, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7963029112898186e+05, + "cpu_time": 2.7768195558160497e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 4.2598400000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.3878400000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time_median", + "family_index": 59, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.7748443845134007e+05, + "cpu_time": 2.7559722782336967e+05, + "time_unit": "ns", + "lhs_rows": 6.4000000000000000e+01, + "model_cost": 4.2598400000000000e+05, + "output_rows": 4.0960000000000000e+03, + "plan_cost": 4.3878400000000000e+05, + "rhs_rows": 6.4000000000000000e+01 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time_stddev", + "family_index": 59, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.9655812254137381e+03, + "cpu_time": 7.7251067468017245e+03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time_cv", + "family_index": 59, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/64/64/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.8486117127201879e-02, + "cpu_time": 2.7819981066546023e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "family_index": 60, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 688, + "real_time": 8.8981536918634991e+05, + "cpu_time": 8.8357706976744218e+05, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.7039360000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.7295360000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "family_index": 60, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 688, + "real_time": 8.9480302035342401e+05, + "cpu_time": 8.8767159738371032e+05, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.7039360000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.7295360000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "family_index": 60, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 688, + "real_time": 9.4535163081546174e+05, + "cpu_time": 9.3712476308139239e+05, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.7039360000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.7295360000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time_mean", + "family_index": 60, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.0999000678507856e+05, + "cpu_time": 9.0279114341084834e+05, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.7039360000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.7295360000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time_median", + "family_index": 60, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.9480302035342390e+05, + "cpu_time": 8.8767159738371044e+05, + "time_unit": "ns", + "lhs_rows": 1.2800000000000000e+02, + "model_cost": 1.7039360000000000e+06, + "output_rows": 1.6384000000000000e+04, + "plan_cost": 1.7295360000000000e+06, + "rhs_rows": 1.2800000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time_stddev", + "family_index": 60, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0725437451581263e+04, + "cpu_time": 2.9804183747610597e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time_cv", + "family_index": 60, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/128/128/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.3764587767432477e-02, + "cpu_time": 3.3013376310944940e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "family_index": 61, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 120, + "real_time": 5.2447048416676503e+06, + "cpu_time": 5.1699889416667586e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 6.8157440000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.8669440000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "family_index": 61, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 120, + "real_time": 5.0897273416618798e+06, + "cpu_time": 5.0246979666667366e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 6.8157440000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.8669440000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "family_index": 61, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 120, + "real_time": 4.7818925250188233e+06, + "cpu_time": 4.7253736416666452e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 6.8157440000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.8669440000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time_mean", + "family_index": 61, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.0387749027827838e+06, + "cpu_time": 4.9733535166667132e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 6.8157440000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.8669440000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time_median", + "family_index": 61, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.0897273416618798e+06, + "cpu_time": 5.0246979666667366e+06, + "time_unit": "ns", + "lhs_rows": 2.5600000000000000e+02, + "model_cost": 6.8157440000000000e+06, + "output_rows": 6.5536000000000000e+04, + "plan_cost": 6.8669440000000000e+06, + "rhs_rows": 2.5600000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time_stddev", + "family_index": 61, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.3557572748748618e+05, + "cpu_time": 2.2671100691828699e+05, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time_cv", + "family_index": 61, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/256/256/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6752580147484624e-02, + "cpu_time": 4.5585138108226682e-02, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "family_index": 62, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 31, + "real_time": 2.2470577064532630e+07, + "cpu_time": 2.2274796290322606e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 2.7262976000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.7365376000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "family_index": 62, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 31, + "real_time": 2.2671293516102575e+07, + "cpu_time": 2.2471355741935614e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 2.7262976000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.7365376000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "family_index": 62, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 31, + "real_time": 2.2575022580689795e+07, + "cpu_time": 2.2343392677418977e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 2.7262976000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.7365376000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time_mean", + "family_index": 62, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.2572297720441666e+07, + "cpu_time": 2.2363181569892395e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 2.7262976000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.7365376000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time_median", + "family_index": 62, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.2575022580689799e+07, + "cpu_time": 2.2343392677418977e+07, + "time_unit": "ns", + "lhs_rows": 5.1200000000000000e+02, + "model_cost": 2.7262976000000000e+07, + "output_rows": 2.6214400000000000e+05, + "plan_cost": 2.7365376000000000e+07, + "rhs_rows": 5.1200000000000000e+02 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time_stddev", + "family_index": 62, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0038596580303139e+05, + "cpu_time": 9.9762742061406781e+04, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time_cv", + "family_index": 62, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/512/512/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.4473082468747078e-03, + "cpu_time": 4.4610263414270879e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "family_index": 63, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 5, + "real_time": 1.1748234059996322e+08, + "cpu_time": 1.1633298520000041e+08, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 1.0905190400000000e+08, + "output_rows": 1.0485760000000000e+06, + "plan_cost": 1.0925670400000000e+08, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "family_index": 63, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 5, + "real_time": 1.1784866999951191e+08, + "cpu_time": 1.1670695120000120e+08, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 1.0905190400000000e+08, + "output_rows": 1.0485760000000000e+06, + "plan_cost": 1.0925670400000000e+08, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "family_index": 63, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 5, + "real_time": 1.1698665840012836e+08, + "cpu_time": 1.1589235359999748e+08, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 1.0905190400000000e+08, + "output_rows": 1.0485760000000000e+06, + "plan_cost": 1.0925670400000000e+08, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time_mean", + "family_index": 63, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1743922299986781e+08, + "cpu_time": 1.1631076333333302e+08, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 1.0905190400000000e+08, + "output_rows": 1.0485760000000000e+06, + "plan_cost": 1.0925670400000000e+08, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time_median", + "family_index": 63, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1748234059996320e+08, + "cpu_time": 1.1633298520000039e+08, + "time_unit": "ns", + "lhs_rows": 1.0240000000000000e+03, + "model_cost": 1.0905190400000000e+08, + "output_rows": 1.0485760000000000e+06, + "plan_cost": 1.0925670400000000e+08, + "rhs_rows": 1.0240000000000000e+03 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time_stddev", + "family_index": 63, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3262032424765010e+05, + "cpu_time": 4.0775319864778483e+05, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + }, + { + "name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time_cv", + "family_index": 63, + "per_family_instance_index": 0, + "run_name": "OperatorCost/NestedLoopCrossJoin/1024/1024/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.6837805393870587e-03, + "cpu_time": 3.5057219724301180e-03, + "time_unit": "ns", + "lhs_rows": 0.0000000000000000e+00, + "model_cost": 0.0000000000000000e+00, + "output_rows": 0.0000000000000000e+00, + "plan_cost": 0.0000000000000000e+00, + "rhs_rows": 0.0000000000000000e+00 + } + ] +} diff --git a/research/benchmark-results/query.json b/research/benchmark-results/query.json new file mode 100644 index 0000000..0d0ca17 --- /dev/null +++ b/research/benchmark-results/query.json @@ -0,0 +1,1266 @@ +{ + "context": { + "date": "2026-06-14T20:44:00+03:00", + "host_name": "nixos", + "executable": "./build-release/bin/benchmarks", + "num_cpus": 8, + "mhz_per_cpu": 4200, + "cpu_scaling_enabled": true, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 49152, + "num_sharing": 2 + }, + { + "type": "Instruction", + "level": 1, + "size": 32768, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 2, + "size": 1310720, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 3, + "size": 8388608, + "num_sharing": 8 + } + ], + "load_avg": [0.801758,1.39307,1.86475], + "library_version": "v1.9.0", + "library_build_type": "release", + "json_schema_version": 1 + }, + "benchmarks": [ + { + "name": "BM_SQL/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 30908, + "real_time": 1.9690791995604395e+04, + "cpu_time": 1.9364304225443251e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 30908, + "real_time": 2.0620323508553727e+04, + "cpu_time": 2.0439727384495920e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 30908, + "real_time": 2.1001014268052593e+04, + "cpu_time": 2.0805882004658986e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0437376590736905e+04, + "cpu_time": 2.0203304538199383e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0620323508553727e+04, + "cpu_time": 2.0439727384495916e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.7399761265075608e+02, + "cpu_time": 7.4930530831593944e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.2978675597544187e-02, + "cpu_time": 3.7088254889153946e-02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 34131, + "real_time": 2.0186661773754931e+04, + "cpu_time": 1.9933065453693125e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 34131, + "real_time": 2.0169296094510435e+04, + "cpu_time": 1.9849478626468608e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 34131, + "real_time": 2.0091933315705788e+04, + "cpu_time": 1.9958257449239689e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0149297061323719e+04, + "cpu_time": 1.9913600509800472e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0169296094510435e+04, + "cpu_time": 1.9933065453693125e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.0431549524075258e+01, + "cpu_time": 5.6941822078140824e+01, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.5028937421781275e-03, + "cpu_time": 2.8594438283582580e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 35049, + "real_time": 2.0029725127609468e+04, + "cpu_time": 1.9744634625809576e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 35049, + "real_time": 1.9694179577221064e+04, + "cpu_time": 1.9469350423692540e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 35049, + "real_time": 1.9976324659756159e+04, + "cpu_time": 1.9649384005249784e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9900076454862228e+04, + "cpu_time": 1.9621123018250630e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9976324659756163e+04, + "cpu_time": 1.9649384005249784e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.8029987703411811e+02, + "cpu_time": 1.3980114635986928e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 9.0602605192536840e-03, + "cpu_time": 7.1250328653376729e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 35368, + "real_time": 1.9960714940043279e+04, + "cpu_time": 1.9790182368242466e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 35368, + "real_time": 1.9953514787403295e+04, + "cpu_time": 1.9870601136620662e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 35368, + "real_time": 1.9325330383437165e+04, + "cpu_time": 1.9268540545125539e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9746520036961247e+04, + "cpu_time": 1.9643108016662885e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9953514787403295e+04, + "cpu_time": 1.9790182368242466e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6477870514374996e+02, + "cpu_time": 3.2686753835056970e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.8473062821244581e-02, + "cpu_time": 1.6640316699032251e-02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 10059, + "real_time": 6.7123694005437108e+04, + "cpu_time": 6.6452997017596266e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 10059, + "real_time": 6.6603497464995104e+04, + "cpu_time": 6.5978846406203447e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 10059, + "real_time": 6.6185145243245774e+04, + "cpu_time": 6.5579015806740179e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6637445571225995e+04, + "cpu_time": 6.6003619743513293e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6603497464995118e+04, + "cpu_time": 6.5978846406203447e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.7019442807371064e+02, + "cpu_time": 4.3751694586045232e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 7.0560091858734202e-03, + "cpu_time": 6.6286810868952479e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 16520, + "real_time": 4.4866463377613101e+04, + "cpu_time": 4.4648068462469775e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 16520, + "real_time": 4.2604707445555112e+04, + "cpu_time": 4.2458468644067827e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 16520, + "real_time": 4.2180054963624643e+04, + "cpu_time": 4.2046813801452801e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3217075262264276e+04, + "cpu_time": 4.3051116969330134e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.2604707445555105e+04, + "cpu_time": 4.2458468644067827e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4441064015139771e+03, + "cpu_time": 1.3982329852147041e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.3415181215997812e-02, + "cpu_time": 3.2478436882620573e-02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 10476, + "real_time": 6.6407572833197148e+04, + "cpu_time": 6.5812611970217578e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 10476, + "real_time": 6.6131168671347084e+04, + "cpu_time": 6.5514603283696037e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 10476, + "real_time": 6.5557157693897243e+04, + "cpu_time": 6.4923682321496744e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6031966399480487e+04, + "cpu_time": 6.5416965858470117e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6131168671347084e+04, + "cpu_time": 6.5514603283696029e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3379983327240069e+02, + "cpu_time": 4.5243649299935231e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.5695428581968394e-03, + "cpu_time": 6.9161950124406639e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 16429, + "real_time": 4.2655499908660488e+04, + "cpu_time": 4.2512722868099081e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 16429, + "real_time": 4.3006030555641300e+04, + "cpu_time": 4.2847248219611705e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 16429, + "real_time": 4.2671599427912239e+04, + "cpu_time": 4.2512723598514865e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.2777709964071335e+04, + "cpu_time": 4.2624231562075220e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.2671599427912239e+04, + "cpu_time": 4.2512723598514865e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9789521982826508e+02, + "cpu_time": 1.9313809089472591e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6261293555563323e-03, + "cpu_time": 4.5311805941522223e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 8249, + "real_time": 8.2960084252814093e+04, + "cpu_time": 8.2177835737665257e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 8249, + "real_time": 8.2548407685846934e+04, + "cpu_time": 8.1742332161474085e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 8249, + "real_time": 8.1475008728372239e+04, + "cpu_time": 8.0718609164747511e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.2327833555677746e+04, + "cpu_time": 8.1546259021295627e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.2548407685846934e+04, + "cpu_time": 8.1742332161474100e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.6671509604269932e+02, + "cpu_time": 7.4911217784934934e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 9.3129511968048466e-03, + "cpu_time": 9.1863463369143677e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 8491, + "real_time": 8.1564215168851792e+04, + "cpu_time": 8.0868939347544539e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 8491, + "real_time": 8.2415557766973274e+04, + "cpu_time": 8.1676025556471272e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 8491, + "real_time": 8.1876454834554199e+04, + "cpu_time": 8.1156611588741158e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.1952075923459750e+04, + "cpu_time": 8.1233858830918994e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.1876454834554184e+04, + "cpu_time": 8.1156611588741158e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3067965666014658e+02, + "cpu_time": 4.0905059525465964e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.2552623201685056e-03, + "cpu_time": 5.0354692137186529e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 8845, + "real_time": 7.8003122781477985e+04, + "cpu_time": 7.7279036404748433e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 8845, + "real_time": 7.8157331147635239e+04, + "cpu_time": 7.7421407687959552e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 8845, + "real_time": 7.7875304239552788e+04, + "cpu_time": 7.7212780214810453e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.8011919389555333e+04, + "cpu_time": 7.7304408102506146e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.8003122781478000e+04, + "cpu_time": 7.7279036404748433e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4121908318453850e+02, + "cpu_time": 1.0660275758583292e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.8102244412082200e-03, + "cpu_time": 1.3789997259208940e-03, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 9102, + "real_time": 7.7994850362925325e+04, + "cpu_time": 7.7232681388705620e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 9102, + "real_time": 7.7683349923208254e+04, + "cpu_time": 7.6985877059986873e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 9102, + "real_time": 7.7163363656431247e+04, + "cpu_time": 7.6465104372665315e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_mean", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.7613854647521584e+04, + "cpu_time": 7.6894554273785921e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_median", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.7683349923208239e+04, + "cpu_time": 7.6985877059986859e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_stddev", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.2007705337852963e+02, + "cpu_time": 3.9185266025673900e+02, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time_cv", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.4123977643718750e-03, + "cpu_time": 5.0959741422199160e-03, + "time_unit": "ns" + } + ] +} diff --git a/research/benchmark-results/ssb-sf001.json b/research/benchmark-results/ssb-sf001.json new file mode 100644 index 0000000..24be094 --- /dev/null +++ b/research/benchmark-results/ssb-sf001.json @@ -0,0 +1,3240 @@ +{ + "context": { + "date": "2026-06-14T20:44:28+03:00", + "host_name": "nixos", + "executable": "./build-release/bin/benchmarks", + "num_cpus": 8, + "mhz_per_cpu": 4200, + "cpu_scaling_enabled": true, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 49152, + "num_sharing": 2 + }, + { + "type": "Instruction", + "level": 1, + "size": 32768, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 2, + "size": 1310720, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 3, + "size": 8388608, + "num_sharing": 8 + } + ], + "load_avg": [1.58936,1.53076,1.89648], + "library_version": "v1.9.0", + "library_build_type": "release", + "json_schema_version": 1 + }, + "benchmarks": [ + { + "name": "SSB/q2.1/Interpreted/Naive/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 65, + "real_time": 9.8329785384591483e+06, + "cpu_time": 9.7378805384615380e+06, + "time_unit": "ns", + "execution_us": 9.8328972153846153e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 65, + "real_time": 9.0062370153296236e+06, + "cpu_time": 8.9505159692307673e+06, + "time_unit": "ns", + "execution_us": 9.0061597846153854e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 65, + "real_time": 9.2073494307652060e+06, + "cpu_time": 9.1503834769230746e+06, + "time_unit": "ns", + "execution_us": 9.2072650923076926e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_mean", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.3488549948513266e+06, + "cpu_time": 9.2795933282051273e+06, + "time_unit": "ns", + "execution_us": 9.3487740307692311e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_median", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.2073494307652060e+06, + "cpu_time": 9.1503834769230764e+06, + "time_unit": "ns", + "execution_us": 9.2072650923076926e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_stddev", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3115340079504781e+05, + "cpu_time": 4.0927634975801205e+05, + "time_unit": "ns", + "execution_us": 4.3115226963000237e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_cv", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.6118310855446563e-02, + "cpu_time": 4.4104987716867425e-02, + "time_unit": "ns", + "execution_us": 4.6118589262182277e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 80, + "real_time": 8.3042533124626055e+06, + "cpu_time": 8.2629934750000034e+06, + "time_unit": "ns", + "execution_us": 8.3041996125000005e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.5440129999999999e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 80, + "real_time": 8.7721496249741893e+06, + "cpu_time": 8.7221346999999993e+06, + "time_unit": "ns", + "execution_us": 8.7720931375000000e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 5.1104020000000000e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 80, + "real_time": 8.3539369250047458e+06, + "cpu_time": 8.3101026999999937e+06, + "time_unit": "ns", + "execution_us": 8.3538819875000008e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.9359480000000003e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_mean", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.4767799541471805e+06, + "cpu_time": 8.4317436249999981e+06, + "time_unit": "ns", + "execution_us": 8.4767249124999998e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.8634543333333331e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_median", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3539369250047458e+06, + "cpu_time": 8.3101026999999946e+06, + "time_unit": "ns", + "execution_us": 8.3538819875000008e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.9359480000000003e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_stddev", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.5700106453506710e+05, + "cpu_time": 2.5258672193873374e+05, + "time_unit": "ns", + "execution_us": 2.5699975846519629e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.9007003225141398e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_cv", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.0318241823574989e-02, + "cpu_time": 2.9956641612040688e-02, + "time_unit": "ns", + "execution_us": 3.0318284610866365e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 5.9642799617407871e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 84, + "real_time": 8.3345388928656960e+06, + "cpu_time": 8.2792537261904702e+06, + "time_unit": "ns", + "execution_us": 8.3344888690476182e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 84, + "real_time": 8.3228132023664545e+06, + "cpu_time": 8.2659602738095270e+06, + "time_unit": "ns", + "execution_us": 8.3227613095238084e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 84, + "real_time": 8.3253920952518536e+06, + "cpu_time": 8.2728575595238097e+06, + "time_unit": "ns", + "execution_us": 8.3251496190476191e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_mean", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3275813968280004e+06, + "cpu_time": 8.2726905198412677e+06, + "time_unit": "ns", + "execution_us": 8.3274665992063474e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_median", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3253920952518536e+06, + "cpu_time": 8.2728575595238088e+06, + "time_unit": "ns", + "execution_us": 8.3251496190476191e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_stddev", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.1617964484808936e+03, + "cpu_time": 6.6483002151819046e+03, + "time_unit": "ns", + "execution_us": 6.1975971870199906e+00, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_cv", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 7.3992629490573798e-04, + "cpu_time": 8.0364425566707514e-04, + "time_unit": "ns", + "execution_us": 7.4423561033684063e-04, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 101, + "real_time": 6.6697983366064373e+06, + "cpu_time": 6.6404850990099097e+06, + "time_unit": "ns", + "execution_us": 6.6697433861386144e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.5430560000000000e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 101, + "real_time": 6.7415753267299142e+06, + "cpu_time": 6.7046504356435630e+06, + "time_unit": "ns", + "execution_us": 6.7415256534653463e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.5653320000000001e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 101, + "real_time": 6.5917710198125793e+06, + "cpu_time": 6.5674319702970274e+06, + "time_unit": "ns", + "execution_us": 6.5917119702970294e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.4078960000000000e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_mean", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6677148943829769e+06, + "cpu_time": 6.6375225016501658e+06, + "time_unit": "ns", + "execution_us": 6.6676603366336640e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.5054279999999999e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_median", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6697983366064383e+06, + "cpu_time": 6.6404850990099087e+06, + "time_unit": "ns", + "execution_us": 6.6697433861386144e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.5430560000000000e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_stddev", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.4923882316378396e+04, + "cpu_time": 6.8657188591943937e+04, + "time_unit": "ns", + "execution_us": 7.4928560893270131e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.5196380862105471e+01, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_cv", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1236815536233537e-02, + "cpu_time": 1.0343797489933172e-02, + "time_unit": "ns", + "execution_us": 1.1237609162781633e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 5.6592796774143618e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 84, + "real_time": 8.3219361428443035e+06, + "cpu_time": 8.2646981428571399e+06, + "time_unit": "ns", + "execution_us": 8.3218814523809524e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 84, + "real_time": 8.2953126071255878e+06, + "cpu_time": 8.2430971309523713e+06, + "time_unit": "ns", + "execution_us": 8.2952615952380966e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 84, + "real_time": 8.3282351904580574e+06, + "cpu_time": 8.2720098809523992e+06, + "time_unit": "ns", + "execution_us": 8.3281770238095232e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_mean", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3151613134759823e+06, + "cpu_time": 8.2599350515873022e+06, + "time_unit": "ns", + "execution_us": 8.3151066904761901e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_median", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3219361428443035e+06, + "cpu_time": 8.2646981428571409e+06, + "time_unit": "ns", + "execution_us": 8.3218814523809524e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_stddev", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.7475636127024853e+04, + "cpu_time": 1.5033364792371891e+04, + "time_unit": "ns", + "execution_us": 1.7472246811903176e+01, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_cv", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.1016593025925943e-03, + "cpu_time": 1.8200342615869534e-03, + "time_unit": "ns", + "execution_us": 2.1012654993248889e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 100, + "real_time": 6.7307652599993162e+06, + "cpu_time": 6.7007803200000105e+06, + "time_unit": "ns", + "execution_us": 6.7307008900000001e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.0578180000000000e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 100, + "real_time": 6.7873448400132479e+06, + "cpu_time": 6.7555737600000044e+06, + "time_unit": "ns", + "execution_us": 6.7872900899999995e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.0378109999999999e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 100, + "real_time": 6.6988098000001637e+06, + "cpu_time": 6.6724952500000084e+06, + "time_unit": "ns", + "execution_us": 6.6987279000000008e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.1611500000000001e+02, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_mean", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.7389733000042411e+06, + "cpu_time": 6.7096164433333399e+06, + "time_unit": "ns", + "execution_us": 6.7389062933333335e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 9.7058133333333319e+02, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_median", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.7307652599993162e+06, + "cpu_time": 6.7007803200000105e+06, + "time_unit": "ns", + "execution_us": 6.7307008900000001e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.0378109999999999e+03, + "plan_cost": 6.5429700000000000e+05 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_stddev", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.4834610158672738e+04, + "cpu_time": 4.2238223360451244e+04, + "time_unit": "ns", + "execution_us": 4.4847651636254497e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.3414527987347694e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_cv", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.6530327637066801e-03, + "cpu_time": 6.2951770368959090e-03, + "time_unit": "ns", + "execution_us": 6.6550341678769734e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.3821127119019766e-01, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 19, + "real_time": 3.7356791631617330e+07, + "cpu_time": 3.7145934894736871e+07, + "time_unit": "ns", + "execution_us": 3.7356630842105267e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 19, + "real_time": 3.6780936842038311e+07, + "cpu_time": 3.6587893368420988e+07, + "time_unit": "ns", + "execution_us": 3.6780794315789470e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 19, + "real_time": 3.5959773421047047e+07, + "cpu_time": 3.5775333947368443e+07, + "time_unit": "ns", + "execution_us": 3.5959546789473679e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_mean", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6699167298234224e+07, + "cpu_time": 3.6503054070175432e+07, + "time_unit": "ns", + "execution_us": 3.6698990649122803e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_median", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6780936842038304e+07, + "cpu_time": 3.6587893368420988e+07, + "time_unit": "ns", + "execution_us": 3.6780794315789470e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_stddev", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.0208949848775752e+05, + "cpu_time": 6.8922784268140828e+05, + "time_unit": "ns", + "execution_us": 7.0212523273231898e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_cv", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.9130938115904838e-02, + "cpu_time": 1.8881374729807530e-02, + "time_unit": "ns", + "execution_us": 1.9132003913821579e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 117, + "real_time": 6.0592878803423746e+06, + "cpu_time": 6.0277963076923024e+06, + "time_unit": "ns", + "execution_us": 6.0592385641025639e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.7728214999999997e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 117, + "real_time": 6.0116561880232226e+06, + "cpu_time": 5.9801104188034283e+06, + "time_unit": "ns", + "execution_us": 6.0115675726495720e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 7.1048990000000005e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 117, + "real_time": 5.8206043589977603e+06, + "cpu_time": 5.7925040085469894e+06, + "time_unit": "ns", + "execution_us": 5.8205405811965811e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.6036979999999996e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_mean", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9638494757877858e+06, + "cpu_time": 5.9334702450142391e+06, + "time_unit": "ns", + "execution_us": 5.9637822393162396e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.8271394999999990e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_median", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.0116561880232235e+06, + "cpu_time": 5.9801104188034274e+06, + "time_unit": "ns", + "execution_us": 6.0115675726495720e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.7728214999999997e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_stddev", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2631930628945892e+05, + "cpu_time": 1.2438687980487074e+05, + "time_unit": "ns", + "execution_us": 1.2632007093950280e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.5497734103889311e+03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_cv", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.1180834090849178e-02, + "cpu_time": 2.0963597130935343e-02, + "time_unit": "ns", + "execution_us": 2.1181201102001616e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 3.7347609645136615e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 20, + "real_time": 3.4855250100008562e+07, + "cpu_time": 3.4689481249999866e+07, + "time_unit": "ns", + "execution_us": 3.4854925650000005e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 20, + "real_time": 3.4714216350039348e+07, + "cpu_time": 3.4550395049999945e+07, + "time_unit": "ns", + "execution_us": 3.4714046049999997e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 20, + "real_time": 3.4567416099889673e+07, + "cpu_time": 3.4415430550000004e+07, + "time_unit": "ns", + "execution_us": 3.4567268850000000e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_mean", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.4712294183312520e+07, + "cpu_time": 3.4551768949999936e+07, + "time_unit": "ns", + "execution_us": 3.4712080183333324e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_median", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.4714216350039355e+07, + "cpu_time": 3.4550395049999945e+07, + "time_unit": "ns", + "execution_us": 3.4714046049999997e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_stddev", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4392662696637286e+05, + "cpu_time": 1.3703051573725880e+05, + "time_unit": "ns", + "execution_us": 1.4383847580339466e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_cv", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.1462723900157440e-03, + "cpu_time": 3.9659479066196709e-03, + "time_unit": "ns", + "execution_us": 4.1437584565288408e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 120, + "real_time": 5.9461910583498441e+06, + "cpu_time": 5.9119624500000076e+06, + "time_unit": "ns", + "execution_us": 5.9461413000000002e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.4858980000000003e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 120, + "real_time": 5.9969888416768909e+06, + "cpu_time": 5.9653847333333232e+06, + "time_unit": "ns", + "execution_us": 5.9969197750000003e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.6036336000000003e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 120, + "real_time": 5.7678943499922752e+06, + "cpu_time": 5.7363008250000002e+06, + "time_unit": "ns", + "execution_us": 5.7678408499999996e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.5913084999999999e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_mean", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9036914166730037e+06, + "cpu_time": 5.8712160027777767e+06, + "time_unit": "ns", + "execution_us": 5.9036339750000006e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.5602800333333333e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_median", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9461910583498441e+06, + "cpu_time": 5.9119624500000076e+06, + "time_unit": "ns", + "execution_us": 5.9461413000000002e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.5913084999999999e+04, + "plan_cost": 8.1671400000000000e+05 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_stddev", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2031515360523395e+05, + "cpu_time": 1.1985434487430479e+05, + "time_unit": "ns", + "execution_us": 1.2030977923165490e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.4710835140662664e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_cv", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.0379648107190021e-02, + "cpu_time": 2.0413887824532358e-02, + "time_unit": "ns", + "execution_us": 2.0378936048733424e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.4190101192834495e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 76, + "real_time": 9.1743526052979231e+06, + "cpu_time": 9.1179729078947455e+06, + "time_unit": "ns", + "execution_us": 9.1742886710526309e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 76, + "real_time": 9.4107480131481830e+06, + "cpu_time": 9.3477200789473876e+06, + "time_unit": "ns", + "execution_us": 9.4106384868421064e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 76, + "real_time": 9.0052268684590477e+06, + "cpu_time": 8.9515037763158120e+06, + "time_unit": "ns", + "execution_us": 9.0051711973684214e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_mean", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.1967758289683852e+06, + "cpu_time": 9.1390655877193138e+06, + "time_unit": "ns", + "execution_us": 9.1966994517543862e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_median", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.1743526052979212e+06, + "cpu_time": 9.1179729078947455e+06, + "time_unit": "ns", + "execution_us": 9.1742886710526309e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_stddev", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0368836593519201e+05, + "cpu_time": 1.9894852468520761e+05, + "time_unit": "ns", + "execution_us": 2.0366053379838627e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_cv", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.2147801547321183e-02, + "cpu_time": 2.1769022530328062e-02, + "time_unit": "ns", + "execution_us": 2.2144959163532898e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 115, + "real_time": 6.0078362869440429e+06, + "cpu_time": 5.9798945304347947e+06, + "time_unit": "ns", + "execution_us": 6.0077843391304350e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.0474788799999998e+05, + "plan_cost": 8.4135300000000000e+05 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 115, + "real_time": 6.0595493999995673e+06, + "cpu_time": 6.0307555652173972e+06, + "time_unit": "ns", + "execution_us": 6.0595045391304348e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.0627205599999998e+05, + "plan_cost": 8.4135300000000000e+05 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 115, + "real_time": 6.0222565738987885e+06, + "cpu_time": 5.9944581304347729e+06, + "time_unit": "ns", + "execution_us": 6.0221855217391303e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.0481579499999998e+05, + "plan_cost": 8.4135300000000000e+05 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_mean", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.0298807536141323e+06, + "cpu_time": 6.0017027420289880e+06, + "time_unit": "ns", + "execution_us": 6.0298247999999994e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.0527857966666657e+05, + "plan_cost": 8.4135300000000000e+05 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_median", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.0222565738987885e+06, + "cpu_time": 5.9944581304347729e+06, + "time_unit": "ns", + "execution_us": 6.0221855217391303e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.0481579499999998e+05, + "plan_cost": 8.4135300000000000e+05 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_stddev", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.6686281170886348e+04, + "cpu_time": 2.6193024122047806e+04, + "time_unit": "ns", + "execution_us": 2.6692952277169827e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.6104544530950545e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_cv", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.4256731204661689e-03, + "cpu_time": 4.3642654839637657e-03, + "time_unit": "ns", + "execution_us": 4.4268205399881320e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.8205236222262312e-03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 85, + "real_time": 8.5987712588308454e+06, + "cpu_time": 8.5449464705882277e+06, + "time_unit": "ns", + "execution_us": 8.5987177058823527e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 85, + "real_time": 8.3122461647315715e+06, + "cpu_time": 8.2646852941176221e+06, + "time_unit": "ns", + "execution_us": 8.3122011999999995e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 85, + "real_time": 8.3148362235292811e+06, + "cpu_time": 8.2647834235293977e+06, + "time_unit": "ns", + "execution_us": 8.3147847882352944e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_mean", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.4086178823638987e+06, + "cpu_time": 8.3581383960784152e+06, + "time_unit": "ns", + "execution_us": 8.4085678980392149e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_median", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3148362235292820e+06, + "cpu_time": 8.2647834235293977e+06, + "time_unit": "ns", + "execution_us": 8.3147847882352944e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_stddev", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6468274662965687e+05, + "cpu_time": 1.6178054559774752e+05, + "time_unit": "ns", + "execution_us": 1.6467963079606903e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_cv", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.9584995885597305e-02, + "cpu_time": 1.9356050107240858e-02, + "time_unit": "ns", + "execution_us": 1.9584741753048161e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 107, + "real_time": 6.3523898691604808e+06, + "cpu_time": 6.3260480093458062e+06, + "time_unit": "ns", + "execution_us": 6.3523282429906540e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.5599400000000003e+02, + "plan_cost": 7.0505700000000000e+05 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 107, + "real_time": 6.3679439158689678e+06, + "cpu_time": 6.3429361869158233e+06, + "time_unit": "ns", + "execution_us": 6.3678898598130836e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6768600000000004e+02, + "plan_cost": 7.0505700000000000e+05 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 107, + "real_time": 6.3869238411001293e+06, + "cpu_time": 6.3593958224299001e+06, + "time_unit": "ns", + "execution_us": 6.3868742523364490e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.7205100000000004e+02, + "plan_cost": 7.0505700000000000e+05 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_mean", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3690858753765263e+06, + "cpu_time": 6.3427933395638429e+06, + "time_unit": "ns", + "execution_us": 6.3690307850467289e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6524366666666674e+02, + "plan_cost": 7.0505700000000000e+05 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_median", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3679439158689678e+06, + "cpu_time": 6.3429361869158223e+06, + "time_unit": "ns", + "execution_us": 6.3678898598130836e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6768600000000004e+02, + "plan_cost": 7.0505700000000000e+05 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_stddev", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.7295284273841808e+04, + "cpu_time": 1.6674365457499382e+04, + "time_unit": "ns", + "execution_us": 1.7301241956305628e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.3024427931277049e+00, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_cv", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.7155049582086766e-03, + "cpu_time": 2.6288678449432161e-03, + "time_unit": "ns", + "execution_us": 2.7164638608633590e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.2480303397293079e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 20, + "real_time": 3.5077894499954708e+07, + "cpu_time": 3.4906336599999934e+07, + "time_unit": "ns", + "execution_us": 3.5077743900000001e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 20, + "real_time": 3.5094477299935535e+07, + "cpu_time": 3.4919402299999908e+07, + "time_unit": "ns", + "execution_us": 3.5094321400000001e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 20, + "real_time": 3.5089531149969846e+07, + "cpu_time": 3.4928554099999681e+07, + "time_unit": "ns", + "execution_us": 3.5089357900000003e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_mean", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5087300983286694e+07, + "cpu_time": 3.4918097666666500e+07, + "time_unit": "ns", + "execution_us": 3.5087141066666663e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_median", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5089531149969846e+07, + "cpu_time": 3.4919402299999900e+07, + "time_unit": "ns", + "execution_us": 3.5089357900000003e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_stddev", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.5133745638521341e+03, + "cpu_time": 1.1166059193544061e+04, + "time_unit": "ns", + "execution_us": 8.5081807416769717e+00, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_cv", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.4263406774739814e-04, + "cpu_time": 3.1977856583531464e-04, + "time_unit": "ns", + "execution_us": 2.4248714722898519e-04, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 118, + "real_time": 6.2030809491508249e+06, + "cpu_time": 6.1696264237288479e+06, + "time_unit": "ns", + "execution_us": 6.2030278305084748e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5802998000000000e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 118, + "real_time": 5.9932255338689005e+06, + "cpu_time": 5.9554913220338793e+06, + "time_unit": "ns", + "execution_us": 5.9931459152542375e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.7717337000000000e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 118, + "real_time": 5.9333370000030743e+06, + "cpu_time": 5.8999043474576101e+06, + "time_unit": "ns", + "execution_us": 5.9332841186440673e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.4845692999999999e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_mean", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.0432144943409329e+06, + "cpu_time": 6.0083406977401124e+06, + "time_unit": "ns", + "execution_us": 6.0431526214689266e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.6122009333333328e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_median", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9932255338689005e+06, + "cpu_time": 5.9554913220338784e+06, + "time_unit": "ns", + "execution_us": 5.9931459152542375e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5802998000000000e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_stddev", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.4164964402453275e+05, + "cpu_time": 1.4241591200219796e+05, + "time_unit": "ns", + "execution_us": 1.4165422874880181e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.4621597001629127e+03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_cv", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.3439453316968676e-02, + "cpu_time": 2.3703035358125436e-02, + "time_unit": "ns", + "execution_us": 2.3440451966339634e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 5.5974243080033854e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 78, + "real_time": 8.7739444743653070e+06, + "cpu_time": 8.7291653205127586e+06, + "time_unit": "ns", + "execution_us": 8.7738903205128190e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 78, + "real_time": 8.9239579359015729e+06, + "cpu_time": 8.8572377692307718e+06, + "time_unit": "ns", + "execution_us": 8.9238945512820519e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 78, + "real_time": 8.8748005127854403e+06, + "cpu_time": 8.8126808461538162e+06, + "time_unit": "ns", + "execution_us": 8.8747350769230779e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_mean", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.8575676410174407e+06, + "cpu_time": 8.7996946452991161e+06, + "time_unit": "ns", + "execution_us": 8.8575066495726496e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_median", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.8748005127854403e+06, + "cpu_time": 8.8126808461538162e+06, + "time_unit": "ns", + "execution_us": 8.8747350769230779e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_stddev", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.6477045984919881e+04, + "cpu_time": 6.5016298646360054e+04, + "time_unit": "ns", + "execution_us": 7.6471768280086707e+01, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_cv", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.6340911054149389e-03, + "cpu_time": 7.3884721308019943e-03, + "time_unit": "ns", + "execution_us": 8.6335547130270869e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 113, + "real_time": 6.2648074336213972e+06, + "cpu_time": 6.2272059203540171e+06, + "time_unit": "ns", + "execution_us": 6.2647564247787614e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.1452629999999999e+03, + "plan_cost": 8.4132300000000000e+05 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 113, + "real_time": 6.3246390177040007e+06, + "cpu_time": 6.2809157699115071e+06, + "time_unit": "ns", + "execution_us": 6.3245860000000002e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.0695810000000001e+03, + "plan_cost": 8.4132300000000000e+05 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 113, + "real_time": 6.2407934070764156e+06, + "cpu_time": 6.2087686106194602e+06, + "time_unit": "ns", + "execution_us": 6.2407438938053101e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 7.8922929999999997e+03, + "plan_cost": 8.4132300000000000e+05 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_mean", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.2767466194672706e+06, + "cpu_time": 6.2389634336283281e+06, + "time_unit": "ns", + "execution_us": 6.2766954395280236e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.0357123333333338e+03, + "plan_cost": 8.4132300000000000e+05 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_median", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.2648074336213982e+06, + "cpu_time": 6.2272059203540171e+06, + "time_unit": "ns", + "execution_us": 6.2647564247787614e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.0695810000000001e+03, + "plan_cost": 8.4132300000000000e+05 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_stddev", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.3179042652491240e+04, + "cpu_time": 3.7483096020353565e+04, + "time_unit": "ns", + "execution_us": 4.3177305933555296e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.2984132686206229e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_cv", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.8792075370020253e-03, + "cpu_time": 6.0079044250071715e-03, + "time_unit": "ns", + "execution_us": 6.8789869366040193e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.6158035713083094e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 18, + "real_time": 3.7342755611057833e+07, + "cpu_time": 3.6945452277777635e+07, + "time_unit": "ns", + "execution_us": 3.7342568388888889e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 18, + "real_time": 3.7229172110932678e+07, + "cpu_time": 3.6959886055555552e+07, + "time_unit": "ns", + "execution_us": 3.7229003333333334e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 18, + "real_time": 3.7165874166627571e+07, + "cpu_time": 3.6779724500000156e+07, + "time_unit": "ns", + "execution_us": 3.7165154833333334e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_mean", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7245933962872691e+07, + "cpu_time": 3.6895020944444448e+07, + "time_unit": "ns", + "execution_us": 3.7245575518518519e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_median", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7229172110932671e+07, + "cpu_time": 3.6945452277777627e+07, + "time_unit": "ns", + "execution_us": 3.7229003333333334e+04, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_stddev", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.9624110071916934e+04, + "cpu_time": 1.0011011967390560e+05, + "time_unit": "ns", + "execution_us": 8.9860282744119473e+01, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_cv", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.4062790360219076e-03, + "cpu_time": 2.7133774994910227e-03, + "time_unit": "ns", + "execution_us": 2.4126431527267149e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 117, + "real_time": 5.9173903247673847e+06, + "cpu_time": 5.8888248376068221e+06, + "time_unit": "ns", + "execution_us": 5.9173418461538467e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.4953026999999998e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 117, + "real_time": 5.9332453589672502e+06, + "cpu_time": 5.8993699743589880e+06, + "time_unit": "ns", + "execution_us": 5.9331977350427351e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.4968883999999998e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 117, + "real_time": 5.8938555470003616e+06, + "cpu_time": 5.8657791965811811e+06, + "time_unit": "ns", + "execution_us": 5.8938118803418802e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5167903999999999e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_mean", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9148304102449985e+06, + "cpu_time": 5.8846580028489968e+06, + "time_unit": "ns", + "execution_us": 5.9147838205128210e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5029938333333328e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_median", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9173903247673847e+06, + "cpu_time": 5.8888248376068212e+06, + "time_unit": "ns", + "execution_us": 5.9173418461538467e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.4968883999999998e+04, + "plan_cost": 8.1637400000000000e+05 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_stddev", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9819288419988934e+04, + "cpu_time": 1.7178677826489915e+04, + "time_unit": "ns", + "execution_us": 1.9817139273891375e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.1974454057016273e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_cv", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.3507788128058946e-03, + "cpu_time": 2.9192312991125459e-03, + "time_unit": "ns", + "execution_us": 3.3504418547241508e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 4.7840525603970157e-03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 76, + "real_time": 9.1387127236693650e+06, + "cpu_time": 9.0868170394736659e+06, + "time_unit": "ns", + "execution_us": 9.1386464999999989e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 76, + "real_time": 9.1050921447324641e+06, + "cpu_time": 9.0507583157894276e+06, + "time_unit": "ns", + "execution_us": 9.1050355394736835e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 76, + "real_time": 9.2715982631201968e+06, + "cpu_time": 9.1604126315789502e+06, + "time_unit": "ns", + "execution_us": 9.2715299473684208e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_mean", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.1718010438406747e+06, + "cpu_time": 9.0993293289473448e+06, + "time_unit": "ns", + "execution_us": 9.1717373289473671e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_median", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.1387127236693650e+06, + "cpu_time": 9.0868170394736659e+06, + "time_unit": "ns", + "execution_us": 9.1386464999999989e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_stddev", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.8046576105792265e+04, + "cpu_time": 5.5887702437635016e+04, + "time_unit": "ns", + "execution_us": 8.8041746832879284e+01, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_cv", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 9.5997041022733449e-03, + "cpu_time": 6.1419584254239074e-03, + "time_unit": "ns", + "execution_us": 9.5992442516868032e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 98, + "real_time": 7.2647804796101162e+06, + "cpu_time": 7.2131055816327147e+06, + "time_unit": "ns", + "execution_us": 7.2646960816326537e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.7666830269999998e+06, + "plan_cost": 8.4282300000000000e+05 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 98, + "real_time": 7.0410532755014012e+06, + "cpu_time": 6.9883607653061263e+06, + "time_unit": "ns", + "execution_us": 7.0409964795918368e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.7000544929999998e+06, + "plan_cost": 8.4282300000000000e+05 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 98, + "real_time": 7.1415873877338860e+06, + "cpu_time": 7.0944541224489762e+06, + "time_unit": "ns", + "execution_us": 7.1415274897959189e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.7497210860000001e+06, + "plan_cost": 8.4282300000000000e+05 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_mean", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.1491403809484662e+06, + "cpu_time": 7.0986401564626051e+06, + "time_unit": "ns", + "execution_us": 7.1490733503401361e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.7388195353333326e+06, + "plan_cost": 8.4282300000000000e+05 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_median", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.1415873877338851e+06, + "cpu_time": 7.0944541224489762e+06, + "time_unit": "ns", + "execution_us": 7.1415274897959189e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.7497210860000001e+06, + "plan_cost": 8.4282300000000000e+05 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_stddev", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1205467970793806e+05, + "cpu_time": 1.1243086887949352e+05, + "time_unit": "ns", + "execution_us": 1.1204054175534115e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 3.4626192989686067e+04, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_cv", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.5673867589248811e-02, + "cpu_time": 1.5838367124038032e-02, + "time_unit": "ns", + "execution_us": 1.5672036957070881e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 9.2612635251460417e-03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 76, + "real_time": 9.0425540394885745e+06, + "cpu_time": 8.9781499736841619e+06, + "time_unit": "ns", + "execution_us": 9.0424679999999989e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 76, + "real_time": 9.1915130789474398e+06, + "cpu_time": 9.1151476842104122e+06, + "time_unit": "ns", + "execution_us": 9.1914571052631582e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 76, + "real_time": 9.0993060131567251e+06, + "cpu_time": 9.0332166447369102e+06, + "time_unit": "ns", + "execution_us": 9.0992465526315791e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_mean", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.1111243771975804e+06, + "cpu_time": 9.0421714342104942e+06, + "time_unit": "ns", + "execution_us": 9.1110572192982454e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_median", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.0993060131567232e+06, + "cpu_time": 9.0332166447369084e+06, + "time_unit": "ns", + "execution_us": 9.0992465526315791e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_stddev", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.5179479617513076e+04, + "cpu_time": 6.8936451629408257e+04, + "time_unit": "ns", + "execution_us": 7.5193465521336293e+01, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_cv", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.2513942851734998e-03, + "cpu_time": 7.6238823971630830e-03, + "time_unit": "ns", + "execution_us": 8.2529901537735997e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 71, + "real_time": 9.6118449718502015e+06, + "cpu_time": 9.5573261408451591e+06, + "time_unit": "ns", + "execution_us": 9.6117805774647877e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 1.0109361800000000e+05, + "plan_cost": 8.4279300000000000e+05 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 71, + "real_time": 9.5582830986195784e+06, + "cpu_time": 9.4922426619718522e+06, + "time_unit": "ns", + "execution_us": 9.5582168169014076e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 9.9442232000000004e+04, + "plan_cost": 8.4279300000000000e+05 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 71, + "real_time": 9.3946305633822624e+06, + "cpu_time": 9.3367950845069978e+06, + "time_unit": "ns", + "execution_us": 9.3945698732394376e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 1.0041698500000000e+05, + "plan_cost": 8.4279300000000000e+05 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_mean", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.5215862112840135e+06, + "cpu_time": 9.4621212957746703e+06, + "time_unit": "ns", + "execution_us": 9.5215224225352104e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 1.0031761166666666e+05, + "plan_cost": 8.4279300000000000e+05 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_median", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.5582830986195784e+06, + "cpu_time": 9.4922426619718522e+06, + "time_unit": "ns", + "execution_us": 9.5582168169014076e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 1.0041698500000000e+05, + "plan_cost": 8.4279300000000000e+05 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_stddev", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1316148181518832e+05, + "cpu_time": 1.1330913126753959e+05, + "time_unit": "ns", + "execution_us": 1.1315909791507667e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.3016578150683313e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_cv", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1884730054860067e-02, + "cpu_time": 1.1975024175407476e-02, + "time_unit": "ns", + "execution_us": 1.1884559306110085e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.2753742609552074e-03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 80, + "real_time": 8.5355717249967717e+06, + "cpu_time": 8.4840434624998905e+06, + "time_unit": "ns", + "execution_us": 8.5349807499999988e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 80, + "real_time": 8.7283317875062488e+06, + "cpu_time": 8.6733479500001203e+06, + "time_unit": "ns", + "execution_us": 8.7282722749999994e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 80, + "real_time": 8.6581378375285566e+06, + "cpu_time": 8.5737115625001080e+06, + "time_unit": "ns", + "execution_us": 8.6580729000000010e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_mean", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.6406804500105251e+06, + "cpu_time": 8.5770343250000384e+06, + "time_unit": "ns", + "execution_us": 8.6404419749999979e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_median", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.6581378375285566e+06, + "cpu_time": 8.5737115625001080e+06, + "time_unit": "ns", + "execution_us": 8.6580729000000010e+03, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_stddev", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.7558601409023788e+04, + "cpu_time": 9.4695975679618903e+04, + "time_unit": "ns", + "execution_us": 9.7844471210802425e+01, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_cv", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.1290615591380299e-02, + "cpu_time": 1.1040643198034361e-02, + "time_unit": "ns", + "execution_us": 1.1324012300979829e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 114, + "real_time": 6.0918677368215611e+06, + "cpu_time": 6.0557457368420251e+06, + "time_unit": "ns", + "execution_us": 6.0918212894736844e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.4798770000000004e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 114, + "real_time": 6.0794830087775560e+06, + "cpu_time": 6.0390325350876907e+06, + "time_unit": "ns", + "execution_us": 6.0794388245614027e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.5196109999999999e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 114, + "real_time": 6.0435537719361791e+06, + "cpu_time": 6.0123747456140062e+06, + "time_unit": "ns", + "execution_us": 6.0435049736842111e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.6384939999999997e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_mean", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.0716348391784308e+06, + "cpu_time": 6.0357176725145727e+06, + "time_unit": "ns", + "execution_us": 6.0715883625730985e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.5459939999999997e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_median", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.0794830087775551e+06, + "cpu_time": 6.0390325350876898e+06, + "time_unit": "ns", + "execution_us": 6.0794388245614027e+03, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.5196109999999999e+03, + "plan_cost": 8.3622300000000000e+05 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_stddev", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.5094922300718245e+04, + "cpu_time": 2.1874687550823292e+04, + "time_unit": "ns", + "execution_us": 2.5096591560550149e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.2534145594462700e+01, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_cv", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.1331409028073076e-03, + "cpu_time": 3.6242065546631111e-03, + "time_unit": "ns", + "execution_us": 4.1334474707231932e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.8155357352971146e-02, + "plan_cost": 0.0000000000000000e+00 + } + ] +} diff --git a/research/benchmark-results/ssb-sf01.json b/research/benchmark-results/ssb-sf01.json new file mode 100644 index 0000000..ff44585 --- /dev/null +++ b/research/benchmark-results/ssb-sf01.json @@ -0,0 +1,3240 @@ +{ + "context": { + "date": "2026-06-15T17:51:46+03:00", + "host_name": "nixos", + "executable": "./build-release/bin/benchmarks", + "num_cpus": 8, + "mhz_per_cpu": 4200, + "cpu_scaling_enabled": true, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 49152, + "num_sharing": 2 + }, + { + "type": "Instruction", + "level": 1, + "size": 32768, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 2, + "size": 1310720, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 3, + "size": 8388608, + "num_sharing": 8 + } + ], + "load_avg": [0.994629,0.942871,0.696289], + "library_version": "v1.9.0", + "library_build_type": "release", + "json_schema_version": 1 + }, + "benchmarks": [ + { + "name": "SSB/q2.1/Interpreted/Naive/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2, + "real_time": 3.0132044150013828e+08, + "cpu_time": 3.0055295099999994e+08, + "time_unit": "ns", + "execution_us": 3.0131933950000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2, + "real_time": 3.0173666499831599e+08, + "cpu_time": 3.0100849399999982e+08, + "time_unit": "ns", + "execution_us": 3.0173572450000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2, + "real_time": 3.0096618300012779e+08, + "cpu_time": 3.0044530150000018e+08, + "time_unit": "ns", + "execution_us": 3.0096520899999997e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_mean", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0134109649952734e+08, + "cpu_time": 3.0066891549999994e+08, + "time_unit": "ns", + "execution_us": 3.0134009100000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_median", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0132044150013822e+08, + "cpu_time": 3.0055295099999994e+08, + "time_unit": "ns", + "execution_us": 3.0131933950000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_stddev", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.8565606325874460e+05, + "cpu_time": 2.9896868059997144e+05, + "time_unit": "ns", + "execution_us": 3.8567668125554911e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Naive/real_time_cv", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.2797990972311655e-03, + "cpu_time": 9.9434515903580817e-04, + "time_unit": "ns", + "execution_us": 1.2798717886348188e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 11, + "real_time": 6.6300351000046991e+07, + "cpu_time": 6.5752068545454495e+07, + "time_unit": "ns", + "execution_us": 6.6300112090909097e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.3765069999999996e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 11, + "real_time": 6.5030775545561813e+07, + "cpu_time": 6.4883817545454472e+07, + "time_unit": "ns", + "execution_us": 6.5030539727272728e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 5.0194009999999998e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 11, + "real_time": 6.3826902363375254e+07, + "cpu_time": 6.3701150545454577e+07, + "time_unit": "ns", + "execution_us": 6.3826689636363641e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.4800919999999996e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_mean", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.5052676302994676e+07, + "cpu_time": 6.4779012212121166e+07, + "time_unit": "ns", + "execution_us": 6.5052447151515153e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.6253333333333330e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_median", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.5030775545561813e+07, + "cpu_time": 6.4883817545454465e+07, + "time_unit": "ns", + "execution_us": 6.5030539727272728e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.4800919999999996e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_stddev", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2368697473645334e+06, + "cpu_time": 1.0294679592404409e+06, + "time_unit": "ns", + "execution_us": 1.2368567463898894e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 3.4518032604760992e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.1/Interpreted/Optimized/real_time_cv", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "SSB/q2.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.9013356831063911e-02, + "cpu_time": 1.5891998412532311e-02, + "time_unit": "ns", + "execution_us": 1.9013223953114292e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 7.4628205400895778e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 4, + "real_time": 1.6835229600110325e+08, + "cpu_time": 1.6807579225000003e+08, + "time_unit": "ns", + "execution_us": 1.6835176475000000e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 4, + "real_time": 1.6602187325042906e+08, + "cpu_time": 1.6574644450000009e+08, + "time_unit": "ns", + "execution_us": 1.6602126324999999e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 4, + "real_time": 1.6629480125084227e+08, + "cpu_time": 1.6603537775000009e+08, + "time_unit": "ns", + "execution_us": 1.6629423624999999e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_mean", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6688965683412486e+08, + "cpu_time": 1.6661920483333340e+08, + "time_unit": "ns", + "execution_us": 1.6688908808333334e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_median", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6629480125084230e+08, + "cpu_time": 1.6603537775000009e+08, + "time_unit": "ns", + "execution_us": 1.6629423624999999e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_stddev", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2740123323341666e+06, + "cpu_time": 1.2696872774559760e+06, + "time_unit": "ns", + "execution_us": 1.2740470313451747e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Naive/real_time_cv", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 7.6338603392326126e-03, + "cpu_time": 7.6202937033940620e-03, + "time_unit": "ns", + "execution_us": 7.6340942716937856e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 11, + "real_time": 6.3132156454561144e+07, + "cpu_time": 6.3026987363636397e+07, + "time_unit": "ns", + "execution_us": 6.3131871818181819e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.5031120000000001e+03, + "plan_cost": 6.5305070000000000e+06 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 11, + "real_time": 6.5914543272810988e+07, + "cpu_time": 6.5730933818181701e+07, + "time_unit": "ns", + "execution_us": 6.5914327363636359e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.4627139999999999e+03, + "plan_cost": 6.5305070000000000e+06 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 11, + "real_time": 6.3453278999738604e+07, + "cpu_time": 6.3343623999999978e+07, + "time_unit": "ns", + "execution_us": 6.3453044727272725e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.4836089999999999e+03, + "plan_cost": 6.5305070000000000e+06 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_mean", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.4166659575703569e+07, + "cpu_time": 6.4033848393939346e+07, + "time_unit": "ns", + "execution_us": 6.4166414636363632e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.4831449999999998e+03, + "plan_cost": 6.5305070000000000e+06 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_median", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3453278999738604e+07, + "cpu_time": 6.3343623999999978e+07, + "time_unit": "ns", + "execution_us": 6.3453044727272725e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.4836089999999999e+03, + "plan_cost": 6.5305070000000000e+06 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_stddev", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5222033327443090e+06, + "cpu_time": 1.4782215303379109e+06, + "time_unit": "ns", + "execution_us": 1.5222309897005246e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.0202996634195987e+01, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.3/Interpreted/Optimized/real_time_cv", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "SSB/q1.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.3722651963024810e-02, + "cpu_time": 2.3085002188902033e-02, + "time_unit": "ns", + "execution_us": 2.3723173537545043e-02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.3621727231117653e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 4, + "real_time": 1.6996481874957681e+08, + "cpu_time": 1.6953964400000033e+08, + "time_unit": "ns", + "execution_us": 1.6996439600000001e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 4, + "real_time": 1.6641380549845052e+08, + "cpu_time": 1.6610005074999988e+08, + "time_unit": "ns", + "execution_us": 1.6641331250000000e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 4, + "real_time": 1.6653429200050595e+08, + "cpu_time": 1.6626286925000012e+08, + "time_unit": "ns", + "execution_us": 1.6653372550000000e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_mean", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6763763874951109e+08, + "cpu_time": 1.6730085466666678e+08, + "time_unit": "ns", + "execution_us": 1.6763714466666666e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_median", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6653429200050595e+08, + "cpu_time": 1.6626286925000012e+08, + "time_unit": "ns", + "execution_us": 1.6653372550000000e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_stddev", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.0162971789446652e+06, + "cpu_time": 1.9405568080502811e+06, + "time_unit": "ns", + "execution_us": 2.0163578301029145e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Naive/real_time_cv", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.2027711640328420e-02, + "cpu_time": 1.1599204390896156e-02, + "time_unit": "ns", + "execution_us": 1.2028108890259877e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 11, + "real_time": 6.3506060363910034e+07, + "cpu_time": 6.3394548454545423e+07, + "time_unit": "ns", + "execution_us": 6.3505853363636365e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.7491200000000003e+02, + "plan_cost": 6.5323070000000000e+06 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 11, + "real_time": 6.3108547727593258e+07, + "cpu_time": 6.3004551090909027e+07, + "time_unit": "ns", + "execution_us": 6.3108312000000005e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.6265099999999995e+02, + "plan_cost": 6.5323070000000000e+06 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 11, + "real_time": 6.3132328454891898e+07, + "cpu_time": 6.3026034545454383e+07, + "time_unit": "ns", + "execution_us": 6.3132119181818183e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.5707600000000002e+02, + "plan_cost": 6.5323070000000000e+06 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_mean", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3248978848798387e+07, + "cpu_time": 6.3141711363636263e+07, + "time_unit": "ns", + "execution_us": 6.3248761515151513e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.6487966666666671e+02, + "plan_cost": 6.5323070000000000e+06 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_median", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3132328454891890e+07, + "cpu_time": 6.3026034545454375e+07, + "time_unit": "ns", + "execution_us": 6.3132119181818183e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 8.6265099999999995e+02, + "plan_cost": 6.5323070000000000e+06 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_stddev", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.2295640786477245e+05, + "cpu_time": 2.1922666491679070e+05, + "time_unit": "ns", + "execution_us": 2.2296604987195695e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 9.1244693178936931e+00, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.2/Interpreted/Optimized/real_time_cv", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "SSB/q1.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 3.5250594068525140e-03, + "cpu_time": 3.4719785096456036e-03, + "time_unit": "ns", + "execution_us": 3.5252239653506016e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.0549987090181361e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1, + "real_time": 1.5763709120001295e+09, + "cpu_time": 1.5739780290000026e+09, + "time_unit": "ns", + "execution_us": 1.5763686769999999e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1, + "real_time": 1.5951611780037637e+09, + "cpu_time": 1.5870406919999986e+09, + "time_unit": "ns", + "execution_us": 1.5951587109999999e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1, + "real_time": 1.5779306569966137e+09, + "cpu_time": 1.5757620909999979e+09, + "time_unit": "ns", + "execution_us": 1.5779279870000000e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_mean", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5831542490001690e+09, + "cpu_time": 1.5789269373333330e+09, + "time_unit": "ns", + "execution_us": 1.5831517916666665e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_median", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5779306569966137e+09, + "cpu_time": 1.5757620909999979e+09, + "time_unit": "ns", + "execution_us": 1.5779279870000000e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_stddev", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0427509730889013e+07, + "cpu_time": 7.0831123383810744e+06, + "time_unit": "ns", + "execution_us": 1.0427485115149297e+04, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Naive/real_time_cv", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.5865405960754868e-03, + "cpu_time": 4.4860291954634838e-03, + "time_unit": "ns", + "execution_us": 6.5865352709936547e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 13, + "real_time": 5.4263174384966701e+07, + "cpu_time": 5.4176567230769292e+07, + "time_unit": "ns", + "execution_us": 5.4262966923076921e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.2146563000000002e+04, + "plan_cost": 8.1735020000000000e+06 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 13, + "real_time": 5.4822232768996254e+07, + "cpu_time": 5.4457524230769284e+07, + "time_unit": "ns", + "execution_us": 5.4821983923076921e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.0021783000000003e+04, + "plan_cost": 8.1735020000000000e+06 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 13, + "real_time": 5.4119724692449935e+07, + "cpu_time": 5.4026307923077062e+07, + "time_unit": "ns", + "execution_us": 5.4119498538461543e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.0062478000000003e+04, + "plan_cost": 8.1735020000000000e+06 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_mean", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.4401710615470976e+07, + "cpu_time": 5.4220133128205217e+07, + "time_unit": "ns", + "execution_us": 5.4401483128205124e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.0743608000000007e+04, + "plan_cost": 8.1735020000000000e+06 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_median", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.4263174384966709e+07, + "cpu_time": 5.4176567230769299e+07, + "time_unit": "ns", + "execution_us": 5.4262966923076921e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 6.0062478000000003e+04, + "plan_cost": 8.1735020000000000e+06 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_stddev", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7117868308285315e+05, + "cpu_time": 2.1888436803367294e+05, + "time_unit": "ns", + "execution_us": 3.7116234077954061e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.2151650380808983e+03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.3/Interpreted/Optimized/real_time_cv", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "SSB/q3.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.8229230089190598e-03, + "cpu_time": 4.0369574068753016e-03, + "time_unit": "ns", + "execution_us": 6.8226511381103668e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.0004821545682602e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1, + "real_time": 1.5819988250004826e+09, + "cpu_time": 1.5796358200000000e+09, + "time_unit": "ns", + "execution_us": 1.5819962500000000e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1, + "real_time": 1.5888754689949565e+09, + "cpu_time": 1.5866314660000000e+09, + "time_unit": "ns", + "execution_us": 1.5888732890000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1, + "real_time": 1.5904124320004485e+09, + "cpu_time": 1.5808928719999998e+09, + "time_unit": "ns", + "execution_us": 1.5904103160000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_mean", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5870955753319626e+09, + "cpu_time": 1.5823867193333333e+09, + "time_unit": "ns", + "execution_us": 1.5870932850000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_median", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5888754689949563e+09, + "cpu_time": 1.5808928719999998e+09, + "time_unit": "ns", + "execution_us": 1.5888732890000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_stddev", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 4.4803138033816479e+06, + "cpu_time": 3.7294035220849733e+06, + "time_unit": "ns", + "execution_us": 4.4805621681357461e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Naive/real_time_cv", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.8229640816965483e-03, + "cpu_time": 2.3568218037473090e-03, + "time_unit": "ns", + "execution_us": 2.8231246458431999e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 13, + "real_time": 5.4271727000013933e+07, + "cpu_time": 5.4172577615384169e+07, + "time_unit": "ns", + "execution_us": 5.4271531692307690e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.3050309999999998e+04, + "plan_cost": 8.1730440000000000e+06 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 13, + "real_time": 5.4145620692557149e+07, + "cpu_time": 5.4056849000000045e+07, + "time_unit": "ns", + "execution_us": 5.4145442000000003e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.1143557000000001e+04, + "plan_cost": 8.1730440000000000e+06 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 13, + "real_time": 5.4057425922879845e+07, + "cpu_time": 5.3956011307692416e+07, + "time_unit": "ns", + "execution_us": 5.4057194230769230e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.0936512999999999e+04, + "plan_cost": 8.1730440000000000e+06 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_mean", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.4158257871816970e+07, + "cpu_time": 5.4061812641025543e+07, + "time_unit": "ns", + "execution_us": 5.4158055974358969e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.1710126666666663e+04, + "plan_cost": 8.1730440000000000e+06 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_median", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.4145620692557156e+07, + "cpu_time": 5.4056849000000060e+07, + "time_unit": "ns", + "execution_us": 5.4145442000000003e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.1143557000000001e+04, + "plan_cost": 8.1730440000000000e+06 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_stddev", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0770799246103613e+05, + "cpu_time": 1.0836844423885930e+05, + "time_unit": "ns", + "execution_us": 1.0772405078816776e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.1652404600308216e+03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.4/Interpreted/Optimized/real_time_cv", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "SSB/q3.4/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.9887639797417766e-03, + "cpu_time": 2.0045285007077702e-03, + "time_unit": "ns", + "execution_us": 1.9890679022742160e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.7936632016081667e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2, + "real_time": 3.5241248099919176e+08, + "cpu_time": 3.5189126650000089e+08, + "time_unit": "ns", + "execution_us": 3.5241135600000003e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2, + "real_time": 3.5169160749865115e+08, + "cpu_time": 3.5110009350000125e+08, + "time_unit": "ns", + "execution_us": 3.5169059200000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2, + "real_time": 3.4873572150172549e+08, + "cpu_time": 3.4818190100000024e+08, + "time_unit": "ns", + "execution_us": 3.4873472200000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_mean", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5094660333318943e+08, + "cpu_time": 3.5039108700000077e+08, + "time_unit": "ns", + "execution_us": 3.5094555666666670e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_median", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5169160749865115e+08, + "cpu_time": 3.5110009350000125e+08, + "time_unit": "ns", + "execution_us": 3.5169059200000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_stddev", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.9483104232848715e+06, + "cpu_time": 1.9536800572068950e+06, + "time_unit": "ns", + "execution_us": 1.9482601541875974e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Naive/real_time_cv", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 5.5515864942996514e-03, + "cpu_time": 5.5757127669371640e-03, + "time_unit": "ns", + "execution_us": 5.5514598124349066e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 12, + "real_time": 5.9645183333486788e+07, + "cpu_time": 5.9522327583333492e+07, + "time_unit": "ns", + "execution_us": 5.9644964416666662e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5695851000000001e+05, + "plan_cost": 8.4052230000000000e+06 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 12, + "real_time": 5.9652490916657068e+07, + "cpu_time": 5.9542881250000216e+07, + "time_unit": "ns", + "execution_us": 5.9652292750000001e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5590823499999999e+05, + "plan_cost": 8.4052230000000000e+06 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 12, + "real_time": 6.0287974500170089e+07, + "cpu_time": 5.9551037000000305e+07, + "time_unit": "ns", + "execution_us": 6.0287758916666666e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5670725500000000e+05, + "plan_cost": 8.4052230000000000e+06 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_mean", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9861882916771315e+07, + "cpu_time": 5.9538748611111343e+07, + "time_unit": "ns", + "execution_us": 5.9861672027777771e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5652466666666666e+05, + "plan_cost": 8.4052230000000000e+06 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_median", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.9652490916657068e+07, + "cpu_time": 5.9542881250000216e+07, + "time_unit": "ns", + "execution_us": 5.9652292750000001e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5670725500000000e+05, + "plan_cost": 8.4052230000000000e+06 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_stddev", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.6902422450914892e+05, + "cpu_time": 1.4794143376796630e+04, + "time_unit": "ns", + "execution_us": 3.6902026201846189e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 5.4842799756808597e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.3/Interpreted/Optimized/real_time_cv", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "SSB/q4.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.1645943383074010e-03, + "cpu_time": 2.4847924623722599e-04, + "time_unit": "ns", + "execution_us": 6.1645498616748366e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.1379152527297674e-03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 4, + "real_time": 1.7256533999898237e+08, + "cpu_time": 1.7227216825000015e+08, + "time_unit": "ns", + "execution_us": 1.7256479375000001e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 4, + "real_time": 1.7304333700121787e+08, + "cpu_time": 1.7278107275000033e+08, + "time_unit": "ns", + "execution_us": 1.7304267400000000e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 4, + "real_time": 1.7286145250000119e+08, + "cpu_time": 1.7259499649999911e+08, + "time_unit": "ns", + "execution_us": 1.7286093799999999e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_mean", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.7282337650006714e+08, + "cpu_time": 1.7254941249999985e+08, + "time_unit": "ns", + "execution_us": 1.7282280191666668e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_median", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.7286145250000116e+08, + "cpu_time": 1.7259499649999911e+08, + "time_unit": "ns", + "execution_us": 1.7286093799999999e+05, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_stddev", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4126255381868556e+05, + "cpu_time": 2.5749635595540810e+05, + "time_unit": "ns", + "execution_us": 2.4121184457857026e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Naive/real_time_cv", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.3960064819043263e-03, + "cpu_time": 1.4923050285981609e-03, + "time_unit": "ns", + "execution_us": 1.3957177056698807e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 11, + "real_time": 6.1513489272990473e+07, + "cpu_time": 6.1417641818181321e+07, + "time_unit": "ns", + "execution_us": 6.1513300000000003e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6536199999999997e+02, + "plan_cost": 7.0400420000000000e+06 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 11, + "real_time": 6.1589314636710860e+07, + "cpu_time": 6.1496196454545386e+07, + "time_unit": "ns", + "execution_us": 6.1589097181818186e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.5630899999999997e+02, + "plan_cost": 7.0400420000000000e+06 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 11, + "real_time": 6.1422892454588287e+07, + "cpu_time": 6.1323304272727206e+07, + "time_unit": "ns", + "execution_us": 6.1422677090909092e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6858399999999995e+02, + "plan_cost": 7.0400420000000000e+06 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_mean", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.1508565454763196e+07, + "cpu_time": 6.1412380848484635e+07, + "time_unit": "ns", + "execution_us": 6.1508358090909089e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6341833333333318e+02, + "plan_cost": 7.0400420000000000e+06 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_median", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.1513489272990465e+07, + "cpu_time": 6.1417641818181328e+07, + "time_unit": "ns", + "execution_us": 6.1513300000000003e+04, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6536199999999997e+02, + "plan_cost": 7.0400420000000000e+06 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_stddev", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 8.3320277637441162e+04, + "cpu_time": 8.6566072945526728e+04, + "time_unit": "ns", + "execution_us": 8.3320036695321633e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.3641406594664716e+00, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q1.1/Interpreted/Optimized/real_time_cv", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "SSB/q1.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.3546125977969609e-03, + "cpu_time": 1.4095866623230382e-03, + "time_unit": "ns", + "execution_us": 1.3546132473927360e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 9.5929526509916669e-03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1, + "real_time": 1.5893743029955659e+09, + "cpu_time": 1.5870318680000041e+09, + "time_unit": "ns", + "execution_us": 1.5893720660000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1, + "real_time": 1.5899839299963787e+09, + "cpu_time": 1.5876547589999959e+09, + "time_unit": "ns", + "execution_us": 1.5899814920000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1, + "real_time": 1.5907088900057716e+09, + "cpu_time": 1.5884123220000036e+09, + "time_unit": "ns", + "execution_us": 1.5907066880000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_mean", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5900223743325720e+09, + "cpu_time": 1.5876996496666677e+09, + "time_unit": "ns", + "execution_us": 1.5900200819999999e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_median", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5899839299963787e+09, + "cpu_time": 1.5876547589999959e+09, + "time_unit": "ns", + "execution_us": 1.5899814920000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_stddev", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.6812356468380569e+05, + "cpu_time": 6.9132097501057561e+05, + "time_unit": "ns", + "execution_us": 6.6814733577609024e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Naive/real_time_cv", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.2019758681965551e-04, + "cpu_time": 4.3542301918106244e-04, + "time_unit": "ns", + "execution_us": 4.2021314280236261e-04, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 12, + "real_time": 5.6016307000390954e+07, + "cpu_time": 5.5919624833333492e+07, + "time_unit": "ns", + "execution_us": 5.6016071999999993e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.3391750000000000e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 12, + "real_time": 5.6160404916226983e+07, + "cpu_time": 5.6047049750000380e+07, + "time_unit": "ns", + "execution_us": 5.6160177166666668e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.3681062000000002e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 12, + "real_time": 5.5984875833625369e+07, + "cpu_time": 5.5872650916666567e+07, + "time_unit": "ns", + "execution_us": 5.5984649500000000e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.3643873000000000e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_mean", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.6053862583414428e+07, + "cpu_time": 5.5946441833333470e+07, + "time_unit": "ns", + "execution_us": 5.6053632888888882e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.3572228333333333e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_median", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.6016307000390954e+07, + "cpu_time": 5.5919624833333492e+07, + "time_unit": "ns", + "execution_us": 5.6016071999999993e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.3643873000000000e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_stddev", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 9.3597174490967431e+04, + "cpu_time": 9.0239137212874077e+04, + "time_unit": "ns", + "execution_us": 9.3598107471192762e+01, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.5740100708784567e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.2/Interpreted/Optimized/real_time_cv", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "SSB/q3.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.6697720759507760e-03, + "cpu_time": 1.6129557887112789e-03, + "time_unit": "ns", + "execution_us": 1.6697955627019861e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 6.6773919233280949e-03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2, + "real_time": 3.0116734100010943e+08, + "cpu_time": 3.0071568349999470e+08, + "time_unit": "ns", + "execution_us": 3.0116636050000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2, + "real_time": 3.0130158599786228e+08, + "cpu_time": 2.9894364249999940e+08, + "time_unit": "ns", + "execution_us": 3.0130057250000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2, + "real_time": 2.9904670999894732e+08, + "cpu_time": 2.9861006699999851e+08, + "time_unit": "ns", + "execution_us": 2.9904576400000002e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_mean", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0050521233230633e+08, + "cpu_time": 2.9942313099999750e+08, + "time_unit": "ns", + "execution_us": 3.0050423233333329e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_median", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0116734100010943e+08, + "cpu_time": 2.9894364249999940e+08, + "time_unit": "ns", + "execution_us": 3.0116636050000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_stddev", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2648822957832406e+06, + "cpu_time": 1.1317407510047185e+06, + "time_unit": "ns", + "execution_us": 1.2648520169424914e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Naive/real_time_cv", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 4.2091858772303143e-03, + "cpu_time": 3.7797372141056395e-03, + "time_unit": "ns", + "execution_us": 4.2090988440371072e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 12, + "real_time": 5.8780332416669510e+07, + "cpu_time": 5.8673196416666687e+07, + "time_unit": "ns", + "execution_us": 5.8780129416666663e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 7.7295889999999999e+03, + "plan_cost": 8.4048830000000000e+06 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 12, + "real_time": 5.8804952749900013e+07, + "cpu_time": 5.8705007250000089e+07, + "time_unit": "ns", + "execution_us": 5.8804767833333339e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 7.4991710000000003e+03, + "plan_cost": 8.4048830000000000e+06 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 12, + "real_time": 5.8585657666602254e+07, + "cpu_time": 5.8484857750000186e+07, + "time_unit": "ns", + "execution_us": 5.8585396333333338e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 7.5757399999999998e+03, + "plan_cost": 8.4048830000000000e+06 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_mean", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8723647611057252e+07, + "cpu_time": 5.8621020472222321e+07, + "time_unit": "ns", + "execution_us": 5.8723431194444449e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 7.6015000000000000e+03, + "plan_cost": 8.4048830000000000e+06 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_median", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8780332416669510e+07, + "cpu_time": 5.8673196416666687e+07, + "time_unit": "ns", + "execution_us": 5.8780129416666663e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 7.5757399999999998e+03, + "plan_cost": 8.4048830000000000e+06 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_stddev", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2013516879223859e+05, + "cpu_time": 1.1898822408740532e+05, + "time_unit": "ns", + "execution_us": 1.2017478959573222e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.1734903868801585e+02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.2/Interpreted/Optimized/real_time_cv", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "SSB/q2.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.0457715703889275e-03, + "cpu_time": 2.0297876619836072e-03, + "time_unit": "ns", + "execution_us": 2.0464538115596590e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.5437616087353266e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 1, + "real_time": 1.6100362020006287e+09, + "cpu_time": 1.6076350769999976e+09, + "time_unit": "ns", + "execution_us": 1.6100339399999999e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 1, + "real_time": 1.6541845409956295e+09, + "cpu_time": 1.6481450769999952e+09, + "time_unit": "ns", + "execution_us": 1.6541821020000000e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 1, + "real_time": 1.6256416459946194e+09, + "cpu_time": 1.6215455370000029e+09, + "time_unit": "ns", + "execution_us": 1.6256393810000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_mean", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6299541296636260e+09, + "cpu_time": 1.6257752303333321e+09, + "time_unit": "ns", + "execution_us": 1.6299518076666668e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_median", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6256416459946194e+09, + "cpu_time": 1.6215455370000029e+09, + "time_unit": "ns", + "execution_us": 1.6256393810000001e+06, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_stddev", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.2387878317331016e+07, + "cpu_time": 2.0583555433167849e+07, + "time_unit": "ns", + "execution_us": 2.2387782824781250e+04, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Naive/real_time_cv", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.3735281202024510e-02, + "cpu_time": 1.2660763338698186e-02, + "time_unit": "ns", + "execution_us": 1.3735242182914689e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 11, + "real_time": 6.3512496454429559e+07, + "cpu_time": 6.3341084545453697e+07, + "time_unit": "ns", + "execution_us": 6.3512237181818185e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.5829574000000001e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 11, + "real_time": 6.3377376727161884e+07, + "cpu_time": 6.2939674181818485e+07, + "time_unit": "ns", + "execution_us": 6.3377143636363631e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.4656114000000001e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 11, + "real_time": 6.3273171454387590e+07, + "cpu_time": 6.3050591090909719e+07, + "time_unit": "ns", + "execution_us": 6.3272941272727279e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.3474784000000000e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_mean", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3387681545326330e+07, + "cpu_time": 6.3110449939393960e+07, + "time_unit": "ns", + "execution_us": 6.3387440696969694e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.4653490666666668e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_median", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 6.3377376727161884e+07, + "cpu_time": 6.3050591090909719e+07, + "time_unit": "ns", + "execution_us": 6.3377143636363631e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 2.4656114000000001e+04, + "plan_cost": 8.1671240000000000e+06 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_stddev", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.1999481601353527e+05, + "cpu_time": 2.0729177821532093e+05, + "time_unit": "ns", + "execution_us": 1.1997981130229516e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.1773971918741618e+03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q3.1/Interpreted/Optimized/real_time_cv", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "SSB/q3.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.8930305240416646e-03, + "cpu_time": 3.2845872341963453e-03, + "time_unit": "ns", + "execution_us": 1.8928010025814298e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 4.7757829014699701e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2, + "real_time": 3.5733826149953532e+08, + "cpu_time": 3.5637254399999565e+08, + "time_unit": "ns", + "execution_us": 3.5733725550000003e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2, + "real_time": 3.4999329749916798e+08, + "cpu_time": 3.4937872349999565e+08, + "time_unit": "ns", + "execution_us": 3.4999221100000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2, + "real_time": 3.5522493250027764e+08, + "cpu_time": 3.5440063499999750e+08, + "time_unit": "ns", + "execution_us": 3.5522369349999999e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_mean", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5418549716632694e+08, + "cpu_time": 3.5338396749999624e+08, + "time_unit": "ns", + "execution_us": 3.5418438666666672e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_median", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5522493250027764e+08, + "cpu_time": 3.5440063499999756e+08, + "time_unit": "ns", + "execution_us": 3.5522369349999999e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_stddev", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.7811960279498813e+06, + "cpu_time": 3.6060492094187345e+06, + "time_unit": "ns", + "execution_us": 3.7812086291369365e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Naive/real_time_cv", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 1.0675750583243718e-02, + "cpu_time": 1.0204337324439523e-02, + "time_unit": "ns", + "execution_us": 1.0675819633730897e-02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 4, + "real_time": 1.6235981549834833e+08, + "cpu_time": 1.6207399474999917e+08, + "time_unit": "ns", + "execution_us": 1.6235926550000001e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.4857716899999999e+06, + "plan_cost": 8.4211030000000000e+06 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 4, + "real_time": 1.6256094900018069e+08, + "cpu_time": 1.6222403449999943e+08, + "time_unit": "ns", + "execution_us": 1.6256034599999999e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.3536921800000002e+06, + "plan_cost": 8.4211030000000000e+06 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 4, + "real_time": 1.6506758575087589e+08, + "cpu_time": 1.6468362775000146e+08, + "time_unit": "ns", + "execution_us": 1.6506698650000000e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.3569620880000000e+06, + "plan_cost": 8.4211030000000000e+06 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_mean", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6332945008313498e+08, + "cpu_time": 1.6299388566666666e+08, + "time_unit": "ns", + "execution_us": 1.6332886599999998e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.3988086526666665e+06, + "plan_cost": 8.4211030000000000e+06 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_median", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6256094900018069e+08, + "cpu_time": 1.6222403449999943e+08, + "time_unit": "ns", + "execution_us": 1.6256034599999999e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 3.3569620880000000e+06, + "plan_cost": 8.4211030000000000e+06 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_stddev", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.5086253249233707e+06, + "cpu_time": 1.4652812742932725e+06, + "time_unit": "ns", + "execution_us": 1.5086104521252651e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 7.5329944054557054e+04, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.2/Interpreted/Optimized/real_time_cv", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "SSB/q4.2/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 9.2367011837453557e-03, + "cpu_time": 8.9897928888563964e-03, + "time_unit": "ns", + "execution_us": 9.2366431548313397e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 2.2163631952464884e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2, + "real_time": 3.5135333549987990e+08, + "cpu_time": 3.5069528999999732e+08, + "time_unit": "ns", + "execution_us": 3.5135238000000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2, + "real_time": 3.5084772699701715e+08, + "cpu_time": 3.5017025850000036e+08, + "time_unit": "ns", + "execution_us": 3.5084673800000001e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2, + "real_time": 3.5227306849992603e+08, + "cpu_time": 3.5165139699999768e+08, + "time_unit": "ns", + "execution_us": 3.5227209200000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_mean", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5149137699894100e+08, + "cpu_time": 3.5083898183333182e+08, + "time_unit": "ns", + "execution_us": 3.5149040333333332e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_median", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.5135333549987990e+08, + "cpu_time": 3.5069528999999732e+08, + "time_unit": "ns", + "execution_us": 3.5135238000000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_stddev", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 7.2262797591793910e+05, + "cpu_time": 7.5095161047108343e+05, + "time_unit": "ns", + "execution_us": 7.2263153594247910e+02, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Naive/real_time_cv", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.0558910494129028e-03, + "cpu_time": 2.1404451881228736e-03, + "time_unit": "ns", + "execution_us": 2.0559068728177392e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 4, + "real_time": 1.6307732399945962e+08, + "cpu_time": 1.6273075100000155e+08, + "time_unit": "ns", + "execution_us": 1.6307669149999999e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.9473111000000004e+04, + "plan_cost": 8.4221730000000000e+06 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 4, + "real_time": 1.6145620075076294e+08, + "cpu_time": 1.6112029925000116e+08, + "time_unit": "ns", + "execution_us": 1.6145562625000000e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.7432722999999998e+04, + "plan_cost": 8.4221730000000000e+06 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 4, + "real_time": 1.6123671624882263e+08, + "cpu_time": 1.6087974650000092e+08, + "time_unit": "ns", + "execution_us": 1.6123599475000001e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.8082785000000003e+04, + "plan_cost": 8.4221730000000000e+06 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_mean", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6192341366634837e+08, + "cpu_time": 1.6157693225000119e+08, + "time_unit": "ns", + "execution_us": 1.6192277083333331e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.8329539666666664e+04, + "plan_cost": 8.4221730000000000e+06 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_median", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.6145620075076294e+08, + "cpu_time": 1.6112029925000116e+08, + "time_unit": "ns", + "execution_us": 1.6145562625000000e+05, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 8.8082785000000003e+04, + "plan_cost": 8.4221730000000000e+06 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_stddev", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.0053234078127592e+06, + "cpu_time": 1.0064490485261864e+06, + "time_unit": "ns", + "execution_us": 1.0053403292820642e+03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.0423347335560413e+03, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q4.1/Interpreted/Optimized/real_time_cv", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "SSB/q4.1/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 6.2086352124732271e-03, + "cpu_time": 6.2289154430098352e-03, + "time_unit": "ns", + "execution_us": 6.2087643640736509e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.1800522650627965e-02, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 2, + "real_time": 3.0640868749833316e+08, + "cpu_time": 3.0580860949999791e+08, + "time_unit": "ns", + "execution_us": 3.0640767900000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 2, + "real_time": 3.0176305550048709e+08, + "cpu_time": 2.9928094450000489e+08, + "time_unit": "ns", + "execution_us": 3.0176174949999998e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 2, + "real_time": 3.0249018450194854e+08, + "cpu_time": 3.0186889550000018e+08, + "time_unit": "ns", + "execution_us": 3.0248920350000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_mean", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0355397583358955e+08, + "cpu_time": 3.0231948316666764e+08, + "time_unit": "ns", + "execution_us": 3.0355287733333331e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_median", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 3.0249018450194854e+08, + "cpu_time": 3.0186889550000018e+08, + "time_unit": "ns", + "execution_us": 3.0248920350000000e+05, + "includes_order_by": 1.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_stddev", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 2.4988423645448335e+06, + "cpu_time": 3.2870768962827683e+06, + "time_unit": "ns", + "execution_us": 2.4989431261091308e+03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Naive/real_time_cv", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Naive/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 8.2319539965924095e-03, + "cpu_time": 1.0872858281748964e-02, + "time_unit": "ns", + "execution_us": 8.2323157272036849e-03, + "includes_order_by": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 0, + "threads": 1, + "iterations": 11, + "real_time": 5.8630359363715716e+07, + "cpu_time": 5.8503151727273136e+07, + "time_unit": "ns", + "execution_us": 5.8630108818181820e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.4296940000000004e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 1, + "threads": 1, + "iterations": 11, + "real_time": 5.8582719363171652e+07, + "cpu_time": 5.8459474727271751e+07, + "time_unit": "ns", + "execution_us": 5.8582436272727275e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.3965360000000001e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "iteration", + "repetitions": 3, + "repetition_index": 2, + "threads": 1, + "iterations": 11, + "real_time": 5.8810965545655400e+07, + "cpu_time": 5.8684096636363074e+07, + "time_unit": "ns", + "execution_us": 5.8810713454545454e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.3967389999999996e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_mean", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "mean", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8674681424180903e+07, + "cpu_time": 5.8548907696969323e+07, + "time_unit": "ns", + "execution_us": 5.8674419515151509e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.4076563333333324e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_median", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "median", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 5.8630359363715708e+07, + "cpu_time": 5.8503151727273136e+07, + "time_unit": "ns", + "execution_us": 5.8630108818181820e+04, + "includes_order_by": 1.0000000000000000e+00, + "optimizer_us": 4.3967389999999996e+03, + "plan_cost": 8.3643830000000000e+06 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_stddev", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "stddev", + "aggregate_unit": "time", + "iterations": 3, + "real_time": 1.2040520644265502e+05, + "cpu_time": 1.1909641927127035e+05, + "time_unit": "ns", + "execution_us": 1.2041676102318856e+02, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 1.9085449073674976e+01, + "plan_cost": 0.0000000000000000e+00 + }, + { + "name": "SSB/q2.3/Interpreted/Optimized/real_time_cv", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "SSB/q2.3/Interpreted/Optimized/real_time", + "run_type": "aggregate", + "repetitions": 3, + "threads": 1, + "aggregate_name": "cv", + "aggregate_unit": "percentage", + "iterations": 3, + "real_time": 2.0520811280116099e-03, + "cpu_time": 2.0341356304660004e-03, + "time_unit": "ns", + "execution_us": 2.0522872150800452e-03, + "includes_order_by": 0.0000000000000000e+00, + "optimizer_us": 4.3300674168581157e-03, + "plan_cost": 0.0000000000000000e+00 + } + ] +} diff --git a/research/benchmarks.ipynb b/research/benchmarks.ipynb new file mode 100644 index 0000000..617a384 --- /dev/null +++ b/research/benchmarks.ipynb @@ -0,0 +1,1258 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 11, + "id": "b5291871", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-14T20:44:00+03:00\n", + "Running ./build-release/bin/benchmarks\n", + "Run on (8 X 4200 MHz CPU s)\n", + "CPU Caches:\n", + " L1 Data 48 KiB (x4)\n", + " L1 Instruction 32 KiB (x4)\n", + " L2 Unified 1280 KiB (x4)\n", + " L3 Unified 8192 KiB (x1)\n", + "Load Average: 0.80, 1.39, 1.86\n", + "***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.\n", + "----------------------------------------------------------------------------------------------------------------------------------------------------\n", + "Benchmark Time CPU Iterations\n", + "----------------------------------------------------------------------------------------------------------------------------------------------------\n", + "\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 20437 ns 20203 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 20620 ns 20440 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 674 ns 749 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 3.30 % 3.71 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 20149 ns 19914 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 20169 ns 19933 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 50.4 ns 56.9 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.25 % 0.29 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 19900 ns 19621 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 19976 ns 19649 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 180 ns 140 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.91 % 0.71 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 19747 ns 19643 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 19954 ns 19790 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 365 ns 327 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 1.85 % 1.66 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 66637 ns 66004 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 66603 ns 65979 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 470 ns 438 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.71 % 0.66 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 43217 ns 43051 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 42605 ns 42458 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 1444 ns 1398 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 3.34 % 3.25 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 66032 ns 65417 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 66131 ns 65515 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 434 ns 452 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.66 % 0.69 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 42778 ns 42624 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 42672 ns 42513 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 198 ns 193 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.46 % 0.45 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 82328 ns 81546 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 82548 ns 81742 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 767 ns 749 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.93 % 0.92 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 81952 ns 81234 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 81876 ns 81157 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 431 ns 409 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.53 % 0.50 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 78012 ns 77304 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 78003 ns 77279 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 141 ns 107 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.18 % 0.14 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_mean \u001b[m\u001b[0;33m 77614 ns 76895 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_median \u001b[m\u001b[0;33m 77683 ns 76986 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_stddev \u001b[m\u001b[0;33m 420 ns 392 ns \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m\u001b[0;32mBM_SQL/real_time_cv \u001b[m\u001b[0;33m 0.54 % 0.51 % \u001b[m\u001b[0;36m 3\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "!mkdir -p benchmark-results\n", + "!cd .. && timeout 120s ./build-release/bin/benchmarks --benchmark_filter='^BM_SQL<(InterpretedExpressionExecutor|CachedJitCompiledExpressionExecutor), (kSimpleSelectSmall|kJoinSmall|kComplex5), PlannerMode::(kNaive|kOptimized)>/real_time$' --benchmark_repetitions=3 --benchmark_display_aggregates_only=true --benchmark_out=research/benchmark-results/query.json --benchmark_out_format=json" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "94b2cd75", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-14T20:44:28+03:00\n", + "Running ./build-release/bin/benchmarks\n", + "Run on (8 X 4200 MHz CPU s)\n", + "CPU Caches:\n", + " L1 Data 48 KiB (x4)\n", + " L1 Instruction 32 KiB (x4)\n", + " L2 Unified 1280 KiB (x4)\n", + " L3 Unified 8192 KiB (x1)\n", + "Load Average: 1.59, 1.53, 1.90\n", + "***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.\n", + "----------------------------------------------------------------------------------------------------------\n", + "Benchmark Time CPU Iterations UserCounters...\n", + "----------------------------------------------------------------------------------------------------------\n", + "\u001b[0;32mSSB/q2.1/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 9348855 ns 9279593 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.34877k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.1/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 9207349 ns 9150383 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.20727k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.1/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 431153 ns 409276 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=431.152\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.1/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 4.61 % 4.41 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=4.61%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.1/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 8476780 ns 8431744 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.47672k\u001b[m includes_order_by=1\u001b[m optimizer_us=4.86345k\u001b[m plan_cost=836.223k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.1/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 8353937 ns 8310103 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.35388k\u001b[m includes_order_by=1\u001b[m optimizer_us=4.93595k\u001b[m plan_cost=836.223k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.1/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 257001 ns 252587 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=257\u001b[m includes_order_by=0\u001b[m optimizer_us=290.07\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.1/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 3.03 % 3.00 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=3.03%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=5.96%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 8327581 ns 8272691 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.32747k\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 8325392 ns 8272858 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.32515k\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 6162 ns 6648 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.1976\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.07 % 0.08 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.07%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 6667715 ns 6637523 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.66766k\u001b[m includes_order_by=0\u001b[m optimizer_us=1.50543k\u001b[m plan_cost=654.297k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 6669798 ns 6640485 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.66974k\u001b[m includes_order_by=0\u001b[m optimizer_us=1.54306k\u001b[m plan_cost=654.297k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 74924 ns 68657 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=74.9286\u001b[m includes_order_by=0\u001b[m optimizer_us=85.1964\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.3/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 1.12 % 1.03 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=1.12%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=5.66%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 8315161 ns 8259935 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.31511k\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 8321936 ns 8264698 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.32188k\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 17476 ns 15033 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=17.4722\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.21 % 0.18 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.21%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 6738973 ns 6709616 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.73891k\u001b[m includes_order_by=0\u001b[m optimizer_us=970.581\u001b[m plan_cost=654.297k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 6730765 ns 6700780 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.7307k\u001b[m includes_order_by=0\u001b[m optimizer_us=1.03781k\u001b[m plan_cost=654.297k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 44835 ns 42238 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=44.8477\u001b[m includes_order_by=0\u001b[m optimizer_us=134.145\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.2/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 0.67 % 0.63 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.67%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=13.82%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 36699167 ns 36503054 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=36.699k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 36780937 ns 36587893 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=36.7808k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 702089 ns 689228 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=702.125\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 1.91 % 1.89 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=1.91%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 5963849 ns 5933470 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=5.96378k\u001b[m includes_order_by=1\u001b[m optimizer_us=68.2714k\u001b[m plan_cost=816.714k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 6011656 ns 5980110 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.01157k\u001b[m includes_order_by=1\u001b[m optimizer_us=67.7282k\u001b[m plan_cost=816.714k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 126319 ns 124387 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=126.32\u001b[m includes_order_by=0\u001b[m optimizer_us=2.54977k\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.3/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 2.12 % 2.10 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=2.12%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=3.73%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 34712294 ns 34551769 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=34.7121k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 34714216 ns 34550395 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=34.714k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 143927 ns 137031 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=143.838\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.41 % 0.40 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.41%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 5903691 ns 5871216 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=5.90363k\u001b[m includes_order_by=1\u001b[m optimizer_us=45.6028k\u001b[m plan_cost=816.714k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 5946191 ns 5911962 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=5.94614k\u001b[m includes_order_by=1\u001b[m optimizer_us=45.9131k\u001b[m plan_cost=816.714k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 120315 ns 119854 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=120.31\u001b[m includes_order_by=0\u001b[m optimizer_us=647.108\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.4/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 2.04 % 2.04 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=2.04%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=1.42%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 9196776 ns 9139066 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.1967k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 9174353 ns 9117973 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.17429k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 203688 ns 198949 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=203.661\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 2.21 % 2.18 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=2.21%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 6029881 ns 6001703 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.02982k\u001b[m includes_order_by=1\u001b[m optimizer_us=305.279k\u001b[m plan_cost=841.353k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 6022257 ns 5994458 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.02219k\u001b[m includes_order_by=1\u001b[m optimizer_us=304.816k\u001b[m plan_cost=841.353k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 26686 ns 26193 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=26.693\u001b[m includes_order_by=0\u001b[m optimizer_us=861.045\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.3/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 0.44 % 0.44 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.44%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=0.28%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 8408618 ns 8358138 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.40857k\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 8314836 ns 8264783 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.31478k\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 164683 ns 161781 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=164.68\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 1.96 % 1.94 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=1.96%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 6369086 ns 6342793 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.36903k\u001b[m includes_order_by=0\u001b[m optimizer_us=665.244\u001b[m plan_cost=705.057k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 6367944 ns 6342936 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.36789k\u001b[m includes_order_by=0\u001b[m optimizer_us=667.686\u001b[m plan_cost=705.057k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 17295 ns 16674 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=17.3012\u001b[m includes_order_by=0\u001b[m optimizer_us=8.30244\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q1.1/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 0.27 % 0.26 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.27%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=1.25%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 35087301 ns 34918098 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=35.0871k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 35089531 ns 34919402 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=35.0894k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 8513 ns 11166 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.50818\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.02 % 0.03 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.02%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 6043214 ns 6008341 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.04315k\u001b[m includes_order_by=1\u001b[m optimizer_us=26.122k\u001b[m plan_cost=816.374k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 5993226 ns 5955491 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=5.99315k\u001b[m includes_order_by=1\u001b[m optimizer_us=25.803k\u001b[m plan_cost=816.374k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 141650 ns 142416 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=141.654\u001b[m includes_order_by=0\u001b[m optimizer_us=1.46216k\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.2/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 2.34 % 2.37 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=2.34%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=5.60%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 8857568 ns 8799695 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.85751k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 8874801 ns 8812681 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.87474k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 76477 ns 65016 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=76.4718\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.86 % 0.74 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.86%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 6276747 ns 6238963 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.2767k\u001b[m includes_order_by=1\u001b[m optimizer_us=8.03571k\u001b[m plan_cost=841.323k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 6264807 ns 6227206 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.26476k\u001b[m includes_order_by=1\u001b[m optimizer_us=8.06958k\u001b[m plan_cost=841.323k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 43179 ns 37483 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=43.1773\u001b[m includes_order_by=0\u001b[m optimizer_us=129.841\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.2/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 0.69 % 0.60 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.69%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=1.62%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 37245934 ns 36895021 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=37.2456k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 37229172 ns 36945452 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=37.229k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 89624 ns 100110 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=89.8603\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.24 % 0.27 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.24%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 5914830 ns 5884658 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=5.91478k\u001b[m includes_order_by=1\u001b[m optimizer_us=25.0299k\u001b[m plan_cost=816.374k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 5917390 ns 5888825 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=5.91734k\u001b[m includes_order_by=1\u001b[m optimizer_us=24.9689k\u001b[m plan_cost=816.374k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 19819 ns 17179 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=19.8171\u001b[m includes_order_by=0\u001b[m optimizer_us=119.745\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q3.1/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 0.34 % 0.29 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.34%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=0.48%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 9171801 ns 9099329 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.17174k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 9138713 ns 9086817 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.13865k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 88047 ns 55888 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=88.0417\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.96 % 0.61 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.96%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 7149140 ns 7098640 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=7.14907k\u001b[m includes_order_by=1\u001b[m optimizer_us=3.73882M\u001b[m plan_cost=842.823k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 7141587 ns 7094454 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=7.14153k\u001b[m includes_order_by=1\u001b[m optimizer_us=3.74972M\u001b[m plan_cost=842.823k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 112055 ns 112431 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=112.041\u001b[m includes_order_by=0\u001b[m optimizer_us=34.6262k\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.2/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 1.57 % 1.58 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=1.57%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=0.93%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 9111124 ns 9042171 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.11106k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 9099306 ns 9033217 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.09925k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 75179 ns 68936 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=75.1935\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 0.83 % 0.76 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.83%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 9521586 ns 9462121 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.52152k\u001b[m includes_order_by=1\u001b[m optimizer_us=100.318k\u001b[m plan_cost=842.793k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 9558283 ns 9492243 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=9.55822k\u001b[m includes_order_by=1\u001b[m optimizer_us=100.417k\u001b[m plan_cost=842.793k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 113161 ns 113309 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=113.159\u001b[m includes_order_by=0\u001b[m optimizer_us=830.166\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q4.1/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 1.19 % 1.20 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=1.19%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=0.83%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Naive/real_time_mean \u001b[m\u001b[0;33m 8640680 ns 8577034 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.64044k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Naive/real_time_median \u001b[m\u001b[0;33m 8658138 ns 8573712 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=8.65807k\u001b[m includes_order_by=1\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Naive/real_time_stddev \u001b[m\u001b[0;33m 97559 ns 94696 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=97.8445\u001b[m includes_order_by=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Naive/real_time_cv \u001b[m\u001b[0;33m 1.13 % 1.10 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=1.13%\u001b[m includes_order_by=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Optimized/real_time_mean \u001b[m\u001b[0;33m 6071635 ns 6035718 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.07159k\u001b[m includes_order_by=1\u001b[m optimizer_us=4.54599k\u001b[m plan_cost=836.223k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Optimized/real_time_median \u001b[m\u001b[0;33m 6079483 ns 6039033 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=6.07944k\u001b[m includes_order_by=1\u001b[m optimizer_us=4.51961k\u001b[m plan_cost=836.223k\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Optimized/real_time_stddev \u001b[m\u001b[0;33m 25095 ns 21875 ns \u001b[m\u001b[0;36m 3\u001b[m execution_us=25.0966\u001b[m includes_order_by=0\u001b[m optimizer_us=82.5341\u001b[m plan_cost=0\u001b[m\n", + "\u001b[m\u001b[0;32mSSB/q2.3/Interpreted/Optimized/real_time_cv \u001b[m\u001b[0;33m 0.41 % 0.36 % \u001b[m\u001b[0;36m 3\u001b[m execution_us=0.41%\u001b[m includes_order_by=0.00%\u001b[m optimizer_us=1.82%\u001b[m plan_cost=0.00%\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "!rm -f benchmark-results/ssb-sf001.json\n", + "!cd .. && SSB_DATA_DIR=benchmarks/datasets/ssb/generated/sf001 timeout 600s ./build-release/bin/benchmarks --benchmark_filter='^SSB/' --benchmark_repetitions=3 --benchmark_display_aggregates_only=true --benchmark_out=research/benchmark-results/ssb-sf001.json --benchmark_out_format=json" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "6f8e3873", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-14T20:45:51+03:00\n", + "Running ./build-release/bin/benchmarks\n", + "Run on (8 X 4200 MHz CPU s)\n", + "CPU Caches:\n", + " L1 Data 48 KiB (x4)\n", + " L1 Instruction 32 KiB (x4)\n", + " L2 Unified 1280 KiB (x4)\n", + " L3 Unified 8192 KiB (x1)\n", + "Load Average: 1.35, 1.47, 1.84\n", + "***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.\n", + "----------------------------------------------------------------------------------------------------------------------\n", + "Benchmark Time CPU Iterations UserCounters...\n", + "----------------------------------------------------------------------------------------------------------------------\n", + "\u001b[0;32mOperatorCost/SeqScan/1024/real_time_mean \u001b[m\u001b[0;33m 39728 ns 39585 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=102.4k\u001b[m output_rows=1.024k\u001b[m plan_cost=102.4k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/1024/real_time_median \u001b[m\u001b[0;33m 39745 ns 39614 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=102.4k\u001b[m output_rows=1.024k\u001b[m plan_cost=102.4k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/1024/real_time_stddev \u001b[m\u001b[0;33m 234 ns 212 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/1024/real_time_cv \u001b[m\u001b[0;33m 0.59 % 0.54 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/2048/real_time_mean \u001b[m\u001b[0;33m 68177 ns 67893 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=204.8k\u001b[m output_rows=2.048k\u001b[m plan_cost=204.8k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/2048/real_time_median \u001b[m\u001b[0;33m 67391 ns 67122 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=204.8k\u001b[m output_rows=2.048k\u001b[m plan_cost=204.8k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/2048/real_time_stddev \u001b[m\u001b[0;33m 1605 ns 1523 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/2048/real_time_cv \u001b[m\u001b[0;33m 2.35 % 2.24 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/4096/real_time_mean \u001b[m\u001b[0;33m 162377 ns 160772 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=409.6k\u001b[m output_rows=4.096k\u001b[m plan_cost=409.6k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/4096/real_time_median \u001b[m\u001b[0;33m 165623 ns 163832 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=409.6k\u001b[m output_rows=4.096k\u001b[m plan_cost=409.6k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/4096/real_time_stddev \u001b[m\u001b[0;33m 5953 ns 5859 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/4096/real_time_cv \u001b[m\u001b[0;33m 3.67 % 3.64 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/8192/real_time_mean \u001b[m\u001b[0;33m 360639 ns 357681 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=819.2k\u001b[m output_rows=8.192k\u001b[m plan_cost=819.2k\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/8192/real_time_median \u001b[m\u001b[0;33m 360693 ns 357881 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=819.2k\u001b[m output_rows=8.192k\u001b[m plan_cost=819.2k\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/8192/real_time_stddev \u001b[m\u001b[0;33m 1345 ns 1523 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/8192/real_time_cv \u001b[m\u001b[0;33m 0.37 % 0.43 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/16384/real_time_mean \u001b[m\u001b[0;33m 791876 ns 785262 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.6384M\u001b[m output_rows=16.384k\u001b[m plan_cost=1.6384M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/16384/real_time_median \u001b[m\u001b[0;33m 785508 ns 779556 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.6384M\u001b[m output_rows=16.384k\u001b[m plan_cost=1.6384M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/16384/real_time_stddev \u001b[m\u001b[0;33m 34264 ns 33251 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/16384/real_time_cv \u001b[m\u001b[0;33m 4.33 % 4.23 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/32768/real_time_mean \u001b[m\u001b[0;33m 1862931 ns 1843550 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.2768M\u001b[m output_rows=32.768k\u001b[m plan_cost=3.2768M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/32768/real_time_median \u001b[m\u001b[0;33m 1859691 ns 1838972 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.2768M\u001b[m output_rows=32.768k\u001b[m plan_cost=3.2768M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/32768/real_time_stddev \u001b[m\u001b[0;33m 17007 ns 17740 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/32768/real_time_cv \u001b[m\u001b[0;33m 0.91 % 0.96 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/65536/real_time_mean \u001b[m\u001b[0;33m 4594572 ns 4551454 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.5536M\u001b[m output_rows=65.536k\u001b[m plan_cost=6.5536M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/65536/real_time_median \u001b[m\u001b[0;33m 4602633 ns 4558259 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.5536M\u001b[m output_rows=65.536k\u001b[m plan_cost=6.5536M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/65536/real_time_stddev \u001b[m\u001b[0;33m 14120 ns 12475 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/65536/real_time_cv \u001b[m\u001b[0;33m 0.31 % 0.27 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/131072/real_time_mean \u001b[m\u001b[0;33m 10053307 ns 9966611 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=13.1072M\u001b[m output_rows=131.072k\u001b[m plan_cost=13.1072M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/131072/real_time_median \u001b[m\u001b[0;33m 10166987 ns 10076585 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=13.1072M\u001b[m output_rows=131.072k\u001b[m plan_cost=13.1072M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/131072/real_time_stddev \u001b[m\u001b[0;33m 291946 ns 286458 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/131072/real_time_cv \u001b[m\u001b[0;33m 2.90 % 2.87 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/262144/real_time_mean \u001b[m\u001b[0;33m 20673458 ns 20513325 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.2144M\u001b[m output_rows=262.144k\u001b[m plan_cost=26.2144M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/262144/real_time_median \u001b[m\u001b[0;33m 20544696 ns 20386411 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.2144M\u001b[m output_rows=262.144k\u001b[m plan_cost=26.2144M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/262144/real_time_stddev \u001b[m\u001b[0;33m 246412 ns 240540 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/262144/real_time_cv \u001b[m\u001b[0;33m 1.19 % 1.17 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/1024/real_time_mean \u001b[m\u001b[0;33m 70811 ns 70565 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=102.4k\u001b[m output_rows=512\u001b[m plan_cost=204.8k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/1024/real_time_median \u001b[m\u001b[0;33m 70326 ns 70110 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=102.4k\u001b[m output_rows=512\u001b[m plan_cost=204.8k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/1024/real_time_stddev \u001b[m\u001b[0;33m 2401 ns 2353 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/1024/real_time_cv \u001b[m\u001b[0;33m 3.39 % 3.34 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/2048/real_time_mean \u001b[m\u001b[0;33m 135097 ns 134476 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=204.8k\u001b[m output_rows=1.024k\u001b[m plan_cost=409.6k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/2048/real_time_median \u001b[m\u001b[0;33m 135164 ns 134571 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=204.8k\u001b[m output_rows=1.024k\u001b[m plan_cost=409.6k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/2048/real_time_stddev \u001b[m\u001b[0;33m 799 ns 724 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/2048/real_time_cv \u001b[m\u001b[0;33m 0.59 % 0.54 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/4096/real_time_mean \u001b[m\u001b[0;33m 262054 ns 260826 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=409.6k\u001b[m output_rows=2.048k\u001b[m plan_cost=819.2k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/4096/real_time_median \u001b[m\u001b[0;33m 261825 ns 260866 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=409.6k\u001b[m output_rows=2.048k\u001b[m plan_cost=819.2k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/4096/real_time_stddev \u001b[m\u001b[0;33m 1211 ns 1402 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/4096/real_time_cv \u001b[m\u001b[0;33m 0.46 % 0.54 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/8192/real_time_mean \u001b[m\u001b[0;33m 508853 ns 506511 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=819.2k\u001b[m output_rows=4.096k\u001b[m plan_cost=1.6384M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/8192/real_time_median \u001b[m\u001b[0;33m 505867 ns 503676 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=819.2k\u001b[m output_rows=4.096k\u001b[m plan_cost=1.6384M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/8192/real_time_stddev \u001b[m\u001b[0;33m 12890 ns 12652 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/8192/real_time_cv \u001b[m\u001b[0;33m 2.53 % 2.50 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/16384/real_time_mean \u001b[m\u001b[0;33m 1010537 ns 1005862 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.6384M\u001b[m output_rows=8.192k\u001b[m plan_cost=3.2768M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/16384/real_time_median \u001b[m\u001b[0;33m 1010307 ns 1005759 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.6384M\u001b[m output_rows=8.192k\u001b[m plan_cost=3.2768M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/16384/real_time_stddev \u001b[m\u001b[0;33m 5573 ns 5402 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/16384/real_time_cv \u001b[m\u001b[0;33m 0.55 % 0.54 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/32768/real_time_mean \u001b[m\u001b[0;33m 2094588 ns 2083947 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.2768M\u001b[m output_rows=16.384k\u001b[m plan_cost=6.5536M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/32768/real_time_median \u001b[m\u001b[0;33m 2094297 ns 2083675 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.2768M\u001b[m output_rows=16.384k\u001b[m plan_cost=6.5536M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/32768/real_time_stddev \u001b[m\u001b[0;33m 13883 ns 12947 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/32768/real_time_cv \u001b[m\u001b[0;33m 0.66 % 0.62 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/65536/real_time_mean \u001b[m\u001b[0;33m 4399525 ns 4374122 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.5536M\u001b[m output_rows=32.768k\u001b[m plan_cost=13.1072M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/65536/real_time_median \u001b[m\u001b[0;33m 4398467 ns 4372248 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.5536M\u001b[m output_rows=32.768k\u001b[m plan_cost=13.1072M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/65536/real_time_stddev \u001b[m\u001b[0;33m 9109 ns 9887 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/65536/real_time_cv \u001b[m\u001b[0;33m 0.21 % 0.23 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/131072/real_time_mean \u001b[m\u001b[0;33m 9087365 ns 9040882 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=13.1072M\u001b[m output_rows=65.536k\u001b[m plan_cost=26.2144M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/131072/real_time_median \u001b[m\u001b[0;33m 9081014 ns 9026851 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=13.1072M\u001b[m output_rows=65.536k\u001b[m plan_cost=26.2144M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/131072/real_time_stddev \u001b[m\u001b[0;33m 206801 ns 203954 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/131072/real_time_cv \u001b[m\u001b[0;33m 2.28 % 2.26 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/262144/real_time_mean \u001b[m\u001b[0;33m 19038369 ns 18931027 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.2144M\u001b[m output_rows=131.072k\u001b[m plan_cost=52.4288M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/262144/real_time_median \u001b[m\u001b[0;33m 18975293 ns 18872990 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.2144M\u001b[m output_rows=131.072k\u001b[m plan_cost=52.4288M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/262144/real_time_stddev \u001b[m\u001b[0;33m 211770 ns 211621 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/262144/real_time_cv \u001b[m\u001b[0;33m 1.11 % 1.12 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/1024/real_time_mean \u001b[m\u001b[0;33m 98176 ns 97832 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=22.528k\u001b[m output_rows=1.024k\u001b[m plan_cost=124.928k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/1024/real_time_median \u001b[m\u001b[0;33m 98024 ns 97688 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=22.528k\u001b[m output_rows=1.024k\u001b[m plan_cost=124.928k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/1024/real_time_stddev \u001b[m\u001b[0;33m 435 ns 437 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/1024/real_time_cv \u001b[m\u001b[0;33m 0.44 % 0.45 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/2048/real_time_mean \u001b[m\u001b[0;33m 182809 ns 182208 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=45.056k\u001b[m output_rows=2.048k\u001b[m plan_cost=249.856k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/2048/real_time_median \u001b[m\u001b[0;33m 183246 ns 182566 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=45.056k\u001b[m output_rows=2.048k\u001b[m plan_cost=249.856k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/2048/real_time_stddev \u001b[m\u001b[0;33m 1840 ns 1824 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/2048/real_time_cv \u001b[m\u001b[0;33m 1.01 % 1.00 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/4096/real_time_mean \u001b[m\u001b[0;33m 356092 ns 354748 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=90.112k\u001b[m output_rows=4.096k\u001b[m plan_cost=499.712k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/4096/real_time_median \u001b[m\u001b[0;33m 356039 ns 354780 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=90.112k\u001b[m output_rows=4.096k\u001b[m plan_cost=499.712k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/4096/real_time_stddev \u001b[m\u001b[0;33m 186 ns 189 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/4096/real_time_cv \u001b[m\u001b[0;33m 0.05 % 0.05 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/8192/real_time_mean \u001b[m\u001b[0;33m 712488 ns 709400 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=180.224k\u001b[m output_rows=8.192k\u001b[m plan_cost=0.999424M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/8192/real_time_median \u001b[m\u001b[0;33m 714174 ns 711185 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=180.224k\u001b[m output_rows=8.192k\u001b[m plan_cost=0.999424M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/8192/real_time_stddev \u001b[m\u001b[0;33m 7591 ns 7419 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/8192/real_time_cv \u001b[m\u001b[0;33m 1.07 % 1.05 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/16384/real_time_mean \u001b[m\u001b[0;33m 1528563 ns 1512518 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=360.448k\u001b[m output_rows=16.384k\u001b[m plan_cost=1.99885M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/16384/real_time_median \u001b[m\u001b[0;33m 1516121 ns 1504982 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=360.448k\u001b[m output_rows=16.384k\u001b[m plan_cost=1.99885M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/16384/real_time_stddev \u001b[m\u001b[0;33m 64472 ns 52538 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/16384/real_time_cv \u001b[m\u001b[0;33m 4.22 % 3.47 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/32768/real_time_mean \u001b[m\u001b[0;33m 3053942 ns 3037121 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=720.896k\u001b[m output_rows=32.768k\u001b[m plan_cost=3.9977M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/32768/real_time_median \u001b[m\u001b[0;33m 3064120 ns 3046222 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=720.896k\u001b[m output_rows=32.768k\u001b[m plan_cost=3.9977M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/32768/real_time_stddev \u001b[m\u001b[0;33m 53559 ns 51365 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/32768/real_time_cv \u001b[m\u001b[0;33m 1.75 % 1.69 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/65536/real_time_mean \u001b[m\u001b[0;33m 6328806 ns 6290455 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.44179M\u001b[m output_rows=65.536k\u001b[m plan_cost=7.99539M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/65536/real_time_median \u001b[m\u001b[0;33m 6334730 ns 6294222 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.44179M\u001b[m output_rows=65.536k\u001b[m plan_cost=7.99539M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/65536/real_time_stddev \u001b[m\u001b[0;33m 45869 ns 43718 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/65536/real_time_cv \u001b[m\u001b[0;33m 0.72 % 0.69 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/131072/real_time_mean \u001b[m\u001b[0;33m 13279845 ns 13170453 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=2.88358M\u001b[m output_rows=131.072k\u001b[m plan_cost=15.9908M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/131072/real_time_median \u001b[m\u001b[0;33m 13400773 ns 13305887 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=2.88358M\u001b[m output_rows=131.072k\u001b[m plan_cost=15.9908M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/131072/real_time_stddev \u001b[m\u001b[0;33m 280345 ns 278079 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/131072/real_time_cv \u001b[m\u001b[0;33m 2.11 % 2.11 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/262144/real_time_mean \u001b[m\u001b[0;33m 31873201 ns 31604072 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=5.76717M\u001b[m output_rows=262.144k\u001b[m plan_cost=31.9816M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/262144/real_time_median \u001b[m\u001b[0;33m 31345011 ns 31017351 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=5.76717M\u001b[m output_rows=262.144k\u001b[m plan_cost=31.9816M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/262144/real_time_stddev \u001b[m\u001b[0;33m 1102243 ns 1112834 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/262144/real_time_cv \u001b[m\u001b[0;33m 3.46 % 3.52 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/1024/real_time_mean \u001b[m\u001b[0;33m 120718 ns 120152 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=123.904k\u001b[m output_rows=1.024k\u001b[m plan_cost=226.304k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/1024/real_time_median \u001b[m\u001b[0;33m 120666 ns 119977 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=123.904k\u001b[m output_rows=1.024k\u001b[m plan_cost=226.304k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/1024/real_time_stddev \u001b[m\u001b[0;33m 349 ns 380 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/1024/real_time_cv \u001b[m\u001b[0;33m 0.29 % 0.32 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/2048/real_time_mean \u001b[m\u001b[0;33m 279420 ns 278255 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=270.336k\u001b[m output_rows=2.048k\u001b[m plan_cost=475.136k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/2048/real_time_median \u001b[m\u001b[0;33m 279887 ns 278699 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=270.336k\u001b[m output_rows=2.048k\u001b[m plan_cost=475.136k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/2048/real_time_stddev \u001b[m\u001b[0;33m 1189 ns 863 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/2048/real_time_cv \u001b[m\u001b[0;33m 0.43 % 0.31 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/4096/real_time_mean \u001b[m\u001b[0;33m 620420 ns 617927 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=585.728k\u001b[m output_rows=4.096k\u001b[m plan_cost=995.328k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/4096/real_time_median \u001b[m\u001b[0;33m 621271 ns 618789 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=585.728k\u001b[m output_rows=4.096k\u001b[m plan_cost=995.328k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/4096/real_time_stddev \u001b[m\u001b[0;33m 3643 ns 3652 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/4096/real_time_cv \u001b[m\u001b[0;33m 0.59 % 0.59 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/8192/real_time_mean \u001b[m\u001b[0;33m 1392947 ns 1386803 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.26157M\u001b[m output_rows=8.192k\u001b[m plan_cost=2.08077M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/8192/real_time_median \u001b[m\u001b[0;33m 1383591 ns 1377946 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.26157M\u001b[m output_rows=8.192k\u001b[m plan_cost=2.08077M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/8192/real_time_stddev \u001b[m\u001b[0;33m 18634 ns 18302 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/8192/real_time_cv \u001b[m\u001b[0;33m 1.34 % 1.32 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/16384/real_time_mean \u001b[m\u001b[0;33m 3221082 ns 3204370 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=2.70336M\u001b[m output_rows=16.384k\u001b[m plan_cost=4.34176M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/16384/real_time_median \u001b[m\u001b[0;33m 3202222 ns 3186356 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=2.70336M\u001b[m output_rows=16.384k\u001b[m plan_cost=4.34176M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/16384/real_time_stddev \u001b[m\u001b[0;33m 73386 ns 71144 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/16384/real_time_cv \u001b[m\u001b[0;33m 2.28 % 2.22 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/32768/real_time_mean \u001b[m\u001b[0;33m 8046817 ns 7978826 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=5.76717M\u001b[m output_rows=32.768k\u001b[m plan_cost=9.04397M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/32768/real_time_median \u001b[m\u001b[0;33m 8025546 ns 7966259 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=5.76717M\u001b[m output_rows=32.768k\u001b[m plan_cost=9.04397M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/32768/real_time_stddev \u001b[m\u001b[0;33m 115887 ns 112807 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/32768/real_time_cv \u001b[m\u001b[0;33m 1.44 % 1.41 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/65536/real_time_mean \u001b[m\u001b[0;33m 20480156 ns 20289990 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.2552M\u001b[m output_rows=65.536k\u001b[m plan_cost=18.8088M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/65536/real_time_median \u001b[m\u001b[0;33m 20550580 ns 20332200 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.2552M\u001b[m output_rows=65.536k\u001b[m plan_cost=18.8088M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/65536/real_time_stddev \u001b[m\u001b[0;33m 181757 ns 168891 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/65536/real_time_cv \u001b[m\u001b[0;33m 0.89 % 0.83 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/131072/real_time_mean \u001b[m\u001b[0;33m 55388937 ns 54787197 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=25.9523M\u001b[m output_rows=131.072k\u001b[m plan_cost=39.0595M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/131072/real_time_median \u001b[m\u001b[0;33m 55356484 ns 54701727 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=25.9523M\u001b[m output_rows=131.072k\u001b[m plan_cost=39.0595M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/131072/real_time_stddev \u001b[m\u001b[0;33m 128703 ns 155072 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/131072/real_time_cv \u001b[m\u001b[0;33m 0.23 % 0.28 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/262144/real_time_mean \u001b[m\u001b[0;33m 168947981 ns 167426580 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=54.7881M\u001b[m output_rows=262.144k\u001b[m plan_cost=81.0025M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/262144/real_time_median \u001b[m\u001b[0;33m 168927098 ns 167324195 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=54.7881M\u001b[m output_rows=262.144k\u001b[m plan_cost=81.0025M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/262144/real_time_stddev \u001b[m\u001b[0;33m 7017901 ns 6982613 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/262144/real_time_cv \u001b[m\u001b[0;33m 4.15 % 4.17 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/1024/real_time_mean \u001b[m\u001b[0;33m 288821 ns 287061 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=522.24k\u001b[m output_rows=1.024k\u001b[m plan_cost=624.64k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/1024/real_time_median \u001b[m\u001b[0;33m 288883 ns 287336 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=522.24k\u001b[m output_rows=1.024k\u001b[m plan_cost=624.64k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/1024/real_time_stddev \u001b[m\u001b[0;33m 515 ns 728 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/1024/real_time_cv \u001b[m\u001b[0;33m 0.18 % 0.25 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/2048/real_time_mean \u001b[m\u001b[0;33m 561285 ns 558165 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.04448M\u001b[m output_rows=2.048k\u001b[m plan_cost=1.24928M\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/2048/real_time_median \u001b[m\u001b[0;33m 560947 ns 558301 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.04448M\u001b[m output_rows=2.048k\u001b[m plan_cost=1.24928M\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/2048/real_time_stddev \u001b[m\u001b[0;33m 2755 ns 2642 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/2048/real_time_cv \u001b[m\u001b[0;33m 0.49 % 0.47 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/4096/real_time_mean \u001b[m\u001b[0;33m 1115801 ns 1110007 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=2.08896M\u001b[m output_rows=4.096k\u001b[m plan_cost=2.49856M\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/4096/real_time_median \u001b[m\u001b[0;33m 1107251 ns 1101741 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=2.08896M\u001b[m output_rows=4.096k\u001b[m plan_cost=2.49856M\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/4096/real_time_stddev \u001b[m\u001b[0;33m 23287 ns 22665 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/4096/real_time_cv \u001b[m\u001b[0;33m 2.09 % 2.04 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/8192/real_time_mean \u001b[m\u001b[0;33m 2490977 ns 2478885 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=4.17792M\u001b[m output_rows=8.192k\u001b[m plan_cost=4.99712M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/8192/real_time_median \u001b[m\u001b[0;33m 2471500 ns 2459821 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=4.17792M\u001b[m output_rows=8.192k\u001b[m plan_cost=4.99712M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/8192/real_time_stddev \u001b[m\u001b[0;33m 78857 ns 77957 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/8192/real_time_cv \u001b[m\u001b[0;33m 3.17 % 3.14 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/16384/real_time_mean \u001b[m\u001b[0;33m 5541260 ns 5509669 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=8.35584M\u001b[m output_rows=16.384k\u001b[m plan_cost=9.99424M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/16384/real_time_median \u001b[m\u001b[0;33m 5562212 ns 5531078 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=8.35584M\u001b[m output_rows=16.384k\u001b[m plan_cost=9.99424M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/16384/real_time_stddev \u001b[m\u001b[0;33m 71339 ns 68139 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/16384/real_time_cv \u001b[m\u001b[0;33m 1.29 % 1.24 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/32768/real_time_mean \u001b[m\u001b[0;33m 16564439 ns 16435384 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=16.7117M\u001b[m output_rows=32.768k\u001b[m plan_cost=19.9885M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/32768/real_time_median \u001b[m\u001b[0;33m 16591871 ns 16467307 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=16.7117M\u001b[m output_rows=32.768k\u001b[m plan_cost=19.9885M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/32768/real_time_stddev \u001b[m\u001b[0;33m 83381 ns 88516 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/32768/real_time_cv \u001b[m\u001b[0;33m 0.50 % 0.54 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/65536/real_time_mean \u001b[m\u001b[0;33m 49221785 ns 48751030 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=33.4234M\u001b[m output_rows=65.536k\u001b[m plan_cost=39.977M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/65536/real_time_median \u001b[m\u001b[0;33m 50072441 ns 49631210 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=33.4234M\u001b[m output_rows=65.536k\u001b[m plan_cost=39.977M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/65536/real_time_stddev \u001b[m\u001b[0;33m 2627450 ns 2694749 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/65536/real_time_cv \u001b[m\u001b[0;33m 5.34 % 5.53 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/131072/real_time_mean \u001b[m\u001b[0;33m 118590633 ns 117787971 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=66.8467M\u001b[m output_rows=131.072k\u001b[m plan_cost=79.9539M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/131072/real_time_median \u001b[m\u001b[0;33m 117502626 ns 116574037 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=66.8467M\u001b[m output_rows=131.072k\u001b[m plan_cost=79.9539M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/131072/real_time_stddev \u001b[m\u001b[0;33m 2312608 ns 2306280 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/131072/real_time_cv \u001b[m\u001b[0;33m 1.95 % 1.96 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/262144/real_time_mean \u001b[m\u001b[0;33m 299699036 ns 297386710 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=133.693M\u001b[m output_rows=262.144k\u001b[m plan_cost=159.908M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/262144/real_time_median \u001b[m\u001b[0;33m 300178360 ns 296997108 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=133.693M\u001b[m output_rows=262.144k\u001b[m plan_cost=159.908M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/262144/real_time_stddev \u001b[m\u001b[0;33m 2483568 ns 2344279 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/262144/real_time_cv \u001b[m\u001b[0;33m 0.83 % 0.79 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/1024/1024/real_time_mean \u001b[m\u001b[0;33m 203861 ns 202673 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.024k\u001b[m model_cost=404.48k\u001b[m output_rows=1.024k\u001b[m plan_cost=609.28k\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/1024/1024/real_time_median \u001b[m\u001b[0;33m 203619 ns 202944 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.024k\u001b[m model_cost=404.48k\u001b[m output_rows=1.024k\u001b[m plan_cost=609.28k\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/1024/1024/real_time_stddev \u001b[m\u001b[0;33m 2527 ns 1821 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/1024/1024/real_time_cv \u001b[m\u001b[0;33m 1.24 % 0.90 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/2048/2048/real_time_mean \u001b[m\u001b[0;33m 388318 ns 386320 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=2.048k\u001b[m model_cost=808.96k\u001b[m output_rows=2.048k\u001b[m plan_cost=1.21856M\u001b[m rhs_rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/2048/2048/real_time_median \u001b[m\u001b[0;33m 389129 ns 386872 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=2.048k\u001b[m model_cost=808.96k\u001b[m output_rows=2.048k\u001b[m plan_cost=1.21856M\u001b[m rhs_rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/2048/2048/real_time_stddev \u001b[m\u001b[0;33m 1477 ns 1399 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/2048/2048/real_time_cv \u001b[m\u001b[0;33m 0.38 % 0.36 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/4096/4096/real_time_mean \u001b[m\u001b[0;33m 792906 ns 785115 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=4.096k\u001b[m model_cost=1.61792M\u001b[m output_rows=4.096k\u001b[m plan_cost=2.43712M\u001b[m rhs_rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/4096/4096/real_time_median \u001b[m\u001b[0;33m 785729 ns 778319 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=4.096k\u001b[m model_cost=1.61792M\u001b[m output_rows=4.096k\u001b[m plan_cost=2.43712M\u001b[m rhs_rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/4096/4096/real_time_stddev \u001b[m\u001b[0;33m 25076 ns 25976 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/4096/4096/real_time_cv \u001b[m\u001b[0;33m 3.16 % 3.31 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/8192/8192/real_time_mean \u001b[m\u001b[0;33m 1562261 ns 1551754 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=8.192k\u001b[m model_cost=3.23584M\u001b[m output_rows=8.192k\u001b[m plan_cost=4.87424M\u001b[m rhs_rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/8192/8192/real_time_median \u001b[m\u001b[0;33m 1572901 ns 1563377 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=8.192k\u001b[m model_cost=3.23584M\u001b[m output_rows=8.192k\u001b[m plan_cost=4.87424M\u001b[m rhs_rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/8192/8192/real_time_stddev \u001b[m\u001b[0;33m 40016 ns 39588 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/8192/8192/real_time_cv \u001b[m\u001b[0;33m 2.56 % 2.55 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/16384/16384/real_time_mean \u001b[m\u001b[0;33m 3419899 ns 3380729 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=16.384k\u001b[m model_cost=6.47168M\u001b[m output_rows=16.384k\u001b[m plan_cost=9.74848M\u001b[m rhs_rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/16384/16384/real_time_median \u001b[m\u001b[0;33m 3441218 ns 3399788 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=16.384k\u001b[m model_cost=6.47168M\u001b[m output_rows=16.384k\u001b[m plan_cost=9.74848M\u001b[m rhs_rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/16384/16384/real_time_stddev \u001b[m\u001b[0;33m 39715 ns 34672 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/16384/16384/real_time_cv \u001b[m\u001b[0;33m 1.16 % 1.03 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/32768/32768/real_time_mean \u001b[m\u001b[0;33m 8103526 ns 7992137 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=32.768k\u001b[m model_cost=12.9434M\u001b[m output_rows=32.768k\u001b[m plan_cost=19.497M\u001b[m rhs_rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/32768/32768/real_time_median \u001b[m\u001b[0;33m 8067914 ns 7981352 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=32.768k\u001b[m model_cost=12.9434M\u001b[m output_rows=32.768k\u001b[m plan_cost=19.497M\u001b[m rhs_rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/32768/32768/real_time_stddev \u001b[m\u001b[0;33m 379774 ns 374953 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/32768/32768/real_time_cv \u001b[m\u001b[0;33m 4.69 % 4.69 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/65536/65536/real_time_mean \u001b[m\u001b[0;33m 18077552 ns 17898348 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=65.536k\u001b[m model_cost=25.8867M\u001b[m output_rows=65.536k\u001b[m plan_cost=38.9939M\u001b[m rhs_rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/65536/65536/real_time_median \u001b[m\u001b[0;33m 17416211 ns 17231467 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=65.536k\u001b[m model_cost=25.8867M\u001b[m output_rows=65.536k\u001b[m plan_cost=38.9939M\u001b[m rhs_rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/65536/65536/real_time_stddev \u001b[m\u001b[0;33m 1955871 ns 1906829 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/65536/65536/real_time_cv \u001b[m\u001b[0;33m 10.82 % 10.65 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/64/64/real_time_mean \u001b[m\u001b[0;33m 378714 ns 375733 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=64\u001b[m model_cost=286.72k\u001b[m output_rows=64\u001b[m plan_cost=299.52k\u001b[m rhs_rows=64\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/64/64/real_time_median \u001b[m\u001b[0;33m 375010 ns 371548 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=64\u001b[m model_cost=286.72k\u001b[m output_rows=64\u001b[m plan_cost=299.52k\u001b[m rhs_rows=64\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/64/64/real_time_stddev \u001b[m\u001b[0;33m 14355 ns 13693 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/64/64/real_time_cv \u001b[m\u001b[0;33m 3.79 % 3.64 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/128/128/real_time_mean \u001b[m\u001b[0;33m 1181625 ns 1174045 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=128\u001b[m model_cost=1.14688M\u001b[m output_rows=128\u001b[m plan_cost=1.17248M\u001b[m rhs_rows=128\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/128/128/real_time_median \u001b[m\u001b[0;33m 1150213 ns 1144394 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=128\u001b[m model_cost=1.14688M\u001b[m output_rows=128\u001b[m plan_cost=1.17248M\u001b[m rhs_rows=128\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/128/128/real_time_stddev \u001b[m\u001b[0;33m 59677 ns 56624 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/128/128/real_time_cv \u001b[m\u001b[0;33m 5.05 % 4.82 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/256/256/real_time_mean \u001b[m\u001b[0;33m 4128424 ns 4098841 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=256\u001b[m model_cost=4.58752M\u001b[m output_rows=256\u001b[m plan_cost=4.63872M\u001b[m rhs_rows=256\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/256/256/real_time_median \u001b[m\u001b[0;33m 4114884 ns 4087364 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=256\u001b[m model_cost=4.58752M\u001b[m output_rows=256\u001b[m plan_cost=4.63872M\u001b[m rhs_rows=256\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/256/256/real_time_stddev \u001b[m\u001b[0;33m 36060 ns 33710 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/256/256/real_time_cv \u001b[m\u001b[0;33m 0.87 % 0.82 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/512/512/real_time_mean \u001b[m\u001b[0;33m 15505186 ns 15412702 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=512\u001b[m model_cost=18.3501M\u001b[m output_rows=512\u001b[m plan_cost=18.4525M\u001b[m rhs_rows=512\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/512/512/real_time_median \u001b[m\u001b[0;33m 15601415 ns 15520219 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=512\u001b[m model_cost=18.3501M\u001b[m output_rows=512\u001b[m plan_cost=18.4525M\u001b[m rhs_rows=512\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/512/512/real_time_stddev \u001b[m\u001b[0;33m 272156 ns 273022 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/512/512/real_time_cv \u001b[m\u001b[0;33m 1.76 % 1.77 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/1024/1024/real_time_mean \u001b[m\u001b[0;33m 58136121 ns 57917496 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.024k\u001b[m model_cost=73.4003M\u001b[m output_rows=1.024k\u001b[m plan_cost=73.6051M\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/1024/1024/real_time_median \u001b[m\u001b[0;33m 58150968 ns 57926298 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.024k\u001b[m model_cost=73.4003M\u001b[m output_rows=1.024k\u001b[m plan_cost=73.6051M\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/1024/1024/real_time_stddev \u001b[m\u001b[0;33m 304440 ns 274278 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/1024/1024/real_time_cv \u001b[m\u001b[0;33m 0.52 % 0.47 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/2048/2048/real_time_mean \u001b[m\u001b[0;33m 233424874 ns 232578334 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=2.048k\u001b[m model_cost=293.601M\u001b[m output_rows=2.048k\u001b[m plan_cost=294.011M\u001b[m rhs_rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/2048/2048/real_time_median \u001b[m\u001b[0;33m 231674264 ns 230876970 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=2.048k\u001b[m model_cost=293.601M\u001b[m output_rows=2.048k\u001b[m plan_cost=294.011M\u001b[m rhs_rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/2048/2048/real_time_stddev \u001b[m\u001b[0;33m 3983608 ns 3922793 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/2048/2048/real_time_cv \u001b[m\u001b[0;33m 1.71 % 1.69 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/32/32/real_time_mean \u001b[m\u001b[0;33m 125882 ns 124938 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=32\u001b[m model_cost=106.496k\u001b[m output_rows=1.024k\u001b[m plan_cost=112.896k\u001b[m rhs_rows=32\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/32/32/real_time_median \u001b[m\u001b[0;33m 126031 ns 125025 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=32\u001b[m model_cost=106.496k\u001b[m output_rows=1.024k\u001b[m plan_cost=112.896k\u001b[m rhs_rows=32\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/32/32/real_time_stddev \u001b[m\u001b[0;33m 3179 ns 3035 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/32/32/real_time_cv \u001b[m\u001b[0;33m 2.53 % 2.43 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/64/64/real_time_mean \u001b[m\u001b[0;33m 279630 ns 277682 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=64\u001b[m model_cost=425.984k\u001b[m output_rows=4.096k\u001b[m plan_cost=438.784k\u001b[m rhs_rows=64\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/64/64/real_time_median \u001b[m\u001b[0;33m 277484 ns 275597 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=64\u001b[m model_cost=425.984k\u001b[m output_rows=4.096k\u001b[m plan_cost=438.784k\u001b[m rhs_rows=64\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/64/64/real_time_stddev \u001b[m\u001b[0;33m 7966 ns 7725 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/64/64/real_time_cv \u001b[m\u001b[0;33m 2.85 % 2.78 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/128/128/real_time_mean \u001b[m\u001b[0;33m 909990 ns 902791 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=128\u001b[m model_cost=1.70394M\u001b[m output_rows=16.384k\u001b[m plan_cost=1.72954M\u001b[m rhs_rows=128\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/128/128/real_time_median \u001b[m\u001b[0;33m 894803 ns 887672 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=128\u001b[m model_cost=1.70394M\u001b[m output_rows=16.384k\u001b[m plan_cost=1.72954M\u001b[m rhs_rows=128\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/128/128/real_time_stddev \u001b[m\u001b[0;33m 30725 ns 29804 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/128/128/real_time_cv \u001b[m\u001b[0;33m 3.38 % 3.30 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/256/256/real_time_mean \u001b[m\u001b[0;33m 5038775 ns 4973354 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=256\u001b[m model_cost=6.81574M\u001b[m output_rows=65.536k\u001b[m plan_cost=6.86694M\u001b[m rhs_rows=256\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/256/256/real_time_median \u001b[m\u001b[0;33m 5089727 ns 5024698 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=256\u001b[m model_cost=6.81574M\u001b[m output_rows=65.536k\u001b[m plan_cost=6.86694M\u001b[m rhs_rows=256\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/256/256/real_time_stddev \u001b[m\u001b[0;33m 235576 ns 226711 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/256/256/real_time_cv \u001b[m\u001b[0;33m 4.68 % 4.56 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/512/512/real_time_mean \u001b[m\u001b[0;33m 22572298 ns 22363182 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=512\u001b[m model_cost=27.263M\u001b[m output_rows=262.144k\u001b[m plan_cost=27.3654M\u001b[m rhs_rows=512\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/512/512/real_time_median \u001b[m\u001b[0;33m 22575023 ns 22343393 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=512\u001b[m model_cost=27.263M\u001b[m output_rows=262.144k\u001b[m plan_cost=27.3654M\u001b[m rhs_rows=512\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/512/512/real_time_stddev \u001b[m\u001b[0;33m 100386 ns 99763 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/512/512/real_time_cv \u001b[m\u001b[0;33m 0.44 % 0.45 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/1024/1024/real_time_mean \u001b[m\u001b[0;33m 117439223 ns 116310763 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.024k\u001b[m model_cost=109.052M\u001b[m output_rows=1.04858M\u001b[m plan_cost=109.257M\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/1024/1024/real_time_median \u001b[m\u001b[0;33m 117482341 ns 116332985 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.024k\u001b[m model_cost=109.052M\u001b[m output_rows=1.04858M\u001b[m plan_cost=109.257M\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/1024/1024/real_time_stddev \u001b[m\u001b[0;33m 432620 ns 407753 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/1024/1024/real_time_cv \u001b[m\u001b[0;33m 0.37 % 0.35 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "!cd .. && timeout 300s ./build-release/bin/benchmarks --benchmark_filter='^OperatorCost/' --benchmark_repetitions=3 --benchmark_display_aggregates_only=true --benchmark_out=research/benchmark-results/operator-cost.json --benchmark_out_format=json" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "bcfd11fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-14T20:48:21+03:00\n", + "Running ./build-release/bin/benchmarks\n", + "Run on (8 X 4200 MHz CPU s)\n", + "CPU Caches:\n", + " L1 Data 48 KiB (x4)\n", + " L1 Instruction 32 KiB (x4)\n", + " L2 Unified 1280 KiB (x4)\n", + " L3 Unified 8192 KiB (x1)\n", + "Load Average: 1.43, 1.47, 1.78\n", + "***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.\n", + "------------------------------------------------------------------------------------------------------------------------------------------------\n", + "Benchmark Time CPU Iterations UserCounters...\n", + "------------------------------------------------------------------------------------------------------------------------------------------------\n", + "\u001b[0;32mOperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_mean \u001b[m\u001b[0;33m 327112 ns 323454 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640k\u001b[m output_rows=6.4k\u001b[m plan_cost=640k\u001b[m rows=6.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_median \u001b[m\u001b[0;33m 317651 ns 314511 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640k\u001b[m output_rows=6.4k\u001b[m plan_cost=640k\u001b[m rows=6.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_stddev \u001b[m\u001b[0;33m 24164 ns 23071 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/SeqScan/6400/real_time_cv \u001b[m\u001b[0;33m 7.39 % 7.13 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Filter/6400/real_time_mean \u001b[m\u001b[0;33m 423529 ns 420790 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640k\u001b[m output_rows=3.328k\u001b[m plan_cost=1.28M\u001b[m rows=6.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Filter/6400/real_time_median \u001b[m\u001b[0;33m 419302 ns 416611 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640k\u001b[m output_rows=3.328k\u001b[m plan_cost=1.28M\u001b[m rows=6.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Filter/6400/real_time_stddev \u001b[m\u001b[0;33m 11539 ns 11860 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Filter/6400/real_time_cv \u001b[m\u001b[0;33m 2.72 % 2.82 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Projection/29091/real_time_mean \u001b[m\u001b[0;33m 2969473 ns 2951961 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640.002k\u001b[m output_rows=29.091k\u001b[m plan_cost=3.5491M\u001b[m rows=29.091k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Projection/29091/real_time_median \u001b[m\u001b[0;33m 2996433 ns 2978522 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640.002k\u001b[m output_rows=29.091k\u001b[m plan_cost=3.5491M\u001b[m rows=29.091k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Projection/29091/real_time_stddev \u001b[m\u001b[0;33m 67715 ns 66378 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Projection/29091/real_time_cv \u001b[m\u001b[0;33m 2.28 % 2.25 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Sort/4476/real_time_mean \u001b[m\u001b[0;33m 708003 ns 704782 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640.068k\u001b[m output_rows=4.476k\u001b[m plan_cost=1.08767M\u001b[m rows=4.476k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Sort/4476/real_time_median \u001b[m\u001b[0;33m 707848 ns 704782 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640.068k\u001b[m output_rows=4.476k\u001b[m plan_cost=1.08767M\u001b[m rows=4.476k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Sort/4476/real_time_stddev \u001b[m\u001b[0;33m 2199 ns 2175 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Sort/4476/real_time_cv \u001b[m\u001b[0;33m 0.31 % 0.31 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_mean \u001b[m\u001b[0;33m 350350 ns 349015 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640.05k\u001b[m output_rows=1.255k\u001b[m plan_cost=765.55k\u001b[m rows=1.255k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_median \u001b[m\u001b[0;33m 350669 ns 349288 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=640.05k\u001b[m output_rows=1.255k\u001b[m plan_cost=765.55k\u001b[m rows=1.255k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_stddev \u001b[m\u001b[0;33m 902 ns 856 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Aggregation/1255/real_time_cv \u001b[m\u001b[0;33m 0.26 % 0.25 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_mean \u001b[m\u001b[0;33m 308857 ns 307622 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.62k\u001b[m model_cost=639.9k\u001b[m output_rows=1.62k\u001b[m plan_cost=963.9k\u001b[m rhs_rows=1.62k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_median \u001b[m\u001b[0;33m 310398 ns 309035 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=1.62k\u001b[m model_cost=639.9k\u001b[m output_rows=1.62k\u001b[m plan_cost=963.9k\u001b[m rhs_rows=1.62k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_stddev \u001b[m\u001b[0;33m 3264 ns 3138 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time_cv \u001b[m\u001b[0;33m 1.06 % 1.02 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_mean \u001b[m\u001b[0;33m 665762 ns 662460 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=96\u001b[m model_cost=645.12k\u001b[m output_rows=96\u001b[m plan_cost=664.32k\u001b[m rhs_rows=96\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_median \u001b[m\u001b[0;33m 665768 ns 662568 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=96\u001b[m model_cost=645.12k\u001b[m output_rows=96\u001b[m plan_cost=664.32k\u001b[m rhs_rows=96\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_stddev \u001b[m\u001b[0;33m 740 ns 743 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time_cv \u001b[m\u001b[0;33m 0.11 % 0.11 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_mean \u001b[m\u001b[0;33m 389871 ns 385981 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=78\u001b[m model_cost=632.736k\u001b[m output_rows=6.084k\u001b[m plan_cost=648.336k\u001b[m rhs_rows=78\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_median \u001b[m\u001b[0;33m 389992 ns 386994 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=78\u001b[m model_cost=632.736k\u001b[m output_rows=6.084k\u001b[m plan_cost=648.336k\u001b[m rhs_rows=78\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_stddev \u001b[m\u001b[0;33m 304 ns 2179 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time_cv \u001b[m\u001b[0;33m 0.08 % 0.56 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_mean \u001b[m\u001b[0;33m 355274 ns 353349 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1M\u001b[m output_rows=10k\u001b[m plan_cost=1M\u001b[m rows=10k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_median \u001b[m\u001b[0;33m 354684 ns 352806 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1M\u001b[m output_rows=10k\u001b[m plan_cost=1M\u001b[m rows=10k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_stddev \u001b[m\u001b[0;33m 1338 ns 1283 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time_cv \u001b[m\u001b[0;33m 0.38 % 0.36 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Filter/10000/real_time_mean \u001b[m\u001b[0;33m 630249 ns 627282 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1M\u001b[m output_rows=5.12k\u001b[m plan_cost=2M\u001b[m rows=10k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Filter/10000/real_time_median \u001b[m\u001b[0;33m 630631 ns 627537 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1M\u001b[m output_rows=5.12k\u001b[m plan_cost=2M\u001b[m rows=10k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Filter/10000/real_time_stddev \u001b[m\u001b[0;33m 2386 ns 2314 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Filter/10000/real_time_cv \u001b[m\u001b[0;33m 0.38 % 0.37 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Projection/45455/real_time_mean \u001b[m\u001b[0;33m 4981809 ns 4954768 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.00001M\u001b[m output_rows=45.455k\u001b[m plan_cost=5.54551M\u001b[m rows=45.455k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Projection/45455/real_time_median \u001b[m\u001b[0;33m 4981602 ns 4954409 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.00001M\u001b[m output_rows=45.455k\u001b[m plan_cost=5.54551M\u001b[m rows=45.455k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Projection/45455/real_time_stddev \u001b[m\u001b[0;33m 27490 ns 27448 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Projection/45455/real_time_cv \u001b[m\u001b[0;33m 0.55 % 0.55 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Sort/6993/real_time_mean \u001b[m\u001b[0;33m 1141898 ns 1137278 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.999999M\u001b[m output_rows=6.993k\u001b[m plan_cost=1.6993M\u001b[m rows=6.993k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Sort/6993/real_time_median \u001b[m\u001b[0;33m 1142329 ns 1137450 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.999999M\u001b[m output_rows=6.993k\u001b[m plan_cost=1.6993M\u001b[m rows=6.993k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Sort/6993/real_time_stddev \u001b[m\u001b[0;33m 3860 ns 3679 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Sort/6993/real_time_cv \u001b[m\u001b[0;33m 0.34 % 0.32 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_mean \u001b[m\u001b[0;33m 533838 ns 531483 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.00011M\u001b[m output_rows=1.961k\u001b[m plan_cost=1.19621M\u001b[m rows=1.961k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_median \u001b[m\u001b[0;33m 532558 ns 530234 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=1.00011M\u001b[m output_rows=1.961k\u001b[m plan_cost=1.19621M\u001b[m rows=1.961k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_stddev \u001b[m\u001b[0;33m 2481 ns 2507 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time_cv \u001b[m\u001b[0;33m 0.46 % 0.47 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_mean \u001b[m\u001b[0;33m 485912 ns 483491 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=2.532k\u001b[m model_cost=1.00014M\u001b[m output_rows=2.532k\u001b[m plan_cost=1.50654M\u001b[m rhs_rows=2.532k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_median \u001b[m\u001b[0;33m 487101 ns 484497 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=2.532k\u001b[m model_cost=1.00014M\u001b[m output_rows=2.532k\u001b[m plan_cost=1.50654M\u001b[m rhs_rows=2.532k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_stddev \u001b[m\u001b[0;33m 2771 ns 2668 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time_cv \u001b[m\u001b[0;33m 0.57 % 0.55 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_mean \u001b[m\u001b[0;33m 1018585 ns 1009349 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=120\u001b[m model_cost=1.008M\u001b[m output_rows=120\u001b[m plan_cost=1.032M\u001b[m rhs_rows=120\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_median \u001b[m\u001b[0;33m 1007538 ns 1002010 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=120\u001b[m model_cost=1.008M\u001b[m output_rows=120\u001b[m plan_cost=1.032M\u001b[m rhs_rows=120\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_stddev \u001b[m\u001b[0;33m 21915 ns 15166 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time_cv \u001b[m\u001b[0;33m 2.15 % 1.50 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_mean \u001b[m\u001b[0;33m 582844 ns 579025 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=98\u001b[m model_cost=998.816k\u001b[m output_rows=9.604k\u001b[m plan_cost=1.01842M\u001b[m rhs_rows=98\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_median \u001b[m\u001b[0;33m 588409 ns 584070 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=98\u001b[m model_cost=998.816k\u001b[m output_rows=9.604k\u001b[m plan_cost=1.01842M\u001b[m rhs_rows=98\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_stddev \u001b[m\u001b[0;33m 16916 ns 16319 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time_cv \u001b[m\u001b[0;33m 2.90 % 2.82 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_mean \u001b[m\u001b[0;33m 1134758 ns 1128664 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24M\u001b[m output_rows=32.4k\u001b[m plan_cost=3.24M\u001b[m rows=32.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_median \u001b[m\u001b[0;33m 1122532 ns 1116998 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24M\u001b[m output_rows=32.4k\u001b[m plan_cost=3.24M\u001b[m rows=32.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_stddev \u001b[m\u001b[0;33m 23936 ns 23140 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time_cv \u001b[m\u001b[0;33m 2.11 % 2.05 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Filter/32400/real_time_mean \u001b[m\u001b[0;33m 2151576 ns 2140612 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24M\u001b[m output_rows=16.384k\u001b[m plan_cost=6.48M\u001b[m rows=32.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Filter/32400/real_time_median \u001b[m\u001b[0;33m 2114934 ns 2104733 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24M\u001b[m output_rows=16.384k\u001b[m plan_cost=6.48M\u001b[m rows=32.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Filter/32400/real_time_stddev \u001b[m\u001b[0;33m 75915 ns 75555 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Filter/32400/real_time_cv \u001b[m\u001b[0;33m 3.53 % 3.53 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Projection/147273/real_time_mean \u001b[m\u001b[0;33m 19626122 ns 19478967 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24001M\u001b[m output_rows=147.273k\u001b[m plan_cost=17.9673M\u001b[m rows=147.273k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Projection/147273/real_time_median \u001b[m\u001b[0;33m 19715132 ns 19566701 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24001M\u001b[m output_rows=147.273k\u001b[m plan_cost=17.9673M\u001b[m rows=147.273k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Projection/147273/real_time_stddev \u001b[m\u001b[0;33m 314025 ns 313346 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Projection/147273/real_time_cv \u001b[m\u001b[0;33m 1.60 % 1.61 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Sort/19636/real_time_mean \u001b[m\u001b[0;33m 3898469 ns 3882409 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.23994M\u001b[m output_rows=19.636k\u001b[m plan_cost=5.20354M\u001b[m rows=19.636k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Sort/19636/real_time_median \u001b[m\u001b[0;33m 3918521 ns 3902628 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.23994M\u001b[m output_rows=19.636k\u001b[m plan_cost=5.20354M\u001b[m rows=19.636k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Sort/19636/real_time_stddev \u001b[m\u001b[0;33m 48064 ns 46087 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Sort/19636/real_time_cv \u001b[m\u001b[0;33m 1.23 % 1.19 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_mean \u001b[m\u001b[0;33m 1853008 ns 1843719 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24003M\u001b[m output_rows=6.353k\u001b[m plan_cost=3.87533M\u001b[m rows=6.353k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_median \u001b[m\u001b[0;33m 1844847 ns 1836541 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=3.24003M\u001b[m output_rows=6.353k\u001b[m plan_cost=3.87533M\u001b[m rows=6.353k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_stddev \u001b[m\u001b[0;33m 34380 ns 34282 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time_cv \u001b[m\u001b[0;33m 1.86 % 1.86 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_mean \u001b[m\u001b[0;33m 1603236 ns 1594598 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=8.203k\u001b[m model_cost=3.24018M\u001b[m output_rows=8.203k\u001b[m plan_cost=4.88078M\u001b[m rhs_rows=8.203k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_median \u001b[m\u001b[0;33m 1607029 ns 1598368 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=8.203k\u001b[m model_cost=3.24018M\u001b[m output_rows=8.203k\u001b[m plan_cost=4.88078M\u001b[m rhs_rows=8.203k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_stddev \u001b[m\u001b[0;33m 9814 ns 9376 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time_cv \u001b[m\u001b[0;33m 0.61 % 0.59 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_mean \u001b[m\u001b[0;33m 2910806 ns 2897804 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=215\u001b[m model_cost=3.23575M\u001b[m output_rows=215\u001b[m plan_cost=3.27875M\u001b[m rhs_rows=215\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_median \u001b[m\u001b[0;33m 2924068 ns 2910697 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=215\u001b[m model_cost=3.23575M\u001b[m output_rows=215\u001b[m plan_cost=3.27875M\u001b[m rhs_rows=215\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_stddev \u001b[m\u001b[0;33m 23345 ns 22420 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time_cv \u001b[m\u001b[0;33m 0.80 % 0.77 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_mean \u001b[m\u001b[0;33m 1943488 ns 1924736 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=177\u001b[m model_cost=3.25822M\u001b[m output_rows=31.329k\u001b[m plan_cost=3.29362M\u001b[m rhs_rows=177\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_median \u001b[m\u001b[0;33m 1913052 ns 1894678 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=177\u001b[m model_cost=3.25822M\u001b[m output_rows=31.329k\u001b[m plan_cost=3.29362M\u001b[m rhs_rows=177\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_stddev \u001b[m\u001b[0;33m 65998 ns 64519 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time_cv \u001b[m\u001b[0;33m 3.40 % 3.35 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_mean \u001b[m\u001b[0;33m 3382780 ns 3353599 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76M\u001b[m output_rows=67.6k\u001b[m plan_cost=6.76M\u001b[m rows=67.6k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_median \u001b[m\u001b[0;33m 3386841 ns 3356555 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76M\u001b[m output_rows=67.6k\u001b[m plan_cost=6.76M\u001b[m rows=67.6k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_stddev \u001b[m\u001b[0;33m 63869 ns 60408 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time_cv \u001b[m\u001b[0;33m 1.89 % 1.80 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Filter/67600/real_time_mean \u001b[m\u001b[0;33m 4670764 ns 4640027 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76M\u001b[m output_rows=33.808k\u001b[m plan_cost=13.52M\u001b[m rows=67.6k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Filter/67600/real_time_median \u001b[m\u001b[0;33m 4712211 ns 4676999 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76M\u001b[m output_rows=33.808k\u001b[m plan_cost=13.52M\u001b[m rows=67.6k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Filter/67600/real_time_stddev \u001b[m\u001b[0;33m 184523 ns 180474 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Filter/67600/real_time_cv \u001b[m\u001b[0;33m 3.95 % 3.89 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Projection/307273/real_time_mean \u001b[m\u001b[0;33m 41478841 ns 41112802 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76001M\u001b[m output_rows=307.273k\u001b[m plan_cost=37.4873M\u001b[m rows=307.273k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Projection/307273/real_time_median \u001b[m\u001b[0;33m 41540129 ns 41133354 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76001M\u001b[m output_rows=307.273k\u001b[m plan_cost=37.4873M\u001b[m rows=307.273k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Projection/307273/real_time_stddev \u001b[m\u001b[0;33m 138078 ns 177153 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Projection/307273/real_time_cv \u001b[m\u001b[0;33m 0.33 % 0.43 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Sort/38409/real_time_mean \u001b[m\u001b[0;33m 10126082 ns 10063562 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.75998M\u001b[m output_rows=38.409k\u001b[m plan_cost=10.6009M\u001b[m rows=38.409k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Sort/38409/real_time_median \u001b[m\u001b[0;33m 10310140 ns 10245214 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.75998M\u001b[m output_rows=38.409k\u001b[m plan_cost=10.6009M\u001b[m rows=38.409k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Sort/38409/real_time_stddev \u001b[m\u001b[0;33m 323868 ns 315894 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Sort/38409/real_time_cv \u001b[m\u001b[0;33m 3.20 % 3.14 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_mean \u001b[m\u001b[0;33m 4259250 ns 4235870 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76005M\u001b[m output_rows=13.255k\u001b[m plan_cost=8.08555M\u001b[m rows=13.255k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_median \u001b[m\u001b[0;33m 4275696 ns 4251838 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=6.76005M\u001b[m output_rows=13.255k\u001b[m plan_cost=8.08555M\u001b[m rows=13.255k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_stddev \u001b[m\u001b[0;33m 49296 ns 48716 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time_cv \u001b[m\u001b[0;33m 1.16 % 1.15 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_mean \u001b[m\u001b[0;33m 3770392 ns 3743836 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=17.114k\u001b[m model_cost=6.76003M\u001b[m output_rows=17.114k\u001b[m plan_cost=10.1828M\u001b[m rhs_rows=17.114k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_median \u001b[m\u001b[0;33m 3738229 ns 3712971 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=17.114k\u001b[m model_cost=6.76003M\u001b[m output_rows=17.114k\u001b[m plan_cost=10.1828M\u001b[m rhs_rows=17.114k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_stddev \u001b[m\u001b[0;33m 99152 ns 95508 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time_cv \u001b[m\u001b[0;33m 2.63 % 2.55 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_mean \u001b[m\u001b[0;33m 5772993 ns 5750595 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=311\u001b[m model_cost=6.77047M\u001b[m output_rows=311\u001b[m plan_cost=6.83267M\u001b[m rhs_rows=311\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_median \u001b[m\u001b[0;33m 5730610 ns 5708990 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=311\u001b[m model_cost=6.77047M\u001b[m output_rows=311\u001b[m plan_cost=6.83267M\u001b[m rhs_rows=311\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_stddev \u001b[m\u001b[0;33m 83980 ns 82214 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time_cv \u001b[m\u001b[0;33m 1.45 % 1.43 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_mean \u001b[m\u001b[0;33m 5119950 ns 5056736 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=255\u001b[m model_cost=6.7626M\u001b[m output_rows=65.025k\u001b[m plan_cost=6.8136M\u001b[m rhs_rows=255\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_median \u001b[m\u001b[0;33m 5104559 ns 5039196 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=255\u001b[m model_cost=6.7626M\u001b[m output_rows=65.025k\u001b[m plan_cost=6.8136M\u001b[m rhs_rows=255\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_stddev \u001b[m\u001b[0;33m 64890 ns 62803 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time_cv \u001b[m\u001b[0;33m 1.27 % 1.24 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_mean \u001b[m\u001b[0;33m 6994680 ns 6932658 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.25M\u001b[m output_rows=122.5k\u001b[m plan_cost=12.25M\u001b[m rows=122.5k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_median \u001b[m\u001b[0;33m 6989477 ns 6923442 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.25M\u001b[m output_rows=122.5k\u001b[m plan_cost=12.25M\u001b[m rows=122.5k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_stddev \u001b[m\u001b[0;33m 222064 ns 216550 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time_cv \u001b[m\u001b[0;33m 3.17 % 3.12 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Filter/122500/real_time_mean \u001b[m\u001b[0;33m 8849196 ns 8783131 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.25M\u001b[m output_rows=61.44k\u001b[m plan_cost=24.5M\u001b[m rows=122.5k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Filter/122500/real_time_median \u001b[m\u001b[0;33m 8814582 ns 8751934 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.25M\u001b[m output_rows=61.44k\u001b[m plan_cost=24.5M\u001b[m rows=122.5k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Filter/122500/real_time_stddev \u001b[m\u001b[0;33m 72027 ns 68512 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Filter/122500/real_time_cv \u001b[m\u001b[0;33m 0.81 % 0.78 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Projection/556818/real_time_mean \u001b[m\u001b[0;33m 74688525 ns 74025484 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.25M\u001b[m output_rows=556.818k\u001b[m plan_cost=67.9318M\u001b[m rows=556.818k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Projection/556818/real_time_median \u001b[m\u001b[0;33m 74892891 ns 74148114 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.25M\u001b[m output_rows=556.818k\u001b[m plan_cost=67.9318M\u001b[m rows=556.818k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Projection/556818/real_time_stddev \u001b[m\u001b[0;33m 1413431 ns 1341941 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Projection/556818/real_time_cv \u001b[m\u001b[0;33m 1.89 % 1.81 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Sort/65536/real_time_mean \u001b[m\u001b[0;33m 20585367 ns 20385385 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.2552M\u001b[m output_rows=65.536k\u001b[m plan_cost=18.8088M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Sort/65536/real_time_median \u001b[m\u001b[0;33m 20777740 ns 20568346 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.2552M\u001b[m output_rows=65.536k\u001b[m plan_cost=18.8088M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Sort/65536/real_time_stddev \u001b[m\u001b[0;33m 515652 ns 489843 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Sort/65536/real_time_cv \u001b[m\u001b[0;33m 2.50 % 2.40 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_mean \u001b[m\u001b[0;33m 10093168 ns 9992808 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.2502M\u001b[m output_rows=24.02k\u001b[m plan_cost=14.6522M\u001b[m rows=24.02k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_median \u001b[m\u001b[0;33m 9945731 ns 9865480 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=12.2502M\u001b[m output_rows=24.02k\u001b[m plan_cost=14.6522M\u001b[m rows=24.02k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_stddev \u001b[m\u001b[0;33m 470190 ns 455759 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time_cv \u001b[m\u001b[0;33m 4.66 % 4.56 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_mean \u001b[m\u001b[0;33m 7152001 ns 7086308 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=31.013k\u001b[m model_cost=12.2501M\u001b[m output_rows=31.013k\u001b[m plan_cost=18.4527M\u001b[m rhs_rows=31.013k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_median \u001b[m\u001b[0;33m 7141940 ns 7077427 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=31.013k\u001b[m model_cost=12.2501M\u001b[m output_rows=31.013k\u001b[m plan_cost=18.4527M\u001b[m rhs_rows=31.013k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_stddev \u001b[m\u001b[0;33m 44807 ns 43542 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time_cv \u001b[m\u001b[0;33m 0.63 % 0.61 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_mean \u001b[m\u001b[0;33m 10945706 ns 10888305 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=418\u001b[m model_cost=12.2307M\u001b[m output_rows=418\u001b[m plan_cost=12.3143M\u001b[m rhs_rows=418\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_median \u001b[m\u001b[0;33m 10932178 ns 10886830 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=418\u001b[m model_cost=12.2307M\u001b[m output_rows=418\u001b[m plan_cost=12.3143M\u001b[m rhs_rows=418\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_stddev \u001b[m\u001b[0;33m 113420 ns 97491 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time_cv \u001b[m\u001b[0;33m 1.04 % 0.90 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_mean \u001b[m\u001b[0;33m 10279043 ns 10138684 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=343\u001b[m model_cost=12.2355M\u001b[m output_rows=117.649k\u001b[m plan_cost=12.3041M\u001b[m rhs_rows=343\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_median \u001b[m\u001b[0;33m 9569170 ns 9452603 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=343\u001b[m model_cost=12.2355M\u001b[m output_rows=117.649k\u001b[m plan_cost=12.3041M\u001b[m rhs_rows=343\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_stddev \u001b[m\u001b[0;33m 1404724 ns 1368081 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time_cv \u001b[m\u001b[0;33m 13.67 % 13.49 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_mean \u001b[m\u001b[0;33m 13336847 ns 13225191 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=260.1k\u001b[m plan_cost=26.01M\u001b[m rows=260.1k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_median \u001b[m\u001b[0;33m 12900366 ns 12805119 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=260.1k\u001b[m plan_cost=26.01M\u001b[m rows=260.1k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_stddev \u001b[m\u001b[0;33m 1172219 ns 1137751 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time_cv \u001b[m\u001b[0;33m 8.79 % 8.60 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Filter/260100/real_time_mean \u001b[m\u001b[0;33m 18594129 ns 18489997 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=130.052k\u001b[m plan_cost=52.02M\u001b[m rows=260.1k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Filter/260100/real_time_median \u001b[m\u001b[0;33m 18564079 ns 18476515 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=130.052k\u001b[m plan_cost=52.02M\u001b[m rows=260.1k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Filter/260100/real_time_stddev \u001b[m\u001b[0;33m 204348 ns 192770 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Filter/260100/real_time_cv \u001b[m\u001b[0;33m 1.10 % 1.04 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_mean \u001b[m\u001b[0;33m 184939680 ns 181161386 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=1.18227M\u001b[m plan_cost=144.237M\u001b[m rows=1.18227M\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_median \u001b[m\u001b[0;33m 185574441 ns 179442372 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=1.18227M\u001b[m plan_cost=144.237M\u001b[m rows=1.18227M\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_stddev \u001b[m\u001b[0;33m 7745905 ns 7726947 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Projection/1182273/real_time_cv \u001b[m\u001b[0;33m 4.19 % 4.27 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Sort/131364/real_time_mean \u001b[m\u001b[0;33m 88475598 ns 87190889 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.0101M\u001b[m output_rows=131.364k\u001b[m plan_cost=39.1465M\u001b[m rows=131.364k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Sort/131364/real_time_median \u001b[m\u001b[0;33m 86036433 ns 85027458 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.0101M\u001b[m output_rows=131.364k\u001b[m plan_cost=39.1465M\u001b[m rows=131.364k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Sort/131364/real_time_stddev \u001b[m\u001b[0;33m 9059453 ns 9329788 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Sort/131364/real_time_cv \u001b[m\u001b[0;33m 10.24 % 10.70 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_mean \u001b[m\u001b[0;33m 31279068 ns 30916289 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=51k\u001b[m plan_cost=31.11M\u001b[m rows=51k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_median \u001b[m\u001b[0;33m 31578403 ns 31313289 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=26.01M\u001b[m output_rows=51k\u001b[m plan_cost=31.11M\u001b[m rows=51k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_stddev \u001b[m\u001b[0;33m 673755 ns 741005 ns \u001b[m\u001b[0;36m 3\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time_cv \u001b[m\u001b[0;33m 2.15 % 2.40 % \u001b[m\u001b[0;36m 3\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_mean \u001b[m\u001b[0;33m 14420059 ns 14342223 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=65.848k\u001b[m model_cost=26.01M\u001b[m output_rows=65.848k\u001b[m plan_cost=39.1796M\u001b[m rhs_rows=65.848k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_median \u001b[m\u001b[0;33m 13858205 ns 13798367 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=65.848k\u001b[m model_cost=26.01M\u001b[m output_rows=65.848k\u001b[m plan_cost=39.1796M\u001b[m rhs_rows=65.848k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_stddev \u001b[m\u001b[0;33m 993851 ns 957202 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time_cv \u001b[m\u001b[0;33m 6.89 % 6.67 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_mean \u001b[m\u001b[0;33m 20663763 ns 20628319 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=610\u001b[m model_cost=26.047M\u001b[m output_rows=610\u001b[m plan_cost=26.169M\u001b[m rhs_rows=610\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_median \u001b[m\u001b[0;33m 20663114 ns 20627234 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=610\u001b[m model_cost=26.047M\u001b[m output_rows=610\u001b[m plan_cost=26.169M\u001b[m rhs_rows=610\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_stddev \u001b[m\u001b[0;33m 62693 ns 60912 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time_cv \u001b[m\u001b[0;33m 0.30 % 0.30 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_mean \u001b[m\u001b[0;33m 16636418 ns 16551291 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=500\u001b[m model_cost=26M\u001b[m output_rows=250k\u001b[m plan_cost=26.1M\u001b[m rhs_rows=500\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_median \u001b[m\u001b[0;33m 16635667 ns 16552195 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=500\u001b[m model_cost=26M\u001b[m output_rows=250k\u001b[m plan_cost=26.1M\u001b[m rhs_rows=500\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_stddev \u001b[m\u001b[0;33m 77985 ns 77061 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0\u001b[m model_cost=0\u001b[m output_rows=0\u001b[m plan_cost=0\u001b[m rhs_rows=0\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time_cv \u001b[m\u001b[0;33m 0.47 % 0.47 % \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=0.00%\u001b[m model_cost=0.00%\u001b[m output_rows=0.00%\u001b[m plan_cost=0.00%\u001b[m rhs_rows=0.00%\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "!cd .. && timeout 300s ./build-release/bin/benchmarks --benchmark_filter='^OperatorCostMatched/' --benchmark_repetitions=3 --benchmark_display_aggregates_only=true --benchmark_out=research/benchmark-results/operator-cost-matched.json --benchmark_out_format=json" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "b16b9fb4", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import re\n", + "from pathlib import Path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "RESULTS = Path(\"benchmark-results\")\n", + "UNIT_TO_MS = {\"ns\": 1e-6, \"us\": 1e-3, \"ms\": 1.0, \"s\": 1e3}\n", + "\n", + "def load_benchmark_result(filename):\n", + " path = RESULTS / filename\n", + " try:\n", + " return json.loads(path.read_text())\n", + " except json.JSONDecodeError as exc:\n", + " raise RuntimeError(\n", + " f\"{path} is not valid JSON. The benchmark run probably timed out or was interrupted; rerun the cell that creates it.\"\n", + " ) from exc\n", + "\n", + "def mean_rows(filename):\n", + " data = load_benchmark_result(filename)\n", + " return [row for row in data[\"benchmarks\"] if row.get(\"aggregate_name\") == \"mean\"]\n", + "\n", + "def time_ms(row):\n", + " return row[\"real_time\"] * UNIT_TO_MS[row[\"time_unit\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "d3172304", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "( executor query mode \\\n", + " 0 InterpretedExpressionExecutor kSimpleSelectSmall Naive \n", + " 1 InterpretedExpressionExecutor kSimpleSelectSmall Optimized \n", + " 2 CachedJitCompiledExpressionExecutor kSimpleSelectSmall Naive \n", + " 3 CachedJitCompiledExpressionExecutor kSimpleSelectSmall Optimized \n", + " 4 InterpretedExpressionExecutor kJoinSmall Naive \n", + " 5 InterpretedExpressionExecutor kJoinSmall Optimized \n", + " 6 CachedJitCompiledExpressionExecutor kJoinSmall Naive \n", + " 7 CachedJitCompiledExpressionExecutor kJoinSmall Optimized \n", + " 8 InterpretedExpressionExecutor kComplex5 Naive \n", + " 9 InterpretedExpressionExecutor kComplex5 Optimized \n", + " 10 CachedJitCompiledExpressionExecutor kComplex5 Naive \n", + " 11 CachedJitCompiledExpressionExecutor kComplex5 Optimized \n", + " \n", + " time_ms \n", + " 0 0.020437 \n", + " 1 0.020149 \n", + " 2 0.019900 \n", + " 3 0.019747 \n", + " 4 0.066637 \n", + " 5 0.043217 \n", + " 6 0.066032 \n", + " 7 0.042778 \n", + " 8 0.082328 \n", + " 9 0.081952 \n", + " 10 0.078012 \n", + " 11 0.077614 ,\n", + " query executor mode time_ms\n", + " 0 q2.1 Interpreted Naive 301.341096\n", + " 1 q2.1 Interpreted Optimized 65.052676\n", + " 2 q1.3 Interpreted Naive 166.889657\n", + " 3 q1.3 Interpreted Optimized 64.166660\n", + " 4 q1.2 Interpreted Naive 167.637639\n", + " 5 q1.2 Interpreted Optimized 63.248979\n", + " 6 q3.3 Interpreted Naive 1583.154249\n", + " 7 q3.3 Interpreted Optimized 54.401711\n", + " 8 q3.4 Interpreted Naive 1587.095575\n", + " 9 q3.4 Interpreted Optimized 54.158258\n", + " 10 q4.3 Interpreted Naive 350.946603\n", + " 11 q4.3 Interpreted Optimized 59.861883\n", + " 12 q1.1 Interpreted Naive 172.823377\n", + " 13 q1.1 Interpreted Optimized 61.508565\n", + " 14 q3.2 Interpreted Naive 1590.022374\n", + " 15 q3.2 Interpreted Optimized 56.053863\n", + " 16 q2.2 Interpreted Naive 300.505212\n", + " 17 q2.2 Interpreted Optimized 58.723648\n", + " 18 q3.1 Interpreted Naive 1629.954130\n", + " 19 q3.1 Interpreted Optimized 63.387682\n", + " 20 q4.2 Interpreted Naive 354.185497\n", + " 21 q4.2 Interpreted Optimized 163.329450\n", + " 22 q4.1 Interpreted Naive 351.491377\n", + " 23 q4.1 Interpreted Optimized 161.923414\n", + " 24 q2.3 Interpreted Naive 303.553976\n", + " 25 q2.3 Interpreted Optimized 58.674681)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "synthetic_pattern = re.compile(\n", + " r\"BM_SQL<(?P[^,]+), (?P[^,]+), PlannerMode::k(?P[^>]+)>/real_time_mean\"\n", + ")\n", + "synthetic = []\n", + "for row in mean_rows(\"query.json\"):\n", + " match = synthetic_pattern.fullmatch(row[\"name\"])\n", + " synthetic.append({**match.groupdict(), \"time_ms\": time_ms(row)})\n", + "synthetic = pd.DataFrame(synthetic)\n", + "\n", + "ssb = []\n", + "for row in mean_rows(\"ssb-sf01.json\"):\n", + " _, query, executor, mode, _ = row[\"name\"].split(\"/\")\n", + " ssb.append({\"query\": query, \"executor\": executor, \"mode\": mode, \"time_ms\": time_ms(row)})\n", + "ssb = pd.DataFrame(ssb)\n", + "\n", + "synthetic, ssb" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "f4e060f2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "( operator input_rows model_cost time_ms\n", + " 0 SeqScan 1024 102400.0 0.039728\n", + " 1 SeqScan 2048 204800.0 0.068177\n", + " 2 SeqScan 4096 409600.0 0.162377\n", + " 3 SeqScan 8192 819200.0 0.360639\n", + " 4 SeqScan 16384 1638400.0 0.791876\n", + " .. ... ... ... ...\n", + " 59 NestedLoopCrossJoin 64 425984.0 0.279630\n", + " 60 NestedLoopCrossJoin 128 1703936.0 0.909990\n", + " 61 NestedLoopCrossJoin 256 6815744.0 5.038775\n", + " 62 NestedLoopCrossJoin 512 27262976.0 22.572298\n", + " 63 NestedLoopCrossJoin 1024 109051904.0 117.439223\n", + " \n", + " [64 rows x 4 columns],\n", + " target_cost operator input_rows model_cost time_ms\n", + " 0 640000 SeqScan 6400 640000.0 0.327112\n", + " 1 640000 Filter 6400 640000.0 0.423529\n", + " 2 640000 Projection 29091 640002.0 2.969473\n", + " 3 640000 Sort 4476 640068.0 0.708003\n", + " 4 640000 Aggregation 1255 640050.0 0.350350\n", + " 5 640000 HashJoin 1620 639900.0 0.308857\n", + " 6 640000 NestedLoopJoin 96 645120.0 0.665762\n", + " 7 640000 NestedLoopCrossJoin 78 632736.0 0.389871\n", + " 8 1000000 SeqScan 10000 1000000.0 0.355274\n", + " 9 1000000 Filter 10000 1000000.0 0.630249\n", + " 10 1000000 Projection 45455 1000010.0 4.981809\n", + " 11 1000000 Sort 6993 999999.0 1.141898\n", + " 12 1000000 Aggregation 1961 1000110.0 0.533838\n", + " 13 1000000 HashJoin 2532 1000140.0 0.485912\n", + " 14 1000000 NestedLoopJoin 120 1008000.0 1.018585\n", + " 15 1000000 NestedLoopCrossJoin 98 998816.0 0.582844\n", + " 16 3240000 SeqScan 32400 3240000.0 1.134758\n", + " 17 3240000 Filter 32400 3240000.0 2.151576\n", + " 18 3240000 Projection 147273 3240006.0 19.626122\n", + " 19 3240000 Sort 19636 3239940.0 3.898469\n", + " 20 3240000 Aggregation 6353 3240030.0 1.853008\n", + " 21 3240000 HashJoin 8203 3240185.0 1.603236\n", + " 22 3240000 NestedLoopJoin 215 3235750.0 2.910806\n", + " 23 3240000 NestedLoopCrossJoin 177 3258216.0 1.943488\n", + " 24 6760000 SeqScan 67600 6760000.0 3.382780\n", + " 25 6760000 Filter 67600 6760000.0 4.670764\n", + " 26 6760000 Projection 307273 6760006.0 41.478841\n", + " 27 6760000 Sort 38409 6759984.0 10.126082\n", + " 28 6760000 Aggregation 13255 6760050.0 4.259250\n", + " 29 6760000 HashJoin 17114 6760030.0 3.770392\n", + " 30 6760000 NestedLoopJoin 311 6770470.0 5.772993\n", + " 31 6760000 NestedLoopCrossJoin 255 6762600.0 5.119950\n", + " 32 12250000 SeqScan 122500 12250000.0 6.994680\n", + " 33 12250000 Filter 122500 12250000.0 8.849196\n", + " 34 12250000 Projection 556818 12249996.0 74.688525\n", + " 35 12250000 Sort 65536 12255232.0 20.585367\n", + " 36 12250000 Aggregation 24020 12250200.0 10.093168\n", + " 37 12250000 HashJoin 31013 12250135.0 7.152001\n", + " 38 12250000 NestedLoopJoin 418 12230680.0 10.945706\n", + " 39 12250000 NestedLoopCrossJoin 343 12235496.0 10.279043\n", + " 40 26010000 SeqScan 260100 26010000.0 13.336847\n", + " 41 26010000 Filter 260100 26010000.0 18.594129\n", + " 42 26010000 Projection 1182273 26010006.0 184.939680\n", + " 43 26010000 Sort 131364 26010072.0 88.475598\n", + " 44 26010000 Aggregation 51000 26010000.0 31.279068\n", + " 45 26010000 HashJoin 65848 26009960.0 14.420059\n", + " 46 26010000 NestedLoopJoin 610 26047000.0 20.663763\n", + " 47 26010000 NestedLoopCrossJoin 500 26000000.0 16.636418)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "operator = []\n", + "for row in mean_rows(\"operator-cost.json\"):\n", + " parts = row[\"name\"].split(\"/\")\n", + " operator.append({\n", + " \"operator\": parts[1],\n", + " \"input_rows\": max(map(int, parts[2:-1])),\n", + " \"model_cost\": row[\"model_cost\"],\n", + " \"time_ms\": time_ms(row),\n", + " })\n", + "operator = pd.DataFrame(operator)\n", + "\n", + "matched = []\n", + "for row in mean_rows(\"operator-cost-matched.json\"):\n", + " parts = row[\"name\"].split(\"/\")\n", + " matched.append({\n", + " \"target_cost\": int(parts[1].split(\":\")[1]),\n", + " \"operator\": parts[2],\n", + " \"input_rows\": max(map(int, parts[3:-1])),\n", + " \"model_cost\": row[\"model_cost\"],\n", + " \"time_ms\": time_ms(row),\n", + " })\n", + "matched = pd.DataFrame(matched)\n", + "\n", + "operator, matched" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "2075a20f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABW0AAAHqCAYAAAB/bWzAAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAbDxJREFUeJzt3XlYVeX+/vF7y6iAKJggioqzJmpqmVaKOZtS2kkLc64sS8MxhzqilVMnh7TB0sQcO6fUOpU45ZDzXMcypzBRQUwJRREQ1u+PfqxvO0BBwb1sv1/Xta+r9axnr/1Z+6Obc24fnm0zDMMQAAAAAAAAAMASijm6AAAAAAAAAADA/yG0BQAAAAAAAAALIbQFAAAAAAAAAAshtAUAAAAAAAAACyG0BQAAAAAAAAALIbQFAAAAAAAAAAshtAUAAAAAAAAACyG0BQAAAAAAAAALIbQFAAAAAAAAAAshtAUAAJYWHR0tm80mT09P/frrrznOh4WFqW7dujd17T59+qhy5cq3WKG1Va5cWX369Lntr/vTTz8pKipKJ06cyHGuKN53R/WysN/fqKgo2Ww2/fbbb4V2zRvZuHGjbDabNm7cWCTXP3HihGw2m6Kjowvtmtnvk5UdP35cHh4e2r59uzlmGIaWLVumhx56SGXLlpWnp6cqVKigdu3aae7cuXbPP3/+vEaPHq06derIy8tLvr6+qlWrlnr27KkffvjBnJf9Gfnnx1133aWwsDB99dVXdtdMSkpSqVKltHLlyiK9dwAAcOsIbQEAwB0hLS1Nr776aqFe87XXXtOKFSsK9Zr4w08//aTx48fnGtoWxftOL29ew4YNtX37djVs2NDRpfytDB8+XG3atFHTpk3NsdGjR+upp55S7dq1NXfuXK1atUpvvPGGAgIC9MUXX5jzUlJSdP/99ys6OlrPPPOMvvzySy1evFjPPfecYmNjdeDAgRyvN3/+fG3fvl3btm3Thx9+KBcXF3Xu3Fn//e9/zTmlS5fWkCFDNGLECKWnpxfp/QMAgFvj6ugCAAAA8qN9+/ZasmSJhg8frvr16xfKNatWrVoo10HBFMX7Ti9vXsmSJXX//fc7uoy/lUOHDmnlypWKiYkxx1JTUzVjxgz16tVLH374od38Pn36KCsryzz+z3/+o2PHjunbb79Vy5Yt7eYOHTrUbm62unXrqnHjxuZx+/btVbp0aS1dulSdO3c2x59//nm98cYb+uyzzxQREXHL9woAAIoGK20BAMAdYeTIkfL399crr7xyw7nvvvuumjdvrrJly8rLy0uhoaGaOnWqMjIy7Ob99Vfq77nnHj300EM5rpeZmany5cura9eu5lh6erreeOMN1apVSx4eHrrrrrvUt29fnTt37ob17dmzR08++aQqV66s4sWLq3LlynrqqadybP+Q/WvPGzZs0AsvvKAyZcrI399fXbt21ZkzZ+zmZmRkaOTIkQoMDFSJEiX04IMPateuXTesJduFCxc0cOBAlS9fXu7u7qpSpYrGjh2rtLQ0u3k2m00vvfSS5syZoxo1asjDw0N16tTRsmXL7Op+4oknJEktW7Y0f2U7+9fjc9vKIPu68+fPV82aNVW8eHE1btxYO3bskGEYeuuttxQSEiJvb289/PDDOnbsmN3z/3rN7F+fz+3x5+0M8tvHW3l/s7cHmDp1qt58801VrFhRnp6eaty4sdavX5/rc86ePaunnnpKvr6+CggIUL9+/ZScnGyeb9WqlWrVqiXDMOyeZxiGqlWrpkceecQce//991W/fn15e3vLx8dHtWrV0pgxY8zzeW2PsHPnTnXu3Fn+/v7y9PRU1apVFRkZaZ4/duyY+vbtq+rVq6tEiRIqX768OnfurP/973/5el/+KruORYsWaejQoQoMDFTx4sXVokUL7d+//4bP//TTT9W2bVuVK1dOxYsXV+3atTVq1ChdvnzZbl6fPn3k7e2tY8eOqWPHjvL29lZwcLCGDRuW48/7jd67vLz//vsKDAxUmzZtzLHLly8rLS1N5cqVy/U5xYr93/81O3/+vCTla25ePD095e7uLjc3N7vxgIAAtWnTRh988MENrwEAAByH0BYAANwRfHx89Oqrr2r16tX69ttvrzv3+PHjioiI0MKFC/XVV1+pf//+euuttzRgwIDrPq9v377asmWLjh49aje+Zs0anTlzRn379pUkZWVl6dFHH9XkyZMVERGhr7/+WpMnT9batWsVFham1NTU677OiRMnVLNmTc2YMUOrV6/WlClTFB8fr3vvvTfXvUyfeeYZubm5acmSJZo6dao2btyop59+2m7Os88+q3/961/q1auXvvjiCz3++OPq2rWrkpKSrluLJF29elUtW7bUJ598oqFDh+rrr7/W008/ralTp9oF1dm+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5+dnd+zu7n7d8atXr97wmmfOnFH79u1VoUIFLV++3HyulP8+3ur7my2v9y09PV0pKSl24zfqufTHn9XixYube5O+++67Kl68uPr162fO6dmzpz7++GP9+uuvevzxx1W2bFk1adLE/POdm+z9fCtUqHDd+xk6dKhee+01PfbYY/rvf/+rnTt3avfu3apfv/4N/55dT17v0/X+bKWkpOihhx7Szp079cYbb2jjxo3avXu3li9fLinn3/sSJUrkCKQ9PDzs/kzdzHv359fKK/B2c3NTu3bt9Oabb2r16tWKi4tTWFiYvvrqK61atcpubkBAgPr27asPPvhAP/zwgzZt2iR3d3e9/PLLOa5bu3ZtNW7cWI0bN1b79u01Z84ctW3bViNHjsyxDUJ2bbfSJwAAULTyvzQAAADAAmw2m6ZMmaI2bdrk+AZ2SVq5cqUuX76s5cuXq1KlSub4gQMH8nX9du3aKSgoSPPnz1e7du00f/58NWnSRHXq1DHnZH8h2J+/Gf7PfHx88rx+cnKyvvrqK40bN06jRo0yx9PS0nThwoV81fhX2QFfQkKCypcvb45fu3YtXyGqv7+/du7cKcMw7ILbxMREXbt2TWXKlLGbn5CQkOMa2WMFCTGL2sWLF9WxY0dlZWXpm2++ka+vr935/PbxVt/fbHm9b+7u7vL29s73dbL5+vqqd+/emjt3roYPH6758+crIiJCpUqVspvXt29f9e3bV5cvX9bmzZs1btw4derUSUeOHLH7O5LtrrvukqQbrppetGiRevXqZe5fnO23337LUUNB5PU+Xe/P1rfffqszZ85o48aN5upaSbe8Z2tB3ztJ5t+X/P599vf3V2RkpDZu3KiDBw+qY8eOec5t3ry52rZtq5UrVyoxMTHX1bx/Vq9ePa1evVpHjhzRfffdZ45n1/bXv9sAAMA6WGkLAADuOK1bt1abNm00YcKEHCsUs0PH7JWJkmQYhj766KN8XdvFxUU9e/bUypUr9d1332nPnj12KxclqVOnTjp//rwyMzPNlW1/ftSsWTPP69tsNhmGYVefJM2dOzfHr8jnV1hYmCRp8eLFduP//ve/de3atRs+v1WrVkpJSdHKlSvtxj/55BPz/J+tX7/e7gupMjMz9emnn6pq1arm6szcVobeTunp6erSpYtOnDihVatW5bpqNL99vNX3N9vy5cvtVnJeunRJ//3vf/XQQw/JxcXlJu5SGjx4sH777Tf94x//0O+//66XXnopz7leXl7q0KGDxo4dq/T0dP3444+5zqtRo4aqVq2qjz/+WGlpaXlez2az5fhz/PXXX9/yr9wvXbrUbpuAX3/9Vdu2bTP7kFctknLUM2fOnFuqJVt+3zvpj+0cihcvruPHj9uNZ2Rk5BnyZ2/fEBQUJOmPVeDZW7T8WWZmpo4ePaoSJUrkKxjP/seq7CA+W/aXuv35H6MAAIC1sNIWAADckaZMmaJGjRopMTFRd999tznepk0bubu766mnntLIkSN19epVvf/++0pKSsr3tfv166cpU6YoIiJCxYsXV/fu3e3OP/nkk1q8eLE6duyol19+Wffdd5/c3Nx06tQpbdiwQY8++qi6dOmS67VLliyp5s2b66233lKZMmVUuXJlbdq0SfPmzbvp1Ym1a9fW008/rRkzZsjNzU2tW7fWwYMH9a9//UslS5a84fN79eqld999V71799aJEycUGhqqLVu2aOLEierYsaNat25tN79MmTJ6+OGH9dprr8nLy0vvvfeefv75Zy1btsycU7duXUnShx9+KB8fH3l6eiokJOS2rcQdMmSIvv32W02cOFEpKSnasWOHee6uu+5S1apV893HW31/s7m4uKhNmzYaOnSosrKyNGXKFF28eFHjx4+/6fusUaOG2rdvr1WrVunBBx9U/fr17c4/++yzKl68uB544AGVK1dOCQkJmjRpknx9fXXvvffmed13331XnTt31v33368hQ4aoYsWKOnnypFavXm2G1506dVJ0dLRq1aqlevXqae/evXrrrbduuK3CjSQmJqpLly569tlnlZycrHHjxsnT01OjR4/O8znNmjVT6dKl9fzzz2vcuHFyc3PT4sWL9f333990HTf73rm7u6tp06Z2f+akP1bZV65cWU888YRat26t4OBgpaSkaOPGjZo5c6Zq166trl27SpIWLlyoOXPmKCIiQvfee698fX116tQpzZ07Vz/++KP++c9/2m31IUkHDx40/xHh/PnzWr58udauXasuXbooJCTEbu6OHTvk7++v0NDQm35/AABA0SK0BQAAd6R77rlHTz31lJYsWWI3XqtWLX3++ed69dVX1bVrV/n7+ysiIkJDhw5Vhw4d8nXtGjVqqFmzZtq2bZt69OiR49fqXVxc9OWXX2rmzJlauHChJk2aJFdXV1WoUEEtWrS4YRCyZMkSvfzyyxo5cqSuXbumBx54QGvXrtUjjzxSsDfhT+bNm6eAgABFR0frnXfeUYMGDfT555/rySefvOFzPT09tWHDBo0dO1ZvvfWWzp07p/Lly2v48OEaN25cjvnh4eG6++679eqrr+rkyZOqWrWqFi9ebBduh4SEaMaMGZo5c6bCwsKUmZmp+fPn2+0NXJSyV0KOGTMmx7nevXsrOjq6QH28lfc320svvaSrV69q8ODB5j82fP3113rggQdu6V67d++uVatW5brK9qGHHlJ0dLT+/e9/KykpSWXKlNGDDz6oTz75JMfqyz9r166dNm/erAkTJmjw4MG6evWqKlSoYPcFaTNnzpSbm5smTZqklJQUNWzYUMuXL9err756S/czceJE7d69W3379tXFixd13333admyZapatWqez/H399fXX3+tYcOG6emnn5aXl5ceffRRffrpp2rYsOFN1XGz7530x5fWPffcc4qPj1e5cuUk/fEPNuPHj9f69es1ZswYnT17VjabTSEhIYqMjNQrr7yiEiVKSJIeeeQRJSQk6JtvvjH/0cnHx0f16tXTwoUL9fTTT+d4zb59+5r/7evrq5CQEE2bNk0DBw60m2cYhr788sscX/gGAACsxWb8+XePAAAAgOuw2Wx5fiM9cnfixAmFhITorbfe0vDhwwv9+o8//rh27NihEydOyM3NrdCvf7ts3LhRLVu21H/+8x/94x//cHQ5t+Tq1auqWLGihg0bpldeecXR5dhZv3692rZtqx9//FG1atVydDkAACAP7GkLAAAA3GHS0tK0fft2zZw5UytWrNCIESPu6MD278bT01Pjx4/XtGnTdPnyZUeXY+eNN95Qv379CGwBALA4tkcAAAAA7jDx8fFq1qyZSpYsqQEDBmjQoEGOLgl/8dxzz+n333/XL7/8Ypm9Y5OSktSiRYscWyYAAADrYXsEAAAAAAAAALAQtkcAAAAAAAAAAAshtAUAAAAAAAAACyG0BQAAAAAAAAAL4YvI8ikrK0tnzpyRj4+PbDabo8sBAAAAAAAAcIcxDEOXLl1SUFCQihXLez0toW0+nTlzRsHBwY4uAwAAAAAAAMAdLi4uThUqVMjzPKFtPvn4+Ej64w0tWbKkg6sBAAAAAAAAcKe5ePGigoODzawxL4S2+ZS9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AABnke/QtkWLFpKk2NhYVaxYUTabrciKAgAAAAAAAABnle/QNlulSpWKog4AAAAAAAAAgKRiji4AAAAAAAAAAPB/CG0BAAAAAAAAwEIIbQEAAAAAAADAQghtAQAAAAAAAMBCChzanj17Vj179lRQUJBcXV3l4uJi9wAAAAAAAAAA3DzXgj6hT58+OnnypF577TWVK1dONputKOoCAAAAAAAAAKdU4NB2y5Yt+u6779SgQYMiKAcAAAAAAAAAnFuBt0cIDg6WYRhFUQsAAAAAAAAAOL0Ch7YzZszQqFGjdOLEiSIoBwAAAAAAAACcW4G3R+jevbuuXLmiqlWrqkSJEnJzc7M7f+HChUIrDgAAAAAAAACcTYFD2xkzZhRBGQAAAAAAAAAA6SZC2969exdFHbm6du2aoqKitHjxYiUkJKhcuXLq06ePXn31VRUr9sfODoZhaPz48frwww+VlJSkJk2a6N1339Xdd99tXictLU3Dhw/X0qVLlZqaqlatWum9995ThQoVbtu9AAAAAAAAAEB+FDi0laTMzEytXLlShw4dks1mU506dRQeHi4XF5dCLW7KlCn64IMPtGDBAt19993as2eP+vbtK19fX7388suSpKlTp2ratGmKjo5WjRo19MYbb6hNmzY6fPiwfHx8JEmRkZH673//q2XLlsnf31/Dhg1Tp06dtHfv3kKvGQAAAAAAAABuRYFD22PHjqljx446ffq0atasKcMwdOTIEQUHB+vrr79W1apVC6247du369FHH9UjjzwiSapcubKWLl2qPXv2SPpjle2MGTM0duxYde3aVZK0YMECBQQEaMmSJRowYICSk5M1b948LVy4UK1bt5YkLVq0SMHBwVq3bp3atWtXaPUCAAAAAAAAwK0qVtAnDB48WFWrVlVcXJz27dun/fv36+TJkwoJCdHgwYMLtbgHH3xQ69ev15EjRyRJ33//vbZs2aKOHTtKkmJjY5WQkKC2bduaz/Hw8FCLFi20bds2SdLevXuVkZFhNycoKEh169Y15wAAAAAAAACAVRR4pe2mTZu0Y8cO+fn5mWP+/v6aPHmyHnjggUIt7pVXXlFycrJq1aolFxcXZWZm6s0339RTTz0lSUpISJAkBQQE2D0vICBAv/76qznH3d1dpUuXzjEn+/m5SUtLU1pamnl88eLFQrknAAAAAAAAALieAq+0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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ssb_interpreted = ssb[ssb.executor == \"Interpreted\"].copy()\n", + "\n", + "wide = ssb_interpreted.pivot(index=\"query\", columns=\"mode\", values=\"time_ms\").sort_index()\n", + "ax = wide[[\"Naive\", \"Optimized\"]].plot.bar(figsize=(14, 5))\n", + "ax.set_ylabel(\"Execution time, ms\")\n", + "ax.set_xlabel(\"Query\")\n", + "ax.set_title(\"Naive and optimized physical plans (SSB)\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "c36bdd23", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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GtWuXTvL9y1YsEDx8fGaNGmSbDabmjVrprFjx8rPz89xzJAhQ3Ts2DH17NlT58+fV2RkpA4ePHhPPtehQ4d04cIF/fLLL3r88ccz7U9ISFBUVJTq16+vDRs26L333lPfvn115swZ5cuXT9HR0RkCaGfVr19fX375pT799FMlJycrLCxMDRs21DvvvKOcOXNmODY73+WAAQMUHR2tcePGafbs2UpJSVFYWJhq1KihF154IcN1s/N5ypcvrx9++EGvv/66OnfurLx58+r555/XM888o7/+9a8Z6nvjjTeUnJysadOm6YMPPtAjjzyiL774Qs2bN8/ys//zn//UjBkz1K5dO6WkpKh+/foaN25cpvWKAQAAcHs26/qjgQEAAICbiI+P15AhQ/THH3841lDFnXnQvstp06apa9eu2rhxY6aHrQEAAODO+Hi6AAAAAAAAAADAfxHaAgAAAAAAAIBBWB4BAAAAAAAAAAzCTFsAAAAAAAAAMAihLQAAAAAAAAAYhNAWAAAAAAAAAAzi6+kC3C09PV2///67goKCZLPZPF0OAAAAAAAAAC9lWZbOnz+viIgI+fjcfD7tAx/a/v777ypatKinywAAAAAAAAAASVJiYqKKFCly0/0PfGgbFBQk6doXERwc7OFqAAAAAAAAAHir5ORkFS1a1JFZ3swDH9peXxIhODiY0BYAAAAAAACAx91uGVePPohs0qRJqlixoiNQrV27tr777jvHfsuyFB8fr4iICPn7+6tevXratWuXBysGAAAAAAAAAPfyaGhbpEgRvf/++9q0aZM2bdqkBg0aqHnz5o5gdtSoURo9erQmTJigjRs3KiwsTA0bNtT58+c9WTYAAAAAAAAAuI3NsizL00XcKDQ0VH//+9/VrVs3RUREqG/fvnrrrbckSSkpKSpUqJBGjhypXr16Zet8ycnJCgkJ0blz51geAQAAAAAAAIDHZDer9OhM2xulpaVpzpw5unjxomrXrq2EhAQlJSWpUaNGjmPsdrtiYmK0du1aD1YKAAAAAAAAAO7j8QeR7dy5U7Vr19bly5cVGBiohQsXKjo62hHMFipUKMPxhQoV0qFDh256vpSUFKWkpDi2k5OT3VM4AAAAAAAAALiBx2falilTRtu2bdP69ev14osvqnPnztq9e7dj//8+Sc2yrFs+XW3EiBEKCQlxvIoWLeq22gEA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399fW7duVUpKiiTp/PnzGj58uMsLBAAAAAAAAABv4nRoO2zYME2ePFlTpkxRzpw5HeN16tTRli1bXFocAAAAAAAAAHgbp0Pbffv2qW7dupnGg4ODdfbsWVfUBAAAAAAAAABey+nQNjw8XL/++mum8TVr1qhEiRIuKQoAAAAAAAAAvJXToW2vXr306quv6ueff5bNZtPvv/+umTNnql+/furdu7c7agQAAAAAAAAAr+Hr7BvefPNNnTt3TvXr19fly5dVt25d2e129evXT3369HFHjQAAAAAAAADgNZwObSXpvffe06BBg7R7926lp6crOjpagYGBrq4NAAAAAAAAALyO08sjTJ8+XRcvXlRAQICqV6+uRx55hMAWAAAAAAAAAFzE6dC2X79+KliwoNq1a6dvvvlGqamp7qgLAAAAAAAAALyS06HtsWPHNHfuXOXIkUPt2rVTeHi4evfurbVr1zp98VWrVqlZs2aKiIiQzWbTV199lWF/ly5dZLPZMrxq1arl9HUAAAAAAAAA4H7hdGjr6+urp556SjNnztSJEyc0duxYHTp0SPXr19dDDz3k1LkuXryoSpUqacKECTc9pkmTJjp27Jjj9e9//9vZkgEAAAAAAADgvnFHDyK7LiAgQI0bN9aZM2d06NAh7dmzx6n3x8bGKjY29pbH2O12hYWF3U2ZAAAAAAAAAHDfcHqmrST9+eefmjlzpp588klFRERozJgxatGihf7zn/+4uj6tWLFCBQsWVOnSpdWzZ0+dOHHC5dcAAAAAAAAAAFM4PdO2ffv2+r//+z8FBASodevWWrFiherUqeOO2hQbG6vWrVsrMjJSCQkJeuedd9SgQQNt3rxZdrs9y/ekpKQoJSXFsZ2cnOyW2gAAAAAAAADAHZwObW02m+bOnavGjRvL1/euVle4rbZt2zr+Ll++vKpXr67IyEh9++23atWqVZbvGTFihIYMGeLWugAAAAAAAADAXZxeHmHWrFlq2rSp2wPbrISHhysyMlL79++/6TEDBgzQuXPnHK/ExMR7WCEAAAAAAAAA3J1sJa8fffSR/vrXvypXrlz66KOPbnnsK6+84pLCsnLq1CklJiYqPDz8psfY7fabLp0AAAAAAAAAAKbLVmg7ZswYPffcc8qVK5fGjBlz0+NsNptToe2FCxf066+/OrYTEhK0bds2hYaGKjQ0VPHx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AAAAYBBCWwAAADzQEhMT1b17d0VERMjPz0+RkZF69dVXderUKU+XBgAAAGSJ0BYAAAAPrN9++03Vq1fXL7/8otmzZ+vXX3/V5MmT9eOPP6p27do6ffq026595coVt50bAAAADzZCWwAAADywXnrpJfn5+en7779XTEyMihUrptjYWP3www86evSoBg0aJEmy2Wz66quvMrw3T548mjZtmmP76NGjatu2rfLmzat8+fKpefPmOnjwoGN/ly5d1KJFC40YMUIREREqXbq0hg4dqgoVKmSqq1q1anr33Xfd8ZEBAADwACC0BQAAwAPp9OnTWrJkiXr37i1/f/8M+8LCwvTcc89p7ty5sizrtuf6888/Vb9+fQUGBmrVqlVas2aNAgMD1aRJkwwzan/88Uft2bNHS5cu1TfffKNu3bpp9+7d2rhxo+OYHTt2aOvWrerSpYvLPisAAAAeLL6eLgAAAABwh/3798uyLJUrVy7L/eXKldOZM2f0xx9/3PZcc+bMkY+Pjz799FPZbDZJ0tSpU5UnTx6tWLFCjRo1kiTlzp1bn376qfz8/Bzvbdy4saZOnaoaNWo43hcTE6MSJUrc7UcEAADAA4qZtgAAAPBK12fY3hiw3szmzZv166+/KigoSIGBgQoMDFRoaKguX76sAwcOOI6rUKFCpvP17NlTs2fP1uXLl3X16lXNnDlT3bp1c+2HAQAAwAOFmbYAAAB4IJUsWVI2m027d+9WixYtMu3fu3evChQooDx58shms2VaJuHq1auOv9PT01WtWjXNnDkz03kKFCjg+Dt37tyZ9jdr1kx2u10LFy6U3W5XSkqKnnnmmbv4ZAAAAHjQEdoCAADggZQvXz41bNhQEydO1GuvvZZhXdukpCTNnDlTL730kqRrweuxY8cc+/fv368///zTsV21alXNnTtXBQsWVHBwsFN1+Pr6qnPnzpo6darsdrvatWungICAu/x0AAAAeJCxPAIAAAAeWBMmTFBKSooaN26sVatWKTExUYsXL1bDhg1VunRpvfvuu5KkBg0aaMKECdqyZYs2bdqkF154QTlz5nSc57nnnlP+/PnVvHlzrV69WgkJCVq5cqVeffVVHTly5LZ19OjRQ8uWLdN3333H0ggAAAC4LUJbAAAAPLBKlSqljRs3qkSJEmrTpo0iIyMVGxur0qVL66efflJgYKAk6cMPP1TRokVVt25ddejQQf369cswGzYgIECrVq1SsWLF1KpVK5UrV07dunXTpUuXsjXztlSpUqpTp47KlCmjmjVruu3zAgAA4MFgs/538S4AAADgATZ48GCNHj1a33//vWrXrn1PrmlZlsqWLatevXopLi7unlwTAAAA9y/WtAUAAIBXGTJkiKKiovTzzz+rZs2a8vFx7398duLECc2YMUNHjx5V165d3XotAAAAPBiYaQsAAAC4kc1mU/78+TVu3Dh16NDB0+UAAADgPsBMWwAAAMCNmCMBAAAAZ/EgMgAAAAAAAAAwCKEtAAAAAAAAABiE0BYAAAAAAAAADEJoCwAAAAAAAAAGIbQFAAAAAAAAAIMQ2gIAAAAAAACAQQhtAQAAAAAAAMAghLYAAAAAAAAAYBBCWwAAAAAAAAAwyP8DTaUeq84wsi4AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "speedup = (wide[\"Naive\"] / wide[\"Optimized\"]).sort_index()\n", + "ax = speedup.plot.bar(figsize=(14, 4))\n", + "ax.axhline(1.0, color=\"black\", linewidth=1)\n", + "ax.set_ylabel(\"Speedup, Naive / Optimized\")\n", + "ax.set_xlabel(\"Query\")\n", + "ax.set_title(\"Optimizer speedup\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "4ee5fc7c", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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btw4jRw5UhMnTlSFChXUtGlTrVixQiVLllSPHj00evRo1ahRQ5I0atQo+fn5mVfL79mzp2bNmqV58+YpKChIDRo00Lx58xQQECDpr1G4tWvXytvbWy1atFBQUJDee+8983TOpk2basSIERo0aJAef/xx3bhxQ507d7bIN3DgQNnb26tixYry8vJSTEyMihUrpsjISP3yyy+qWrWqevfurR49emj48OGP8JOzHZORl9YKzSLx8fFyd3fX9evX5ebmZus4OQKra/6fU07tbR0hZxl93dYJAADIUxISEnTy5EkFBATIyYllvf9rHvTnn9k6hZE8AAAAAMhDKPIAAAAAIA+xusg7c+aMzp49a379yy+/qH///vrss8+yNBgAAAAAwHpWF3nt27dXVFSUJCkuLk6hoaH65ZdfNHToUI0dOzbLAwIAAAAAMs/qIu/gwYOqVauWJOnrr79W5cqVtW3bNn311VeaN29eVucDAAAAAFjB6iLv3r175qfb//TTT2rdurUkqXz58oqNjc3adAAAAAAAq1hd5FWqVEkzZ87U5s2btW7dOjVr1kySdP78eXl6emZ5QAAAAABA5lld5L3//vv69NNP1bBhQ73yyiuqWrWqJOmHH34wT+MEAAAAANhGPmt3aNiwoS5duqT4+HgVKlTI3P7qq6/K2dk5S8MBAAAAAKzzUM/Js7e3tyjwJKlkyZLy9vbOklAAAAAAkJaGDRuqf//+WX7c0aNHq1q1all+XFuweiTv8uXLGjlypKKionThwgWlpKRYbL9y5UqWhQMAAADwl5KDVz3S9zv13jNW7xMXF6fx48dr1apVOnfunLy9vVWtWjX1799fTz31VDakfHROnTqlgIAARUdHq1q1ahavly9frjFjxjxw/5MnT6pkyZKPJKvVRV7Hjh31xx9/qEePHvLx8ZHJZMqOXAAAAABykVOnTqlu3bry8PDQpEmTVKVKFd27d09r1qzR66+/rt9//93WEbPNwIED1bt3b/Prxx9/XK+++qp69eplbvPy8npkeawu8rZs2aItW7aYF1wBAAAAgL59+8pkMumXX36Ri4uLub1SpUrq3r27+fWUKVM0d+5cnThxQoULF1arVq00adIkFSxY0Nxn69atGjp0qHbt2iVHR0fVqlVLixcvNt8ylpKSokGDBmnWrFlycHBQ7969NXr0aPP+169f19tv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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "jit = synthetic[synthetic[\"mode\"] == \"Optimized\"].copy()\n", + "jit[\"executor\"] = jit[\"executor\"].replace({\n", + " \"InterpretedExpressionExecutor\": \"Interpreted\",\n", + " \"CachedJitCompiledExpressionExecutor\": \"Cached JIT\",\n", + "})\n", + "ax = jit.pivot(index=\"query\", columns=\"executor\", values=\"time_ms\").plot.bar(figsize=(9, 4))\n", + "ax.set_ylabel(\"Execution time, ms\")\n", + "ax.set_xlabel(\"Query\")\n", + "ax.set_title(\"Expression evaluation for optimized plans\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "bbe140d5", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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IiIiItKJKMkTERERERHpRJTkiYiIiIiIdCJK8kRERERERDqRAH8H0J6dO5PG7Xb7ORIREREREenqzuUllzo7U0leA4qLiwFITEz0cyQiIiIiIiKW4uJinE7nRX+uEgoN8Hq9HDt2jMjISAzD8EsMGRkZbNmypd1fu7nXas7zmvKcxvS9VB+3201iYiJHjhzptKU0WnOctZc4fHXtllynNce7r/ppvHeOGDriePf1e/ul+nWFsQ6df7x3xLHe1Ofob5nG6exj/dz1N2/eTHFxMT179sRmu/jOO83kNcBms9G7d2+/xmC321vtH6Mvr93cazXneU15TmP6NvZ6Doej074xtuY4ay9x+OraLblOa453X/fTeO/YMXTE8e7r9/bG9uvMYx06/3jviGO9qc/R3zKN09nH+rnrO53OBmfwztHBK+3c3LlzO8S1m3ut5jyvKc9pTN/W/B13FO3ld9ARxntLrtOa493X/Tqz9vA7aO0YOuJ49/V7e3Ni6Izaw+9A7+0te47+lmmc9vA7aE/v7VquKXIJbrcbp9OJy+Xy+zdEIq1N4126Co116Uo03rsezeSJXEJwcDCPP/44wcHB/g5FpNVpvEtXobEuXYnGe9ejmTwREREREZFORDN5IiIiIiIinYiSPBERERERkU5ESZ6IiIiIiEgnoiRPRERERESkE1GSJyIiIiIi0okoyRNppiNHjjBhwgRSUlIYNmwYCxYs8HdIIq2muLiYjIwMRowYwdChQ3nllVf8HZJIqysrKyMpKYmHHnrI36GItJqAgABGjBjBiBEjuOeee/wdjviISiiINNPx48fJz89nxIgRFBQUMHLkSL788kvCw8P9HZqIz3k8HiorKwkLC6OsrIwhQ4awZcsWYmJi/B2aSKv55S9/yb59++jTpw/PPPOMv8MRaRWxsbGcPHnS32GIj2kmT6SZevTowYgRIwDo3r070dHRnD592r9BibQSu91OWFgYABUVFXg8HvQdoXRm+/btY8+ePUyfPt3foYiINJmSPOmyPvnkE2bNmkXPnj0xDINFixZd0OfFF18kOTmZkJAQ0tLSWLduXb3X2rp1K16vl8TExFaOWqR5fDHei4qKGD58OL179+bhhx8mNja2jaIXaRpfjPeHHnqIefPmtVHEIs3ji7HudrtJS0vj6quvZu3atW0UubQ2JXnSZZWWljJ8+HBeeOGFen8+f/58HnjgAX75y1+yfft2xo0bx7Rp0zh8+HCdfqdOneL222/n5ZdfbouwRZrFF+M9KiqKzz77jNzcXP75z3+Sn5/fVuGLNElLx/t7773HwIEDGThwYFuGLdJkvnhvP3ToENnZ2fzlL3/h9ttvx+12t1X40ppMETEBc+HChXXaRo0aZd5333112gYNGmQ+8sgjtY8rKirMcePGmf/4xz/aIkwRn2jueP+6++67z3z77bdbK0QRn2nOeH/kkUfM3r17m0lJSWZMTIzpcDjMJ554oq1CFmkWX7y3Z2Zmmlu2bGmtEKUNaSZPpB5VVVVkZ2czZcqUOu1Tpkxhw4YNAJimyZ133sm1117Lbbfd5o8wRXyiMeM9Pz+/9ttdt9vNJ598wuWXX97msYq0VGPG+7x58zhy5AiHDh3imWee4Yc//CG/+tWv/BGuSLM1ZqyfOXOGyspKAI4ePUpOTg79+vVr81jF9wL8HYBIe3Ty5Ek8Hg/x8fF12uPj4zlx4gQAn376KfPnz2fYsGG1a+DfeOMNhg4d2tbhirRIY8b70aNHufvuuzFNE9M0uf/++xk2bJg/whVpkcaMd5HOoDFjfffu3fzXf/0XNpsNwzB4/vnniY6O9ke44mNK8kQaYBhGncemada2XX311Xi9Xn+EJdIqGhrvaWlp7Nixww9RibSOhsb71915551tFJFI62horI8ZM4adO3f6IyxpZVquKVKP2NhY7Hb7Bd/qFhQUXPCNmEhHp/EuXYnGu3QVGutdm5I8kXoEBQWRlpbGypUr67SvXLmSMWPG+Ckqkdah8S5dica7dBUa612blmtKl1VSUsL+/ftrH+fm5rJjxw6io6Pp06cPDz74ILfddhvp6elcddVVvPzyyxw+fJj77rvPj1GLNI/Gu3QlGu/SVWisy0X58WRPEb/6+OOPTeCC/+64447aPllZWWZSUpIZFBRkjhw50ly7dq3/AhZpAY136Uo03qWr0FiXizFM0zTbNq0UERERERGR1qI9eSIiIiIiIp2IkjwREREREZFOREmeiIiIiIhIJ6IkT0REREREpBNRkiciIiIiItKJKMkTERERERHpRJTkiYiIiIiIdCJK8kRERERERDoRJXkiIiIiIiKdiJI8ERERERGRTkRJnoiIdFp33nkns2fPbvP7vvbaa0RFRbX5fUVEREBJnoiISLtRXV3t7xBERKQTUJInIiJdxoQJE/jJT37Cww8/THR0NAkJCfz617+u08cwDF566SWmTZtGaGgoycnJLFiwoPbna9aswTAMioqKatt27NiBYRgcOnSINWvW8IMf/ACXy4VhGBiGccE9zvn1r3/NiBEjePXVV+nXrx/BwcGYpsnhw4e54YYbiIiIwOFwcMstt5Cfnw+Ay+XCbreTnZ0NgGmaREdHk5GRUXvdf/3rX/To0QOAqqoq7r//fnr06EFISAh9+/Zl3rx5PvhtiohIe6UkT0REupTXX3+d8PBwNm3axNNPP81vfvMbVq5cWafP//2//5ebbrqJzz77jO9///t85zvfYffu3Y26/pgxY3juuedwOBwcP36c48eP89BDD120//79+3n77bd555132LFjBwCzZ8/m9OnTrF27lpUrV3LgwAFuvfVWAJxOJyNGjGDNmjUAfP7557X/63a7ASsRHT9+PAB/+tOfWLx4MW+//TZffvklb775Jn379m3sr0tERDqgAH8HICIi0paGDRvG448/DsCAAQN44YUXWL16NZMnT67t861vfYt77rkHgN/+9resXLmSP//5z7z44ouXvH5QUBBOpxPDMEhISLhk/6qqKt544w3i4uIAWLlyJZ9//jm5ubkkJiYC8MYbb5CamsqWLVvIyMhgwoQJrFmzhp///OesWbOG6667joMHD7J+/XqmT5/OmjVr+NnPfgbA4cOHGTBgAFdffTWGYZCUlNS0X5iIiHQ4mskTEZEuZdiwYXUe9+jRg4KCgjptV1111QWPGzuT11RJSUm1CR7A7t27SUxMrE3wAFJSUoiKiqqNYcKECaxbtw6v18vatWuZMGECEyZMYO3atZw4cYK9e/fWzuTdeeed7Nixg8svv5yf/OQnrFixolVeh4iItB9K8kREpEsJDAys89gwDLxe7yWfZxgGADab9dFpmmbtz1pyYEp4eHidx6Zp1t7rYu3XXHMNxcXFbNu2jXXr1jFhwgTGjx/P2rVr+fjjj+nevTuDBw8GYOTIkeTm5vLb3/6W8vJybrnlFm6++eZmxysiIu2fkjwREZFv2Lhx4wWPBw0aBFA763b8+PHan5/bS3dOUFAQHo+nWfdOSUnh8OHDHDlypLYtJycHl8tVm7id25f3wgsvYBgGKSkpjBs3ju3bt7NkyZLaWbxzHA4Ht956K6+88grz58/nnXfe4fTp082KT0RE2j8leSIiIt+wYMECXn31Vfbu3cvjjz/O5s2buf/++wG47LLLSExM5Ne//jV79+5l6dKl/OEPf6jz/L59+1JSUsLq1as5efIkZWVljb73pEmTGDZsGN/73vfYtm0bmzdv5vbbb2f8+PGkp6fX9pswYQJvvvkm48ePxzAMunXrRkpKCvPnz2fChAm1/f74xz/y1ltvsWfPHvbu3cuCBQtISEhQHT8RkU5MSZ6IiMg3PPHEE7z11lsMGzaM119/nf/93/8lJSUFsJZ7/utf/2LPnj0MHz6cp556iv/+7/+u8/wxY8Zw3333ceuttxIXF8fTTz/d6HsbhsGiRYvo1q0b11xzDZMmTaJfv37Mnz+/Tr+JEyfi8XjqJHTjx4/H4/HUmcmLiIjgqaeeIj09nYyMDA4dOsSyZctql52KiEjnY5hf31QgIiLSxRmGwcKFC5k9e7a/QxEREWkWfY0nIiIiIiLSiSjJExERERER6URUDF1ERORrtItBREQ6Os3kiYiIiIiIdCJK8kRERERERDoRJXkiIiIiIiKdiJI8ERERERGRTkRJnoiIiIiISCeiJE9ERERERKQTUZInIiIiIiLSiSjJExERERER6UT+f4roIO4o2F2PAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(9, 5))\n", + "for name, group in operator.groupby(\"operator\"):\n", + " group = group.sort_values(\"input_rows\")\n", + " ax.plot(group.input_rows, group.time_ms, marker=\"o\", label=name)\n", + "ax.set_xscale(\"log\")\n", + "ax.set_yscale(\"log\")\n", + "ax.set_xlabel(\"Input rows\")\n", + "ax.set_ylabel(\"Execution time, ms\")\n", + "ax.set_title(\"Physical operator scaling\")\n", + "ax.legend()\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "cde81524", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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MnUaLZYgCIiPjzd1GkRERETURFjkNQFJo0HJkaMo2vFflBw5CkmjMep4UVFREAQBK1as0GmPj4+HIAgGGcOYhZm5FH0VFRVYtWoVunTpAjs7O7i5uaFfv37YuHEjKisrTZJTbGwsnJyc9HpPRkYGhg8fbpyEiIiIiMjscLmmkal//hlZy99FVWamts3CywueC+ZD+dBDRhvXxsYGK1euxPTp0+HszDNi9FVRUYFhw4bh9OnTePvtt9GvXz8olUocPnwY77//Prp164auXbvW+T4rK6umT7gBXl5epk6BiIiIiJoQZ/KMSP3zz7j+yiydAg8AqrKycP2VWVD//LPRxg4PD4eXlxfefffdemMSExMxYMAA2NrawtfXFzNnzkRJSYm2/9NPP0VgYCBsbGzg6emJJ554AkD1TOG+ffvw4YcfQhAECIKAK1euAACSk5MxYsQIKBQKeHp6YuLEicjNzdXes6SkBJMmTYJCoYC3tzc++OADvT/b1q1bERISAmtra7Rt27bWPQoKCjBp0iQ4OzvDzs4Ow4cPR2pqqra/ZjYsPj4eHTp0gI2NDSIiInDt2jVtzJo1a/Dbb79h9+7diI6ORteuXREQEICnn34aR44cQWBgIIDqWceXXnoJs2fPhpubGyIiIgAA+/btQ69evWBtbQ1vb2/MmzcPVVVV2vt/9913CA0Nha2tLVxdXREeHq797vfu3YtevXrB3t4eTk5O6NevH9LT0+v9PtauXYt27drBysoKQUFB2Lx5s07/rcs1r1y5AkEQ8P3332Pw4MGws7NDly5dcOjQIb1/H4iIiIjIPLHIMxJJo0HW8neBujYv/asta/m7Rlu6KZfLsXz5cnz88cf4448/avWfPXsWw4YNw2OPPYYzZ85gy5YtOHDgAF566SUAwPHjxzFz5ky89dZbuHDhAhISEjBgwAAAwIcffog+ffpg6tSpyMjIQEZGBnx9fZGRkYGBAweia9euOH78OBISEpCVlYWxY8dqx3399dexZ88e/PDDD/j555+xd+9enDhxotGf68SJExg7diyeeuopnD17FkuWLMHChQsRGxurjYmKisLx48exbds2HDp0CJIkYcSIETpLLEtLS7Fs2TJs2rQJBw8ehFqtxlNPPaXt//rrrxEeHo5u3brVysHS0hL29vba602bNsHCwgIHDx7E+vXrcf36dYwYMQI9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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(9, 5))\n", + "for name, group in matched.groupby(\"operator\"):\n", + " ax.scatter(group.model_cost, group.time_ms, label=name)\n", + "ax.set_xscale(\"log\")\n", + "ax.set_yscale(\"log\")\n", + "ax.set_xlabel(\"Model cost\")\n", + "ax.set_ylabel(\"Execution time, ms\")\n", + "ax.set_title(\"Cost-model calibration\")\n", + "ax.legend()\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "7b508249", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
operatorcoefficientvalue
0SeqScanrow100
1Filteroutput row100
2Projectionoutput row22
3Sortn * (floor(log2(n)) + 1)11
4Aggregationinput row510
5NestedLoopJoininput row pair70
6NestedLoopCrossJoininput row pair104
7HashJoinbuild row * width100
8HashJoinprobe row35
9HashJoinoutput row * width10
\n", + "
" + ], + "text/plain": [ + " operator coefficient value\n", + "0 SeqScan row 100\n", + "1 Filter output row 100\n", + "2 Projection output row 22\n", + "3 Sort n * (floor(log2(n)) + 1) 11\n", + "4 Aggregation input row 510\n", + "5 NestedLoopJoin input row pair 70\n", + "6 NestedLoopCrossJoin input row pair 104\n", + "7 HashJoin build row * width 100\n", + "8 HashJoin probe row 35\n", + "9 HashJoin output row * width 10" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "coefficients = pd.DataFrame([\n", + " (\"SeqScan\", \"row\", 100),\n", + " (\"Filter\", \"output row\", 100),\n", + " (\"Projection\", \"output row\", 22),\n", + " (\"Sort\", \"n * (floor(log2(n)) + 1)\", 11),\n", + " (\"Aggregation\", \"input row\", 510),\n", + " (\"NestedLoopJoin\", \"input row pair\", 70),\n", + " (\"NestedLoopCrossJoin\", \"input row pair\", 104),\n", + " (\"HashJoin\", \"build row * width\", 100),\n", + " (\"HashJoin\", \"probe row\", 35),\n", + " (\"HashJoin\", \"output row * width\", 10),\n", + "], columns=[\"operator\", \"coefficient\", \"value\"])\n", + "coefficients" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/research/benchmarks.pdf b/research/benchmarks.pdf new file mode 100644 index 0000000..cb0f791 Binary files /dev/null and b/research/benchmarks.pdf differ diff --git a/research/converter.py b/research/converter.py new file mode 100644 index 0000000..d0dc6e9 --- /dev/null +++ b/research/converter.py @@ -0,0 +1,488 @@ +#!/usr/bin/env python3 +""" +Converter from MS SQL Server ShowPlanXML to serialized physical plan format. + +Serialized format (s-expressions): + (SeqScan table) + (SeqScan table alias) + (PhysicalFilter EXPR SOURCE) + (PhysicalProjection (exprs EXPR...) SOURCE) + (NestedLoopCrossJoin LHS RHS) + (NestedLoopJoin JoinType EXPR LHS RHS) JoinType: Inner | Full | Left | Right + (MergeJoin JoinType EXPR LHS RHS) JoinType: Inner | Full | Left | Right + (Sort (keys table.col Asc|Desc ...) SOURCE) + (HashAggregate (group_by EXPR...) (aggs EXPR...) SOURCE) + +Expressions: + 42 NULL TRUE FALSE UNKNOWN + (str "value") + (attr table column) + (OP LHS RHS) OP: = != > < >= <= and or + - * / % ^ + (not EXPR) + (uminus EXPR) + (isnull EXPR) + (SUM EXPR) (COUNT EXPR) (COUNT *) +""" + +import xml.etree.ElementTree as ET + +NS = "{http://schemas.microsoft.com/sqlserver/2004/07/showplan}" + +_JOIN_TYPES = { + "Inner Join": "Inner", + "Left Outer Join": "Left", + "Right Outer Join":"Right", + "Full Outer Join": "Full", +} + +_COMPARE_OPS = { + "EQ": "=", + "NE": "!=", + "GT": ">", + "LT": "<", + "GE": ">=", + "LE": "<=", +} + +_LOGICAL_OPS = { + "AND": "and", + "OR": "or", +} + +_ARITHMETIC_OPS = { + "ADD": "+", + "SUB": "-", + "MUL": "*", + "DIV": "/", + "MOD": "%", +} + + +def convert(plan_xml: str) -> str: + root = ET.fromstring(plan_xml) + top_relop = root.find(f".//{NS}RelOp") + if top_relop is None: + raise ValueError("no RelOp found in plan XML") + return _convert_relop(top_relop) + + +# ─── Operator conversion ───────────────────────────────────────────────────── + +def _convert_relop(relop: ET.Element) -> str: + phys_op = relop.get("PhysicalOp", "") + logical_op = relop.get("LogicalOp", "") + + if phys_op in ("Clustered Index Scan", "Index Scan", "Table Scan", + "Clustered Index Seek", "Index Seek"): + return _convert_scan(relop) + if phys_op == "Compute Scalar": + return _convert_compute_scalar(relop) + if phys_op == "Sort": + return _convert_sort(relop) + if phys_op == "Stream Aggregate": + return _convert_aggregate(relop, "StreamAggregate", "GroupBy", drop_enforcer_sort=True) + if phys_op == "Nested Loops": + return _convert_nested_loops(relop, logical_op) + if phys_op == "Hash Match": + if logical_op == "Aggregate": + return _convert_aggregate(relop, "Hash", "HashKeysBuild", drop_enforcer_sort=False) + return _convert_hash_match(relop, logical_op) + if phys_op == "Merge Join": + return _convert_merge_join(relop, logical_op) + + raise NotImplementedError(f"unhandled PhysicalOp: {phys_op!r} (LogicalOp={logical_op!r})") + + +def _convert_compute_scalar(relop: ET.Element) -> str: + # MS SQL wraps aggregate outputs, casts and computed projections in a + # Compute Scalar node. The serialized projection is re-added by + # reach_fuzz._wrap_projection, so pass straight through to the child. + child = relop.find(f"{NS}ComputeScalar/{NS}RelOp") + if child is None: + raise ValueError("Compute Scalar has no child RelOp") + return _convert_relop(child) + + +def _convert_sort(relop: ET.Element) -> str: + sort = relop.find(f"{NS}Sort") + if sort is None: + raise ValueError("Sort element missing") + child = sort.find(f"{NS}RelOp") + if child is None: + raise ValueError("Sort has no child RelOp") + + keys = [] + for ob in sort.findall(f"{NS}OrderBy/{NS}OrderByColumn"): + ascending = ob.get("Ascending", "true").lower() not in ("false", "0") + cr = ob.find(f"{NS}ColumnReference") + if cr is None: + continue + table = _strip_sql_name(cr.get("Alias") or cr.get("Table")) + col = cr.get("Column", "").strip("[]") + keys.append(f"{table}.{col} {'Asc' if ascending else 'Desc'}") + if not keys: + raise ValueError("Sort node has no OrderByColumn entries") + return f"(Sort (keys {' '.join(keys)}) {_convert_relop(child)})" + + +_AGG_TYPES = { + "SUM": "SUM", + "COUNT": "COUNT", + "COUNT_BIG": "COUNT", + "CNT": "COUNT", + "CNT_BIG": "COUNT", +} + + +def _convert_aggregate( + relop: ET.Element, container_tag: str, groupby_tag: str, drop_enforcer_sort: bool +) -> str: + container = relop.find(f"{NS}{container_tag}") + if container is None: + raise ValueError(f"{container_tag} element missing from aggregate node") + + child = container.find(f"{NS}RelOp") + if child is None: + raise ValueError("aggregate node has no child RelOp") + # A Sort directly under a Stream Aggregate is MS's enforcer for ordered + # stream aggregation; our model uses hash aggregation over unordered input, + # so drop it. A user ORDER BY sort lives above the aggregate/projection. + if drop_enforcer_sort and child.get("PhysicalOp", "") == "Sort": + inner_sort = child.find(f"{NS}Sort/{NS}RelOp") + if inner_sort is None: + raise ValueError("enforcer Sort has no child RelOp") + child_str = _convert_relop(inner_sort) + else: + child_str = _convert_relop(child) + + group_by = [ + _col_ref_to_attr(cr) + for cr in container.findall(f"{NS}{groupby_tag}/{NS}ColumnReference") + ] + + aggs = [] + for dv in container.findall(f"{NS}DefinedValues/{NS}DefinedValue"): + agg = dv.find(f".//{NS}Aggregate") + if agg is None: + continue + aggs.append(_convert_agg_func(agg)) + + return ( + f"(HashAggregate (group_by {' '.join(group_by)})" + f" (aggs {' '.join(aggs)}) {child_str})" + ) + + +def _convert_agg_func(agg: ET.Element) -> str: + agg_type = agg.get("AggType", "") + if agg_type == "countstar": + return "(COUNT *)" + + func = _AGG_TYPES.get(agg_type.upper()) + if func is None: + raise NotImplementedError(f"unhandled AggType: {agg_type!r}") + + inner = agg.find(f"{NS}ScalarOperator") + if inner is None: + # COUNT with no argument behaves like COUNT(*). + return f"({func} *)" if func == "COUNT" else f"({func})" + return f"({func} {_convert_scalar(inner)})" + + +_SEEK_SCAN_TYPES = {"GT": ">", "GE": ">=", "LT": "<", "LE": "<=", "EQ": "="} + + +def _strip_sql_name(value: str | None) -> str: + return (value or "").strip("[]") + + +def _object_table_and_alias(obj: ET.Element) -> tuple[str, str | None]: + table = _strip_sql_name(obj.get("Table")) + alias = _strip_sql_name(obj.get("Alias")) + return table, alias or None + + +def _convert_scan(relop: ET.Element) -> str: + phys_op = relop.get("PhysicalOp", "") + obj = relop.find(f".//{NS}IndexScan/{NS}Object") + if obj is None: + obj = relop.find(f".//{NS}TableScan/{NS}Object") + if obj is None: + raise ValueError("cannot find Object element in scan") + table, alias = _object_table_and_alias(obj) + visible_table = alias or table + pred_elem = relop.find(f".//{NS}IndexScan/{NS}Predicate/{NS}ScalarOperator") + if pred_elem is None: + pred_elem = relop.find(f".//{NS}TableScan/{NS}Predicate/{NS}ScalarOperator") + + if "Seek" in phys_op: + seek_pred = _convert_seek_predicates(relop, visible_table) + if seek_pred is None: + if pred_elem is None: + raise ValueError(f"Index Seek node has no SeekPredicates: {phys_op!r}") + seek_pred = _convert_scalar(pred_elem) + pred_elem = None + base = f"(IndexSeek {seek_pred} {table} {alias})" if alias else f"(IndexSeek {seek_pred} {table})" + else: + base = f"(SeqScan {table} {alias})" if alias else f"(SeqScan {table})" + + # Residual predicate (pushed-down filter evaluated after the scan/seek) + if pred_elem is not None: + return f"(PhysicalFilter {_convert_scalar(pred_elem)} {base})" + + return base + + +def _convert_seek_predicates(relop: ET.Element, table: str) -> str | None: + seek_preds = relop.find(f".//{NS}SeekPredicates") + if seek_preds is None: + return None + + alternatives = [] + for seek_keys in seek_preds.findall(f".//{NS}SeekKeys"): + conditions = [] + for range_elem in (seek_keys.findall(f"{NS}Prefix") + + seek_keys.findall(f"{NS}StartRange") + + seek_keys.findall(f"{NS}EndRange")): + op = _SEEK_SCAN_TYPES.get(range_elem.get("ScanType", "")) + if not op: + continue + col_refs = range_elem.findall(f"{NS}RangeColumns/{NS}ColumnReference") + expr_elems = range_elem.findall(f"{NS}RangeExpressions/{NS}ScalarOperator") + for col_ref, expr_elem in zip(col_refs, expr_elems): + col = col_ref.get("Column", "").strip("[]") + col_table = _strip_sql_name(col_ref.get("Alias") or col_ref.get("Table") or table) + conditions.append(f"({op} (attr {col_table} {col}) {_convert_scalar(expr_elem)})") + + if conditions: + alternatives.append(_fold_binary("and", conditions)) + + if not alternatives: + return None + return _fold_binary("or", alternatives) + +def _fold_binary(op: str, exprs: list[str]) -> str: + if not exprs: + raise ValueError("_fold_binary requires at least one expression") + result = exprs[0] + for expr in exprs[1:]: + result = f"({op} {result} {expr})" + return result + + +def _convert_nested_loops(relop: ET.Element, logical_op: str) -> str: + nl = relop.find(f"{NS}NestedLoops") + if nl is None: + raise ValueError("NestedLoops element missing") + lhs, rhs = _expect_two_relops(nl, "NestedLoops") + if _has_outer_references(nl) and _contains_index_seek(rhs): + raise NotImplementedError( + "nested loops with outer-reference index seek require unsupported " + "parameterized index lookup operator" + ) + + pred_elem = nl.find(f"{NS}Predicate/{NS}ScalarOperator") + if pred_elem is None: + return f"(NestedLoopCrossJoin {_convert_relop(lhs)} {_convert_relop(rhs)})" + + join_type = _JOIN_TYPES.get(logical_op, "Inner") + pred = _convert_scalar(pred_elem) + return f"(NestedLoopJoin {join_type} {pred} {_convert_relop(lhs)} {_convert_relop(rhs)})" + + +def _convert_hash_match(relop: ET.Element, logical_op: str) -> str: + hash_elem = relop.find(f"{NS}Hash") + if hash_elem is None: + raise ValueError("Hash element missing from Hash Match node") + build_relop, probe_relop = _expect_two_relops(hash_elem, "Hash") + + build_refs = hash_elem.findall(f"{NS}HashKeysBuild/{NS}ColumnReference") + probe_refs = hash_elem.findall(f"{NS}HashKeysProbe/{NS}ColumnReference") + if not build_refs or len(build_refs) != len(probe_refs): + raise ValueError("cannot reconstruct Hash Match predicate: key lists empty or mismatched") + + pairs = [f"(= {_col_ref_to_attr(b)} {_col_ref_to_attr(p)})" + for b, p in zip(build_refs, probe_refs)] + pred = pairs[0] + for p in pairs[1:]: + pred = f"(and {pred} {p})" + + join_type = _JOIN_TYPES.get(logical_op, "Inner") + return f"(HashJoin {join_type} {pred} {_convert_relop(build_relop)} {_convert_relop(probe_relop)})" + + +def _convert_merge_join(relop: ET.Element, logical_op: str) -> str: + merge = relop.find(f"{NS}Merge") + if merge is None: + raise ValueError("Merge element missing from Merge Join node") + outer_relop, inner_relop = _expect_two_relops(merge, "Merge") + + pred_elem = merge.find(f"{NS}Residual/{NS}ScalarOperator") + if pred_elem is None: + raise NotImplementedError("Merge Join with no Residual predicate") + + join_type = _JOIN_TYPES.get(logical_op, "Inner") + pred = _convert_scalar(pred_elem) + return f"(MergeJoin {join_type} {pred} {_convert_relop(outer_relop)} {_convert_relop(inner_relop)})" + + +def _expect_two_relops(parent: ET.Element, label: str) -> tuple[ET.Element, ET.Element]: + children = parent.findall(f"{NS}RelOp") + if len(children) != 2: + raise ValueError(f"{label} has {len(children)} RelOp children, expected 2") + return children[0], children[1] + + +def _has_outer_references(nested_loops: ET.Element) -> bool: + return nested_loops.find(f"{NS}OuterReferences/{NS}ColumnReference") is not None + + +def _contains_index_seek(relop: ET.Element) -> bool: + if "Seek" in relop.get("PhysicalOp", ""): + return True + return any(_contains_index_seek(child) for child in relop.findall(f".//{NS}RelOp")) + + +def _col_ref_to_attr(cr: ET.Element) -> str: + table = _strip_sql_name(cr.get("Alias") or cr.get("Table")) + col = cr.get("Column", "").strip("[]") + return f"(attr {table} {col})" + + +# ─── Scalar expression conversion ──────────────────────────────────────────── + +def _convert_scalar(scalar: ET.Element) -> str: + convert = scalar.find(f"{NS}Convert") + if convert is not None: + child = convert.find(f"{NS}ScalarOperator") + if child is None: + raise ValueError("Convert has no child ScalarOperator") + return _convert_scalar(child) + + compare = scalar.find(f"{NS}Compare") + if compare is not None: + return _convert_compare(compare) + + logical = scalar.find(f"{NS}Logical") + if logical is not None: + return _convert_logical(logical) + + arithmetic = scalar.find(f"{NS}Arithmetic") + if arithmetic is not None: + return _convert_arithmetic(arithmetic) + + identifier = scalar.find(f"{NS}Identifier") + if identifier is not None: + return _convert_identifier(identifier) + + const = scalar.find(f"{NS}Const") + if const is not None: + return _convert_const(const) + + raise NotImplementedError(f"unhandled ScalarOperator children: {[c.tag.split('}')[1] for c in scalar]}") + + +def _convert_compare(compare: ET.Element) -> str: + op_str = compare.get("CompareOp", "") + scalars = compare.findall(f"{NS}ScalarOperator") + + if op_str in ("IS", "IS NOT"): + # MS SQL emits "lhs IS NULL" / "lhs IS NOT NULL" as a Compare where the + # rhs is a NULL constant. Map to the project's (isnull lhs) / (not (isnull lhs)). + if len(scalars) != 2: + raise ValueError(f"IS/IS NOT Compare has {len(scalars)} ScalarOperators, expected 2") + lhs = _convert_scalar(scalars[0]) + rhs_const = scalars[1].find(f"{NS}Const") + if rhs_const is None or rhs_const.get("ConstValue", "").upper() not in ("NULL", "(NULL)"): + raise NotImplementedError( + f"CompareOp={op_str!r} with non-NULL rhs (IS DISTINCT FROM-like) is not supported" + ) + isnull = f"(isnull {lhs})" + return f"(not {isnull})" if op_str == "IS NOT" else isnull + + op = _COMPARE_OPS.get(op_str) + if op is None: + raise NotImplementedError(f"unhandled CompareOp: {op_str!r}") + + if len(scalars) != 2: + raise ValueError(f"Compare has {len(scalars)} ScalarOperator children, expected 2") + return f"({op} {_convert_scalar(scalars[0])} {_convert_scalar(scalars[1])})" + + +def _convert_logical(logical: ET.Element) -> str: + op_str = logical.get("Operation", "").upper() + children = [_convert_scalar(s) for s in logical.findall(f"{NS}ScalarOperator")] + + if op_str == "NOT": + if len(children) != 1: + raise ValueError(f"NOT expects 1 child, got {len(children)}") + return f"(not {children[0]})" + + op = _LOGICAL_OPS.get(op_str) + if op is None: + raise NotImplementedError(f"unhandled Logical Operation: {op_str!r}") + # MS SQL AND/OR can be n-ary — fold left into binary pairs. + result = children[0] + for child in children[1:]: + result = f"({op} {result} {child})" + return result + + +def _convert_arithmetic(arithmetic: ET.Element) -> str: + op_str = arithmetic.get("Operation", "").upper() + op = _ARITHMETIC_OPS.get(op_str) + if op is None: + raise NotImplementedError(f"unhandled Arithmetic Operation: {op_str!r}") + scalars = arithmetic.findall(f"{NS}ScalarOperator") + if len(scalars) != 2: + raise ValueError(f"Arithmetic has {len(scalars)} ScalarOperator children, expected 2") + return f"({op} {_convert_scalar(scalars[0])} {_convert_scalar(scalars[1])})" + + +def _convert_identifier(identifier: ET.Element) -> str: + col_ref = identifier.find(f"{NS}ColumnReference") + if col_ref is None: + raise ValueError("Identifier has no ColumnReference") + column = col_ref.get("Column", "").strip("[]") + + if column.startswith("ConstExpr"): + child = col_ref.find(f"{NS}ScalarOperator") + if child is None: + raise ValueError(f"ConstExpr {column!r} has no child ScalarOperator") + return _convert_scalar(child) + + if column.startswith("@"): + raise NotImplementedError( + f"parameter reference {column!r} — use OPTION (RECOMPILE) to embed literal values in the plan" + ) + + table = _strip_sql_name(col_ref.get("Alias") or col_ref.get("Table")) + return f"(attr {table} {column})" + + +def _convert_const(const: ET.Element) -> str: + raw = const.get("ConstValue", "").strip() + # String literal: 'text' or N'text', with '' as an escaped single quote. + s = raw[1:] if raw.startswith("N'") else raw + if len(s) >= 2 and s.startswith("'") and s.endswith("'"): + inner = s[1:-1].replace("''", "'") + return f"(str {_quote_cpp_string(inner)})" + return raw.strip("()") + + +def _quote_cpp_string(value: str) -> str: + """Re-quote a string into the C++ double-quoted form (plan_serializer.cpp).""" + out = ['"'] + for c in value: + if c == "\\": + out.append("\\\\") + elif c == '"': + out.append('\\"') + elif c == "\n": + out.append("\\n") + elif c == "\t": + out.append("\\t") + else: + out.append(c) + out.append('"') + return "".join(out) diff --git a/research/cost-on-random.ipynb b/research/cost-on-random.ipynb new file mode 100644 index 0000000..837c3ca --- /dev/null +++ b/research/cost-on-random.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "id": "aed19ec2", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-01T23:13:22.984825Z", + "iopub.status.busy": "2026-06-01T23:13:22.984699Z", + "iopub.status.idle": "2026-06-01T23:13:22.988427Z", + "shell.execute_reply": "2026-06-01T23:13:22.987712Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "collected=25 attempted=25 skipped=0\n", + "collected=50 attempted=50 skipped=0\n", + "collected=75 attempted=75 skipped=0\n", + "collected=99 attempted=100 skipped=0\n", + "collected=100 attempted=100 skipped=0\n", + "collected=125 attempted=125 skipped=0\n", + "collected=150 attempted=150 skipped=0\n", + "collected=175 attempted=175 skipped=0\n", + "collected=199 attempted=200 skipped=0\n", + "collected=200 attempted=200 skipped=0\n", + "collected=225 attempted=225 skipped=0\n", + "collected=250 attempted=250 skipped=0\n", + "collected=275 attempted=275 skipped=0\n", + "collected=299 attempted=300 skipped=0\n", + "collected=300 attempted=300 skipped=0\n", + "collected=325 attempted=325 skipped=0\n", + "collected=350 attempted=350 skipped=0\n", + "collected=375 attempted=375 skipped=0\n", + "collected=399 attempted=400 skipped=0\n", + "collected=400 attempted=400 skipped=0\n", + "done: collected=400 attempted=400 skipped=0 -> research/cost-stats.csv\n" + ] + } + ], + "source": [ + "!cd ~/c/iu9-sql-compiler/ && python -m research.fuzz.cost_stats \\\n", + " --cli build-release/bin/sql \\\n", + " --data-dir test/static/executor/test_data \\\n", + " --count 400 --repeats 5 \\\n", + " --out research/cost-stats.csv" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c50c4c78", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-01T23:13:22.990006Z", + "iopub.status.busy": "2026-06-01T23:13:22.989865Z", + "iopub.status.idle": "2026-06-01T23:13:23.532199Z", + "shell.execute_reply": "2026-06-01T23:13:23.531501Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "400 samples\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
seedplan_costexec_usrows
count400.0000004.000000e+024.000000e+024.000000e+02
mean199.5000008.429012e+064.220586e+071.402288e+04
std115.6143017.494437e+072.657503e+081.405880e+05
min0.0000003.660000e+023.660000e+021.110000e+02
25%99.7500002.425500e+032.930000e+031.927500e+02
50%199.5000003.615500e+045.414800e+043.265000e+02
75%299.2500006.801598e+051.550466e+061.315500e+03
max399.0000001.196362e+092.984422e+092.615226e+06
\n", + "
" + ], + "text/plain": [ + " seed plan_cost exec_us rows\n", + "count 400.000000 4.000000e+02 4.000000e+02 4.000000e+02\n", + "mean 199.500000 8.429012e+06 4.220586e+07 1.402288e+04\n", + "std 115.614301 7.494437e+07 2.657503e+08 1.405880e+05\n", + "min 0.000000 3.660000e+02 3.660000e+02 1.110000e+02\n", + "25% 99.750000 2.425500e+03 2.930000e+03 1.927500e+02\n", + "50% 199.500000 3.615500e+04 5.414800e+04 3.265000e+02\n", + "75% 299.250000 6.801598e+05 1.550466e+06 1.315500e+03\n", + "max 399.000000 1.196362e+09 2.984422e+09 2.615226e+06" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "df = pd.read_csv('cost-stats.csv')\n", + "df['exec_ms'] = df['exec_us'] / 1e3\n", + "print(f'{len(df)} samples')\n", + "df[['seed', 'plan_cost', 'exec_us', 'rows']].describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "49d0ecdc", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-01T23:13:23.533678Z", + "iopub.status.busy": "2026-06-01T23:13:23.533532Z", + "iopub.status.idle": "2026-06-01T23:13:23.674869Z", + "shell.execute_reply": "2026-06-01T23:13:23.674232Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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apS6d++umTNnKiIiQq+99prGjx+vefPmeb4UTp48WW+//baefvpp9erVS06nU999950OHz4sSXr88cfldDr1wQcfaOXKlZ77b9KkSbm1/f73v9fLL7+s//znP7rllls8y48dO6b//e9/uvPOOxUWFqZdu3bpkksu0ZAhQ/TWW2+pXr162rdvn5KTk1VQUCC73X7G16A8BQUFuvTSS3Xbbbdp6tSpKioqktvt1mWXXably5frwQcf1ODBg/XLL7/oySef1PDhw7VmzRrZbDZJ0vbt2zV27FhPOPrhhx/07LPP6ttvvz1rT37N19et1COPPKIRI0Zo9uzZ2rVrl6ZMmaLrrrtOoaGh6tGjh+bNm6f169frkUceUd26dfXKK694tr3ttts0e/Zs3XPPPXr22Wd15MgR/elPf9LgwYO1ceNGxcbG+vQcTufzzz9Xr169VK9evQqtf+pnp6r6UV7vnU6nRo0apTZt2ujVV19VbGysMjIytGTJEuXk5EgqCWOXX365vvrqKz388MMaMmSINm3apCeffFIrV67UypUrFRER4XmcjRs36v/+7/80depUxcbG6t///rduvvlmtW/fXkOHDj3ra/HKK6+oQ4cOeuGFFxQVFaULLrjgtOsWFxdX6PAxq9Uqq/X0v2nu2LFDubm56t69e5mx7t27a9GiRcrLy1NkZORZH+tUpT3o0qWLZ1lBQYEaNGhQZt3S13DTpk0aOHCgcnNztWPHDl1xxRXl1pSbm6uff/5ZHTp08CwfPny4Hn74YR07dqzC7z3gvBDgPSYA/FB6KNQ333xjCgsLTU5Ojvnss89MTEyMqVu3rsnIyDDGVOxQqKKiInP8+HHjcDjMyy+/XOYx7rjjDq/1n3vuOSPJpKenn7HGdu3amXbt2pnc3NzTrjNw4EDTuHFjk5OT41VP165dTfPmzY3b7TbGGNO1a1dz+eWXn/HxfD2kp3fv3mbw4MFey1577TUjyWzevNkYY8wHH3xgJJkNGzZU+H7PZOLEiUaSeeutt7yWz5s3z0gy8+fP91q+evVqI8m89tpr5d6f2+02hYWFZtmyZUaS2bhxo2esMg6FatWqlZk4caLndun7afz48V7r3XfffUaSueeee7yWX3755aZBgwae2ytXrjSSzIsvvui13p49e4zNZjvr4S6+sNvt5vbbby+zvPR1ycjIMIWFhebo0aNm7ty5xmazmRYtWpjc3Nwq6cfper9mzRojyXz88cenfS7JyclGknnuuee8lr///vtGknnzzTc9y1q1amUiIyPNL7/84lmWm5trGjRoYG677bbTPoYxxuzcudNIMu3atTMFBQVnXLfUsGHDjKSz/p36PipPWlqakWTmzZtXZuyZZ54xksz+/fsrVFOpjRs3GpvNZq644gqv5ZdffrmpV6+e1787xhgzZMgQI8k888wzxhhj9u3bZySZ6dOnl7nvd99910gyK1as8Fq+aNEiI8l88cUXPtUKBLtafSjU119/rfHjx6tp06ayWCznNIuDMUYvvPCCOnTooIiICLVo0ULPPPNM5RcLnMHAgQMVFhamunXraty4cYqLi9MXX3xxxl99jx8/roceekjt27dXaGioQkNDVadOHTmdTn3//fdl1r/00ku9bpf+onimw61+/PFH7dixQzfffPNpf2F0Op1atWqVrrrqKtWpU8ezPCQkRDfccIP27t3rOTmyf//++uKLLzR16lQtXbpUubm5p39RKmjSpElasWKF1wmYs2bNUr9+/dS1a1dJUs+ePRUeHq4//OEP+n//7/9V2uE6EyZM8Lr92WefqV69eho/fryKioo8fz179lRcXJzXrF4///yzfvvb3youLk4hISEKCwvTsGHDJKnc/lWFcePGed3u1KmTJOmSSy4ps/zIkSOew6E+++wzWSwWXX/99V7PMy4uTj169Ki02cuOHTsml8ulxo0bn3aduLg4hYWFqX79+rr++uvVu3dvJScnKzIyskr78evet2/fXvXr19dDDz2kN954Q1u3bi2zTemv7r+eoevqq6+Ww+HQV1995bW8Z8+eatmyped2ZGSkOnTocMbP7KkuvfRShYWFVWjdf/7zn1q9evVZ/yo6Y9uZZjKryCxnpXbt2qVx48apRYsW+ve//+01dtdddykrK0s33nijfv75Zx04cECPP/64VqxYIUll9qz4UlPpe+7U2fmA2qBWHwrldDrVo0cPTZo0qcw/8hV17733auHChXrhhRfUrVs3ZWVl6dChQ5VcKXBmc+bMUadOnRQaGqrY2NizHsIiSb/97W/11Vdf6fHHH1e/fv0UFRUli8WisWPHlvuFvWHDhl63Sw8XONOX+9Lj0Js3b37adY4ePSpjTLk1N23aVJI8hzq98sorat68ud5//309++yzioyMVGJiop5//vkzHqZxJr/73e80ZcoUzZ49W9OnT9fWrVu1evVqvfbaa5512rVrpy+//FLPPfec7rzzTjmdTrVt21b33HOP7r333nN6XLvd7nUMuSQdOHBAx44dU3h4eLnblP7bcvz4cQ0ZMkSRkZF6+umn1aFDB9ntdu3Zs0dXXnllpQSuivj1YSSldZ9ueV5enurUqaMDBw7IGHPa4Nu2bdtKqa/0dTjTYTNffvmloqOjFRYWpubNm3u9z6uqH+X1Pjo6WsuWLdNf/vIXPfLIIzp69KiaNGmiW2+9VY899pjCwsJ0+PBhhYaGljnE0WKxKC4uzvM5KfXrz6xU8rmt6PujIv+OlGrfvn2FD4U6k9Kaf/1cJOnIkSOyWCwVPrTol19+0YgRIxQaGqqvvvqqzPty5MiRmjVrlv7v//5P7dq1kyR17txZf/7zn/XII494zr2oX7++LBbLaWuSyr7nS99z1fVZBGqKWh0sxowZozFjxpx2vKCgQI899pjeeecdHTt2TF27dtWzzz6r4cOHSyr5Fer111/Xd999pwsvvLCaqgbK6tSpk/r27Vvh9bOysvTZZ5/pySef1NSpUz3L8/PzPf+hrAylX4BOPfn01+rXry+r1ar09PQyY6Unh5ee7+FwODRt2jRNmzZNBw4c8Oy9GD9+vH744YdzqrF+/fq67LLLNGfOHD399NOaNWuWIiMjdd1113mtN2TIEA0ZMkTFxcVas2aN/v73v+u+++5TbGysrr32Wp8ft7xfP0tPjE9OTi53m7p160oq+eV6//79Wrp0qedXcUlBc/2FRo0ayWKxaPny5V7nBJQqb9m5KP2Seqb3dI8ePcqcT3RqnVXRj9P98t2tWze99957MsZo06ZNmj17tv70pz/JZrNp6tSpatiwoYqKinTw4EGvcGGMUUZGhvr163fa53kufNkzMHLkSC1btuys602cOFGzZ88+7Xi7du1ks9m0efPmMmObN29W+/btK3R+xS+//KLhw4fLGKOlS5ee9seNiRMn6ne/+522b9+usLAwtW/fXtOnT5fFYtGQIUMkSTabTe3btz9tTTabrUwYLn3Pne69BZyvavWhUGczadIkpaWl6b333tOmTZt09dVXKykpSdu3b5ckffrpp2rbtq0+++wztWnTRq1bt9Ytt9xSqV/MgKpgsVhkjCnzBe7f//63iouLK+1xOnTooHbt2umtt95Sfn5+ues4HA4NGDBAH374odeve263W3PnzlXz5s29ToosFRsbq5tuuknXXXedtm3b5pmdqSJ7Un5t0qRJ2r9/vxYsWKC5c+fqiiuuOO2voiEhIRowYIBnFqVfXyDLH+PGjdPhw4dVXFysvn37lvkr/QGj9Avfr/v3z3/+85wf+1xet3NVej2Bffv2lfs8u3XrVimPEx4errZt22rHjh3nXGcg+mGxWNSjRw/97W9/U7169TzvsdLZo+bOneu1/vz58+V0Oj3jgVBZh0KFhoZq/Pjx+vDDDz0nrUsl16dYsmSJrrzyyrPWsnv3bg0fPlzFxcVavHix10xOp3vMTp06qX379srKytKbb76pyy67zGu7K664QosXL/aaVSonJ0cffvihLr30UoWGev9OW3q4ZOfOnc9aL3A+qdV7LM5kx44dmjdvnvbu3es5HGPKlClKTk7WrFmz9Mwzz+jnn3/WL7/8ov/+97+aM2eOiouLdf/99+uqq67yeVYWoDpFRUVp6NChev7559WoUSO1bt1ay5Yt08yZMyt9BpNXX31V48eP18CBA3X//ferZcuW2r17t1JSUvTOO+9IkqZPn65Ro0ZpxIgRmjJlisLDw/Xaa6/pu+++07x58zxf3AYMGKBx48ape/fuql+/vr7//nu9/fbbGjRokGdmptIvpc8++6zGjBmjkJAQde/e/bSHs0jS6NGj1bx5c91xxx3KyMjQpEmTvMbfeOMNLV68WJdccolatmypvLw8z7SdF198sWe99u3bSzo5s5Cvrr32Wr3zzjsaO3as7r33XvXv319hYWHau3evlixZossuu0xXXHGFBg8erPr16+v222/Xk08+qbCwML3zzjvauHHjOT2udG6v27mKj4/XH/7wB02aNElr1qzR0KFD5XA4lJ6ertTUVHXr1k1//OMfK+Wxhg8fri+++OKctq3Ofnz22Wd67bXXdPnll6tt27YyxujDDz/UsWPHNGrUKEnSqFGjlJiYqIceekjZ2dmKj4/3zArVq1cv3XDDDef0PCtDZe61nzZtmvr166dx48Zp6tSpysvL0xNPPKFGjRrp//7v/7zWDQ0N1bBhwzznl2RmZmrEiBFKT0/XzJkzlZmZqczMTM/6zZs39+y9yMzM1Isvvqj4+HjVrVtXP/zwg5577jlZrVbPDwelpkyZorfffluXXHKJ/vSnPykiIkJ//etflZeXV25Y+uabb9SwYcNKC8lA0AjUWeM1jSTz0UcfeW7/5z//MZKMw+Hw+gsNDfVcDOfWW281ksy2bds8261du9ZIMj/88EN1PwXUQqe7QN6vlTcr1N69e82ECRNM/fr1Td26dU1SUpL57rvvyswAdLrH8OWieytXrjRjxowx0dHRJiIiwrRr187cf//9XussX77cXHTRRcbhcBibzWYGDhxoPv30U691pk6davr27Wvq169vIiIiTNu2bc39999vDh065FknPz/f3HLLLSYmJsZYLBYjyezcufOsNT7yyCNGUpkLY5XWf8UVV5hWrVqZiIgI07BhQzNs2DDzySefeK3XqlUr06pVq7M+1sSJE43D4Sh3rLCw0LzwwgumR48eJjIy0tSpU8d07NjR3HbbbWb79u2e9VasWGEGDRpk7Ha7iYmJMbfccotZt26dkWRmzZrlWa+is0Kd6XU73axQ//3vf73u43TvldNdjO6tt94yAwYM8PS8Xbt25sYbbzRr1qw5a70V9dVXXxlJ5ttvv61QTb9W2f04Xe9/+OEHc91115l27doZm81moqOjTf/+/c3s2bO91svNzTUPPfSQadWqlQkLCzNNmjQxf/zjH83Ro0e91mvVqpW55JJLyjzOsGHDzLBhw874nEtnhXr++efPuF5VWrNmjRk5cqSx2+0mKirKXH755eann34qs54kr+dT+t483d+TTz7pWffw4cNm9OjRJiYmxoSFhZmWLVuau++++7TviZ9++slcfvnlJioqytjtdjNy5Eizdu3aMuu53W7TqlUrc/fdd/v9OgDBxmJMBc62qgUsFos++ugjXX755ZJKLnb0u9/9Tlu2bFFISIjXunXq1FFcXJyefPJJPfPMM54LaEklhxHY7XYtXLjQ8ysTACBwunfvrvj4eL3++uuBLgW1wFdffaXRo0dry5Yt6tixY6DLAaoV51icRq9evVRcXKzMzEy1b9/e6y8uLk5Sye78oqIir+N3f/zxR0k66zGdAIDq8dxzz2n27NlnnEQAqCxPP/20fv/73xMqUCvV6j0Wx48f9xwL3atXL7300ksaMWKEGjRooJYtW+r6669XWlqaXnzxRfXq1UuHDh3S4sWL1a1bN40dO1Zut1v9+vVTnTp1NGPGDLndbt15552KioryuqowACCw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p44aNGjg2cNQ+lxKn/+pz8tqtcpisZRZZrVaFRoaqs6dO+t3v/udfv/73+uFF15Qr169dOjQIS1evFjdunXT2LFjy63n1OPoz8U999yj5557Th07dtQFF1yg6dOny2636/rrr/fUOnHiRDVt2lTTp0+XVDIF7LBhw/Tiiy/qsssu0//+9z999dVXWr58uWebyZMn68Ybb1S/fv00aNAgvfnmm9qzZ4/uuOMOzzpHjhzR7t27tX//fknSjh07FBoaqri4OMXFxalhw4b6/e9/r4ceekiNGzdWgwYN9MADD6hbt25KTExUSEiIEhISVL9+fd1yyy16/PHHZbPZ9K9//Uu7du3S+PHjz/jeKn3tTxUSEuJZdur/hoaG6r777tOYMWPUoUMHHT161PMD+Oke4+6771aLFi30xBNPqKCgQH369NHUqVP16quvnrYXr732mm644QZNnTpV0dHR+uabb9S/f39deOGFmjJlip566im1bdtWffr00axZs7Rx40a98847Cg0N1d/+9jc1adJEPXv2lNVq1Ycffqi4uDg1atRIVqtVrVu31tKlSzV06FBFRESUOXel9LlarVY1bNhQkZHe3y1/fftMatwn2hjjdejSqQYNGqRPP/3Ua9nChQvVt2/f055fERERoYiIiDLLSz/UgWKxWHyq4WhuofSrvTJH84oUW4//8FYXX3uGwKNnwYm+BR9felb6Rbv0L1g88MADmjhxorp06aLc3Fzt3LnTU/+vn8+pz6u8Zb8emzVrlp5++mlNmTJF+/btU8OGDTVo0CBdcsklVfIaGWM0ZcoU5efn684779TRo0c1YMAALVy40OtH1927d3v6JZUc1//ee+/pscce0xNPPKF27drp/fff18CBAz3bXHvttTpy5Ij+/Oc/Kz09XV27dtWCBQs8sxRJ0qeffqpJkyZ5bl933XWSpCeffFJPPfWUJGnGjBkKCwvTNddco9zcXI0cOVKzZ8/2fJmPiYlRcnKyHn30UY0cOVKFhYXq0qWL/ve//6lnz55nfP7lvffK61/pMrfbrbvuukt79+5VVFSUkpKS9Le//a3c3syZM0cLFizQ+vXrFRYWprCwML3zzjsaPHiwxo0bV25QbNSokRYvXqwHHnhAw4cPV0hIiHr27Ok5Gfvee+9Vdna2HnroIWVmZqpz58765JNPPOfl1K1bV88995y2b9+ukJAQ9evXTwsWLPAE3xdffFGTJ0/Wv//9bzVr1ky7du067WtS3ufYl3+LLcaXgwsr2SOPPKIxY8aoRYsWysnJ0Xvvvae//vWvSk5O1qhRo/Twww9r3759mjNnjqSS6dG6du2q2267TbfeeqtWrlyp22+/XfPmzavwrFDZ2dmKjo5WVlZWQPdYZGZmqnHjxhVu1oqfDmnVziOe26FWi36f0EaOiBqXDc9L59IzBBY9C070Lfj42rO8vDzt3LnTc2VmVL/SQ7NCQ0ODKtzVZlXdszN9Ln357hzQb6UHDhzQDTfcoPT0dEVHR6t79+6eUCGVHE936pUa27RpowULFuj+++/Xq6++qqZNm+qVV16pFVPN9m/TQPlFbv14IEeOiFAltG9EqAAAAECNEdBvpjNnzjzj+OzZs8ssGzZsmNatW1dFFdVcoSFWjejYWCM6Ng50KQAAAEAZ7GcGAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAqh1XVwdqjsr6PDKtEAAAqDbh4eGyWq3av3+/YmJiFB4ezpSn1YzpZoNPVfXMGKOCggIdPHhQVqtV4eHhft0fwQIAAFQbq9WqNm3aKD093XPlZVQvY4zcbrfXxe9Qs1V1z+x2u1q2bOn39YMIFgAAoFqFh4erZcuWKioqUnFxcaDLqXXcbrcOHz6shg0bciHKIFGVPQsJCam0PSEECwAAUO0sFovCwsIUFhYW6FJqHbfbrbCwMEVGRhIsgkSw9KzmVgYAAAAgaBAsAAAAAPiNYAEAAADAbwQLAAAAAH4jWAAAAADwG8ECAAAAgN8IFgAAAAD8RrAAAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAAMBvBAsAAAAAfiNYAAAAAPAbwQIAAACA3wgWAAAAAPxGsAAAAADgN4IFAAAAAL8RLAAAAAD4jWABAAAAwG8ECwAAAAB+I1gAAAAA8BvBAgAAAIDfCBYAAAAA/EawAAAAAOA3ggUAAAAAvxEsAAAAAPiNYAEAAADAbwQLAAAAAH4jWAAAAADwG8ECAAAAgN8IFgAAAAD8RrAAAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAAMBvBAsAAAAAfiNYAAAAAPAbwQIAAACA3wgWAAAAAPxGsAAAAADgN4IFAAAAAL8RLAAAAAD4jWABAAAAwG8ECwAAAAB+I1gAAAAA8BvBAgAAAIDfCBYAAAAA/EawAAAAAOA3ggUAAAAAvxEsAAAAAPiNYAEAAADAbwQLAAAAAH4jWAAAAADwG8ECAAAAgN8IFgAAAAD8RrAAAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAAMBvAQ0W06dPV79+/VS3bl01btxYl19+ubZt23bGbZYuXSqLxVLm74cffqimqgEAAAD8WkCDxbJly3TnnXfqm2++0aJFi1RUVKTRo0fL6XSeddtt27YpPT3d83fBBRdUQ8UAAAAAyhMayAdPTk72uj1r1iw1btxYa9eu1dChQ8+4bePGjVWvXr0qrA4AAABARQU0WPxaVlaWJKlBgwZnXbdXr17Ky8tT586d9dhjj2nEiBHlrpefn6/8/HzP7ezsbEmS2+2W2+2uhKp953a7ZYwJ2OPDd/Qs+NCz4ETfgg89Cz70LPgEsme+PGaNCRbGGE2ePFkJCQnq2rXraddr0qSJ3nzzTfXp00f5+fl6++23NXLkSC1durTcvRzTp0/XtGnTyiw/ePCg8vLyKvU5VJTb7VZWVpaMMbJaOX8+GNCz4EPPghN9Cz70LPjQs+ATyJ7l5ORUeF2LMcZUYS0Vduedd+rzzz9Xamqqmjdv7tO248ePl8Vi0SeffFJmrLw9Fi1atNDRo0cVFRXld93nwu126+DBg4qJieEDHSToWfChZ8GJvgUfehZ86FnwCWTPsrOzVb9+fWVlZZ31u3ON2GNx991365NPPtHXX3/tc6iQpIEDB2ru3LnljkVERCgiIqLMcqvVGtAPk8ViCXgN8A09Cz70LDjRt+BDz4IPPQs+geqZL48X0GBhjNHdd9+tjz76SEuXLlWbNm3O6X7Wr1+vJk2aVHJ1AAAAACoqoMHizjvv1Lvvvqv//e9/qlu3rjIyMiRJ0dHRstlskqSHH35Y+/bt05w5cyRJM2bMUOvWrdWlSxcVFBRo7ty5mj9/vubPnx+w5wEAAADUdgENFq+//rokafjw4V7LZ82apZtuukmSlJ6ert27d3vGCgoKNGXKFO3bt082m01dunTR559/rrFjx1ZX2QAAAAB+JeCHQp3N7NmzvW4/+OCDevDBB6uoIgAAAADngjN2AAAAAPiNYAEAAADAbwQLAAAAAH4jWAAAAADwG8ECAAAAgN8IFgAAAAD8RrAAAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAAMBvBAsAAAAAfiNYAAAAAPAbwQIAAACA3wgWAAAAAPxGsAAAAADgN4IFAAAAAL8RLAAAAAD4jWABAAAAwG8ECwAAAAB+I1gAAAAA8BvBAgAAAIDfCBYAAAAA/EawAAAAAOA3ggUAAAAAvxEsAAAAAPiNYAEAAADAbwQLAAAAAH4jWAAAAADwG8ECAAAAgN8IFgAAAAD8RrAAAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAAMBvBAsAAAAAfiNYAAAAAPAbwQIAAACA3wgWAAAAAPxGsAAAAADgN4IFAAAAAL8RLAAAAAD4jWABAAAAwG8ECwAAAAB+I1gAAAAA8BvBAgAAAIDfCBYAAAAA/EawAAAAAOA3ggUAAAAAvxEsAAAAAPiNYAEAAADAbwQLAAAAAH4jWAAAAADwG8ECAAAAgN8IFgAAAAD8RrAAAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAAMBvBAsAAAAAfiNYAAAAAPAbwQIAAACA3wgWAAAAAPxGsAAAAADgN4IFAAAAAL8RLAAAAAD4jWABAAAAwG8BDRbTp09Xv379VLduXTVu3FiXX365tm3bdtbtli1bpj59+igyMlJt27bVG2+8UQ3VAgAAADidgAaLZcuW6c4779Q333yjRYsWqaioSKNHj5bT6TztNjt37tTYsWM1ZMgQrV+/Xo888ojuuecezZ8/vxorBwAAAHCq0EA+eHJystftWbNmqXHjxlq7dq2GDh1a7jZvvPGGWrZsqRkzZkiSOnXqpDVr1uiFF17QhAkTqrpkAAAAAOUIaLD4taysLElSgwYNTrvOypUrNXr0aK9liYmJmjlzpgoLCxUWFuY1lp+fr/z8fM/t7OxsSZLb7Zbb7a6s0n3idrtljAnY48N39Cz40LPgRN+CDz0LPvQs+ASyZ748Zo0JFsYYTZ48WQkJCeratetp18vIyFBsbKzXstjYWBUVFenQoUNq0qSJ19j06dM1bdq0Mvdz8OBB5eXlVU7xPnK73crKypIxRlYr588HA3oWfOhZcKJvwYeeBR96FnwC2bOcnJwKr1tjgsVdd92lTZs2KTU19azrWiwWr9vGmHKXS9LDDz+syZMne25nZ2erRYsWiomJUVRUlJ9Vnxu32y2LxaKYmBg+0EGCngUfehac6FvwoWfBh54Fn0D2LDIyssLr1ohgcffdd+uTTz7R119/rebNm59x3bi4OGVkZHgty8zMVGhoqBo2bFhm/YiICEVERJRZbrVaA/phslgsAa8BvqFnwYeeBSf6FnzoWfChZ8EnUD3z5fEC+m4yxuiuu+7Shx9+qMWLF6tNmzZn3WbQoEFatGiR17KFCxeqb9++Zc6vAAAAAFA9Ahos7rzzTs2dO1fvvvuu6tatq4yMDGVkZCg3N9ezzsMPP6wbb7zRc/v222/XL7/8osmTJ+v777/XW2+9pZkzZ2rKlCmBeAoAAAAAFOBg8frrrysrK0vDhw9XkyZNPH/vv/++Z5309HTt3r3bc7tNmzZasGCBli5dqp49e+rPf/6zXnnlFaaaBQAAAAIooOdYlJ50fSazZ88us2zYsGFat25dFVQEAAAA4Fxwxg4AAAAAv9WIWaFqq4Iit5yFhdqWkaPdh11q4AjXoHYlM1t98/MRHT6er5YN7OrfpoFCQ8iAAAAAqLkIFgGy69Bxrd2ao20Hjisnr1AXxtVVelae0rNyZbVYdOh4gSQpPStPuYXFGtkp9iz3CAAAAASOXz+D5+fnV1YdtUpOXqE27c1WQZFbh4/ny1VQrF8OuyRJe47k6pcjLq/1f8io+BUPAQAAgEDwKVikpKTopptuUrt27RQWFia73a66detq2LBh+stf/qL9+/dXVZ3nlcPOAs+J62EnDnFy5hdJksJDrQr/1WFPjvCQ6i0QAAAA8FGFgsXHH3+sCy+8UBMnTpTVatUDDzygDz/8UCkpKZo5c6aGDRumL7/8Um3bttXtt9+ugwcPVnXdQS22bqRCrBZJUosGdlksUpSt5OJ+A9s29JxnIUlWi0UJFzQKSJ0AAABARVXoHItnnnlGL7zwgi655JJyL+v9m9/8RpK0b98+vfzyy5ozZ47+7//+r3IrPY/YwkPUp1V9rc10y2K1qHOTZrowrq6a1rMpLjpSktQhtq4OHc9X8/p2Rdu4ojgAAABqtgoFi2+//bZCd9asWTM999xzfhVUWzSpZ9OkC2Iki9Wz9+JUsVGRio2KDEBlAAAAgO/8nsO0uLhYGzZs0NGjRyujnlrFYrGUGyoAAACAYONzsLjvvvs0c+ZMSSWhYtiwYerdu7datGihpUuXVnZ9AAAAAIKAz8Higw8+UI8ePSRJn376qXbu3KkffvhB9913nx599NFKLxAAAABAzedzsDh06JDi4uIkSQsWLNDVV1+tDh066Oabb9bmzZsrvUAAAAAANZ/PwSI2NlZbt25VcXGxkpOTdfHFF0uSXC6XQkK43gIAAABQG1VoVqhTTZo0Sb/5zW/UpEkTWSwWjRo1SpK0atUqdezYsdILBAAAAFDz+RwsnnrqKXXt2lV79uzR1VdfrYiICElSSEiIpk6dWukFAgAAAKj5fA4WknTVVVeVWTZx4kS/iwEAAAAQnM4pWHz77bdaunSpMjMz5Xa7vcZeeumlSikMAAAAQPDwOVg888wzeuyxx3ThhRcqNjZWFsvJC7yd+v8BAAAA1B4+B4uXX35Zb731lm666aYqKAcAAABAMPJ5ulmr1ar4+PiqqAUAAABAkPI5WNx///169dVXq6IWAAAAAEHK50OhpkyZoksuuUTt2rVT586dFRYW5jX+4YcfVlpxAAAAAIKDz8Hi7rvv1pIlSzRixAg1bNiQE7YBAAAA+B4s5syZo/nz5+uSSy6pinoAAAAABCGfz7Fo0KCB2rVrVxW1AAAAAAhSPgeLp556Sk8++aRcLldV1AMAAAAgCPl8KNQrr7yiHTt2KDY2Vq1bty5z8va6desqrTgAAAAAwcHnYHH55ZdXQRkAAAAAgpnPweLJJ5+sijpqrey8Qq3ZdUQ5eUVqF1NHnZpEaf3uo9p7NFeNoyLUp1V9RYSGBLpMAAAA4Ix8DhYVYYxhGtoKKCp2679r9io7t1CS9PNBp1J/OqTcgmJJ0s5DTh3IztMVvZoHskwAAADgrCp08nanTp307rvvqqCg4Izrbd++XX/84x/17LPPVkpx57u9x3I9oUIqCWSrdx7xWmfXIZeO5xdVd2kAAACATyq0x+LVV1/VQw89pDvvvFOjR49W37591bRpU0VGRuro0aPaunWrUlNTtXXrVt1111264447qrru80JEaNlc9+tlIVaLQq3s/QEAAEDNVqFgcdFFF2n16tVasWKF3n//fb377rvatWuXcnNz1ahRI/Xq1Us33nijrr/+etWrV6+KSz5/NIm2qXUju3YdKpm6NzTEqkt7NNX3GTmedXq1rKfIMM6xAAAAQM3m0zkWgwcP1uDBg6uqllrpsh7N9PMhp7LzCtWuUR1F28PUs2V97TvmUuO6kWrRwB7oEgEAAICzqpKTt1FxVqtF7RvX8VoWFx2puOjIAFUEAAAA+M7nK28DAAAAwK8RLAAAAAD4jWABAAAAwG8ECwAAAAB+O6dgsWPHDj322GO67rrrlJmZKUlKTk7Wli1bKrU4AAAAAMHB52CxbNkydevWTatWrdKHH36o48ePS5I2bdqkJ598stILBAAAAFDz+Rwspk6dqqefflqLFi1SeHi4Z/mIESO0cuXKSi0OAAAAQHDwOVhs3rxZV1xxRZnlMTExOnz4cKUUBQAAACC4+Bws6tWrp/T09DLL169fr2bNmlVKUQAAAACCi8/B4re//a0eeughZWRkyGKxyO12Ky0tTVOmTNGNN95YFTUCAAAAqOF8DhZ/+ctf1LJlSzVr1kzHjx9X586dNXToUA0ePFiPPfZYVdQIAAAAoIYL9XWDsLAwvfPOO/rTn/6k9evXy+12q1evXrrggguqoj4AAAAAQcDnYFGqXbt2ateuXWXWAgAAACBI+RwsjDH64IMPtGTJEmVmZsrtdnuNf/jhh5VWHAAAAIDg4HOwuPfee/Xmm29qxIgRio2NlcViqYq6AAAAAAQRn4PF3Llz9eGHH2rs2LFVUQ8AAACAIOTzrFDR0dFq27ZtVdQCAAAAIEj5HCyeeuopTZs2Tbm5uVVRDwAAAIAg5POhUFdffbXmzZunxo0bq3Xr1goLC/MaX7duXaUVBwAAACA4+BwsbrrpJq1du1bXX389J28DAAAAkHQOweLzzz9XSkqKEhISqqIeAAAAAEHI53MsWrRooaioqKqoBQAAAECQ8jlYvPjii3rwwQe1a9euKigHAAAAQDDy+VCo66+/Xi6XS+3atZPdbi9z8vaRI0cqrTgAAAAAwcHnYDFjxowqKAMAAABAMPM5WEycOLEq6gAAAAAQxCoULLKzsz0nbGdnZ59xXU7sBgAAAGqfCgWL+vXrKz09XY0bN1a9evXKvXaFMUYWi0XFxcWVXiQAAACAmq1CwWLx4sVq0KCBJGnJkiVVWhAAAACA4FOhYDFs2DDP/2/Tpo1atGhRZq+FMUZ79uyp3OoAAAAABAWfr2PRpk0bHTx4sMzyI0eOqE2bNpVSFAAAAIDg4nOwKD2X4teOHz+uyMhIn+7r66+/1vjx49W0aVNZLBZ9/PHHZ1x/6dKlslgsZf5++OEHnx4XAAAAQOWq8HSzkydPliRZLBY9/vjjstvtnrHi4mKtWrVKPXv29OnBnU6nevTooUmTJmnChAkV3m7btm1es0/FxMT49LgAAAAAKleFg8X69eslleyx2Lx5s8LDwz1j4eHh6tGjh6ZMmeLTg48ZM0ZjxozxaRtJntmpAAAAANQMFQ4WpbNBTZo0SS+//HJAr1fRq1cv5eXlqXPnznrsscc0YsSI066bn5+v/Px8z+3S63C43W653e4qr7U8brdbxpiAPT58R8+CDz0LTvQt+NCz4EPPgk8ge+bLY/p85e1Zs2b5ukmladKkid5880316dNH+fn5evvttzVy5EgtXbpUQ4cOLXeb6dOna9q0aWWWHzx4UHl5eVVdcrncbreysrJkjJHV6vNpLggAehZ86Flwom/Bh54FH3oWfALZs5ycnAqvazHGmCqspcIsFos++ugjXX755T5tN378eFksFn3yySfljpe3x6JFixY6evRowPa6uN1uHTx4UDExMXyggwQ9Cz70LDjRt+BDz4IPPQs+gexZdna26tevr6ysrLN+d/Z5j0VNM3DgQM2dO/e04xEREYqIiCiz3Gq1BvTDZLFYAl4DfEPPgg89C070LfjQs+BDz4JPoHrmy+MF/btp/fr1atKkSaDLAAAAAGq1gO6xOH78uH766SfP7Z07d2rDhg1q0KCBWrZsqYcfflj79u3TnDlzJEkzZsxQ69at1aVLFxUUFGju3LmaP3++5s+fH6inAAAAAEDnGCx+/PFHLV26VJmZmWXOFH/iiScqfD9r1qzxmtGp9FoZEydO1OzZs5Wenq7du3d7xgsKCjRlyhTt27dPNptNXbp00eeff66xY8eey9MAAAAAUEl8Pnn7X//6l/74xz+qUaNGiouL87oKt8Vi0bp16yq9yMqUnZ2t6OjoCp2AUlXcbrcyMzPVuHFjjm0MEvQs+NCz4ETfgg89Cz70LPgEsme+fHf2eY/F008/rb/85S966KGHzrlAAAAAAOcXnyPP0aNHdfXVV1dFLQAAAACClM/B4uqrr9bChQurohYAAAAAQcrnQ6Hat2+vxx9/XN988426deumsLAwr/F77rmn0ooDAAAAEBx8DhZvvvmm6tSpo2XLlmnZsmVeYxaLhWABAAAA1EI+B4udO3dWRR0AAAAAgphf81UZY+TjbLUAAAAAzkPnFCzmzJmjbt26yWazyWazqXv37nr77bcruzYAAAAAQcLnQ6FeeuklPf7447rrrrsUHx8vY4zS0tJ0++2369ChQ7r//vurok4AAAAANZjPweLv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iyt3/dDQUDVs2LDKakWJc+lZqffff18333yz/vvf/+riiy+uyjJxCl97lpOTozVr1mj9+vW66667JJV8YTXGKDQ0VAsXLtRFF11ULbXXZufyWWvSpImaNWum6Ohoz7JOnTrJGKO9e/fqggsuqNKaa7tz6dn06dMVHx+vBx54QJLUvXt3ORwODRkyRE8//TS/ftdAfA8JXjXtewh7LCpZeHi4+vTpo0WLFnktX7RokQYPHlzuNoMGDSqz/sKFC9W3b1+FhYVVWa0ocS49k0p+Ibjpppv07rvvcuxwNfO1Z1FRUdq8ebM2bNjg+bv99tt14YUXasOGDRowYEB1lV6rnctnLT4+Xvv379fx48c9y3788UdZrVY1b968SuvFufXM5XLJavX+ehESEiLp5K/gqFn4HhKcauT3kACdNH5ee++990xYWJiZOXOm2bp1q7nvvvuMw+Ewu3btMsYYM3XqVHPDDTd41v/555+N3W43999/v9m6dauZOXOmCQsLMx988EGgnkKt42vP3n33XRMaGmpeffVVk56e7vk7duxYoJ5CreNrz36NWaECw9e+5eTkmObNm5urrrrKbNmyxSxbtsxccMEF5pZbbgnUU6h1fO3ZrFmzTGhoqHnttdfMjh07TGpqqunbt6/p379/oJ5CrZOTk2PWr19v1q9fbySZl156yaxfv9788ssvxhi+h9REvvaspn4PIVhUkVdffdW0atXKhIeHm969e5tly5Z5xiZOnGiGDRvmtf7SpUtNr169THh4uGndurV5/fXXq7li+NKzYcOGGUll/iZOnFj9hddivn7OTkWwCBxf+/b999+biy++2NhsNtO8eXMzefJk43K5qrnq2s3Xnr3yyiumc+fOxmazmSZNmpjf/e53Zu/evdVcde21ZMmSM/43iu8hNY+vPaup30MsxrBfEgAAAIB/OMcCAAAAgN8IFgAAAAD8RrAAAAAA4DeCBQAAAAC/ESwAAAAA+I1gAQAAAMBvBAsAAAAAfiNYAAAAAPAbwQIAKkHr1q01Y8aMQJdRbWbOnKnRo0dXy2MNHz5c9913n1/3MXv2bNWrV69S6gkEi8Wijz/++Jy3/8c//qFLL7208goCUGN8/fXXGj9+vJo2bXrO/1b85z//Uc+ePWW329WqVSs9//zz51QLwQIAIKniX17z8/P1xBNP6PHHH6/Ux1+6dKks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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "cost = df['plan_cost'].to_numpy(dtype=float)\n", + "time_ms = df['exec_ms'].to_numpy(dtype=float)\n", + "\n", + "# Proportional fit through the origin: time = slope * cost.\n", + "slope = float(np.sum(cost * time_ms) / np.sum(cost * cost))\n", + "r = float(np.corrcoef(cost, time_ms)[0, 1])\n", + "\n", + "fig, ax = plt.subplots(figsize=(8, 6))\n", + "ax.scatter(cost, time_ms, s=14, alpha=0.5, edgecolor='none')\n", + "xs = np.array([0.0, cost.max()])\n", + "ax.plot(xs, slope * xs, color='crimson', lw=1.5,\n", + " label=f'time = {slope:.3g} ms x cost')\n", + "ax.set_xlabel('plan cost (optimizer units)')\n", + "ax.set_ylabel('execution time (ms)')\n", + "ax.set_title(f'Plan cost vs. real time (Pearson r = {r:.3f})')\n", + "ax.legend()\n", + "ax.grid(True, alpha=0.3)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "226372f9", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-01T23:13:23.676337Z", + "iopub.status.busy": "2026-06-01T23:13:23.676195Z", + "iopub.status.idle": "2026-06-01T23:13:24.113644Z", + "shell.execute_reply": "2026-06-01T23:13:24.112874Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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2f70pz+xaNEiXn311XpfLEpKSnjrrbeYOnUqDoeDbdu2ceaZZzJixAieffZZUlJS2L17N++//z41NTWG3jTU1NRw9tlnc/XVVzNr1ix8Ph+apjF+/Hg++eQTbrnlFoYPH8727du56667GDVqFGvWrCE2NhaAzZs3c8YZZwQmRz///DMPPPAAX3/9dZOaHEy49VbHrbfeyujRo1m2bBnbtm1jxowZXHLJJdjtdgYPHszy5cv59ttvufXWW0lMTOSRRx4JpL366qtZtmwZN9xwAw888ABFRUXMmTOH4cOH891335GdnR3WMwRj48aNDB8+nKysLB555BHS09N56aWXmDhxIvv37+eWW24BYO/evYwcOZL4+HiWLl1KVlYWy5cv57rrrmtW/tdeey1PPvkk1113HWeddRbbtm3jjjvu4KOPPuKbb74JtLHbbruN+fPnc9VVV3Heeeexc+dOJk+ejNfrpV+/fs2uhzq++uorCgsLGT16dFD7I488Qr9+/XjooYdISkqib9++IX35/X6UUk3mabVaG11y9euvv1JdXc3hhx/ewHb44YezYsUK3G43MTExQdPXLT86+M1K3efvv/8+ZN6aprFs2TL69OnDyJEjA+HV1dX8+uuvnHvuuUHLVF1dzZYtW+ppM2rUKGbPnk1JSUm9Nx+C0CFQgiBEBc8995wC1Jdffqm8Xq8qLy9Xb7/9tsrMzFSJiYlq3759SimlVq5cqQC1cuXKkL58Pp+qqKhQ8fHxatGiRQ3ymDJlSr34CxYsUIDau3dvo2XMzc1Vubm5qrq6OmScY489VmVlZany8vJ65Rk0aJDq1q2b0jRNKaXUoEGD1DnnnNNoflOnTlXhdHNHHXWUGj58eL2wJUuWKECtX79eKaXUa6+9pgC1bt063X4bY8KECQpQzz77bL3w5cuXK0D985//rBe+evVqBaglS5YE9adpmvJ6vWrVqlUKUN99913Adtddd+mqj8bqLScnR02YMCHwue7vady4cfXiTZs2TQHqhhtuqBd+zjnnqLS0tMDnL774QgHqr3/9a714O3fuVLGxseqWW25psrzBANRdd90V+HzxxRcrl8ulduzYUS/e2LFjVVxcnCopKVFKKXXzzTcri8Wifvzxx3rxTjvttCbbTR0H1/NPP/0UtN189dVXClC33nqrUkqpoqIi5XK51EUXXVQvXl0d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WrWKZ555xvQTTRYvXsy4ceM49thjuemmm+jRowc7duzggw8+4G9/+xsA8+fP59RTT2X06NHMmDEDp9PJkiVL+OGHH1i+fHngi/QxxxzDWWedxeGHH05qaio//fQTL774Iscdd1zgZKa6L6UPPPAAY8eOxWazcfjhh4dcXgQwZswYunXrxpQpU9i3b1+D5S6PP/44H374IWeeeSY9evTA7Xbz7LPPArVn5tfRp08f4PdTucLl4osv5m9/+xtnnHEGN954I0cffTQOh4Ndu3axcuVKxo8fz7nnnsvw4cNJTU3lmmuu4a677sLhcPC3v/2N7777zlC+YKzejHL88cdz1VVXMWnSJNasWcOJJ55IfHw8e/fu5dNPP+Wwww7j2muvBWqPth09ejR33XVX2Ldq33XXXbz99tuMHj2aO++8k7S0NP72t7/xzjvvsGDBApKTk4HaY4efffZZxo4dy5w5c8jOzubll18OvAUzcpFc//79ueqqq3j00UexWq2MHTs2cCpU9+7duemmm4DaJTXTp09n/vz5pKamcu6557Jr1y7uueceOnfubOoldnVH13755ZdBT2EKh7pJrhncc889DBs2jLPOOotZs2bhdru58847ycjI4C9/+Uu9uHa7nZEjRwb2qAAsWLCATp060aNHD/bv38+rr77Km2++yYsvvhh0KdRXX33Fjz/+GNiHEYwZM2bw4osvcuaZZzJnzhxcLhf3338/brc76N/hl19+SXp6ummTYkGIKNpw47ggCCZSd2LT6tWrG40X7FSoXbt2qfPPP1+lpqaqxMREdfrpp6sffvihwQlAofLQc9JUHV988YUaO3asSk5OVi6XS+Xm5qqbbrqpXpxPPvlEnXTSSSo+Pl7FxsaqY489Vv373/+uF2fWrFlq6NChKjU1VblcLtW7d2910003qYKCgkAcj8ejJk+erDIzM5XFYlGA2rp1a5NlvPXWWxWgunfvrvx+f4Pyn3vuuSonJ0e5XC6Vnp6uRo4cqf71r3/Vi5eTk6NycnKazGvChAkqPj4+qM3r9aqHHnpIDR48WMXExKiEhAQ1YMAAdfXVV6vNmzcH4n3++efquOOOU3FxcSozM1NNnjxZffPNNwpQzz33XCCe3lOhGqu3UKdC/eMf/6jnI9TfSl0Z8vPz64U/++yz6phjjglonpubq6644gq1Zs2aQJx///vf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8s033/DRRx/xn//8hzlz5jB37ly+/vprUlJSwqrHuvqo839wA62Lo2kad999N+edd17AVvcWIiEhoUE6h8PB3/72tyZvmn/88cf54x//2OSz1y1FaqqeOnfuzOrVq+vFKy4uxuv10qVLl0AdPvjgg9x///3s27ePzMxM/ve//wGQm5sbNA+5x8J8zMrXiB/RoyFtpYfe+Gbr0Zyw1iBa9WhO2zDyPGYhekSmHnKPhQFCrYur+29gXXpCXFh+lVLYfA6sv32xNiu+Uvr2WBxyyCH4fL7AUhylFEVFRWzatIlDDjkEi8XCgAEDWL16dT1/a9euDfy/3W6nb9++gXwP/iXaYrHQt29fnE4nn332WWAJjNfrZc2aNUybNi2Qz7vvvluvvAfm01T9HPy8dWlcLhfwewMJp84cDgennnoqp556KnfffTcpKSmsXLmy3hf/YPUIUFhYGKjHung+n4+1a9cGli5t3LiRkpISBgwYAMBRRx3Fpk2bGtSn/SC9Dyzr+PHjOfbYY4PGrQvr2rWrrr+vA+utMYYPH868efPYt28fnTt3BmDFihW4XC6GDh1arw7tdjvdunUD4O9//zvHHXdcYElaqLzr2puRNaV60pi9hjxU+MFh7XmNbEv5ET0aEq1ryEPZomENeUv4aWk9mtM2jDyPWYge4evh25uPvXNmk89oBD31Eo5WMrGIcvr27cv48eP585//zBNPPEFCQgKzZs2ia9eujB8/HoDrr7+eE088kYcffpjx48ezcuVK3nvvPd1fVAHi4+O59tprufnmm0lLS6NHjx4sWLCAqqoqrrzySgCuvvpqFi5cyMyZM7nyyitZt24dy5YtA0JPjvSQlZVFbGws77//Pt26dSMmJobk5OQm07399tts2bKFE088kdTUVN599100TaN///4N4h5cj4mJiQ3qEWonKtdffz2PPPIIDoeD6667jmOPPZZhw4YBcOedd3LWWWfRvXt3LrzwQiwWC99++y0bNmzgvvvuC1rOxMREEhMTG51YNPUGYt26dQBUVFSQn5/PunXrcDqdgUnRG2+8wezZs/n5558BGDNmDIcccgiXX345Dz74IEVFRcyYMYM///nPJCUloZSioKCAN998k9GjR+N2u3nuuef4xz/+wapVq5qse0EQBEEQGkdpGqVPvErRfU+R/dTdxI8d0dZFahKZWPxG3Tr+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WLZsGfn5+X3dtJTmlltuYePGjQwePJgHH3ywr5uT0hQUFFBTU9PXzRD2csQRR3D33Xdz2GGHUVdXR1ZWFmazua+bldI0NzczZMgQtm/fTnp6el83JyWprq5mxowZfPvtt1gsFg4//HAefPBBDjnkkL5uWkry9ddfc95557F06VIAjjnmGP7zn/+Qk5PTtw3rBHljIcQFk8mEw+EAwOv1EgqF5K8cfczGjRtZt24dJ5xwQl83RRD6Fft+aTrssMMAyMvLk0FFP+Bf//oXxxxzjAwq+phgMIjX6yUQCBAIBCgqKurrJqUsa9euZebMmdjtdux2O5MmTeKdd97p62Z1igwsBAAWL17MSSedRGlpKYqihH0V/eSTTzJ06FDsdjtTp05lyZIl7eINDQ1MnDiRQYMGccMNN1BQUNBLrU8+4uHHddddx7x583qpxclNPPxwuVxMnTqVQw89lI8++qiXWp6cdNePjRs3kpGRwcknn8yUKVO49957e7H1yUU87o19vPLKK5xxxhk93OLkprt+FBYWct111zF48GBKS0v5wQ9+wAEHHNCL3yC56K4f48aN48MPP6ShoYGGhgY++OADKioqevEbxI4MLAQAWlpamDhxIk888UTY+Msvv8w111zDLbfcwsqVKznssMOYPXs2O3bsaMuTk5PDV199xdatW/nb3/5GVVVVbzU/6eiuH2+++SYjR45k5MiRvdnspCUe98e2bdtYsWIFf/jDHzj33HNxuVy91fyko7t+BAIBlixZwoIFC/j0009ZuHAhCxcu7M2vkDTE496A1oH3J598Im9Yu0l3/aivr+ett95i27ZtVFRUsHTpUhYvXtybXyGp6K4f+9a5HH300Zx66qlMnz69/79d1QXhewD666+/3i7toIMO0i+77LJ2aQceeKB+4403htW47LLL9FdeeaWnmphSGPHjxhtv1AcNGqSXl5fr+fn5elZWln7nnXf2VpOTmnjcHz/84Q/1ZcuW9VQTUwojfixdulQ//vjj22L333+/fv/99/d4W5Od7twbL7zwgn7WWWf1dBNTCiN+vPLKK/rll1/eFrv//vv1++67r8fbmgrEo++46KKL9LfeequnmhgX5I2F0CV+v58VK1Zw3HHHtUs/7rjj2hYUVVVVtf0F1uVysXjxYkaNGtXrbU0FovFj3rx57Ny5k23btvHggw9y8cUXc/vtt/dFc5OeaPyor6/H5/MBsGvXLtasWcOwYcN6va2pQDR+TJ8+naqqKurr69E0jcWLFzN69Oi+aG5SE40X+5BpUD1PNH6UlZWxdOnStrWSixYtkr68h4j2/nA6nUDrDmpffPEFxx9/fK+2M1b6+fsUoT9QU1NDKBSiuLi4XXpxcTF79uwBWn9Zuuiii9B1HV3XueKKK5gwYUJfNDfpicYPofeIxo+1a9dy6aWXoqoqiqLw6KOPkpeX1xfNTXqi8cNsNnPvvfdy+OGHo+s6xx13HD/60Y/6orlJTbTPqsbGRr744gteffXV3m5iShGNHzNmzOCEE05g8uTJqKrKMcccw8knn9wXzU16or0/fvzjH9PQ0EB6ejrPPfdcv58K1b9bJ/QrFEVp91nX9ba0qVOnsmrVqj5oVerSmR/7c/755/dSi1KbzvyYOXMmX3/9dV80K2Xp6v6YPXs2s2fP7u1mpSRdeZGdnS1r8nqRrvy45557uOeee3q7WSlLV358/+1ef0emQgldUlBQgMlk6vDXcKfT2WGkLfQ84kf/QvzoX4gf/Qfxon8hfvQvktUPGVgIXWK1Wpk6dWqHXVMWLlzIzJkz+6hVqYv40b8QP/oX4kf/QbzoX4gf/Ytk9UOmQglA64mnmzZtavu8detWVq1aRV5eHoMHD2bu3Lmcc845TJs2jUMOOYRnnnmGHTt2cNlll/Vhq5MX8aN/IX70L8SP/oN40b8QP/oXKelHX21HJfQvPvzwQx3o8O+8885ry7NgwQK9vLxct1qt+pQpU/SPPvqo7xqc5Igf/Qvxo38hfvQfxIv+hfjRv0hFPxRd1/UeHrsIgiAIgiAIgpDkyBoLQRAEQRAEQRC6jQwsBEEQBEEQBEHoNjKwEARBEARBEASh28jAQhAEQRAEQRCEbiMDC0EQBEEQBEEQuo0MLARBEARBEARB6DYysBAEQRAEQRAEodvIwEIQBEEQBEEQhG4jAwtBEARBEARBELqNDCwEQRDiyJAhQ3jkkUf6uhm9xp/+9CeOO+64XqnryCOP5JprrumWxvPPP09OTk5c2tMXKIrCG2+8Ybj8E088wcknnxy/BgmCIOyHDCwEQRCEdkT7y6vP5+P222/ntttui2v9ixYtQlEUGhoa2qW/9tpr/O53v+uW9hlnnMGGDRu6pdGXVFZWMnv2bAC2bduGoiisWrUq6vIXX3wxy5Yt4+OPP+6hFgqCkMrIwEIQBEEwxKuvvkpGRgaHHXZYr9SXl5dHZmZmtzTS0tIoKiqKU4s64vf7e0wbYMCAAdhsNsPlbTYbv/jFL3j88cfj2CpBEIRWZGAhCIIQJUceeSRXXHEFV1xxBTk5OeTn53Prrbei63rEMg8//DDjx48nPT2dsrIyLr/8cpqbm9vi+6bmvPvuu4wePZqMjAx++MMfUllZ2Wlbvv32W0488USysrLIzMzksMMOY/PmzQBomsZdd93FoEGDsNlsTJo0iXfeeaetrN/v54orrqCkpAS73c6QIUOYN28e0DqVC+DUU09FUZS2z+F46aWXOkyr6arufX9lf+mll5g5cyZ2u52xY8eyaNGitvhRRx0FQG5uLoqicP7557dd//2nQg0ZMoS7776bc889l4yMDMrLy3nzzTeprq7mlFNOISMjg/Hjx7N8+fIO13t/DUVROvzbR0VFBWeccQa5ubnk5+dzyimnsG3btrb4+eefz49//GPmzZtHaWkpI0eODHut9uXbn2uuuYYjjzyy7fORRx7JVVddxQ033EBeXh4DBgzgjjvuaFdm/7dJQ4cOBWDy5MkoitKmtWjRIg466CDS09PJyclh1qxZbN++vU3j5JNP5o033sDj8YRtqyAIglFkYCEIghADf/7znzGbzXz++ec89thjzJ8/nz/+8Y8R86uqymOPPcY333zDn//8Zz744ANuuOGGdnncbjcPPvggL774IosXL2bHjh1cd911ETUrKio4/PDDsdvtfPDBB6xYsYILL7yQYDAIwKOPPspDDz3Egw8+yOrVqzn++OM5+eST2bhxIwCPPfYY//rXv3jllVdYv349f/nLX9oGEMuWLQPgueeeo7Kysu1zOJYsWcK0adPapXVV9z6uv/56rr32WlauXMnMmTM5+eSTqa2tpaysjFdffRWA9evXU1lZyaOPPhqxDfPnz2fWrFmsXLmSE088kXPOOYdzzz2Xs88+my+//JLhw4dz7rnnRhz8LVu2jMrKSiorK9m1axczZsxoewPjdrs56qijyMjIYPHixXz88cdtA7/930y8//77rF27loULF/LWW29FbGs0/PnPfyY9PZ3PP/+c+++/n7vuuouFCxeGzfvFF18A8N5771FZWclrr71GMBjkxz/+MUcccQSrV6/m008/5ZJLLmk3WJo2bRqBQKCtvCAIQtzQBUEQhKg44ogj9NGjR+uaprWl/eY3v9FHjx7d9rm8vFyfP39+RI1XXnlFz8/Pb/v83HPP6YC+adOmtrQFCxboxcXFETVuuukmfejQobrf7w8bLy0t1e+55552adOnT9cvv/xyXdd1/corr9SPPvrodt9jfwD99ddfj1i/rut6fX29DuiLFy+Oqe6tW7fqgP773/++LR4IBPRBgwbp9913n67ruv7hhx/qgF5fX99O54gjjtCvvvrqts/l5eX62Wef3fa5srJSB/TbbrutLe3TTz/VAb2yslLX9dbrnZ2dHfY7XXXVVXp5ebnudDp1Xdf1P/3pT/qoUaPaXSefz6enpaXp7777rq7run7eeefpxcXFus/ni3it9uU75ZRT2qVdffXV+hFHHNHu+x166KHt8kyfPl3/zW9+0/Z5f2/2XcuVK1e2xWtra3VAX7RoUaftyc3N1Z9//vlO8wiCIMSKvLEQBEGIgRkzZrT76+8hhxzCxo0bCYVCYfN/+OGHHHvssQwcOJDMzEzOPfdcamtraWlpacvjcDg44IAD2j6XlJTgdDojtmHVqlUcdthhWCyWDjGXy8Xu3buZNWtWu/RZs2axdu1aoHVazqpVqxg1ahRXXXUV//vf/6L78vuxbxqN3W6Pqe59HHLIIW3/bzabmTZtWoc80TBhwoS2/y8uLgZg/PjxHdI6u54AzzzzDH/605948803KSwsBGDFihVs2rSJzMxMMjIyyMjIIC8vD6/X2zbtbF99Vqs15rZ39X2g65+F75OXl8f555/P8ccfz0knncSjjz4adlpdWloabre72+0VBEHYHxlYCIIg9BDbt2/nhBNOYNy4cbz66qusWLGCBQsWABAIBNryfX+AoChKp+s20tLSuqx7/8EPgK7rbWlTpkxh69at/O53v8Pj8fCzn/2M008/PervBZCfn4+iKNTX18dUdyxtjob9r92+8uHSNE2LqLFo0SKuvPJKXnjhBSZOnNiWrmkaU6dOZdWqVe3+bdiwgV/84hdt+dLT07tsp6qqHTzd/2cg3PfZ1/7O2h6O5557jk8//ZSZM2fy8ssvM3LkSD777LN2eerq6toGUIIgCPFCBhaCIAgx8P1f0D777DNGjBiByWTqkHf58uUEg0EeeughZsyYwciRI9m9e3e32zBhwgSWLFkS9hfTrKwsSktLO2wnunTpUkaPHt0u3xlnnMGzzz7Lyy+/zKuvvkpdXR3Q+sttpDcw+7BarYwZM4Y1a9bEXDe0v47BYJAVK1Zw4IEHtmkDXbYhHmzatInTTjuNm2++mZ/85CftYlOmTGHjxo0UFRUxfPjwdv+ys7NjqqewsLDDm4NYtokNR2fXafLkydx0000sXbqUcePG8be//a0ttnnzZrxeL5MnT+5W/YIgCN9HBhaCIAgxsHPnTubOncv69ev5+9//zuOPP87VV18dNu8BBxxAMBjk8ccfZ8uWLbz44ov84Q9/6HYbrrjiClwuFz//+c9Zvnw5Gzdu5MUXX2T9+vVA68Lo++67j5dffpn169dz4403smrVqrZ2zp8/n5deeol169axYcMG/vGPfzBgwIC23ZKGDBnC+++/z549e8K+kdjH8ccf32EQ0VXd+1iwYAGvv/4669atY86cOdTX13PhhRcCUF5ejqIovPXWW1RXV7fbRSueeDweTjrpJCZNmsQll1zCnj172v4BnHXWWRQUFHDKKaewZMkStm7dykcffcTVV1/Nrl27Yqrr6KOPZvny5bzwwgts3LiR3/72t3zzzTfdan9RURFpaWm88847VFVV0djYyNatW7npppv49NNP2b59O//73//YsGFDu4HdkiVLGDZsWLvpd4IgCPFABhaCIAgxcO655+LxeDjooIOYM2cOV155JZdccknYvJMmTeLhhx/mvvvuY9y4cfz1r39t29a1O+Tn5/PBBx/Q3NzMEUccwdSpU3n22WfbptFcddVVXHvttVx77bWMHz+ed955h3/961+MGDECgIyMDO677z6mTZvG9OnT2bZtG2+//Taq2tolPPTQQyxcuJCysrJO/6p98cUX8/bbb9PY2NiW1lXd+/j973/Pfffdx8SJE1myZAlvvvkmBQUFAAwcOJA777yTG2+8keLiYq644opuX7NwVFVVsW7dOj744ANKS0spKSlp+weta18WL17M4MGD+clPfsLo0aO58MIL8Xg8ZGVlxVTX8ccfz2233cYNN9zA9OnTaWpq4txzz+1W+81mM4899hhPP/00paWlnHLKKTgcDtatW8dpp53GyJEjueSSS7jiiiu49NJL28r9/e9/5+KLL+5W3YIgCOFQ9M4m8gqCIAhtHHnkkUyaNIlHHnmkr5vSb/jZz37WNu0mGrZt28bQoUNZuXIlkyZN6tnGCR345ptvOOaYY9iwYUPM07kEQRC6Qt5YCIIgCIZ54IEHyMjI6OtmCFGye/duXnjhBRlUCILQI5j7ugGCIAhC4lJeXs6VV17Z180QouS4447r6yYIgpDEyFQoQRAEQRAEQRC6jUyFEgRBEARBEASh28jAQhAEQRAEQRCEbiMDC0EQBEEQBEEQuo0MLARBEARBEARB6DYysBAEQRAEQRAEodvIwEIQBEEQBEEQhG4jAwtBEARBEARBELqNDCwEQRAEQRAEQeg2MrAQBEEQBEEQBKHb/D8iTJwO6z+hEQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mask = (cost > 0) & (time_ms > 0)\n", + "lc = np.log10(cost[mask])\n", + "lt = np.log10(time_ms[mask])\n", + "\n", + "a, b = np.polyfit(lc, lt, 1)\n", + "r_log = float(np.corrcoef(lc, lt)[0, 1])\n", + "\n", + "fig, ax = plt.subplots(figsize=(8, 6))\n", + "ax.scatter(cost[mask], time_ms[mask], s=14, alpha=0.5, edgecolor='none')\n", + "xs = np.array([cost[mask].min(), cost[mask].max()])\n", + "ax.plot(xs, 10**b * xs**a, color='crimson', lw=1.5,\n", + " label=f'log-log fit, slope = {a:.2f}')\n", + "ax.set_xscale('log')\n", + "ax.set_yscale('log')\n", + "ax.set_xlabel('plan cost (optimizer units)')\n", + "ax.set_ylabel('execution time (ms)')\n", + "ax.set_title(f'Plan cost vs. real time, log-log (r = {r_log:.3f})')\n", + "ax.legend()\n", + "ax.grid(True, which='both', alpha=0.3)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "5dd0a12c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-01T23:13:24.115407Z", + "iopub.status.busy": "2026-06-01T23:13:24.115256Z", + "iopub.status.idle": "2026-06-01T23:13:24.118679Z", + "shell.execute_reply": "2026-06-01T23:13:24.118166Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "samples : 400\n", + "cost range : 366 .. 1196362017\n", + "time range (ms) : 0.366 .. 2984422.2\n", + "Pearson r (linear) : 0.290\n", + "log-log slope : 1.091 (1.0 == perfectly proportional)\n", + "log-log correlation : 0.970\n" + ] + } + ], + "source": [ + "print(f'samples : {len(df)}')\n", + "print(f'cost range : {int(cost.min())} .. {int(cost.max())}')\n", + "print(f'time range (ms) : {time_ms.min():.3f} .. {time_ms.max():.1f}')\n", + "print(f'Pearson r (linear) : {r:.3f}')\n", + "print(f'log-log slope : {a:.3f} (1.0 == perfectly proportional)')\n", + "print(f'log-log correlation : {r_log:.3f}')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/research/cost-on-random.pdf b/research/cost-on-random.pdf new file mode 100644 index 0000000..ef134a0 Binary files /dev/null and b/research/cost-on-random.pdf differ diff --git a/research/cost-stats.csv b/research/cost-stats.csv new file mode 100644 index 0000000..8c74bae --- /dev/null +++ b/research/cost-stats.csv @@ -0,0 +1,1305 @@ +seed,plan_cost,exec_us,rows,query +0,2196,3466,198,"SELECT t1.price AS c2289 +FROM regions AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id;" +1,56100,56100,250,"SELECT t0.region_id AS c3748 +FROM customers AS t0 +WHERE t0.region_id = 7;" +2,110500,110500,296,"SELECT t0.region_id, t0.id +FROM customers AS t0 +ORDER BY t0.id DESC, t0.region_id DESC;" +3,610,610,122,"SELECT t0.id +FROM departments AS t0;" +4,574,574,179,"SELECT t0.id, SUM(t0.id) +FROM markets AS t0 +WHERE t0.id IS NULL +GROUP BY t0.id +ORDER BY t0.id DESC;" +5,35285800,35285800,54434,"SELECT markets.region, markets.id AS c7685 +FROM markets CROSS JOIN orders CROSS JOIN departments +GROUP BY markets.region, markets.id;" +6,112653,440694,312,"SELECT t1.id, t0.id AS c6716, t0.region_id +FROM customers AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN departments AS t2 ON t0.id = t2.id +WHERE t1.department_id <= 11;" +7,2188,5724,193,"SELECT departments.id +FROM departments RIGHT JOIN employees ON departments.id = employees.id +WHERE (employees.id != 33 AND employees.department_id IN (34, 74)) +GROUP BY departments.id +ORDER BY departments.id DESC;" +8,552500,552500,1136,"SELECT orders.customer_id, orders.id +FROM orders +WHERE (orders.id = 117 AND orders.id > 60) +ORDER BY orders.customer_id DESC, orders.id DESC;" +9,3939128,3939128,7819,"SELECT t0.customer_id AS c3674 +FROM orders AS t0 CROSS JOIN departments AS t1 +WHERE (t1.id >= 4 AND (t0.id IN (130) OR t1.id IS NULL));" +10,4143,6932,235,"SELECT regions.id +FROM books RIGHT JOIN employees ON books.id = employees.id JOIN regions ON books.id = regions.id +WHERE books.price >= 6;" +11,5889,16616,258,"SELECT employees.department_id +FROM users FULL JOIN employees ON users.id = employees.id +ORDER BY employees.department_id DESC;" +12,676416,1550366,903,"SELECT orders.id AS c2380, markets.region, markets.note +FROM markets JOIN orders ON markets.id = orders.id;" +13,2390300,2390300,2752,"SELECT markets.region, orders.customer_id AS c8705, markets.note +FROM markets CROSS JOIN orders;" +14,2076,2076,127,"SELECT t0.department_id, t0.id AS c1827 +FROM employees AS t0 +WHERE (t0.department_id BETWEEN 5 AND 34 OR t0.id NOT IN (2, 68, 69));" +15,422,422,129,"SELECT books.price +FROM books +WHERE (books.id < 2 AND (books.price NOT BETWEEN 41 AND 55 OR books.price IN (57, 63, 77, 77)));" +16,5405,16132,176,"SELECT t0.age AS c4948 +FROM users AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id;" +17,19505,29510,302,"SELECT users.age, COUNT(departments.id), COUNT(*) +FROM regions JOIN users ON regions.id = users.id CROSS JOIN departments +WHERE (users.age >= 52 OR regions.id IN (4, 9)) +GROUP BY users.age;" +18,102500,102500,311,"SELECT customers.region_id +FROM customers +WHERE customers.region_id != 8 +ORDER BY customers.region_id ASC;" +19,2736,4463,221,"SELECT t0.id +FROM books AS t0 RIGHT JOIN regions AS t1 ON t0.id = t1.id JOIN markets AS t2 ON t1.id = t2.id +WHERE t1.id < 3 +ORDER BY t0.id ASC;" +20,49896040,49896040,28905,"SELECT orders.id AS c5497, orders.customer_id, departments.id AS c8484 +FROM departments CROSS JOIN users CROSS JOIN orders +WHERE orders.id = 82;" +21,1220,1220,268,"SELECT regions.id +FROM regions;" +22,3740,3740,171,"SELECT t0.department_id +FROM employees AS t0 +GROUP BY t0.department_id +ORDER BY t0.department_id ASC;" +23,109562,155822,514,"SELECT customers.region_id +FROM customers LEFT JOIN books ON customers.id = books.id +GROUP BY customers.region_id +ORDER BY customers.region_id ASC;" +24,39522,56902,387,"SELECT t2.id, COUNT(t0.id) AS c2763, COUNT(t2.id) +FROM regions AS t0 CROSS JOIN users AS t1 LEFT JOIN markets AS t2 ON t0.id = t2.id +GROUP BY t2.id;" +25,68652,155432,247,"SELECT customers.id +FROM books JOIN customers ON books.id = customers.id +ORDER BY customers.id DESC;" +26,1417306,4786826,2866,"SELECT customers.region_id, employees.department_id +FROM employees FULL JOIN orders ON employees.id = orders.id FULL JOIN customers ON employees.id = customers.id +ORDER BY customers.region_id ASC;" +27,1323180,6466259,8053,"SELECT orders.customer_id, users.id +FROM users RIGHT JOIN orders ON users.id = orders.id CROSS JOIN departments +WHERE orders.customer_id IN (34, 321) +GROUP BY orders.customer_id, users.id;" +28,1425,1425,325,"SELECT departments.id AS c2364, SUM(departments.id) +FROM departments +GROUP BY departments.id +ORDER BY departments.id ASC;" +29,110161,226916,341,"SELECT customers.id, markets.id +FROM customers FULL JOIN departments ON customers.id = departments.id JOIN markets ON customers.id = markets.id;" +30,32788,32788,384,"SELECT markets.id AS c2619 +FROM markets CROSS JOIN users CROSS JOIN books +WHERE markets.region != 'AMERICA';" +31,3125,3125,151,"SELECT t0.id, SUM(t0.id) +FROM users AS t0 +WHERE ((t0.id NOT IN (5) AND t0.age != 12) AND t0.age NOT BETWEEN 33 AND 64) +GROUP BY t0.id;" +32,3740,3740,183,"SELECT t0.id AS c8338, COUNT(t0.department_id) +FROM employees AS t0 +GROUP BY t0.id +ORDER BY t0.id DESC;" +33,1760905,2705500,221424,"SELECT orders.customer_id, customers.region_id +FROM departments RIGHT JOIN orders ON departments.id = orders.id CROSS JOIN customers +WHERE departments.id BETWEEN 4 AND 96 +ORDER BY customers.region_id DESC;" +34,27634772,27634772,65216,"SELECT markets.note AS c1833, markets.region, SUM(books.id) AS c1917, COUNT(*) +FROM orders CROSS JOIN markets CROSS JOIN books +WHERE ((orders.id != 89 OR markets.note IS NOT NULL) OR (markets.id IN (22) AND books.id NOT BETWEEN 1 AND 3)) +GROUP BY markets.note, markets.region +ORDER BY markets.region DESC;" +35,7686500,1755811500,17292,"SELECT regions.id, orders.id +FROM orders CROSS JOIN regions JOIN customers ON regions.id = customers.id +ORDER BY orders.id DESC, regions.id DESC;" +36,8426,8426,283,"SELECT books.price AS c7084 +FROM books CROSS JOIN users;" +37,1916300,525797800,2823,"SELECT orders.customer_id +FROM customers CROSS JOIN markets FULL JOIN orders ON markets.id = orders.id +WHERE orders.id < 95;" +38,1310710,4010688,2504,"SELECT t1.department_id, COUNT(*) +FROM regions AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN orders AS t2 ON t0.id = t2.id +WHERE ((t1.id = 3 OR t2.id = 185) OR (t1.id < 3 AND t2.id IS NULL)) +GROUP BY t1.department_id +ORDER BY t1.department_id DESC;" +39,433,433,141,"SELECT t0.id, t0.note, t0.region +FROM markets AS t0 +WHERE t0.note IS NULL +ORDER BY t0.note DESC, t0.id DESC;" +40,615800,615800,614,"SELECT t1.id, t1.region_id AS c2127 +FROM books AS t0 CROSS JOIN customers AS t1 +GROUP BY t1.id, t1.region_id;" +41,1196362017,1196362017,2615226,"SELECT customers.region_id, orders.id AS c1616 +FROM orders CROSS JOIN customers +WHERE ((orders.id >= 3 OR customers.region_id != 41) OR orders.id IN (73)) +GROUP BY customers.region_id, orders.id;" +42,745022,1705422,1024,"SELECT t1.price, t0.region_id, t2.customer_id AS c2615 +FROM customers AS t0 JOIN books AS t1 ON t0.id = t1.id JOIN orders AS t2 ON t0.id = t2.id +WHERE t2.id IS NOT NULL;" +43,422,422,147,"SELECT markets.region, markets.id AS c9574 +FROM markets +WHERE ((markets.note IS NOT NULL OR markets.region != 'AMERICA') AND (markets.region IN ('EUROPE', 'EUROPE') AND markets.note IN ('North, South', 'North, South')));" +44,72448,648028,274,"SELECT t1.id AS c8372, t1.age AS c1550 +FROM customers AS t0 JOIN users AS t1 ON t0.id = t1.id +WHERE t1.id > 11 +ORDER BY t1.id ASC, t1.age DESC;" +45,4560,11225,179,"SELECT t1.department_id AS c7018, t0.id AS c6686 +FROM regions AS t0 JOIN employees AS t1 ON t0.id = t1.id +WHERE t0.id >= 10 +GROUP BY t1.department_id, t0.id;" +46,1122,1122,152,"SELECT t0.id +FROM regions AS t0 +WHERE ((t0.id IS NOT NULL AND t0.id != 5) AND (t0.id > 8 AND t0.id > 9));" +47,110844,436233,363,"SELECT employees.id +FROM customers FULL JOIN employees ON customers.id = employees.id +WHERE employees.department_id = 32 +ORDER BY employees.id ASC;" +48,1443652,175655822,2977,"SELECT customers.region_id AS c1201, SUM(customers.region_id) +FROM orders FULL JOIN customers ON orders.id = customers.id FULL JOIN markets ON orders.id = markets.id +GROUP BY customers.region_id;" +49,560554,560554,1733,"SELECT orders.customer_id, orders.id +FROM orders +WHERE ((orders.id = 130 OR orders.id <= 96) AND orders.id = 97) +ORDER BY orders.customer_id DESC, orders.id DESC;" +50,1417,1972,202,"SELECT markets.id +FROM markets RIGHT JOIN departments ON markets.id = departments.id +WHERE markets.id < 45;" +51,610,610,116,"SELECT t0.id +FROM departments AS t0;" +52,4742675,5714300,3855,"SELECT orders.id, COUNT(*) +FROM books CROSS JOIN orders +WHERE books.price NOT IN (45, 55, 55, 66) +GROUP BY orders.id;" +53,30725,62340,430,"SELECT users.id, COUNT(employees.id) AS c5902 +FROM employees CROSS JOIN users JOIN markets ON users.id = markets.id +GROUP BY users.id +ORDER BY users.id ASC;" +54,5100,5100,191,"SELECT users.age, users.id AS c207, SUM(users.id) +FROM users +WHERE (users.age > 27 OR (users.age <= 123 AND users.age > 1)) +GROUP BY users.age, users.id +ORDER BY users.id ASC;" +55,1342,1342,111,"SELECT t0.department_id AS c7732 +FROM employees AS t0;" +56,53882332,53882332,55305,"SELECT books.id, SUM(orders.customer_id) AS c2549, COUNT(*) AS c1539 +FROM books CROSS JOIN employees CROSS JOIN orders +WHERE employees.id < 68 +GROUP BY books.id;" +57,881250,881250,1813,"SELECT orders.id AS c9114 +FROM orders +WHERE (orders.customer_id > 214 OR orders.customer_id BETWEEN 75 AND 84);" +58,4073600,1927083100,3996,"SELECT employees.department_id, customers.region_id, COUNT(*), SUM(orders.customer_id) +FROM employees CROSS JOIN customers FULL JOIN orders ON customers.id = orders.id +WHERE customers.id <= 149 +GROUP BY employees.department_id, customers.region_id;" +59,175500,175500,549,"SELECT customers.id, customers.region_id, COUNT(customers.region_id) +FROM customers +GROUP BY customers.id, customers.region_id;" +60,3577,5702,189,"SELECT t0.id +FROM markets AS t0 FULL JOIN users AS t1 ON t0.id = t1.id +ORDER BY t0.id ASC;" +61,2493,2493,269,"SELECT t1.region, t1.id, t1.note +FROM departments AS t0 CROSS JOIN markets AS t1 +WHERE ((t0.id < 43 OR t0.id >= 2) AND t1.note = 'Old World') +ORDER BY t1.note DESC, t1.id ASC, t1.region ASC;" +62,69881,156916,259,"SELECT departments.id AS c7718 +FROM departments LEFT JOIN books ON departments.id = books.id JOIN customers ON departments.id = customers.id;" +63,4771,9075,210,"SELECT users.age AS c5029, COUNT(*) AS c2096, SUM(users.age) +FROM users FULL JOIN departments ON users.id = departments.id +GROUP BY users.age +ORDER BY users.age ASC;" +64,1494000,175610500,1644,"SELECT customers.region_id AS c6827, orders.id, customers.id +FROM customers LEFT JOIN orders ON customers.id = orders.id +ORDER BY customers.region_id ASC, customers.id ASC, orders.id DESC;" +65,1558,2113,229,"SELECT markets.id AS c8656, markets.region +FROM markets FULL JOIN departments ON markets.id = departments.id +WHERE departments.id = 60 +GROUP BY markets.id, markets.region;" +66,563,563,157,"SELECT markets.note AS c5953, SUM(markets.id), SUM(markets.id) +FROM markets +WHERE markets.region = 'EUROPE' +GROUP BY markets.note;" +67,61000,61000,400,"SELECT customers.region_id, customers.id AS c651 +FROM customers;" +68,1311707,6465260,3026,"SELECT users.id, regions.id AS c5610, orders.id AS c9918 +FROM users FULL JOIN orders ON users.id = orders.id FULL JOIN regions ON orders.id = regions.id +ORDER BY users.id DESC;" +69,546522,548611,2460,"SELECT t0.id AS c499 +FROM books AS t0 FULL JOIN users AS t1 ON t0.id = t1.id CROSS JOIN customers AS t2 +WHERE ((t0.price > 66 OR t0.price NOT IN (55, 77, 93)) OR t2.id < 93) +ORDER BY t0.id ASC;" +70,574,574,354,"SELECT markets.region, markets.note +FROM markets +WHERE markets.id IN (2, 31) +GROUP BY markets.region, markets.note +ORDER BY markets.note ASC, markets.region ASC;" +71,5300,5300,272,"SELECT departments.id AS c3175, SUM(departments.id), COUNT(books.price) +FROM books CROSS JOIN departments +GROUP BY departments.id;" +72,788,788,148,"SELECT t0.id +FROM departments AS t0 +WHERE t0.id >= 2 +ORDER BY t0.id ASC;" +73,67647,155422,478,"SELECT customers.id, customers.region_id +FROM customers JOIN markets ON customers.id = markets.id +WHERE ((markets.id = 3 AND markets.region != 'AMERICA') AND (customers.id < 17 AND customers.region_id IS NULL));" +74,109996,226972,328,"SELECT books.id AS c9785 +FROM customers RIGHT JOIN departments ON customers.id = departments.id RIGHT JOIN books ON departments.id = books.id +WHERE books.price >= 55;" +75,15960,15960,272,"SELECT t1.department_id AS c5193, t1.id +FROM regions AS t0 CROSS JOIN employees AS t1;" +76,6261,16988,270,"SELECT t0.id AS c3431 +FROM users AS t0 LEFT JOIN employees AS t1 ON t0.id = t1.id +WHERE ((t1.id != 67 AND t0.id IS NOT NULL) OR (t0.age NOT BETWEEN 33 AND 123 OR t0.age >= 22));" +77,315425000,315425000,206324,"SELECT t1.region_id AS c1177, t0.id AS c3278, COUNT(*), COUNT(t0.id) AS c3474 +FROM orders AS t0 CROSS JOIN customers AS t1 +WHERE (t0.id = 64 AND t0.customer_id > 397) +GROUP BY t1.region_id, t0.id;" +78,61000,61000,748,"SELECT t0.id, t0.region_id +FROM customers AS t0;" +79,2310,2310,125,"SELECT t0.id AS c7963, t0.age AS c5437 +FROM users AS t0 +WHERE t0.age IN (5, 11, 63);" +80,6250,6250,246,"SELECT regions.id AS c2137 +FROM regions CROSS JOIN markets +WHERE markets.note != 'AMERICA';" +81,4658,9622,227,"SELECT markets.region AS c3832, users.age, departments.id +FROM users LEFT JOIN departments ON users.id = departments.id RIGHT JOIN markets ON users.id = markets.id +WHERE departments.id <= 5;" +82,1833,1833,138,"SELECT users.age AS c4898, users.id AS c4014 +FROM users +WHERE ((users.id = 4 OR users.id BETWEEN 11 AND 16) AND (users.age = 22 AND users.id <= 57)) +ORDER BY users.age DESC;" +83,3478,5692,198,"SELECT t0.age AS c499, t1.price AS c7644, t0.id +FROM users AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id +WHERE t0.id < 7;" +84,3124,4574,221,"SELECT books.price, regions.id, markets.id AS c8988 +FROM markets LEFT JOIN regions ON markets.id = regions.id LEFT JOIN books ON regions.id = books.id +WHERE ((books.price BETWEEN 6 AND 77 OR markets.id NOT IN (1, 2, 2, 3)) OR books.price NOT IN (55));" +85,175500,175500,342,"SELECT t0.id, t0.region_id +FROM customers AS t0 +GROUP BY t0.id, t0.region_id;" +86,610000,610000,1188,"SELECT orders.customer_id, orders.id +FROM orders;" +87,3256,3256,169,"SELECT employees.department_id, employees.id AS c6314 +FROM employees +GROUP BY employees.department_id, employees.id;" +88,3612,6540,228,"SELECT departments.id AS c2839, COUNT(*) +FROM employees RIGHT JOIN departments ON employees.id = departments.id +GROUP BY departments.id +ORDER BY departments.id DESC;" +89,822,822,155,"SELECT t0.region AS c5477, t0.id AS c2794, COUNT(*) AS c9570 +FROM markets AS t0 +GROUP BY t0.region, t0.id;" +90,135000,135000,422,"SELECT customers.region_id, customers.id AS c6247, COUNT(*) AS c8943 +FROM customers +WHERE customers.region_id <= 40 +GROUP BY customers.region_id, customers.id;" +91,610,610,115,"SELECT departments.id +FROM departments;" +92,12280,12280,260,"SELECT markets.region, markets.id +FROM markets CROSS JOIN regions +GROUP BY markets.region, markets.id +ORDER BY markets.region ASC;" +93,676086,1550366,1339,"SELECT t0.id, t1.id AS c1156, t0.customer_id +FROM orders AS t0 JOIN books AS t1 ON t0.id = t1.id;" +94,5916,22032,1068,"SELECT t1.price +FROM departments AS t0 CROSS JOIN books AS t1 RIGHT JOIN users AS t2 ON t1.id = t2.id +WHERE (t2.id BETWEEN 45 AND 86 AND t1.id IN (2));" +95,4669,7432,789,"SELECT t2.id +FROM departments AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE ((t1.department_id IN (1, 5, 12, 34) AND t1.id IN (69)) AND t1.department_id <= 1);" +96,684299,6455219,995,"SELECT t1.id AS c5555, t0.id +FROM orders AS t0 JOIN users AS t1 ON t0.id = t1.id +GROUP BY t1.id, t0.id;" +97,1274,1274,172,"SELECT regions.id, SUM(regions.id), COUNT(regions.id) AS c9550 +FROM regions +WHERE ((regions.id < 2 AND regions.id = 32) AND (regions.id <= 2 AND regions.id != 3)) +GROUP BY regions.id +ORDER BY regions.id ASC;" +98,2482,2482,272,"SELECT books.id +FROM books CROSS JOIN departments +WHERE ((books.price IS NULL AND departments.id = 1) AND (books.price IS NULL OR books.price NOT IN (55, 77)));" +99,58943,401263,308,"SELECT customers.id, SUM(customers.id), COUNT(customers.id) AS c8662 +FROM regions JOIN customers ON regions.id = customers.id +WHERE customers.region_id = 63 +GROUP BY customers.id;" +100,3009,3009,157,"SELECT users.age, users.id AS c7470 +FROM users +ORDER BY users.id ASC;" +101,5266,6624,355,"SELECT markets.id, departments.id AS c1258, markets.region +FROM employees LEFT JOIN markets ON employees.id = markets.id CROSS JOIN departments +WHERE ((markets.id IN (3) AND markets.region = 'EUROPE') OR employees.id < 69);" +102,610000,610000,1156,"SELECT t0.customer_id, t0.id AS c8685 +FROM orders AS t0;" +103,686541,6467000,1466,"SELECT users.age AS c405 +FROM users JOIN orders ON users.id = orders.id RIGHT JOIN regions ON orders.id = regions.id +GROUP BY users.age +ORDER BY users.age DESC;" +104,1222,1222,135,"SELECT employees.id +FROM employees +WHERE employees.id = 14;" +105,1306776,1551752,1478,"SELECT markets.id, orders.customer_id, COUNT(*) +FROM orders RIGHT JOIN markets ON orders.id = markets.id LEFT JOIN books ON markets.id = books.id +GROUP BY markets.id, orders.customer_id;" +106,10632,18447,831,"SELECT books.price AS c2329, markets.note +FROM users CROSS JOIN books JOIN markets ON books.id = markets.id +WHERE (markets.region = 'EUROPE' OR (users.age NOT IN (5, 22, 64, 123) AND users.age < 22)) +ORDER BY markets.note ASC;" +107,1274,1274,448,"SELECT regions.id +FROM regions +WHERE regions.id IN (5) +GROUP BY regions.id +ORDER BY regions.id ASC;" +108,102500,102500,227,"SELECT customers.id AS c9166, customers.region_id +FROM customers +WHERE customers.id < 52 +ORDER BY customers.region_id ASC, customers.id DESC;" +109,5405,16132,182,"SELECT employees.id, users.age, employees.department_id +FROM employees FULL JOIN users ON employees.id = users.id;" +110,5426,16153,222,"SELECT employees.department_id, employees.id, COUNT(users.id) AS c6401, COUNT(*) +FROM employees FULL JOIN users ON employees.id = users.id +WHERE ((employees.department_id = 31 AND employees.department_id NOT IN (6, 59, 72)) AND (employees.department_id NOT IN (1, 5, 6, 32) AND users.id = 5)) +GROUP BY employees.department_id, employees.id;" +111,541846,541846,2127,"SELECT orders.customer_id, orders.id +FROM orders +WHERE ((orders.id IN (97, 99, 184, 198) OR orders.id BETWEEN 31 AND 58) AND (orders.customer_id IS NOT NULL AND orders.id BETWEEN 3 AND 65));" +112,1305844,1550574,2813,"SELECT markets.id, markets.note, COUNT(orders.id), SUM(orders.customer_id) +FROM markets RIGHT JOIN orders ON markets.id = orders.id +WHERE ((orders.id < 188 AND orders.customer_id >= 193) OR (orders.customer_id BETWEEN 239 AND 436 OR markets.id = 1)) +GROUP BY markets.id, markets.note +ORDER BY markets.id DESC, markets.note DESC;" +113,422,422,131,"SELECT t0.region, t0.note AS c6295 +FROM markets AS t0 +WHERE t0.id < 2;" +114,61000,61000,210,"SELECT t0.id +FROM customers AS t0;" +115,3922,10020,186,"SELECT t1.id AS c2831, t0.id, t0.department_id +FROM employees AS t0 RIGHT JOIN regions AS t1 ON t0.id = t1.id;" +116,677833,1551363,1025,"SELECT t2.customer_id +FROM markets AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id JOIN orders AS t2 ON t1.id = t2.id +WHERE t2.customer_id > 244 +ORDER BY t2.customer_id DESC;" +117,744,744,138,"SELECT t0.id AS c6139 +FROM departments AS t0 +WHERE t0.id >= 5;" +118,4923,9763,250,"SELECT books.id AS c118 +FROM departments RIGHT JOIN users ON departments.id = users.id FULL JOIN books ON departments.id = books.id +WHERE ((users.id > 3 AND books.price = 77) AND users.age > 443) +GROUP BY books.id;" +119,676308,2251048,970,"SELECT orders.id, COUNT(departments.id) +FROM departments JOIN orders ON departments.id = orders.id +WHERE departments.id < 36 +GROUP BY orders.id;" +120,1377824,4786342,1518,"SELECT orders.customer_id, employees.department_id +FROM employees JOIN customers ON employees.id = customers.id LEFT JOIN orders ON employees.id = orders.id;" +121,1222,1222,151,"SELECT employees.id +FROM employees +WHERE ((employees.id > 68 OR employees.department_id = 5) AND (employees.id BETWEEN 2 AND 4 AND employees.department_id IN (3, 5, 5)));" +122,3217,5382,214,"SELECT markets.note, employees.id +FROM employees LEFT JOIN markets ON employees.id = markets.id JOIN departments ON employees.id = departments.id +WHERE ((departments.id != 36 OR markets.region IS NULL) AND departments.id = 4);" +123,544,544,145,"SELECT t0.id, t0.note, t0.region +FROM markets AS t0 +WHERE t0.id NOT BETWEEN 2 AND 21;" +124,59693,155433,328,"SELECT books.price AS c5178 +FROM books RIGHT JOIN customers ON books.id = customers.id +WHERE customers.id = 158 +ORDER BY books.price DESC;" +125,1222,1222,180,"SELECT employees.id AS c9929, employees.department_id +FROM employees +WHERE (employees.department_id = 35 AND employees.department_id < 32);" +126,3107,4593,243,"SELECT books.price, books.id +FROM books FULL JOIN markets ON books.id = markets.id FULL JOIN regions ON books.id = regions.id +WHERE (markets.region != 'EUROPE' AND (regions.id IS NULL AND books.price > 66)) +GROUP BY books.price, books.id;" +127,81500,81500,257,"SELECT t0.region_id +FROM customers AS t0 +WHERE t0.id IN (16, 32, 137) +ORDER BY t0.region_id DESC;" +128,1288,1288,345,"SELECT regions.id +FROM regions +WHERE regions.id BETWEEN 9 AND 10 +ORDER BY regions.id DESC;" +129,1306842,1551818,1390,"SELECT orders.customer_id, COUNT(orders.id) AS c1702 +FROM markets LEFT JOIN books ON markets.id = books.id LEFT JOIN orders ON markets.id = orders.id +GROUP BY orders.customer_id +ORDER BY orders.customer_id DESC;" +130,1308015,2255110,1326,"SELECT regions.id, orders.id AS c452, orders.customer_id +FROM regions JOIN departments ON regions.id = departments.id LEFT JOIN orders ON departments.id = orders.id;" +131,1308440,4002960,2060,"SELECT regions.id AS c9060, COUNT(orders.customer_id), COUNT(orders.id) +FROM orders FULL JOIN regions ON orders.id = regions.id +GROUP BY regions.id;" +132,2187,5572,201,"SELECT t0.id AS c4692, t1.department_id AS c2149, t1.id +FROM departments AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id +WHERE (t1.id IN (32, 66, 68, 80) AND (t1.id <= 26 OR t1.department_id <= 5));" +133,678982,4351342,996,"SELECT t1.id AS c8251, t0.customer_id, t0.id AS c2451 +FROM orders AS t0 JOIN employees AS t1 ON t0.id = t1.id;" +134,17111100,17111100,20339,"SELECT orders.customer_id AS c6356 +FROM employees CROSS JOIN orders +ORDER BY orders.customer_id ASC;" +135,1311707,6465260,1861,"SELECT users.id, regions.id AS c3154 +FROM orders FULL JOIN users ON orders.id = users.id RIGHT JOIN regions ON orders.id = regions.id +ORDER BY users.id DESC, regions.id DESC;" +136,7798,11608,343,"SELECT books.price +FROM regions CROSS JOIN markets LEFT JOIN books ON regions.id = books.id +GROUP BY books.price +ORDER BY books.price DESC;" +137,1425,1425,154,"SELECT departments.id, SUM(departments.id), COUNT(*) AS c6998 +FROM departments +GROUP BY departments.id;" +138,2960,2960,148,"SELECT regions.id AS c7258 +FROM regions +GROUP BY regions.id;" +139,563,563,166,"SELECT markets.id +FROM markets +WHERE ((markets.note = 'Old World' OR markets.id >= 2) OR (markets.region = 'AMERICA' AND markets.region = 'EUROPE')) +GROUP BY markets.id;" +140,422,422,137,"SELECT t0.price, t0.id AS c3105 +FROM books AS t0 +WHERE t0.id IS NOT NULL;" +141,3365,4903,441,"SELECT t1.id AS c8895, t1.note AS c8854, SUM(t1.id) +FROM books AS t0 RIGHT JOIN markets AS t1 ON t0.id = t1.id RIGHT JOIN employees AS t2 ON t0.id = t2.id +WHERE t1.id > 24 +GROUP BY t1.id, t1.note;" +142,77205,661132,258,"SELECT t1.region_id AS c1630 +FROM users AS t0 JOIN customers AS t1 ON t0.id = t1.id LEFT JOIN employees AS t2 ON t0.id = t2.id;" +143,422,422,126,"SELECT t0.id, t0.price +FROM books AS t0 +WHERE t0.price <= 55;" +144,9744,9744,291,"SELECT t1.department_id AS c3966, SUM(t1.id) AS c1392, COUNT(*) AS c3892 +FROM books AS t0 CROSS JOIN employees AS t1 +WHERE t1.department_id != 12 +GROUP BY t1.department_id;" +145,2444,3832,211,"SELECT t1.department_id AS c6376, t1.id AS c3596 +FROM books AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id +WHERE ((t0.id < 1 OR t1.id IS NOT NULL) AND (t0.id != 3 AND t1.department_id <= 35));" +146,1590,1590,185,"SELECT departments.id +FROM departments +GROUP BY departments.id +ORDER BY departments.id DESC;" +147,9987,12750,336,"SELECT regions.id +FROM departments FULL JOIN employees ON departments.id = employees.id CROSS JOIN regions;" +148,6250,6250,409,"SELECT books.id, books.price AS c5525, regions.id +FROM books CROSS JOIN regions +WHERE books.price < 69;" +149,102500,102500,442,"SELECT t0.region_id AS c6318 +FROM customers AS t0 +WHERE t0.id > 78 +ORDER BY t0.region_id DESC;" +150,1130500,1130500,1135,"SELECT t0.id, t1.id AS c921 +FROM regions AS t0 CROSS JOIN customers AS t1 +WHERE t1.region_id IN (5, 9, 51) +GROUP BY t0.id, t1.id;" +151,1310829,4014888,2184,"SELECT regions.id, users.age, orders.id +FROM regions RIGHT JOIN users ON regions.id = users.id FULL JOIN orders ON users.id = orders.id +WHERE ((orders.customer_id BETWEEN 283 AND 359 AND users.id IN (10, 17, 17, 39)) OR (users.id IN (14, 16, 64, 75) AND users.id >= 81)) +ORDER BY orders.id ASC, regions.id DESC, users.age ASC;" +152,12160,12160,286,"SELECT departments.id, employees.department_id, employees.id +FROM departments CROSS JOIN employees +ORDER BY employees.id ASC;" +153,239300,239300,1501,"SELECT t1.id, t0.region_id, t0.id +FROM customers AS t0 CROSS JOIN books AS t1;" +154,3226,3226,171,"SELECT regions.id, SUM(regions.id) AS c8443 +FROM regions +WHERE (regions.id >= 1 OR (regions.id >= 8 OR regions.id = 5)) +GROUP BY regions.id +ORDER BY regions.id ASC;" +155,1308138,1554232,1496,"SELECT markets.region +FROM markets LEFT JOIN orders ON markets.id = orders.id JOIN employees ON markets.id = employees.id +GROUP BY markets.region;" +156,1307140,4001220,1259,"SELECT t1.customer_id, t1.id AS c2544, t0.id AS c7170 +FROM regions AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id;" +157,141171,675750,1773,"SELECT t1.region_id, t2.id +FROM users AS t0 RIGHT JOIN customers AS t1 ON t0.id = t1.id CROSS JOIN regions AS t2 +WHERE t0.id IS NULL;" +158,4574,12322,208,"SELECT t1.id, t0.id AS c3888, t2.id AS c5161 +FROM employees AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id JOIN books AS t2 ON t0.id = t2.id +WHERE t0.id IS NULL;" +159,1307045,2251640,1418,"SELECT orders.customer_id AS c3770, departments.id AS c4567, COUNT(*) AS c2771 +FROM departments LEFT JOIN orders ON departments.id = orders.id +WHERE ((orders.customer_id NOT BETWEEN 115 AND 380 OR departments.id IS NULL) OR orders.id = 174) +GROUP BY orders.customer_id, departments.id;" +160,610000,610000,1199,"SELECT t0.id, t0.customer_id AS c1926 +FROM orders AS t0;" +161,91868,1210558,361,"SELECT t1.region AS c961, t2.id +FROM employees AS t0 CROSS JOIN markets AS t1 JOIN customers AS t2 ON t1.id = t2.id;" +162,1466,1466,150,"SELECT employees.id, employees.department_id +FROM employees +WHERE ((employees.id NOT IN (5, 67, 68) AND employees.department_id = 28) OR (employees.id > 4 AND employees.department_id > 32));" +163,127054,1849979,485,"SELECT markets.region, COUNT(*), COUNT(*) +FROM users CROSS JOIN markets LEFT JOIN customers ON markets.id = customers.id +WHERE users.age >= 1 +GROUP BY markets.region;" +164,433,433,151,"SELECT markets.id, markets.note AS c529, markets.region +FROM markets +WHERE (markets.region = 'EUROPE' OR markets.id <= 91) +ORDER BY markets.region ASC, markets.id ASC, markets.note ASC;" +165,822,822,167,"SELECT books.id, COUNT(books.price), SUM(books.id) +FROM books +GROUP BY books.id;" +166,2328,2328,153,"SELECT regions.id +FROM regions +WHERE regions.id NOT IN (2, 95) +ORDER BY regions.id ASC;" +167,52196,52196,244,"SELECT t0.id +FROM customers AS t0 +WHERE ((t0.region_id > 32 AND t0.id = 78) AND (t0.id >= 20 OR t0.id IS NULL));" +168,111678,157908,1393,"SELECT t1.region AS c6909, t1.note, SUM(t2.id) +FROM customers AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id CROSS JOIN departments AS t2 +WHERE ((t0.id = 89 OR t1.id BETWEEN 49 AND 71) AND t0.id IS NOT NULL) +GROUP BY t1.region, t1.note +ORDER BY t1.region ASC;" +169,1558,2113,195,"SELECT departments.id +FROM departments RIGHT JOIN markets ON departments.id = markets.id +WHERE departments.id != 1 +GROUP BY departments.id;" +170,1306995,2251590,3084,"SELECT t1.id AS c5737, t0.customer_id +FROM orders AS t0 LEFT JOIN departments AS t1 ON t0.id = t1.id +GROUP BY t1.id, t0.customer_id +ORDER BY t0.customer_id DESC, t1.id ASC;" +171,24626,37006,372,"SELECT employees.id AS c3715 +FROM regions CROSS JOIN employees FULL JOIN books ON regions.id = books.id;" +172,111497,436875,360,"SELECT t0.id, t0.department_id AS c3676, t1.region_id +FROM employees AS t0 FULL JOIN customers AS t1 ON t0.id = t1.id +WHERE t0.department_id >= 57 +ORDER BY t0.id DESC, t1.region_id ASC;" +173,27106700,27106700,21320,"SELECT orders.customer_id +FROM users CROSS JOIN orders +ORDER BY orders.customer_id DESC;" +174,2475,2475,161,"SELECT users.id, users.age +FROM users +WHERE users.id IN (3, 11, 12) +ORDER BY users.age DESC, users.id DESC;" +175,2273,3832,200,"SELECT books.id, employees.id, employees.department_id +FROM employees RIGHT JOIN books ON employees.id = books.id +WHERE books.price = 77;" +176,563,563,150,"SELECT books.price, SUM(books.price) AS c5357 +FROM books +WHERE books.price >= 66 +GROUP BY books.price;" +177,3692,3692,179,"SELECT users.id, users.age AS c4847 +FROM users +WHERE (users.age < 16 OR users.age < 71) +ORDER BY users.id ASC, users.age DESC;" +178,3009,3009,158,"SELECT users.age AS c7380, users.id +FROM users +ORDER BY users.id DESC;" +179,221288,221288,370,"SELECT customers.id, customers.region_id +FROM customers +WHERE ((customers.id != 141 OR customers.region_id != 10) OR (customers.id NOT IN (197) OR customers.region_id IN (8))) +GROUP BY customers.id, customers.region_id;" +180,225000,225000,809,"SELECT customers.id AS c95, customers.region_id +FROM customers +GROUP BY customers.id, customers.region_id +ORDER BY customers.region_id DESC;" +181,3199300,3199300,3994,"SELECT customers.region_id AS c397, customers.id AS c5003, SUM(books.id) +FROM customers CROSS JOIN books CROSS JOIN departments +GROUP BY customers.region_id, customers.id;" +182,970500,175675500,2055,"SELECT t1.id AS c6022, t0.id +FROM orders AS t0 JOIN customers AS t1 ON t0.id = t1.id +GROUP BY t1.id, t0.id;" +183,65900,65900,231,"SELECT customers.id, COUNT(customers.id), COUNT(customers.id) +FROM customers +WHERE customers.id = 24 +GROUP BY customers.id;" +184,297800,297800,451,"SELECT books.price AS c5308, customers.region_id AS c1694 +FROM books CROSS JOIN customers +WHERE books.price <= 55;" +185,610,610,159,"SELECT t0.id AS c9527 +FROM departments AS t0;" +186,5081,14820,206,"SELECT users.id +FROM regions FULL JOIN users ON regions.id = users.id;" +187,5791,15530,221,"SELECT users.id, regions.id, users.age AS c4755 +FROM users LEFT JOIN regions ON users.id = regions.id +WHERE ((regions.id != 31 AND users.age <= 64) OR users.id >= 66) +ORDER BY users.age DESC;" +188,4810,7573,355,"SELECT t2.price AS c5573, COUNT(*) +FROM employees AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE (t2.price > 55 AND t0.id BETWEEN 66 AND 69) +GROUP BY t2.price;" +189,57522,155422,505,"SELECT markets.id AS c7660 +FROM markets JOIN customers ON markets.id = customers.id +WHERE (markets.note IS NULL AND (customers.id = 198 AND markets.region IN ('AMERICA', 'EUROPE')));" +190,1590,1590,157,"SELECT departments.id +FROM departments +GROUP BY departments.id +ORDER BY departments.id DESC;" +191,19044,21594,338,"SELECT departments.id, employees.id +FROM departments JOIN regions ON departments.id = regions.id CROSS JOIN employees +WHERE employees.id IS NOT NULL +GROUP BY departments.id, employees.id +ORDER BY departments.id DESC, employees.id ASC;" +192,1658,1658,282,"SELECT books.id, markets.region +FROM markets CROSS JOIN books +WHERE markets.region = 'EUROPE';" +193,114435,414820,288,"SELECT customers.region_id, regions.id +FROM users JOIN regions ON users.id = regions.id LEFT JOIN customers ON regions.id = customers.id;" +194,5620982,5620982,9666,"SELECT t2.id +FROM customers AS t0 CROSS JOIN markets AS t1 CROSS JOIN users AS t2 +WHERE ((t1.id IN (3) OR t2.age != 1) OR (t1.id != 2 OR t2.id > 61));" +195,1307400,1553466,2051,"SELECT t0.id +FROM regions AS t0 FULL JOIN books AS t1 ON t0.id = t1.id LEFT JOIN orders AS t2 ON t1.id = t2.id;" +196,617630,618900,790,"SELECT customers.region_id, books.id, SUM(regions.id), SUM(customers.region_id) +FROM books JOIN regions ON books.id = regions.id CROSS JOIN customers +GROUP BY customers.region_id, books.id;" +197,70476,158466,399,"SELECT t0.price AS c8028, t2.id +FROM books AS t0 JOIN customers AS t1 ON t0.id = t1.id JOIN regions AS t2 ON t1.id = t2.id;" +198,2074,2074,125,"SELECT t0.age AS c2991 +FROM users AS t0;" +199,30140,30140,291,"SELECT t1.id, t0.id +FROM regions AS t0 CROSS JOIN users AS t1 +WHERE (t1.age BETWEEN 33 AND 33 OR t1.id IN (12, 49, 95));" +200,2428,2428,155,"SELECT employees.department_id +FROM employees +WHERE ((employees.id > 66 OR employees.id IN (33)) OR employees.department_id IN (6, 12, 12, 50)) +ORDER BY employees.department_id DESC;" +201,610000,610000,1150,"SELECT orders.customer_id, orders.id +FROM orders;" +202,4879,16712,213,"SELECT employees.id AS c3304, users.id AS c2088 +FROM users JOIN employees ON users.id = employees.id +WHERE ((users.age IN (42, 64) OR users.id < 16) AND users.id != 99) +GROUP BY employees.id, users.id;" +203,822,822,373,"SELECT books.id, books.price +FROM books +GROUP BY books.id, books.price;" +204,69372,155822,320,"SELECT t0.id AS c8592, t1.region +FROM customers AS t0 JOIN markets AS t1 ON t0.id = t1.id +GROUP BY t0.id, t1.region;" +205,9975800,9975800,11468,"SELECT t1.id AS c8725, SUM(t1.id), COUNT(*) +FROM users AS t0 CROSS JOIN orders AS t1 +WHERE (t0.age IN (33, 64) AND t1.id = 156) +GROUP BY t1.id;" +206,61000,61000,282,"SELECT t0.id AS c4257 +FROM customers AS t0;" +207,8356600,1931301600,3875,"SELECT t1.customer_id, t0.department_id +FROM employees AS t0 CROSS JOIN orders AS t1 JOIN customers AS t2 ON t1.id = t2.id +WHERE t0.department_id IS NULL;" +208,2388,3776,373,"SELECT books.price AS c3720, employees.department_id, books.id +FROM employees RIGHT JOIN books ON employees.id = books.id;" +209,69533,156493,322,"SELECT t2.region, t1.id, COUNT(t2.id) +FROM books AS t0 JOIN customers AS t1 ON t0.id = t1.id FULL JOIN markets AS t2 ON t0.id = t2.id +WHERE t0.id < 1 +GROUP BY t2.region, t1.id;" +210,2390300,2390300,2800,"SELECT orders.id, books.id AS c7933, orders.customer_id +FROM books CROSS JOIN orders;" +211,26935700,2984422200,41119,"SELECT t2.region_id, t2.id, t0.customer_id +FROM orders AS t0 CROSS JOIN users AS t1 LEFT JOIN customers AS t2 ON t1.id = t2.id +WHERE t2.region_id >= 23;" +212,1306062,1550822,1393,"SELECT orders.id +FROM books LEFT JOIN orders ON books.id = orders.id +GROUP BY orders.id;" +213,1620,1620,142,"SELECT t0.id +FROM regions AS t0 +WHERE t0.id IN (4, 4, 9, 88) +ORDER BY t0.id ASC;" +214,51596600,51596600,95942,"SELECT orders.id, orders.customer_id, employees.id +FROM orders CROSS JOIN employees CROSS JOIN departments +WHERE orders.customer_id >= 52;" +215,805000,805000,1006,"SELECT t0.customer_id +FROM orders AS t0 +WHERE t0.id < 30;" +216,744,744,135,"SELECT departments.id +FROM departments +WHERE departments.id IN (1, 3, 4, 60);" +217,112147,437525,350,"SELECT t0.id, t1.region_id +FROM employees AS t0 LEFT JOIN customers AS t1 ON t0.id = t1.id +WHERE t1.region_id >= 8 +GROUP BY t0.id, t1.region_id;" +218,1331,1916,188,"SELECT books.price AS c5412, books.id, departments.id AS c9618 +FROM departments LEFT JOIN books ON departments.id = books.id;" +219,9485600,9485600,25727,"SELECT t1.note, t0.customer_id AS c4395 +FROM orders AS t0 CROSS JOIN markets AS t1 CROSS JOIN books AS t2 +WHERE t1.region IS NOT NULL;" +220,17063620,1931305120,29765,"SELECT customers.region_id, orders.id, employees.id +FROM employees CROSS JOIN orders RIGHT JOIN customers ON employees.id = customers.id +WHERE (customers.id IN (181) OR employees.id BETWEEN 3 AND 92) +ORDER BY customers.region_id ASC;" +221,1133,1133,142,"SELECT t0.id AS c9777 +FROM regions AS t0 +WHERE ((t0.id <= 5 OR t0.id IN (4, 12)) AND (t0.id = 9 AND t0.id NOT IN (2, 4, 5, 89))) +ORDER BY t0.id ASC;" +222,3764,3764,176,"SELECT t0.id, t0.department_id AS c2878, COUNT(t0.id), COUNT(*) AS c2633 +FROM employees AS t0 +WHERE ((t0.department_id IS NOT NULL OR t0.department_id IS NOT NULL) OR (t0.id > 26 OR t0.id = 52)) +GROUP BY t0.id, t0.department_id;" +223,7431100,7431100,13982,"SELECT t0.id +FROM orders AS t0 CROSS JOIN employees AS t1;" +224,1342,1342,311,"SELECT employees.department_id +FROM employees;" +225,791024,4786342,1020,"SELECT orders.customer_id, customers.id AS c4775, employees.department_id AS c6986 +FROM employees LEFT JOIN customers ON employees.id = customers.id JOIN orders ON employees.id = orders.id;" +226,2374,3644,190,"SELECT books.id, regions.id, books.price AS c1520 +FROM books LEFT JOIN regions ON books.id = regions.id +WHERE ((regions.id < 10 OR books.price = 77) OR books.price > 60);" +227,1305662,1550422,1899,"SELECT orders.id, books.price, orders.customer_id AS c7568 +FROM orders FULL JOIN books ON orders.id = books.id +WHERE orders.customer_id = 63;" +228,283748,285106,1361,"SELECT t1.id AS c1443, t2.region_id AS c2237 +FROM employees AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id CROSS JOIN customers AS t2 +WHERE ((t1.id < 3 OR t1.region = 'AMERICA') AND (t2.region_id IS NULL OR t1.note != 'North, South'));" +229,112350,228445,447,"SELECT departments.id +FROM departments LEFT JOIN customers ON departments.id = customers.id CROSS JOIN markets +WHERE markets.region IS NULL +ORDER BY departments.id ASC;" +230,1309060,2258260,2184,"SELECT departments.id, users.age AS c5139, orders.id +FROM departments JOIN users ON departments.id = users.id FULL JOIN orders ON users.id = orders.id;" +231,610,610,163,"SELECT departments.id +FROM departments;" +232,116575,651154,521,"SELECT customers.id, COUNT(*) AS c3789 +FROM customers RIGHT JOIN users ON customers.id = users.id +GROUP BY customers.id +ORDER BY customers.id DESC;" +233,317095,317650,896,"SELECT departments.id +FROM departments JOIN markets ON departments.id = markets.id CROSS JOIN customers +WHERE (customers.region_id NOT IN (10, 10) AND (customers.id <= 80 OR departments.id >= 1));" +234,2226,3466,203,"SELECT t0.note +FROM markets AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id;" +235,68764,155544,271,"SELECT t1.region_id AS c5511 +FROM books AS t0 JOIN customers AS t1 ON t0.id = t1.id +WHERE (t0.price NOT BETWEEN 66 AND 94 OR (t0.id > 3 AND t1.region_id = 10));" +236,1311747,4366616,1382,"SELECT t1.department_id AS c2338, t0.customer_id +FROM orders AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN users AS t2 ON t1.id = t2.id +ORDER BY t1.department_id ASC;" +237,134541,669120,1920,"SELECT regions.id +FROM users FULL JOIN customers ON users.id = customers.id CROSS JOIN regions;" +238,103844,436222,814,"SELECT customers.region_id +FROM customers LEFT JOIN employees ON customers.id = employees.id +WHERE (employees.id IN (38) AND customers.region_id != 5);" +239,2690,2690,155,"SELECT employees.id +FROM employees +WHERE employees.id > 5 +GROUP BY employees.id +ORDER BY employees.id DESC;" +240,95524,95524,401,"SELECT t0.price AS c6143, SUM(t2.id) +FROM books AS t0 CROSS JOIN users AS t1 CROSS JOIN departments AS t2 +GROUP BY t0.price;" +241,3759405,3875500,4164,"SELECT customers.id, customers.region_id, departments.id +FROM customers RIGHT JOIN departments ON customers.id = departments.id CROSS JOIN orders;" +242,5320,5320,288,"SELECT books.id +FROM books CROSS JOIN employees +WHERE (employees.id != 33 AND (employees.id < 1 AND employees.department_id < 12));" +243,680155,4353843,1006,"SELECT employees.department_id, books.price AS c4475, books.id AS c4655 +FROM orders JOIN employees ON orders.id = employees.id RIGHT JOIN books ON employees.id = books.id +WHERE ((orders.id >= 13 AND orders.customer_id != 233) OR (orders.id > 39 AND orders.customer_id <= 359)) +ORDER BY books.id DESC;" +244,4604,12322,221,"SELECT regions.id +FROM regions RIGHT JOIN employees ON regions.id = employees.id LEFT JOIN markets ON employees.id = markets.id +WHERE (regions.id NOT BETWEEN 2 AND 8 AND markets.note = 'Fast lane');" +245,433,433,251,"SELECT markets.note, markets.region, markets.id +FROM markets +WHERE ((markets.region != 'AMERICA' AND markets.region = 'AMERICA') OR markets.id < 2) +ORDER BY markets.note DESC, markets.id ASC, markets.region DESC;" +246,1244,1244,130,"SELECT regions.id AS c713 +FROM regions +WHERE regions.id BETWEEN 2 AND 45;" +247,1306088,1550848,1439,"SELECT t0.id AS c7208 +FROM orders AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id +WHERE t0.customer_id NOT IN (244, 365) +GROUP BY t0.id +ORDER BY t0.id ASC;" +248,1311645,6455252,3301,"SELECT t1.id, COUNT(*), SUM(t0.id) AS c7387 +FROM users AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t1.customer_id = 494 AND t1.customer_id != 145) OR (t0.age > 53 OR t1.customer_id IS NOT NULL)) +GROUP BY t1.id;" +249,3633,5692,210,"SELECT markets.region, users.age +FROM markets LEFT JOIN users ON markets.id = users.id +WHERE users.id < 17;" +250,110640,401220,381,"SELECT customers.id, customers.region_id +FROM regions LEFT JOIN customers ON regions.id = customers.id;" +251,1307530,4001775,2243,"SELECT orders.customer_id, regions.id, orders.id AS c3346 +FROM regions FULL JOIN orders ON regions.id = orders.id +WHERE orders.id <= 42 +ORDER BY regions.id ASC;" +252,4641438,7310933,7529,"SELECT orders.id AS c8852, orders.customer_id, books.price AS c8813 +FROM books CROSS JOIN orders FULL JOIN departments ON orders.id = departments.id +WHERE ((orders.customer_id = 163 OR orders.id IN (69, 96, 152)) AND (orders.id IS NOT NULL AND books.id IS NOT NULL)) +ORDER BY orders.id DESC;" +253,121234,3916834,377,"SELECT customers.id +FROM regions CROSS JOIN employees JOIN customers ON employees.id = customers.id +WHERE (employees.id IS NOT NULL AND customers.id >= 65);" +254,2418,3776,173,"SELECT markets.id AS c3977, employees.department_id +FROM markets FULL JOIN employees ON markets.id = employees.id;" +255,1975000,1975000,2385,"SELECT orders.id, orders.customer_id AS c7286 +FROM orders +GROUP BY orders.id, orders.customer_id;" +256,7496,7496,274,"SELECT t0.id, t1.id AS c9218 +FROM markets AS t0 CROSS JOIN employees AS t1 +WHERE t1.department_id IN (5, 12, 33) +GROUP BY t0.id, t1.id;" +257,2652,3922,190,"SELECT t1.price, t0.id AS c7828 +FROM regions AS t0 JOIN books AS t1 ON t0.id = t1.id +GROUP BY t1.price, t0.id;" +258,75739,651158,681,"SELECT customers.region_id +FROM customers JOIN users ON customers.id = users.id RIGHT JOIN markets ON customers.id = markets.id +GROUP BY customers.region_id +ORDER BY customers.region_id DESC;" +259,110500,110500,245,"SELECT customers.id +FROM customers +ORDER BY customers.id ASC;" +260,72814,72814,261,"SELECT customers.region_id, customers.id +FROM customers +WHERE ((customers.region_id IS NULL OR customers.id >= 198) AND customers.region_id <= 4);" +261,109628,155888,313,"SELECT customers.region_id AS c4278, books.id +FROM books LEFT JOIN customers ON books.id = customers.id +GROUP BY customers.region_id, books.id +ORDER BY books.id ASC;" +262,15960,15960,264,"SELECT t1.department_id AS c5186, t0.id, t1.id +FROM regions AS t0 CROSS JOIN employees AS t1;" +263,3077,6483,244,"SELECT markets.id, departments.id, regions.id +FROM regions LEFT JOIN departments ON regions.id = departments.id JOIN markets ON regions.id = markets.id +WHERE markets.note != 'Old World' +ORDER BY markets.id ASC, regions.id DESC;" +264,112407,160636,449,"SELECT users.id AS c6920, markets.region AS c5238, users.age AS c1104 +FROM markets RIGHT JOIN users ON markets.id = users.id RIGHT JOIN customers ON markets.id = customers.id;" +265,5081,14820,204,"SELECT t1.id, t0.id AS c2693 +FROM users AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id;" +266,59400,59400,227,"SELECT customers.id AS c604, customers.region_id +FROM customers +WHERE customers.id = 135 +ORDER BY customers.id DESC, customers.region_id DESC;" +267,422,422,120,"SELECT books.id +FROM books +WHERE books.price IS NULL;" +268,76971400,76971400,122051,"SELECT employees.department_id, SUM(orders.customer_id) +FROM orders CROSS JOIN books CROSS JOIN employees +GROUP BY employees.department_id;" +269,366,366,128,"SELECT books.price AS c8205 +FROM books;" +270,610000,610000,1109,"SELECT orders.id AS c7922 +FROM orders;" +271,534702,2250622,1173,"SELECT orders.id AS c8902, departments.id AS c4368, orders.customer_id +FROM orders JOIN departments ON orders.id = departments.id +WHERE ((orders.customer_id = 483 AND departments.id > 4) AND orders.id != 24);" +272,850563,1550563,2028,"SELECT orders.customer_id, orders.id, COUNT(*), COUNT(*) +FROM orders RIGHT JOIN books ON orders.id = books.id +WHERE books.id != 1 +GROUP BY orders.customer_id, orders.id;" +273,3256,3256,156,"SELECT t0.id, SUM(t0.department_id), COUNT(t0.department_id) +FROM employees AS t0 +GROUP BY t0.id;" +274,2066,2066,148,"SELECT t0.id AS c7269, t0.age +FROM users AS t0 +WHERE t0.age IN (33, 33);" +275,545750,545750,1457,"SELECT orders.id AS c6877 +FROM orders +WHERE ((orders.customer_id <= 56 OR orders.customer_id != 343) AND (orders.customer_id IN (52, 86) AND orders.customer_id >= 224));" +276,3536700,3536700,2753,"SELECT users.age AS c7960, SUM(users.id) +FROM customers CROSS JOIN users +GROUP BY users.age +ORDER BY users.age ASC;" +277,774,774,173,"SELECT t0.id +FROM departments AS t0 +WHERE ((t0.id IN (2, 10, 59) OR t0.id IS NULL) AND (t0.id != 5 AND t0.id IS NOT NULL)) +GROUP BY t0.id +ORDER BY t0.id DESC;" +278,4334,8638,449,"SELECT t1.id AS c8751 +FROM users AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id +WHERE (t0.age IS NULL OR (t1.id != 5 OR t1.id < 14));" +279,851363,4001263,1051,"SELECT regions.id, orders.id, COUNT(*), SUM(orders.customer_id) +FROM orders RIGHT JOIN regions ON orders.id = regions.id +WHERE ((regions.id IN (8, 9, 22) AND regions.id <= 6) AND orders.id IN (39, 49, 104, 106)) +GROUP BY regions.id, orders.id;" +280,1308743,6451822,2349,"SELECT orders.customer_id +FROM users FULL JOIN orders ON users.id = orders.id +WHERE ((users.age > 33 AND orders.customer_id < 84) AND (users.id = 15 OR orders.id IN (68, 128, 128, 198)));" +281,433,433,147,"SELECT t0.id, t0.note, t0.region +FROM markets AS t0 +WHERE ((t0.note = 'unknown' AND t0.id != 1) AND t0.id > 67) +ORDER BY t0.id ASC, t0.note DESC;" +282,4643352,13612010,5295,"SELECT employees.id, orders.customer_id AS c5471, books.price +FROM books CROSS JOIN orders LEFT JOIN employees ON orders.id = employees.id +WHERE employees.id > 66;" +283,415563,521733,813,"SELECT books.id, markets.id, books.price AS c2723 +FROM books CROSS JOIN customers FULL JOIN markets ON customers.id = markets.id +WHERE ((markets.id > 55 AND customers.region_id NOT IN (8, 9, 10)) AND markets.region IN ('AMERICA', 'EUROPE')) +ORDER BY books.id DESC, markets.id ASC;" +284,822,822,143,"SELECT t0.region AS c1153, SUM(t0.id) AS c3569 +FROM markets AS t0 +GROUP BY t0.region +ORDER BY t0.region ASC;" +285,9478,10836,448,"SELECT employees.id AS c5654, users.id +FROM markets FULL JOIN employees ON markets.id = employees.id CROSS JOIN users +WHERE ((markets.note = 'Fast lane' AND employees.department_id NOT BETWEEN 11 AND 37) AND (users.id != 4 AND users.age <= 443));" +286,109668,225774,380,"SELECT customers.id, COUNT(customers.id) AS c434, COUNT(*) +FROM customers FULL JOIN departments ON customers.id = departments.id +WHERE departments.id BETWEEN 2 AND 4 +GROUP BY customers.id +ORDER BY customers.id DESC;" +287,3181,5072,240,"SELECT t2.id, t1.id, t0.id +FROM books AS t0 JOIN departments AS t1 ON t0.id = t1.id RIGHT JOIN regions AS t2 ON t1.id = t2.id +WHERE t0.id IS NULL;" +288,5252,15332,194,"SELECT regions.id AS c1673 +FROM users JOIN regions ON users.id = regions.id +WHERE (users.age IN (33, 47, 98) OR users.id > 54);" +289,2493,2493,854,"SELECT departments.id, books.price AS c551 +FROM departments CROSS JOIN books +WHERE departments.id = 5 +ORDER BY departments.id DESC;" +290,680174,4353832,1013,"SELECT t2.region, t1.department_id, t0.customer_id AS c8493 +FROM orders AS t0 JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN markets AS t2 ON t1.id = t2.id +WHERE t0.customer_id < 239;" +291,12321160,68844660,4574,"SELECT orders.id, COUNT(orders.id), SUM(regions.id) +FROM users CROSS JOIN orders JOIN regions ON orders.id = regions.id +GROUP BY orders.id;" +292,888,888,174,"SELECT t0.region, t0.id, SUM(t0.id), COUNT(*) +FROM markets AS t0 +GROUP BY t0.region, t0.id +ORDER BY t0.region ASC, t0.id DESC;" +293,3603,5692,601,"SELECT t0.age, t1.id +FROM users AS t0 FULL JOIN books AS t1 ON t0.id = t1.id +WHERE (t0.age = 18 AND (t0.age != 123 AND t1.id <= 89));" +294,848,848,176,"SELECT books.price AS c6589, books.id, COUNT(*) +FROM books +WHERE ((books.price > 77 OR books.id < 3) OR (books.price IN (49, 55, 55, 77) OR books.price IN (26, 66, 66, 68))) +GROUP BY books.price, books.id;" +295,4370,9075,193,"SELECT users.id, SUM(users.age), COUNT(users.id) +FROM users JOIN departments ON users.id = departments.id +GROUP BY users.id;" +296,432,432,179,"SELECT t0.region, t0.id AS c2775, t0.note +FROM markets AS t0 +ORDER BY t0.id DESC;" +297,685683,6467110,1086,"SELECT t2.department_id +FROM orders AS t0 JOIN users AS t1 ON t0.id = t1.id FULL JOIN employees AS t2 ON t0.id = t2.id +WHERE ((t2.id NOT BETWEEN 4 AND 6 OR t1.age != 21) OR t0.id <= 61);" +298,988,988,127,"SELECT departments.id +FROM departments +WHERE ((departments.id NOT IN (4, 4, 5, 7) OR departments.id NOT IN (2, 5)) OR (departments.id NOT BETWEEN 3 AND 27 OR departments.id IS NULL));" +299,2455,3843,339,"SELECT books.price, employees.id AS c7128 +FROM employees RIGHT JOIN books ON employees.id = books.id +WHERE ((books.id > 68 OR books.id >= 52) AND (employees.department_id < 6 OR employees.department_id != 1)) +ORDER BY books.price DESC;" +300,1306863,2251983,2076,"SELECT orders.id AS c9121 +FROM orders LEFT JOIN departments ON orders.id = departments.id RIGHT JOIN books ON departments.id = books.id +WHERE orders.customer_id <= 497 +ORDER BY orders.id ASC;" +301,3967,6092,180,"SELECT users.id, COUNT(markets.id), SUM(markets.id) +FROM users JOIN markets ON users.id = markets.id +GROUP BY users.id;" +302,75362,410460,975,"SELECT regions.id, customers.region_id, employees.id +FROM regions FULL JOIN employees ON regions.id = employees.id JOIN customers ON regions.id = customers.id +ORDER BY customers.region_id ASC, employees.id ASC, regions.id ASC;" +303,1832,1832,418,"SELECT employees.id, employees.department_id +FROM employees +WHERE ((employees.id <= 33 OR employees.id = 3) OR (employees.department_id NOT IN (5) AND employees.id = 67));" +304,1342,1342,122,"SELECT employees.department_id AS c3770 +FROM employees;" +305,1822,1822,181,"SELECT regions.id AS c2143, COUNT(*), COUNT(*) +FROM regions +WHERE regions.id IN (3, 10, 89) +GROUP BY regions.id;" +306,3457,6272,196,"SELECT employees.id, employees.department_id, SUM(employees.id), SUM(employees.department_id) AS c8963 +FROM employees JOIN departments ON employees.id = departments.id +WHERE (departments.id <= 3 OR (employees.id = 68 OR employees.id <= 6)) +GROUP BY employees.id, employees.department_id;" +307,1307657,1553663,2258,"SELECT t0.note AS c4138 +FROM markets AS t0 JOIN regions AS t1 ON t0.id = t1.id RIGHT JOIN orders AS t2 ON t0.id = t2.id +WHERE ((t2.customer_id = 298 OR t0.region = 'AMERICA') OR t0.region != 'AMERICA') +GROUP BY t0.note;" +308,1092,1092,182,"SELECT departments.id, COUNT(*) AS c9125 +FROM departments +WHERE (departments.id > 56 OR departments.id = 4) +GROUP BY departments.id +ORDER BY departments.id DESC;" +309,563,563,159,"SELECT t0.id AS c7541, COUNT(t0.id) +FROM markets AS t0 +WHERE (t0.note != 'Fast lane' AND (t0.id = 3 OR t0.note = 'Old World')) +GROUP BY t0.id;" +310,1422102,7099420,1516,"SELECT t1.id, t2.id, SUM(t2.id) +FROM customers AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id LEFT JOIN orders AS t2 ON t0.id = t2.id +WHERE (t2.customer_id < 243 OR (t2.id >= 38 AND t2.customer_id = 302)) +GROUP BY t1.id, t2.id +ORDER BY t2.id ASC;" +311,1305703,1550433,2695,"SELECT t0.region, t1.customer_id AS c7183 +FROM markets AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t0.id <= 3 OR t0.note = 'Fast lane') AND (t0.note != 'North, South' OR t0.note = 'Fast lane')) +ORDER BY t0.region ASC;" +312,1308302,2255560,1538,"SELECT t1.id, t0.id +FROM departments AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id JOIN employees AS t2 ON t1.id = t2.id;" +313,678803,2255816,930,"SELECT employees.id, orders.id AS c4699, orders.customer_id +FROM departments JOIN orders ON departments.id = orders.id LEFT JOIN employees ON departments.id = employees.id +WHERE employees.department_id NOT BETWEEN 5 AND 33;" +314,2840,2840,202,"SELECT t0.age, t0.id +FROM users AS t0 +WHERE (t0.age > 5 AND t0.id != 2) +GROUP BY t0.age, t0.id;" +315,2638,5548,212,"SELECT t0.id +FROM regions AS t0 JOIN departments AS t1 ON t0.id = t1.id +WHERE t1.id IS NOT NULL +GROUP BY t0.id;" +316,822,822,200,"SELECT books.id +FROM books +GROUP BY books.id;" +317,1305606,1550366,1943,"SELECT orders.id AS c7996, books.price +FROM books FULL JOIN orders ON books.id = orders.id;" +318,4668,7332,216,"SELECT books.price, books.id +FROM books FULL JOIN employees ON books.id = employees.id RIGHT JOIN regions ON employees.id = regions.id +GROUP BY books.price, books.id;" +319,563,563,159,"SELECT books.id AS c9150, books.price, COUNT(books.id) +FROM books +WHERE ((books.price IN (76, 77) AND books.id BETWEEN 1 AND 96) OR books.price IN (55, 66, 77)) +GROUP BY books.id, books.price;" +320,71482,436342,260,"SELECT customers.region_id, employees.id +FROM customers JOIN employees ON customers.id = employees.id;" +321,702625,702625,996,"SELECT t0.id AS c6071, COUNT(*), COUNT(*) AS c7241 +FROM regions AS t0 CROSS JOIN customers AS t1 +WHERE ((t1.id >= 59 OR t0.id IS NULL) AND t1.id = 126) +GROUP BY t0.id;" +322,111676,403466,440,"SELECT t1.region_id, t1.id, t2.price +FROM regions AS t0 RIGHT JOIN customers AS t1 ON t0.id = t1.id LEFT JOIN books AS t2 ON t0.id = t2.id;" +323,113190,650703,322,"SELECT t0.region_id +FROM customers AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id RIGHT JOIN markets AS t2 ON t1.id = t2.id +WHERE t1.id < 14 +ORDER BY t0.region_id ASC;" +324,678469,2255244,1202,"SELECT orders.id AS c4187, regions.id +FROM departments JOIN orders ON departments.id = orders.id LEFT JOIN regions ON departments.id = regions.id +WHERE regions.id < 4;" +325,2991,5932,295,"SELECT markets.note AS c7216, books.price +FROM departments CROSS JOIN markets LEFT JOIN books ON markets.id = books.id +WHERE ((departments.id != 2 AND markets.note = 'Old World') AND (departments.id BETWEEN 1 AND 5 OR departments.id >= 41));" +326,2074,2074,122,"SELECT t0.age +FROM users AS t0;" +327,6154,6154,204,"SELECT t0.age +FROM users AS t0 +GROUP BY t0.age +ORDER BY t0.age ASC;" +328,16894318,44723198,32898,"SELECT employees.department_id +FROM orders CROSS JOIN employees LEFT JOIN regions ON employees.id = regions.id +WHERE ((employees.id <= 25 OR orders.id NOT BETWEEN 47 AND 184) OR orders.id IS NULL);" +329,3583,5858,180,"SELECT t0.id +FROM markets AS t0 JOIN users AS t1 ON t0.id = t1.id +WHERE (t0.id != 2 OR t0.id != 1) +ORDER BY t0.id ASC;" +330,111330,158984,597,"SELECT books.price, employees.id, COUNT(*) +FROM customers LEFT JOIN books ON customers.id = books.id FULL JOIN employees ON customers.id = employees.id +WHERE ((customers.id > 96 AND employees.department_id != 32) AND (employees.id > 5 OR books.id IS NOT NULL)) +GROUP BY books.price, employees.id +ORDER BY books.price DESC, employees.id DESC;" +331,15545500,1755762000,23081,"SELECT t2.region_id, t2.id +FROM orders AS t0 CROSS JOIN regions AS t1 FULL JOIN customers AS t2 ON t1.id = t2.id;" +332,1610,1610,143,"SELECT regions.id AS c7728 +FROM regions +WHERE regions.id IS NOT NULL;" +333,1415505,2476425,2558,"SELECT departments.id, COUNT(*) +FROM orders FULL JOIN departments ON orders.id = departments.id LEFT JOIN customers ON departments.id = customers.id +GROUP BY departments.id;" +334,1775,1775,216,"SELECT regions.id AS c1480 +FROM regions +WHERE regions.id IS NOT NULL +ORDER BY regions.id DESC;" +335,433,433,195,"SELECT books.price, books.id +FROM books +WHERE (books.price < 44 AND (books.price IN (55, 55, 66, 66) AND books.price != 77)) +ORDER BY books.price DESC, books.id DESC;" +336,1222,1222,152,"SELECT t0.department_id +FROM employees AS t0 +WHERE ((t0.department_id = 12 AND t0.department_id < 35) AND (t0.id IN (4) OR t0.department_id != 31));" +337,2432,2432,132,"SELECT users.id, users.age +FROM users +WHERE users.age IN (1, 21, 33, 64);" +338,1732,1732,220,"SELECT t0.id +FROM regions AS t0 +WHERE ((t0.id NOT BETWEEN 1 AND 15 OR t0.id < 10) AND (t0.id <= 40 OR t0.id != 3));" +339,744,744,137,"SELECT departments.id +FROM departments +WHERE ((departments.id <= 98 OR departments.id > 1) AND (departments.id IS NULL OR departments.id IN (3, 3, 30, 91)));" +340,109333,155563,454,"SELECT customers.region_id AS c4276, markets.region AS c7002, COUNT(*), COUNT(*) AS c3968 +FROM customers LEFT JOIN markets ON customers.id = markets.id +WHERE (markets.note = 'Fast lane' OR customers.id IS NOT NULL) +GROUP BY customers.region_id, markets.region;" +341,112731,161092,473,"SELECT customers.id, SUM(customers.region_id) +FROM markets JOIN users ON markets.id = users.id RIGHT JOIN customers ON users.id = customers.id +GROUP BY customers.id;" +342,1222,1222,143,"SELECT employees.department_id, employees.id +FROM employees +WHERE ((employees.id >= 66 OR employees.id != 2) AND (employees.id > 69 AND employees.department_id = 33));" +343,3009,3009,151,"SELECT users.age +FROM users +ORDER BY users.age ASC;" +344,2101,3522,207,"SELECT t1.price AS c605, t0.id AS c1651, t1.id +FROM regions AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id +WHERE (t1.id != 3 OR t1.id = 3);" +345,109172,155432,323,"SELECT books.id, customers.id AS c5626 +FROM books LEFT JOIN customers ON books.id = customers.id +ORDER BY customers.id DESC;" +346,10087,29195,383,"SELECT t1.region, t2.id, COUNT(t1.id) +FROM employees AS t0 CROSS JOIN markets AS t1 FULL JOIN regions AS t2 ON t1.id = t2.id +WHERE t0.department_id = 36 +GROUP BY t1.region, t2.id;" +347,1309597,6452676,2002,"SELECT users.age AS c6973 +FROM users RIGHT JOIN orders ON users.id = orders.id +WHERE users.id > 94;" +348,525100,525100,1405,"SELECT t1.id, t0.id +FROM departments AS t0 CROSS JOIN customers AS t1 +WHERE ((t1.id BETWEEN 28 AND 146 AND t1.id IN (46, 50, 104, 146)) OR (t0.id < 4 AND t1.region_id NOT IN (2, 2, 10, 12))) +ORDER BY t1.id DESC;" +349,2832,5592,230,"SELECT regions.id, departments.id AS c6050, COUNT(*), COUNT(*) +FROM regions RIGHT JOIN departments ON regions.id = departments.id +WHERE departments.id IS NOT NULL +GROUP BY regions.id, departments.id +ORDER BY departments.id ASC;" +350,3612,6375,207,"SELECT employees.department_id, departments.id AS c3106, COUNT(*) AS c7893 +FROM departments RIGHT JOIN employees ON departments.id = employees.id +GROUP BY employees.department_id, departments.id;" +351,7680023,32852932,15314,"SELECT t0.customer_id, t2.id AS c6006, t2.age +FROM orders AS t0 CROSS JOIN departments AS t1 FULL JOIN users AS t2 ON t1.id = t2.id +WHERE (t1.id >= 3 AND (t0.id < 55 OR t0.id < 88));" +352,5464128,5464128,4017,"SELECT t1.customer_id, t1.id, t0.id +FROM departments AS t0 CROSS JOIN orders AS t1 +WHERE (t1.customer_id IN (426) OR (t1.customer_id IS NULL OR t0.id >= 57));" +353,61000,61000,225,"SELECT t0.id, t0.region_id +FROM customers AS t0;" +354,2372,2372,163,"SELECT t0.department_id, t0.id +FROM employees AS t0 +WHERE (t0.department_id >= 31 AND (t0.department_id > 34 OR t0.id < 33)) +GROUP BY t0.department_id, t0.id +ORDER BY t0.department_id DESC;" +355,62200,62200,221,"SELECT customers.id, customers.region_id +FROM customers +WHERE customers.id IN (153, 183);" +356,3413,5326,220,"SELECT employees.department_id, departments.id +FROM books LEFT JOIN employees ON books.id = employees.id FULL JOIN departments ON employees.id = departments.id;" +357,1940000,1940000,2539,"SELECT orders.id AS c1456, orders.customer_id, COUNT(*), COUNT(orders.customer_id) +FROM orders +WHERE (orders.id > 86 OR orders.customer_id IS NULL) +GROUP BY orders.id, orders.customer_id;" +358,2525,2525,143,"SELECT t0.department_id, t0.id AS c8666, COUNT(*) AS c1170, COUNT(*) +FROM employees AS t0 +WHERE t0.id IS NULL +GROUP BY t0.department_id, t0.id;" +359,2960,2960,139,"SELECT regions.id, COUNT(regions.id) AS c1004, COUNT(regions.id) AS c9498 +FROM regions +GROUP BY regions.id;" +360,366,366,122,"SELECT t0.id, t0.note AS c1949, t0.region +FROM markets AS t0;" +361,130976600,130976600,209436,"SELECT t2.department_id, COUNT(t2.id), SUM(t0.id) +FROM departments AS t0 CROSS JOIN orders AS t1 CROSS JOIN employees AS t2 +GROUP BY t2.department_id;" +362,785550822,785550822,812242,"SELECT markets.id AS c6682, markets.note AS c3493 +FROM orders CROSS JOIN customers RIGHT JOIN markets ON customers.id = markets.id +GROUP BY markets.id, markets.note;" +363,433,433,150,"SELECT books.price +FROM books +WHERE books.price IS NOT NULL +ORDER BY books.price DESC;" +364,69900,69900,260,"SELECT customers.region_id AS c2372 +FROM customers +WHERE customers.region_id IN (4, 89) +ORDER BY customers.region_id DESC;" +365,1373762,1705422,2392,"SELECT t0.id AS c4468, t1.customer_id, t0.region +FROM markets AS t0 FULL JOIN orders AS t1 ON t0.id = t1.id JOIN customers AS t2 ON t0.id = t2.id +WHERE t0.id <= 17;" +366,2310,2310,147,"SELECT users.id, users.age +FROM users +WHERE (users.id = 15 OR (users.id != 3 AND users.id IS NULL));" +367,2074,2074,319,"SELECT t0.age +FROM users AS t0;" +368,12062,12062,330,"SELECT t0.id, t1.age +FROM books AS t0 CROSS JOIN users AS t1 +WHERE (t1.id = 96 OR (t1.age != 18 OR t0.price <= 61));" +369,850563,1550563,1025,"SELECT t0.id AS c4185, SUM(t0.id) +FROM markets AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t0.id BETWEEN 1 AND 7 OR t0.note != 'North, South') AND (t0.note != 'Old World' AND t0.note IN ('ASIA', 'Old World', 'unknown'))) +GROUP BY t0.id;" +370,422,422,131,"SELECT markets.note AS c2495, markets.id, markets.region +FROM markets +WHERE ((markets.note = 'North, South' AND markets.note != 'North, South') OR markets.note != 'Fast lane');" +371,15960,15960,288,"SELECT regions.id +FROM regions CROSS JOIN employees;" +372,1293750,1293750,1586,"SELECT t0.id +FROM orders AS t0 +WHERE (t0.id <= 95 OR t0.customer_id BETWEEN 55 AND 369) +ORDER BY t0.id DESC;" +373,175500,175500,366,"SELECT customers.id, COUNT(customers.region_id) +FROM customers +GROUP BY customers.id;" +374,6653000,6653000,5857,"SELECT orders.id AS c9907, COUNT(*) AS c8627 +FROM departments CROSS JOIN orders +WHERE ((orders.id >= 79 OR orders.customer_id IN (63, 185, 226, 480)) AND departments.id IS NULL) +GROUP BY orders.id +ORDER BY orders.id ASC;" +375,957500,957500,1391,"SELECT t0.customer_id AS c3627, t0.id AS c1197 +FROM orders AS t0 +WHERE (t0.customer_id <= 56 OR t0.id > 1);" +376,5325,14288,226,"SELECT t1.id, t0.id, t1.department_id +FROM regions AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id FULL JOIN departments AS t2 ON t0.id = t2.id +WHERE t0.id NOT IN (3);" +377,77608,77608,397,"SELECT customers.id, customers.region_id, SUM(customers.region_id), SUM(customers.id) +FROM customers +WHERE ((customers.region_id != 9 OR customers.id = 20) AND (customers.region_id >= 3 AND customers.id > 5)) +GROUP BY customers.id, customers.region_id +ORDER BY customers.id DESC, customers.region_id ASC;" +378,822,822,338,"SELECT markets.region, markets.id AS c5516 +FROM markets +GROUP BY markets.region, markets.id;" +379,1342,1342,168,"SELECT employees.department_id, employees.id +FROM employees;" +380,1692500,1692500,2768,"SELECT orders.customer_id +FROM orders +WHERE (orders.id < 86 OR (orders.customer_id IN (21, 71) OR orders.customer_id NOT BETWEEN 371 AND 436)) +ORDER BY orders.customer_id ASC;" +381,10354,10354,276,"SELECT t0.age AS c6869, t0.id +FROM users AS t0 CROSS JOIN markets AS t1 +WHERE t1.note != 'Old World';" +382,848,848,192,"SELECT t0.id AS c3548, t0.note, SUM(t0.id) AS c5991, COUNT(*) +FROM markets AS t0 +WHERE ((t0.note IN ('Fast lane', 'Fast lane', 'Old World') OR t0.region = 'AMERICA') OR t0.id NOT IN (1, 2, 3)) +GROUP BY t0.id, t0.note;" +383,14597700,14597700,11236,"SELECT customers.id, customers.region_id +FROM regions CROSS JOIN customers CROSS JOIN users +WHERE regions.id < 2;" +384,28390,28390,476,"SELECT markets.id, books.id, COUNT(markets.id), COUNT(markets.id) +FROM regions CROSS JOIN books CROSS JOIN markets +WHERE regions.id != 5 +GROUP BY markets.id, books.id;" +385,2432,2432,628,"SELECT t0.id AS c9611, t0.age AS c4657 +FROM users AS t0 +WHERE ((t0.id >= 3 OR t0.id IS NULL) AND t0.age IS NULL);" +386,1307748,1553776,1919,"SELECT markets.note, markets.region AS c394 +FROM markets LEFT JOIN orders ON markets.id = orders.id LEFT JOIN employees ON orders.id = employees.id;" +387,1811,3832,187,"SELECT employees.id AS c1393 +FROM employees LEFT JOIN books ON employees.id = books.id +WHERE ((employees.id IS NULL AND books.id IS NOT NULL) AND (books.price = 55 AND employees.department_id = 12));" +388,3650500,3650500,11216,"SELECT orders.customer_id AS c3285 +FROM orders CROSS JOIN departments;" +389,1307562,1553832,1997,"SELECT employees.id AS c6990 +FROM orders LEFT JOIN markets ON orders.id = markets.id JOIN employees ON orders.id = employees.id +WHERE markets.id < 3;" +390,113850,160080,1935,"SELECT markets.note AS c1438, customers.region_id, markets.id AS c7869 +FROM customers LEFT JOIN markets ON customers.id = markets.id CROSS JOIN regions;" +391,1306995,2251590,1312,"SELECT orders.customer_id AS c6961, departments.id +FROM departments LEFT JOIN orders ON departments.id = orders.id +GROUP BY orders.customer_id, departments.id +ORDER BY departments.id DESC;" +392,43248,62366,417,"SELECT employees.department_id AS c8803, SUM(users.id), SUM(employees.id) +FROM users CROSS JOIN employees LEFT JOIN markets ON employees.id = markets.id +WHERE ((markets.id >= 18 OR users.id = 4) OR employees.id >= 2) +GROUP BY employees.department_id +ORDER BY employees.department_id ASC;" +393,291050000,291050000,536042,"SELECT orders.customer_id, customers.id +FROM orders CROSS JOIN customers +WHERE (orders.id IS NOT NULL AND (orders.customer_id <= 190 AND customers.region_id IN (1, 5, 8, 9)));" +394,1832,1832,312,"SELECT t0.id +FROM employees AS t0 +WHERE t0.department_id NOT IN (6, 11, 32, 94);" +395,2390300,2390300,10589,"SELECT orders.id, orders.customer_id, books.price AS c2061 +FROM orders CROSS JOIN books;" +396,422,422,141,"SELECT markets.region, markets.id, markets.note +FROM markets +WHERE markets.region IS NULL;" +397,175500,175500,531,"SELECT t0.id, t0.region_id, SUM(t0.region_id) +FROM customers AS t0 +GROUP BY t0.id, t0.region_id;" +398,70776,401976,402,"SELECT regions.id, customers.id, customers.region_id +FROM regions JOIN customers ON regions.id = customers.id +WHERE (customers.id NOT BETWEEN 18 AND 150 OR regions.id IN (1, 9, 10, 63));" +399,1092,1092,165,"SELECT departments.id AS c2818, COUNT(departments.id), COUNT(departments.id) +FROM departments +WHERE departments.id <= 1 +GROUP BY departments.id +ORDER BY departments.id DESC;" diff --git a/research/datasets b/research/datasets new file mode 160000 index 0000000..e761ca3 --- /dev/null +++ b/research/datasets @@ -0,0 +1 @@ +Subproject commit e761ca32458a9d6640e5127a6c23b2cfd8e46118 diff --git a/research/docker-compose.yaml b/research/docker-compose.yaml new file mode 100644 index 0000000..51933a8 --- /dev/null +++ b/research/docker-compose.yaml @@ -0,0 +1,12 @@ +services: + mssql: + image: mcr.microsoft.com/mssql/server:2025-latest + container_name: research-mssql + environment: + ACCEPT_EULA: "Y" + MSSQL_SA_PASSWORD: "Password123!" + MSSQL_PID: Developer + ports: + - "1433:1433" + volumes: + - ./datasets:/datasets:ro diff --git a/research/dump_plan.py b/research/dump_plan.py new file mode 100644 index 0000000..4ccd847 --- /dev/null +++ b/research/dump_plan.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +"""Phase 0 helper: pretty-print an MS SQL Server execution plan XML.""" + +import sys +import xml.dom.minidom + +from ms_sql_server_extractor import MsSqlServerExtractor + +QUERY = sys.argv[1] if len(sys.argv) > 1 else "SELECT * FROM dbo.Titles WHERE isAdult = 1" + +extractor = MsSqlServerExtractor( + dataset="./datasets", + host="localhost", + port=1433, + user="sa", + password="Password123!", +) + +xml_str = extractor.extract(QUERY) +print(xml.dom.minidom.parseString(xml_str).toprettyxml(indent=" ")) diff --git a/research/extractor.py b/research/extractor.py new file mode 100644 index 0000000..769d85c --- /dev/null +++ b/research/extractor.py @@ -0,0 +1,16 @@ +from abc import ABC, abstractmethod + + +class PlanExtractor(ABC): + def __init__(self, dataset: str): + self.dataset = dataset + self.load_dataset() + + @abstractmethod + def load_dataset(self) -> None: + pass + + @abstractmethod + def extract(self, request) -> str: + pass + diff --git a/research/fuzz/__init__.py b/research/fuzz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/research/fuzz/cost_stats.py b/research/fuzz/cost_stats.py new file mode 100644 index 0000000..62ec851 --- /dev/null +++ b/research/fuzz/cost_stats.py @@ -0,0 +1,133 @@ +""" +Collect (plan cost, actual execution time) pairs over random queries. + +Generates random queries with the shared query generator, runs each through our +compiler with --stats, and records the optimizer's chosen-plan cost alongside +the measured execution wall time (microseconds, taken inside the process so it +excludes interpreter/process startup). To damp timing noise each query is run +several times and the median execution time is kept. + +The resulting CSV (seed, plan_cost, exec_us, rows, query) feeds +research/cost-on-random.ipynb, which checks whether cost tracks real time. + +Usage: + python -m research.fuzz.cost_stats \\ + --cli build-release/bin/sql \\ + --data-dir test/static/executor/test_data \\ + --count 500 \\ + --out research/cost-stats.csv +""" +from __future__ import annotations + +import argparse +import csv +import random +import re +import statistics +import subprocess +import sys +from itertools import count +from pathlib import Path + +from research.query_generator import DIALECTS, QueryGenerator, load_schema, render_query + +#STATS plan_cost=2074 naive_plan_cost=2074 exec_us=133 rows=17 +_STATS_RE = re.compile(r"STATS plan_cost=(-?\d+) naive_plan_cost=(-?\d+) exec_us=(-?\d+) rows=(\d+)") + + +def _run_ours(cli: str, data_dir: str, query: str, jit: bool) -> subprocess.CompletedProcess: + cmd = [cli, "--data-dir", data_dir, "--stats"] + if jit: + cmd.append("--jit") + return subprocess.run( + cmd, + input=query, + capture_output=True, + text=True, + timeout=60, + ) + + +def _parse_stats(stderr: str) -> tuple[int, int, int] | None: + """Return (plan_cost, exec_us, rows) from the CLI's STATS line, or None.""" + m = _STATS_RE.search(stderr) + if m is None: + return None + return int(m.group(1)), int(m.group(2)), int(m.group(3)) + + +def main() -> None: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--cli", required=True, help="Path to our sql binary") + ap.add_argument("--data-dir", required=True, help="CSV table directory") + ap.add_argument("--out", required=True, help="Output CSV path") + ap.add_argument("--start-seed", type=int, default=0) + ap.add_argument("--count", type=int, default=500, help="How many valid samples to collect") + ap.add_argument("--repeats", type=int, default=5, + help="Runs per query; the median exec time is kept") + ap.add_argument("--jit", action="store_true", help="Run our CLI with the JIT executor") + args = ap.parse_args() + + schema = load_schema(Path(args.data_dir)) + pg = DIALECTS["pg"] + + out_path = Path(args.out) + out_path.parent.mkdir(parents=True, exist_ok=True) + + collected = 0 + attempted = 0 + skipped = 0 + with out_path.open("w", newline="") as f: + writer = csv.writer(f) + writer.writerow(["seed", "plan_cost", "exec_us", "rows", "query"]) + + for seed in count(args.start_seed): + if collected >= args.count: + break + attempted += 1 + rng = random.Random(seed) + query = QueryGenerator(schema, rng).generate() + sql = render_query(query, pg) + ";" + + if attempted%100==0: + print(f"collected={collected} attempted={attempted} skipped={skipped}", + flush=True) + + cost: int | None = None + rows: int | None = None + samples: list[int] = [] + failed = False + for _ in range(args.repeats): + try: + proc = _run_ours(args.cli, args.data_dir, sql, args.jit) + except subprocess.TimeoutExpired: + failed = True + break + if proc.returncode != 0: + failed = True + break + parsed = _parse_stats(proc.stderr) + if parsed is None: + failed = True + break + cost, exec_us, rows = parsed + samples.append(exec_us) + + if failed or not samples: + skipped += 1 + continue + + exec_us = int(statistics.median(samples)) + writer.writerow([seed, cost, exec_us, rows, sql]) + f.flush() + collected += 1 + if collected % 25 == 0: + print(f"collected={collected} attempted={attempted} skipped={skipped}", + flush=True) + + print(f"done: collected={collected} attempted={attempted} skipped={skipped} -> {out_path}", + file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/research/fuzz/diff_fuzz.py b/research/fuzz/diff_fuzz.py new file mode 100644 index 0000000..2e06156 --- /dev/null +++ b/research/fuzz/diff_fuzz.py @@ -0,0 +1,211 @@ +""" +Differential fuzzer: generate a random SQL query, run it through our compiler +and MS SQL Server, stop on the first observable divergence. + +Compares result rows as multisets of tab-separated strings. Column names are +ignored (the two engines disagree on naming conventions), but column count +must match. + +Usage: + python -m research.fuzz.diff_fuzz \\ + --cli build/bin/sql \\ + --data-dir test/static/executor/test_data \\ + [--start-seed 0] +""" +from __future__ import annotations + +import argparse +import random +import subprocess +import sys +from itertools import count +from pathlib import Path + +from research.fuzz.mssql_runner import MsSqlRunner, RunResult +from research.query_generator import ( + DIALECTS, + Attr, + QueryGenerator, + SelectQuery, + load_schema, + render_query, +) + + +def _value_to_canonical(v) -> str: + if v is None: + return "NULL" + if isinstance(v, bool): + return "1" if v else "0" + return str(v) + + +def _mssql_canonical_rows(res: RunResult) -> list[str]: + return ["\t".join(_value_to_canonical(v) for v in row) for row in res.rows] + + +def _ours_canonical_rows(stdout: str) -> tuple[int, list[str]]: + """Parse the CLI's output. Returns (column_count, row_strings).""" + lines = stdout.splitlines() + if not lines: + return 0, [] + header = lines[0] + col_count = len(header.split("\t")) + return col_count, lines[1:] + + +def _run_ours(cli: str, data_dir: str, query: str, jit: bool) -> subprocess.CompletedProcess: + cmd = [cli, "--data-dir", data_dir] + if jit: + cmd.append("--jit") + return subprocess.run( + cmd, + input=query, + capture_output=True, + text=True, + timeout=30, + ) + + +def _typed(cell: str): + if cell == "NULL": + return None + try: + return int(cell) + except ValueError: + return cell + + +def _order_by_indices(query: SelectQuery) -> list[tuple[int, str]]: + """Map each ORDER BY key to its column index in the projection order.""" + out: list[tuple[int, str]] = [] + for key_attr, direction in query.order_by: + for i, t in enumerate(query.targets): + e = t.expr + if isinstance(e, Attr) and e.table == key_attr.table and e.column == key_attr.column: + out.append((i, direction)) + break + return out + + +def _check_order(query: SelectQuery, our_rows: list[str]) -> str | None: + """ + Verify the CLI's (order-preserved) output is monotonic in the ORDER BY keys. + NULL key values are skipped because the two engines disagree on NULL + ordering; this still catches gross ordering bugs without tie-break flakiness. + """ + keys = _order_by_indices(query) + if not keys: + return None + + prev: list | None = None + for row in our_rows: + cells = row.split("\t") + cur = [(_typed(cells[i]), direction) for i, direction in keys if i < len(cells)] + if prev is not None: + for (va, da), (vb, _) in zip(prev, cur): + if va == vb: + continue + if va is None or vb is None: + break + less = va < vb + if da == "desc": + less = not less + if not less: + return "ORDER BY ordering violated in our output" + break + prev = cur + return None + + +def _compare( + query: SelectQuery, + ours_proc: subprocess.CompletedProcess, + theirs: RunResult, +) -> str | None: + """Returns a diagnostic string on divergence, None on match.""" + if ours_proc.returncode != 0: + return f"our CLI exited {ours_proc.returncode}\nstderr: {ours_proc.stderr.strip()}" + if theirs.error is not None: + return f"MS SQL rejected the query: {theirs.error}" + + our_cols, our_rows = _ours_canonical_rows(ours_proc.stdout) + their_rows = _mssql_canonical_rows(theirs) + their_cols = len(theirs.columns) + + if our_cols != their_cols: + return f"column count: ours={our_cols} theirs={their_cols}" + + # Row correctness: compare as multisets regardless of ordering. + if sorted(our_rows) != sorted(their_rows): + return "row contents differ" + + # If the query asked for an order, additionally check our output respects it. + return _check_order(query, our_rows) + + +def main() -> None: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--cli", required=True, help="Path to our sql binary") + ap.add_argument("--data-dir", required=True, help="CSV table directory") + ap.add_argument("--start-seed", type=int, default=0) + ap.add_argument("--jit", action="store_true", help="Run our CLI with the JIT executor") + ap.add_argument("--mssql-host", default="localhost") + ap.add_argument("--mssql-port", type=int, default=1433) + ap.add_argument("--mssql-user", default="sa") + ap.add_argument("--mssql-password", default="Password123!") + ap.add_argument("--mssql-database", default="fuzz") + args = ap.parse_args() + + schema = load_schema(Path(args.data_dir)) + + mssql = MsSqlRunner( + host=args.mssql_host, + port=args.mssql_port, + user=args.mssql_user, + password=args.mssql_password, + database=args.mssql_database, + ) + print("Setting up MS SQL schema...", flush=True) + mssql.setup_schema(args.data_dir) + print("Schema ready. Starting fuzz loop.\n", flush=True) + + pg = DIALECTS["pg"] + ms = DIALECTS["mssql"] + + for seed in count(args.start_seed): + rng = random.Random(seed) + query = QueryGenerator(schema, rng).generate() + ours_sql = render_query(query, pg) + ";" + theirs_sql = render_query(query, ms) + ";" + + try: + ours_proc = _run_ours(args.cli, args.data_dir, ours_sql, args.jit) + except subprocess.TimeoutExpired: + print(f"TIMEOUT seed={seed}: our CLI timed out\n--- query (ours):\n{ours_sql}", flush=True) + continue + + theirs = mssql.run(theirs_sql) + + diag = _compare(query, ours_proc, theirs) + if diag is not None: + print(f"DIVERGENCE seed={seed}: {diag}") + print(f"\n--- query (ours):\n{ours_sql}") + print(f"\n--- query (mssql):\n{theirs_sql}") + print(f"\n--- ours stdout:\n{ours_proc.stdout}", end="") + if ours_proc.stderr: + print(f"\n--- ours stderr:\n{ours_proc.stderr}", end="") + print(f"\n--- mssql columns: {theirs.columns}") + print(f"--- mssql rows ({len(theirs.rows)}):") + for r in theirs.rows[:50]: + print(r) + if len(theirs.rows) > 50: + print(f"... ({len(theirs.rows) - 50} more)") + sys.exit(1) + + if seed % 25 == 0: + print(f"seed={seed} ok", flush=True) + + +if __name__ == "__main__": + main() diff --git a/research/fuzz/mssql_runner.py b/research/fuzz/mssql_runner.py new file mode 100644 index 0000000..312ab21 --- /dev/null +++ b/research/fuzz/mssql_runner.py @@ -0,0 +1,164 @@ +""" +Run queries against MS SQL Server for the differential fuzzer. + +Responsibilities: + - setup_schema(data_dir): drop+recreate the fuzzer schema, mirror every + *.csv in data_dir as a `dbo.(... INT NULL)` table, and bulk-load + rows. + - run(query): execute one SELECT and return columns/rows or a structured + error. + +The connection is reused across calls so the fuzz loop pays one TCP/login cost +per session, not per query. +""" +from __future__ import annotations + +import csv +import re +from dataclasses import dataclass +from pathlib import Path + +import pymssql + + +_BENCH_RE = re.compile(r"_\d+$") + + +@dataclass +class RunResult: + columns: list[str] # column names in projection order + rows: list[tuple] # values as returned by the driver + error: str | None = None # set when execution failed; columns/rows empty + + +class MsSqlRunner: + def __init__( + self, + host: str = "localhost", + port: int = 1433, + user: str = "sa", + password: str = "Password123!", + database: str = "fuzz", + ): + self._host = host + self._port = port + self._user = user + self._password = password + self._database = database + self._conn = None # opened lazily after the database exists + + def _connect(self, database: str): + return pymssql.connect( + server=self._host, + port=str(self._port), + user=self._user, + password=self._password, + database=database, + autocommit=True, + ) + + def _ensure_database(self) -> None: + with self._connect("master") as conn: + cur = conn.cursor() + cur.execute("SELECT COUNT(*) FROM sys.databases WHERE name=%s", (self._database,)) + if cur.fetchone()[0] == 0: + cur.execute(f"CREATE DATABASE [{self._database}]") + + def setup_schema(self, data_dir: str | Path) -> None: + """ + Drop everything under dbo and recreate one INT-NULL table per *.csv + in data_dir (skipping the benchmark-only ``_`` siblings — they + share the base table's schema). + """ + data_dir = Path(data_dir) + self._ensure_database() + self._conn = self._connect(self._database) + cur = self._conn.cursor() + + # Drop everything we own so reruns are idempotent. + cur.execute(""" + DECLARE @sql NVARCHAR(MAX) = N''; + SELECT @sql += 'DROP TABLE [dbo].[' + t.name + ']; ' + FROM sys.tables t + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE s.name = 'dbo'; + EXEC sp_executesql @sql; + """) + + for path in sorted(data_dir.glob("*.csv")): + if _BENCH_RE.search(path.stem): + continue + self._create_and_load(cur, path) + + _SQL_TYPE = {"int": "INT NULL", "string": "VARCHAR(255) NULL"} + + def _create_and_load(self, cur, path: Path) -> None: + with path.open() as f: + reader = csv.reader(f) + header = next(reader) + cols = [] + types = [] + for h in header: + name, type_ = (s.strip() for s in h.split(":")) + if type_ not in self._SQL_TYPE: + raise ValueError(f"{path}: unsupported column type {type_!r}") + cols.append(name) + types.append(type_) + + def conv(v: str, type_: str): + v = v.strip() + if v == "NULL": + return None + return int(v) if type_ == "int" else v + + rows = [tuple(conv(v, t) for v, t in zip(r, types)) for r in reader] + + col_defs = ", ".join(f"[{c}] {self._SQL_TYPE[t]}" for c, t in zip(cols, types)) + cur.execute(f"CREATE TABLE [dbo].[{path.stem}] ({col_defs})") + + if rows: + placeholders = ", ".join(["%s"] * len(cols)) + cur.executemany( + f"INSERT INTO [dbo].[{path.stem}] VALUES ({placeholders})", + rows, + ) + + for col, type_ in zip(cols, types): + if type_ == "int": + index_name = f"ix_{path.stem}_{col}" + cur.execute( + f"CREATE INDEX [{index_name}] ON [dbo].[{path.stem}] ([{col}])" + ) + + def run(self, query: str) -> RunResult: + if self._conn is None: + self._conn = self._connect(self._database) + cur = self._conn.cursor() + try: + cur.execute(query) + rows = cur.fetchall() + columns = [d[0] for d in cur.description] if cur.description else [] + return RunResult(columns=columns, rows=rows) + except pymssql.Error as e: + return RunResult(columns=[], rows=[], error=str(e)) + + def get_plan(self, query: str) -> str: + """Return the ShowPlanXML for `query`. Use OPTION (RECOMPILE) to inline literals.""" + if self._conn is None: + self._conn = self._connect(self._database) + cur = self._conn.cursor() + cur.execute("SET STATISTICS XML ON") + try: + cur.execute(query) + while cur.nextset(): + row = cur.fetchone() + if row and isinstance(row[0], str) and row[0].startswith(" None: + if self._conn is not None: + self._conn.close() + self._conn = None diff --git a/research/fuzz/reach_fuzz.py b/research/fuzz/reach_fuzz.py new file mode 100644 index 0000000..95a0470 --- /dev/null +++ b/research/fuzz/reach_fuzz.py @@ -0,0 +1,399 @@ +""" +Reachability fuzzer: generate a random SQL query, ask MS SQL Server for its +execution plan, convert that plan to the project's serialized format, then +invoke our CLI in ``--check-reachable`` mode to confirm that our exhaustive +search would have considered it. Stops at the first plan our optimizer cannot +reach, unless ``--collect-stats`` is used. + +The fuzzer only flags a divergence when the MS SQL plan converts cleanly but +our optimizer cannot reach it. Coverage gaps in the XML→s-expr converter +(unhandled physical operators or scalar shapes) are logged and skipped, since +they are converter limitations rather than optimizer bugs. + +ORDER BY is part of the comparison: MS SQL's plan keeps the ORDER BY Sort on +top, and we keep it too. The CLI propagates the query's ORDER BY as a required +sort property, so the exhaustive search generates the matching Sort enforcer on +the root group and the ordered plan is genuinely reachable (or not — which is +exactly the divergence we want to surface). + +Usage: + python -m research.fuzz.reach_fuzz \\ + --cli build/bin/sql \\ + --data-dir test/static/executor/test_data \\ + [--collect-stats N] \\ + [--start-seed 0] +""" +from __future__ import annotations + +import argparse +import os +import random +import re +import shutil +import subprocess +import sys +import tempfile +from itertools import count +from pathlib import Path + +from research.converter import convert as convert_plan +from research.fuzz.mssql_runner import MsSqlRunner +from research.query_generator import ( + Aggregate, + DIALECTS, + QueryGenerator, + SelectQuery, + load_schema, + render_query, +) + +DISTANCE_RE = re.compile(r"\bdistance=(\d+)\b") + + +def _make_reachability_data_dir(source_dir: Path, schema: "Schema", tmp_dir: Path) -> Path: + for path in source_dir.glob("*.csv"): + target = tmp_dir / path.name + try: + os.symlink(path.resolve(), target) + except OSError: + shutil.copy2(path, target) + + with (tmp_dir / "indexes.meta").open("w") as meta: + meta.write("# Generated by reach_fuzz.py for optimizer reachability checks.\n") + for table in sorted(schema.tables.values(), key=lambda t: t.name): + for col in table.columns: + if col.type == "int": + meta.write( + f"{table.name} {col.name} sorted {table.name}.{col.name}.sorted.idx\n" + ) + return tmp_dir + + +def _quote_alias(alias: str) -> str: + return '"' + alias.replace("\\", "\\\\").replace('"', '\\"') + '"' + + +def _render_attr(a) -> str: + return f"(attr {a.table} {a.column})" + + +def _render_agg(agg: Aggregate) -> str: + if agg.func == "COUNT" and agg.arg is None: + return "(COUNT *)" + return f"({agg.func} {_render_attr(agg.arg)})" + + +def _split_sexpr_args(inner: str) -> list[str]: + """Split the body of an s-expr into top-level atoms/groups, respecting + nested parens and double-quoted strings.""" + out: list[str] = [] + i, n = 0, len(inner) + while i < n: + while i < n and inner[i].isspace(): + i += 1 + if i >= n: + break + if inner[i] == "(": + depth, j = 0, i + while j < n: + c = inner[j] + if c == '"': + j += 1 + while j < n and inner[j] != '"': + j += 2 if inner[j] == "\\" else 1 + j += 1 + continue + if c == "(": + depth += 1 + elif c == ")": + depth -= 1 + if depth == 0: + j += 1 + break + j += 1 + out.append(inner[i:j]) + i = j + else: + j = i + while j < n and not inner[j].isspace(): + j += 1 + out.append(inner[i:j]) + i = j + return out + + +def _top_node(sexpr: str) -> tuple[str, list[str]]: + s = sexpr.strip() + args = _split_sexpr_args(s[1:-1]) + return args[0], args[1:] + + +def _make_projection(plan_sexpr: str, query: SelectQuery) -> str: + """Wrap `plan_sexpr` in the PhysicalProjection our visitor emits. + + For GROUP BY queries this sits above the HashAggregate the converter + produced: the C++ visitor replaces each aggregate target with a synthetic + attribute ``__aggN`` (empty table, counter in target order) and keeps the + HashAggregate's aggregate expressions in its ``aggs`` list.""" + exprs = [] + agg_counter = 0 + for t in query.targets: + if isinstance(t.expr, Aggregate): + # Synthetic aggregate-output attribute: empty table, serialized "-". + exprs.append(f"(attr - __agg{agg_counter})") + agg_counter += 1 + else: # plain column target + a = t.expr + exprs.append(f"(attr {a.table} {a.column})") + exprs_str = " ".join(exprs) + + # The visitor clears the alias list when no target is aliased; mirror that + # so the serialized shape matches (with vs without the (aliases ...) form). + if any(t.alias for t in query.targets): + aliases = " ".join(_quote_alias(t.alias) if t.alias else "-" for t in query.targets) + return f"(PhysicalProjection (exprs {exprs_str}) (aliases {aliases}) {plan_sexpr})" + return f"(PhysicalProjection (exprs {exprs_str}) {plan_sexpr})" + + +def _rebuild_aggregate(plan_sexpr: str, query: SelectQuery) -> str: + """Replace a converted HashAggregate's group_by/aggs with query-derived + ones, keeping only its converted source subtree. + + MS SQL augments aggregation with hidden helper aggregates (e.g. a COUNT_BIG + alongside every SUM, for the empty-set NULL semantics) and orders them by + its own internal column ids, so its agg list never matches ours. The + aggregates are pure query semantics both engines share, so we emit them from + the query in target order — the same order the C++ visitor's + CollectAggregates uses — and reuse only the real input subtree from MS.""" + head, args = _top_node(plan_sexpr) + if head != "HashAggregate" or len(args) != 3: + return plan_sexpr # unexpected shape; leave as-is (best effort) + child = args[2] + + group_by = " ".join(_render_attr(a) for a in query.group_by) + aggs = " ".join( + _render_agg(t.expr) for t in query.targets if isinstance(t.expr, Aggregate) + ) + return f"(HashAggregate (group_by {group_by}) (aggs {aggs}) {child})" + + +def _wrap_projection(plan_sexpr: str, query: SelectQuery) -> str: + """Insert the projection node the optimizer's search produces, keeping any + top-of-plan ORDER BY Sort. + + MS SQL places the ORDER BY Sort above the projection. Our optimizer reaches + that exact shape: with the query's ORDER BY propagated as a required sort + property (see IsPlanReachable), the search puts a Sort *enforcer* on the + root projection group. So we lift the converter's Sort off the top, nest the + synthesized projection beneath it, and put the Sort back — yielding + ``(Sort (keys ...) (PhysicalProjection ... ))``. The Sort keys pass + through verbatim from the converter; they match the enforcer's keys, which + the CLI derives from the same ORDER BY clause. + + For non-aggregate column targets the converter emits no projection of its + own (MS SQL just selects the columns), so the projection is always + synthesized here regardless of the Sort.""" + head, args = _top_node(plan_sexpr) + sort_keys = None + if head == "Sort" and len(args) == 2: + sort_keys, plan_sexpr = args[0], args[1] + if query.group_by: + plan_sexpr = _rebuild_aggregate(plan_sexpr, query) + projected = _make_projection(plan_sexpr, query) + if sort_keys is not None: + return f"(Sort {sort_keys} {projected})" + return projected + + +def _run_reach_check( + cli: str, data_dir: str, sql: str, plan_sexpr: str +) -> subprocess.CompletedProcess: + with tempfile.NamedTemporaryFile("w", suffix=".plan", delete=False) as f: + f.write(plan_sexpr) + plan_path = f.name + try: + # --data-dir lets the Sort enforcer validate ORDER BY keys against the + # group schema, matching how the real optimizer places enforcers. + return subprocess.run( + [cli, "--check-reachable", plan_path, "--data-dir", data_dir], + input=sql, + capture_output=True, + text=True, + timeout=60, + ) + finally: + Path(plan_path).unlink(missing_ok=True) + + +def _parse_distance(proc: subprocess.CompletedProcess) -> int | None: + match = DISTANCE_RE.search(proc.stdout) + if match is None: + return None + return int(match.group(1)) + + +def main() -> None: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--cli", required=True, help="Path to our sql binary") + ap.add_argument("--data-dir", required=True, help="CSV table directory (shared with MS SQL)") + ap.add_argument("--start-seed", type=int, default=0) + ap.add_argument( + "--collect-stats", + type=int, + metavar="N", + help=( + "Check exactly N seeds, keep going after reachability divergences, " + "and print skipped/ok/divergent totals plus average closest-plan distance" + ), + ) + ap.add_argument("--mssql-host", default="localhost") + ap.add_argument("--mssql-port", type=int, default=1433) + ap.add_argument("--mssql-user", default="sa") + ap.add_argument("--mssql-password", default="Password123!") + ap.add_argument("--mssql-database", default="fuzz") + args = ap.parse_args() + if args.collect_stats is not None and args.collect_stats < 0: + ap.error("--collect-stats must be non-negative") + + source_data_dir = Path(args.data_dir) + schema = load_schema(source_data_dir) + reach_tmp = tempfile.TemporaryDirectory(prefix="iu9-sql-reach-") + reach_data_dir = _make_reachability_data_dir( + source_data_dir, schema, Path(reach_tmp.name) + ) + + mssql = MsSqlRunner( + host=args.mssql_host, + port=args.mssql_port, + user=args.mssql_user, + password=args.mssql_password, + database=args.mssql_database, + ) + print("Setting up MS SQL schema...", flush=True) + mssql.setup_schema(args.data_dir) + print("Schema ready. Starting reachability fuzz loop.\n", flush=True) + + pg = DIALECTS["pg"] + ms = DIALECTS["mssql"] + + skipped_convert = 0 + skipped_converter_errors = 0 + skipped_mssql = 0 + ok = 0 + divergent = 0 + checked = 0 + distance_sum = 0 + distance_count = 0 + distance_missing = 0 + + seeds = ( + range(args.start_seed, args.start_seed + args.collect_stats) + if args.collect_stats is not None + else count(args.start_seed) + ) + collect_stats = args.collect_stats is not None + + try: + for seed in seeds: + checked += 1 + rng = random.Random(seed) + query = QueryGenerator(schema, rng).generate() + ours_sql = render_query(query, pg) + ";" + # OPTION (RECOMPILE) embeds literal values in the plan so the converter + # sees Const nodes instead of @P parameter references. + theirs_sql = render_query(query, ms) + " OPTION (RECOMPILE);" + + try: + plan_xml = mssql.get_plan(theirs_sql) + except Exception as e: + skipped_mssql += 1 + if seed % 25 == 0: + print(f"seed={seed} mssql refused: {e}", flush=True) + continue + + try: + converted = convert_plan(plan_xml) + except NotImplementedError as e: + skipped_convert += 1 + if "outer-reference index seek" in str(e): + print(f"seed={seed} converter skip: {e}", flush=True) + elif seed % 25 == 0: + print(f"seed={seed} converter skip: {e}", flush=True) + continue + except Exception as e: + print(f"seed={seed} converter error: {e}\n--- query:\n{theirs_sql}") + if collect_stats: + skipped_converter_errors += 1 + continue + sys.exit(2) + + target_plan = _wrap_projection(converted, query) + + try: + proc = _run_reach_check(args.cli, str(reach_data_dir), ours_sql, target_plan) + except subprocess.TimeoutExpired: + divergent += 1 + distance_missing += 1 + print(f"DIVERGENCE seed={seed}: reachability check timed out") + print(f"\n--- query (ours):\n{ours_sql}") + print(f"\n--- target plan:\n{target_plan}") + if collect_stats: + continue + sys.exit(1) + + distance = _parse_distance(proc) + if distance is None: + distance_missing += 1 + else: + distance_sum += distance + distance_count += 1 + + if proc.returncode == 0: + ok += 1 + if seed % 25 == 0: + print( + f"seed={seed} ok" + f" (skipped: {skipped_convert} convert, {skipped_mssql} mssql)", + flush=True, + ) + continue + + # Non-zero: either parse/optimizer error, or NOT REACHABLE. + divergent += 1 + print(f"DIVERGENCE seed={seed}: exit={proc.returncode}") + print(f"\n--- query (ours):\n{ours_sql}") + print(f"\n--- query (mssql):\n{theirs_sql}") + print(f"\n--- mssql plan (converted):\n{target_plan}") + if proc.stdout: + print(f"\n--- stdout:\n{proc.stdout}", end="") + if proc.stderr: + print(f"\n--- stderr:\n{proc.stderr}", end="") + if collect_stats: + continue + sys.exit(1) + finally: + if collect_stats: + skipped = skipped_convert + skipped_converter_errors + skipped_mssql + average_distance = distance_sum / distance_count if distance_count else None + print( + "\nReachability fuzz stats:" + f"\n seeds: {checked}/{args.collect_stats}" + f"\n ok: {ok}" + f"\n skipped: {skipped}" + f"\n converter: {skipped_convert}" + f"\n converter_errors: {skipped_converter_errors}" + f"\n mssql: {skipped_mssql}" + f"\n divergent: {divergent}" + + ( + f"\n average_distance: {average_distance:.3f}" + if average_distance is not None + else "\n average_distance: n/a" + ) + + f"\n distance_samples: {distance_count}" + + f"\n distance_missing: {distance_missing}", + flush=True, + ) + + +if __name__ == "__main__": + main() diff --git a/research/fuzz/test_diff_fuzz.py b/research/fuzz/test_diff_fuzz.py new file mode 100644 index 0000000..ca3af52 --- /dev/null +++ b/research/fuzz/test_diff_fuzz.py @@ -0,0 +1,24 @@ +from research.fuzz.diff_fuzz import _check_order +from research.query_generator import Attr, SelectQuery, TableScan, Target + + +def _query() -> SelectQuery: + users_id = Attr("users", "id") + regions_id = Attr("regions", "id") + return SelectQuery( + targets=[Target(users_id), Target(regions_id)], + from_=TableScan("users"), + order_by=[(users_id, "desc"), (regions_id, "desc")], + ) + + +def test_check_order_does_not_use_later_key_after_null_difference() -> None: + assert _check_order(_query(), ["10\t10", "1\t1", "NULL\t9"]) is None + + +def test_check_order_uses_later_key_when_earlier_null_keys_are_equal() -> None: + assert _check_order(_query(), ["NULL\t1", "NULL\t9"]) is not None + + +def test_check_order_rejects_non_null_ordering_violation() -> None: + assert _check_order(_query(), ["10\t10", "1\t1", "5\t5"]) is not None diff --git a/research/imdb-sqlserver b/research/imdb-sqlserver new file mode 160000 index 0000000..876c8f4 --- /dev/null +++ b/research/imdb-sqlserver @@ -0,0 +1 @@ +Subproject commit 876c8f4a2261c1dac8edf90813ddeec88e15d7f8 diff --git a/research/ms-server-plans/cross_join.xml b/research/ms-server-plans/cross_join.xml new file mode 100644 index 0000000..c623c97 --- /dev/null +++ b/research/ms-server-plans/cross_join.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/filter_and.xml b/research/ms-server-plans/filter_and.xml new file mode 100644 index 0000000..e2605cb --- /dev/null +++ b/research/ms-server-plans/filter_and.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/filter_eq.xml b/research/ms-server-plans/filter_eq.xml new file mode 100644 index 0000000..97f6e2a --- /dev/null +++ b/research/ms-server-plans/filter_eq.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/filter_gt.xml b/research/ms-server-plans/filter_gt.xml new file mode 100644 index 0000000..d7463d2 --- /dev/null +++ b/research/ms-server-plans/filter_gt.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/filter_lt.xml b/research/ms-server-plans/filter_lt.xml new file mode 100644 index 0000000..13ac111 --- /dev/null +++ b/research/ms-server-plans/filter_lt.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/filter_not.xml b/research/ms-server-plans/filter_not.xml new file mode 100644 index 0000000..a6c9cd8 --- /dev/null +++ b/research/ms-server-plans/filter_not.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/filter_or.xml b/research/ms-server-plans/filter_or.xml new file mode 100644 index 0000000..bc4fc89 --- /dev/null +++ b/research/ms-server-plans/filter_or.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/fuzz/group_by.xml b/research/ms-server-plans/fuzz/group_by.xml new file mode 100644 index 0000000..d180a60 --- /dev/null +++ b/research/ms-server-plans/fuzz/group_by.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/research/ms-server-plans/fuzz/group_order.xml b/research/ms-server-plans/fuzz/group_order.xml new file mode 100644 index 0000000..2bbcbea --- /dev/null +++ b/research/ms-server-plans/fuzz/group_order.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/research/ms-server-plans/fuzz/in_filter.xml b/research/ms-server-plans/fuzz/in_filter.xml new file mode 100644 index 0000000..228c3c6 --- /dev/null +++ b/research/ms-server-plans/fuzz/in_filter.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/research/ms-server-plans/fuzz/order_by.xml b/research/ms-server-plans/fuzz/order_by.xml new file mode 100644 index 0000000..183c84c --- /dev/null +++ b/research/ms-server-plans/fuzz/order_by.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/research/ms-server-plans/fuzz/string_filter.xml b/research/ms-server-plans/fuzz/string_filter.xml new file mode 100644 index 0000000..13e48d2 --- /dev/null +++ b/research/ms-server-plans/fuzz/string_filter.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/research/ms-server-plans/inner_join.xml b/research/ms-server-plans/inner_join.xml new file mode 100644 index 0000000..aa4ef91 --- /dev/null +++ b/research/ms-server-plans/inner_join.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/join_compound_predicate.xml b/research/ms-server-plans/join_compound_predicate.xml new file mode 100644 index 0000000..4b7078f --- /dev/null +++ b/research/ms-server-plans/join_compound_predicate.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/join_with_filter.xml b/research/ms-server-plans/join_with_filter.xml new file mode 100644 index 0000000..7657b97 --- /dev/null +++ b/research/ms-server-plans/join_with_filter.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/left_join.xml b/research/ms-server-plans/left_join.xml new file mode 100644 index 0000000..2ba5d6b --- /dev/null +++ b/research/ms-server-plans/left_join.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-server-plans/seq_scan.xml b/research/ms-server-plans/seq_scan.xml new file mode 100644 index 0000000..42aeb63 --- /dev/null +++ b/research/ms-server-plans/seq_scan.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/research/ms-sql-rules-list.txt b/research/ms-sql-rules-list.txt new file mode 100644 index 0000000..348588b --- /dev/null +++ b/research/ms-sql-rules-list.txt @@ -0,0 +1,448 @@ +JNtoNL +LOJNtoNL +LSJNtoNL +LASJNtoNL +JNtoSM +FOJNtoSM +LOJNtoSM +ROJNtoSM +LSJNtoSM +RSJNtoSM +LASJNtoSM +RASJNtoSM +IdxJNtoSM +FOJnoneqToSM +JNtoHS +FOJNtoHS +LOJNtoHS +ROJNtoHS +LSJNtoHS +RSJNtoHS +LASJNtoHS +RASJNtoHS +IdxJNtoHS +PSJNtoHS +HJwBMtoHS +JoinCommute +JoinSwitch +JoinLinearize +JoinLeftAssociate +JoinRightAssociate +JoinExchange +ColocatedJoin +StarJoinToIdxStrategy +StarSelJoinToIdxStrategy +StarJoinToHashJoinsWithBitmap +StarSelJoinToHashJoinsWithBitmap +GbTopToGbAgg +GbTopAfterJoin +GenGbApplySimple +GenKeyBatch +GenGbApplyAgg +GbApplyAfterJoin +CommFOJN +CommLOJN +CommLSJN +CommLASJN +CommROJN +CommRSJN +CommRASJN +CommPSJN +JoinOJSwitch +OJJoinSwitch +SelOJJoinSwitch +OJOJSwitch +SelOJOJSwitch +ReorderLOJN +LSJNtoDistOnJN +LSJNtoJNonDist +SubLSJNtoJNonDist +LASJNtoLASJNonDist +LSJOnLclDist +LASJOnLclDist +LSJIsNullToDist +LASJIsNullToDist +FOJNtoLSJNandLASJN +DiscardLSJN +DiscardRSJN +PullJoinAboveLSJN +PullJoinAboveLASJN +PullJoinAboveApply +PullJoinAboveSelApply +JNtoJNSELNotNull +JNonPrjLeft +JNonPrjRight +RedundantLOJN +RedundantROJN +RedundantApplyOJ +SimplifyJoinWithCTG +JoinWithCTGToSel +ExpandDistinctGbAgg +ReduceGbAgg +GbAggWithNoReqdCol +NormalizeGbAgg +NormalizeTop +GbAggOnPrj +GbAggOnRestrRemap +SelOnGbAgg +SelOnSeqPrj +GbAggToPrj +GbAggSmpfyEmptyT +ReduceGbAggExpr +GbAggToStrm +GbAggToHS +GbAggToSort +GbAggToUde +GbAggAfterJoin +GbAggAfterJoinSel +GbAggAfterLOJ +GbAggBeforeJoin +GbAggUnderTopBeforeJoin +GbAggUnderTopJoinBeforeJoin +GbAggBeforeLOJ +ScalarGbAggToTop +GbAggToConstScanOrTop +GbAggJNtoLSJN +GbAggIFFPredicateUnderJoin +GenLGAgg +GenLGTop +ReduceForDistinctAggs +LocalAggBelowJoin +LocalAggBelowPrjJoin +LocalAggBelowLOJ +LocalAggBelowFOJ +PushLocalAgg +LocalAggBelowUniAll +GbAggBelowUniAll +JoinOnGbAgg +RmtEmptyVectorAgg +DiscardPrj +ReducePrjExpr +SelOnPrj +TOpOnPrj +PrjOnConstTbl +AddCompSca +AddCCPrjToGet +AddCCPrjToInsert +AddCCPrjToUpdate +AddCCPrjToDelete +SelPredNorm +JoinPredNorm +ImpliedPredInnerAndAllLeftJn +SimplifyMultiColumnJoinPred +EnforceSort +OrderByToNOP +SELonUNI +SELonUNIA +ReduceUnionCol +ReduceUnionAllCol +ReduceSwitchUnionCol +UNIAtoCON +SWUtoSWU +UNIAtoMERGE +UNItoDISonUNIA +UNItoMERGE +UNItoHASH +CollapseUNI +CollapseUNIA +JNonUNIA +LOJonUNIA +LSJonUNIA +LASJonUNIA +CorrelateJNonUNIA +JNonUNIAUNIA +LOJonUNIAUNIA +LSJonUNIAUNIA +LASJonUNIAUNIA +UNIAReorderInputs +SplitUniIntoDistCompat +SelOnRestrRemap +SELonJN +CleanupNaryJoin +SELonNaryJoin +SELonLOJ +SELonROJ +SELonSJ +SELonApply +SimplifyLOJN +SimplifyROJN +SimplifyFOJN +LOJToLASJ +ROJToLASJ +SelOnTFP +ApplyToNL +ApplyCnstToNL +RemoveSubqInSel +RemoveSubqInPrj +RemoveSubqInGbAgg +RemoveSubqInRollup +RemoveSubqInCube +RemoveSubqInJN +RemoveSubqInLOJN +RemoveSubqInLSJN +RemoveSubqInLASJN +RemoveSubqInFOJN +RemoveSubqInTop +RemoveSubqInSwitchUN +RemoveSubqInTFP +RemoveSubqInTVF +RemoveSubqInSTVF +FOJNtoUnionAll +ApplyHandler +SelApplyHandler +PrjApplyHandler +ApplyUAtoUniSJ +LSJNtoApply +LASJNtoApply +LOJNtoApply +JNtoApplySTVF +LOJPrjGetToApply +LOJSelPrjGetToApply +SplitLASJN +SplitApplyLASJN +LSJNtoAgg +LASJNtoAgg +PushApplyBelowJN +PullApplyOverJoin +PullSelApplyOverJoin +PullLSJOverJoin +PullLASJOverJoin +PullColumnRestrPrjOverJoin +PullColumnRestrPrjOverJoinSel +LASJDistincttoLASJ +LSJDistincttoLSJ +PushTFPBelowJoin +GetToScan +GetToAssert +SelectToFilter +ConstGetToConstScan +ImplRestrRemap +ProjectToComputeScalar +ReduceSequenceProjectExpr +ImplementSequenceProject +ImplementLocalSequenceProject +ImplementGlobalSequenceProject +SplitSequenceProject +EnforceBatch +EnforceRow +FetchToApply +SelToIdxStrategy +SelSTVFToSTVF +SelSeqPrjToTop +SelSeqPrjToAnyAgg +SelToTrivialFilter +GetToTrivialScan +SelPrjGetToTrivialScan +GetIdxToRng +GetToIdxScan +SelResToFilter +AppIdxToApp +SelIdxToRng +CrsFtchToUnionAll +JNtoIdxLookup +LeftSideJNtoIdxLookup +PSJNtoIdxLookup +WCJNonSELtoIdxLookup +PSJNonSELtoIdxLookup +SelToIndexOnTheFly +JoinToIndexOnTheFly +SelIterToIdxOnFly +JoinIterToIdxOnFly +SelOnFetch +JNFetchGetToApply +JNSelFetchGetToApply +RemoveViewAnchor +AnyOnEmptyTrivial +RmtUpdateOnEmpty +RmtDeleteOnEmpty +RmtInsertOnEmpty +SelectOnEmpty +GbAggOnEmpty +RollupOnEmpty +CubeOnEmpty +PrjOnEmpty +SpoolOnEmpty +UpdateOnEmpty +DeleteOnEmpty +InsertOnEmpty +ApplyOnEmpty +NaryJoinOnEmpty +IJOnEmpty +LSJOnEmpty +RSJOnEmpty +LASJOnEmpty +RASJOnEmpty +LOJOnEmpty +ROJOnEmpty +FOJOnEmpty +LASJOnEmptyRight +RASJOnEmptyLeft +LOJOnEmptyRight +ROJOnEmptyLeft +FOJOnOneEmpty +SELonTrue +TopOnEmpty +DiscardTop +EmptyIterator +SelToLSJ +SelToLASJ +SelInToJoinGb +ImplementFastFwd +ImplementFastFwdAsInsert +EnforceHPandAccCard +InsertSpoolForPlanForcing +BuildSpool +AddNOPToCSRootSpool +ScrollLockAsFetch +ScrollLockAsDynamic +SpoolGetToGet +UpdateToStreamUpdate +DeleteToStreamUpdate +InsertToStreamUpdate +PutToPhysicalPut +ExpandUpdateCons +ExpandDeleteCons +ExpandInsertCons +ExpandUpdateImplicitAssignments +ExpandInsertImplicitAssignments +ExpandInsertToPut +ExpandPtnViewIns +ExpandPtnViewUpd +ExpandPtnViewDel +AssertToStreamCheck +ExpandVerifyCnst +BuildSplit +BuildCollapse +BuildSequence +IndexCreate +IndexCreateOnPrj +SplitDictionaryBuild +ExpandInsteadOfTriggerIns +ExpandInsteadOfTriggerUpd +ExpandInsteadOfTriggerDel +ColocatedInsert +ResumableIndexBuild +MatchAggGraphOp +MatchJoin +MatchPrjJoin +MatchSelectGet +MatchPrjSelectGet +MatchGet +MatchPrjGet +MatchGbNAryJoin +MatchGbJoin +CubeSimpleAgg +RollupSimpleAgg +CubeToRollup +CubeNaive +CubeGbAgg +CubeNoGbAgg +ReduceRollupExpr +ReduceCubeExpr +RollupGbAgg +RollupToStrm +BuildTop +BuildGlobalTop +BuildLocalTop +TopRowcountGbPrj +BuildGbTop +BuildGbApply +BuildKeyBatchApply +BuildUde +BuildBsl +BuildTFP +IJtoIJSEL +LSJtoLSJSEL +RSJtoRSJSEL +LASJtoLASJSEL +RASJtoRASJSEL +LOJtoLOJSEL +ROJtoROJSEL +ApplyToSM +ApplyCnstToSM +PushImpliedIsNotNull +ExpandNAryJoinNoSnowflake +ExpandNAryJoinWithSnowflake +GatherDistrCompatJoins +ExpandNAryJoinToBatchSnowflake +ExpandPivot +ExpandUnpivot +ExpandPivotLOJ +SelOnPivot +SpoolOnIterator +IterateToDepthFirst +RecRefToConstTble +IteratorToAnchor +SelOnIterator +LogTVFToPhyTVF +LogSTVFToPhySTVF +STVFOrderCheck +JoinToStarJoin +JoinOnStarJoin +JoinOnSelStarJoin +GetToRmtScan +SpoolOverRmtScan +BuildRmtQuery +SpoolOverRmtQuery +LogRmtQToPhyRmtQ +RmtInsertToRmtModify +RmtUpdateToRmtModify +RmtDeleteToRmtModify +ParametrizeRmtUpdate +ParametrizeRmtUpdateNoPrj +ParametrizeRmtDelete +ParametrizeRmtDeleteNoPrj +GetIdxToRmtRange +SelIdxToRmtRange +FtchToRmtFtch +RemoteHint +JoinToApply +NAryJoinToApply +SplitSelects +CollapseSelects +TopOnRmtGet +TopOnRmtQuery +OffsetOnRmtGet +OffsetOnRmtQuery +SpatialIntersectFilterOverGridIndex +JoinExtToPrimaryFilter +JoinExtToPrimaryFilterOverSelect +SpatialJointoApply +SpatialNearestNeighbor +SelSTVFToIdxOnFly +SelSTVFToSTVFExp +NormalizeSequenceProject +BuildNOP +BuildExternalComputation +SpoolOverExtStrTable +RefIntegrityMaintainer +CollapseIdenticalScalarSubquery +GetToExtExtractScan +SelOnChoose +GraphIterateToDepthFirst +GraphIterateWithOneJoin +GenLGSequenceProject +LocalGlobalCube +SinglePassCube +SplitSemiApplyUnionAll +ExpCubeToRollup +PutSemijoinUnderGbApply +GraphIterateOnGraphIterator +GbAggSplitToRanges +SelOnGbAggSplitToRanges +BuildRemotePut +EnforceDistribution +ImplementDistribution +BuildDataExportPut +SelectToFilteredExternalGet +LogicalToPhysicalSort +RedundantCseSpool +SelOnPrjGet +JsonFilterToJsonIndex +SpoolDecorrelation +BuildANN +SimplifyApplyOJ +TightenRestrictsUnderChoose +ApplyOpenjsonToJsonIndex +ApplyOpenjsonFilterAboveToJsonIndex +BuildBitmapProcessor diff --git a/research/ms_sql_server_extractor.py b/research/ms_sql_server_extractor.py new file mode 100644 index 0000000..18771b7 --- /dev/null +++ b/research/ms_sql_server_extractor.py @@ -0,0 +1,190 @@ +import re +from pathlib import Path + +import pymssql + +from extractor import PlanExtractor + +_SUBMODULE = Path(__file__).parent / "imdb-sqlserver" + +# (tsv filename, raw staging table, column count) +_RAW_FILES = [ + ("name.basics.tsv", "[Raw].[name.basics.tsv.gz]", 6), + ("title.basics.tsv", "[Raw].[title.basics.tsv.gz]", 9), + ("title.akas.tsv", "[Raw].[title.akas.tsv.gz]", 8), + ("title.crew.tsv", "[Raw].[title.crew.tsv.gz]", 3), + ("title.episode.tsv", "[Raw].[title.episode.tsv.gz]", 4), + ("title.principals.tsv", "[Raw].[title.principals.tsv.gz]", 6), + ("title.ratings.tsv", "[Raw].[title.ratings.tsv.gz]", 3), +] + + +def _run_sql_file(cursor, path: Path) -> None: + sql = path.read_text(encoding="utf-8") + for stmt in re.split(r'^\s*GO\s*$', sql, flags=re.MULTILINE | re.IGNORECASE): + stmt = stmt.strip() + if stmt: + cursor.execute(stmt) + + +def _drop_raw_pks(cursor) -> None: + cursor.execute(""" + SELECT kc.name, t.name + FROM sys.key_constraints kc + JOIN sys.tables t ON t.object_id = kc.parent_object_id + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE s.name = 'Raw' AND kc.type = 'PK' + """) + rows = cursor.fetchall() + for pk_name, table_name in rows: + cursor.execute(f"ALTER TABLE [Raw].[{table_name}] DROP CONSTRAINT [{pk_name}]") + + +def _get_raw_columns(cursor, raw_table_name: str) -> list[str]: + cursor.execute(""" + SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = 'Raw' AND TABLE_NAME = %s + ORDER BY ORDINAL_POSITION + """, (raw_table_name,)) + return [row[0] for row in cursor.fetchall()] + + +def _load_raw_table(cursor, tsv_path: Path, raw_table: str, n_cols: int, server_path: str, max_rows: int | None = None) -> None: + if not tsv_path.exists(): + print(f" skipping {tsv_path.name} (not found)") + return + + # raw_table looks like [Raw].[name.basics.tsv.gz] — extract just the table name + raw_table_name = raw_table[len("[Raw].["):-1] + stage_table = f"[Stage].[{raw_table_name}]" + + col_defs = ", ".join(f"c{i} varchar(max) NULL" for i in range(1, n_cols + 1)) + cursor.execute(f"CREATE TABLE {stage_table} ({col_defs})") + + lastrow_clause = f"LASTROW = {max_rows + 1}," if max_rows is not None else "" + cursor.execute(f""" + BULK INSERT {stage_table} + FROM '{server_path}' + WITH ( + FIRSTROW = 2, + {lastrow_clause} + FIELDTERMINATOR = '\\t', + ROWTERMINATOR = '0x0a', + TABLOCK + ) + """) + + col_names = _get_raw_columns(cursor, raw_table_name) + raw_cols = ", ".join(f"[{c}]" for c in col_names) + nullif_sel = ", ".join(f"NULLIF(c{i}, '\\N')" for i in range(1, n_cols + 1)) + + cursor.execute(f""" + INSERT INTO {raw_table} WITH (TABLOCK) ({raw_cols}) + SELECT {nullif_sel} + FROM {stage_table} + """) + + cursor.execute(f"DROP TABLE {stage_table}") + + +class MsSqlServerExtractor(PlanExtractor): + def __init__( + self, + dataset: str, + host: str, + port: int, + user: str, + password: str, + server_dataset: str = "/datasets/mysql-data/imdb", + max_rows: int | None = None, + ): + self._host = host + self._port = port + self._user = user + self._password = password + self._server_dataset = server_dataset.rstrip("/") + self._max_rows = max_rows + super().__init__(dataset) + + def _conn(self, database="master"): + c = pymssql.connect( + server=self._host, + port=str(self._port), + user=self._user, + password=self._password, + database=database, + autocommit=True, + ) + return c + + def _is_dataset_loaded(self) -> bool: + with self._conn("master") as conn: + cur = conn.cursor() + cur.execute("SELECT COUNT(*) FROM sys.databases WHERE name=N'imdb'") + if not cur.fetchone()[0]: + return False + with self._conn("imdb") as conn: + cur = conn.cursor() + cur.execute(""" + SELECT COUNT(*) FROM sys.tables t + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE s.name = 'dbo' + """) + return cur.fetchone()[0] > 0 + + def load_dataset(self) -> None: + if self._is_dataset_loaded(): + print("Dataset already loaded, skipping.") + return + + dataset_dir = Path(self.dataset) + + print("Recreating database...") + with self._conn("master") as conn: + cur = conn.cursor() + cur.execute("SELECT COUNT(*) FROM sys.databases WHERE name=N'imdb'") + row = cur.fetchone() + if row and row[0]: + cur.execute("ALTER DATABASE imdb SET SINGLE_USER WITH ROLLBACK IMMEDIATE") + cur.execute("DROP DATABASE imdb") + cur.execute("CREATE DATABASE imdb") + + with self._conn("imdb") as conn: + cur = conn.cursor() + + print("Creating schema and tables...") + _run_sql_file(cur, _SUBMODULE / "Create IMDB-schema.sql") + + cur.execute("CREATE SCHEMA [Stage]") + _drop_raw_pks(cur) + + for tsv_name, raw_table, n_cols in _RAW_FILES: + print(f"Loading {tsv_name}...") + server_path = f"{self._server_dataset}/{tsv_name}" + _load_raw_table(cur, dataset_dir / tsv_name, raw_table, n_cols, server_path, self._max_rows) + + cur.execute("DROP SCHEMA [Stage]") + + print("Transforming into relational tables...") + if self._max_rows is not None: + cur.execute(""" + DECLARE @sql NVARCHAR(MAX) = ''; + SELECT @sql += 'ALTER TABLE [dbo].[' + t.name + '] NOCHECK CONSTRAINT ALL; ' + FROM sys.tables t JOIN sys.schemas s ON s.schema_id = t.schema_id WHERE s.name = 'dbo'; + EXEC(@sql) + """) + _run_sql_file(cur, _SUBMODULE / "Load IMDB relational tables.sql") + + print("Dataset loaded.") + + def extract(self, request: str) -> str: + with self._conn("imdb") as conn: + cur = conn.cursor() + cur.execute("SET STATISTICS XML ON") + cur.execute(request) + while cur.nextset(): + row = cur.fetchone() + if row and isinstance(row[0], str) and row[0].startswith(" sampled actual values (typed) + + +@dataclass +class Schema: + tables: dict[str, TableSchema] + + +def load_schema(csv_dir: Path, max_sample: int = 200) -> Schema: + """ + Read all *.csv files from csv_dir. + Files whose stem ends in _ (e.g. departments_1000) are skipped — + they share a schema with the base table and are only used for benchmarks. + """ + tables: dict[str, TableSchema] = {} + for path in sorted(csv_dir.glob("*.csv")): + if re.search(r"_\d+$", path.stem): + continue + with path.open() as f: + reader = csv.reader(f) + header_row = next(reader) + columns = [] + for h in header_row: + col_name, col_type = h.strip().split(":") + columns.append(Column(col_name.strip(), col_type.strip())) + + by_name = {c.name: c for c in columns} + sample_values: dict[str, list] = {c.name: [] for c in columns} + for i, row in enumerate(reader): + if i >= max_sample: + break + for col, val in zip(columns, row): + v = val.strip() + if v == "NULL": + continue + if by_name[col.name].type == "int": + try: + sample_values[col.name].append(int(v)) + except ValueError: + pass + else: + sample_values[col.name].append(v) + + tables[path.stem] = TableSchema(path.stem, columns, sample_values) + return Schema(tables) + + +# --------------------------------------------------------------------------- +# Query IR — only constructs that the C++ parser accepts +# --------------------------------------------------------------------------- + +@dataclass +class Attr: + table: str # visible name (alias if the scan is aliased, else table name) + column: str + + +@dataclass +class IntLit: + value: int + + +@dataclass +class StrLit: + value: str + + +@dataclass +class NullLit: + pass + + +@dataclass +class BoolLit: + value: bool + + +@dataclass +class BinaryExpr: + # Comparison: "=", "!=", "<", ">", "<=", ">=" + # Logical: "and", "or" + # Arithmetic: "+", "-", "*" + op: str + lhs: "Expr" + rhs: "Expr" + + +@dataclass +class UnaryExpr: + op: str # "not" | "uminus" + child: "Expr" + + +@dataclass +class IsNullExpr: + child: "Expr" + negated: bool # False -> IS NULL, True -> IS NOT NULL + + +@dataclass +class InExpr: + attr: Attr + values: list["Expr"] # IntLit / StrLit, emitted sorted ascending + negated: bool # False -> IN, True -> NOT IN + + +@dataclass +class BetweenExpr: + attr: Attr # int columns only + lo: "Expr" + hi: "Expr" + negated: bool # False -> BETWEEN, True -> NOT BETWEEN + + +@dataclass +class Aggregate: + func: str # "SUM" | "COUNT" + arg: "Expr | None" # None -> COUNT(*) + + +Expr = ( + Attr | IntLit | StrLit | NullLit | BoolLit | BinaryExpr | UnaryExpr + | IsNullExpr | InExpr | BetweenExpr | Aggregate +) + + +@dataclass +class TableScan: + name: str # real table name + alias: str | None = None + + +@dataclass +class ExplicitJoin: + join_type: str # "INNER" | "LEFT" | "RIGHT" | "FULL" + lhs: "TableRef" + rhs: "TableRef" + on: Expr + + +@dataclass +class CrossJoin: + lhs: "TableRef" + rhs: "TableRef" + + +TableRef = TableScan | ExplicitJoin | CrossJoin + + +# A projected output: an expression with an optional `AS alias`. +@dataclass +class Target: + expr: Expr + alias: str | None = None + + +@dataclass +class SelectQuery: + targets: list[Target] + from_: TableRef + where: Expr | None = None + group_by: list[Attr] = field(default_factory=list) + order_by: list[tuple[Attr, str]] = field(default_factory=list) # (key, "asc"|"desc") + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _scan_bindings(ref: TableRef) -> list[tuple[str, str]]: + """List of (visible_name, real_table) for every scan in the ref tree.""" + if isinstance(ref, TableScan): + return [(ref.alias or ref.name, ref.name)] + return _scan_bindings(ref.lhs) + _scan_bindings(ref.rhs) + + +# --------------------------------------------------------------------------- +# Random query builder +# --------------------------------------------------------------------------- + +_INT_COMPARISON_OPS = ["=", "!=", "<", ">", "<=", ">="] +_STR_COMPARISON_OPS = ["=", "!="] +_LOGICAL_OPS = ["and", "or"] +_JOIN_TYPES = ["INNER", "LEFT", "RIGHT", "FULL"] + + +class QueryGenerator: + def __init__(self, schema: Schema, rng: random.Random): + self.schema = schema + self.rng = rng + # Per-query map: visible table name -> real table name. + self._bindings: dict[str, str] = {} + + # -- attribute / type helpers ------------------------------------------ + + def _real_table(self, visible: str) -> str: + return self._bindings[visible] + + def _column_type(self, attr: Attr) -> str: + real = self._real_table(attr.table) + for c in self.schema.tables[real].columns: + if c.name == attr.column: + return c.type + return "int" + + def _samples(self, attr: Attr) -> list: + real = self._real_table(attr.table) + return self.schema.tables[real].sample_values.get(attr.column, []) + + def _available_attrs(self) -> list[Attr]: + attrs = [] + for visible, real in self._bindings.items(): + for col in self.schema.tables[real].columns: + attrs.append(Attr(visible, col.name)) + return attrs + + # -- top-level --------------------------------------------------------- + + def generate(self) -> SelectQuery: + from_ = self._gen_from() + self._bindings = dict(_scan_bindings(from_)) + attrs = self._available_attrs() + + where = self._gen_predicate(attrs) if self.rng.random() < 0.6 else None + + if self.rng.random() < 0.3 and attrs: + return self._gen_aggregated(from_, attrs, where) + + n_targets = self.rng.randint(1, min(3, len(attrs))) + chosen = self.rng.sample(attrs, n_targets) + targets = [Target(a, self._maybe_alias()) for a in chosen] + order_by = self._gen_order_by(chosen) + return SelectQuery(targets, from_, where, [], order_by) + + def _gen_aggregated( + self, from_: TableRef, attrs: list[Attr], where: Expr | None + ) -> SelectQuery: + n_group = self.rng.randint(1, min(2, len(attrs))) + group_by = self.rng.sample(attrs, n_group) + + # Every non-aggregate target must be a group-by column (MS SQL rule). + targets = [Target(a, self._maybe_alias()) for a in group_by] + + int_attrs = [a for a in attrs if self._column_type(a) == "int"] + for _ in range(self.rng.randint(0, 2)): + agg = self._gen_aggregate(int_attrs) + targets.append(Target(agg, self._maybe_alias())) + + order_by = self._gen_order_by(group_by) + return SelectQuery(targets, from_, where, group_by, order_by) + + def _gen_aggregate(self, int_attrs: list[Attr]) -> Aggregate: + choice = self.rng.random() + if choice < 0.34 and int_attrs: + return Aggregate("SUM", self.rng.choice(int_attrs)) + if choice < 0.67: + return Aggregate("COUNT", None) # COUNT(*) + return Aggregate("COUNT", self.rng.choice(int_attrs) if int_attrs else None) + + def _maybe_alias(self) -> str | None: + if self.rng.random() < 0.3: + return f"c{self.rng.randint(0, 9999)}" + return None + + def _gen_order_by(self, output_attrs: list[Attr]) -> list[tuple[Attr, str]]: + # ORDER BY keys restricted to projected output columns (C++ allows only + # column refs there). + if not output_attrs or self.rng.random() >= 0.3: + return [] + n = self.rng.randint(1, len(output_attrs)) + keys = self.rng.sample(output_attrs, n) + return [(k, self.rng.choice(["asc", "desc"])) for k in keys] + + # -- FROM -------------------------------------------------------------- + + def _gen_from(self) -> TableRef: + names = list(self.schema.tables.keys()) + n = self.rng.randint(1, min(3, len(names))) + chosen = self.rng.sample(names, n) + use_alias = self.rng.random() < 0.4 + + def make_scan(table: str, idx: int) -> TableScan: + return TableScan(table, f"t{idx}" if use_alias else None) + + # Track visible name -> real table while building so joins can pick + # a valid lhs binding. + scans: list[TableScan] = [make_scan(chosen[0], 0)] + ref: TableRef = scans[0] + + for i, table_name in enumerate(chosen[1:], start=1): + rhs_scan = make_scan(table_name, i) + scans.append(rhs_scan) + + rhs_cols = {c.name for c in self.schema.tables[table_name].columns} + # Candidate lhs scans that share a column name with rhs. + candidates = [ + (s, c.name) + for s in scans[:-1] + for c in self.schema.tables[s.name].columns + if c.name in rhs_cols + ] + + if candidates and self.rng.random() < 0.7: + lhs_scan, col = self.rng.choice(candidates) + lhs_vis = lhs_scan.alias or lhs_scan.name + rhs_vis = rhs_scan.alias or rhs_scan.name + on: Expr = BinaryExpr("=", Attr(lhs_vis, col), Attr(rhs_vis, col)) + jtype = self.rng.choice(_JOIN_TYPES) + ref = ExplicitJoin(jtype, ref, rhs_scan, on) + else: + ref = CrossJoin(ref, rhs_scan) + + return ref + + # -- WHERE ------------------------------------------------------------- + + def _gen_predicate(self, attrs: list[Attr], depth: int = 0) -> Expr: + if depth >= 2 or self.rng.random() < 0.45: + return self._gen_comparison(attrs) + + op = self.rng.choice(_LOGICAL_OPS) + lhs = self._gen_predicate(attrs, depth + 1) + rhs = self._gen_predicate(attrs, depth + 1) + return BinaryExpr(op, lhs, rhs) + + def _gen_comparison(self, attrs: list[Attr]) -> Expr: + attr = self.rng.choice(attrs) + is_string = self._column_type(attr) == "string" + + roll = self.rng.random() + if roll < 0.1: + return IsNullExpr(attr, negated=self.rng.random() < 0.5) + if roll < 0.3: + return self._gen_in(attr) + if roll < 0.4 and not is_string: + return self._gen_between(attr) + + ops = _STR_COMPARISON_OPS if is_string else _INT_COMPARISON_OPS + op = self.rng.choice(ops) + return BinaryExpr(op, attr, self._gen_literal(attr)) + + def _gen_in(self, attr: Attr) -> InExpr: + n = self.rng.randint(1, 4) + values = [self._gen_literal(attr) for _ in range(n)] + # Emit sorted ascending so the OR-chain MS SQL expands matches ours. + values.sort(key=lambda v: v.value) + return InExpr(attr, values, negated=self.rng.random() < 0.3) + + def _gen_between(self, attr: Attr) -> BetweenExpr: + lo = self._gen_literal(attr) + hi = self._gen_literal(attr) + if lo.value > hi.value: + lo, hi = hi, lo + return BetweenExpr(attr, lo, hi, negated=self.rng.random() < 0.3) + + def _gen_literal(self, attr: Attr) -> Expr: + sample = self._samples(attr) + if self._column_type(attr) == "string": + if sample and self.rng.random() < 0.8: + return StrLit(self.rng.choice(sample)) + return StrLit(self.rng.choice(["AMERICA", "EUROPE", "ASIA", "unknown"])) + if sample and self.rng.random() < 0.75: + return IntLit(self.rng.choice(sample)) + return IntLit(self.rng.randint(1, 100)) + + +# --------------------------------------------------------------------------- +# Dialect protocol +# --------------------------------------------------------------------------- + +class Dialect(Protocol): + def fmt_table(self, name: str) -> str: ... + def fmt_col_ref(self, table: str, col: str) -> str: ... + def fmt_not_equals(self) -> str: ... + def fmt_join_keyword(self, join_type: str) -> str: ... + + +class PostgresSubsetDialect: + """Plain identifiers, no schema prefix. Matches the C++ ANTLR visitor.""" + + def fmt_table(self, name: str) -> str: + return name + + def fmt_col_ref(self, table: str, col: str) -> str: + return f"{table}.{col}" + + def fmt_not_equals(self) -> str: + return "!=" + + def fmt_join_keyword(self, join_type: str) -> str: + return { + "INNER": "JOIN", + "LEFT": "LEFT JOIN", + "RIGHT": "RIGHT JOIN", + "FULL": "FULL JOIN", + }[join_type] + + +class MsSqlDialect: + """Bracket-quoted identifiers with an optional schema prefix.""" + + def __init__(self, schema_prefix: str = "dbo"): + self._schema = schema_prefix + + def fmt_table(self, name: str) -> str: + if self._schema: + return f"{self._schema}.{name}" + return name + + def fmt_col_ref(self, table: str, col: str) -> str: + return f"{table}.{col}" + + def fmt_not_equals(self) -> str: + return "<>" + + def fmt_join_keyword(self, join_type: str) -> str: + return { + "INNER": "INNER JOIN", + "LEFT": "LEFT OUTER JOIN", + "RIGHT": "RIGHT OUTER JOIN", + "FULL": "FULL OUTER JOIN", + }[join_type] + + +# --------------------------------------------------------------------------- +# Renderer +# --------------------------------------------------------------------------- + +def _fmt_str_literal(value: str) -> str: + return "'" + value.replace("'", "''") + "'" + + +def render_expr(expr: Expr, d: Dialect) -> str: + match expr: + case Attr(table, col): + return d.fmt_col_ref(table, col) + case IntLit(value): + return str(value) + case StrLit(value): + return _fmt_str_literal(value) + case NullLit(): + return "NULL" + case BoolLit(value): + return "TRUE" if value else "FALSE" + case Aggregate("COUNT", None): + return "COUNT(*)" + case Aggregate(func, arg): + return f"{func}({render_expr(arg, d)})" + case InExpr(attr, values, negated): + kw = "NOT IN" if negated else "IN" + vals = ", ".join(render_expr(v, d) for v in values) + return f"{render_expr(attr, d)} {kw} ({vals})" + case BetweenExpr(attr, lo, hi, negated): + kw = "NOT BETWEEN" if negated else "BETWEEN" + return f"{render_expr(attr, d)} {kw} {render_expr(lo, d)} AND {render_expr(hi, d)}" + case BinaryExpr(op, lhs, rhs): + l = render_expr(lhs, d) + r = render_expr(rhs, d) + if op == "!=": + op = d.fmt_not_equals() + elif op in ("and", "or"): + op = op.upper() + return f"({l} {op} {r})" + return f"{l} {op} {r}" + case UnaryExpr("not", child): + return f"NOT ({render_expr(child, d)})" + case UnaryExpr("uminus", child): + return f"-{render_expr(child, d)}" + case IsNullExpr(child, negated): + c = render_expr(child, d) + return f"{c} IS NOT NULL" if negated else f"{c} IS NULL" + raise ValueError(f"unhandled expr type: {type(expr)}") + + +def render_table_ref(ref: TableRef, d: Dialect) -> str: + match ref: + case TableScan(name, alias): + base = d.fmt_table(name) + return f"{base} AS {alias}" if alias else base + case CrossJoin(lhs, rhs): + return f"{render_table_ref(lhs, d)} CROSS JOIN {render_table_ref(rhs, d)}" + case ExplicitJoin(jtype, lhs, rhs, on): + kw = d.fmt_join_keyword(jtype) + return ( + f"{render_table_ref(lhs, d)}" + f" {kw} {render_table_ref(rhs, d)}" + f" ON {render_expr(on, d)}" + ) + raise ValueError(f"unhandled ref type: {type(ref)}") + + +def render_query(query: SelectQuery, d: Dialect) -> str: + target_parts = [] + for t in query.targets: + rendered = render_expr(t.expr, d) + if t.alias: + rendered += f" AS {t.alias}" + target_parts.append(rendered) + targets = ", ".join(target_parts) + + from_clause = render_table_ref(query.from_, d) + sql = f"SELECT {targets}\nFROM {from_clause}" + if query.where is not None: + sql += f"\nWHERE {render_expr(query.where, d)}" + if query.group_by: + cols = ", ".join(d.fmt_col_ref(a.table, a.column) for a in query.group_by) + sql += f"\nGROUP BY {cols}" + if query.order_by: + keys = ", ".join( + f"{d.fmt_col_ref(a.table, a.column)} {direction.upper()}" + for a, direction in query.order_by + ) + sql += f"\nORDER BY {keys}" + return sql + + +# --------------------------------------------------------------------------- +# Registry — add new dialects here +# --------------------------------------------------------------------------- + +DIALECTS: dict[str, Dialect] = { + "pg": PostgresSubsetDialect(), + "mssql": MsSqlDialect(), +} + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--data-dir", + default="../test/static/executor/test_data", + help="Directory containing *.csv table files", + ) + parser.add_argument("--seed", type=int, default=None) + parser.add_argument("--count", type=int, default=5) + parser.add_argument( + "--dialect", + choices=list(DIALECTS), + default=None, + help="Print only one dialect. Omit to print all.", + ) + args = parser.parse_args() + + schema = load_schema(Path(args.data_dir)) + print(f"Loaded tables: {', '.join(schema.tables)}\n") + + rng = random.Random(args.seed) + gen = QueryGenerator(schema, rng) + + dialects_to_print: dict[str, Dialect] = ( + {args.dialect: DIALECTS[args.dialect]} if args.dialect else DIALECTS + ) + + for i in range(1, args.count + 1): + query = gen.generate() + print(f"-- Query {i} " + "-" * 60) + for name, dialect in dialects_to_print.items(): + print(f"-- [{name}]") + print(render_query(query, dialect)) + print() + + +if __name__ == "__main__": + main() diff --git a/research/research.ipynb b/research/research.ipynb new file mode 100644 index 0000000..bfed507 --- /dev/null +++ b/research/research.ipynb @@ -0,0 +1,5105 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "f336c923", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-09T23:39:57+03:00\n", + "Running ./build-release/bin/benchmarks\n", + "Run on (8 X 4200 MHz CPU s)\n", + "CPU Caches:\n", + " L1 Data 48 KiB (x4)\n", + " L1 Instruction 32 KiB (x4)\n", + " L2 Unified 1280 KiB (x4)\n", + " L3 Unified 8192 KiB (x1)\n", + "Load Average: 0.33, 0.62, 1.01\n", + "***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.\n", + "---------------------------------------------------------------------------------------------------------------\n", + "Benchmark Time CPU Iterations UserCounters...\n", + "---------------------------------------------------------------------------------------------------------------\n", + "\u001b[0;32mOperatorCost/SeqScan/1024/real_time \u001b[m\u001b[0;33m 37629 ns 37491 ns \u001b[m\u001b[0;36m 18331\u001b[m model_cost=102.4k\u001b[m output_rows=1.024k\u001b[m plan_cost=102.4k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/2048/real_time \u001b[m\u001b[0;33m 72904 ns 72542 ns \u001b[m\u001b[0;36m 10020\u001b[m model_cost=204.8k\u001b[m output_rows=2.048k\u001b[m plan_cost=204.8k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/4096/real_time \u001b[m\u001b[0;33m 149078 ns 147494 ns \u001b[m\u001b[0;36m 4364\u001b[m model_cost=409.6k\u001b[m output_rows=4.096k\u001b[m plan_cost=409.6k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/8192/real_time \u001b[m\u001b[0;33m 342105 ns 339455 ns \u001b[m\u001b[0;36m 1927\u001b[m model_cost=819.2k\u001b[m output_rows=8.192k\u001b[m plan_cost=819.2k\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/16384/real_time \u001b[m\u001b[0;33m 944473 ns 935009 ns \u001b[m\u001b[0;36m 837\u001b[m model_cost=1.6384M\u001b[m output_rows=16.384k\u001b[m plan_cost=1.6384M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/32768/real_time \u001b[m\u001b[0;33m 2017875 ns 1998804 ns \u001b[m\u001b[0;36m 317\u001b[m model_cost=3.2768M\u001b[m output_rows=32.768k\u001b[m plan_cost=3.2768M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/65536/real_time \u001b[m\u001b[0;33m 4946438 ns 4895355 ns \u001b[m\u001b[0;36m 131\u001b[m model_cost=6.5536M\u001b[m output_rows=65.536k\u001b[m plan_cost=6.5536M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/131072/real_time \u001b[m\u001b[0;33m 11151818 ns 11034605 ns \u001b[m\u001b[0;36m 59\u001b[m model_cost=13.1072M\u001b[m output_rows=131.072k\u001b[m plan_cost=13.1072M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/SeqScan/262144/real_time \u001b[m\u001b[0;33m 23816470 ns 23575784 ns \u001b[m\u001b[0;36m 30\u001b[m model_cost=26.2144M\u001b[m output_rows=262.144k\u001b[m plan_cost=26.2144M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/1024/real_time \u001b[m\u001b[0;33m 73010 ns 72684 ns \u001b[m\u001b[0;36m 9776\u001b[m model_cost=102.4k\u001b[m output_rows=512\u001b[m plan_cost=204.8k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/2048/real_time \u001b[m\u001b[0;33m 132643 ns 132118 ns \u001b[m\u001b[0;36m 4789\u001b[m model_cost=204.8k\u001b[m output_rows=1.024k\u001b[m plan_cost=409.6k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/4096/real_time \u001b[m\u001b[0;33m 254451 ns 253392 ns \u001b[m\u001b[0;36m 2735\u001b[m model_cost=409.6k\u001b[m output_rows=2.048k\u001b[m plan_cost=819.2k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/8192/real_time \u001b[m\u001b[0;33m 528183 ns 525175 ns \u001b[m\u001b[0;36m 1327\u001b[m model_cost=819.2k\u001b[m output_rows=4.096k\u001b[m plan_cost=1.6384M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/16384/real_time \u001b[m\u001b[0;33m 1030577 ns 1025841 ns \u001b[m\u001b[0;36m 626\u001b[m model_cost=1.6384M\u001b[m output_rows=8.192k\u001b[m plan_cost=3.2768M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/32768/real_time \u001b[m\u001b[0;33m 2219699 ns 2207102 ns \u001b[m\u001b[0;36m 320\u001b[m model_cost=3.2768M\u001b[m output_rows=16.384k\u001b[m plan_cost=6.5536M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/65536/real_time \u001b[m\u001b[0;33m 4560355 ns 4535821 ns \u001b[m\u001b[0;36m 139\u001b[m model_cost=6.5536M\u001b[m output_rows=32.768k\u001b[m plan_cost=13.1072M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/131072/real_time \u001b[m\u001b[0;33m 10006961 ns 9936819 ns \u001b[m\u001b[0;36m 64\u001b[m model_cost=13.1072M\u001b[m output_rows=65.536k\u001b[m plan_cost=26.2144M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Filter/262144/real_time \u001b[m\u001b[0;33m 19752857 ns 19634987 ns \u001b[m\u001b[0;36m 34\u001b[m model_cost=26.2144M\u001b[m output_rows=131.072k\u001b[m plan_cost=52.4288M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/1024/real_time \u001b[m\u001b[0;33m 93864 ns 93490 ns \u001b[m\u001b[0;36m 5562\u001b[m model_cost=22.528k\u001b[m output_rows=1.024k\u001b[m plan_cost=124.928k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/2048/real_time \u001b[m\u001b[0;33m 179235 ns 178627 ns \u001b[m\u001b[0;36m 3861\u001b[m model_cost=45.056k\u001b[m output_rows=2.048k\u001b[m plan_cost=249.856k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/4096/real_time \u001b[m\u001b[0;33m 356387 ns 354765 ns \u001b[m\u001b[0;36m 1964\u001b[m model_cost=90.112k\u001b[m output_rows=4.096k\u001b[m plan_cost=499.712k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/8192/real_time \u001b[m\u001b[0;33m 700870 ns 697982 ns \u001b[m\u001b[0;36m 988\u001b[m model_cost=180.224k\u001b[m output_rows=8.192k\u001b[m plan_cost=0.999424M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/16384/real_time \u001b[m\u001b[0;33m 1478362 ns 1470833 ns \u001b[m\u001b[0;36m 383\u001b[m model_cost=360.448k\u001b[m output_rows=16.384k\u001b[m plan_cost=1.99885M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/32768/real_time \u001b[m\u001b[0;33m 3033831 ns 3017459 ns \u001b[m\u001b[0;36m 223\u001b[m model_cost=720.896k\u001b[m output_rows=32.768k\u001b[m plan_cost=3.9977M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/65536/real_time \u001b[m\u001b[0;33m 6682282 ns 6638837 ns \u001b[m\u001b[0;36m 108\u001b[m model_cost=1.44179M\u001b[m output_rows=65.536k\u001b[m plan_cost=7.99539M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/131072/real_time \u001b[m\u001b[0;33m 14104423 ns 14007623 ns \u001b[m\u001b[0;36m 41\u001b[m model_cost=2.88358M\u001b[m output_rows=131.072k\u001b[m plan_cost=15.9908M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Projection/262144/real_time \u001b[m\u001b[0;33m 34153739 ns 33892260 ns \u001b[m\u001b[0;36m 21\u001b[m model_cost=5.76717M\u001b[m output_rows=262.144k\u001b[m plan_cost=31.9816M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/1024/real_time \u001b[m\u001b[0;33m 114633 ns 114236 ns \u001b[m\u001b[0;36m 6156\u001b[m model_cost=123.904k\u001b[m output_rows=1.024k\u001b[m plan_cost=226.304k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/2048/real_time \u001b[m\u001b[0;33m 271031 ns 269939 ns \u001b[m\u001b[0;36m 2607\u001b[m model_cost=270.336k\u001b[m output_rows=2.048k\u001b[m plan_cost=475.136k\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/4096/real_time \u001b[m\u001b[0;33m 607491 ns 604728 ns \u001b[m\u001b[0;36m 1090\u001b[m model_cost=585.728k\u001b[m output_rows=4.096k\u001b[m plan_cost=995.328k\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/8192/real_time \u001b[m\u001b[0;33m 1353148 ns 1347248 ns \u001b[m\u001b[0;36m 506\u001b[m model_cost=1.26157M\u001b[m output_rows=8.192k\u001b[m plan_cost=2.08077M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/16384/real_time \u001b[m\u001b[0;33m 3425096 ns 3405518 ns \u001b[m\u001b[0;36m 215\u001b[m model_cost=2.70336M\u001b[m output_rows=16.384k\u001b[m plan_cost=4.34176M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/32768/real_time \u001b[m\u001b[0;33m 8128588 ns 8085581 ns \u001b[m\u001b[0;36m 89\u001b[m model_cost=5.76717M\u001b[m output_rows=32.768k\u001b[m plan_cost=9.04397M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/65536/real_time \u001b[m\u001b[0;33m 21036047 ns 20881034 ns \u001b[m\u001b[0;36m 32\u001b[m model_cost=12.2552M\u001b[m output_rows=65.536k\u001b[m plan_cost=18.8088M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/131072/real_time \u001b[m\u001b[0;33m 68694607 ns 68149827 ns \u001b[m\u001b[0;36m 11\u001b[m model_cost=25.9523M\u001b[m output_rows=131.072k\u001b[m plan_cost=39.0595M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Sort/262144/real_time \u001b[m\u001b[0;33m 195667437 ns 194198663 ns \u001b[m\u001b[0;36m 4\u001b[m model_cost=54.7881M\u001b[m output_rows=262.144k\u001b[m plan_cost=81.0025M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/1024/real_time \u001b[m\u001b[0;33m 279850 ns 278798 ns \u001b[m\u001b[0;36m 2449\u001b[m model_cost=522.24k\u001b[m output_rows=1.024k\u001b[m plan_cost=624.64k\u001b[m rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/2048/real_time \u001b[m\u001b[0;33m 547978 ns 545873 ns \u001b[m\u001b[0;36m 1261\u001b[m model_cost=1.04448M\u001b[m output_rows=2.048k\u001b[m plan_cost=1.24928M\u001b[m rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/4096/real_time \u001b[m\u001b[0;33m 1112645 ns 1108182 ns \u001b[m\u001b[0;36m 606\u001b[m model_cost=2.08896M\u001b[m output_rows=4.096k\u001b[m plan_cost=2.49856M\u001b[m rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/8192/real_time \u001b[m\u001b[0;33m 2393267 ns 2381758 ns \u001b[m\u001b[0;36m 293\u001b[m model_cost=4.17792M\u001b[m output_rows=8.192k\u001b[m plan_cost=4.99712M\u001b[m rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/16384/real_time \u001b[m\u001b[0;33m 6024309 ns 5985839 ns \u001b[m\u001b[0;36m 128\u001b[m model_cost=8.35584M\u001b[m output_rows=16.384k\u001b[m plan_cost=9.99424M\u001b[m rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/32768/real_time \u001b[m\u001b[0;33m 16651256 ns 16529865 ns \u001b[m\u001b[0;36m 41\u001b[m model_cost=16.7117M\u001b[m output_rows=32.768k\u001b[m plan_cost=19.9885M\u001b[m rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/65536/real_time \u001b[m\u001b[0;33m 52569017 ns 52196226 ns \u001b[m\u001b[0;36m 13\u001b[m model_cost=33.4234M\u001b[m output_rows=65.536k\u001b[m plan_cost=39.977M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/131072/real_time \u001b[m\u001b[0;33m 134126306 ns 133168248 ns \u001b[m\u001b[0;36m 5\u001b[m model_cost=66.8467M\u001b[m output_rows=131.072k\u001b[m plan_cost=79.9539M\u001b[m rows=131.072k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/Aggregation/262144/real_time \u001b[m\u001b[0;33m 331639606 ns 329151453 ns \u001b[m\u001b[0;36m 2\u001b[m model_cost=133.693M\u001b[m output_rows=262.144k\u001b[m plan_cost=159.908M\u001b[m rows=262.144k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/1024/1024/real_time \u001b[m\u001b[0;33m 159954 ns 159278 ns \u001b[m\u001b[0;36m 4422\u001b[m lhs_rows=1.024k\u001b[m model_cost=404.48k\u001b[m output_rows=1.024k\u001b[m plan_cost=609.28k\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/2048/2048/real_time \u001b[m\u001b[0;33m 303150 ns 301902 ns \u001b[m\u001b[0;36m 2251\u001b[m lhs_rows=2.048k\u001b[m model_cost=808.96k\u001b[m output_rows=2.048k\u001b[m plan_cost=1.21856M\u001b[m rhs_rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/4096/4096/real_time \u001b[m\u001b[0;33m 606974 ns 604010 ns \u001b[m\u001b[0;36m 1075\u001b[m lhs_rows=4.096k\u001b[m model_cost=1.61792M\u001b[m output_rows=4.096k\u001b[m plan_cost=2.43712M\u001b[m rhs_rows=4.096k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/8192/8192/real_time \u001b[m\u001b[0;33m 1240293 ns 1232604 ns \u001b[m\u001b[0;36m 565\u001b[m lhs_rows=8.192k\u001b[m model_cost=3.23584M\u001b[m output_rows=8.192k\u001b[m plan_cost=4.87424M\u001b[m rhs_rows=8.192k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/16384/16384/real_time \u001b[m\u001b[0;33m 2840201 ns 2819090 ns \u001b[m\u001b[0;36m 243\u001b[m lhs_rows=16.384k\u001b[m model_cost=6.47168M\u001b[m output_rows=16.384k\u001b[m plan_cost=9.74848M\u001b[m rhs_rows=16.384k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/32768/32768/real_time \u001b[m\u001b[0;33m 7253468 ns 7190828 ns \u001b[m\u001b[0;36m 97\u001b[m lhs_rows=32.768k\u001b[m model_cost=12.9434M\u001b[m output_rows=32.768k\u001b[m plan_cost=19.497M\u001b[m rhs_rows=32.768k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/HashJoin/65536/65536/real_time \u001b[m\u001b[0;33m 17894077 ns 17744853 ns \u001b[m\u001b[0;36m 46\u001b[m lhs_rows=65.536k\u001b[m model_cost=25.8867M\u001b[m output_rows=65.536k\u001b[m plan_cost=38.9939M\u001b[m rhs_rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/64/64/real_time \u001b[m\u001b[0;33m 353589 ns 351322 ns \u001b[m\u001b[0;36m 1913\u001b[m lhs_rows=64\u001b[m model_cost=286.72k\u001b[m output_rows=64\u001b[m plan_cost=299.52k\u001b[m rhs_rows=64\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/128/128/real_time \u001b[m\u001b[0;33m 1133881 ns 1126145 ns \u001b[m\u001b[0;36m 534\u001b[m lhs_rows=128\u001b[m model_cost=1.14688M\u001b[m output_rows=128\u001b[m plan_cost=1.17248M\u001b[m rhs_rows=128\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/256/256/real_time \u001b[m\u001b[0;33m 4047780 ns 4028194 ns \u001b[m\u001b[0;36m 171\u001b[m lhs_rows=256\u001b[m model_cost=4.58752M\u001b[m output_rows=256\u001b[m plan_cost=4.63872M\u001b[m rhs_rows=256\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/512/512/real_time \u001b[m\u001b[0;33m 15228342 ns 15163560 ns \u001b[m\u001b[0;36m 39\u001b[m lhs_rows=512\u001b[m model_cost=18.3501M\u001b[m output_rows=512\u001b[m plan_cost=18.4525M\u001b[m rhs_rows=512\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/1024/1024/real_time \u001b[m\u001b[0;33m 58972880 ns 58759088 ns \u001b[m\u001b[0;36m 12\u001b[m lhs_rows=1.024k\u001b[m model_cost=73.4003M\u001b[m output_rows=1.024k\u001b[m plan_cost=73.6051M\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopJoin/2048/2048/real_time \u001b[m\u001b[0;33m 239655377 ns 238650148 ns \u001b[m\u001b[0;36m 3\u001b[m lhs_rows=2.048k\u001b[m model_cost=293.601M\u001b[m output_rows=2.048k\u001b[m plan_cost=294.011M\u001b[m rhs_rows=2.048k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/32/32/real_time \u001b[m\u001b[0;33m 127064 ns 124736 ns \u001b[m\u001b[0;36m 5276\u001b[m lhs_rows=32\u001b[m model_cost=106.496k\u001b[m output_rows=1.024k\u001b[m plan_cost=112.896k\u001b[m rhs_rows=32\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/64/64/real_time \u001b[m\u001b[0;33m 291643 ns 289230 ns \u001b[m\u001b[0;36m 2417\u001b[m lhs_rows=64\u001b[m model_cost=425.984k\u001b[m output_rows=4.096k\u001b[m plan_cost=438.784k\u001b[m rhs_rows=64\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/128/128/real_time \u001b[m\u001b[0;33m 953553 ns 946151 ns \u001b[m\u001b[0;36m 765\u001b[m lhs_rows=128\u001b[m model_cost=1.70394M\u001b[m output_rows=16.384k\u001b[m plan_cost=1.72954M\u001b[m rhs_rows=128\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/256/256/real_time \u001b[m\u001b[0;33m 5520151 ns 5445272 ns \u001b[m\u001b[0;36m 118\u001b[m lhs_rows=256\u001b[m model_cost=6.81574M\u001b[m output_rows=65.536k\u001b[m plan_cost=6.86694M\u001b[m rhs_rows=256\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/512/512/real_time \u001b[m\u001b[0;33m 24979219 ns 24715855 ns \u001b[m\u001b[0;36m 28\u001b[m lhs_rows=512\u001b[m model_cost=27.263M\u001b[m output_rows=262.144k\u001b[m plan_cost=27.3654M\u001b[m rhs_rows=512\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCost/NestedLoopCrossJoin/1024/1024/real_time \u001b[m\u001b[0;33m 112934816 ns 111900069 ns \u001b[m\u001b[0;36m 5\u001b[m lhs_rows=1.024k\u001b[m model_cost=109.052M\u001b[m output_rows=1.04858M\u001b[m plan_cost=109.257M\u001b[m rhs_rows=1.024k\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "!cd ~/c/iu9-sql-compiler/ && ./build-release/bin/benchmarks --benchmark_filter='^OperatorCost/' --benchmark_out=/tmp/operator-cost.json --benchmark_out_format=json" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fb1ddd67", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-rw-r--r-- 1 st users 40322 Jun 9 23:41 operator-cost.json\n", + "-rw-r--r-- 1 st users 33223 May 31 16:03 operator-cost-matched-calibrated-smoke.json\n", + "-rw-r--r-- 1 st users 33305 May 31 16:16 operator-cost-matched.json\n", + "-rw-r--r-- 1 st users 6149 May 31 15:08 operator-cost-matched-plan-cost-smoke.json\n", + "-rw-r--r-- 1 st users 2010 May 30 19:02 operator-cost-smoke.json\n" + ] + } + ], + "source": [ + "!ls -l /tmp | grep operator-cost" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2d033649", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'context': {'date': '2026-06-09T23:39:57+03:00',\n", + " 'host_name': 'nixos',\n", + " 'executable': './build-release/bin/benchmarks',\n", + " 'num_cpus': 8,\n", + " 'mhz_per_cpu': 4200,\n", + " 'cpu_scaling_enabled': True,\n", + " 'caches': [{'type': 'Data', 'level': 1, 'size': 49152, 'num_sharing': 2},\n", + " {'type': 'Instruction', 'level': 1, 'size': 32768, 'num_sharing': 2},\n", + " {'type': 'Unified', 'level': 2, 'size': 1310720, 'num_sharing': 2},\n", + " {'type': 'Unified', 'level': 3, 'size': 8388608, 'num_sharing': 8}],\n", + " 'load_avg': [0.331543, 0.623535, 1.01465],\n", + " 'library_version': 'v1.9.0',\n", + " 'library_build_type': 'release',\n", + " 'json_schema_version': 1},\n", + " 'benchmarks': [{'name': 'OperatorCost/SeqScan/1024/real_time',\n", + " 'family_index': 0,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 18331,\n", + " 'real_time': 37628.93579271746,\n", + " 'cpu_time': 37491.17609513937,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 102400.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 102400.0,\n", + " 'rows': 1024.0},\n", + " {'name': 'OperatorCost/SeqScan/2048/real_time',\n", + " 'family_index': 1,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/2048/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 10020,\n", + " 'real_time': 72903.61267470378,\n", + " 'cpu_time': 72541.76367265466,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 204800.0,\n", + " 'output_rows': 2048.0,\n", + " 'plan_cost': 204800.0,\n", + " 'rows': 2048.0},\n", + " {'name': 'OperatorCost/SeqScan/4096/real_time',\n", + " 'family_index': 2,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/4096/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 4364,\n", + " 'real_time': 149077.84784833947,\n", + " 'cpu_time': 147494.45623281397,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 409600.0,\n", + " 'output_rows': 4096.0,\n", + " 'plan_cost': 409600.0,\n", + " 'rows': 4096.0},\n", + " {'name': 'OperatorCost/SeqScan/8192/real_time',\n", + " 'family_index': 3,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/8192/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1927,\n", + " 'real_time': 342105.1670973816,\n", + " 'cpu_time': 339454.6050856253,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 819200.0,\n", + " 'output_rows': 8192.0,\n", + " 'plan_cost': 819200.0,\n", + " 'rows': 8192.0},\n", + " {'name': 'OperatorCost/SeqScan/16384/real_time',\n", + " 'family_index': 4,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/16384/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 837,\n", + " 'real_time': 944473.1648865611,\n", + " 'cpu_time': 935008.9928315411,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1638400.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 1638400.0,\n", + " 'rows': 16384.0},\n", + " {'name': 'OperatorCost/SeqScan/32768/real_time',\n", + " 'family_index': 5,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/32768/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 317,\n", + " 'real_time': 2017874.6560967471,\n", + " 'cpu_time': 1998803.5678233458,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 3276800.0,\n", + " 'output_rows': 32768.0,\n", + " 'plan_cost': 3276800.0,\n", + " 'rows': 32768.0},\n", + " {'name': 'OperatorCost/SeqScan/65536/real_time',\n", + " 'family_index': 6,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/65536/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 131,\n", + " 'real_time': 4946438.267068113,\n", + " 'cpu_time': 4895354.954198475,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 6553600.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 6553600.0,\n", + " 'rows': 65536.0},\n", + " {'name': 'OperatorCost/SeqScan/131072/real_time',\n", + " 'family_index': 7,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/131072/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 59,\n", + " 'real_time': 11151817.93210144,\n", + " 'cpu_time': 11034604.779660998,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 13107200.0,\n", + " 'output_rows': 131072.0,\n", + " 'plan_cost': 13107200.0,\n", + " 'rows': 131072.0},\n", + " {'name': 'OperatorCost/SeqScan/262144/real_time',\n", + " 'family_index': 8,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/SeqScan/262144/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 30,\n", + " 'real_time': 23816470.46713624,\n", + " 'cpu_time': 23575783.766666625,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 26214400.0,\n", + " 'output_rows': 262144.0,\n", + " 'plan_cost': 26214400.0,\n", + " 'rows': 262144.0},\n", + " {'name': 'OperatorCost/Filter/1024/real_time',\n", + " 'family_index': 9,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 9776,\n", + " 'real_time': 73010.3600680018,\n", + " 'cpu_time': 72684.05441898521,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 102400.0,\n", + " 'output_rows': 512.0,\n", + " 'plan_cost': 204800.0,\n", + " 'rows': 1024.0},\n", + " {'name': 'OperatorCost/Filter/2048/real_time',\n", + " 'family_index': 10,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/2048/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 4789,\n", + " 'real_time': 132642.64752141281,\n", + " 'cpu_time': 132118.3029860095,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 204800.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 409600.0,\n", + " 'rows': 2048.0},\n", + " {'name': 'OperatorCost/Filter/4096/real_time',\n", + " 'family_index': 11,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/4096/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2735,\n", + " 'real_time': 254451.37185068423,\n", + " 'cpu_time': 253392.1930530162,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 409600.0,\n", + " 'output_rows': 2048.0,\n", + " 'plan_cost': 819200.0,\n", + " 'rows': 4096.0},\n", + " {'name': 'OperatorCost/Filter/8192/real_time',\n", + " 'family_index': 12,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/8192/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1327,\n", + " 'real_time': 528182.7332362107,\n", + " 'cpu_time': 525174.8666164279,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 819200.0,\n", + " 'output_rows': 4096.0,\n", + " 'plan_cost': 1638400.0,\n", + " 'rows': 8192.0},\n", + " {'name': 'OperatorCost/Filter/16384/real_time',\n", + " 'family_index': 13,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/16384/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 626,\n", + " 'real_time': 1030577.062292563,\n", + " 'cpu_time': 1025840.7140575078,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1638400.0,\n", + " 'output_rows': 8192.0,\n", + " 'plan_cost': 3276800.0,\n", + " 'rows': 16384.0},\n", + " {'name': 'OperatorCost/Filter/32768/real_time',\n", + " 'family_index': 14,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/32768/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 320,\n", + " 'real_time': 2219699.446868617,\n", + " 'cpu_time': 2207102.043749998,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 3276800.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 6553600.0,\n", + " 'rows': 32768.0},\n", + " {'name': 'OperatorCost/Filter/65536/real_time',\n", + " 'family_index': 15,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/65536/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 139,\n", + " 'real_time': 4560355.187088868,\n", + " 'cpu_time': 4535820.6115108,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 6553600.0,\n", + " 'output_rows': 32768.0,\n", + " 'plan_cost': 13107200.0,\n", + " 'rows': 65536.0},\n", + " {'name': 'OperatorCost/Filter/131072/real_time',\n", + " 'family_index': 16,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/131072/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 64,\n", + " 'real_time': 10006960.781083763,\n", + " 'cpu_time': 9936818.984374985,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 13107200.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 26214400.0,\n", + " 'rows': 131072.0},\n", + " {'name': 'OperatorCost/Filter/262144/real_time',\n", + " 'family_index': 17,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Filter/262144/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 34,\n", + " 'real_time': 19752857.20597158,\n", + " 'cpu_time': 19634987.35294109,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 26214400.0,\n", + " 'output_rows': 131072.0,\n", + " 'plan_cost': 52428800.0,\n", + " 'rows': 262144.0},\n", + " {'name': 'OperatorCost/Projection/1024/real_time',\n", + " 'family_index': 18,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 5562,\n", + " 'real_time': 93863.53218365033,\n", + " 'cpu_time': 93489.92502696834,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 22528.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 124928.0,\n", + " 'rows': 1024.0},\n", + " {'name': 'OperatorCost/Projection/2048/real_time',\n", + " 'family_index': 19,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/2048/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 3861,\n", + " 'real_time': 179234.84356321077,\n", + " 'cpu_time': 178626.77570577548,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 45056.0,\n", + " 'output_rows': 2048.0,\n", + " 'plan_cost': 249856.0,\n", + " 'rows': 2048.0},\n", + " {'name': 'OperatorCost/Projection/4096/real_time',\n", + " 'family_index': 20,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/4096/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1964,\n", + " 'real_time': 356387.378309584,\n", + " 'cpu_time': 354765.20315682417,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 90112.0,\n", + " 'output_rows': 4096.0,\n", + " 'plan_cost': 499712.0,\n", + " 'rows': 4096.0},\n", + " {'name': 'OperatorCost/Projection/8192/real_time',\n", + " 'family_index': 21,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/8192/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 988,\n", + " 'real_time': 700869.6093069869,\n", + " 'cpu_time': 697981.6255060721,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 180224.0,\n", + " 'output_rows': 8192.0,\n", + " 'plan_cost': 999424.0,\n", + " 'rows': 8192.0},\n", + " {'name': 'OperatorCost/Projection/16384/real_time',\n", + " 'family_index': 22,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/16384/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 383,\n", + " 'real_time': 1478362.0443614565,\n", + " 'cpu_time': 1470832.856396862,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 360448.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 1998848.0,\n", + " 'rows': 16384.0},\n", + " {'name': 'OperatorCost/Projection/32768/real_time',\n", + " 'family_index': 23,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/32768/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 223,\n", + " 'real_time': 3033831.156999073,\n", + " 'cpu_time': 3017459.340807185,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 720896.0,\n", + " 'output_rows': 32768.0,\n", + " 'plan_cost': 3997696.0,\n", + " 'rows': 32768.0},\n", + " {'name': 'OperatorCost/Projection/65536/real_time',\n", + " 'family_index': 24,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/65536/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 108,\n", + " 'real_time': 6682282.166568459,\n", + " 'cpu_time': 6638837.333333332,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1441792.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 7995392.0,\n", + " 'rows': 65536.0},\n", + " {'name': 'OperatorCost/Projection/131072/real_time',\n", + " 'family_index': 25,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/131072/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 41,\n", + " 'real_time': 14104422.85407221,\n", + " 'cpu_time': 14007622.82926831,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 2883584.0,\n", + " 'output_rows': 131072.0,\n", + " 'plan_cost': 15990784.0,\n", + " 'rows': 131072.0},\n", + " {'name': 'OperatorCost/Projection/262144/real_time',\n", + " 'family_index': 26,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Projection/262144/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 21,\n", + " 'real_time': 34153738.76175311,\n", + " 'cpu_time': 33892259.61904768,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 5767168.0,\n", + " 'output_rows': 262144.0,\n", + " 'plan_cost': 31981568.0,\n", + " 'rows': 262144.0},\n", + " {'name': 'OperatorCost/Sort/1024/real_time',\n", + " 'family_index': 27,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 6156,\n", + " 'real_time': 114632.56253664369,\n", + " 'cpu_time': 114235.66406757633,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 123904.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 226304.0,\n", + " 'rows': 1024.0},\n", + " {'name': 'OperatorCost/Sort/2048/real_time',\n", + " 'family_index': 28,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/2048/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2607,\n", + " 'real_time': 271030.7748304336,\n", + " 'cpu_time': 269938.7472190262,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 270336.0,\n", + " 'output_rows': 2048.0,\n", + " 'plan_cost': 475136.0,\n", + " 'rows': 2048.0},\n", + " {'name': 'OperatorCost/Sort/4096/real_time',\n", + " 'family_index': 29,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/4096/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1090,\n", + " 'real_time': 607491.4275380194,\n", + " 'cpu_time': 604727.7247706426,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 585728.0,\n", + " 'output_rows': 4096.0,\n", + " 'plan_cost': 995328.0,\n", + " 'rows': 4096.0},\n", + " {'name': 'OperatorCost/Sort/8192/real_time',\n", + " 'family_index': 30,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/8192/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 506,\n", + " 'real_time': 1353148.0118422494,\n", + " 'cpu_time': 1347248.1798418993,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1261568.0,\n", + " 'output_rows': 8192.0,\n", + " 'plan_cost': 2080768.0,\n", + " 'rows': 8192.0},\n", + " {'name': 'OperatorCost/Sort/16384/real_time',\n", + " 'family_index': 31,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/16384/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 215,\n", + " 'real_time': 3425096.1256560027,\n", + " 'cpu_time': 3405518.395348841,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 2703360.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 4341760.0,\n", + " 'rows': 16384.0},\n", + " {'name': 'OperatorCost/Sort/32768/real_time',\n", + " 'family_index': 32,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/32768/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 89,\n", + " 'real_time': 8128587.966209299,\n", + " 'cpu_time': 8085581.056179743,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 5767168.0,\n", + " 'output_rows': 32768.0,\n", + " 'plan_cost': 9043968.0,\n", + " 'rows': 32768.0},\n", + " {'name': 'OperatorCost/Sort/65536/real_time',\n", + " 'family_index': 33,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/65536/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 32,\n", + " 'real_time': 21036046.65626335,\n", + " 'cpu_time': 20881033.562499817,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 12255232.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 18808832.0,\n", + " 'rows': 65536.0},\n", + " {'name': 'OperatorCost/Sort/131072/real_time',\n", + " 'family_index': 34,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/131072/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 11,\n", + " 'real_time': 68694607.27268068,\n", + " 'cpu_time': 68149827.09090923,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 25952256.0,\n", + " 'output_rows': 131072.0,\n", + " 'plan_cost': 39059456.0,\n", + " 'rows': 131072.0},\n", + " {'name': 'OperatorCost/Sort/262144/real_time',\n", + " 'family_index': 35,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Sort/262144/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 4,\n", + " 'real_time': 195667436.74658936,\n", + " 'cpu_time': 194198662.99999988,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 54788096.0,\n", + " 'output_rows': 262144.0,\n", + " 'plan_cost': 81002496.0,\n", + " 'rows': 262144.0},\n", + " {'name': 'OperatorCost/Aggregation/1024/real_time',\n", + " 'family_index': 36,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2449,\n", + " 'real_time': 279850.0351074257,\n", + " 'cpu_time': 278797.8321764009,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 522240.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 624640.0,\n", + " 'rows': 1024.0},\n", + " {'name': 'OperatorCost/Aggregation/2048/real_time',\n", + " 'family_index': 37,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/2048/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1261,\n", + " 'real_time': 547978.1998470603,\n", + " 'cpu_time': 545873.2402854833,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1044480.0,\n", + " 'output_rows': 2048.0,\n", + " 'plan_cost': 1249280.0,\n", + " 'rows': 2048.0},\n", + " {'name': 'OperatorCost/Aggregation/4096/real_time',\n", + " 'family_index': 38,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/4096/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 606,\n", + " 'real_time': 1112644.844901711,\n", + " 'cpu_time': 1108182.447194716,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 2088960.0,\n", + " 'output_rows': 4096.0,\n", + " 'plan_cost': 2498560.0,\n", + " 'rows': 4096.0},\n", + " {'name': 'OperatorCost/Aggregation/8192/real_time',\n", + " 'family_index': 39,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/8192/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 293,\n", + " 'real_time': 2393266.856598862,\n", + " 'cpu_time': 2381757.6996586947,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 4177920.0,\n", + " 'output_rows': 8192.0,\n", + " 'plan_cost': 4997120.0,\n", + " 'rows': 8192.0},\n", + " {'name': 'OperatorCost/Aggregation/16384/real_time',\n", + " 'family_index': 40,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/16384/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 128,\n", + " 'real_time': 6024309.031090524,\n", + " 'cpu_time': 5985839.437500029,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 8355840.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 9994240.0,\n", + " 'rows': 16384.0},\n", + " {'name': 'OperatorCost/Aggregation/32768/real_time',\n", + " 'family_index': 41,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/32768/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 41,\n", + " 'real_time': 16651256.390415678,\n", + " 'cpu_time': 16529865.097560914,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 16711680.0,\n", + " 'output_rows': 32768.0,\n", + " 'plan_cost': 19988480.0,\n", + " 'rows': 32768.0},\n", + " {'name': 'OperatorCost/Aggregation/65536/real_time',\n", + " 'family_index': 42,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/65536/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 13,\n", + " 'real_time': 52569017.46123242,\n", + " 'cpu_time': 52196226.23076958,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 33423360.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 39976960.0,\n", + " 'rows': 65536.0},\n", + " {'name': 'OperatorCost/Aggregation/131072/real_time',\n", + " 'family_index': 43,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/131072/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 5,\n", + " 'real_time': 134126305.60342221,\n", + " 'cpu_time': 133168247.79999904,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 66846720.0,\n", + " 'output_rows': 131072.0,\n", + " 'plan_cost': 79953920.0,\n", + " 'rows': 131072.0},\n", + " {'name': 'OperatorCost/Aggregation/262144/real_time',\n", + " 'family_index': 44,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/Aggregation/262144/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2,\n", + " 'real_time': 331639605.50155026,\n", + " 'cpu_time': 329151452.9999979,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 133693440.0,\n", + " 'output_rows': 262144.0,\n", + " 'plan_cost': 159907840.0,\n", + " 'rows': 262144.0},\n", + " {'name': 'OperatorCost/HashJoin/1024/1024/real_time',\n", + " 'family_index': 45,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/HashJoin/1024/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 4422,\n", + " 'real_time': 159954.39688340446,\n", + " 'cpu_time': 159277.9552238794,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 1024.0,\n", + " 'model_cost': 404480.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 609280.0,\n", + " 'rhs_rows': 1024.0},\n", + " {'name': 'OperatorCost/HashJoin/2048/2048/real_time',\n", + " 'family_index': 46,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/HashJoin/2048/2048/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2251,\n", + " 'real_time': 303149.62326934596,\n", + " 'cpu_time': 301901.92003554106,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 2048.0,\n", + " 'model_cost': 808960.0,\n", + " 'output_rows': 2048.0,\n", + " 'plan_cost': 1218560.0,\n", + " 'rhs_rows': 2048.0},\n", + " {'name': 'OperatorCost/HashJoin/4096/4096/real_time',\n", + " 'family_index': 47,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/HashJoin/4096/4096/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1075,\n", + " 'real_time': 606973.833474872,\n", + " 'cpu_time': 604010.4744186023,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 4096.0,\n", + " 'model_cost': 1617920.0,\n", + " 'output_rows': 4096.0,\n", + " 'plan_cost': 2437120.0,\n", + " 'rhs_rows': 4096.0},\n", + " {'name': 'OperatorCost/HashJoin/8192/8192/real_time',\n", + " 'family_index': 48,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/HashJoin/8192/8192/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 565,\n", + " 'real_time': 1240293.1486866313,\n", + " 'cpu_time': 1232603.897345126,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 8192.0,\n", + " 'model_cost': 3235840.0,\n", + " 'output_rows': 8192.0,\n", + " 'plan_cost': 4874240.0,\n", + " 'rhs_rows': 8192.0},\n", + " {'name': 'OperatorCost/HashJoin/16384/16384/real_time',\n", + " 'family_index': 49,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/HashJoin/16384/16384/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 243,\n", + " 'real_time': 2840201.378562353,\n", + " 'cpu_time': 2819089.868312745,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 16384.0,\n", + " 'model_cost': 6471680.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 9748480.0,\n", + " 'rhs_rows': 16384.0},\n", + " {'name': 'OperatorCost/HashJoin/32768/32768/real_time',\n", + " 'family_index': 50,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/HashJoin/32768/32768/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 97,\n", + " 'real_time': 7253468.051353547,\n", + " 'cpu_time': 7190828.268041248,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 32768.0,\n", + " 'model_cost': 12943360.0,\n", + " 'output_rows': 32768.0,\n", + " 'plan_cost': 19496960.0,\n", + " 'rhs_rows': 32768.0},\n", + " {'name': 'OperatorCost/HashJoin/65536/65536/real_time',\n", + " 'family_index': 51,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/HashJoin/65536/65536/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 46,\n", + " 'real_time': 17894076.999610938,\n", + " 'cpu_time': 17744853.413043536,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 65536.0,\n", + " 'model_cost': 25886720.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 38993920.0,\n", + " 'rhs_rows': 65536.0},\n", + " {'name': 'OperatorCost/NestedLoopJoin/64/64/real_time',\n", + " 'family_index': 52,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopJoin/64/64/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1913,\n", + " 'real_time': 353588.65500607423,\n", + " 'cpu_time': 351322.2059592261,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 64.0,\n", + " 'model_cost': 286720.0,\n", + " 'output_rows': 64.0,\n", + " 'plan_cost': 299520.0,\n", + " 'rhs_rows': 64.0},\n", + " {'name': 'OperatorCost/NestedLoopJoin/128/128/real_time',\n", + " 'family_index': 53,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopJoin/128/128/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 534,\n", + " 'real_time': 1133880.501897301,\n", + " 'cpu_time': 1126145.076779034,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 128.0,\n", + " 'model_cost': 1146880.0,\n", + " 'output_rows': 128.0,\n", + " 'plan_cost': 1172480.0,\n", + " 'rhs_rows': 128.0},\n", + " {'name': 'OperatorCost/NestedLoopJoin/256/256/real_time',\n", + " 'family_index': 54,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopJoin/256/256/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 171,\n", + " 'real_time': 4047780.2515729764,\n", + " 'cpu_time': 4028194.456140374,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 256.0,\n", + " 'model_cost': 4587520.0,\n", + " 'output_rows': 256.0,\n", + " 'plan_cost': 4638720.0,\n", + " 'rhs_rows': 256.0},\n", + " {'name': 'OperatorCost/NestedLoopJoin/512/512/real_time',\n", + " 'family_index': 55,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopJoin/512/512/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 39,\n", + " 'real_time': 15228341.769379301,\n", + " 'cpu_time': 15163559.56410255,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 512.0,\n", + " 'model_cost': 18350080.0,\n", + " 'output_rows': 512.0,\n", + " 'plan_cost': 18452480.0,\n", + " 'rhs_rows': 512.0},\n", + " {'name': 'OperatorCost/NestedLoopJoin/1024/1024/real_time',\n", + " 'family_index': 56,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopJoin/1024/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 12,\n", + " 'real_time': 58972879.74948994,\n", + " 'cpu_time': 58759088.33333317,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 1024.0,\n", + " 'model_cost': 73400320.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 73605120.0,\n", + " 'rhs_rows': 1024.0},\n", + " {'name': 'OperatorCost/NestedLoopJoin/2048/2048/real_time',\n", + " 'family_index': 57,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopJoin/2048/2048/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 3,\n", + " 'real_time': 239655377.33359572,\n", + " 'cpu_time': 238650148.0000002,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 2048.0,\n", + " 'model_cost': 293601280.0,\n", + " 'output_rows': 2048.0,\n", + " 'plan_cost': 294010880.0,\n", + " 'rhs_rows': 2048.0},\n", + " {'name': 'OperatorCost/NestedLoopCrossJoin/32/32/real_time',\n", + " 'family_index': 58,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopCrossJoin/32/32/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 5276,\n", + " 'real_time': 127063.62054887325,\n", + " 'cpu_time': 124735.67304776366,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 32.0,\n", + " 'model_cost': 106496.0,\n", + " 'output_rows': 1024.0,\n", + " 'plan_cost': 112896.0,\n", + " 'rhs_rows': 32.0},\n", + " {'name': 'OperatorCost/NestedLoopCrossJoin/64/64/real_time',\n", + " 'family_index': 59,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopCrossJoin/64/64/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2417,\n", + " 'real_time': 291643.43234941515,\n", + " 'cpu_time': 289229.67273479357,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 64.0,\n", + " 'model_cost': 425984.0,\n", + " 'output_rows': 4096.0,\n", + " 'plan_cost': 438784.0,\n", + " 'rhs_rows': 64.0},\n", + " {'name': 'OperatorCost/NestedLoopCrossJoin/128/128/real_time',\n", + " 'family_index': 60,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopCrossJoin/128/128/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 765,\n", + " 'real_time': 953553.1555241571,\n", + " 'cpu_time': 946151.47320261,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 128.0,\n", + " 'model_cost': 1703936.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 1729536.0,\n", + " 'rhs_rows': 128.0},\n", + " {'name': 'OperatorCost/NestedLoopCrossJoin/256/256/real_time',\n", + " 'family_index': 61,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopCrossJoin/256/256/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 118,\n", + " 'real_time': 5520151.406664685,\n", + " 'cpu_time': 5445272.305084768,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 256.0,\n", + " 'model_cost': 6815744.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 6866944.0,\n", + " 'rhs_rows': 256.0},\n", + " {'name': 'OperatorCost/NestedLoopCrossJoin/512/512/real_time',\n", + " 'family_index': 62,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopCrossJoin/512/512/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 28,\n", + " 'real_time': 24979218.64310878,\n", + " 'cpu_time': 24715854.607142873,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 512.0,\n", + " 'model_cost': 27262976.0,\n", + " 'output_rows': 262144.0,\n", + " 'plan_cost': 27365376.0,\n", + " 'rhs_rows': 512.0},\n", + " {'name': 'OperatorCost/NestedLoopCrossJoin/1024/1024/real_time',\n", + " 'family_index': 63,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCost/NestedLoopCrossJoin/1024/1024/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 5,\n", + " 'real_time': 112934816.00005178,\n", + " 'cpu_time': 111900069.20000002,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 1024.0,\n", + " 'model_cost': 109051904.0,\n", + " 'output_rows': 1048576.0,\n", + " 'plan_cost': 109256704.0,\n", + " 'rhs_rows': 1024.0}]}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import json\n", + "\n", + "results = None\n", + "with open('/tmp/operator-cost.json') as f:\n", + " results = json.load(f)\n", + "\n", + "results" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1e9f5e6f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
operatorleft_rowsright_rowsreal_time_msmodel_cost
36Aggregation1024NaN0.279850522240.0
37Aggregation2048NaN0.5479781044480.0
38Aggregation4096NaN1.1126452088960.0
39Aggregation8192NaN2.3932674177920.0
40Aggregation16384NaN6.0243098355840.0
..................
31Sort16384NaN3.4250962703360.0
32Sort32768NaN8.1285885767168.0
33Sort65536NaN21.03604712255232.0
34Sort131072NaN68.69460725952256.0
35Sort262144NaN195.66743754788096.0
\n", + "

64 rows × 5 columns

\n", + "" + ], + "text/plain": [ + " operator left_rows right_rows real_time_ms model_cost\n", + "36 Aggregation 1024 NaN 0.279850 522240.0\n", + "37 Aggregation 2048 NaN 0.547978 1044480.0\n", + "38 Aggregation 4096 NaN 1.112645 2088960.0\n", + "39 Aggregation 8192 NaN 2.393267 4177920.0\n", + "40 Aggregation 16384 NaN 6.024309 8355840.0\n", + ".. ... ... ... ... ...\n", + "31 Sort 16384 NaN 3.425096 2703360.0\n", + "32 Sort 32768 NaN 8.128588 5767168.0\n", + "33 Sort 65536 NaN 21.036047 12255232.0\n", + "34 Sort 131072 NaN 68.694607 25952256.0\n", + "35 Sort 262144 NaN 195.667437 54788096.0\n", + "\n", + "[64 rows x 5 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "assert results\n", + "\n", + "df = pd.DataFrame(results['benchmarks'])\n", + "df = df[df['run_type'] == 'iteration'].copy()\n", + "\n", + "parts = df['name'].str.split('/')\n", + "df['operator'] = parts.str[1]\n", + "df['left_rows'] = parts.str[2].astype(int)\n", + "df['right_rows'] = parts.apply(lambda p: int(p[3]) if len(p) == 5 else None)\n", + "\n", + "df['real_time_ms'] = df['real_time'] / 1e6\n", + "\n", + "df[['operator', 'left_rows', 'right_rows', 'real_time_ms', 'model_cost']].sort_values(['operator', 'left_rows'])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d49c7121", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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/3zHfarifpPAogp88803jBs3jrfeeoujR4/SqVMnevfuzYULF0rMHxcXR58+fejUqRNHjx5lypQpjB07lrVr11ZxzUVZr12h06dPEx8fb/zx9/evohoLgJs3b9KsWTMWLlxYqvxyz9UcZb12heSeq147d+5k9OjR7N+/n61bt5Kfn8/DDz/MzZs3b7uP3HfV716uWyG556pX/fr1mTlzJhEREURERNCtWzf69u3LyZMnS8xfZfebEpWuTZs26uWXXy6S1qhRIzV58uQS87/xxhuqUaNGRdJeeukl1a5du0qroyhZWa/d9u3bFaBSUlKqoHaiNAC1bt26O+aRe65mKs21k3uuZkpISFCA2rlz523zyH1X85Tmusk9V3M5Ojqq//73vyVuq6r7TVosKllubi6HDx/m4YcfLpL+8MMPs3fv3hL32bdvX7H84eHhREREkJeXV2l1FUXdy7Ur1KJFCzw8POjevTvbt2+vzGqKCiD3XO0n91zNkpaWBoCTk9Nt88h9V/OU5roVknuu5tDr9axevZqbN28SGhpaYp6qut8ksKhkSUlJ6PV63NzciqS7ublx9erVEve5evVqifnz8/NJSkqqtLqKou7l2nl4eLBkyRLWrl3L999/T2BgIN27d2fXrl1VUWVxj+Seq73knqt5lFKMHz+ejh078tBDD902n9x3NUtpr5vcczXH8ePH0el0WFhY8PLLL7Nu3TqCgoJKzFtV95tphZUk7kij0RT5rJQqlna3/CWli8pXlmsXGBhIYGCg8XNoaCgXL17ko48+onPnzpVaT1E+cs/VTnLP1Tyvvvoqf/zxB3v27LlrXrnvao7SXje552qOwMBAjh07RmpqKmvXrmXYsGHs3LnztsFFVdxv0mJRyerUqYOJiUmxN9wJCQnFIsdC7u7uJeY3NTXF2dm50uoqirqXa1eSdu3aERMTU9HVExVI7rn7i9xz1WfMmDFs2LCB7du3U79+/Tvmlfuu5ijLdSuJ3HPVw9zcnIYNG9KqVSs++OADmjVrxrx580rMW1X3mwQWlczc3JyWLVuydevWIulbt26lffv2Je4TGhpaLP+WLVto1aoVZmZmlVZXUdS9XLuSHD16FA8Pj4qunqhAcs/dX+Seq3pKKV599VW+//57fv31V3x8fO66j9x31e9erltJ5J6rGZRS5OTklLityu63Ch0KLkq0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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "fig, ax = plt.subplots(figsize=(8, 5))\n", + "for op, grp in df.groupby('operator'):\n", + " ax.plot(grp['model_cost'], grp['real_time_ms'], marker='o', label=op)\n", + "\n", + "ax.set_xlabel('model_cost')\n", + "ax.set_ylabel('real_time (ms)')\n", + "ax.set_title('Operator cost model vs measured time')\n", + "ax.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "dfeea54d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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9ZWvr6+ioqJ09uzZPNtfvnxZL774ourWrasyZcqoUqVK6tWrl06ePFlofQQAwCzOktfFAYU4AMB0mfpnutt1t0LsR48ePRQbG6tVq1Zp1apVio2NVVRUVJ7tL1y4oN27d2v06NHavXu3vvzySx0+fFidO3cuxF4CAGAOZ8lrqegPnjM1HQBguoJMdUtOTs6239PTU56engXuw4EDB7Rq1Spt375djRs3liTNmTNH4eHhOnTokGrUqJHjGF9fX61ZsybbvnfffVdhYWE6duyYKleuXOD+AADgbJxpanqPHj30559/atWqVZKkp556SlFRUVq+fHmu7a8ePK9fv77OnDmjoUOHqnPnztq5c2fhdjYXFOIAANMVJNiDg4Oz7R8zZozGjh1b4D5s27ZNvr6+tiJckpo0aSJfX19t3bo110I8N0lJSbJYLCpXrlyB+wIAgDNylkK8OAyeU4gDAEyXabUo087HnGS1O378uHx8fGz7b+RquCTFx8crICAgx/6AgADFx8fbdY5Lly5pxIgR6tGjR7a+AQBQHBQkr42ewSYVj8Fz7hEHAJgua4Td3k2SfHx8sm15hfrYsWNlsViuuWVNSbNYcv5xYbVac93/b5cvX9YjjzyizMxMzZgxo+DfDAAAnFRB8jo4ONh2H7evr68mTpx4w/0oDoPnXBEHAJiuMKe6DRo0SI888sg121SpUkX79u1TQkJCjtf++usvBQYGXvP4y5cv66GHHtKRI0e0du1aroYDAIqlguR1fmawjR07VuPGjbvmef/73/9KKvqD5xTiAADTWf+3wqq9bfPDz89Pfn5+120XHh6upKQk7dixQ2FhYZKkH374QUlJSWratGmex2UV4b/88ovWrVunChUq5K+DAAAUEQXJ66yZa/YoSYPnFOIAANNZrRZZ7bznzN52+VWrVi3dc8896tevn2bNmiXpygqs9913X7Z7zWrWrKmJEyeqS5cuSk9PV7du3bR7926tWLFCGRkZtilx5cuXl4eHR6H0FQAAMxR2XpekwXPuEQcAmK4g95wVho8//lh169ZVZGSkIiMjVa9ePX300UfZ2hw6dEhJSUmSpD///FPLli3Tn3/+qZCQEFWsWNG2bd26tfA6CgCACZwlr68ePN++fbu2b9+ufv365Tp4/tVXX0mSbfB8586d+vjjj22D5/Hx8UpLSyu8zuaBK+IAANNl5mOqm73tCqJ8+fJatGjRNdtYr/rLokqVKtm+BgCgOHOWvJauDJ4PGTJEkZGRkqTOnTtr+vTp2drkNnguSSEhIdnarVu3Tq1atSrcDv8LhTgAwHTO8lxSAACQN2fK66I+eE4hDgAwnTMFOwAAyB15bRwKcQCA6ZxpqhsAAMgdeW0cCnEAgOkYYQcAwPmR18Zh1XQAAAAAAByIK+IAANNlZl7Z7G0LAAAcj7w2DoU4AMB0THUDAMD5kdfGoRAHAJiOYAcAwPmR18ahEAcAmC5T+ViFtVB7AgAA8kJeG4dCHCWeh7tFY5+rpduqeCvxVKpenvSTkpLTs7W5t02gBvapplN/p0mS5n78hzbvOC13N4tGDKmh6lXL6PJlq95495B+OXLejI8BA61du1avT3xDmZmZ6v/UU3r44Yeyvb537169+OIIpaalqWuXBzR48GBJ0tGjRzXk2aFKTk5Ws2ZN9er48bJYLGZ8hCLHarXKaufQub3tABQ/N5LZkREB6tE1WJLk4iJVCS6jjj23KuVceo73QdFAXjseeW2cfK2aXqVKFdWsWVPp6f/8wgoNDdX69esL9OZjx45VWlpagY5t1aqVVqxYIUnq06ePpk+fXqDz5Ndvv/2m7t27q2rVqqpbt64aNGiguXPnFvr7VqlSRT/++ON124WEhOjixYuF3p/ipHP7ijqRcEkP99+hjdtPqWe3yrm2+2Zdgvo8u0t9nt2lzTtOS5Luv6eiLl7MUK/Bu/TypJ816MnbHNl1FIL09HS99vpELfpooZZ9vVSzZs/W2bNns7UZM3acYmKmac3qb/X92nU6dPiwJGnS5Ml6dshgrVv7vU6dOq1169aZ8AmKpqypbvZuuD4ym8wujm4ks1dvSLTte2fub9r7cxJFeBFGXpuDvDZOvh9flpqaqnnz5hny5uPGjStwqJshPj5ezZs3V2RkpI4cOaL9+/fru+++y/ZHTpbc9jlCbGysSpUqZcp7F1XNwiro27UJkqRVaxPUvFEFu4+9Nbi0du49I0mKS7ikCjd5qHw590LpJxxj7759ql69uoKCguTt7a1WrSK0cdMm2+sJCQnKSE9XzZo15ebmps6dOmnt92tltVq1Z0+s7r77bklSly4P6Pu1a836GEWONfOflVivt1mZ62Y3MpvMLm5uJLOv1rq5v77flGhk1+Bg5LU5yGvj5LsQHzdunF599VVduHAh2/6UlBT169dPYWFhqlevngYMGKDLly9LkiZMmKBatWopJCREISEhOnr0qAYMGCBJatq0qUJCQpSYmHjNc/z8889q3LixGjRooMcee0yXLl26bl937typ8PBw1atXT2FhYdqyZYvttY8++kh169ZVvXr11LFjR504cUKStGDBArVr104PPvigQkJCFBERoWPHjkmS3nvvPbVo0UL9+vWznad8+fK2z9KnTx8NGTJE99xzj+rXry9Jmjx5smrXrq26devqscceU1JSkiRp+fLlqlevnkJCQlSnTh19/fXXeX6v/u3XX39V27ZtbccvXbrU9prFYtG5c+ckXRmRHzdunJo2baqqVatqwoQJ1/x+paamKjk5OdtWEviV99Rff6dKklLOp8u7TO53bLRrGaAP32mol4fVUFnvK21+O3JeLZv4yWKRqt1aRjdXLCX/Cp4O6zuMl5iQoKDAQNvXQUFBSkhIsH2dkJiowKCcr585c0a+vr62qW0V/3Ucro0R9sJBZhfPzC6peS3dWGZncXWRmjf20/qtpwq9vyg85LU5yGvj5LsQb9CggVq2bKlp06Zl2z98+HC1bNlSO3bs0N69e5Wenq7p06frzJkzmjJlinbv3q3Y2Fht3bpVgYGBmjlzpiRp69atio2NVUBAQJ7nkKSoqCgNHDhQu3fv1uDBg/Xf//73mv1MS0tT165dNXbsWO3bt09Tp05Vt27ddP78ef344496/vnntWrVKu3bt09NmzbVU089ZTt28+bNev311xUbG6uOHTvaQnvXrl0KDw+/5vtu3rxZX3zxhX766Sd98803mj9/vrZs2aL9+/erTJkyeumllyRJL7/8smbOnKnY2Fjt27dPEREReX6v/u2xxx7TQw89pH379unzzz/Xk08+qePHj+fan7Nnz2rr1q3asWOH3nzzTdsfL7mZOHGifH19bVtwcPA1P2txYc8tQZt3nFb3vj+o95BdOnbiogb/bwr68jXxSjmXrvkxDdX7oco6+EuKMjL4rVOU5RYaFlmu08CS631Q2Y7DNWVa87fBPmR28czskprX0o1ldpaG9W/S73+c19mky4XUSzgCeW0O8to4+S7EpSsjwDExMTp9+rRt39KlS/Xmm28qJCREd911lzZt2qRffvlFPj4+ql69unr27KlZs2bp77//lpeXV67nzesc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r9akSZPk5+cnLy8vffjhh4qNjdW7776rjIwMvf322/L19TW7u3CQVatWaciQIerQoYO+/PJL9e7dWyNGjFBCQoKqV6+uZ599VtOmTTO7mwBQYpDXyA15jeKMqekotjIyMmSxWLRp0yYNGjRIzz77rIYNG6Y//vhDrVq1UkhIiB5//HFZLBYdP37c7O7CQU6ePKnRo0dr1qxZevvtt/XVV1/pl19+0axZs3TbbbfpwIEDuueee8zuJgCUGOQ1ckNeo7ijEEexExcXp8zMTLm6uiojI0P79u3TU089pc6dO6tJkybasGGDLl26pM8++0zNmzfXW2+9pTp16pjdbRSSI0eOaPr06crIyJB0ZeXVW265RQ0aNJAkhYaG6tFHH9WiRYt09uxZ1ahRQ+3btxeThQCgcJHXuBp5jZKGQhzFyuXLlzV69Gh16tTJtspmRkaGPv30U9vjLSSpUaNGtv8uX768GV2Fg/z1118aPXq0YmJilJmZqdKlS+u3337TmDFjbG0qVaqkKlWqZLu/zGKxmNFdACgRyGv8G3mNkoZCHMWKu7u7hgwZIl9fX/Xo0UMZGRmKiopSw4YN9c477+jIkSP6+eeftWnTJt1yyy1mdxeFLDMzU2FhYVq5cqVmzJihSZMmydPTU8uWLdPixYv10EMPafLkyerfv7/69OmjUqVKmd1lACgRyGtcjbxGScRibSh2rFarDhw4oFdeeUWenp5atGiRtm7dqoULF2rbtm0qV66coqOj9cADD5jdVRSirIV/Tp06JT8/P23btk09e/ZUv379NGLECCUmJuqdd96Rr6+vGjRooDZt2tiOAQAUPvIaEnmNkotCHMVC1i/kc+fOydvbW5L022+/afjw4fL19dX8+fPl4uKiY8eOqVSpUvL39+eXeDGW9W+7YsUKvfXWW/roo490yy23aOvWrerVq5f69Omjl19+2exuAkCJQ17jauQ1SjIKcRQbq1at0qRJk3TzzTerUqVKmjx5sn7//XcNHz5cXl5eWrRokVxdXQn0EmLFihV6+eWX9dZbb6lNmzY6f/68ypQpoz179uihhx7S008/rejoaLO7CQAlDnmNq5HXKKm4RxzFwg8//KA333zTdu/Q119/rccee0zVqlXTpEmTZLFYbL/ECfXi79KlS/r000/1wQcfqFGjRvq///s/tW3bVi+//LLq1Kmj+fPna9SoUfr+++/N7ioAlCjkNa5GXqMkoxBHkXf48GGNGDFCjz76qB555BG1bdtWu3fv1t69e/Wf//xHt99+u/r27auLFy8qLS3N7O6ikGRN7jl79qy8vLxUunRpdenSRb1799aff/6phx9+WIcOHdJvv/2m5s2bq2/fvoqLizO51wBQcpDXkMhrIAuFOIqsrF/kR48e1cWLFzV37lydP39eklSmTBk1bdpUFotFLi4u+v3337Vz505dunTJzC6jkGRNX1y+fLmio6OVkJCg2bNn64UXXtCrr76qF154QQ888IB+/fVXZWRk6JdfftHBgwcVFhZmdtcBoNgjr5GFvAb+wT3iKHKyfomnpKSobNmykqS9e/fqjTfekLe3t8aPH6+kpCTdf//9+uCDD9SsWTP95z//UdWqVXXnnXea3HsUlhUrVuiVV17RO++8o+bNm0u68jgUFxcXff7555owYYJeffVVde7cWZcuXdKFCxd4Ji0AFCLyGrkhr4ErKMRRpGSF+urVqzVlyhSVL19eZcuW1Zw5c7Rz504NHz5cx44dU3h4uAYNGqSmTZua3WU4wKVLl9SzZ08NGzZMd955p9asWaMvv/xS/v7+eumll/Tee++pcePG6tSpkzIyMuTq6mp2lwGgWCOvkRvyGvgHhTiKnA0bNqhfv36aNGmS/P399dxzz6lcuXJatWqVduzYoQULFigzM1MzZ86UJFZdLQEuXryohx9+WKVLl9bZs2fVuHFjlSpVSr/++qvGjRunoKAgVuAFAAcjr/Fv5DXwDwpxFDlvv/22rFarhg4datsXGhqql156SQ888IDWrVun999/X1WrVtWkSZPk4sJSCMVNVkAfOnRIFotFN910kzIyMvTFF1+ocePGatSokfbs2aNevXpp6dKluu2228zuMgCUOOQ1yGsgb25mdwDIr8uXL+vzzz9Xz5495efnJ0lq3LixbaGXu+++W+7u7qpevTqhXkxlLfQyfvx4BQUF6a+//lLPnj01aNAgSdKyZcs0ZswYTZw4kVAHAJOQ1yCvgbxRiMOpZY2knjhxQunp6QoODtajjz6qo0eP6u2339bTTz+ts2fPasuWLerZs6ckycXFRS1btjS55yhMBw4c0EsvvaTFixerYsWK+vHHHzVw4ED5+vqqe/fu+vTTTzVhwgR17NjR7K4CQIlAXiM35DWQNwpxODWL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oKZNmyowMFARERHavHmz3e8LAICRimNe//LLL6pUqZKqVq2qRx55RL///vuNfpuUlJQkHx8fubm53fC5gOLAYrVarWZ3AkB2rVq1UlJSki2kJWnUqFFasmSJDhw4IIvFIkmaMWOGXnzxRSUlJcnFxUUNGzZUjx49NHz4cHXp0kWNGjXSuHHjdOrUKZ0/f14VK1bUgQMHVLNmTdWrV08PPvigxowZY1efEhMTlZycfM02VapUsStgrVar7r//fp05c0abNm2y7V+8eLG8vb1166236siRIxo9erTS09O1a9cueXp6avv27QoPD1f58uU1ZcoUhYSEaOHChZoxY4Z+/PFHVa9e3a7PAgCAEYpjXn/zzTe6cOGC7rjjDiUkJGjChAk6ePCgfvrpJ1WoUCFb2z/++ENVq1bVnj17FBISkuf7nT59Wg0aNFBUVJRtQAIo6RiSApxUaGhotq8PHDig8PBwW6hLUrNmzXTu3Dn9+eefqly5slq1aqX169crOjpamzZt0oQJE7RkyRJt3rxZZ8+eVWBgoGrWrClJGjJkiJ5++mmtXr1abdu21YMPPqh69erl2Z+AgAAFBAQY8tkGDRqkffv25biS/fDDD9v+u06dOgoNDdWtt96q//znP+ratattGl///v31+OOPS5Luuusuff/99/rggw80ceJEQ/oHAIC9ilted+jQwfbfdevWVXh4uG677TZ9+OGHio6Ozvf5kpOT1bFjR9155512DyYAJQFT0wEnVaZMmWxfW63WbKGetU+SbX+rVq20adMm7d27Vy4uLrrzzjsVERGhDRs2ZJvmJkl9+/bV77//rqioKO3fv1+hoaF699138+yPUVPTBw8erGXLlmndunW65ZZbrtm2YsWKuvXWW/XLL7/YvpakO++8M1u7WrVq5WtaPAAARimueX3156tbt64ti/MjJSVF99xzj7y9vfXVV1/J3d093+cAiiuuiANFxJ133qklS5ZkC/itW7eqbNmyuvnmmyX9c99ZTEyMIiIiZLFYFBERoYkTJ+rMmTN69tlns50zODhYAwYM0IABAzRy5EjNmTNHgwcPzvX9x48fr+eee+6afaxUqVKer1mtVg0ePFhfffWV1q9fr6pVq173M58+fVrHjx+3FeBVqlRRpUqVcjz27PDhw9lG8AEAMEtRz+t/S01N1YEDB9SiRQu7j5GuXAlv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u5e2bdvy008/YWtry/79+/H19cXf35/169eTIkUKTp8+TcaMGY3uqoiISJKlvBZJmlSIiyRiW7ZsoWfPnkyZMoVy5crx9OlTgoODWbduHZkyZaJ06dJGd1FERCTJU16LJD0qxEUSua1bt9KpUydmzZqFt7d3uDVlYf/9tc5MRETEWMprkaRFhbhIErBt2zZatmzJggULKFeunNHdERERkUgor0WSDhXiIknEli1bsLOzo0KFCkZ3RURERKKgvBZJGlSIiyQxuuWJiIhIwqe8FkncVIiLiIiIiIiIxCPdjFBEREREREQkHqkQFxEREREREYlHKsRFRERERERE4pEKcREREREREZF4pEJcREREREREJB6pEBcRERERERGJRyrERUREREREROKRCnERERERERGReKRCXERERERERCQeqRAXERERERERiUcqxEVERERERETikQpxERERERERkXikQlxEREREREQkHqkQFxEREREREYlHKsRFRERERERE4pEKcREREREREZF4pEJcREREREREJB6pEBcRERERERGJRyrERUREREREROKRCnERERERERGReKRCXERERERERCQeqRAXERERERERiUcqxEVERERERETikQpxERERERERkXikQlxEREREREQkHqkQFxEREREREYlHKsRFRERERERE4pEKcREREREREZF4pEJcREREREREJB6pEBcRERERERGJRyrERUREREREROKRCnERERERERGReKRCXERERERERCQeqRAXERERERERiUcqxEVERERERETikQpxERERERERkXikQlxEREREREQkHqkQFxEREREREYlHKsRFRERERERE4pEKcREREREREZF4pEJcREREREREJB6pEBcRERERERGJRyrERUREREREROKRCnERERERERGReKRCXERERERERCQeqRAXERERERERiUcqxEVERERERETikQpxERERERERkXikQlxEREREREQkHqkQFxEREREREYlHKsRFRERERERE4pEKcREREREREZF4pEJcREREREREJB6pEBcRERERERGJRyrERUREREREROKRCnERERERERGReKRCXERERERERCQeqRAXERERERERiUcqxEVERERERETikQpxERERERERkXikQlxEREREREQkHqkQFxEREREREYlHKsRFRERERERE4pEKcREREREREZF4pEJcREREREREJB6pEBcRERERERGJRyrERUREREREROKRCnERERERERGReKRCXERERERERCQeqRAXEavZvn079erVI0OGDJhMJlasWBGt/bZt20axYsVwcnIie/bsTJs2LdzzM2fOpHz58qROnZrUqVNTrVo19u/fH+XxRo4ciclkonv37m/wakRERBKvkSNHUqJECVxcXEiXLh0NGzbkzJkzr93vdZn9ol9//RWTyUTDhg3DbQ8KCmLAgAFky5aNZMmSkT17doYOHUpISMibviyRt4YKcZEE4tmzZ0Z34Y09fPiQQoUKMWXKlGjvc+HCBd577z3Kly/PkSNH+Prrr+natSvLli2ztPH19eWTTz5h69at7Nmzh8yZM1OjRg2uXr0a4XgHDhxgxowZFCxY0CqvSURE5GWJIbO3bdtGp06d2Lt3Lxs3biQoKIgaNWrw8OHDKPeJTmaHuXTpEl9++SXly5eP8NyoUaOYNm0aU6ZM4dSpU4wePZoxY8YwefJkq75GkQTNLCKGqFixorlTp07mHj16mNOmTWuuUKGC2Ww2m319fc0lSpQwOzg4mD09Pc19+vQxP3/+3Gw2m82rVq0yu7q6moODg81ms9l85MgRM2D+8ssvLcdt166d+eOPPzabzWbzxYsXzXXr1jWnSpXKnDx5cnPevHnNa9eujZfXB5iXL1/+2na9e/c2586dO9y29u3bm0uXLh3lPkFBQWYXFxfzvHnzwm0PDAw058yZ07xx40ZzxYoVzd26dYtN10VERMJJ7JltNpvNN2/eNAPmbdu2RdkmupkdFBRkLlu2rHnWrFnmli1bmhs0aBDu+Tp16pg///zzcNsaN25sbt68+Zu9CJG3iD4RFzHQvHnzsLOzY9euXUyfPp2rV6/y3nvvUaJECY4dO8aPP/7I7NmzGTZsGAAVKlQgMDCQI0eOAKGj2W5ubmzbts1yTF9fXypWrAhAp06dePr0Kdu3b+fPP/9k1KhRODs7R9mfDh064Ozs/MrH5cuXrfo92LNnDzVq1Ai3rWbNmhw8eJDnz59Hus+jR494/vw5adKkCbe9U6dO1KlTh2rVqlm1jyIiIok9s/39/QEiZOuLopvZQ4cOxd3dndatW0d6nHLlyrF582bOnj0LwLFjx9i5cyfvvfdetPsr8razM7oDIknZO++8w+jRoy1f9+/f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RERExIrUiIuIiIiIiIhYkRpxEREREREREStSIy4iIiIiIiJiRWrERURERERERKxIjbiIiIiIiIiIFakRFxEREREREbEiNeIiIiIiIiIiVqRGXERERERERMSK1IiLiIiIiIiIWJEacRERERERERErUiMuIiIiIiIiYkVqxEVERERERESsSI24iIiIiIiIiBWpERcRERERERGxIjXiIiIiIiIiIlakRlxERERERETEitSIi4iIiIiIiFiRGnERERERERERK1IjLiIiIiIiImJFasRFRERERERErEiNuIiIiIiIiIgVqREXERERERERsSI14iIiIiIiIiJWpEZcRERERERExIrUiIuIiIiIiIhYkRpxEREREREREStSIy4iIiIiIiJiRWrERURERERERKxIjbiIiIiIiIiIFakRFxEREREREbEiNeIiIiIiIiIiVqRGXERERERERMSK1IiLiIiIiIiIWJEacRERERERERErUiMuIiIiIiIiYkVqxEVERERERESsSI24iIiIiIiIiBWpERcRERERERGxIjXiIiIiIiIiIlakRlxERERERETEitSIi4iIiIiIiFiRGnERERERERERK1IjLiIiIiIiImJFasRFRERERERErEiNuIiIiIiIiIgVqREXERERERERsSI14iIiIiIiIiJWpEZcRERERERExIrUiIuIiIiIiIhYkRpxEREREREREStSIy4iIiIiIiJiRWrERURERERERKxIjbiIiIiIiIiIFakRFxEREREREbEiNeIiIiIiIiIiVqRGXERERERERMSK1IiLiIiIiIiIWJEacRERERERERErUiMuIiIiIiIiYkVqxEVERERERESsSI24iIiIiIiIiBWpERcRERERERGxIjXiImIR48aNw2AwMGDAgBeO3blzJyVKlMDJyYkcOXIwY8aMOMsfPXrE6NGjyZkzJ05OThQpUoRNmzbFe56rV6/Stm1b0qdPj7OzM0WLFuXw4cPmeksiIiKvjaioKIYNG0b27NlJlSoVOXLkYPTo0cTExDzzMdevX6d169bkzZsXOzu7BDM+MZk9btw4SpUqhaurK15eXjRp0oSzZ8+a+y2KJGtqxEWSiYcPH9q6BLP57bffmDVrFoULF37h2AsXLlCvXj0qVqzI0aNH+eSTT+jXrx8rVqwwjRk2bBgzZ85kypQpnDp1ih49etC0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Ivq5MK+JNIuzphOR2qTOuhofH49u3brB2dkZq1atQufOnTFmzBg8efIEYWFhqFq1Kq5evQpdXV2UK1dO6rKJiIh+KsxrIs1jI05EapeYmIiRI0di7dq1WL16Nbp16wa5XK68ruu7d++gra0tcZVEREQ/N+Y1kebw0HQiUjtdXV0MHjwYRYoUyfDSJgx1IiIi6TGviTSHjTgRacQvv/yChg0b4siRI0hKSlJuXSciIqLsg3lNpBk8NJ2INCYgIADx8fGoVauW1KUQERHRZzCvidSPjTgRERERERGRBvFYEyIiIiIiIiINYiNOREREREREpEFsxImIiIiIiIg0iI04ERERERERkQaxESciIiIiIiLSIDbiRERERERERBrERpyIiIiIiIhIg9iIExEREREREWkQG3EiIiIiIiIiDWIjTkRERERERKRBbMSJiIiIiIiINIiNOBEREREREZEGsREnIiIiIiIi0iA24kREREREREQaxEaciIiIiIiISIPYiBMRERERERFpEBtxIiIiIiIiIg1iI05ERERERESkQWzEiYiIiIiIiDSIjTgRERERERGRBrERJyIiIiIiItIgNuJEREREREREGsRGnIiIiIiIiEiD2IgTERERERERaRAbcSIiIiIiIiINYiNOREREREREpEFsxImIiIiIiIg0iI04ERERERERkQaxESciIiIiIiLSIDbiRERERERERBrERpyIiIiIiIhIg9iIExEREREREWkQG3EiIiIiIiIiDWIjTkRERERERKRBbMSJiIiIiIiINIiNOBEREREREZEGsREnIiIiIiIi0iA24kREREREREQaxEaciIiIiIiISIPYiBMRERERERFpEBtxIiIiIiIiIg1iI05ERERERESkQWzEiYiIiIiIiDSIjTgRERERERGRBrERJyIiIiIiItIgNuJEREREREREGsRGnIiIiIiIiEiD2IgTERERERERaRAbcSIiIiIiIiINYiNOREREREREpEFsxImIiIiIiIg0iI04ERERERERkQaxESciIiIiIiLSIDbiRERERERERBrERpyIiIiIiIhIg9iIExEREREREWkQG3EiIiIiIiIiDWIjTkRERERERKRBbMSJiIiIiIiINIiNOBEREREREZEGsREnIiIiIiIi0iA24kREREREREQaxEaciIiIiIiISIPYiBMRERERERFpEBtxIiIiIiIiIg1iI05ERERERESkQWzEiYiIiIiIiDSIjTgRERERERGRBrERJyIiIiIiItIgNuJEREREREREGsRGnIiIiIiIiEiD2IgTERERERERaRAbcSIiIiIiIiINYiNOREREREREpEFsxImIiIiIiIg0iI04ERERERERkQaxESciIiIiIiLSIDbiRERERERERBrERpyIiIiIiIhIg9iIExEREREREWkQG3EiIiIiIiIiDWIjTkRERERERKRBbMSJiIiIiIiINIiNOBEREREREZEGsREnIiIiIiIi0iA24kREREREREQaxEaciIiIiIiISIPYiBMRERERERFpEBtxIiIiIiIiIg1iI05ERERERESkQWzEiYiIiIiIiDSIjTgRERERERGRBrERJyIiIiIiItIgNuJEREREREREGsRGnIiIiIiIiEiD2IgTERERERERaRAbcSIiIiIiIiINYiNOREREREREpEFsxImIiIiIiIg0iI04ERERERERkQaxESciIiIiIiLSIDbiRERERERERBrERpyIiIiIiIhIg9iIExEREREREWkQG3EiIiIiIiIiDWIjTkTfzN/fH25ubihSpAhkMhn279+f4bjAwEA0b94cxsbGMDQ0RI0aNfDkyZPPPu/evXvh4OCAfPnyQV9fH7a2tti6dWuaMSkpKZgwYQJKliyJPHnyoFSpUpg2bRrkcnmGz9mvXz/IZDIsWbLkW39cIiKiHGnlypWoXLkyjIyMYGRkBEdHRxw9elS5Pjk5GWPGjEGlSpWgr6+PIkWKoGvXrnj+/PkXn9fZ2RkymSzdrVmzZmnGhYSEoHPnzihYsCDy5s0LW1tbXL58Wbk+NjYWgwYNQrFixZAnTx6UK1cOK1euVO0vgSib0ZG6AKKfVVJSEnLnzi11Gd8lLi4ONjY26NGjB1q3bp3hmAcPHqB27dro1asXpk6dCmNjYwQGBkJPT++zz1ugQAGMHz8e1tbWyJ07Nw4dOoQePXrA1NQUjRo1AgDMnTsXq1atwubNm1GhQgVcunQJPXr0gLGxMYYOHZrm+fbv34+LFy+iSJEiqvvhiYjop/Aj5HWxYsUwZ84clClTBgCwefNmtGjRAgEBAahQoQLi4+Nx5coVTJw4ETY2NoiIiMDvv/+O5s2b49KlS5993r179yIpKUl5//Xr17CxsUHbtm2VyyIiIlCrVi24uLjg6NGjMDU1xYMHD5AvXz7lmGHDhsHHxwceHh6wtLTEiRMnMGDAABQpUgQtWrRQ/S+EKDsQRKQRTk5OYuDAgWLYsGGiYMGCom7dukIIIXx9fUXVqlVF7ty5hbm5uRgzZoxITk4WQghx8OBBYWxsLN69eyeEECIgIEAAECNHjlQ+b9++fUWHDh2EEEI8evRI/PrrryJfvnwib968onz58uLw4cMa+fkAiH379qVb3r59e9G5c+fvfn47OzsxYcIE5f1mzZqJnj17phnTqlWrdK/17NkzUbRoUXHz5k1RokQJsXjx4u+uhYiIflw/el6nyp8/v1i3bt1n1//7778CgHj8+HGmn3Px4sXC0NBQxMbGKpeNGTNG1K5d+4uPq1Chgpg2bVqaZVWqVEmT+0Q/Gh6aTqRBmzdvho6ODs6ePYvVq1cjJCQETZs2RdWqVXHt2jWsXLkS69evx4wZMwAAdevWRUxMDAICAgAAfn5+KFSoEPz8/JTP6evrCycnJwDAwIEDkZiYCH9/f9y4cQNz586FgYHBZ+vp378/DAwMvnj70iHkXyOXy3H48GH88ssvaNSoEUxNTVG9evXPHsKeESEEvLy8cPfuXdStW1e5vHbt2vDy8sK9e/cAANeuXcOZM2fQtGnTNK/fpUsXjBo1ChUqVPjmn4OIiH4uP3Jev3v3Djt27EBcXBwcHR0/Oy4qKgoymSzNnuuvWb9+PTp06AB9fX3lsoMHD8LBwQFt27aFqakp7OzssHbt2jSPq127Ng4ePIiQkBAIIeDj44N79+4pj4Ij+iFJvSWA6Gfh5OQkbG1t0ywbN26csLKyEnK5XLnsr7/+EgYGBsqt6lWqVBELFiwQQgjh7u4uZs6cKXLnzi2io6PFixcvBAARGBgohBCiUqVKYsqUKZmuKSwsTNy/f/+Lt9St/V+DDPaIp9aXN29esWjRIhEQECBmz54tZDKZ8PX1/eLzRUZGCn19faGjoyN0dXXF+vXr06yXy+Xijz/+EDKZTOjo6AiZTCZmzZqVZsysWbNEgwYNlL9f7hEnIqKv+VHz+vr160JfX19oa2sLY2PjL+6Bf/v2rbC3txedOnXKdI0XL14UAMTFixfTLNfV1RW6urpi7Nix4sqVK2LVqlVCT09PbN68WTkmMTFRdO3aVQAQOjo6Infu3GLLli2Zfm2inIjniBNpkIODQ5r7gYGBcHR0hEwmUy6rVasWYmNj8ezZM1hYWMDZ2Rm+vr4YPnw4Tp8+jRkzZmDPnj04c+YMIiMjYWZmBmtrawDAkCFD8L///Q8nTpxA/fr10bp1a1SuXPmz9ZiamsLU1FQ9PyygnDitRYsWGDZsGADA1tYW586dw6pVq5R7BjJiaGiIq1evIjY2Fl5eXhg+fDhKlSoFZ2dnAMDOnTvh4eGB7du3o0KFCrh69Sp+//13FClSBN26dcPly5fx559/4sqVK2l+v0RERF/zI+a1lZUVrl69isjISOzZswfdunWDn58fypcvn2ZccnIyOnToALlcjhUrVmT6+devX4+KFSuiWrVqaZbL5XI4ODhg1qxZAAA7OzvcunULK1euRNeuXQEAS5cuxYULF3Dw4EGUKFEC/v7+GDBgAAoXLoz69et/189NlF3x0HQiDfr4UC1Acdj1p02iEAIAlMudnZ1x+vRpXLt2DVpaWihfvjycnJzg5+eX5jA3AOjduzcePnyILl264MaNG3BwcMCyZcs+W4+6D00vVKgQdHR00oV8uXLlvvq8WlpaKFOmDGxtbTFixAi0adMGs2fPVq4fNWoU/vjjD3To0AGVKlVCly5dMGzYMOWY06dP4+XLl7CwsICOjg50dHTw+PFjjBgxApaWlt/8MxER0Y/vR8zr3Llzo0yZMnBwcMDs2bNhY2ODP//8M82Y5ORktGvXDsHBwTh58iSMjIy+/ssCEB8fjx07dqB3797p1hUuXPiL3wPevn2LcePGYdGiRXBzc0PlypUxaNAgtG/fHgsWLMjU6xPlRNwjTiSh8uXLY8+ePWkC/ty5czA0NETRokUBfDjvbMmSJXBycoJMJoOTkxNmz56NiIiIdDOEFy9eHP3790f//v0xduxYrF27FoMHD87w9adNm4aRI0d+scbvmWk8d+7cqFq1Ku7evZtm+b1791CiRIksPZcQAomJicr78fHx0NJKuy1RW1tbuRe+S5cu6baiN2rUCF26dEGPHj2y9NpERPRz+xHz+tNcTW3C79+/Dx8fHxQsWDDTz7Vr1y4kJiaic+fO6dbVqlXri98DkpOTkZyc/MVMJ/oRsREnktCAAQOwZMkSDB48GIMGDcLdu3cxefJkDB8+XBlIxsbGsLW1hYeHh3LLdd26ddG2bVskJycrD9UGgN9//x1NmjTBL7/8goiICHh7e6NcuXKfff3vPdQtNjYWQUFByvvBwcG4evUqChQoAAsLCwCKPdft27dH3bp14eLigmPHjsHT0xO+vr6ffd7Zs2fDwcEBpUuXRlJSEo4cOYItW7akuaaom5sbZs6cCQsLC1SoUAEBAQFYtGgRevbsCQAoWLBgui8RuXLlgrm5OaysrL75ZyYiop9PTs/rcePGoUmTJihevDhiYmKwY8cO+Pr64tixYwCAlJQUtGnTBleuXMGhQ4fw7t07hIaGAlBcUvRrl29bv3493N3dM2zehw0bhpo1a2LWrFlo164d/v33X6xZswZr1qwBABgZGcHJyQmjRo1Cnjx5UKJECfj5+WHLli1YtGjRN//MRNmedKenE/1cnJycxNChQ9Mt/9LlUFKNGDFCABA3b95ULrOxsREmJiZpJo4ZNGiQKF26tNDV1RUmJiaiS5cuIjw8XG0/k4+PjwCQ7tatW7c049avXy/KlCkj9PT0hI2Njdi/f3+a9d26dRNOTk7K++PHj1eOz58/v3B0dBQ7duxI85jo6GgxdOhQYWFhIfT09ESpUqXE+PHjRWJi4mfr5WRtRET0NT9iXvfs2VOUKFFC5M6dW5iYmAhXV1dx4sQJ5frg4OAM8xyA8PHxUY77NK+FEOLu3bsCQJrn+5Snp6eoWLGi0NXVFdbW1mLNmjVp1r948UJ0795dFClSROjp6QkrKyuxcOHCNL8zoh+NTIj3J7gQEUnE2dkZzs7OmDJlitSlEBER0Wcwr4lUh404EUkqJiYG5cuXR2Bg4BevoUpERETSYV4TqRYbcSIiIiIiIiIN4uXLiIiIiIiIiDSIjTgRERERERFJavbs2ahatSoMDQ1hamoKd3f3dJe+y4ifnx/s7e2hp6eHUqVKYdWqVRqo9vuxESciIiIiIiJJ+fn5YeDAgbhw4QJOnjyJlJQUNGzYEHFxcZ99THBwMJo2bYo6deogICAA48aNw5AhQ7Bnzx4NVv5teI44ERFJKiEhAUlJSVl6TO7cuaGnp6emioiIiOhTms7rV69ewdTUFH5+fqhbt26GY8aMGYODBw8iMDBQuax///64du0azp8//02vqyk6UhdA2ZdcLsfz589haGgImUwmdTlElM0IIRATE4MiRYpAS+vbDrBKSEhAkTwGiMC7LD3O3NwcwcHBbMaJwLwmoq/73sz+nry+du1amrzW1dWFrq7uVx8bFRUFAChQoMBnx5w/fx4NGzZMs6xRo0ZYv349kpOTkStXrizVq0lsxOmznj9/juLFi0tdBhFlc0+fPkWxYsW+6bFJSUmIwDts1iuFvJk8WyoecnQLfYikpCQ24kRgXhNR5n1rZn9PXpuZmaVZPnny5K9ei14IgeHDh6N27dqoWLHiZ8eFhoame34zMzOkpKQgPDwchQsXzlStUmAjTp9laGgIADhz2j/HXC8yWXx961p2M2Hp5897yY7adigrdQlZ8otJhNQlZFmxpAdSl5ApMXFxsKnvrvys+B76OtrQl2lnaqxMZG1rPNGPLifmdZLII3UJWTZ8aojUJWRJ+54OUpeQJUULZu2Q5+zAKs9DqUvItNjYOFR3cv3uzP6WvH769CmMjIyUyzOzN3zQoEG4fv06zpw58/XX+eRIoNQzr7P7EUJsxOmzUv94DQwMVPJFWxOScmAjnit39v6Q+FRefaOvD8pGDAxSpC4hywyT9KUuIUtUEXSyXFqQyTK3hV3GqU2I0siJeZ2YAxtxnVw567M5Tw7La32DnNeIG+bJGRu+Pva9mf0teW1kZJSmEf+awYMH4+DBg/D39//q3ntzc3OEhoamWfby5Uvo6OigYMGCmX5NKbARJyIiyWlpy6CllbkvB1rynLXxioiI6EehzrwWQmDw4MHYt28ffH19UbJkya8+xtHREZ6enmmWnThxAg4ODtn6/HCAly8jIqJsQJZLlqUbERERaZ4683rgwIHw8PDA9u3bYWhoiNDQUISGhuLt27fKMWPHjkXXrl2V9/v374/Hjx9j+PDhCAwMxIYNG7B+/XqMHDlSZT+zunCPOBERSU5Lh3vEiYiIsjt15vXKlSsBAM7OzmmWb9y4Ed27dwcAvHjxAk+ePFGuK1myJI4cOYJhw4bhr7/+QpEiRbB06VK0bt06S68tBTbiREQkOVkuGWSZDHYZG3EiIiJJqDOvRSbmgNm0aVO6ZU5OTrhy5UqWXis7YCNORESS09KWQUs7k1vY37ERJyIikgLzWnXYiBMRkeRk2jLIMhnsMjDYiYiIpMC8Vh1O1kZERJJL3cKe2VtWzJ49G1WrVoWhoSFMTU3h7u6Ou3fvfvVxfn5+sLe3h56eHkqVKoVVq1Z9649HRET0Q1BnXv9s2IgTEZHkZFqyLN2yws/PDwMHDsSFCxdw8uRJpKSkoGHDhoiLi/vsY4KDg9G0aVPUqVMHAQEBGDduHIYMGYI9e/Z8749KRESUY6kzr382PDSdiIgkJ9PWgkw7c9uGZfj6ZC4fO3bsWJr7GzduhKmpKS5fvoy6detm+JhVq1bBwsICS5YsAQCUK1cOly5dwoIFC3LETKxERETqoM68/tmwESciIsllafKX9+ecRUdHp1muq6sLXV3drz4+KioKAFCgQIHPjjl//jwaNmyYZlmjRo2wfv16JCcnI1euXJmqlYiI6EfyLXlNGeOh6UREJDmZLAuHuskUwV68eHEYGxsrb7Nnz/7q6wghMHz4cNSuXRsVK1b87LjQ0FCYmZmlWWZmZoaUlBSEh4d/3w9LRESUQ31LXlPGuEeciIgkJ9NGprewy94f6fb06VMYGRkpl2dmb/igQYNw/fp1nDlz5uuv88kXiNTrm/KLBRER/ay+Ja8pY2zEiYhIclm6HIpQjDMyMkrTiH/N4MGDcfDgQfj7+6NYsWJfHGtubo7Q0NA0y16+fAkdHR0ULFgw069JRET0I/mWvKaMsREnIiLJybS0INPK5OQvmRyXSgiBwYMHY9++ffD19UXJkiW/+hhHR0d4enqmWXbixAk4ODjw/HAiIvppqTOvfzb87ZDKeXt7o36DhqjnWh87d+5Kt/7atWto3LgJXOq5YtmyZcrljx8/Rgv3lnCp54oJEycqDwNVt8TEBAwe0BeNXOugW+f2iHjzJt0YIQSmTByLRq510KZlMzx5/AgAkJSYiNEjh6J5swZo07IZAm/fUnu9VSsZYPmkkjiw0hoWRTI+FNcgrxYmDiyGpRNLYs7IEjDJr9jm5lTNCH9OKIk/J5TEskklsX+lNQzyqv9jICkxAYvHt8awDr9gxhBXREemP8fWc/sCjO1RBWN7VMGI38qhdxPFRFrJSYlYMb0rxnSzwfje1fDo/lW115uYmIARg7qjeYOq6NvFHRFvXqcbc/jAP2jnVhft3JzQv3trhIU+BwAEXLqA9s2d0aGFMzq3boBrV/5Ve70n/M6ihlsHVGvWDlv3HEyzLv5tAjr8bwQc3TqgTstOWLvtH+W6X7v9D85tusG5TTdY122K8XOXqL1WKQwcOBAeHh7Yvn07DA0NERoaitDQULx9+1Y5ZuzYsejatavyfv/+/fH48WMMHz4cgYGB2LBhA9avX4+RI0dK8SMQqUVOy2tA8fk8ZEBvNHKthe6d22aY2devBaBty6aoXM4Svt6n0qxbsWwxGrnWgluTerh+LUDt9daokg8bFlaG187qsCyeJ8MxhgbamDnGCusWVMKf08rDtFBuAEBFa0Osm18Ja+dXwsrZFVHhFwO115uclIC/prTG2K5lMX9EPcREpc/rt3HR+HPcr5jazw6T+9jgxr9HAQD3b5zBlL62mNrPDjMGVEPQrXNqrzcpMQHThrdHd7fyGNW7IaIiPj+HxwX/I2hkq4dHQYrvau9SUjBvQk/0a2OPPq3scOLAFrXXe8rHF86NfkXdhk3x9z+7062/ev0GXJu1QJ0GTbBk+Urlcv+z59C4RWu4NmuBqbPmqr1O0gw24t9p7969sLe3h62tLcqVKwdXV1fI5fJs83yalpKSgpmzZsNj6xYcPLAfq9esQWRkZJoxk6dMxZIli3HyxHF4efvg7r17AIC58+Zh6JDB8PH2Qnj4a/j4+Gik5n92/o3iFhY47nUarvUbYu2aFenG+HqfQkTEGxz3Oo3/DRyKhfMVk0Lt2rkdefPq4+Dhk1iydCXmzZmu9nqfhSVizuoQ3Lof/9kx7ZoWwu2gtxgyPRgbdoehWytTAIDfv9EYOiMYQ2cEY92uMNy+H4/YePX/ffl4roNpkVJYvOMe7Ou0gOe29CHi9ttIzN54BbM3XsGvHUfAoXYLAID3wbXQzWOAuZuvYei0ndi2fJTa6923ayuKFS+Bgyf/g3P9Jti0dmm6McUsLLF++yHs8vRDo2buWL5oJgDAukJlbN/nhR0HfDFt7nLMmjJarbWmpKRg4vyl2LduGbx3bcSyDR6IiEo7m/jgnp1x3nMHjm1bh4079+Lhk2cAgEObV8J392b47t6M0pYWaFov40t5aYI6r0u6cuVKREVFwdnZGYULF1bedu7cqRzz4sULPHnyRHm/ZMmSOHLkCHx9fWFra4vp06dj6dKlvHRZDsa8Tisn5jUA/LNzO4pZlMBxr7Nwrd8Ia9f8lW6MqakZps2cj6a/tkiz/N7dQPj7eePwcT/MW7gMM6ZOUHu9T58nYPLCe7geGPPZMZ1bFcWNOzHoPfIGVm15gr6dLAAA9x/Goe+YG+gz6gbm/PUAw/p8/Wie7+V/eC1MCpfE7C33YVerBY7umJN+zJG1KFaqEiavDkC/CTuwY8UwAIBF2SqYtPIyJq8OQM8xm+Dx5wC113t07waYFyuJTZ63UdOlOXZuXJDhuKTEBOzzWAqrilWVy875euJdSjJW776MBetPYt2ScWp9D6ekpGD6nPnYsWU9juz9ByvXbkBkZFSaMROmzsDyhfPgc9QTp3x8cffefcjlcoyZMBlr/1oKr8MHkJiYCP8zZ9VW59fwOuKqw0b8O4SGhqJ///7Yu3cvrl69isDAQMyfP/+bJ/JR9fNJ4dr16yhbtizMzc1hYGAAZ2cn+J8+rVwfFhaGdykpsLa2ho6ODpq7ucHbyxtCCAQEXIWLiwsAoGVLd3h5e2ukZh/vU2jeohUAoEXL1vD5ZOs5APj4nEJzd8UXcJd69XHlyiUIIfDgQRAcHWsBAIoVt8CrV6/w6tVLtdb74mUynoUlfXFMcXNdXLsTBwC49ygBduX1042p7WCE05ei0y1XhyvnPFG7UWcAQJ3GXXDl7KEvjr/g8w9quLYDAIQ8DkRF+3oAANMiJRH5JhSRr0O/9PDv5u9zAs1atAUA/OreHv7ex9ONsbGrCkNDxfnJ1uUr42XYCwBAnjx5oa2tDQCIi4uFut++V24Gwrp0SRQ2M4GBvj7q13GEz9kLyvV58+ihVlU7AIB+3jwoVaI4wl6l3WPwIuwVnoQ8h6O9rXqL/YLUy6Fk9pYVQogMb927d1eO2bRpE3x9fdM8zsnJCVeuXEFiYiKCg4PRv39/FfykJAXmdXo5Ma8BxYbx5i0Uedy8ZRv4ep9MN8a8cBGUK18BWrK0X3N9vE+hwfIfygABAABJREFU2a8toKOjg3LlKyA5OQmvXoaptd6Q0AQ8fZ7wxTEWRfPgyg1FQxYYFAuHysYAgMQkOVL7wrx62hq5IvO1C4fgWL8LAMCxQVdcO58+r2UyGRLeKjYsJLyNgXGBwgAAXb280HqffwnxMRp5P1zwOwzXZr8BAOq7dcJFv8MZjtu1aSF+bdsXurp6ymUymQwJCW/x7t07JLyNg1G+gtBS46HUV6/fwC9lSsPczAwGBvpwqVsHfh811KFhL/Hu3TuUs7aCjo4O3N2a4aSPL95EREBfXx/FixUFANSsUR1HT6T/rqop6szrnw0b8e/w4sWLdBP3VKlSBTKZDPfv30ezZs1QtWpV2NjYYMWKD3tZ9+7dC2trazg6OmL69OmQyWSIjY394vMBQGBgIBo1aoTKlSujcuXKWLVqFQBg0aJFqFq1Kuzs7FCtWjVcvHhR+XiZTIa5c+eievXqKFmyJDZu3KjW38nLsDCYf3TJH3Nzc4SFfQi5sJcvYWaefn1ERASMjY2VP2vhTx6n1ppfhsHMzBwAYGycDzHR6ZvTl2FhyksZaWlpwdg4HyIjImBlZQ2vUycgl8tx7+4dPHn8GC/D1NskZsajkAQ42hkCAOzK68PIQAeG+trK9VpaQPXKhjgX8Pkt9KoUEf4C+QspAsTAMD/iYiM/OzY6MhyPg66jokN9AIBF6Uq4dPoA5HI5njy4gbCQILwJD1Frva9ehsLEVPHFwsg4H2JivrzBwnPfDjjWclbev3jOD62a1MTgPh0wbmrGW+dVJfTlK5ibmijvFzEzxYuXGR+aFxIahtv3glC5nFWa5QdOeOPX+s5q/QLyNdzCTurEvE4vJ+Y1kLnM/pxXL0Nh+v6xAGBmVhhh2SCzHz6JR93qitOxqtoYw9goF4wMFKeUValkhE2LbTBnvDUWrQlWey2Rr58j3/u81jfMj/gM8rpus754/ug2RrQviiV/NEa7/h9y7vblU5jQszyWjGuKzkNXpnusqr1+9QKFTIsAAAyN8iM2JirdmNCQR7hz/V/UadAqzXJHp1+hp5cHvzUoiX5t7NF72Ncvgfk9wl6+SvOeK2xuhtBP33Nmpsr75mZmCAt7iYIFCiA+Ph537t6DXC7HSS9vhL5U706fL2Feqw4na/sONjY2cHR0hIWFBZycnFCzZk389ttvMDc3x2+//YatW7fC2toa8fHxqFGjBmrUqIFixYqhT58+OHfuHKysrDBv3ryvPl/RokWRkpKCFi1aYMaMGWjXTrGnMPVatl26dMHw4cMBABcuXECvXr1w8+ZN5fPq6enh4sWLCAwMRLVq1dClSxfo6KT/r09MTERiYqLyfnQWwi1VRqeJySD7ygBZhueXpXmcGmXm3LYMh8hkaN22A4KC7qN1i6YoWbo0KlaqBG1t6d9W/xx9jf4dzbFkfEncDX6LF6+S8O7dhx/Cxkofj0ISEBXzTiP1ZOX8wf/898K+lht0dBQTYjn/2gvPgm9jfC8HFLawQikrB7X/jrNSr/fJw7hx9TLWb/8wsVf1mk7Ye/Qcrl+9hFVL52LFhn++8Azf5zNvqXQSEhPRZ+RETBkxCPp5056nePC4FyaPGKSmCjNHJsvC5C8ybkOmrGFep5cT8xrI2udz+semX5YdjmLYvu85hvSyxJp5lXD7fixCQhPwTq4o9sqNaHQfdg3lyhqgR/tiGD3jjnqLycTv9+Z/x1CqXHWMWuiNJ/cDsG5uV0xZcw1aWloob18fMzbcxoPbF3Bg82QMn5v+iDLVlvv1etcuHoseQ9KfOnjnxr/IrZsH208G4/XLEPzRrykqVakNfYPMX40jKzJ878i+/J6TyRTX4l4ybw7GTp4GuVyOqvZ2iP9ojhNNY16rjvQdQw6mpaWFPXv24M6dO/Dz88PRo0cxc+ZM+Pv749atW+jQoYNybExMDG7fvo1nz56hSpUqsLJS7JHq27cvxowZ88Xnu3TpEhITE5GSkqIMdQAoVKgQACAgIAAzZ87E69evoaOjg9u3byMpKQm5cysm++jUqRMAoFy5ctDR0UFoaGiGl+6ZPXs2pk6d+l2/E7NPtu6FhobC1sbmw3ozM4SFpl1vamKCAgUKICoqCkIIyGQyvAgNhYmpKdRl6+YN2LtbMTFNoUImCAsLRf4CBRAVFQnDDC6HZGZuhrCwMFSsBMjlckRFRSJfvnyQyWSYMGmaclzTRi4o+pXLIn0LN5f8qF8rHwBgxOxgpHylf45PkGPRRsXkYTo6MqyaWgrxCR/Oe6rtYIQzl9S7N/zY7mXwO6zYo2NcwAwR4SEwylcIsTER0DfI99nHXfD6B827jFHe19HJhe7DPpyjPaJTeZiYW6q83r+3rMGBPdsBAAUKmuDVyxfIX6AgoqMilYegf+rW9QAsXzgDqzfvQ+7c6SfOq2zrgNAXIYh4E478BQqpvGYAKGxmgtCXr5T3n4e9hH2lCmnGCCEwaPwMuNZxRPOG9dKsCwkNw/OwV6hmW0kt9WVWVraccws7ZRXzOr2cktcAsHXzeuzdrZjToVChQl/N7M8xNTNPc9RaWNgLmJiovvZWTczRpJ7iSKX/jb2JlJQvN4tx8e8we9kDAEAuHRm2/GmLuPi0QR94PxamBXVhbKSDqOgUldZ7at9SnD2myGuj/GaIDA+BoXEhxMVEIG8GeX322CY07zoZAGBR1g4QArFR4TDK/+F3Wbp8Dbx59RQxka9gmM8k3XN8j/3b/8Lx/ZsBAPkLmiL85XMY5y+EmOgIGBgapxsfFBiAqb+3AQC8eR2Gcf9zw+xVh+FzdCeq1moEbW1tmBa2QFGLMngafBfWlaqmew5VMDczTfOeexEaBjubysr7Zu/3gKcKDQuDqYnis6OaQxXs2+EBANh7wFOjG78+xbxWHW6mUAFra2v069cP+/fvR40aNeDp6YlChQrh6tWryltwcDA6d+6cqS13nz7fwYMHPzs2KSkJrVu3xqJFi3Dz5k34+/tDCIGkpA/nEOvpfTgfRltbGykpGX+Ajx07FlFRUcrb06dPs/BbULCpXBn37t1DaGgoYmNj4evrhzp16ijXm5mZQUtbG3fu3EFKSgo8Dx2Cq2s9yGQy2NraKCd82bdvP1zruWT59TOrS7ee2Od5DPs8j8G1fkMcPLAXAHBg3x44u7imG+/s4oqD+/cAUJxjZmfnAJlMhvj4eOXMy4cPHUSFCpU+27R9D0+fCOUka19rwgFAP48WtN+/u91dC8Dv3w97S7S1FDOvn7+q3ka8cZvBysnXHOq0wJnjigA5fWwr7Go2y/AxUREvEfI4EBXsPvzfJ7yNQ2KCYmK6c6d2oKRVFeQ1SB+036tj177YccAXOw74wqV+Exw+oNiLfWj/TtRxaZhu/PNnTzB+VH/MWbIOJh8d6hjy9DHevVP8JwXdC8Tb+DgY5yug8npTValYDoFBD/Ei7BVi4+Jw6vR5uNSqnmbM9CUrkUdPFyP69Uj3+APHvdC8oYvke4V4zhlpAvP6g5yS1wDQpVsv7PM8gX2eJ1CvfiMcPKDI44P7dsPJpX6mn8fZxRWHDx1ASkoKAm/fgo5OrjSHqqvK3qOh6DNKMcna15pwANDPqw3t959rbX4tjFNnFEdQmJvqInXHo2XxPMijp4XoGNU24QBQv+UQTF4dgMmrA2BbqwXOn9oKADh/cgsq10if1wVMiyEwwAsA8OpFMN7GR8PAuBBevQiG/H3+hQTfROLbWOgbFUz3+O/l/ttArNz1L1bu+hc1XZrD67BiI/opz22oVrdpuvGbD9/BlqP3sOXoPZSrVA2zVnqiROlyMDEvhoB/FX/H0VFv8PjBbZgXtVR5valsK1fC3ftBCA0LQ2xsHHz8T8Opdi3lenMzU2hpaSHwzl2kpKTgwKEjqO/iDAAIf624ektcXDw2eWxH+zatMngFzWBeqw4b8e8QEhKCs2c/TLIQERGB4OBgVKxYEXnz5sWWLR8ugxAUFIQ3b97A0dERAQEBuPd+5tF169Z99flKly4NKysr5M6dG//88+EQ1/DwcCQkJCA5ORnFixcHgDSXF8kqXV1dGBkZpblllY6ODsaNHYtOnbvArXkL9OnTG/nz50fPXr2V55BNmTwJv/8+DA0aNISzk5Nyb8Po0aOx5M+lcHGphwIFCignglG3tu1/w5PHj9DItQ5OnjiGPn0Vs3x6e53A0iULAQDOLvVhnC8fGtarjRXLl2D4qD8AAOGvXqJViyZo2sgFhw7uw7gJU9Rer115fWycUwbWpfJgxu8WGNlLcW5UtcoG6OSm2HJaoqgu/ppSCiunlkKxwrmx88iHc4Zty+nj4dMExMRp5rB0AKjn1huhz4IwrMMv+M9vH5p3UuxVunzmIP5ZN1k57l/fPbCv3Vw52QsARL0Jxbie9hjRqTzOntyOrkOWqL3elu264OnjYDRvUBXeJw+jR98hAAA/r2NY+adiBtl1KxchKjICk8YMRIcWzhgxsJviZ7jgj/bNndChhTOmTRiGGfNXqPXcax0dHUwbORjuvQbBpW13DOr+GwrkM0aH/41A6MtXeB76Ess2eCDgZqDyUmXeH03mduC4N1o0Sr/xSdN4zhmpE/M6vZyY18DHmV0LJ08cRZ++AwEoMnvZkvkAgKD79+BS2wHHjx3CuD+GoXNHRdNiZV0etes6o1lDJ4weMRgTJqn/SidVbYyxa5Udyv9igIWTymHC0DIAgJoO+dGjveJoh1IWebFxUWVs/tMGJYrmwdY9iitbVKlohPULKmPt/EoY1b8UZi0LysyR49+lbtM+eBnyAGO7lsWVM/vQtIPi+87Vcwexf9MkAMCvnSfi9pVTmNzHBn9Nbomuw1YrGsgAL0zpa4Op/eyweWEf9P5jq9rnHmnSqieeP3mA7m7lcdZ7P9r3UFxi8rzvIWxe8eWjRtza90fUm1fo27oKRvRwRef+E5CvgGr33n9MR0cHE8aMQvuuPdGkZRv069UD+fPnQ7c+/0Po+z3h0yeNx6ARo+Hc+Fe4ONWBtdUvAIC/Vq9DvSZucGvTHt06dUSZ0qXUVufXMK9VRyY0efHHH8zjx4/Rt29fBAcHI2/evEhJScFvv/2GcePG4f79+xg2bBiePHmCd+/ewcTEBNu2bUPRokWxd+9ejB07FgULFkSbNm0wYsQIxMTE4PXr1599PgC4e/cuBg0ahNDQUMhkMgwcOBD9+vXDvHnzsGLFClhYWKB58+YYNWoUYmJiYGBgAJlMpvw3oDik69KlS7C0tPzqzxcdHQ1jY2NcDbgCQ0NDdf4qVSZJZHxd7exs1PxYqUvIkt+6WH19UDZibZr+GrPZXfGk+1KXkCkxsXEo5dgAUVFR39QIAB8+Z/51c4ZBrsydLRWbnIJqnr7f9br0c2FeZz+JIuNramdnA8Zm/cgDKXXuX/3rg7KR4oW+fEWY7KhcniCpS8i0mNhYVLCv8c3ZybxWPTbi2cCn4Ztd5MRgZyOufmzE1e9nbMT/a+GSpWCvesCHwU4ax7xWHTbi6sdGXP1+xkacea06nKyNiIgkx8lfiIiIsj/mteqwEc8GeFACEf3sGOyUEzCviehnx7xWHU7WRkREklMEu1Ymbwx2IiIiKag7r/39/eHm5oYiRYpAJpNh//79Xxzv6+urvN76x7c7d+5840+oOdwjTkREkpNpZf4yJ7J3bMSJiIikoO68jouLg42NDXr06IHWrVtn+nF3795Ncx66iYn6ZsBXFTbiREQkOR7qRkRElP2pO6+bNGmCJk2aZPlxpqamyJcvX5YfJyUemk5ERJLL/GFuihsRERFp3rfkdXR0dJpbYmKiyuuys7ND4cKF4erqCh8fH5U/vzrw2wwREUkudQt7Zm9ERESked+S18WLF4exsbHyNnv2bJXVU7hwYaxZswZ79uzB3r17YWVlBVdXV/j7+6vsNdSFh6YTEZHkeGg6ERFR9vctef306dM052/r6uqqrB4rKytYWVkp7zs6OuLp06dYsGAB6tatq7LXUQc24kREJLmsHHLOQ9OJiIik8S15bWRklKYRV7caNWrAw8NDY6/3rdiIExGR5LhHnIiIKPvLCXkdEBCAwoULS/LaWcFGnIiIJMc94kRERNmfuvM6NjYWQUFByvvBwcG4evUqChQoAAsLC4wdOxYhISHYsmULAGDJkiWwtLREhQoVkJSUBA8PD+zZswd79uzJ8mtrGhtxIiKSnkymuGV2LBEREWmemvP60qVLcHFxUd4fPnw4AKBbt27YtGkTXrx4gSdPnijXJyUlYeTIkQgJCUGePHlQoUIFHD58GE2bNs3ya2sadysQEZHkZLIszMKaxWD39/eHm5sbihQpAplMhv37939xvK+vr6KeT2537tz5jp+QiIgo51NnXgOAs7MzhBDpbps2bQIAbNq0Cb6+vsrxo0ePRlBQEN6+fYs3b97g9OnTOaIJB7hHnIiIsgF1HuoWFxcHGxsb9OjRA61bt8704+7evZtmchkTE5MsvS4REdGPhqeSqQ4bcSIikpw6J39p0qQJmjRpkuWaTE1NkS9fviw/joiI6EeVEyZryym4mYKIiHKk6OjoNLfExESVPr+dnR0KFy4MV1dX+Pj4qPS5iYiI6OfGPeL0VbnkScj1TrVfcNVGW+oCsi63Xm6pS8gSvdxC6hKyRFcrSeoSskw7JUHqEjJFS4V1fsuhbsWLF0+zfPLkyZgyZcp311K4cGGsWbMG9vb2SExMxNatW+Hq6gpfX1/UrVv3u5+fSF1yyxOR+10uqcvInByY13oGeaUuIUty2NcL6OmkSF1CluVOiZe6hExTVa08NF112IgTEZHkZFqZP4RN9j7Xnz59muYcbl1dXZXUYmVlBSsrK+V9R0dHPH36FAsWLGAjTkREP7VvyWvKGBtxIiKS3Lecc2ZkZJSmEVenGjVqwMPDQyOvRURElF3xHHHVYSNORETS09JS3DI7VsMCAgJQuHBhjb8uERFRtpLN8zonYSNORESSS71Wd2bHZkVsbCyCgoKU94ODg3H16lUUKFAAFhYWGDt2LEJCQrBlyxYAwJIlS2BpaYkKFSogKSkJHh4e2LNnD/bs2ZOl1yUiIvrRqDOvfzZsxImISHLqnPzl0qVLcHFxUd4fPnw4AKBbt27YtGkTXrx4gSdPnijXJyUlYeTIkQgJCUGePHlQoUIFHD58GE2bNs3S6xIREf1oOFmb6rARJyIiyanznDNnZ2cI8fnZ/jdt2pTm/ujRozF69OgsvQYREdHPgOeIqw4bcSIikp4sC+eccRpWIiIiaTCvVYaNOBERSS8LW9jBLexERETSYF6rDBtxIiKSnEymBVkmt5xndhwRERGpFvNaddiIExGR9LRkmd9yzi3sRERE0mBeqwwbcSIikhxnYSUiIsr+mNeqw0aciIgkx1lYiYiIsj/mteqwESciIunJZJmfXVXGYCciIpIE81pl2IgTEZHkuIWdiIgo+2Neqw4bcSIikp5WFq5LynPOiIiIpMG8Vhk24kREJDmZTAZZJg9hy+w4IiIiUi3mteqwESciIunJsrCFndclJSIikgbzWmXYiBMRkeR4zhkREVH2x7xWHW6mIJU75eML50a/om7Dpvj7n93p1l+9fgOuzVqgToMmWLJ8pXK5/9lzaNyiNVybtcDUWXM1Vq+3tzfqN2iIeq71sXPnrnTrr127hsaNm8ClniuWLVumXP748WO0cG8Jl3qumDBxIoQQGqnXvkJeLP6jOP5ZXArFC+fOcEweXRnG9TXHglHFsGhMMdiVywsAqGNvgAWjiimX71pcCgZ51f8xkJSYgLlj2mBAaytM+p8roiPD043Zv3UBhne2x/DO9hjUtjw6uxZUrtu1bjoGtLbC0A6Vcf/Wv2qvNzExAUMH9EZj11ro3rkNIt68STfm+rUAtGvZBDblSsDX+6Ry+eVL/6KlW320cmuAdq2aIuDKf2qv97j/eVRz7wqH5l2wZe/hdOub9xmOOu16w7F1D8xbvUW5PCExCQMnzUU1966o3qo7LgTcUHutnyXTytqNiL7bKR8/ODVyQ52GzfD3P3vSrR8/dQZsHZ3QtFX7NMsfPXmKpq3ao3aDphg7aZrG8g/IeZldzcYQK6f/gsPrK6FEUd0Mx+TV08LU3y2xfGpZrJhWFg6VDAEAFcrmxV9Ty2L51LL4c1IZlCuTV+31JiUmYMn41hjesSxmDq2HmAzy+tDf8zGupx3G9bTDyE7W6Ns0PwDg7vUzGNfDFuN62mFi32q4d+Oc2utNTEzAhKEd8VuTyhjaowkiI9LX63N8L3q0rI5erR0xqEsDPH54FwCQkpKCmWP7oHvLaujWwgFH93uovd4TvqdRq1kbODZpjW2796db/8f0eahQpxEatuuaZrnfuYtwbdUJdZu3x6Q5i9Re5xepOa/9/f3h5uaGIkWKQCaTYf/+/V99jJ+fH+zt7aGnp4dSpUph1apV3/CDaR6/zWQzlpaWsLa2hq2tLWxtbVGqVCmMGjUKAODr6wsHBwcAQGRkJObNmydlqRlKSUnB9DnzsWPLehzZ+w9Wrt2AyMioNGMmTJ2B5QvnweeoJ075+OLuvfuQy+UYM2Ey1v61FF6HDyAxMRH+Z85qpN6Zs2bDY+sWHDywH6vXrEFkZGSaMZOnTMWSJYtx8sRxeHn74O69ewCAufPmYeiQwfDx9kJ4+Gv4+PiovV4AeP4yGQs2hOL2g4TPjqlf0wiPnydh5PxnWLgpDD1aKpra05djMXL+M4yc/wyb9r1G4IMExMbL1V7zyQPrYFakJFbsuYtqTi2wd3P6DS3uXUZikcdlLPK4jBadR6C6UwsAwOOgG7h87iiW7byF36dtwZr5Q9Re7+6d21HMwgLHvM7CtX5jrFuzPN0YU1MzTJ05H01/bZFmefkKlbB7/3Hs9TyJWXOXYNrksWqtNSXlHSYsXIn9axbC5+/VWLppByKiotOM8Vg8Had3rcOZXetw6uy/uH7nPgBg4bqtKF2iGP7dvwVndq5DuTIl1VrrF2nJsnYjktiPkNfT5szHji3rcHTvLqxYuwERn+S1+6/NsGXtinSPnTV/EYYPHoAzJ4/g1evX8PL111jNOS2zn4UmYuZfj3HzXtxnxzR2KoDgZwkYNPk+Zq98gn4diwAAgh6/xeAp9zFo8n0sXPcUg7oUVXu9vofWwrRISSz6+z7sa7eA57Y56cb82nEUZm0IwKwNAWjWcSTsa7sDACx/qYIZ6y5j1oYA9B+3CZsWDVB7vYd2b0SRYiWx/eh11Kn3K7avS9+kVq/TEBv2XsD6PefRuc9IrF48CQBw1ucQUlKSsWnfv/hz0zGsXDgecrn6vhOlpKRgyrwl2L1hBU7u3oLl67eme8+1atYI21ctSbNMLpdj+KSZ2LhsPvwP7kRCUhJ8z15QW51fpea8jouLg42NDZYvT//dKyPBwcFo2rQp6tSpg4CAAIwbNw5DhgzBnj3pNy5mN2zEs6Hdu3fj6tWruHr1Kh4+fIj58+enG/M9wZ6SkvK9JX7W1es38EuZ0jA3M4OBgT5c6taB30cNdWjYS7x79w7lrK2go6MDd7dmOOnjizcREdDX10fxYoqQqVmjOo6eOKW2OlNdu34dZcuWhbm5OQwMDODs7AT/06eV68PCwvAuJQXW1tbQ0dFBczc3eHt5QwiBgICrcHFxAQC0bOkOL29vtdcLAC9eJSPkZfKXBwkgj67i7Z1HVwsR0e/SDalpZ4BzAbHqKDGdS6cPwalJZwCAc9MuuHQm/V7bj5079Q9q1W8LAPjvzCHUadgB2jo6KPmLLVKSk/Am/IVa6/X1Pgm3Fq0BAM1btkmzxzuVeeEiKFe+ImSfbO3NkycPtLW1AQDxcbGQQb1N4+WbgbAuXQJFTE1gqJ8X9WtXh/e5tHvhjQz0AQBJySlITklWTp6y6/ApDOis+D3nyqUDY0MDtdb6JTKZVpZuRNlBzs7rm/ilTGkUfp/X9T7JawCoam+H/PnypVkmhMDlgGtwda4LAGjt3hynfHzVVufHcmJmPw9LwrPQxC+OEQLIo/c+s/W08CZKkfGJSQLy9zvu8+hpQRP78K+cPYRa/2fvvuNrOv8Ajn9uEhKRvRNC7NgJqb0Sm9qz9l61q+YPoUWpqiqKVo2i1VJ7V8ReQey9SybZSCT3/v64XCIJN3Fvbuj3/Xqd18s95zn3fG8k93uecZ6nYVcAajbqxukjW99a/njAX1Txaw+AqZk5Ri/y39MncdmyhvSRwB00aPYZAA2bd+LI/h1pypibW2jy3pOEOM2/FShIfPaUlJQUnj19grWNPUZ6nOX7zPlLlChaGFdnJyzy5qVurWppKtSVKpTH1sY61b5HUdFY5DWnQD51A02Nyj5s/yd7GpLSo+983bhxY77++mtat26tVflFixZRoEAB5s6dS8mSJenTpw+9evVi9uzZmb52dpO7mRxu+fLltG3bNs3+AQMGEB0djZeXl6bVPTQ0lPbt21OpUiXKlSvHpEmTNOU9PDyYNm0avr6+dO/ePd1rJSYmEhsbm2rLrLDwCFycnTWvXV2cCQ0Le+14OM7OTprXLs7OhIWFY29nx5MnT7hy9RpKpZI9ewMIDQ/P9PUzKzwsLFW8Li4uhL0Zr0va41FRUVhbW2u+zF3fOM/Q9hyJxd01Nz9PLcjEga6s2Jh6qJaREfiUMefo2Yxb6HXpcWQI9k7qRhYLK1sS4qIzLBsbHcmd6+coV6keAFERIdg5vuoFsHfKx+OIB3qNNzw8DGdnVwCsrW2Iy+TfwtHDB2jWsDb9+3Rl8tS0vQm6FBrxCFcnB81rN2cHHkakHZrXsPtgStRtTe3KFSlboigxcfGYmBgz6fufqPNZPz6fPJO4hCd6jfWtpEdcfOA+vHwdjstr+Vidr9+dd6OiorGxeS3/OWt3ni58rDl7x/7HFHQzY9Wcknz9RSF+/uNVY7NXKQsWTyvOVyMKMX+FfnMfQPSjh9g5qHNuXktbnsRHZ1g2LjqSezfPUtqnnmbfhaB/GN2lFN9+2YReX/yU4bm68igiBIcX+drS2pb42Oh0y+3ctIZOTcqzcPZ4Bo6aBkA136aYmuWhjW9RerSsxMAvpuk11tDwCFycXvubc3YiJDzinec52NmS8OQpl6/dQKlUsivgACFh7z5Pb7KQr9/8vkpMfHvjVGYcPXqUBg0apNrXsGFDgoKCeP78HR1XBiYV8Ryobdu2mqFuUVFR6ZZZtGgRNjY2BAcHExQUBED37t0ZPHgwJ06c4PTp05w4cYINGzZozrl37x4BAQGsXr063fecMWMG1tbWms3d3T3Tsaf3zFWqpQsyOK5QKJg76xvGTZ5Kq45dcHR0wORFq6o+pfeIWKoezHQLKNL/nHru+cwMr5LmXLvzjL6T7uI//yFDujinapguWywP90KSiI1P21OuF5l4Fu/Yvg18UqsZJia5Xpya/T/r9312sGr1WmzZtZ9FP69k/g/6bZFVpdNHkt7PZ9eK+Vzc/Rfnr97g0o3bPH+ezO37D6lXvTKBvy/BxcGeH5b9rtdYhfjYfNT5OqPz0vvOyaYlij7WnO1T1pIrt57QZeRlxs26xRd93DU5O/hSPP0nXGPS93fo2sr57W+kA5nJfycP/E2F6q/yNUAZn3rMWnWJL2dtY93SyfoIMRVt423UohNrtp9l6NhvWblI/Xjc5XMnMTXNw/p9N1ix6SQLvh1HQnzmG7W0jjWLfzsKhYIF30xh9JRv+LRzHxwd7DEx0f89si65u7un+s6aMWOGzt47NDQUZ+fUfxvOzs4kJycTGZm2YyInkVnTc6B169ZRpkwZQN3Cro2EhAQCAgJStfDGx8dz5coVzeuePXu+9Q9+3LhxjBw5UvM6NjY208ndxdkpVQ94SGgY3uXLaV47v+gBfyk0LAwnR3VvXiWfCmz4Qz1Rxt+btmRLknR+o8c+NDQUr/LlU8cbmvq4k6MjdnZ2xMTEoFKpUCgUhISG4vhaK6euNalljV9l9eQtY+f8S/I76s9+lS1Zu1N9U3j7QRIKwDKvsabiXb2CBYdP63dY+ra1P7J3y3IArO2ceBT+ACsbB+Jjo8hraZPheYf/+ZPW3cdoXts5uaXqAX8U/gBbB1edx7tqxVL+XvcHAPYODoSFhWBrZ0dMTDSWVlZZes/y3hUJDXnI40ePsLO3f/cJWeDq6EBI+KtE8zAsEp+yJdMta5nXnFqfePPP4eMM6dYBS4u8NKhZBYCmfjWYuWiFXmLUhsLICIWWQwK1LSeEvn3Y+Tp1T3ZIaBhe5cu+8zw7W1uio1/Lf6/lcX37UHJ283r2NKhpB8DwqTdITnl7ZbF+DVtWb1LHffPeMxQKsLIwJibuVbK/cusJDna5sLZMvV8Xdq2bx/7tywCwtnXmceQDLG0cSIiLwtzCJsPzjgX8SfMuY9M9VrR0FR6H3yc2OgIrG0edxrtu1UJ2bPgNAFt7JyLDQrCxdSAuJgoLq4zjBahVvwWzp6rnmvln+59UrlkfY2NjnF3dyV+gCPduX6NkWR+dxvuSq5NTqtGeIWHhVChbWqtzK1f0YsvqXwBYt2WHQdfnzkq+vn//Plav3UuZmqY/cWGWY3rj5/GygSanr2MudzMfCaVSiUKh4OTJk5rn1W7cuMG4ca8mirKwePvzn6amplhZWaXaMsurXFmuXr9BaFgY8fEJ7DtwkNo1qmuOuzg7YWRkxOUrV0lOTmbT1u3U860DQOSjRwAkJDxh+ao1dGir3bMh76N8uXJcu3aN0NBQ4uPjCQzcT82aNTXHnZ2dMTI25sqVKyQnJ7Nl61bq1vVDoVDg5VVeM9nLhg0bqevnq7c4tx+I0Uyy9q5KOEBkdDLliucBwMnOhDxmRsQlqE80NoIKpcw5fk6/w9KbdhiimXytcu0W7N+hbmQJ3P4bFWs0Sfec6Mfh/HvnCmUqvvpZ+lRvysHdf5CSnMzta8GYmOTCztFN5/F26d6bv7fs4e8te6hbrxFbNqkn+di8YR21feu94+xX/r1/j5QU9c/6+rUrPHmSgI2trc7jfalimZJcvnGHh+ERxCU84Z9Dx/Gr+uomIjY+gYjH6kaZxKQk9h0LorhHARQKBb5VKnLi7EUADgUFU7xQAb3F+U4KReY2IT5QOSdfl+Hq9RuEvMjXAQcOUue1fJ0RhUJBBa9ymgna1m/crMnj+vah5OzN/zxi8GT1JGvvqoQDRDx+TvmS6v9zZ4dcmOcxIjY+BWeHXJoncQrmMyWPmZFeRrI1bDtUM/laxZotOLxLXck9uHMl3lWbpntOTFQ4D+9eppT3q59j+MPbKF/kv/u3LvDsaTwWVrpvhG7bZRBL1x9l6fqj1PD7lN1b1KO5dm1eQ9XajdKU//feTc2/Tx4JwNlV3Wjl5JKf08cCAYiNecydm5dxzVdQ5/G+5F22FFeu3yIkLJz4hAT2HjhCnRpVtDo34pF69ZaEhCcsXf0nnVo311uc75SFfP3m95UuK+IuLi6Ehoam2hceHo6JiQn2euoE0RWpiH+grKysePLkiWYiF0tLS2rWrMk337x6HvXhw4f8+++/2RqXiYkJ/xvzJR269aJxq7b0790TW1sbuvcdqGl5/2rSBAZ/MZo6jT7Ft3ZNPEsUB2DB4l/wa9yMZm070L3zZxQtUjhb4h0/bhydu3SlWfMW9O3bB1tbW3r17qPprfCfPInhw0dQv34D6tSuTYkSJQAYPXo0c3+Yh6+vH3Z2dppJYPTNyzMPS6YUpEQhMyYPcmVEN3Wrvk8Zczo2Vlf41u2KolyJPMwZk58xfVxYtDZCM2KvXAlzbv+blC2zpb9Ur0UfQv+9yaA2JTgWuIHW3dQ93icObOH3xa+Grh3b9zeVajXXTHYG4FGsHN5VGzK4fSnmTupGn1E/6D3eth06ce/uHRrVrc4/u3fQp99gAAL27ubHuerJmG5cv4ZfjYrs3rmVCWNH0PWzVurPcPQQrT5VL182afwovpn9o14nfzExMearkQNo0fcL6nzWj8HdO2BnY037wWMJCY8kNj6BDoPHUaN9H3w7DaCyV1ka1a4GgP+wfkz6fhE12vfh6OlzjOzdWW9xvpORQj15gVZb5iri/6WlUETOk5Pz9cQxo+jQrTeNWrVjQO8e2Nra0O21fP3lhMm07NiFK9eu8UmtuuzYsxeA8aNGMOfHBVSv1xh7O1vNxG3ZEfOHlrMrlLHgt+88KVnEnOlfFmZMf3WDZ2UvK7q2VA+n/X1zGBVKW7BwajEmDfFg3vIHqFTgVcqShV8VZ/6UYgzvmZ9vl9zPzJNeWeLbrC9hD24y8rNiBB3YQLMXPd6nDm1m3dJXcxmc3L+eijWaayZnA7h4ei/jepZnfC9vfpnVl4ETf9Nr/gNo1rYnD+7folPjchzYu5nOvdUjRQ7v28bS+V8B8M+2P+nWvCK921Rl1ZJZjP1a/X3e6rN+RD2OoEfLTxjSrQE9Bo3Hxk63vfevMzExwf/LYbTpOZB6bboyqGcX7Gxs6DRgOKEvnhUfOelrPu3Um8tXb+Dt96lmUrYff15OzWbtadSxB706taNYYQ+9xflOeszXWVG1alX27Ek9qe7u3bvx8fEhV65cGZyVMyhU2bn4o3gnDw8Ptm7dmmqo29atW1m3bh2BgYGMGjVK84xZ3759OXToEHnz5iUoKIjQ0FBGjhzJ+fPqtYAtLCxYtGgR5cuXT/O+2oiNjcXa2pqLp45h+Y7W+ZziubFuh7pkhy/nfVi9e106exg6hEwpYZdzJuTRlmv8VUOHoJXY+AQ8ajYjJiYmSz1y8Op7JuSncVjlMdPunKfPcB04Q+vr7tixg8OHD1OhQgXatGnDhg0baNmyZYblb9++TZkyZejbty/9+/fn8OHDDBo0iN9//502bdpo+9HERy4n5utLp45+MPk6yVi7v/ec5POvDTihZRZ06fXuRw1yEne7D+vnC1BCccnQIWgtLj6eYpX9spyzsyNfg/pRnRs3bgDg7e3NnDlz8PX1xc7OjgIFCjBu3DgePHjAypUrgVc5u3///vTt25ejR48yYMCADyJnS0VcZEgq4tlDKuL6JRVx/dFlRTx08YRMJXaX/tOydF2FQvHOiviYMWPYvHkzly9f1uwbMGAAZ8+e5ejRo5m6nhDZQSri2UMq4volFXH90lVFXN/5OjAwMN0RK927d2f58uX06NGDO3fuEBgYqDm2f/9+RowYwcWLF3Fzc2PMmDEMGDBAq+sZkkzWJoQQwvAURupN27KQZskmU1NTnTx3ltFSKEuXLuX58+c5fqibEEIIoTdZyNeZUadOnbfOhp/exJi1a9fm9OnTmb6Wockz4kIIIQxPkYk1SV9M/qKv5VA+5KVQhBBCCL3KQr4W6ZMecSGEEAanUBih0LLl/GU5fS6H8qEuhSKEEELoU1bytUifVMSFEEIY3svWc23LQpaXbXqXD3kpFCGEEEKvspCvRfqkIi6EEMLw9PzMWWZUrVqVLVu2pNr3oSyFIoQQQuhVDsrXHzr56QghhDA8hSJzWybEx8cTHBxMcHAwoF7qJDg4mHv37gEwbtw4unXrpik/YMAA7t69y8iRI7l8+TK//vorS5cuZdSoUTr7uEIIIcQHSY/5+r9GesSFEEIYnpGRetO2bCYEBQWlWgpl5MiRwKulUEJCQjSVcoBChQqxfft2RowYwYIFC3Bzc2PevHk5fj1SIYQQQu/0mK//a6QiLoQQwvD0ONTtv7QUihBCCKFXMjRdZ6QiLoQQwvBk8hchhBAi55N8rTNSERdCCGF4CkUmWtglsQshhBAGIflaZ6QiLoQQwvAyM6mLJHYhhBDCMCRf64xUxIUQQhieTP4ihBBC5HySr3VGKuJCCCEMT1rYhRBCiJxP8rXOSEVcCCGE4cksrEIIIUTOJ/laZ6QiLoQQwvAUmRjqJoldCCGEMAzJ1zojFXEhhBCGJ0PdhBBCiJxP8rXOSEVcvFPooM+JN/kwflVy581t6BAy7duFvxo6hExx3DHO0CFkSsjhc4YOIdMO/3Xb0CFo5YkqRXdvJkPdhHhv15r2JK+RsaHD0Iq5i5mhQ8i0BX/+YegQMsVh6yhDh5Ap0VfuGDqETDv58wVDh6A1neVsydc682HUroQQQnzcpIVdCCGEyPkkX+uMVMSFEEIYniyHIoQQQuR8kq91Rn46QgghhBBCCCFENpIecSGEEAanUihQaTmETdtyQgghhNAtyde6IxVxIYQQhqdQZGLyF0nsQgghhEFIvtYZqYgLIYQwPJmFVQghhMj5JF/rjPx0hBBCGNzLoW7abkIIIYTIftmRrxcuXEihQoUwMzOjYsWKHDx4MMOygYGBKBSKNNuVK1ey+hGzjfSICyGEMDxpYRdCCCFyPj3n67Vr1zJ8+HAWLlxI9erVWbx4MY0bN+bSpUsUKFAgw/OuXr2KlZWV5rWjo2Omr53d5G5GCCGE4b1cl1TbTQghhBDZT8/5es6cOfTu3Zs+ffpQsmRJ5s6di7u7Oz/99NNbz3NycsLFxUWzGRsbZ/UTZhupiAshhDC8l+uSartlwX9lqJsQQgihN1nI17Gxsam2xMTEdN86KSmJU6dO0aBBg1T7GzRowJEjR94alre3N66urtStW5d9+/bp5rPqmVTEhRBCGJy+nzl7OdRtwoQJnDlzhpo1a9K4cWPu3bv31vOuXr1KSEiIZitWrFhWP6IQQgjxwctKvnZ3d8fa2lqzzZgxI933joyMJCUlBWdn51T7nZ2dCQ0NTfccV1dXlixZwvr16/n7778pUaIEdevW5cCBA7r94Hogz4gLIYQwPD0/c/b6UDeAuXPnsmvXLn766acMbwhAPdTNxsYm09cTQgghPkpZyNf3799P9fy2qanp2097o8FdpVKl2fdSiRIlKFGihOZ11apVuX//PrNnz6ZWrVraxWkg0iMuhBDC4FQKo0xtIEPdhBBCiOyWlXxtZWWVasuoIu7g4ICxsXGa3u/w8PA0veRvU6VKFa5fv571D5lNpCIuhBDC8LIw+YsMdRNCCCGymR4na8udOzcVK1Zkz549qfbv2bOHatWqaf0+Z86cwdXVNVPXNgQZmi6EEMLgVLxqOdemLMhQNyGEECK7ZSVfZ8bIkSPp2rUrPj4+VK1alSVLlnDv3j0GDBgAwLhx43jw4AErV64E1I+aeXh4ULp0aZKSkli1ahXr169n/fr1mb52dpOKuBBCCMPLTMv5i3Ivh7i9iy6Huq1atUrr8kIIIcRHJwv5OjM6dOjAo0ePmDp1KiEhIZQpU4bt27dTsGBBAEJCQlJNtJqUlMSoUaN48OABefLkoXTp0mzbto0mTZpk+trZTSriQgghDE+hyMTkL1kf6taqVSvN/j179tCiRQut3+dDGeomhBBC6I0e8/VLgwYNYtCgQekeW758earXo0ePZvTo0Vm6jqFJRVzoVNHp32DlXYHYoCBuTByf5rh9/Ya4du0GCgWR27cR+vtqAEouWISRuTkAuR0cebRnF/fmzc2WmD0mfY1FOW/ig09x5+tJaY7b1PbD+bNuoIBnd25zb/Z0VM+fU/S7HzHOo445l70jUfv28GDRj3qPNyAggOkzvkGpVNK/Xz86dGif6vjZs2cZM2YsiUlJtG7VkiFDhgBw9+5dhg4bTmxsLNWrV+OrqVMzHJarS51+3sCh6/epXaIgv/VOW+kZ+eceNp65hrutJftHd9Ps33/1LhM2BqJUqnC0MmdZj2bY5c2j93jdRk4kT8lyPLkYTMjcaamOKczy4D55tuZ1LicXHq37jegdGzX7XIdPIJejM/cmDNV7rABm+V3wXjGL3I72qJJTuD5tISHrd2qOG+Uxw+fPeZgXckeZnMy9n9dyZ0HqXt2Ka38gT8H8HKrSJltiTk9mliXLyvJl/6WhbkJoq9wvP2Bb1YfHh49zvt/INMddWjXFY3AfUCgI+XMTdxctA6D0vG+wKFkMhUJB9MkzXBn/NahUeo+3xOxvsa5YkZgTJ7k6Zkya4/YNGpC/dy8UKHhy8ybXJ09G9fw5xb6ainnRomBkRFxwMLe+mZkt8cKHlbM7L93EwRv/Urt4AX7r2SzN8VN3Qxj0+24Sk5P5zKcUYxpVVX/Gq3eZtOkAz5VK/EoUZEarOnqN83UO/UdjVrw0z66cJ/Ln2ekXUihwHj2DlMeRaco49B2Fsb0jYd+k/X3StXflawCbT8pS/pcZGJnm5t9Vm7j+9QIAvH+bjXWFMiifPyd82z6uTJij93gzou98/V8ik7W9g4eHB56ennh5eVGqVCkWLFiQqfM3b97Ml19+meXr37lzhyVLlqTa16RJE27evJnl99SnsL/+5NbXU9M9ZmJtTb6+/bj8+QAudOuMlZc3Zu4FALj8+QAu9uzGxZ7deHb/HlEHs29CpMhN67n37bQMj+frP5gbXw7lav8eAFhXVz8feuOLIVwd1Jurg3rz7N97xBw5qPdYk5OTmTZ9Bqt+W8nmTRtZvGQJ0dHRqcpM9p/C3Lnfs2f3LvYG7OPqtWsAzJw1i2FDh7AvYC+RkY+ybQboAbUrsrhrxsOD2vuUZP3AtBXAMesDWNajGUfG9aB8fmeWHT6rzzA1onZuIvSn9JO56tlT7o37XLMpE+KJDzqqOW5e1huUymyJUxNTcgoXR05nf7mmHGvYg1LfjcPYPHWDxY1vfyawTGMOV2tPwQGdMC9SQHPMoV41VCnZG3O6Xi6Hou2WSR06dGDu3LlMnToVLy8vDhw4oNVQt3LlylGzZk0OHTrEtm3baN26tc4+stA9ydmZc//XVVwcnrbRHCCXrQ2FvxxMUOvuHKvbCpuqPpgX8QDgyvivOF6/DcfqtcbExhrHhn7ZEm/IH39wfdLkDI8XGjmCi/36E9yhAwD2fuq4bn0zk7OfdeJsh46YWFlhV6d2tsT7oeXs/rW8Wdy5UYbHv1gXwNJuTQga15MdF29xKSQSpVLF0D92s7p3c46P7c6z58nsvXJH77G+FLdvO4+Wv70TJG+1uiRHhqfZb+ZZDlU25mxt8nWZeZM53eULAks3xrmpL5aliwHw76pNBJZuxMGKLbGp7IW9b5VsizsNPefr/xL56Whh3bp1BAcHs2vXLiZMmMC5c+c0x5RKJcq3/BE3b96cb7/9NsvXTi+pb9++nSJFimT5PfUp7sxpUp48SfeYqVs+nt2+TUpcHKhUxAafwbZ26mSYy8ERU1dX4oLPZEe4AMSfPYPyafoxqykwMjMDIyOMTM1Ifvwo1dFc9g7kdnEl/rz+K4pnz52jWLFiuLi4YGFhQZ06tTlw8FUDQFhYGCnJyXh6emJiYkLzZs0I2BuASqXizJlgfH19AWjVqiV7AwL0Hi9AreIFsDDLneHxKoXzp9vTrVBAXGISAPGJSbhYWegtxtc9vXQO5dOn7yxnVqwkyTFRJEeEqXcYG2PXoiOPNvyu5whTSwyNIPbsFQCSIh7z/HEMueysNceVT5/x+MBJAFKePCXhxl3MXB0BUJiYUHTsAK5P/ylbY06PCkWmtqwYNGgQd+7cITExkVOnTqWadG358uUEBgZqXo8ePZobN27w9OlTHj9+zMGDBz+I582E5OzMiDpykpT49PNfnoL5Sbh+i+SYWFCpiD4ahGOjugCkxCcAoDA2xtjMNNt6l2ODTmV4j6EO6LV8bWZGUmQkACkJ6ngxNsbI1Czb4v3QcnatYgWwME0/X4fExJOsVFLGzRETYyPaVfRkx4VbPEp4ioVpbgraq/NOreIF2HLuht5jfSnx2gWUzzLO2UbmFuT1qU78oT1vHDDGqlEbYnes03OEr7wrX5u6OqEwMSbu/FVUKSk8+GMLTp+qfwcidql/b1QpKcSdv4qZm/bzm+haduTr/wqpiGeCu7s7xYsXp1OnTnTt2pXWrVvj5eVFSEgIv/32G2XLlqVcuXI0bdqUBw8eAOqbu7Zt22re47fffqNy5cpUqFCB2rVrc+HCBc2xmTNnUrZsWcqXL0+VKlV48uQJAwYM4NKlS3h5edG8eXNA3eL/8rwbN25Qr149ypUrh5eXFxs3btS8n0KhYObMmVSuXJlChQqxbNmyt36+xMTENOvy6tKzf/8lT5Ei5HJwRJErFzZVq5HbwSlVGTtfPx4HBmZbktTGvwvn4rl4OaV/34Dy2RPizwWnOm5Ty5eYQ/uzJebwsDBcXptcysXFhbCwMM3rsPBwnF3SHo+KisLa2lozrM31jfNyou871Kf1wnUUn7CQiw8i6FiplKFDSsWyai3ijr4auWHbpDWxB/7RqhKvL9YVy4CRgmf/pr8kl1l+F6zKFifm9CUACo/oyb8rN5Acl5CdYaYrK+uSCvE2H3PO1ne+Bnhy5x4WnsUwdXFCkTsX9nVrYubyKmeXXTKHmmf3k5LwhIjd2TPC6l1uzZyF159r+WTXTlKePiH21CnNsRIzZ/LJ7l2kPH3C4/3ZM+ruY8rZITHxuFm/ahB3s7EkJCYOB4s8JCQ+5+LDCJRKFdvP3yAkJt6AkaZm3aITMdvXpRmtZlmvGQnHAt9aiddrXOnkazM3J549fPX//OzfMPK8UeE2scyLU5M6PNp/PNtifZPka92Rn04mnD9/nitXrlC+fHn27dvHokWLOHfuHFFRUXz55Zfs3LmTc+fOUa1aNfr165fm/MOHD/PHH39w4MABTp8+zddff03nzp0BWLFiBRs3buTw4cOcPXuWHTt2YGpqyqJFiyhVqhTBwcFs3rw5zXt27tyZ9u3bc+7cOf766y969+7N/fv3NcfNzMw4fvw427dvZ+jQoSQnJ2f4+WbMmJFqTV53d3cd/NReSYmL5e4P31Nsxkw8f1jA07t3UKWkjsfOry6PA/7R6XXfi7Ex9o2bcaV/Dy5+1gpQYOtXP1URm1q+RO/Pnt7l9Or6itdbG9MtoECVzn5FDm+lXLDvFJs+b8e1aYOoVMiN73YbLumkx+KT6sQfU9/Mmdjak7dcBWIP7HnHWfqTy84Gr2UzOT8w7TwHAEamuanw+/dcHj2LlCdPMXNzwrF+df5duSGbI82ADHUTOvYx52x952uA5OhYrk36hnJL51Hxz19JuH4LVUqK5vj5fiM56F0HFArsahhwmOwLChNjnFu3IrhDB042bAQocGjcWHP86pgxBL3Yb12pUrbE9DHl7PS6GhQKBQqFgiVdGzPir73U/+F3nKzyYmKUM+4vcuUvhJF5XhKvX0y139jajjwly5NwzDANSBnm63Sep37zd6H8r99wd9GaDBvcs4Xka52Rydq00LZtW8zMzDA3N+fXX3/lwoULWFpa4uSkbhnet28fn376Kfny5QPUwx+//vrrNH88mzZt4uzZs1SuXFmzLyIigqSkJLZu3crAgQM1S/HY2tq+M664uDiCg4Pp3bs3AMWKFaNGjRocOnSIzz77DEBz01CyZElMTEwIDQ0lf/786b7fuHHjGDny1WQtsbGxOk/u0QcPEP3i+W+37j1JjnvVip/byYncTk7Enz+X0enZLk+RYqhSUngeoX62KPrwASzLexMVoK5w5XJ0IpeDIwmXLrztbXTG2cWZ0NdaxUNDQ/EqX/7VcWdnwkJTH3dydMTOzo6YmBjNuskhoaE4OqUejZCTRMY94WroI8q7q1uCW3qXYPr2wwaO6pU8JUqTHBlB8mP1sEdTjyLkzleQQvNWgJERxlbW5Bs9lQez0q8U65pR7lz4rJ/PjZlLiDqa/mMdXstmEr7jACF/7wLAyqskFiWL4HdjLwoTE3I72lFpyxJONEtbIckOMvmL0JX/Qs7OjnwNELErgIhd6obmQsP68Tw6dc+76nkyEbsCcGzkx+ODR9N7i2yTt3gJSEkh6UUOfLwvAGsfHyJ37NCUUSUn8zgwELs6tYk5rv/G3Y8pZ7tZW/DwtZ7uh9FxOFvlBaBq4XzsHtYRgLVBl3JMM79p4eKYFi2J29c/oTDJhcIsD3adBvDk3Alyubrj9vVPYGSMsYUVjp9PIGJBxvMF6crb8vWzB2Gphpyb5XcmMTRC87rkzNE8fxzDre/fPsJV3yRf645UxLWwbt06ypQpo3l94cIFLCxeDc95+UX5UkazWqpUKnr16sXUqelPZpZZL28a3rze66/NzMw0/zY2Nn5rj7ipqSmmpqY6iS0jJja2JEdHkcvODju/ulwe/GppAju/ejzelz09y9p6HhlBnsJFMLawICU+Hkvvijy7d1dz3KaWL9EHA7MtnvLlynHt2jVCQ0OxsLAgMHA/QwYP1hx3dnbGyNiYK1euULRoUbZs3co3M6ajUCjw8lL3Cvn5+bFhw0batTXcDNnvYmNuRmT8U+5ERuPhYMP+q/co5mRn6LA0LKrWIu7Yfs3rhDMnuDWoEwAmDs64jZiQbZVwULeQP9p3jAerN6V73HP6F6Q8ecqN154FD9++n3/cawKQp2A+Kq6dZ7BKOJCpIWwy1E28zX8hZ2dHvgbIZW/H80ePye1oj3OzRgS17YHC2BhTV2ee/fsQjIxwqFuL2ODsaYx+m8TwcMyLFsXY0pKUuDisP6nE0zu3wdgYUycnEkNCwMgI2xo1iL90KVti+phytqu1BcZGRlx4GIGnsz3rTl9hfscGAETEPcHR0pz4xCQWHwhm0VsmfMtO8Qd2EX9A3fhsWqw0lnUa83jNIgAejO0DgLGdIw79RmVLJRzenq8TQ8JRpSixLFuC+Es3yNfxU872nQBAgX4dsSrvyYlPDZenX5J8rTvy09GBunXrsn37dkJD1cNEFi1aRN26ddMk22bNmrFy5UrNMDSlUklQUBCgniDmp59+0jznFR0dTUpKClZWVsTExKR7XSsrK7y8vFixYgUAN2/e5PDhw1SvXl0vn1MbJb6bS9Gp07CuWg2vvzeT17Mkxb+dQy57BwA8vhhF2d9+p8T387i/4EdSXusRVw9L35vtMReeNhuPCVOx+qQKpVatI09xTwp/NQsTO3uSHz8ifO1qin3/EyUWLcfYPC+Ptr0abpidw9IBTExMGD9uHJ27dKVZ8xb07dsHW1tbevXuo3l+zH/yJIYPH0H9+g2oU7s2JUqUANSTT839YR6+vn7Y2dlpJoHRt5YL/qLbr5vZffEWnhN/4tTdENr8tE7zDNngNTupN2e1OrlP/IktZ69hYmzEnPb16LBkA9VmLOfwzfuMapA9Qx/zjZ2G6/Dx5PX6hELzf8O0cHHyjZ6Kse2LhgCFAgufasQdP5Qt8byLbfWKuLVvgnPzetQM2kjNoI1YlilOpS1LMHV1wiyfM0VH98Pmk3Ka444Nahg67LQUisxtQmSR5OxXvFcvpuzi73Dwq0mNoH+wKl8Gr5ULye2sntDRc/oEquzbRIXff+baV7NJjo4FYyPKLJxFlX/+psqe9SQnPOHf3/7MlnhLzv+REjO/waZGdSpu34ZFqVKU/OEHcjk48DwykgfLV1B22a+UX/sHxhYWhK7/G4WREcWnT6P82j/w+v13lE+fELY+eybo+tBydquf1tN9+Vb2XLpNyclLOHUvlLaL/9bk69ltfOm9cjsVpy+jQclClHZT/57M+ecEn0xfju93a+hX04viztnXcO44ZCIOfb/ArEwF3KYvIXfBIjh+PgFj63ePUslu78rXABeGTaXCqu+oc2kn4Tv2E3dBPYt+mXkTMS+YjxrH1lEzaCP5uxtwBQ/J1zqjUKX3IIrQ8PDwYOvWrala1/39/YmPj2f27FdLHK1cuVLz2t3dnSVLlpAvXz6WL1/Otm3b+OuvvwBYs2YNs2fPJiUlhefPn9O0aVPNDK0zZ85k5cqV5MqVC3Nzc/755x9y585Ny5YtuXPnDoULF2bz5s2pYrpx4wb9+/cnMjIShUKBv78/LVu2BNSt7HFxcZqeAAcHB4KCgvDw8NDqs8fGxmJtbc3eyhWxMPkwBk/kzpvx7Nw5ldXCXw0dQqY47ljy7kI5SMjhnPOog7au/XXb0CFo5YkqhQ7Km8TExGiG6GbWy++Z20d3Y2mRV6tz4uITKFS1wXtdV3yc/qs5++Xf0UYXT/IaGeviR6l35i5m7y6Uw7j8+YehQ8gUh62GXxEjM6KzcdkzXTn3s+FHg2jrfXO25Gvdk4q4ns2aNYtbt26xaNEiQ4eSaVIRzx5SEdcvqYjrjy4r4reO7slUYi9ctb4kdqFzH2rOlop49pCKuH5JRVy/dFURl3ytOx9G7eoDNWHCBDZs2MCaNWsMHYoQQggh3kJythBCiOwkz4jr0bRp0zTriQohhMiYrEsqDE1ythBCvJvka92RHnEhhBCGp0D7SV1k7hchhBDCMCRf64xUxIUQQhicCiNUWg7S0racEEIIIXRL8rXuSEVcCCGEwakUClRatrBrW04IIYQQuiX5WnekIi6EEMLgMvMsmTxzJoQQQhiG5GvdkYq4EEIIg1OhQKXlw2TalhNCCCGEbkm+1h2piAshhDA4aWEXQgghcj7J17ojFXEhhBAGJ8+cCSGEEDmf5GvdkYq4EEIIg5OhbkIIIUTOJ/lad6QiLoQQwuBkqJsQQgiR80m+1h2piAshhDA4aWEXQgghcj7J17ojzRRCCCEMToWRppX9nVsWU9fChQspVKgQZmZmVKxYkYMHD761/P79+6lYsSJmZmYULlyYRYsWZem6QgghxMdC8rXuSEVcCCGEwb1sYdd2y6y1a9cyfPhwJkyYwJkzZ6hZsyaNGzfm3r176Za/ffs2TZo0oWbNmpw5c4bx48czdOhQ1q9f/74fVQghhPhgSb7WHamICyGEMDj1LKzatrJnPrHPmTOH3r1706dPH0qWLMncuXNxd3fnp59+Srf8okWLKFCgAHPnzqVkyZL06dOHXr16MXv27Pf9qEIIIcQHS/K17khFXAghhMFlpYU9NjY21ZaYmJjueyclJXHq1CkaNGiQan+DBg04cuRIuuccPXo0TfmGDRsSFBTE8+fPdfCJhRBCiA+P5GvdkcnaxDuVGdkZK/M8hg5DKyqzDyPO1/1wupChQ8iU9o37GTqETMnnFWzoEDItf4MLhg5BK7FPE+Hzb3TyXllZl9Td3T3V/smTJ+Pv75+mfGRkJCkpKTg7O6fa7+zsTGhoaLrXCA0NTbd8cnIykZGRuLq6ahWrENmpyjddsDI3M3QYWlF8gPl6TpCHoUPIlI6fDjR0CJliV/OOoUPINL+qRw0dgtZinz6DAdPf+30kX+uOVMSFEEIYnEqlQKXSMrG/KHf//n2srKw0+01NTd96nuKNGweVSpVm37vKp7dfCCGE+K+QfK07UhEXQgiRA2RmdlV1OSsrq1SJPSMODg4YGxunaU0PDw9P04r+kouLS7rlTUxMsLe31zJOIYQQ4mMj+VpX5BlxIYQQBqfPWVhz585NxYoV2bNnT6r9e/bsoVq1aumeU7Vq1TTld+/ejY+PD7ly5crchxNCCCE+EpKvdUcq4kIIIQxO38uhjBw5kl9++YVff/2Vy5cvM2LECO7du8eAAQMAGDduHN26ddOUHzBgAHfv3mXkyJFcvnyZX3/9laVLlzJq1CidfWYhhBDiQyP5WndkaLoQQgiDy0zCzkpi79ChA48ePWLq1KmEhIRQpkwZtm/fTsGCBQEICQlJtUZpoUKF2L59OyNGjGDBggW4ubkxb9482rRpk+lrCyGEEB8Lyde6IxVxIYQQBqfvxA4waNAgBg0alO6x5cuXp9lXu3ZtTp8+naVrCSGEEB8jyde6IxVxIYQQBpeVWViFEEIIkb0kX+uOVMSFEEIYXHa0sAshhBDi/Ui+1h2ZrE0IIYQQQgghhMhG0iMuhBDC4KSFXQghhMj5JF/rjlTEhRBCGJwkdiGEECLnk3ytO1IRF0IIYXAqMjH5iyR2IYQQwiAkX+uOVMSFEEIYnBIFSi0TtrblhBBCCKFbkq91RyriQgghDE6GugkhhBA5n+Rr3ZGKuBBCCIOTdUmFEEKInE/yte5IRVwIIYTBqdC+5Vyl31CEEEIIkQHJ17ojFXEhhBAGJy3sQgghRM4n+Vp3pCIudG77ifOM/XUdSqWKL9o0oGfDGppjcU+eUW/sd5rXd8Ii+V+nTxnSoi57z1xm/LK/eZ6cQl3vknzbt132xHssmHGL/0CpVDGyQxN6Nqn9WrxPqTdiuub13dBI/te9JYNbN+TWw3C6fr2QmIQn+HqXYt6w7igU+v/CuXxqK7vWTCD830sM/fYMLgXKpClz/8YJNv0yhJC7Z+kyah0lK34KQEpKMut/6sPD28GoVEpqNf+CinW66z3mgIAAps/4BqVSSf9+/ejQoX2q42fPnmXMmLEkJiXRulVLhgwZAsDdu3cZOmw4sbGxVK9eja+mTs2Wn/GOw6cYv2AFSpWKEZ1a0qNZ3VTHS7UbhKV5HoyMFLg62PH3t+MBeJaYxLDZSzhx8RoKIyPmj+5PtXIl9Rprxx/XcvDqHeqULMTqz9unOR506wEDft1EYnIynaqVZ1xz9e/3s+fJDF25lRM3/sXISMH87s2oVryAXmN9G3nmTIjst/3UJcat3IJSpWJkC1961q2c6vhfh88w6++9qFBRyt2Vnz/viGkuE3rNW8PF+yEolSqqehZibu9WGBkZZU/MJ88z9te/X9xj1Kdng+qaY3FPnlFv3BzN6zthj/jfZ00Z0sKPnt8t5+LdhyhVKqqVLMzcAR2yJeYrp7ax6/cJRDy4xJBZp3F2T5uzVSoVm5Z+zs3zAZjltabj0DXYuxQh+XkiGxb3I+TuWYxzmdKq32LcPLz0Gu+Hlq93HjjGxLmLUSqVDOvegW6tmqQ63qzfKKJi40hJSaFV/dqM7tcVgE/7fUF4ZBSmprkAOPj7Yr3HCrAj+Crjft+p/ptrUpMedSqmOv7HkbPM3noQVCo61/RmRBP1PXSvReu4+G+Y+ve3WEG+79Y02/7m3iT5WnekIv4B+Pvvv5k2bRopKSkkJibi5ubGnj17MvUHGBgYSFJSEg0aNNBjpJCcksKYpevYOX0EVnnMqDp8Oi2qeWNnmRcAS3Mzjs+bAKgTT4ne/6NZ5fIolUoG/riKPTNGUtDZnqEL1/DP6UvUq1BK7/GOXfQ7O2aPxcrcjGoD/WlRoyJ2VhYv4s3D8cVfaeL17DKKT6tWAGDCz2uZ0K0lTap40cF/HjuOn6VJFS+9xgvg6FaCTiPXsvHnQRmWsbJ1o/WAxRzaOjfV/ssnN5OS/Jzh3wWTEBvJ9yPK4F2rq16/zJOTk5k2fQarV/2GhYUFzVu0pGHDBtjY2GjKTPafwty531O0aFHatmtPg4YNKVG8ODNnzWLY0CH4+fkxYOAg9u3bh5+fn95iVcebwrj5K9g+bzKWec2p0Xs0zWtXws7KMlW5vT99jYV5nlT7Zq5cT1F3NxZPGMzz5GQSnibqNVaAgfUq0a2mF6sPn033+IhV21nWvw0l3RzxnbaU5hVKUjq/EzO3HKCYsz1LerfkeXIKCUnP9R7r20gLu/hYfCg5OzklhbErN7Nj8kCs8phSbcxcWlQui52FOaDOeWNWbuHk7C+wt8xLt7mr2HT8PO1reDO3T2uszM0A6Pr9b2wJukiLSmX1FuvrMY9Z+jc7pw1T32OM+IYWVb1S32P8MF4Tf4k+E2lWuRwAPwzsgNWL7+wus5ay5fg5WlT10nvMDm7F6TTiDzb98nmGZa6c3saT2Ed88cMVLgVtZtea8XQauZaTe38mt5kFQ78N5nH4bTYs7k/vibv1FuuHmK//9/0iNi+ejWVec+p0HkgzvxrYWltpyqyeMwUri7ykpKTQuPcIGtWqSjnPogAsnzWRUkUL6TXGVPGmpDB2zU52jO2JZR5Tqk/+ieY+JTV/c5FxCXz1dwCHpgzAOo8p7eauoam3J8VdHZjb/VOs8qj/5rot+JOtp6/Q3Ee/98gZkXytO4ZpShFaCw0NZcCAAfz9998EBwdz+fJlvv3220y1MiYnJxMYGMju3fr78n7p5LU7lCzgSj57GyzNzWjoU4Y9py+lW/bYlVs421rh4eJAZGw8lnlMKehsD0DtciXYdDRY7/EGXblFSY985HOwxdI8Dw0rleOfoAvplj1+6QbOdtZ4uDqiUqk4fukmjSuXB6BTvepsP6b/eAEcXIvhlM/zrWWs7fPj5uGFQvHGn7hCwfOkJyiVKSQlJmBu5aD3FtWz585RrFgxXFxcsLCwoE6d2hw4eFBzPCwsjJTkZDw9PTExMaF5s2YE7A1ApVJx5kwwvr6+ALRq1ZK9AQF6jRUg6PINPAvlx83RHkvzPDSo4s0/J9Kv5L5p7e6DDOmgHn2Qy8QEmxc3h/pUu2QhLMxM0z0WEhVHslJJWXdnTIyNaF+lDNvPXgXgj6PnGNKw6otYjbF5cVNtKCpAqeUmz5yJnOpDytlBN+5TMr8L+eysscxjRkNvT/4JvpqqjEql4mnic1KUSp4kJuFiq26QfFkJT05J4WnScxTZ1Ot18trdN+4xSrPn9OV0yx67cltzj6GOOY8m5mdJz7OltxbUOdvxHTn7yqmteNXqDIBnhU+5e+0IKpWK8AdXKFJGXZm1cypEfEwYcdGheov1Q8vXpy5ewbOwB25ODljmNad+9UrsPRqUqoyVhToPJz1PJul5Mtn0356uoFsPKJnPCTc7KyzzmNKgXHH+OX9Dc/xOeBSebo7Y5s2DkZERNTw92HxKfQ/9shKu+Zsz4AeRfK07UhHP4UJCQjAxMcHe3l6zr0KFCigUCoKCgqhatSrlypWjUqVKHD58GIA7d+7g4ODA1KlTqVmzJj/++COLFi1i5cqVeHl5MXXq1HSvlZiYSGxsbKot0/E+jsHN3kbzOp+DDQ8fRadbdv2hU7StqR6S42htSfyzRC7ceYBSqWTr8XMZnqdLIY+icbO3fRWvoy0PH0WlH+/+E7StXQmAR7Hx2Fnl1XwR5nO05WFk+uflJCV9mpErtzkz+rsz94vyNO7yjd6vGR4Whouzs+a1i4sLYWFhmtdh4eE4u6Q9HhUVhbW1teZn7PrGefoSEvkYN0c7zet8jvaERDxOVUahgIaDJ1O731g2Bh4DIDouARNjI8YvXEn1XqMZMH0BcU+e6j3etwmJjsPN5lVPfj5bK0Ki4oh+8kwd69rdVPNfTP+lm4jLht77t3nZwq7tJkROlF05Wyf5OioGN7tXPYf57K15+DhG81qhUPB9r1b4fDGbwv2mYGFmSq3SRTXHO323Ao++6v2fZlPPXMjjaNzsrF+L2YaHj6PTLbv+0Gna1kg97Pezb36mYLdx5DUz5dNs6MHXVlxUCFa2bgAYGRlhbmHHk7hHuBQoy6WgTSiVSkLvnedR6A1iHz/QWxwfWr4OjXiEq9OrvzU3Z0dCwh+lKdeg5zCK129H7crelC3x6ne474QZ1O40kF/+3Kz3WOFFTrZ9LSfbWfEw6tXfbmFnOy7+G8bDx7EkPk9m99lrhETFaY53/vEPCg2ZRV6z3DT1LpEtMadH8rXuyND0HK58+fJUrVqVAgUKULt2bapVq0anTp1wdHSkdevW/PzzzzRs2JBDhw7Rtm1bbtxQt6w9evSIokWLMmnSJABiYmKIj49n9uzZGV5rxowZTJky5b3iVanStn2l12qnUqnYdCSYgFmjNGV+HdmTIQvXkKJUUq1kURKe6b9ikG68GZTbdOgUAXNfDatPc94H8F1z//pxcuXOw7jF94l9/IBfvmpIoZK1MDO3evfJWZTOjyp170m6BRQZ/N/o/4ecXuvtm7/D/yz8GlcHOx6EP6LpsCmULVoQq7zm3HoQRoPK3swZ0Qf/xWv4btUG/Pt10nvMGVGl82kUCnienMKt8CgalC3KnC5NmLx+L99tP4R/m7rpvEv2kGfOxMcgu3K2bvJ12n2vf9U9T07h173HOPndF7jZWdNr3hp+P3CKz2qpK7drvuhOUnIyfef/wb4LN6hbrvh7xZPlmNMtp2LT0WACZn6Rav/vY/uS9DyZPnNXsu/sVep663cOD22l/12twMe3F+H/XmLBuE9wdPMkX+GKGBnr79b9g8vXWt6L7V72A3EJT+gxeiqXbtymVNFC/DxtHK6ODkTFxNJ2yHhKFilI9YrlDRDvq4DtLMz5tnMTOsxbQ25jY8oWcMH4tVGLq4d0VP/NLdnAvku3qFumaJr3yw6Sr3VHesRzOCMjI9avX8+RI0do1KgRhw8fpnTp0ly9epXcuXPTsGFDAGrUqIGTkxPnzp0DwMzMjM8++yxT1xo3bhwxMTGa7f79+5mO180+dQ/4g8hoXGzTVvIOX7qBu6Md7q/1PFYvXZR9s77kwOwxlC+cnyKujpm+fqbjdUjdA/4gIgqX13r0Xzpy4RruTvbkf9Hy6mBtyePYBM2X6oOIKFzs0p6nK4e3/8i8Lysy78uKJCcnZfl9zh76gxLejTAyMsbGoQAOLkWJeHhFh5Gm5eziTOhrLeOhoaE4Ob36v3V2diYs9I3jjo7Y2dkRExOj+RmHhIbi6OSk11gB3BzsePhaD/iDiEdpfidcHdS/t/mc7KldsQznrt/BwcYKq7x5aFRNfZParFYlzl+/o/d438bNxoqH0a9a0x9ExeJibYmDpTlWeUxpVF5949y8gifn7uu/9+JtpIVdfAyyK2frJF/bWfPw8aveuAePYlLl67N3HmBsZIS7gy3GRka0qFyWY9fupHqP3CYmNKtUhi0n0n+kS9fc7G1S9do/eBSNy2s95C8dvnQTd0db3B1t0xzLncuEZlXKs+X4Ob3FeWTHj/w4piI/jtEuZ1vZuhEb9RAApVLJk/jH5LGww9gkF816/sCQmafoOGw1T+IfY+voobe4P7R87erkkKoH/GFYBM4O9umWtcxrTs1PvPjnyEn1uY7qRxZsra1o5leD0xev6T1eN1srHr7Ww/3gcSwuNqnnn2lWsSQH/Qewd2JfXG0sKeJsl+p4bhMTmlX0ZOtp/d67vY3ka92RivgHwtPTk/79+7Nx40aqVKnChg0b0u1pfrkvb968mX5+xNTUFCsrq1RbZn1S3INLdx/y4FE0cU+esSvoAvXTmXBt/aHTmmHpL4VHq28I4p8+Y+HWQHrUr57mPF3z8SzMpdsPeBAZRdyTp+w6cY56PmmHq63ff4K2dSppXisUCiqVLMKO4+pnh9f8c1ivE7VVbzKEod+eYui3pzAxyZ3l97F2yM+N8+rntp7EPybs30vYOul3opLy5cpx7do1QkNDiY+PJzBwPzVr1tQcd3Z2xsjYmCtXrpCcnMyWrVupW9cPhUKBl1d59u3bB8CGDRup6+er11gBfEoW5fLt+zyMeETck6fsPnaGupW8NMcTnj7TDDmPjkvg8NnLlPDIj0KhwO+T8hy/oH7G8uCZi5TwyK/3eN/G1dYSY4WC8/fDSE5R8tfxCzT2Ko5CoaBu6SIcv6G+eT9w5Q4lXB0MGuvLFnZtNyFyMn3nbF3ka5+i7ly6H8qDxzHEPX3GrjNXqFf+1XBXNztrLtwLISr+CQD7zl+nuJsTySkp3A1XN1amKJXsPH2ZEvn0X+kC+KR4wTfuMS5SP51e7TfvMZJTUrgbpq6wpaQo2Rl0geL5ndOcpyvVGg9hyMxTDJmpXc72rNCU4AOrAbhyeisFi1dFoVCQ9CyBpET1z//ckbXkK+SNmXnahgdd+dDydcXSnly+eZuH4ZHEJTxhz+ET1K3qozkeG59AxGN1Z0tiUhL7jp2imIc7yckpPIpSN+g8S0wi4OgpPIsU1Hu8PoXzcemBeuh53NNEdp+7Rr2yqXu1w2PjAQiNjmPdiQu0q1JW/fsbof4cKUolO4OvUdyAOVvyte7I0PQc7sGDB9y5c4fq1dWV0qioKG7fvs3AgQP55ZdfCAgIwM/PjyNHjhAeHk7ZsmWJiIhI8z5WVlY8eKC/54peMjE25pvebWg0/nv10gyt62NvZUFL//ksHNIFN3sblEolm48Gc2jO2FTnfvvXLs3EbqPbNaKEu0u2xDujf0caj/pGvVRV+ybqeMfPYeHInrg52KrjPXyag/Mnpzr36z7t6Db9J75cuJo63qU0E7fp27XgXaxf1I+E2AiWftWQwqXr8Nnw1VwK2sKDm0HU7zCFsH8v8evXjXmaEMWV09twyudJ/6n7qdpwEH8t6MncL8qjUqmo124SFlb6HXlgYmLC+HHj6NylK0qlkn79+mJra0uv3n2YMX0azs7O+E+exPDhI0hMTKRly5aUKKG+GRw9ejTDhg3nq6++pmq1apqJYPQbrzHTP+9Gk6FTUKqUDO/UAntrS1p/OZ0FYwbwLOk5n43/FgClSsnAtk0oVcgdgK8GdqHvVz8S9+QpBVwcWTwh41lydaX5d6s4ezeEhKQkin0xhz8Gd+DrjYEs7NEcV1tL5nRpQs/F63n2PJnPqpajzIubz6/a1qPPLxuIe5ZIAXsblvRuqfdY30apUm/alhUiJ/qQcraJsTEzujWj8ZSfUCpVjGjhi71lXlrO+IWF/dvhZmfNFy388Js4HxMjI0q5u9C7XhVSlCq6/7CahGeJqFBRvWRh+tSvqtdYX4/5m16taTThB5RK5at7jCkLWDi486t7jGNnOfTdaM15KUol3WYvU8esUlGjdFH6Nqr5livpzvWzu/l7sTpn//p1IwqXrkOHoau4HLSFB7dOUa+9PyUqNOXK6W18N7QEZnmt6TBUXSmPiw5l5czmoFBg71KUNgN+0WusH2K+/mpEf5r3H4VSqWJo9/bY2VjRbuh45k0cSUqKkq6j/El6noxSpaK5Xw0a16pKwtOntBk8jufJySiVSlrWr0396pXefcH3jdfYmBkdG9H4m2Xqe84mNbC3MKfVd7+xsFcLXG2tGLlyG5cfhGNkpGB6x4bYWZiT+DyZHj/9RXxiEioV1ChRkD6+n+g93oxIvtYdhSq9BxZEjnH37l369evH7du3MTc3Jzk5mU6dOjF+/HhOnjzJ0KFDSUhIwMzMjDlz5lCjRg3u3LmDj48PkZGRmve5ffs2rVu3RqVS0bp1a81zaG8TGxuLtbU1YWvnaGYbzelUZh9GnK/7IaqLoUPIlPYVbhs6hExxDgk2dAiZpriePcM831fs00RcP/+GmJiYLPXIwavvmW3HQshrod17JMTH0rSK63tdNyNRUVEMHTqUzZvVk/c0b96cH3/8MdXyPW/q0aMHK1asSLWvcuXKHDt2TKexiZzPUDn75d9R6PKvNTOa53SKDzBfz3nSz9AhZEpHnzuGDiFT7GLuGDqETMt94aihQ9Ba7NNnuA6YnuXcmdPy9cdAesRzuIIFC7Jr1650j33yySccPZr2C8DDwyNVQgcoVKgQZ86c0UuMQgjxvnLKuqSdOnXi33//ZefOnQD069ePrl27smXLlree16hRI5YtW6Z5nTt31h8hER8uydlCiI9dTsnXHwOpiAshhDA4lSr9CXkzKqsPly9fZufOnRw7dozKlSsD8PPPP1O1alWuXr2qGYKZHlNTU1xc9P84jRBCCGFIOSFffyxksjYhhBAGp0SRqQ1Is45yYuL7LXl49OhRrK2tNZVwgCpVqmBtbc2RI0feem5gYCBOTk4UL16cvn37Eh4e/l6xCCGEEDlRVvK1vkRFRdG1a1esra2xtrama9euREdHv/WcHj16oFAoUm1VqlTRa5wZkYq4EEIIg8vKciju7u6a5Gttbc2MGTPeKwb1Uj1pZ392cnIiNDQ0w/MaN27M6tWrCQgI4LvvvuPkyZP4+fm9d8OAEEIIkdPkpOXLOnXqRHBwMDt37mTnzp0EBwfTtWvXd57XqFEjQkJCNNv27dv1GmdGZGi6EEIIg8vKULf79++nmvzF1NQ03fL+/v5MmTLlre958qR6bdn0lpBSqVRvXVqqQ4cOmn+XKVMGHx8fChYsyLZt22jduvVbryuEEEJ8SHLK0PSP4XEyqYgLIYQwuMysN/qynLbrJw8ePJiOHTu+tYyHhwfnzp0jLCwszbGIiAicnbVfc9jV1ZWCBQty/fp1rc8RQgghPgRZydexsbGp9puammbYeK6tdz1O9raK+MvHyWxsbKhduzbTpk1Ld0ScvklFXAghhMHpc11SBwcHHBwc3lmuatWqxMTEcOLECSpVUq8pe/z4cWJiYqhWrZrW13v06BH379/H1dU1c4EKIYQQOVxW8rW7u3uq/ZMnT8bf3/+94nifx8natWtHwYIFuX37NhMnTsTPz49Tp069d+NAZskz4kIIIQRQsmRJGjVqRN++fTl27BjHjh2jb9++fPrpp6la1j09PdmwYQMA8fHxjBo1iqNHj3Lnzh0CAwNp1qwZDg4OtGrVylAfRQghhMgx7t+/T0xMjGYbN25chmX9/f3TTKb25hYUFARk/XGypk2bUqZMGZo1a8aOHTu4du0a27Zte/8PmknSIy6EEMLwMjOpix4nf1m9ejVDhw6lQYMGADRv3pz58+enKnP16lViYmIAMDY25vz586xcuZLo6GhcXV3x9fVl7dq1WFpa6i1OIYQQwiCykK+1fZQM/luPk0lFXAghhMHllMlf7OzsWLVq1Tuu/yqAPHnysGvXLv0FJIQQQuQg+s7X/6XHyWRouhBCCIPLSeuSCiGEECJ9OSVffwyPk0lFXAghhMG9bGHXdhNCCCFE9stJ+Xr16tWULVuWBg0a0KBBA8qVK8dvv/2Wqkx6j5O1aNGC4sWL0717d4oXL87Ro0cN8jiZDE0XQghhcKpMPHOm9bNpQgghhNCpnJSvP/THyaQiLoQQwuD0uXyZEEIIIXRD8rXuSEVcCCGEweWUydqEEEIIkTHJ17ojFXEhhBAGp0KBSstJXbQtJ4QQQgjdknytO1IRF0IIYXBKMjHUTa+RCCGEECIjkq91Ryri4p3iCpYHCwtDh6GV5yZmhg4h0y4Ghhk6hEy5VqyAoUPInOxfFvK9OZqYGjoEraQkPNHZe8lQNyHeX4pbIVLymhs6DK0oc+cxdAiZdm9vrKFDyJSHZZ0NHULmWBs6gMxzKRBu6BC0p6OcLflad6QiLoQQwuAksQshhBA5n+Rr3ZGKuBBCCINTqhQotVzmRNtyQgghhNAtyde6IxVxIYQQBict7EIIIUTOJ/lad6QiLoQQwuAksQshhBA5n+Rr3ZGKuBBCCINTqbSfhVUSuxBCCGEYkq91RyriQgghDE6lUqDS8lkybcsJIYQQQrckX+uOVMSFEEIYnAx1E0IIIXI+yde6IxVxIYQQBqfMxFA3bcsJIYQQQrckX+uOVMSFEEIYnLSwCyGEEDmf5GvdkYq4EEIIg5PELoQQQuR8kq91RyriQgghDE6GugkhhBA5n+Rr3TEydABCCCHEyxZ2bTd9mTZtGtWqVcPc3BwbGxstY1fh7++Pm5sbefLkoU6dOly8eFF/QQohhBAGklPy9cdAKuJCCCEMTqnM3KYvSUlJtGvXjoEDB2p9zqxZs5gzZw7z58/n5MmTuLi4UL9+feLi4vQXqBBCCGEAOSVffwxkaLoQQgiDyynPnE2ZMgWA5cuXaxmLirlz5zJhwgRat24NwIoVK3B2dmbNmjX0799fX6EKIYQQ2S6n5OuPgfSICyGE+CDFxsam2hITE7M9htu3bxMaGkqDBg00+0xNTalduzZHjhzJ9niEEEII8WGQirgQQgiDy8ozZ+7u7lhbW2u2GTNmZHvcoaGhADg7O6fa7+zsrDkmhBBCfCzkGXHdkYq4EEIIg1PyaibWd24vzrl//z4xMTGabdy4cem+t7+/PwqF4q1bUFDQe8WvUChSvVapVGn2CSGEEB+6rORrkT6piAud2x14iGqftqNKkzasWrcp1bEnT5/RaeBwqjdrT62Wn/HL6j81xwaNmYxv687UbtWJ0VNnosymGR7+2befWo2aU6NhM9b89Xea4xOmTqd8tTo0afNZqv1DvhxH/RbtqNusDeP8v862eL1KmDJjiCMrprqS3yn9aR6a1MjL15878vXnjswa5sSiCS6pjhdwMWH5FFe8SphmR8gkJT7jqy/a06t5Scb0bUBMVGSaMns2r6SjX34+7/AJn3f4hGOBWzTHTh/7h4HtKzKgrTczxnTWe7wBAQHUq98Av7r1WLv2zzTHz549S6NGjfH1q8uPP/6o2X/37l1atGyFr19d/jdxIqpsagrecegkFToMxKtdf1Zs3p3meJlWfajaZQjVuw2jzcgpmv23/g2hds+RlG/bj+EzF2ZbvOlRqVSZ2gCsrKxSbaam6f8+Dx48mMuXL791K1OmTJbidnFR/2292fsdHh6eppdciJxmx+GTeHf8HK8Og1i+eU+6ZZRKJXX6fEmXCbM0+/afOkf1HiOp2n0ELYb78zg2+yYm3HnwOBXb9Ma7dS9WbNyR5vinA0ZTvdNAKrfvx8yfV7+K+WQwNTp/TvVOA2k5eDyPY7In5rJFcjGxlxULR9vi5mD8zrKLxtilKZfP0ZgFX9pStkgufYYKQGLiMyYM60zHRuUZ2qMp0VGP0pTZt2sD3VtVpWfr6gzq0oC7t64BcObEQRpXcadn6+r0bF2djWuX6j3eDy5fHz2NV7dRlOsykuXb9qU5XrLjMCr1HkuVPuNoNfbV31zg6YtU7Tueyr3H0ezLGTyOjc+WeNOTlXytLx/6Sif/uYp4XFwcFhYW9OnTx9ChaCU4OJg//0z9xeLl5cXTp08NFNHbJScnM/nbuaxfuoB//lrJ/F9XEhUTk6rM4F7dOLzlT3as+ZXlf6zn9r37AMyc+CX7/l7N/g1riIqJZWfAgWyJd8o3s1m74md2rv+Dhb8sIyo6dbwtP23Mb0sWpjl3+uQJ7Nn0F3u3rCc6JoZde9N+oepDSGQy836P4urdpAzLbD+UwP8WRPC/BRFsOxTP6cvPUh1vX9+KCzez73naHX8vxSVfIX7dfJmqvs34c9m36Zar+2lnFqw9yYK1J6lSpxkAcbFRLJn9JdMWbGXRujMMHPO9XmNNTk5m2vQZrPptJZs3bWTxkiVER0enKjPZfwpz537Pnt272Buwj6vX1DchM2fNYtjQIewL2Etk5CP27dP/70Rycgrj5y1l649fc3D593z/2/p0bzD3LJnF4ZU/sH7OZM2+SQuWM7b3Z5xdt4Twx9HsPPx+vcLvQ59D3RwcHPD09HzrZmZmlqW4CxUqhIuLC3v2vKrEJCUlsX//fqpVq5al9xSvSM7Wn+TkFMb9uIxtP07l4K/fMXf1hnQr1Cu2/kNB19SNSqPnLmXZlC84uuJ7yhUvzK8b0zYA6ivm8XOXsOWnmRz4bT5zV/6V5vtuzezJHF7zE0d+/4k9R09y9uoNAMZ+t4hl08ZyeM1PlCtRhGV/b8uWmMMep7BkYzw37ie/tZyJMdT1MeP2w7TlWtXOw+U7z/UVYipb1i3HLb8Hf+w8S826TVn9y5w0ZSrXqM/yv4+w7O/DdO33BYvmTNIcq1ilDsv+Psyyvw/TskNvvcb6weXrlBTGLlzN9u/Gc3jJNOb8viXdCnXAfH+O/TKDDd+M1uz7cv5KVkwczPGlMyhf1IOlW/bqPd6M5KSh6R/6Sif/uYr4H3/8QYUKFVi/fj3x8bptTUpJSdHp+0H6ST04OJg8efLo/Fq6cOb8JUoUKYyrsxMWefNSt2Y19h0+pjlunseMap9UACCveR4KFXQnLELdO2ppYQGov1ifJSZCNgzrDD53geLFiuDq7IyFRV78atVg/6HUEyx9UsEbWxvrNOemivdZYrYNQw17lEJI5NsT+usqlzXj2PlXN4HVvfJw6VYisfHZN2Do+IFt1G2q7smu+2kXjh/Q/gYocMcf1G7YHjtHVwBs7Jz0EuNLZ8+do1ixYri4uGBhYUGdOrU5cPCg5nhYWBgpycl4enpiYmJC82bNCNgbgEql4syZYHx9fQFo1aolewMC9BorwKlL1yhZqABuTvZY5jWnQTUf9h4//c7zVCoVJy5cpVF1HwA+a+zLzkMn9B1uxvFkYikUlR5/de/du0dwcDD37t0jJSWF4OBggoODU+ULT09PNmzYAKiHpA8fPpzp06ezYcMGLly4QI8ePTA3N6dTp076C/Q/QnK2/gRdvq7+7nC0xzJvHhpUrcDe48GpyjyOjWP9P4fo2aJBqv0KhYL4J+q8kvD0KS72ttkS86mLVylZuCBuTg4vvu8+IeBY6gZEK4u8ACQ9TybpeTIKFC9ihriXMT95iouDXbbEHB6lJOzxu7+0GlQ240DwM54np665VC6dm6v3kolLyJ4e2yOBO2jYrCMAjZp/xuHAtKMOzPNaaO55niTEZ8v9Wno+tHwddPkmJT3y4+Zoh6V5HhpU9uKfk+e0OlehUBD/VN2pkvD0GS72NnqM9O1ySr4G9UonI0aMoGzZstrF/sZKJ2XKlGHFihU8efKENWvW6DfYdPznKuJLly5lzJgx1KxZU5Msk5KS6NevH8WLF6d69eoMGjSItm3bvvPY8uXLadSoEd26dcPHx4cTJ05w8uRJ/Pz88PHx0dw8vDR//nyKFSuGj48PEydOxMHBAVBX5Bo2bIiPjw+lS5emc+fOPHnyhPDwcCZNmsQ///yDl5cXAwYMAF78Mb64IQkKCqJq1aqUK1eOSpUqcfjwYQDu3LmDg4MDkyZNomLFihQtWpTt27e/9WeTmJiYZhbizAqNiMDV2VHz2s3ZidCwiHTLPggJ4/K1G5Qt6anZ13vEWMrUbkxe8zw08q2Z6etnOt7wCFycXlXsXF2cCQ0L1/r8fkO/wKu6H+bm5jTwq6OHCN+PhbkRBVxycfFF77eZqYI6PubsOpaQrXE8jgjB3skNAEsrWxLiYtItt3/nnwxsX5HZ/+tFXMxjAB7cu0HUozBG9fJjWJfqnDj49t/j9xUeFobLa0OKXVxcCAsL07wOCw/H2SXt8aioKKytrTU3J65vnKcvIZGPcXV8dUOZz9GekIjHqcooFNB44Djq9PqCTfvUDU2PY+KwtXp1M+Xm5MDDiLRDELNLTmlhnzRpEt7e3kyePJn4+Hi8vb3x9vZO9Qz51atXiXltpM/o0aMZPnw4gwYNwsfHhwcPHrB7924sLS31F+h/hOTs9OkkX0c+xu21yqibo32a74Cpi1czpkd7jI1S3y5+P6o/rb6YSrHmvbhw4y6fNaqd6etnRUjkI1wd7V/F7OzAw/C031v1e42gaMOO1KnkTbkSRQCYM3YIbYb+jxKNO3Hxxm06NqmbLTFrw97KiMJuJpy+mrrX2yw31ChvSkDQswzO1L3I8FAcnF/ka2tb4jPI1zs3reGzxl4s+HYCn4/6WrM/+ORBerSqxvihnQh9eE+vsX5w+fpRFG4Orxqt8jna8TAyKlUZhUJBg2FfUWvgRDbuf9U4/sPwnrQcPZMibT/nwq37dKqv/3vkjGQlX+eEVU4g56108p+qiF+8eJH79+/TqFEjevfuzdKl6mdXFi9ezL1797h06RJ79+7l9OlXvUlvOwZw6NAhJk6cSFBQECVLlqR///6sXr2aoKAgdu/ezciRIwkNDeXcuXPMmDGDw4cPExQUlGr4g7GxMWvWrCEoKIgLFy5gZWXFwoULcXJyYurUqdSrV4/g4GAWLVqU6tpJSUm0bt0af39/zp07x5w5c2jbti0JCepK1qNHj6hYsSKnTp1i/vz5jBgx4q0/nxkzZqSagdjd3T3TP+P0bpDT6yl+lphIv1ETmDxqCHnNX/UULP3+G84FbkelUnHw2MlMXz/z0gacmYbdJfO+4/TBvaBScejocR3GpRuflDLj9JVnpLxokWzjZ8nWA/HooSPorbR5Rqhyrab8uvUKC9cGkc+jOD/PGQNASvJzbl87z/SftjNxzp8smDGcuNiod7zb+8Sadt/LHpWMCyjS/YypztOTdK/7xmV3L57JwRVzWf3NOPx/WsnN+w8zOM9wk4tpPfHLi01fli9fnu4zbnXq1NGUUalU9OjRQ/NaoVDg7+9PSEgIz549Y//+/Vl+5ly8Ijk745ytm3z99u+As9duER2XQM0KaX+XF6zdwua5/lzf/CuVypTgu9/Szq+iD9p+b+359XuubF/N+Ws3uXTjDgAL1mxg44LpXN2xhk/KlmTO8rX6DldrbfzM2bA/7eMLzWqYs+vYqxyeHbR9prdRi078viOYYeNmsWKR+lnm4qXK8+fuCyzfcITa9Zszfbz2w4Wz4sPL1+mGk8reHydzZMk0fp86nMm/rOXmA/X8I/PX7WDz7HHcXLeASqWKMnvNprRvlk2ykq9zwionkPNWOvlPVcSXLl1Kt27dMDY2pmnTpty6dYvLly+zb98+unbtiomJCWZmZnz22atJud52DKBGjRoUK1YMgCNHjnDr1i0aN26Ml5cX9erVQ6VScfXqVQIDA2nSpAlOL3pfe/bsqXkPlUrF999/j7e3N+XKlWPbtm0EBwe/8/NcvXqV3Llz07BhQ00sTk5OnDunHuaSN29eWrRoAUDVqlW5efPmW99v3LhxqWYgvn///jtjeJOrkyMhr/WAPwwLx8nRIVUZlUrFkPFTqVurGs0apG2Rzp0rF439arMjYH+mr59ZLk5OhIa/6gEPCQ3DydHxLWeklTt3LhrW89XrM+INqryafM347XO9pFK5bB6OvzYs3SNfLro3s2bOF058UtqMPq1sKFNUPxO2bVozXzPxmq29M4/CHwLqZ77zWqYd6m9lY0/u3KYoFAoaterJtYvqHkgHp/x8UqMRuU3NcHDKR8EiJXl4/+2/y+/D2cWZ0NdaxkNDQ3FyevU74ezsTFjoG8cdHbGzsyMmJkaT4ENCQ3F00u8welD3Yr3eA/4g4hHO9qmHXL7sQcrn5EAdn3Kcv34bexsromLjNfE+DI/ExSF7hpemJ6f0iIucQ3J2xt9zOsnXjvY8jHz13fEw4lGqIeYnL17jyNlLlG7Tjx6Tv2PPsdMMmbmQiKgYrt75l/LFCwPQyq8ax89fyfT1s8LN0YGQ13rtH4ZFZjjE3DKvObV8vNhz9CSRUdFcu32P8iWKAtCybk2On7ustzh9K5oyoYcVE3pYYazFnba7szGD2lgwbYA1hdxMGNreEld7Iwq4GPNZA3OmDbDGu0RuujbOS0mP9CdpfR/rVv2kmWDNzt6JyLAX+TomCot08vXratdvztGD6jkC8lpYYZ5X/dhew2YduXXjks5jfd0Hl68dbFP1gD+IeIyLnU2qMq4v8nA+R3vqVCjNuRt3iYiO5erdh3gV8wCgdZ3KHLt4Xe/xZiQr+VrbVU7gv7XSie7/mnOo58+fs2rVKnLlysXvv/8OwJMnT/j111/f+sN/13+MxYvnhF+WLVeuHAcOpJ1kLDg4OMP3WbNmDfv37+fAgQNYWloyb968dN9D29he7nt98iFjY+N3Pg9namqa4azD2vIuW4orN24SEhaOpUVe9h48whcDU0+y8/XchZjnMWVk/16afcnJyTwMC6dAPjdSUlL458BhvMqUeq9YtOFVrgxXr98kJCwMy7wWBBw4xIhB/d95XnJyMiGhYbjnz0dKSgp79x+kfJnSeotz97EEdmdyOLlVXiPcHE24dOvVpG7Tfnl1A9OvtQ0nLj7lwg39DA9q0WkwLToNBtSV8r3bVlO4RDn2bl1F5ZpN0pR/HBmKnYN6Buqj+zZTsIj6/79KnU/55fuxtO3xBU8SYrl/+woubh56iRmgfLlyXLt2jdDQUCwsLAgM3M+QwYM1x52dnTEyNubKlSsULVqULVu38s2M6SgUCry8yrNv3z78/PzYsGEj7dq20VucL1UsVZxLt+7yMPwRlnnzsPtIEGN6ddAcT3j6DKVSiWVec6Lj4jkcfJF+bT9FoVDwSeni7DwcROMan/D7jn10/bSe3uPNiEqpQqVlV7e25cSHS3L223O2LvK1T8liXL51j4cRj7A0N2f30dOM6dlec7xPq0b0adUIgIOnL7B4/XZ+HDOI5OQUIqNjuPMwDA83ZwKDzlGsQL73ikVbFUuX4NLNOzwMj8Qyrzm7j5xkTJ9XK2nExieQmPQcRzsbEpOSCDh2mkGftcTG0lId84NQPPK5sP9kMMUK5tdbnPtOJbLvlPa5deLiV8O/R35myR97nhDySMl3a16NxOjeJC+nryZx+Y72c8Roq22XgbTtou69XrfqJ3Zt+YOinmXZufl3qtVulKb8v3dvkr+gesj/ySMBOLuqR2Q8jgzHzkFdoT1+6B/c8nvoPNbXfWj52qdkES7dvs/DiMfqfH08mHHdWmmOJzx9hlKlwtI8D9HxCRw+e4X+LRtga5mXiJg47oSE4+HqxL7TFynm7qr3eDOSlXz9cnUTbQwePJiOHTu+tYyHh4dW7/Wm11c6cXV99TM01Eon/5mK+KZNmyhcuDDHjr2aOOzChQvUrVuX8ePHs2rVKtq3b09ycjJr167FzU39fIyvr2+Gx95UrVo1rl+/TkBAAH5+foA6mZcqVYo6derw7bffEhkZiYODAytWrNCcFxUVhb29PZaWlsTFxbF8+XIKF1a3NFtZWaV6FvF1np6eJCYmaq535MgRwsPDKVu2LBER6T+XrW8mJib4fzmM1r0GoVSq+LxXF+xsrOk0cDhzpkxAqVQyf+lKShQphF+bLgBMHDmYap9UYMDoiSQ8eYpKpaJqRW+6t2+dLfFOGvMF7bv1QalSMbB3D2xtbeja73O+/WoyLs5OjPqfPwH7DxIVHY1P7fp89b+x+NWuyedfjCXhyRNARWWfinTt2E7v8QKULWpKn1Y2WOY1YkxPey7fTmThn9F4e5pSKF9u/t6rTtyflDbj9OVnOaL3sFHr3swc15VezUti75iPCd+qb6yPBW7h2qXTdBs0mY2rf+TEwe0YGRlj7+TGsEk/AVCwSClKe1dnQFtvjI2N6TrIH2tbh7dd7r2YmJgwftw4OnfpilKppF+/vtja2tKrdx9mTJ+Gs7Mz/pMnMXz4CBITE2nZsiUlSpQA1M8KDxs2nK+++pqq1appJoLRJxMTY6YP7UXTweq/r+FdWmNvbUWbkVOYP24wz5Ke03nsdACUShUD2jWjZOECAEz5vAe9Jn7LmLk/U8enHA1fTNxmCJkZci718I+f5Gz9MzExZtrgHjQZMhGVUsWwzq3U3x1ffMX8sZ+nmnvizfO+H9Wf9qOnYWxsjJuDHYv+NzT7Yh7Wl08HjkGpVDKsazvsbKxoO2wiP/5vOCkpSjqPnsrz589RKlU096tB41pVAPhuzGA6jpyMsbERro4OLPL/IltiLlUoF10b58Uij4JhHSy5du85S7ckUK5oLgq6mLDlUM6aUb9Z2x74f9mLjo3K4+Dsxlff/wbAoYDtXLl4mj5D/seebX+xd8d6cuXKjYWVNeOnqfN1wK6/2bT2V0xMcpHX0opxX/+k11g/uHxtbMyMgZ1pPHIaSqWKER0/xd7aklZjZ7FwVF+eJT2n48TvAVCqlAxs3ZBShdQNRnOH96Dt+O8wNjLCzdGWJWMH6D3ejOg7Xzs4OGjm5NC111c68fb2Bl6tdDJz5ky9XPNtFCpDLhybjRo3bkyTJk0YMmRIqv3e3t6MGzeOXbt2cejQIfLnz0/JkiV5+vQpS5cuJSkpiYEDB6Z7bPny5WzdupV169Zp3i8oKIgvv/ySx48f8/z5cwoUKMDGjRsxMzNj3rx5zJs3D1dXV/z8/Fi1ahU3b94kJiaGNm3a8PDhQ/Lly0epUqV48OAB69atIyYmhsaNG5OQkEDVqlVZtGgRCoVCs6TLyZMnGTp0KAkJCZiZmTFnzhxq1KjBnTt38PHxITJSPSN5fHw8lpaWmVrPLzY2Fmtra24c26uZITyne26StSWIDGnsb/qrVOpD57YfVrzFLfU7WYw+OEbob8ikLsUmPCF/vY7ExMRo3dKd5j1efM/4r4jCzFy793j2JBb/7rbvdV2Rs0nOzlzOfvl39GD3aqzymuvuP0KPlLlz3kzy7zJ67yeGDiFTujTT/7rjuuRmqv8J03TN5b7hVhvJrNiEJ7h+2jfLuTMn5ut79+7x+PFjNm/ezLfffsvBF7PmFy1aVDMCytPTkxkzZtCqlXr0wcyZM5kxYwbLli2jWLFiTJ8+ncDAQK5evZrtk6z+Zyri7xIXF4elpSWJiYk0b96cdu3aadYtfduxrFwD1M8/3Lhxg1WrVun0c+iSVMSzh1TE9Usq4vqjy4r4pGWPM5XYp/a0k4r4f5jk7NSkIp49pCKuX1IR1y9dVcRzUr7u0aNHqhFLL+3bt08zyapCoWDZsmWaSVZVKhVTpkxh8eLFREVFUblyZRYsWGCQSVb/M0PT36VevXokJiby7Nkz6tWrl2pG3Lcdy4yxY8dy+PBhkpKSKFSoED///LNughdCiA9cZiZhk+ZjITlbCCEMIyfl6+XLl7N8+fJ3xJA6iJcrnfj7++svMC1JRfyF48czXnrqbccyY8GCBTp5HyGE+NjkpMQucj7J2UIIYRiSr3VHKuJCCCEMTqlSodQyY2tbTgghhBC6Jflad6QiLoQQwuBUSvWmbVkhhBBCZD/J17ojFXEhhBAGp0Kl9QzRKqSFXQghhDAEyde6IxVxIYQQBqdSglJa2IUQQogcTfK17khFXAghhMGpVJloYZdnzoQQQgiDkHytO1IRF0IIYXBKlXrTtqwQQgghsp/ka92RirgQQgiDUylVqLTM2NqWE0IIIYRuSb7WHamICyGEMDhZl1QIIYTI+SRf645UxIUQQhicUqlCqWXLubblhBBCCKFbkq91RyriQgghDE4mfxFCCCFyPsnXumNk6ACEEEIIIYQQQoj/EukRF0IIYXAqpfbrjcq6pEIIIYRhSL7WHekRF0IIYXBKlSpTm75MmzaNatWqYW5ujo2NjVbn9OjRA4VCkWqrUqWK3mIUQgghDCWn5OuPgfSICyGEMLic8sxZUlIS7dq1o2rVqixdulTr8xo1asSyZcs0r3Pnzq2P8IQQQgiDyin5+mMgFXEhhBAGl1NmYZ0yZQoAy5cvz9R5pqamuLi46CEiIYQQIufIKfn6YyAVcfFO/5oUxsLEytBhaCVJ+eH9St86e8bQIWTKtSquhg4hU3IVyGfoEDLP0dABaCfOLE5n75WVdUljY2NT7Tc1NcXU1FRnMWVGYGAgTk5O2NjYULt2baZNm4aTk5NBYhH/XQ8dvIi1tDR0GFp5pjQzdAiZdv7QeUOHkClny9YydAiZEuWUx9AhZJ67oQPQXlycbnK2rCOuO/KMuBBCCINTqVSolFpuLzK7u7s71tbWmm3GjBkGib1x48asXr2agIAAvvvuO06ePImfnx+JiYkGiUcIIYTQl6zka5G+D6/7UAghxEdHlYlJXV4m9vv372Nl9Wq0Tka94f7+/poh5xk5efIkPj4+WkabWocOHTT/LlOmDD4+PhQsWJBt27bRunXrLL2nEEIIkRNlJV+L9ElFXAghhMG9bD3XtiyAlZVVqop4RgYPHkzHjh3fWsbDw0Ora2vD1dWVggULcv36dZ29pxBCCJETZCVfi/RJRVwIIYTB6TOxOzg44ODgkJWwsuTRo0fcv38fV9cPaz4FIYQQ4l2kIq478oy4EEIIg1OqMrfpy7179wgODubevXukpKQQHBxMcHAw8fHxmjKenp5s2LABgPj4eEaNGsXRo0e5c+cOgYGBNGvWDAcHB1q1aqW/QIUQQggDyCn5+mMgFXEhhBAGp/XEL5loic+KSZMm4e3tzeTJk4mPj8fb2xtvb2+CgoI0Za5evUpMTAwAxsbGnD9/nhYtWlC8eHG6d+9O8eLFOXr0KJYfyOzVQgghhLZySr4GmDZtGtWqVcPc3BwbGxutzunRowcKhSLVVqVKFb3GmREZmi6EEMLgVCrtZ1fV5+Qvy5cvf+ca4q9fP0+ePOzatUtv8QghhBA5SU7J1wBJSUm0a9eOqlWrsnTpUq3Pa9SoEcuWLdO8zp07tz7CeyepiAshhDA4pRKUWracK5V6DkYIIYQQ6cpJ+frliijvakB/k6mpKS4uLnqIKHNkaLoQQgiDe9nCru0mhBBCiOyXlXwdGxubaktMTDToZwgMDMTJyYnixYvTt29fwsPDDRKHVMSFEEIYXE565kwIIYQQ6ctKvnZ3d8fa2lqzzZgxw2DxN27cmNWrVxMQEMB3333HyZMn8fPzM0jjgAxNF0IIYXCyHIoQQgiR82UlX9+/fx8rKyvNflNT0wzP8ff31ww5z8jJkyfx8fHRKoY3dejQQfPvMmXK4OPjQ8GCBdm2bRutW7fO0ntmlVTEhRBCGJwSFUoth5wrkYq4EEIIYQhZyddWVlapKuJvM3jwYDp27PjWMh4eHlq9lzZcXV0pWLAg169f19l7aksq4kIIIQxOesSFEEKInE/f+drBwQEHB4dMn5dVjx494v79+7i6umbbNV+SZ8SFEEIYnEzWJoQQQuR8OSlf37t3j+DgYO7du0dKSgrBwcEEBwcTHx+vKePp6cmGDRsAiI+PZ9SoURw9epQ7d+4QGBhIs2bNcHBwoFWrVnqNNT3SIy6EEMLgVEqV1suhSI+4EEIIYRg5KV9PmjSJFStWaF57e3sDsG/fPurUqQPA1atXiYmJAcDY2Jjz58+zcuVKoqOjcXV1xdfXl7Vr12JpaanXWNMjFXEhhBAGJ0PThRBCiJwvJ+Xr5cuXv3MN8dd75fPkycOuXbv0GlNmyNB0oXOJic/4ckg3WjbwoX+3FkRHPUpTZvvmP+nQrAYdm9dkUM9WhIU+AGDHlr/o1LI2nVrWpkOzGlQq5UhMdJTe4x03tDPtGnoxuMen6cYbsHMDXVtWo3urGgzo0pA7t65pjp04EkDXltXo3LwKE0f20GusAB1b5mf1Qh9WzKvI9PGlMc9jnKZMgzpOrPyxIivmVWTuV+VwtM+d6nhRj7zs31iLap/Y6T1egAsntjLj87IMb27Mw7sX0i2jUqlYu2AAX/UrxuwRnxAZclNz7MqZPXwzpDwzPi/L8plvn8BDF5ISn+E/oiPdPi3DF70bERMVmabMrk2/0bZOQfq3r0z/9pU5ErhVc+y3RdPp9mkZereqyJXzJ/Ueb0BAAPXqN8Cvbj3Wrv0zzfGzZ8/SqFFjfP3q8uOPP2r23717lxYtW+HrV5f/TZxo0CHfOWmomxD/JYmJzxgyqB8N69ake5cORD1+nKaMSqXCf+I4GtatSdtWTbl39w4ASYmJjB41jOZN69O2VVMuX7qYLfF+MbgHzetXom/XVkQ9TpuzL5w7TafW9fmktBsH9u3W7E9KSuR/Xw6iXbPadGpdn6uXz+s9Xm1yNsCXg4qxdnElls6pQD4XMwA+8bLl17kVWPljRRbN8qJwwbx6j/d50jN+/roN/n2K8cNYP+Jj0ua/hLjHLJrSnOmfl+f70bV4HH4PgJsXDzFjsBczBnsza3glbl06ovd4kxKfMf3LdvRr5cmEAfWIjU4b794tK+jawI1hnSoyrFNFju/fAkBKcjLfT+7BkI5efN6+PHu3rEhzrq5Jvhavk4r4e/Dw8ODChdSVijp16rB169YMzsjYnTt33joxQXrXSo+XlxdPnz7N9PV1acOfK8mX34ONu4OoU68Jy5f8kKZMfvdCLF2znT82H6RBk9Ys+P5rABo3a8eajftZs3E/I8d9jVfFqljb2Oo13s1/rcAtvwd/7QqmZt2m/PbznDRlqtSsz8oNh1mx4RDd+n7Bwu8mAxAbE8UP34zn+5//ZvXmY4yY8K1eYwW4eiOOnsNO0X3oKW7fS6BTa/c0ZR6EPGPgmGC6Dz3F3oPh9O9WKNXxAd0LcTJYvw0cr3PKX4JeY/+iSOlaGZa5eHIrCbGPmLjkOg07/I/Ny8cC8CQ+ig2/jGTglJ2MW3CeNv3n6T3ebeuX4Zq/ECu3XqC6XzP++PW7dMvVb9aJxX8eZ/Gfx6lW51MAbl2/wPFDu1i2MZhxM35l3owReo01OTmZadNnsOq3lWzetJHFS5YQHR2dqsxk/ynMnfs9e3bvYm/APq5eUzckzZw1i2FDh7AvYC+RkY/Yt2+fXmN9G5VSmalNiMySnJ2+v9b+jnuBAuzae5C69Rrw85KFacoEBvxDVNRjdu09yMDPh/Hdt+o1gP9cuwZz87xs3raHufN+YtY3X+k93r//XEU+94Js3nMC33qNWfZz2pzg6OTC5GlzaNg09TOf69f+Rh7zvPy1ZT/f/vALc77x13u82uTs6p/YY22Viw79T7Bs7V0G9igMQHRMEqP8z9NtyCl+WX2HLwYU1Xu8h3f+jL1LIfx/uU65qi3Y/dc3acrsWjuNIqWqM37BWVr1+pZNy8cB4F6kAmN+OMW4+WfoOnI5axcO0nu8uzf+gku+QizZcIXKtZuzbvmsdMv5NunCD2tO8cOaU1Su3QyA4wc2k5yczI9/BDNjyV6WzRuLUo/5RfK1eJNUxD8ywcHB5MmTx6AxHNy3iyYt2gPQtEUHDuzbmaZMOe9PsLBUL2PgWaocEWEhacr8s2MTDZq01GusAIcCd9CoubqXtXHzjhwOTBuveV4LFAoFAE+exGn+vWfbOuo1aYODowsAdvaOeo/3zIUYkp6rWxiv3YzHwS53mjIXr8aS8CQFgKs343G0f7VeYyNfZ06diyYqOknvsb7k5FYMZ3fPt5a5cGILn/h2AaB0pWbcvnwYlUrFqf1rqFCrA9Z26tksLW2c9B7vsf3bqffpZ4C6sn10//ZMnevXuD3GJiYU9SxP8vPnPIpI+/utK2fPnaNYsWK4uLhgYWFBnTq1OXDwoOZ4WFgYKcnJeHp6YmJiQvNmzQjYG4BKpeLMmWB8fX0BaNWqJXsDAvQW57soXzxzpu0mxMcgJ+TsfQH/0LyFeu3cFq3asC/gn7Rl9v1D85ZtAPD1q8fp00GoVCpu3rxB1arVAcjvXoCIiAgiIsL1Gu+Bfbto2qIdAJ+2bM+BgN1pyji7uFGiZFmMjFLf5t6+eY1KVdUNwvncCxIZGU5kRJhe49UmZ1evZMfOfeo4Dp94RNmS6vuj67cTeBz9HEiby/XlwomtVPLrCkBlv25cOJG2oSr0/hWKe9UFwMOzMlfO7EalUpHbzBwjY3WPf+KTOECh93hPHNyGb5POAPg17crJg9sycbaCxGdPSElJ4dnTBKxsHNL8zuiS5GvxJqmI68maNWuoXLky3t7eeHl5sX27+kZeqVQyePBgPD09KV++PBUrVuTZs2ea8yZNmkTFihUpWrSo5pw33bhxg3r16lGuXDm8vLzYuHGj5phCodDMFOjh4cGUKVOoVq0ahQoV4uuvv9bfB35NREQoTs7qSpOVtQ3xcbFvLb9lw+9Uru6bal9ycjIH9u3Er0EzvcX5UmR4KI7ObgBYWdsSFxeTbrkdm36nfSNvfpz1P4Z8qW71v3/3JlGR4Qzs0ojeHXw5vD97nztpUteZk2fe3rPdpK4zJ16UMc9jTLMGLvy15UF2hJcpMY9CsLbPB4CRkRHmFnYkxD4i4uF14qLD+GFMLb4bWZmLJzOTZLPmUUQIDk7q3wlLK1viM/idCNjxJ33bVuKbCX2IjVEP53wU/upcAAdnNyLDH+ot1vCwMFycnTWvXVxcCAt7dWMZFh6Os0va41FRUVhbW2salVzfOE+I/5L/cs4ODw/D2VndmGxtbUNcbNqcHR4WhvOL7xkjIyOsrW2IjoqiRAlP9v6zG6VSybWrV7h39y7hYaF6jTciPAwnp1f3GBnl7PQUK1GKwH+2o1QquX71Evfv3tZ7vK/LKGc72JsS+SgRAJUK4uKTsbYyeeNcF00u16eYxw+xeZGLzS1teZoQnaZMPo+ynD3yNwCXTu0iIfYRCXHqHHjlzD981b8UCyc3oePgn/Qe7+OIEOyd1PFaWNkSH582XoADu9cy5DNvvp/cg7gX+bpyrWaYmpnTo0kBhnT0oufQtL3/uiT5WrxJJmt7T23btsXMzEzz+saNGwA0bNiQzz77DIVCwZ07d6hWrRp3797lwoUL7N27l0uXLmFkZERMTAy5c6tbRx89ekTFihWZOnUqO3fuZNiwYTRp0iTNNTt37kzv3r3p168f169fp0qVKlSsWBF397TDnaKjozly5AgREREULVqUnj17ki9fvnQ/S2JiIomJiZrXsekkY61k4nmQfXu2cv5sEL+sTl25Cjp2kCLFS2ZLD7O28TZu8RmNW3xG4J7NLFv0LRNnLCI5+Tk3r11i7tKNxEY/pn/nhpT1qoSVtX6H0wO0b67+f9x7KCLDMrWq2FO6hBWDxgQD0KezB6vW3yc5OSe2UKaNSaFQkJL8nJC7Fxj01W4S4h7xw+gaFCpZDXML/f2MtXmmqUrtJvg2bk+uXLlZ88ssFs8ey5dfLUGVwefQl/RCVbzeC5FuAUW6n1GRDb0XGcnMs2TyzJnIqo8lZ+ssX6Pd31O6RRQK2rTryI0b12nTogmFihShTNmyGBvr99byff7+W7btzM0bV/msVV08ChejVJnyGJuk/8y2rr0tZ6f3zfv6xyxdwpIWjVwZMPqMnqJ7/brv/vk2aD+OP38awjdDKuDhWRkHl8Ka/3dP73pMXHyJ21eOsW3VZAZ/rd8OCm3i/aTmp9Rq2BGTXLn5a9k3/Dr3S4ZNXsrVi8fJbWrG8u33eBzxgImfN6K0d03MLaz0FGvafZKv/9ukIv6e1q1bR5kyZTSvX06Vf/v2bTp37sy///6LiYkJkZGR3L17l8KFC/P8+XN69eqFr68vTZs21QyDyZs3Ly1atACgatWq3Lx5M8314uLiCA4Opnfv3gAUK1aMGjVqcOjQIT777LM05Tt3Vg/XcXR0pHDhwty+fTvDiviMGTOYMmVKln4Of6xczOa/1wDq4dnhYSHY2NoTGxOtGYL+povnTzN/zlcsWr6R3LlTD7favWMDDRrrbz2/P39bxLa/VwFg6+BIRNjDF/FGYWlp/dZz69Rvziz/4QA4Obvh5JIPU1MzHJ3dKFTUk3/v3aJU2Yo6jbdts3x8Wk/dY9Hni9NU9rajoa8zg8cFZ3iOZzFLBnQvzNAJZ3n+ouJdoogFtaqon2u0tspFlQp2fPX9Fb20su/fPI/j/ywDYOR3xzHJlXY43uus7d2IefQAivmgVCp5Ev8Yc0s7bBzyY+PgTq7cZtjY58OlQGkiHt6gYPFPdBrvhtUL2blJPVGLrb0TkeEPsbZ1IC42Cot0fiesbew1/27Sugdf9m0KgINT6h7wyLCH2Dm46DTW1zm7OBP6Wst4aGgoXuXLvzru7ExYaOrjTo6O2NnZERMTg0qlQqFQEBIaiqOT/of9ZyQnzcIqPl4fS85+n3wN8NuKX/l7nXqiKAcHR8LCQrG1syMmJhpLq7Q529nFmbCwMMqUVY8SiImJxsbGBoVCwf8mTdWUa9LQl3z582c5roysWfkzm9ar7zHs7R0JDw/B1k59j/GunP26XLlyMXbiDM3rVo2q4ZavgM7jzWzOjniUiIO9KdyIR6EASwsTYuOSAXB1NuN/IzwZP/2iZp+uBW6ex9Hd6nxtZeNM9KMHWFg78CQuijx5bdKUz5PXmu6jVgLw/HkiX/XzJE/e1P8PhTyrEBVxn7iYCCytddupsuWPH/ln83IAbOydeBT+ACsbB+Jjo7CwSBuv1Wv5un6LXkwc1BCAAzv/oGK1RhgbG+PoUgA396L8e/cKxUtX0mm8L0m+Fm+Soel60rFjRwYMGMCFCxcIDg7GwsKCZ8+eYW1tzcWLF+nUqRNXrlyhXLlymhb511vpjY2NSUlJSfO+L1uW3uxhy6jH7c33TE7O+Et83LhxxMTEaLb79+9r/3m79ddMslanbhO2b1In+G2b1lKzTsM05R/+e4+JowbwzdxfcXwxjP2l5OfPObx/D3XqNdX6+pnVvusAVmw4xIoNh6jl9yk7N/8BwI7Nf1CtdqM05f+9++oG68SRAJxd1TcaNXybcDboCEqlkrjYaO7euopbfg+dx7tuywN6DDtFj2GnKFIwL4N7FWbs1xd4+iz9STBcnEyZ/IUnE2deIvLxq2fBPx93lrZ9jtO2z3ECj0Qw48erehvqVrv5UEbPO8PoeWfeWQkHKP3Jp5zcp24cuXhiCx6e1VAoFJSp1JybFw++qJxHE3b/MvbOhd7xbpnXqvMgzcRr1X2b8c/W3wHYs2UNVWo1TlP+ceSr4YyHA7ZQsEhJAKrUakzAjj9JSU7mxpWzmJiYpBqqrmvly5Xj2rVrhIaGEh8fT2DgfmrWrKk57uzsjJGxMVeuXCE5OZktW7dSt64fCoUCL6/ymglfNmzYSF0/34wuo3cvE7u2mxC69KHl7PfJ1wBdu/diw5adbNiyk7r1GrB5k3qY8aYN66njWzdN+Tq+ddm8cT2gfqbc29sHhULBkydPNJPNbdu6mdKly2KZQeP7++jUrS9rN+1j7aZ91KnXmG2b/gJg68Y/qelbX+v3efokgadPnwCwc9sGSpYup5d4M5uzj5x8TCNf9ZDk6pXsuXBFPcLBIq8x30wozZxFN7h974nO43ypTvOhjJt/hnHzz1CuagtOBPwGwPGAlZSplPZe7El8NCnJ6mfX9234Hp86nQCIDL2N8sXfwcM7F0h8Fk9eS/s057+vZh2HaCZeq1K7Ofu2rwYgYNtv+NRIOyol6rV8fXz/JgoULgWAg7M7506qn7WOi3nMvVuXcHbT/f3FS5KvxZukR1xPoqKi8PDwAGDVqlVERakrOxERERgbG9OgQQPq16/P/v37uXTpEuXKldPqfa2srPDy8mLFihX07NmTmzdvcvjwYebPn//eMZuammJq+v4TgbRs340JX/SlZQMfnJxcmTlP3cq6P2AHly8EM2DoOJYu+o6Y6MdMHqOeUdMtfwFmz3/xxX90PyVKlsXGNnuW1mrRrjuTRvWmXUMvHJ3dmDZX3cp7MGA7Vy6eoe+QCezeto5/tq8nV67cWFhZ87/p6ueeChcrSbkKVejSvApGxsb0HfI/bGx1n3ReN7BHYczNjfl2UlkAzl2OYc6iG9SoZI9nMUt+WX2HHh0KYm2Zi4kj1BOkPQx7xvjp+l9WJiOXT+/i93l9iI+JYOH/6lOsnC/dv1zD+eObuX89iCZdplL6k0+5eHIrU/sWJU9eG7qPVleEXQuWpnCp6nwzuCxGRsY06TIVC+uMZyvWhSZtejJtbHe6fVoGByc3Js1WJ/kjgVu5dvE0PT6fxPpVCzh+YAdGxsY4OLkyctICAAoXL8sn1evTo0V5cuc244sp+n1GzsTEhPHjxtG5S1eUSiX9+vXF1taWXr37MGP6NJydnfGfPInhw0eQmJhIy5YtKVGiBACjR49m2LDhfPXV11StVk0zEYwhKFGiVGk3u6oSmYVV6NaHlrN1la8B2nXoxKgRg2lYtyZOzi788OMiAAL27ubC+fMMHf4FdXzrEbhvLw38amBpZcV3c9Xfd5ER4fTv2wOFQkHBgh5M/yb9FSZ0qXX7Lowb2Z/m9Svh6OzKt/OWAhC4dyeXLgQzaNhYbt64yqBe7YmNjeHgvj0UKlKMX9dsITIynCF9O6FQKCjgURj/6WlXddE1bXL24ZOPqPaJHX8uqURcQjKTZ10GoE3TfLg5m/F5T/Us6knPlfQbpd/h6dUa9mX5rE749ymGjX0+eo9XN3qcO7aZe9eD+LTrVB7eOc/qH/qgUCjw8KxCx8/Vee7a2b0EbJyLsXEucuU2o/uo3/Q6+RlAg5Z9mP2/LvRr5Ym9oxtjZ64F4Pj+Ldy4fIrOA/zZ/Ps8Th7ajpGxEfaO+Rg8Qf073qTdQOb692JwBy9UKhWf9Z2Ita3+HomUfC3epFDJ4P0s8/DwYOvWrWmGuY0aNYro6GgmTpxIvnz5qFq1Kn/++Sfbtm0jKSmJvn378vz5c5RKJdWqVWPBggU8ePAAHx8fIiPV6x/Gx8djaWmpaU3Pnz8/gYGBFC1alBs3btC/f38iIyNRKBT4+/vTsmVLQN3KHhcXh4WFRZr4fHx8mD17tmYo3rvExsZibW1NYNBtLPT0vIyuJSk/vLalUdnwzJcudeif8RJkOVHpAs/eXSiHKZQ3502ml564uDi8vCsQExODVTrDWbXx8numad/T5MptodU5z5Pi2fbz+11X/Pd8zDn75d/RydMXsbC01O0PTk+eKc3eXSiH+fyLS4YOIVM6Dfyw8nUBp7SjSnK60rZ3DB2C1t43Z0u+1r0Pr9aSg9y5cyfNvsDAQM2/u3Tpovn3t9++Wl/61KlTac7z8PDQJHQACwsLTUIPCQkhLi5O85xY0aJF2bt3b7oxvd6u8mZ8QUFBGX8YIYQwoJzwzNmdO3f46quvCAgIIDQ0FDc3N7p06cKECRM0E3SlG49KxZQpU1iyZAlRUVFUrlyZBQsWULp0ab3EKbJGcrYQQry/nJCvPxbyjHgON2fOHOrUqcPs2bMNvtaoEELoy8tZWLXd9OHKlSsolUoWL17MxYsX+f7771m0aBHjx49/63mzZs1izpw5zJ8/n5MnT+Li4kL9+vWJi4vTS5wi55KcLYT42OWEfP2xkB7xHG7kyJGMHDnS0GEIIYReKZVKlEotnznTslxmNWrUiEaNXk3WWLhwYa5evcpPP/3E7Nmz0z1HpVIxd+5cJkyYQOvWrQFYsWIFzs7OrFmzhv79++slVpEzSc4WQnzsckK+/lhIj7gQQgiDy8osrLGxsam219dV1pWYmBjs7DKeOPL27duEhobSoEEDzT5TU1Nq167NkSNHdB6PEEIIYUgya7ruSEVcCCGEwalUykxtAO7u7lhbW2u2GTNmvOMqmXPz5k1+/PFHBgwYkGGZ0FD1sjjOzs6p9js7O2uOCSGEEB+LrORrkT6piAshhDC4rLSw379/P9VayuPGjUv3vf39/VEoFG/d3pwY6+HDhzRq1Ih27drRp0+fd8b/5rrQKpUqw7WihRBCiA+V9IjrjjwjLoQQwvAyk7BflLOystJqOZTBgwfTsWPHt5Z5uYY0qCvhvr6+VK1alSVLlrz1PBcXF0DdM+7q6qrZHx4enqaXXAghhPjgZSFfi/RJRVwIIYTBKVVKlFoOYdO23EsODg44ODhoVfbBgwf4+vpSsWJFli1bhpHR2weOFSpUCBcXF/bs2YO3tzcASUlJ7N+/n5kzZ2YqTiGEECKn02e+/q+RoelCCCEMLicMdXv48CF16tTB3d2d2bNnExERQWhoaJpnvT09PdmwYQOgHpI+fPhwpk+fzoYNG7hw4QI9evTA3NycTp066SVOIYQQwlByQr7+WEiPuBBCCINTqZSotFzmRF+Tv+zevZsbN25w48YN8ufP/8Y1X91MXL16lZiYGM3r0aNH8/TpUwYNGkRUVBSVK1dm9+7dWFpa6iVOIYQQwlByQr7+WEhFXAghhMFlpuVcXy3sPXr0oEePHu++vir19RUKBf7+/vj7++slLiGEECKnyAn5+mMhFXEhhBAGl5llTqSFXQghhDAMyde6IxVxIYQQBqdUglLLlnMtR8QJIYQQQsckX+uOTNYmhBDC4FRKZaY2IYQQQmS/nJKv79y5Q+/evSlUqBB58uShSJEiTJ48maSkpLfHr1Lh7++Pm5sbefLkoU6dOly8eFFvcb6NVMSFEEIYnMzCKoQQQuR8OSVfX7lyBaVSyeLFi7l48SLff/89ixYtYvz48W89b9asWcyZM4f58+dz8uRJXFxcqF+/PnFxcXqLNSMyNF0IIYTByTNnQgghRM6XU/J1o0aNaNSokeZ14cKFuXr1Kj/99BOzZ8/OIB4Vc+fOZcKECbRu3RqAFStW4OzszJo1a+jfv7/e4k2P9IgLIYQwuJzSwi6EEEKIjGUlX8fGxqbaEhMT9RJbTEwMdnZ2GR6/ffs2oaGhNGjQQLPP1NSU2rVrc+TIEb3E9DbSIy4y9HKJnoT47B+qkVXPlR/er3Ty8wRDh5Apz57EGjqETEmIf2boEDItTvlh/M3Fx8cDaZfzyorkpDitnyVLSf6w/maE0LeXf4Mv/yY/BM9Uzw0dQqZ9aPn66QeWr5/Epxg6hEyLM/kw8jXoLmdnJV+7u7un2j958mSdL/l58+ZNfvzxR7777rsMy4SGhgLg7Oycar+zszN3797VaTza+PBqLSLbvHxWommdcgaOROQkJ3YZOgKR08TFxWFtbZ2lc3Pnzo2LiwtBe9tn6jwXFxdy586dpWsK8bF5ma99a1U2cCQiJ5F8LdKT1Zz9Pvn67NmzmJmZafaZmppmWN7f358pU6a89T1PnjyJj4+P5vXDhw9p1KgR7dq1o0+fPu+MSaFQpHqtUqnS7MsOCpUuujLER0mpVPLw4UMsLS11+ssZGxuLu7s79+/fx8rKSmfvqy8Sr/59aDFLvGoqlYq4uDjc3NwwMsr6k07Pnj175yynb8qdO3eqpC7Ef5m+8jXI952+Sbz6JfG+ooucnR35OjIyksjIyLeW8fDw0Lznw4cP8fX1pXLlyixfvvytn+3WrVsUKVKE06dP4+3trdnfokULbGxsWLFihdZx6oL0iIsMGRkZkT9/fr29v5WV1QfxpfiSxKt/H1rMEi9Z7gl/nZmZmVSqhXgP+s7XIN93+ibx6pfEq/a+OTs78rWDgwMODg5alX3w4AG+vr5UrFiRZcuWvbOBoVChQri4uLBnzx5NRTwpKYn9+/czc+bM9449s2SyNiGEEEIIIYQQH4yHDx9Sp04d3N3dmT17NhEREYSGhmqeA3/J09OTDRs2AOoh6cOHD2f69Ols2LCBCxcu0KNHD8zNzenUqVO2fwbpERdCCCGEEEII8cHYvXs3N27c4MaNG2lGBL3+5PXVq1eJiYnRvB49ejRPnz5l0KBBREVFUblyZXbv3o2lpWW2xf6SVMRFtjM1NWXy5MlvnaghJ5F49e9Di1niFUL8V3xo3x8Sr35JvPr1ocVrSD169KBHjx7vLPfmdGgKhQJ/f3+dz9qeFTJZmxBCCCHE/9m777Aori4M4O/QlY4g2BBiFLABir2gYo89xiT22JMYY6wh1lhiN0aNNfYSTexiV0DsilJs2BFFiigd6ff7g49VAijg7g7o+3uefZKduTN7Zl327Llz5w4REZEa8RpxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuKkciVlYv7w8HC5Q/ggJSYmKv7/4cOHMkZSNCXl80tE9L5K0vcdc7byMV8TqRdvX0ZKJYSAJEkICQlBUlIS7O3t5Q4pX5mZmdDQyOqLWrVqFS5cuIDVq1dDT09P5sgK583jKG4SEhJw4sQJ6OrqIiQkBNevX8f8+fOhr68vd2h5yv783rt3D6mpqXBwcICGhgYyMjKgqakpd3h5yo6ZiKgwSlK+Bj6MnM18rTzM1/Qh0JI7APqwSJKE/fv3Y9y4cdDV1UX16tWxbds2aGtryx1aLtnJ8OrVq7h58yaWLFlS7BN69pe4r68vQkJCUKdOHdjY2MgdVr60tbWRlJSE6dOnIyEhAd7e3tDX1y+2iVKSJBw+fBhDhw5F7dq1ER4ejitXrkBLS6tYx3zmzBlcuHABdevWhZubm9whEVEJUJLyNVDycjbztWoxX9OHoHh2y1GJ9ejRIxw5cgRbtmzBpUuX8ODBA/Tr1w+pqalyh5ZLZmYmbty4gVatWuHu3buKZcWZJEk4ceIEPvvsM+zcuRMODg44efKk3GHlS1dXF2ZmZkhPT4ezszMuXLiA9PT0YpkgAeDGjRs4deoUtm/fjsOHD8PGxgbVq1dXxJyRkSF3iArZg5m8vLzQu3dvPHnyBF9++SVWrFiB2NhYmaMjouKuJOVroOTlbOZr1WK+pg8BC3FSCiEEbt++DTs7O+jr66Nhw4bQ19fHuXPnEBwcjC+++AIpKSlyh5nj+iENDQ3UrFkTy5cvR1BQEM6fP19sh4xlu3btGvz8/LBnzx7s3LkT8+bNQ//+/YtVcn/zPd66dSuOHj0KDw8PtG3bFocOHcLGjRsBAD4+Pjh9+rRMUeYWGhqK5s2bIyoqCq6urpAkCXv37kWtWrVgbW1d7H6QSJIEPz8/nD17Ftu3b8eyZcuwefNmbNiwAdu2bUNMTIzcIRJRMVRS8jVQsnM287XqMF/TB0MQKdGQIUOEoaGhePr0qWJZUlKScHR0FNeuXZMxMiEyMzMV/79v3z6xevVq4eXlJYQQYv369aJKlSri4MGDMkX3dhkZGSIxMVEYGBgIe3t7ERERoTieZcuWCX19fXH06FGZo8xpx44dYurUqeLevXtCCCFevHghFi9eLPr16ye6d+8u6tWrJx49eiRvkP+xYMECoaenp/hcZPvss8+Et7e3PEG94datW2L//v1CCCHS0tJEgwYNRMWKFcWpU6dERkaGEEKIw4cPCzs7O/HHH3+I9PR0OcMlomKsOOdrIUpuzma+Vg/ma/oQsBCnIstOLBERESI8PFyxfODAgcLKyipHcn8zocpt2bJlolmzZmLmzJnC1tZWbNmyRQghxJo1a4SJiYk4cuSIzBHm7+bNm8LCwkL88ssvOZb//vvv4uTJkzJFldurV69E48aNhZmZmYiIiFAsj4mJEceOHRPTpk0Tt27dkjHC15/JBw8eiKCgIPHixQshRNbnw8LCQpw6dSrfbeRy9uxZcfLkSUWsz58/F66urmLw4MEiNjZW0c7Dw0OcPXtWrjCJqJgpqflaiJKbs5mvlYf5mj5ULMSpSLK/4Dw8PES9evXEl19+KXr16qVYP2TIEFG6dOkcyb048PLyEu3btxcZGRli2bJlon379iIlJUWkpKQIIYTYsGGDokdYbtnv8dWrV8XBgwcVifvevXvCwMBATJkyJd9t1C2v142KihINGzYUHTp0kCGigjly5IioXr266Nq1q6hcubI4cOCAEEKI5cuXCz09vWL1Yyn7PY6LixOSJImlS5cKIYSIjIwULi4uYtiwYeLly5dyhkhExVBJzddClJyczXyteszX9CFiIU5Fdvz4ceHs7CyCgoLEggULhCRJokWLFor1/fv3FydOnJAxwtz8/f3FunXrxIwZM4Sbm5sima9du1bcvHlT5uhyO3LkiKhatar4/vvvRcWKFcWUKVNEYmKiCAoKEpIk5eppl8ObSf3vv/8Wf/75p1iwYIEQIivpuLq6im7duskVXi7Z8d68eVM4ODiIM2fOCCGyknnDhg2Fr6+vEEKIJUuWFLvPb7Y9e/YIXV1dsWrVKiFEVk+7g4OD+Oabb0RaWprM0RFRcVMS87UQJStnM18rH/M1fehYiFORJCYmil9++UVxDUyTJk3E48ePhY2NjXBzc8vRVq5e37CwMOHp6SmEEGLlypXi9OnT4tKlS8LU1FQ0btxY0W7z5s2iRo0aIjg4WJY48/P06VNRt25dxfVPFy9eFF988YX4/fffhRBCXL9+vVgMycu+1mn58uXC2dlZrFixQtjZ2YmhQ4eKyMhI8eLFC1G9enXx9ddfyxrn7du3xY0bNxTP/fz8RP/+/YUQrz+jP/zwg+jRo0eOz6zcw9uyX//GjRvCy8tLXL9+XQghxKlTp4SGhoZYs2aNECLrRxSHtxHRf5WEfC1Eyc7ZzNfKxXxNHwsW4lRg2V8wjx49EsnJySI6Olo8f/5ctGvXTnH90Pjx44WJiYm4dOmSnKEKIYQICQkR9erVE+3atRP169cXISEhQggh1q1bJ/T09MSCBQvEhAkThJOTU44vfLncu3dPbNu2TfH8xYsXonv37iIuLk6x7J9//hHOzs4iJiZGsUyuxHPlyhURFRUlhMj6EdKoUSNx//59IUTWcKzWrVuL7777TgiRNexN7h9N69evF56eniIpKUkIIURgYKAwNTUVHh4eijb//vuvGDNmjFwh5pL9b3vkyBFRrVo10bt3b2FtbS0WLlwohBDi2LFjQpIksXLlSjnDJKJipqTlayFKVs5mvlYt5mv6WBTf+z5QsSKEgCRJ8PDwwPDhw3H37l2YmJggKSkJ9+/fh56eHm7cuIGoqCj4+/ujfv36ssV68eJFeHt7o1KlSmjTpg08PT3RuHFjVKpUCRkZGRg0aBA2btyI2NhYGBoaYufOnahRo4Zs8WaLi4uDjY0NoqKikJmZiVKlSuHOnTuYMWOGoo21tTVsbW2ho6OjWCZJktpjPXLkCL766iucOHECmZmZyMjIQFpaGkxNTQEAhoaGWLhwIe7fv4/k5GSUKVMGlStXVnucb/rmm2/g7OwMQ0NDnDlzBrVq1cK8efMwbtw4LF26FPv378fMmTPh5uYma5xvkiQJDx8+hLu7O9atW4dt27bhzz//xLlz57Bz5060bdsW+/fvh7W1tdyhElExUZLyNVAyczbztWoxX9NHQ+6eACo5vLy8hKOjozh//nyO5d99952oUqWKsLOzE7t27ZIputfmzJkj7O3txeXLl8X9+/fF/v37hY2NjZg8ebKiTXGdJCMtLU1UrlxZzJgxQwiR1etuYWEhvv76a7FgwQLh5OQk9u3bJ2uMhw4dEs7OzoprtbINGjRIdO/eXfF8w4YNokOHDiI5OVndISokJSUpbrly5coVkZaWJmbMmCEMDQ3F5cuXhRBC7Nq1S7Rr104MGjRI0dsu5/C2Bw8eiD179iieP336VHzxxRciIyNDMaxw0aJFokmTJuLVq1eKdnIPySOi4qOk5GshSm7OZr5WLuZr+hixEKd8ZQ8JyrZw4ULFzI/Jyck5Jpl48OCBePDggRBCvi+Y27dvi1evXomXL1+KBQsWCBcXF+Hj4yOEyLqNhLW1tfj111/FP//8I5ycnERcXJzii1IuiYmJ4sKFC0IIIU6fPi2uX78uTp8+LapVq6aYQOXZs2fC3d1dzJkzRzErqFzv8atXr8SXX36piOPly5fi4sWLYtq0acLDw0M0b95cODs7iwkTJghHR0fF9VFyyMzMFFeuXBE//fSTmD59uqhfv74intmzZws9PT3FkMw3f3zInSD//fdfYWRkJHbu3CmEECI8PFxUqFBBrFixQtHGx8dH9O/fX6SmpsoVJhEVIyUtXwtR8nI287XqMF/Tx4qFOOXp1q1bom3btiIoKEix7Pvvvxd9+vTJ0c7Hx0esX79e9oJ2//79olGjRiI6Olqkp6cLIbJ62V1cXIS3t7cQIuu2InXq1BGtW7cW/v7+coar8PjxY/HNN9+Ir776Sjg7O4uLFy8KIbKSvK2trVi0aJHMEeb06tUr0axZM7F9+3YRFxcnhgwZInr06CFq1aol2rZtK/744w+xatUqsWvXLnH37l25wxXR0dHiq6++EgYGBoofStmJ+7fffhOSJOU6Y1QcbNu2TdjY2IitW7cKIbLObpUqVUqMHTtWLF++XDg5OYn9+/fLHCURFQclLV8LUTJzNvO1ajFf08eIhTjlcvv2beHi4iJ+//13ER0drVh+7949Ubt2bTFr1iyRnp4ufHx8RLVq1cSpU6fkC1ZkTYDh5OQkfHx8xK1bt0SfPn1EdHS0yMzMVCT27JlMExMTcxxTcTBz5kwhSZIYPHhwjuXe3t7CwsJCzJs3T/Ze3zdt2bJF2NjYCEtLS/HNN98oZoLdvn276Ny5s+JHVXGxYMECMXjwYNGrV68cE70IIcSyZcvE4cOHZYrstbz+fTdu3Jgjufv6+orvvvtOTJgwQRw7dizf7Yjo41HS8rUQJTtnM1+rFvM1fWxYiFMOUVFRok6dOmL9+vU5lt+8eVOkpKSI8+fPi1q1aomuXbuKevXq5fqiVLcjR46IOnXqKJL2vn37xPDhw8W3334rYmJiRGZmppg/f76oUqWKOH36tKyxvin7CzkmJkb4+/uLxYsXizZt2ohp06blaOfv7684tuLkzp07ivcz++zKpk2bRPfu3UViYqKcoSne25CQEJGWliZSUlLEq1evxKxZs0TXrl3FuXPnxM2bN8W3336riF3uBJk9bPTu3bsiICBAMYRt/fr1wsbGRmzZskXO8IioGCpp+VqIkpmzma9Vh/maPnYsxCmHe/fuiW7duimeL126VHz99ddCV1dXDB06VNy6dUu8evVKhIeHi6dPnwoh5PtSjImJEfr6+mLx4sVCiKxrs5o2bSo2bdokBg8eLIYPH65I7EuWLBEPHz6UJc7/yn6/Dh06JGrVqiUiIyOFEFlnCZo3by5mz54tAgICRNOmTRVnAuROPO+ybds24eLiIus1ZkK8fp8OHjwoGjduLEaMGCEmTJggwsPDRWxsrPjtt99Ew4YNhY2NjTh06JCssQohxP379xX3zT148KAoV66c6NChg6hRo4ZiYpf169cLW1tbxcRKxWFYKRHJryTlayFKZs5mvlYd5msiFuL0HwkJCcLGxkYMHDhQNGvWTHTv3l3MmzdPeHl5iaZNm4q5c+fKHWIOp06dEvXr1xe7du0STZs2VUxO4+3tLYYPHy769OkjYmNjZY4yNy8vL1GjRg1x4sQJxbLU1FTh6ekpmjRpIqpXr14irimKiIgQv/32m6hRo4bsST3b4cOHRb169cSjR4/EsGHDRPXq1UXv3r3Fs2fPhBBCXL9+Xfj6+socZZb169cLSZLE5s2bxZgxYxTXRvbs2VPY2Ngokvtff/0lSpUqJW7fvi1nuERUjJS0fC1EyczZzNeqw3xNHzsW4iSEyNmDe/XqVTF48GAxZswYERoaKhISEoQQWbdgmDJlilwh5svb21sYGxuL0aNHK5alp6eLEydOiFGjRomwsDAZo8sp+32ePHmyWL16tRAia5jTm72mr169Evfv38/RvrhKT08XFy9eVNxyRO5YUlNTxeDBg8WlS5eEh4eHqFu3rti7d69wdXUVPXv2FHfu3JE7zFzWrFkjzM3NxYABA3Is79WrlzA3N1fMhtynT59iMbSUiORVkvO1ECUnZzNfqzYW5msiFuL0hqNHj+bbg37u3DlRvXr1YjHRS17OnDkjateuLc6fP58jGf73li7FxZQpU8SIESNyxHf06FGxd+9e+YIq4bKHDCYmJoqQkBDRqlUrRa963759xddffy38/PxkjPC17M9odg/6qlWrhKamZo4zLkII0a1bN+Hl5SUCAwNFo0aNFLccIqKPW0nO10KUrJzNfK18zNdEWViIf+Syv2ACAwPF999/LyRJEnPmzFGsDwsLE5s3bxYODg7F4hqdt/H09BSOjo6K4ULFRfZ7/OTJExERESEyMjLEyZMnxRdffCGOHTsmYmNjhb+/v6hVq1axmBG0JMl+b2/duiVMTEwUPdARERGiRYsW4siRI+LGjRvCzc1N3Lx5U85QFbJjPnDggOjWrZviDNby5cuFubm5OHr0aK5tXrx4ofjhQkQfpw8pXwtRPHM287XqMF8T5cZCnMThw4eFnZ2d8PDwEIsWLRKlSpVSzAYaHBwsfvjhhxKR1IXImkClUaNGxa5X/fDhw8LFxUWMGDFC1K1bV6SmpoqpU6eKnj17imbNmon69euXiGvMiqNDhw6Jn376Sbi4uAgrKyvFWYqJEyeKtm3biipVqogDBw7IG+R/HDhwQDg5OSn+rrJnYV23bp3Q1dVV3GKGiOhNH1K+FqJ45mzma9VhvibKiYX4Ry4zM1NMnDhRbN++XbHs2rVrQpIksWjRIiHE6+E4xf36p2xy344jW/Z1ZGfPnhWOjo4iKChIrFy5UtjY2Ijk5GQhhBDPnz8XQUFBIjg4WAhRct7j4iIwMFBYW1uLK1euiEePHimu38q+VcujR49EYGCgEKL4vLcvXrwQbdq0Ebdu3RLJycliz549ok2bNmLr1q0iPT1dLFu2LNeQNyKiDzFfC1E8cjbzteoxXxPlJgkhBOijNmzYMISHh+PAgQOKZX379sX27dsxY8YMTJ48WcboSp7w8HAYGhpCX18fALBp0yaYmZnBwMAA7u7u+Pvvv2Fra4uTJ0+iVatW0NDQkDnikuvw4cNYtWpVjs9u//79ceTIEWzbtg1t27aVMbr89erVC0+ePEHVqlVha2uL2NhYBAUFYfv27TAzMwMACCEgSZLMkRJRccJ8rVzM1+rDfE2UG79RPjLZ/S7BwcG4desWAGD8+PHQ19fHr7/+CgC4du0aKlasiOPHj2Pq1KmYO3eubPGWNElJSVi7di2ePn2KzMxMAICBgQHGjRuHsWPH4sCBA7C1tYWPjw/mzZuHx48fyxxxyebg4ICwsDDs2rVLsczNzQ2dO3fGrFmzEBYWJmN0WbL/5l68eKGI5/fff0fz5s0xcuRI/PrrrxgzZgxiY2MRFxen2I5JnejjxnytWszX6sV8TZQbz4h/hDw8PDBz5kyUK1cOGRkZGDNmDJKSkjB37lwIIRAeHo758+ejR48euHXrFjQ0NGBvby932CWCEAKxsbFITEzEjBkzMH/+fCQkJGDYsGFwdnbGsGHDEBISgpEjR2LGjBno0qWL3CGXGNk9zj4+Pnj48CF0dXXRrl07rFu3Dnfu3IGNjQ2aNWuGMWPG4LfffsPWrVuxZMkSlClTRvaYDx48iLlz50JHRweOjo5YvHix4syKh4cHpkyZgmnTpqFbt26yxUpExQ/zteowX6sO8zVRAal7LDyp35v3vPTx8RH169cX4eHhYvXq1cLZ2VlxfVZGRoa4ceOGePjwoRBCiNTUVFniLanevKbp1KlT4osvvhA//vijSE5OFseOHRPfffedcHR0FB06dFBM9FJcroMqKQ4fPixq1qwp/vnnHyFJkli9erV4+PCh2LVrl2jTpo3o0aOH8PX1FT4+PqJevXoiIiJCljhjY2NFTEyMECLrNjdOTk4iODhY/Pbbb0KSJDFgwAARGxsrnj59KoYMGSL27dsnhODngehjx3ytHszXqsd8TfRuLMQ/cLdu3RJDhgwRcXFxQgghDh48KLy9vcXevXtFvXr1FEn89OnTIj09Xc5QS7TsL+TY2FjFMl9fX9GnTx8xatQoxY+nyMhIxRc+v8QLJywsTDRr1kw8evRInDp1Sjg7O4vQ0FDF+szMTJGSkiKOHDkiHB0dRUBAgCxxxsXFic6dO4sVK1aIx48fi7lz54qgoCCxZ88e0bx5cxEYGCjKly8v+vfvL168eCHi4+MV8RPRx4v5Wj2Yr1WP+ZqoYHiN+Afszp076Nu3L2xtbZGamgoAePr0KXr16oWFCxfi2LFjsLW1xalTp/Djjz8iODhY3oBLKPH/4UzHjx/H559/jq+++gqDBw9G3bp18dNPP+Hly5cYN24cYmNjYWFhASMjIwC8pqggxP+vnImIiEBqairq1KmD8+fPY/LkydixYwfKly+PdevW4ejRo5AkCdra2nj48CF27tyJ2rVryxKzoaEhOnbsiH379sHHxwfdunWDpaUlVqxYgcWLF6NWrVr48ssvcerUKTx//hwGBgYA+Hkg+pgxX6sH87XqMF8TFYG8/QCkKqGhoaJWrVpi/fr1OZanpKSIYcOGidatW4uoqCixf/9+4ejoKA4ePChTpB+G06dPi6pVq4q9e/eKc+fOicaNG4vWrVsLIbJuhzJ48GDxww8/iJSUFJkjLXlOnjwpunbtKqKiokSzZs2EhYWFiIqKEkIIcenSJWFvby9OnTolc5SvZQ8tXb9+vahSpYpYv3698Pf3F66uruLJkyfi/PnzYtCgQeLGjRsyR0pExQHztXoxX6sO8zVR4WjJ3RFAqvH48WPUq1cP33zzDTIzM7Fx40Z4enri4sWLGDZsGIKCgtC7d29oa2vjt99+Q8eOHXn7hUJ68/3y9/fHiBEjFJN3nDt3DvXq1cOuXbvQo0cPpKenY/Xq1YiPj5d1MpKSJiAgAJs3b8bEiRNRpkwZuLu7Y/ny5Rg5ciSaNWuG1atXY8GCBWjVqpWscQYHByMyMhL169eHhoYGhBA4duwYKlWqhG3btsHY2BgaGhr48ssvER4ejsWLF6NGjRqyxkxExQPzteoxX6se8zVR4bEQ/0AZGhri77//houLCw4ePAgDAwNUrVoVDRs2xNatWzF37ly0aNECCQkJHGpTBJmZmdDQ0MDhw4chSRIkScKOHTvQr18/WFhYAAAaNGgASZIUX/Q3b95Eenq6zJGXHHFxcVi9ejUOHTqESZMmAQCaNm2KatWq4Y8//kBmZiZ+//13tGrVSvYfpU+fPsUXX3yB48ePo1atWujWrRuqVq2KHTt2YOvWrdiwYQOGDx+OihUromLFiqhcubLsMRNR8cB8rVrM16rHfE1URPKciCd12LJli2jVqpUYOHCguHPnjmICkuHDh4vVq1cLIXLO0Erv9uYEHdevXxdNmzYVPj4+4tmzZ+K7774TkyZNEiEhIeLmzZvC0dFRnDt3TgghxMOHD8WTJ0/kCrvEefDggRBCiICAANGlSxfRr18/ER4eLnNUb+fl5SWqV68umjRpIsaNG5dj3erVq0WzZs3E06dPZYqOiIoz5mvlY75WD+ZroqLjfcQ/cMnJydDT01M8P3fuHIYMGYL169ejUaNGMkZW8ty9exc7d+5EZmYm6tSpg61bt8La2hoLFiwAAOzfvx9HjhzBhQsXYGRkhLFjx6Jbt27sSS2g7Pfp7t27+Omnn9C0aVO4u7sjICAAf/75JwBgxowZsLKykjnS/F24cAHdu3fH8ePHUbt2baSnp0NLK2vgUWhoKCpUqCBzhERUXDFfKw/ztWoxXxMpBwvxj0RUVBTOnDmDqVOnYu7cufjss8/kDqlEuXPnDnr16oVevXrh6NGjuH//PhwcHBAXF4fff/8dzZo1U7R9+vQpdHR0ULZsWcUsokzsBXPw4EH8+eefSE5OxqtXr9C5c2dMnjwZAQEBWLhwITQ1NbFmzRro6OjIHWq+vLy8MGrUKKxcuRJNmzZVLOcPPCIqCObr98N8rR7M10Tvj4X4RyAzMxOBgYGYMWMGvvnmG3Tu3FnukEqUu3fv4vPPP8fPP/+MPn36ID09HU2bNoWdnR0sLS2RlJSEvn37omHDhnKHWqLdunULvXr1wt69e1GpUiUcOHAA//77Lxo0aIBx48bh6tWr0NbWlu02J4Vx+vRpDBgwAFu3bs2R3ImI3ob5+v0wX6sH8zWRcnCyto+AhoYGnJycsG7dOpiamrKnr5Di4uIQHByMunXrAgC0tLTQqlUr1K1bF5UrV8aOHTuwZs0aAGByfw8JCQkwNzdHhQoVoKenhw4dOuD06dPYsmULdHR0MGrUKLlDLDBXV1esX78emZmZcodCRCUI8/X7Yb5WD+ZrIuXQkDsAUh9TU1MAHHZVWNkz2fbs2RO3bt3C2rVrceLECTRu3BguLi744osvYGhoCENDQ7lDLZGCgoKQkpICGxsbmJiY4PTp04iPj4ehoSFat26N5s2b4+LFiwgNDZU71EJp1aoVmjdvDg46IqLCYr4uGuZr1WK+JlIuDk0nKiAvLy/0798fxsbGOHbsGCpUqKC4LUpcXByMjIzkDrHEyD7Lc+fOHUycOBF2dnaYN28elixZAh8fH9jb26NKlSpYunQpli5dit9++w0LFy5ErVq15A6diIiKOeZr5WG+JlIdnhEnKqCWLVvin3/+QVpaGuLj4wFkDSMEwKReSJIk4eDBg/juu++QkpICT09PTJ48GaNHj0a/fv2Qnp6OkydPYv369dDX18fz589hbm4ud9hERFQCMF8rD/M1kerwjDhRIXl7e2PAgAHYtm0bJ/YopDdvedKzZ0/s2rUL1apVw8GDB7F9+3bY29tj8uTJ0NTURFpaGg4cOIDp06dj27ZtJWLSFyIiKj6Yr4uO+ZpI9XhGnKiQWrRogQ0bNnBij0JISUkB8Pp6x/T0dJiYmMDExAQA0KZNG1hbW2PXrl2YOXMm0tPToa2tDXNzc+zYsYNJnYiICo35uvCYr4nUh4U4URFwYo+Cu3v3Lrp374758+cjKSkJycnJsLa2RtmyZXH27Fm8fPkSenp6aNGiBVxdXXHr1i08efIEQNZspjVq1JD5CIiIqKRivi445msi9eLty4jeA2e0fbfbt2/D09MT165dQ2BgIAwMDDB16lQ0adIE//77L86cOYPKlStj7dq1WL9+PWbOnInIyEjY2trKHToREX0gmK/fjfmaSL14RpyIVKp58+YYPnw4Nm3ahMGDB8Pc3ByNGzdGTEwMtLS0UKlSJQQEBGDr1q3Q0NDAkydPULFiRbnDJiIi+qgwXxOpFwtxIlKp7PvhLlq0CC1btsSsWbPw4sULxMbG4sSJE7h//z4WLVqE4OBgDBw4EFu3bkWFChVkjpqIiOjjwnxNpF6cNZ2IVCZ71tWkpCQMGDAALVq0wKpVq9C3b19MnDgRISEhiIiIQL169eDv7w9dXV04ODjIHTYREdFHhfmaSP1YiBORyqWkpGDcuHFYu3YtVq9ejQEDBiAzM1NxX9eMjAxoamrKHCUREdHHjfmaSH04NJ2IVE5XVxc//PADypcvn+etTZjUiYiI5Md8TaQ+LMSJSC2qVauGtm3b4vDhw0hNTVX0rhMREVHxwXxNpB4cmk5EauPn54ekpCQ0adJE7lCIiIgoH8zXRKrHQpyIiIiIiIhIjTjWhIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIE9F7CQ0NRd++fVGmTBmULl0aTk5OuHr1qmL9wIEDIUlSjkfDhg3fus+NGzfm2kaSJCQnJyvarFy5ErVr14aRkRGMjIzQqFEjHDlyJNe+bt++jS5dusDY2BiGhoZo2LAhQkJClPcGEBERFTM+Pj7o3LkzypcvD0mSsG/fvlxtpk+fDnt7e+jr68PU1BStW7fGpUuXcrRZs2YNWrRoASMjI0iShJiYmAK9/ooVK2Braws9PT3UrVsXZ86cybE+rxwvSRIWLFgAAHj58iV++OEH2NnZoXTp0rC2tsaoUaMQGxtbpPeDqDhiIU4kk9TUVLlDeG/R0dFo0qQJtLW1ceTIEdy6dQuLFi2CiYlJjnbt27dHWFiY4nH48OF37tvIyCjHNmFhYdDT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sD9BeQ3PiBORSjVq1AiDBg3C77//jn79+sHCwgLOzs6IioqCnp4eSpYsiYCAAOzYsQM6Ojp4/vw5SpQoIXXYREREpEHYn6C8hoU4EalUxrVvly5dCjc3N8yZMwdv3rxBdHQ0Tp8+jUePHmHp0qUIDg5Gnz59sGPHDhQvXlziqImIiEiTsD9BeQ1XTScilclY1TQhIQG9e/eGq6sr1q9fjx49emDChAl49uwZwsLCULt2bfj7+0NfXx+VK1eWOmwiIiLSIOxPUF7EQpyIVC4pKQljx47Fpk2bsGHDBvTu3RtyuVxx/dW0tDTo6upKHCURERFpMvYnKC/h0HQiUjl9fX0MHz4cxYoVy/bSIUyaRERE9CXsT1BewkKciNSiYsWKaNq0KY4dO4bk5GTF0WsiIiIiZbE/QXkFh6YTkdrcuHEDCQkJqF+/vtShEBERkZZif4LyAhbiRERERERERGrEsRxEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRERERERERGrEQpyIiIiIiIhIjViIExEREREREakRC3EiIiIiIiIiNWIhTkRERERERKRGLMSJiIiIiIiI1IiFOBEREREREZEasRAnIiIiIiIiUiMW4kRERERERERqxEKciIiIiIiISI1YiBMRERERERGpEQtxIiIiIiIiIjViIU5ERERERESkRizEiYiIiIiIiNSIhTgRERERERGRGrEQJyIiIiIiIlIjFuJEREREREREasRCnIiIiIiIiEiNWIgTERERERERqRELcSIiIiIiIiI1YiFOREREREREpEYsxImIiIiIiIjUiIU4ERERERERkRqxECciIiIiIiJSIxbiRKSU+fPno3bt2jAyMoKlpSXatWuH+/fvZ2l39+5dtGnTBiYmJjAyMkK9evXw7NkzAMDbt28xfPhwVKpUCQULFkSpUqUwYsQIREdHf/a1z549i9atW6NYsWKQyWQ4cODAZ9sPGjQIMpkMK1asyHa/EAItWrRQ6rmIiIiIiHIbC3EiNUhOTpY6hG/m6+uLH3/8EZcvX8bp06eRmpqKpk2bIj4+XtHm8ePHaNCgAWxtbeHj44OAgABMnToVBgYGAICXL1/i5cuXWLJkCW7evImtW7fixIkT6Nev32dfOz4+HnZ2dlizZs0X4zxw4ACuXLmCYsWKfbLNihUrIJPJlHznRERERES5SyaEEFIHQZTXuLq6olq1asifPz+2bduGqlWrwtfXF76+vhg3bhwCAgJgbm6O3r17Y86cOdDT08Phw4fRs2dPvH37Fjo6OvD394eDgwPGjh2L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OSXiIiJiODMmzDYm2La2ExEREcmqlIiLiIjhNDRdREREchIl4iIiYjg9vkxERERyEiXiIiJiOAu2V7otGRuKiIiISIZTIi4iIoZTRVxERERyEiXiIiJiOM0RFxERkZxEibiIiBhOFXERERHJSZSIi4iI4SyAOQ1tRURERLIzJeIiImI4VcRFREQkJ1EiLiIihtMccREREclJ7IwOQERERERERCQnUUVcREQMp6HpIiIikpMoERcREcNpaLqIiIjkJErE5aF8A8ri4pjL6DDSTXSVcKNDSHenI4sZHULG8DI6gPTnnsfV6BDSTWzUnXS7ltmSuNnaVkRERCQ7UyIuIiKGU0VcREREchIl4iIiYjjNERcREZGcRKumi4iI4SyWtG0Z5datW3Tr1g03Nzfc3Nzo1q0bt2/ffuA5r776KiaTKdlWp06djAtSREREsj1VxEVExHBmTJhtHHJua7tH0bVrV/788082bNgAQO/evenWrRurV69+4HktWrRg3rx51te5c+fOsBhFREQk+1MiLiIihssKQ9OPHz/Ohg0b+Pnnn6lduzYAc+fOpW7dupw8eZJy5crd91xHR0e8vB7D1QVFREQkQ2houoiIGO5RhqZHREQk22JjY/9TDHv37sXNzc2ahAPUqVMHNzc39uzZ88Bzt23bRuHChSlbtiy9evUiLCzsP8UiIiIijzcl4iIiYrikVdNt3QCKFStmncvt5uZGYGDgf4ohJCSEwoULp9hfuHBhQkJC7ntey5Yt+eabbwgKCuKjjz5i3759NGnS5D9/MSAiIiKPLw1NFxERwz3Kc8QvXbqEq+vfz2V3dHRMtf3YsWN5//33H3jNffv2AWAypRz2brFYUt2f5Pnnn7f+u1KlStSoUYMSJUqwdu1aOnTo8MD7ioiISM6kRFxERIyXhjni/NXO1dU1WSJ+P/379+eFF154YBtfX1+OHDlCaGhoimPXrl3D09PTttgAb29vSpQowenTp20+R0RERHIWJeIiImK4tDyWLK2PL/Pw8MDDw+Oh7erWrUt4eDi//vortWrVAuCXX34hPDycevXq2Xy/GzducOnSJby9vdMWqIiIiOQYmiMuIiKGS3p8ma1bRqhQoQItWrSgV69e/Pzzz/z888/06tWLVq1aJVsxvXz58ixfvhyAqKgohgwZwt69ezl//jzbtm2jdevWeHh40L59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YUOuX79Oo0aNMJlMLF++nMqVK1O8ePFsX+0XERERyUlMln9mGCLprFevXixZsoTjx49TpEgRAO7evUvdunWZN28e1apVMzjCh/vnYlgrV64kNDSUsmXLEhAQwLx585gwYQIzZsygVatWBkeadmazmZiYGDw9PSlatCjbt2+nUKFCmEwmPvnkE9555x2WLl1K8+bNjQ7VZjExMTRt2pQTJ05w/PhxChcuDEB4eDi//PILe/bs4fnnn6dChQoGR5p2U6dOZdSoUaxfv56AgADr/latWjF06FAaNWpkXHAiIiIiYjMl4pJukhLWsLAwLBYLnp6eAHTv3p0NGzawf/9+azKeHVd6/uSTT/j+++9p1qwZX331FePGjeOll15i7ty5DBs2jG+//ZYWLVoYHeYjOXbsGAEBAfTq1YsJEyZY98+YMYPKlSvTtGlTA6N7sNT+W7px4watWrWiQIECrFu3zqDI/pukfp09e5Z79+5RqFAh3N3d+eSTTxg3bhzfffcdTZo0SfUcEREREcnatGq6pIukBGDt2rW8//77lCxZEovFwpIlS5g3bx69evWibNmynDp1iiJFimS7ZGHbtm2sXbuWbdu2MWvWLMqVK0eXLl2Ii4ujV69e5MqVi9KlSxsdpk2S3quDBw9y5coV8uTJQ9OmTdmzZw/VqlXD3t6ecePGATBo0KBk52Q1/4zru+++4+bNm0RHRzNkyBBWrVpF586dad++PcuXLzc40rQzmUxs2LCBt99+mzJlyhAcHMzHH39M//79MZlMPPfcc6xZsybZlyRZ8T0SERERkZSy98RWyTKSFvsaNWoUCxcupEaNGvzwww80btwYgLlz59KpUyeOHz9ucKSPpkCBAnTu3JkJEyawYsUKVq5cSe7cuVmwYAHHjh3j1VdfzTaJeFKC98ILL7BhwwZeffVVRo8ejY+PD/v37+eDDz5g5MiRKc7JipIG9Hz66adMnjwZk8nEF198Qe/evQFYtmwZp06domvXrkaGmSZJfTp27BiDBw/m888/Z8WKFQwdOpSJEydy4MAB3njjDT788EM0oElEREQke9LQdEkX0dHRTJgwgZdeeonTp08zefJkFi9eTKNGjShVqlSyRb+yanU1SUhICMePH6dx48bMnj2bihUr4uTkRIsWLahQoQK7d+8GYOHChUyaNIm1a9dmq4W/Ll++TNu2bZk6dSoBAQH88ssvfPTRR9SrV49Bgwbx+++/8+eff2bpYfb79+/Hz8+PggULcvnyZTp37szChQspVaoUkZGRdOjQgbJly/Lpp59y48YNoqKisvx7dOLECRISEnjiiScACA4OZvr06Xz99dfW35mBAwdy+fJlfvzxR+vvUFb/fRIRERGRlFQRl0eW9B3O+fPnsbe3Z+jQoRQqVIhZs2Yxd+5cihcvTufOnTlw4AC//vqr9bysnjTcu3eP4cOH06JFC+bNm4efnx+1atVi6tSpHDx4kKlTpzJ8+HCmTZvGkiVLsnyCd+bMGRYvXmx9nSdPHooXL0716tUBqF27Np07d2bBggWEh4dTqVIlWrRokWWrrevXr+eFF15g8+bNmM1mEhISuHfvHgUKFADAxcWFqVOncubMGWJiYihYsGCWf48A9u7dS1hYGHfv3gXA3t6e1atXs3btWuvvTMOGDfH19U32O5TVf59EREREJCUl4vJIkqpwa9asoU+fPpw6dYr8+fMTHR3NmTNncHJy4vfff+f69esEBwdTq1Yto0N+qJ9//plt27ZRrFgxnnnmGYKCgqhXrx7FihUjISGB1157jfnz5xMeHo6LiwtLliyxVi+zsoiICHx9fbl+/Tpms5k8efJw8uRJ6zxwgOLFi+Pn50fu3Lmt+7Jigrdu3TpGjhzJ/PnzeeGFF7Czs6N48eJUqVKFnj17WtsdOnQIe3v7LNmH++nevTvVqlXDxcWFnTt3UrlyZSZNmsSQIUOYOXMmK1euZPz48Vl64TwRERERsY2Gpssj27ZtG4MGDeKzzz6jbt261v1vvPEGGzduxMHBgQkTJtCxY0cDo7Tdhx9+yNdff82CBQtwd3fn6NGjvPnmm7z00kuMHz8egFu3blkrr9lJfHw8pUuXpkePHowaNYozZ85Qr149nn76aZ588km++eYbxo4dS9u2bY0O9b5iYmJ49dVX6dWrF02bNuXWrVucOnWK9evXU7NmTSZPnkxkZCTPPPMMGzduZNGiRVSqVMnosB/o7t27hIaG4uvry/79+/H39ycwMJApU6awZcsWatasydKlS5k7dy5FihShQ4cOPPfccxqOLiIiIpLNadV0sdndu3fJkyeP9fWBAwfo0aMHdevWJTY2Fnt7exwcHPj00085e/YsgHX19KycNJw4cQJfX1/69OmDg4MDr7/+OtOmTaNNmzYULFiQrl27kitXLipUqMDEiRPZsWMH+fLlw84u6w4oiY6O5siRI9SpU4cdO3bg7u7OggUL6NWrF3ny5GHIkCEcPnyYjz/+mPj4eKZOnUrTpk2z/Ht15coVwsLCiIyMZNiwYdy8eZPTp0+zd+9eOnbsiKOjIx4eHvTs2ZMyZcoYHe4DWSwWjh49yuLFi3Fzc2PdunV8+eWXjBo1Cnt7exo2bMj27dvp2LEjrVq1wtHR0XpeVn6PRERERMQGFhEbHDt2zNKsWTPLiRMnrPveeOMNy4svvpis3Y4dOyxfffWVJSEhIbNDfCQrV6601K1b13Lr1i1LfHy8xWKxWAIDAy01atSwbNu2zWKxWCwHDhywPPnkk5ann37aEhwcbGS4Nrtw4YKle/fulhdeeMFSrVo1y88//2yxWCyW7du3W/z8/CwfffSRwRE+moULF1p8fX0tnp6elu7du1vWr19vsVgslsWLF1tat25tfQ+zi1u3blleeOEFi7Ozs2XKlCkWi8ViMZvNFovFYpk4caLFZDJZ9uzZY2SIIiIiIpIBVBGXhzpx4gQvv/wyL774Ip6entb9gwYNomPHjkyYMIF33nmHPXv20LNnTz777LMsXS1OsmnTJsaMGcPMmTO5evUq/fv355NPPmH48OEADBkyhClTphAQEMDOnTuJi4sjf/78xgZto+LFi1OyZElGjx7Na6+9Ru3atYHExb7mzZtH586diY+PZ+jQodmquvrSSy9Rq1YtQkJCaNiwIWazGUhcYM/BwYHY2Fjy5s1rcJS2y58/P9WrVydfvnzs27ePtWvX8txzzwEwYsQIXFxcuH37trFBioiIiEi60xxxeaAbN27QrFkz+vfvT/fu3a37jx07RunSpTlw4AB9+vShZMmSXLlyhTFjxlgTiaxsw4YNjBw5ko8++oiAgABWrlzJ+vXrsbOzIzAwEFdXV6ZOncrnn3/OV199RcOGDY0O2SaWv4Yth4eHc/78eYKCgli/fj316tVj7Nix1naHDx/m1q1bBAQEGBZrelm8eDHTp09n3rx5WX5OOPz9Hl26dAlvb2/MZjNms5mPPvqIffv2MWzYMPLnz88nn3zCJ598gp2dnYaji4iIiDxmlIjLA505c4ahQ4eyfPlyAD7++GP27t3LsmXLePnll3nrrbfw8/MjPDyc+Ph4ihQpkuWThvDwcIoUKcL48eN56623uHr1Kl26dKFXr17s2LEDBwcHJk2ahKurKzNnzqRNmzb4+fkZHfZDJf3c161bxzvvvMOWLVsoVKgQmzZtYsKECTRv3pxWrVrxxhtvsHr1avLnz5/l36sHCQsL48svv+Sbb77hu+++y1ZJ+Jo1awgMDKRKlSq4uroyePBg8uTJw6effsqqVasICQnh008/5dlnnzU6ZBERERHJAErE5YHu3LlDpUqVCAgI4I8//sDDw4M6depQq1YtRo0aRatWraxDubOToKAgRowYwbBhw5gxYwZdunRhwIABbN++nW+//ZaoqChmzZqFq6ur0aGmybZt2+jfvz8zZszg6aefBhKHbe/atYtRo0Zx69YtAgMDadOmjcGR/ncJCQns378fT09PfH19jQ7HZuvXr2fMmDF8//33BAYGsmvXLvz9/Zk6dSre3t78/vvvxMbGWp/zLiIiIiKPHyXikqp/VkoPHjzIrFmzcHNz4+2338bNzY18+fIxbdo0bt++nex51NnJ9u3badu2Ld27d2f69OlAYnK3detWVq9ezYgRI/Dy8jI4StskvV+jRo2iWLFi9O7dm/j4eOzs7Kzz9WNiYrh8+TKlSpXK1pXw7CohIQGz2Uy/fv3o3bs3165dY8yYMbz33nvMmDGDQoUKMWHCBMqWLWt0qCIiIiKSwbRYm6TKZDKxceNGgoODGT58OF988UWy43v27OHLL7/k448/NijC/65Ro0asWbOGN954g71791KnTh3s7e15+umnqV+/frJHtWV1SUm1yWTi0KFDyR41t3HjRu7evUu7du0oVapUsvaSeW7evEmhQoWYOXMmN27cYMSIEaxevRpvb2+WLl1KQkIC0dHRRocpIiIiIpkg6y9tLZkqaYDEb7/9Zq0Kf/jhh9bjISEhLFy4kJ49ezJlyhSaNGliVKjp4qmnnmLGjBn069ePHTt2WPdnhyQ86b36888/CQsLw2w206hRI27cuMHOnTuJiIjg8OHDDB061PoMaslcSe/R8ePHKVu2LGvXriVv3rw4OjpiNps5fPgwR48e5erVq7z33nv4+/sbG7CIiIiIZAoNTZcU1q9fz1tvvcVHH33EyZMnee+99xg2bBhjx47lwoULfPTRR7Ro0eKxWkhq06ZNjB07li1btmSLJDzJ+vXrGT16NDVq1GDfvn3s3buXDz74gGPHjhEaGkpsbCwjR458LOaEZ1fr1q3jp59+YufOnfz555989tlntGvXjnfeeYdDhw7xxx9/MH36dFq3bm10qCIiIiKSSZSISzIWi4URI0ZQtWpV/ve//wFw6NAhqlevztSpUxk8eDAxMTE4OTk9dvOMo6Ojs8UzqM1mM3Z2duzevZs33niDJUuWsHXrViZNmsSJEydwdHTk+vXr3LhxAycnJ0qUKPHYvVfZxW+//UarVq1YunQpHh4ebN68mXfffZelS5fSsGFDzp8/T2RkJJUrV9Z7JCIiIpKDaGi6JGMymbh58ybffvutdV+1atXo2rUrQ4YM4YMPPsDJycna9nGS1ZPwkJAQ7ty5Y1187cyZM4wfP54rV64wf/58goKCcHR05KeffsLd3Z1y5cpRokQJ4PF7r7KLS5cuUbVqVWrUqIGvry+9evWiZcuWdOzYkU2bNuHr60vlypUBvUciIiIiOYkS8RwuaUDE+fPnOXbsGABDhw4lX758vP/++0DiqulFixZl06ZNjB49Otmccckc0dHRzJ07lz///BOz2QyAs7MzQ4YM4e2332bVqlX4+fmxY8cOJk2axIULFwyOWAAqVKjA1atX+fHHH637mjZtSuvWrfnggw+4evWqgdGJiIiIiFG0anoOZzKZWLNmDePHj8fb25uEhAQGDx7MSy+9xIcffsjmzZsJCQlh8uTJPP300/z+++/Wiqxknjx58jBgwADu3LlDv379mDx5MnXq1KF06dJUq1aNmJgYdu3axcCBAxk3bhx+fn5Gh5zjJA0t37FjB2fPnsXR0ZHmzZvTpUsXNmzYwIkTJ2jQoAEzZ85k4sSJLFq0iNy5cxsdtoiIiIgYQHPEc6CkOcYAO3fuZMiQIaxatYqVK1cye/Zsdu3aRd68eTGbzRw/fpy8efPi5+fHvXv3yJUrl8HR5zz/nDscFBTE7Nmz8fHxYdKkSWzfvp2VK1eye/dufHx86Nu3L23atNF8Y4OsX7+eYcOGMXr0aJ5//nlmz57NM888w8GDB/n8889xcXHh3XffJTo6mrfffps1a9ZQuHBho8MWERERkUymRDyHOX78ONOmTWPatGm4uLiwZs0aXFxcuHXrFhMnTmTJkiXWIc7169fH3t7e6JBztKSEOiIiAldXVwAOHDjA9OnTKViwIIGBgeTNm5dr166RO3du3NzclIQbJCQkhC5durBgwQLOnj3LkCFDWLNmDT4+PkDie3nv3j2CgoJ45513WLBgAVWqVDE4ahERERExgsYY5yAnT57kpZdews/Pj7i4OCDxGdRdunRh6tSpbNy4ET8/P7Zs2cKbb77J+fPnjQ04h0tKqDdt2kTHjh154YUX6NGjB9WrV+ett97i5s2bDBkyhPDwcAoVKmRN1JWEZ56k7zFDQ0OJi4vjySefZM+ePbz33nt89913+Pj48OWXX7JhwwZMJhO5cuXi7NmzLFmyREm4iIiISA6mRDyHuHLlCp07d6Z///68++67FCxYEIDXXnuNdu3akSdPHsxmM6tWreLtt99m/PjxlCpVyuCoc7ak+cb9+/fnjTfeYODAgZw4cYJnnnmG6tWr07dvX+Li4hg1ahRxcXFKwA1gMpnYsmULffr0IV++fBw8eJBBgwaxevVqypYty6+//srUqVOtc8FNJhOvv/465cqVMzhyERERETGSEvEc4sKFC9SsWZPu3btjNpv56quveOmll6hYsSKlSpUiLi6Orl27MmfOHCZOnEirVq3QrAVj/PPnHhwcTN++fWnXrh316tVj9+7d3L59mx9//JG6devSrVs3rl+/TmRkpIER51yHDx9mwYIFDB8+nIIFCzJixAhq1qxJ//79mTVrFr169WLKlCk0adLE6FBFREREJAvRquk5hIuLC99++y01atRg9erVODs7U6ZMGerUqcOiRYv48MMPCQgIICoqCmdnZ0BDnI2QtJDeunXrMJlMmEwmvvvuO7p160ahQoUAqF27NiaTCTs7OywWC0ePHiU+Pt7gyHOeiIgIPv/8c9auXcvIkSMBeOqppyhbtiz/93//h9lsZvr06TRp0kTz9kVEREQkGS3WloMsWrSIefPmUbx4cUaMGEHRokXJmzcvffv25cknn6R3797JVlSXzPPPRO3333+nX79+TJw4kdKlS/PBBx9QoEAB+vTpQ2RkJF27dmXWrFnUq1ePc+fOkStXLooWLWpwD3KWs2fPUrJkSY4cOcKoUaNwc3NjypQpeHp6Gh2aiIiIiGQDSsRzmJiYGJycnKyvd+/eTc+ePfnqq6+oW7eugZHlXKdOnWLJkiWYzWaefPJJFi1aRPHixZkyZQoAK1euZP369ezduxdXV1fefvtt2rVrpyprJkv6eZ86dYq33nqLp556ihEjRnD48GE+/fRTAMaNG4eXl5fBkYqIiIhIVqdEPIe6fv06O3fuZPTo0Xz44Yc899xzRoeUI508eZIuXbrQpUsXNmzYwJkzZ6hQoQIRERFMnz6dBg0aWNv++eef5M6dm8KFC1vnkSsRz1yrV6/m008/JSYmhrt379K6dWvee+89Dh8+zNSpU7G3t2fOnDnWxdlERERERFKjRDwHMpvNHDlyhHHjxtG9e3dat25tdEg50qlTp+jYsSPvvPMOL774IvHx8Tz11FOUK1cOT09PoqOjeemll6hTp47RoQpw7NgxunTpwvLlyylWrBirVq3ihx9+oHbt2gwZMoQDBw6QK1cuPZZMRERERB5Ki7XlQHZ2dvj7+/Pll19SoEABDXE2SEREBOfPn6d69eoAODg40KRJE6pXr06JEiX47rvvmDNnDoCS8SwgKioKDw8PihQpgpOTEy1btmT79u0sXLiQ3LlzM3DgQKNDFBEREZFsQqty5WAFChQANLzZKEkr2Hfq1Iljx44xd+5cNm/eTL169ahRowadO3fGxcUFFxcXo0PN0U6cOEFsbCy+vr7kz5+f7du3ExkZiYuLC08//TQNGzbk559/5vLly0aHKiIiIiLZhIamixhs69atvPzyy7i5ubFx40aKFCliXb0+IiICV1dXo0PMcZJGiZw8eZLhw4dTrlw5Jk2axIwZM9ixYwfly5enVKlSzJw5k5kzZzJx4kSmTp1K5cqVjQ5dRERERLIBVcRFDNa4cWO+//577t27R2RkJID1EXJKwo1hMplYvXo1r7/+OrGxsQQFBfHee+8xaNAgunXrRnx8PD/99BNfffUV+fLl49q1a3h4eBgdtoiIiIhkE6qIi2QR27Zt45VXXuGbb77hqaeeMjqcHOmfjyjr1KkTP/74I2XLlmX16tUsXryY8uXL895772Fvb8+9e/dYtWoVY8eO5ZtvvtEibSIiIiJiM1XERbKIgIAA5s2bh9lsNjqUHCc2Nhb4e72E+Ph48ufPT/78+QF45plnKF68OD/++CPjx48nPj6eXLly4eHhwXfffackXERERETSRIm4SBbSpEkTGjZsiAaqZJ5Tp07Rvn17Jk+eTHR0NDExMRQvXpzChQuza9cubt68iZOTEwEBATRq1Ihjx45x6dIlABo1asQTTzxhcA9EREREJLvR48tEsiCtZJ95jh8/TlBQEAcPHuTIkSM4OzszevRo6tevzw8//MDOnTspUaIEc+fO5auvvmL8+PGEhYXh5+dndOgiIiIikk2pIi4iOVrDhg3p06cPX3/9NT169MDDw4N69epx+/ZtHBwcKFasGIcPH2bRokXY2dlx6dIlihYtanTYIiIiIpKNKREXkRytQIECAHz00Uc0btyYDz74gBs3bhAeHs7mzZs5c+YMH330EefPn+fVV19l0aJFFClSxOCoRURERCQ706rpIpJjJa2SHh0dzSuvvEJAQACzZ8/mpZdeYvjw4Vy8eJHQ0FBq1qxJcHAwjo6OVKhQweiwRURERCSbUyIuIjlebGwsQ4YMYe7cuXz++ee88sormM1m6/PcExISsLe3NzhKEREREXlcaGi6iOR4jo6ODBgwAB8fn1QfRaYkXERERETSkxJxERGgbNmyNGvWjHXr1hEXF2ethouIiIiIpDc9vkxE5C99+vQhOjqa3LlzGx2KiIiIiDzGNEdcREREREREJBNp7KWIiIiIiIhIJlIiLiIiIiIiIpKJlIiLiIiIiIiIZCIl4iIiIiIiIiKZSIm4iIiIiIiISCZSIi4iIiIiIiKSiZSIi4iIiIiIiGQiJeIiIiIiIiIimUiJuIiIiIiIiEgm+n+y7gScgtnBHAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "pivot = df.set_index(['operator', 'left_rows'])[['cpu_time', 'model_cost']]\n", + "\n", + "all_counts = sorted(df['left_rows'].unique())\n", + "valid_counts = [rc for rc in all_counts\n", + " if pivot.index.get_level_values('left_rows').isin([rc]).sum() >= 2]\n", + "\n", + "ncols = 2\n", + "nrows = (len(valid_counts) + ncols - 1) // ncols\n", + "fig, axes = plt.subplots(nrows, ncols, figsize=(5 * ncols, 4.5 * nrows), squeeze=False)\n", + "\n", + "for idx, rc in enumerate(valid_counts):\n", + " ax = axes[idx // ncols][idx % ncols]\n", + " sub = pivot.xs(rc, level='left_rows')\n", + " ops = sub.index.tolist()\n", + " m = len(ops)\n", + "\n", + " mat = np.zeros((m, m))\n", + " for i, a in enumerate(ops):\n", + " for j, b in enumerate(ops):\n", + " if i != j:\n", + " mat[i, j] = (np.log(sub.loc[a, 'cpu_time']) - np.log(sub.loc[b, 'cpu_time']) -\n", + " (np.log(sub.loc[a, 'model_cost']) - np.log(sub.loc[b, 'model_cost'])))\n", + "\n", + " vmax = np.abs(mat).max() or 1.0\n", + " im = ax.imshow(mat, cmap='coolwarm', vmin=-vmax, vmax=vmax)\n", + " ax.set_xticks(range(m)); ax.set_xticklabels(ops, rotation=45, ha='right', fontsize=8)\n", + " ax.set_yticks(range(m)); ax.set_yticklabels(ops, fontsize=8)\n", + " ax.set_title(f'rows = {rc:,}', fontsize=10)\n", + " plt.colorbar(im, ax=ax, shrink=0.8)\n", + " for i in range(m):\n", + " for j in range(m):\n", + " ax.text(j, i, f'{mat[i,j]:.2f}', ha='center', va='center', fontsize=7,\n", + " color='black' if abs(mat[i,j]) < vmax * 0.6 else 'white')\n", + "\n", + "for idx in range(len(valid_counts), nrows * ncols):\n", + " axes[idx // ncols][idx % ncols].set_visible(False)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c13aca01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
row_count641282565121024204840968192163843276865536131072262144
ab
AggregationFilterNaNNaNNaNNaN-0.285-0.211-0.154-0.1170.1350.3840.8140.9661.190
HashJoinNaNNaNNaNNaN0.3040.3370.3510.4030.4970.5770.823NaNNaN
NestedLoopCrossJoinNaNNaNNaNNaN-0.653NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoinNaNNaNNaNNaN-0.405-0.442NaNNaNNaNNaNNaNNaNNaN
ProjectionNaNNaNNaNNaN-2.051-2.026-2.004-1.916-1.740-1.443-1.081-0.891-0.870
SeqScanNaNNaNNaNNaN0.3770.3890.3870.3190.2270.4830.7370.8611.007
SortNaNNaNNaNNaN-0.546-0.647-0.666-0.628-0.564-0.349-0.087-0.276-0.364
FilterAggregationNaNNaNNaNNaN0.2850.2110.1540.117-0.135-0.384-0.814-0.966-1.190
HashJoinNaNNaNNaNNaN0.5890.5470.5050.5210.3630.1930.010NaNNaN
NestedLoopCrossJoinNaNNaNNaNNaN-0.369NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoinNaNNaNNaNNaN-0.120-0.231NaNNaNNaNNaNNaNNaNNaN
ProjectionNaNNaNNaNNaN-1.766-1.816-1.851-1.799-1.874-1.827-1.895-1.857-2.060
SeqScanNaNNaNNaNNaN0.6620.6000.5410.4360.0930.099-0.076-0.105-0.183
SortNaNNaNNaNNaN-0.262-0.437-0.512-0.510-0.699-0.733-0.901-1.242-1.554
HashJoinAggregationNaNNaNNaNNaN-0.304-0.337-0.351-0.403-0.497-0.577-0.823NaNNaN
FilterNaNNaNNaNNaN-0.589-0.547-0.505-0.521-0.363-0.193-0.010NaNNaN
NestedLoopCrossJoinNaNNaNNaNNaN-0.958NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoinNaNNaNNaNNaN-0.709-0.778NaNNaNNaNNaNNaNNaNNaN
ProjectionNaNNaNNaNNaN-2.355-2.363-2.356-2.319-2.237-2.019-1.905NaNNaN
SeqScanNaNNaNNaNNaN0.0730.0520.036-0.084-0.270-0.093-0.086NaNNaN
SortNaNNaNNaNNaN-0.851-0.984-1.017-1.031-1.062-0.926-0.911NaNNaN
NestedLoopCrossJoinAggregationNaNNaNNaNNaN0.653NaNNaNNaNNaNNaNNaNNaNNaN
FilterNaNNaNNaNNaN0.369NaNNaNNaNNaNNaNNaNNaNNaN
HashJoinNaNNaNNaNNaN0.958NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoin-0.59-0.57-0.0940.0930.248NaNNaNNaNNaNNaNNaNNaNNaN
ProjectionNaNNaNNaNNaN-1.397NaNNaNNaNNaNNaNNaNNaNNaN
SeqScanNaNNaNNaNNaN1.031NaNNaNNaNNaNNaNNaNNaNNaN
SortNaNNaNNaNNaN0.107NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoinAggregationNaNNaNNaNNaN0.4050.442NaNNaNNaNNaNNaNNaNNaN
FilterNaNNaNNaNNaN0.1200.231NaNNaNNaNNaNNaNNaNNaN
HashJoinNaNNaNNaNNaN0.7090.778NaNNaNNaNNaNNaNNaNNaN
NestedLoopCrossJoin0.590.570.094-0.093-0.248NaNNaNNaNNaNNaNNaNNaNNaN
ProjectionNaNNaNNaNNaN-1.646-1.585NaNNaNNaNNaNNaNNaNNaN
SeqScanNaNNaNNaNNaN0.7820.831NaNNaNNaNNaNNaNNaNNaN
SortNaNNaNNaNNaN-0.141-0.206NaNNaNNaNNaNNaNNaNNaN
ProjectionAggregationNaNNaNNaNNaN2.0512.0262.0041.9161.7401.4431.0810.8910.870
FilterNaNNaNNaNNaN1.7661.8161.8511.7991.8741.8271.8951.8572.060
HashJoinNaNNaNNaNNaN2.3552.3632.3562.3192.2372.0191.905NaNNaN
NestedLoopCrossJoinNaNNaNNaNNaN1.397NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoinNaNNaNNaNNaN1.6461.585NaNNaNNaNNaNNaNNaNNaN
SeqScanNaNNaNNaNNaN2.4282.4152.3922.2351.9671.9261.8191.7531.877
SortNaNNaNNaNNaN1.5041.3791.3381.2881.1751.0940.9940.6150.506
SeqScanAggregationNaNNaNNaNNaN-0.377-0.389-0.387-0.319-0.227-0.483-0.737-0.861-1.007
FilterNaNNaNNaNNaN-0.662-0.600-0.541-0.436-0.093-0.0990.0760.1050.183
HashJoinNaNNaNNaNNaN-0.073-0.052-0.0360.0840.2700.0930.086NaNNaN
NestedLoopCrossJoinNaNNaNNaNNaN-1.031NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoinNaNNaNNaNNaN-0.782-0.831NaNNaNNaNNaNNaNNaNNaN
ProjectionNaNNaNNaNNaN-2.428-2.415-2.392-2.235-1.967-1.926-1.819-1.753-1.877
SortNaNNaNNaNNaN-0.924-1.036-1.053-0.947-0.792-0.832-0.825-1.138-1.371
SortAggregationNaNNaNNaNNaN0.5460.6470.6660.6280.5640.3490.0870.2760.364
FilterNaNNaNNaNNaN0.2620.4370.5120.5100.6990.7330.9011.2421.554
HashJoinNaNNaNNaNNaN0.8510.9841.0171.0311.0620.9260.911NaNNaN
NestedLoopCrossJoinNaNNaNNaNNaN-0.107NaNNaNNaNNaNNaNNaNNaNNaN
NestedLoopJoinNaNNaNNaNNaN0.1410.206NaNNaNNaNNaNNaNNaNNaN
ProjectionNaNNaNNaNNaN-1.504-1.379-1.338-1.288-1.175-1.094-0.994-0.615-0.506
SeqScanNaNNaNNaNNaN0.9241.0361.0530.9470.7920.8320.8251.1381.371
\n", + "
" + ], + "text/plain": [ + "row_count 64 128 256 512 \\\n", + "a b \n", + "Aggregation Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "Filter Aggregation NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "HashJoin Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "NestedLoopCrossJoin Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopJoin -0.59 -0.57 -0.094 0.093 \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "NestedLoopJoin Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopCrossJoin 0.59 0.57 0.094 -0.093 \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "Projection Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "SeqScan Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "Sort Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + "\n", + "row_count 1024 2048 4096 8192 \\\n", + "a b \n", + "Aggregation Filter -0.285 -0.211 -0.154 -0.117 \n", + " HashJoin 0.304 0.337 0.351 0.403 \n", + " NestedLoopCrossJoin -0.653 NaN NaN NaN \n", + " NestedLoopJoin -0.405 -0.442 NaN NaN \n", + " Projection -2.051 -2.026 -2.004 -1.916 \n", + " SeqScan 0.377 0.389 0.387 0.319 \n", + " Sort -0.546 -0.647 -0.666 -0.628 \n", + "Filter Aggregation 0.285 0.211 0.154 0.117 \n", + " HashJoin 0.589 0.547 0.505 0.521 \n", + " NestedLoopCrossJoin -0.369 NaN NaN NaN \n", + " NestedLoopJoin -0.120 -0.231 NaN NaN \n", + " Projection -1.766 -1.816 -1.851 -1.799 \n", + " SeqScan 0.662 0.600 0.541 0.436 \n", + " Sort -0.262 -0.437 -0.512 -0.510 \n", + "HashJoin Aggregation -0.304 -0.337 -0.351 -0.403 \n", + " Filter -0.589 -0.547 -0.505 -0.521 \n", + " NestedLoopCrossJoin -0.958 NaN NaN NaN \n", + " NestedLoopJoin -0.709 -0.778 NaN NaN \n", + " Projection -2.355 -2.363 -2.356 -2.319 \n", + " SeqScan 0.073 0.052 0.036 -0.084 \n", + " Sort -0.851 -0.984 -1.017 -1.031 \n", + "NestedLoopCrossJoin Aggregation 0.653 NaN NaN NaN \n", + " Filter 0.369 NaN NaN NaN \n", + " HashJoin 0.958 NaN NaN NaN \n", + " NestedLoopJoin 0.248 NaN NaN NaN \n", + " Projection -1.397 NaN NaN NaN \n", + " SeqScan 1.031 NaN NaN NaN \n", + " Sort 0.107 NaN NaN NaN \n", + "NestedLoopJoin Aggregation 0.405 0.442 NaN NaN \n", + " Filter 0.120 0.231 NaN NaN \n", + " HashJoin 0.709 0.778 NaN NaN \n", + " NestedLoopCrossJoin -0.248 NaN NaN NaN \n", + " Projection -1.646 -1.585 NaN NaN \n", + " SeqScan 0.782 0.831 NaN NaN \n", + " Sort -0.141 -0.206 NaN NaN \n", + "Projection Aggregation 2.051 2.026 2.004 1.916 \n", + " Filter 1.766 1.816 1.851 1.799 \n", + " HashJoin 2.355 2.363 2.356 2.319 \n", + " NestedLoopCrossJoin 1.397 NaN NaN NaN \n", + " NestedLoopJoin 1.646 1.585 NaN NaN \n", + " SeqScan 2.428 2.415 2.392 2.235 \n", + " Sort 1.504 1.379 1.338 1.288 \n", + "SeqScan Aggregation -0.377 -0.389 -0.387 -0.319 \n", + " Filter -0.662 -0.600 -0.541 -0.436 \n", + " HashJoin -0.073 -0.052 -0.036 0.084 \n", + " NestedLoopCrossJoin -1.031 NaN NaN NaN \n", + " NestedLoopJoin -0.782 -0.831 NaN NaN \n", + " Projection -2.428 -2.415 -2.392 -2.235 \n", + " Sort -0.924 -1.036 -1.053 -0.947 \n", + "Sort Aggregation 0.546 0.647 0.666 0.628 \n", + " Filter 0.262 0.437 0.512 0.510 \n", + " HashJoin 0.851 0.984 1.017 1.031 \n", + " NestedLoopCrossJoin -0.107 NaN NaN NaN \n", + " NestedLoopJoin 0.141 0.206 NaN NaN \n", + " Projection -1.504 -1.379 -1.338 -1.288 \n", + " SeqScan 0.924 1.036 1.053 0.947 \n", + "\n", + "row_count 16384 32768 65536 131072 \\\n", + "a b \n", + "Aggregation Filter 0.135 0.384 0.814 0.966 \n", + " HashJoin 0.497 0.577 0.823 NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection -1.740 -1.443 -1.081 -0.891 \n", + " SeqScan 0.227 0.483 0.737 0.861 \n", + " Sort -0.564 -0.349 -0.087 -0.276 \n", + "Filter Aggregation -0.135 -0.384 -0.814 -0.966 \n", + " HashJoin 0.363 0.193 0.010 NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection -1.874 -1.827 -1.895 -1.857 \n", + " SeqScan 0.093 0.099 -0.076 -0.105 \n", + " Sort -0.699 -0.733 -0.901 -1.242 \n", + "HashJoin Aggregation -0.497 -0.577 -0.823 NaN \n", + " Filter -0.363 -0.193 -0.010 NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection -2.237 -2.019 -1.905 NaN \n", + " SeqScan -0.270 -0.093 -0.086 NaN \n", + " Sort -1.062 -0.926 -0.911 NaN \n", + "NestedLoopCrossJoin Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "NestedLoopJoin Aggregation NaN NaN NaN NaN \n", + " Filter NaN NaN NaN NaN \n", + " HashJoin NaN NaN NaN NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " Projection NaN NaN NaN NaN \n", + " SeqScan NaN NaN NaN NaN \n", + " Sort NaN NaN NaN NaN \n", + "Projection Aggregation 1.740 1.443 1.081 0.891 \n", + " Filter 1.874 1.827 1.895 1.857 \n", + " HashJoin 2.237 2.019 1.905 NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " SeqScan 1.967 1.926 1.819 1.753 \n", + " Sort 1.175 1.094 0.994 0.615 \n", + "SeqScan Aggregation -0.227 -0.483 -0.737 -0.861 \n", + " Filter -0.093 -0.099 0.076 0.105 \n", + " HashJoin 0.270 0.093 0.086 NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection -1.967 -1.926 -1.819 -1.753 \n", + " Sort -0.792 -0.832 -0.825 -1.138 \n", + "Sort Aggregation 0.564 0.349 0.087 0.276 \n", + " Filter 0.699 0.733 0.901 1.242 \n", + " HashJoin 1.062 0.926 0.911 NaN \n", + " NestedLoopCrossJoin NaN NaN NaN NaN \n", + " NestedLoopJoin NaN NaN NaN NaN \n", + " Projection -1.175 -1.094 -0.994 -0.615 \n", + " SeqScan 0.792 0.832 0.825 1.138 \n", + "\n", + "row_count 262144 \n", + "a b \n", + "Aggregation Filter 1.190 \n", + " HashJoin NaN \n", + " NestedLoopCrossJoin NaN \n", + " NestedLoopJoin NaN \n", + " Projection -0.870 \n", + " SeqScan 1.007 \n", + " Sort -0.364 \n", + "Filter Aggregation -1.190 \n", + " HashJoin NaN \n", + " NestedLoopCrossJoin NaN \n", + " NestedLoopJoin NaN \n", + " Projection -2.060 \n", + " SeqScan -0.183 \n", + " Sort -1.554 \n", + "HashJoin Aggregation NaN \n", + " Filter NaN \n", + " NestedLoopCrossJoin NaN \n", + " NestedLoopJoin NaN \n", + " Projection NaN \n", + " SeqScan NaN \n", + " Sort NaN \n", + "NestedLoopCrossJoin Aggregation NaN \n", + " Filter NaN \n", + " HashJoin NaN \n", + " NestedLoopJoin NaN \n", + " Projection NaN \n", + " SeqScan NaN \n", + " Sort NaN \n", + "NestedLoopJoin Aggregation NaN \n", + " Filter NaN \n", + " HashJoin NaN \n", + " NestedLoopCrossJoin NaN \n", + " Projection NaN \n", + " SeqScan NaN \n", + " Sort NaN \n", + "Projection Aggregation 0.870 \n", + " Filter 2.060 \n", + " HashJoin NaN \n", + " NestedLoopCrossJoin NaN \n", + " NestedLoopJoin NaN \n", + " SeqScan 1.877 \n", + " Sort 0.506 \n", + "SeqScan Aggregation -1.007 \n", + " Filter 0.183 \n", + " HashJoin NaN \n", + " NestedLoopCrossJoin NaN \n", + " NestedLoopJoin NaN \n", + " Projection -1.877 \n", + " Sort -1.371 \n", + "Sort Aggregation 0.364 \n", + " Filter 1.554 \n", + " HashJoin NaN \n", + " NestedLoopCrossJoin NaN \n", + " NestedLoopJoin NaN \n", + " Projection -0.506 \n", + " SeqScan 1.371 " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rows = []\n", + "for rc in valid_counts:\n", + " sub = pivot.xs(rc, level='left_rows')\n", + " ops = sub.index.tolist()\n", + " for a in ops:\n", + " for b in ops:\n", + " if a != b:\n", + " delta = (np.log(sub.loc[a, 'cpu_time']) - np.log(sub.loc[b, 'cpu_time']) -\n", + " (np.log(sub.loc[a, 'model_cost']) - np.log(sub.loc[b, 'model_cost'])))\n", + " rows.append({'row_count': rc, 'a': a, 'b': b, 'delta': delta})\n", + "\n", + "comparison = pd.DataFrame(rows)\n", + "comparison.pivot_table(index=['a', 'b'], columns='row_count', values='delta').round(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "68ae0220", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-06-09T23:41:04+03:00\n", + "Running ./build-release/bin/benchmarks\n", + "Run on (8 X 4200 MHz CPU s)\n", + "CPU Caches:\n", + " L1 Data 48 KiB (x4)\n", + " L1 Instruction 32 KiB (x4)\n", + " L2 Unified 1280 KiB (x4)\n", + " L3 Unified 8192 KiB (x1)\n", + "Load Average: 1.09, 0.80, 1.04\n", + "***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.\n", + "-----------------------------------------------------------------------------------------------------------------------------------------\n", + "Benchmark Time CPU Iterations UserCounters...\n", + "-----------------------------------------------------------------------------------------------------------------------------------------\n", + "\u001b[0;32mOperatorCostMatched/target_cost:640000/SeqScan/6400/real_time \u001b[m\u001b[0;33m 309954 ns 306993 ns \u001b[m\u001b[0;36m 2231\u001b[m model_cost=640k\u001b[m output_rows=6.4k\u001b[m plan_cost=640k\u001b[m rows=6.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Filter/6400/real_time \u001b[m\u001b[0;33m 407352 ns 405145 ns \u001b[m\u001b[0;36m 1738\u001b[m model_cost=640k\u001b[m output_rows=3.328k\u001b[m plan_cost=1.28M\u001b[m rows=6.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Projection/29091/real_time \u001b[m\u001b[0;33m 3320695 ns 3294023 ns \u001b[m\u001b[0;36m 197\u001b[m model_cost=640.002k\u001b[m output_rows=29.091k\u001b[m plan_cost=3.5491M\u001b[m rows=29.091k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Sort/4476/real_time \u001b[m\u001b[0;33m 720259 ns 716718 ns \u001b[m\u001b[0;36m 971\u001b[m model_cost=640.068k\u001b[m output_rows=4.476k\u001b[m plan_cost=1.08767M\u001b[m rows=4.476k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/Aggregation/1255/real_time \u001b[m\u001b[0;33m 348253 ns 347048 ns \u001b[m\u001b[0;36m 2013\u001b[m model_cost=640.05k\u001b[m output_rows=1.255k\u001b[m plan_cost=765.55k\u001b[m rows=1.255k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time \u001b[m\u001b[0;33m 246864 ns 245902 ns \u001b[m\u001b[0;36m 2845\u001b[m lhs_rows=1.62k\u001b[m model_cost=639.9k\u001b[m output_rows=1.62k\u001b[m plan_cost=963.9k\u001b[m rhs_rows=1.62k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time \u001b[m\u001b[0;33m 697766 ns 693468 ns \u001b[m\u001b[0;36m 1046\u001b[m lhs_rows=96\u001b[m model_cost=645.12k\u001b[m output_rows=96\u001b[m plan_cost=664.32k\u001b[m rhs_rows=96\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time \u001b[m\u001b[0;33m 399168 ns 395553 ns \u001b[m\u001b[0;36m 1705\u001b[m lhs_rows=78\u001b[m model_cost=632.736k\u001b[m output_rows=6.084k\u001b[m plan_cost=648.336k\u001b[m rhs_rows=78\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time \u001b[m\u001b[0;33m 380590 ns 377409 ns \u001b[m\u001b[0;36m 1982\u001b[m model_cost=1M\u001b[m output_rows=10k\u001b[m plan_cost=1M\u001b[m rows=10k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Filter/10000/real_time \u001b[m\u001b[0;33m 657861 ns 654417 ns \u001b[m\u001b[0;36m 1094\u001b[m model_cost=1M\u001b[m output_rows=5.12k\u001b[m plan_cost=2M\u001b[m rows=10k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Projection/45455/real_time \u001b[m\u001b[0;33m 5492013 ns 5446973 ns \u001b[m\u001b[0;36m 129\u001b[m model_cost=1.00001M\u001b[m output_rows=45.455k\u001b[m plan_cost=5.54551M\u001b[m rows=45.455k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Sort/6993/real_time \u001b[m\u001b[0;33m 1165643 ns 1159924 ns \u001b[m\u001b[0;36m 598\u001b[m model_cost=0.999999M\u001b[m output_rows=6.993k\u001b[m plan_cost=1.6993M\u001b[m rows=6.993k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time \u001b[m\u001b[0;33m 531581 ns 529216 ns \u001b[m\u001b[0;36m 1293\u001b[m model_cost=1.00011M\u001b[m output_rows=1.961k\u001b[m plan_cost=1.19621M\u001b[m rows=1.961k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time \u001b[m\u001b[0;33m 396404 ns 394343 ns \u001b[m\u001b[0;36m 1798\u001b[m lhs_rows=2.532k\u001b[m model_cost=1.00014M\u001b[m output_rows=2.532k\u001b[m plan_cost=1.50654M\u001b[m rhs_rows=2.532k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time \u001b[m\u001b[0;33m 982848 ns 978044 ns \u001b[m\u001b[0;36m 711\u001b[m lhs_rows=120\u001b[m model_cost=1.008M\u001b[m output_rows=120\u001b[m plan_cost=1.032M\u001b[m rhs_rows=120\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time \u001b[m\u001b[0;33m 592521 ns 588477 ns \u001b[m\u001b[0;36m 945\u001b[m lhs_rows=98\u001b[m model_cost=998.816k\u001b[m output_rows=9.604k\u001b[m plan_cost=1.01842M\u001b[m rhs_rows=98\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time \u001b[m\u001b[0;33m 1145653 ns 1137800 ns \u001b[m\u001b[0;36m 602\u001b[m model_cost=3.24M\u001b[m output_rows=32.4k\u001b[m plan_cost=3.24M\u001b[m rows=32.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Filter/32400/real_time \u001b[m\u001b[0;33m 2130491 ns 2118603 ns \u001b[m\u001b[0;36m 333\u001b[m model_cost=3.24M\u001b[m output_rows=16.384k\u001b[m plan_cost=6.48M\u001b[m rows=32.4k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Projection/147273/real_time \u001b[m\u001b[0;33m 19355047 ns 19193728 ns \u001b[m\u001b[0;36m 37\u001b[m model_cost=3.24001M\u001b[m output_rows=147.273k\u001b[m plan_cost=17.9673M\u001b[m rows=147.273k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Sort/19636/real_time \u001b[m\u001b[0;33m 4048476 ns 4028779 ns \u001b[m\u001b[0;36m 172\u001b[m model_cost=3.23994M\u001b[m output_rows=19.636k\u001b[m plan_cost=5.20354M\u001b[m rows=19.636k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time \u001b[m\u001b[0;33m 1831355 ns 1822600 ns \u001b[m\u001b[0;36m 389\u001b[m model_cost=3.24003M\u001b[m output_rows=6.353k\u001b[m plan_cost=3.87533M\u001b[m rows=6.353k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time \u001b[m\u001b[0;33m 1297606 ns 1289387 ns \u001b[m\u001b[0;36m 539\u001b[m lhs_rows=8.203k\u001b[m model_cost=3.24018M\u001b[m output_rows=8.203k\u001b[m plan_cost=4.88078M\u001b[m rhs_rows=8.203k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time \u001b[m\u001b[0;33m 2799041 ns 2788075 ns \u001b[m\u001b[0;36m 249\u001b[m lhs_rows=215\u001b[m model_cost=3.23575M\u001b[m output_rows=215\u001b[m plan_cost=3.27875M\u001b[m rhs_rows=215\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time \u001b[m\u001b[0;33m 1823392 ns 1805263 ns \u001b[m\u001b[0;36m 377\u001b[m lhs_rows=177\u001b[m model_cost=3.25822M\u001b[m output_rows=31.329k\u001b[m plan_cost=3.29362M\u001b[m rhs_rows=177\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time \u001b[m\u001b[0;33m 3559425 ns 3523442 ns \u001b[m\u001b[0;36m 206\u001b[m model_cost=6.76M\u001b[m output_rows=67.6k\u001b[m plan_cost=6.76M\u001b[m rows=67.6k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Filter/67600/real_time \u001b[m\u001b[0;33m 4628039 ns 4602719 ns \u001b[m\u001b[0;36m 134\u001b[m model_cost=6.76M\u001b[m output_rows=33.808k\u001b[m plan_cost=13.52M\u001b[m rows=67.6k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Projection/307273/real_time \u001b[m\u001b[0;33m 38239999 ns 37998344 ns \u001b[m\u001b[0;36m 19\u001b[m model_cost=6.76001M\u001b[m output_rows=307.273k\u001b[m plan_cost=37.4873M\u001b[m rows=307.273k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Sort/38409/real_time \u001b[m\u001b[0;33m 10169495 ns 10119187 ns \u001b[m\u001b[0;36m 72\u001b[m model_cost=6.75998M\u001b[m output_rows=38.409k\u001b[m plan_cost=10.6009M\u001b[m rows=38.409k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time \u001b[m\u001b[0;33m 4172917 ns 4152985 ns \u001b[m\u001b[0;36m 174\u001b[m model_cost=6.76005M\u001b[m output_rows=13.255k\u001b[m plan_cost=8.08555M\u001b[m rows=13.255k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time \u001b[m\u001b[0;33m 2975624 ns 2955759 ns \u001b[m\u001b[0;36m 204\u001b[m lhs_rows=17.114k\u001b[m model_cost=6.76003M\u001b[m output_rows=17.114k\u001b[m plan_cost=10.1828M\u001b[m rhs_rows=17.114k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time \u001b[m\u001b[0;33m 5785900 ns 5761548 ns \u001b[m\u001b[0;36m 121\u001b[m lhs_rows=311\u001b[m model_cost=6.77047M\u001b[m output_rows=311\u001b[m plan_cost=6.83267M\u001b[m rhs_rows=311\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time \u001b[m\u001b[0;33m 5343956 ns 5282506 ns \u001b[m\u001b[0;36m 131\u001b[m lhs_rows=255\u001b[m model_cost=6.7626M\u001b[m output_rows=65.025k\u001b[m plan_cost=6.8136M\u001b[m rhs_rows=255\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time \u001b[m\u001b[0;33m 7067011 ns 7008813 ns \u001b[m\u001b[0;36m 94\u001b[m model_cost=12.25M\u001b[m output_rows=122.5k\u001b[m plan_cost=12.25M\u001b[m rows=122.5k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Filter/122500/real_time \u001b[m\u001b[0;33m 8754703 ns 8704815 ns \u001b[m\u001b[0;36m 83\u001b[m model_cost=12.25M\u001b[m output_rows=61.44k\u001b[m plan_cost=24.5M\u001b[m rows=122.5k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Projection/556818/real_time \u001b[m\u001b[0;33m 74966988 ns 74476933 ns \u001b[m\u001b[0;36m 10\u001b[m model_cost=12.25M\u001b[m output_rows=556.818k\u001b[m plan_cost=67.9318M\u001b[m rows=556.818k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Sort/65536/real_time \u001b[m\u001b[0;33m 19754016 ns 19632989 ns \u001b[m\u001b[0;36m 35\u001b[m model_cost=12.2552M\u001b[m output_rows=65.536k\u001b[m plan_cost=18.8088M\u001b[m rows=65.536k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time \u001b[m\u001b[0;33m 10917472 ns 10837185 ns \u001b[m\u001b[0;36m 70\u001b[m model_cost=12.2502M\u001b[m output_rows=24.02k\u001b[m plan_cost=14.6522M\u001b[m rows=24.02k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time \u001b[m\u001b[0;33m 6093819 ns 6031366 ns \u001b[m\u001b[0;36m 106\u001b[m lhs_rows=31.013k\u001b[m model_cost=12.2501M\u001b[m output_rows=31.013k\u001b[m plan_cost=18.4527M\u001b[m rhs_rows=31.013k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time \u001b[m\u001b[0;33m 10007416 ns 9965044 ns \u001b[m\u001b[0;36m 70\u001b[m lhs_rows=418\u001b[m model_cost=12.2307M\u001b[m output_rows=418\u001b[m plan_cost=12.3143M\u001b[m rhs_rows=418\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time \u001b[m\u001b[0;33m 10467055 ns 10353758 ns \u001b[m\u001b[0;36m 70\u001b[m lhs_rows=343\u001b[m model_cost=12.2355M\u001b[m output_rows=117.649k\u001b[m plan_cost=12.3041M\u001b[m rhs_rows=343\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time \u001b[m\u001b[0;33m 16761908 ns 16627441 ns \u001b[m\u001b[0;36m 42\u001b[m model_cost=26.01M\u001b[m output_rows=260.1k\u001b[m plan_cost=26.01M\u001b[m rows=260.1k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Filter/260100/real_time \u001b[m\u001b[0;33m 19593865 ns 19459568 ns \u001b[m\u001b[0;36m 36\u001b[m model_cost=26.01M\u001b[m output_rows=130.052k\u001b[m plan_cost=52.02M\u001b[m rows=260.1k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Projection/1182273/real_time \u001b[m\u001b[0;33m 159555687 ns 158424515 ns \u001b[m\u001b[0;36m 5\u001b[m model_cost=26.01M\u001b[m output_rows=1.18227M\u001b[m plan_cost=144.237M\u001b[m rows=1.18227M\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Sort/131364/real_time \u001b[m\u001b[0;33m 56860476 ns 56499320 ns \u001b[m\u001b[0;36m 13\u001b[m model_cost=26.0101M\u001b[m output_rows=131.364k\u001b[m plan_cost=39.1465M\u001b[m rows=131.364k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time \u001b[m\u001b[0;33m 34235903 ns 33978390 ns \u001b[m\u001b[0;36m 21\u001b[m model_cost=26.01M\u001b[m output_rows=51k\u001b[m plan_cost=31.11M\u001b[m rows=51k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time \u001b[m\u001b[0;33m 15134376 ns 15002297 ns \u001b[m\u001b[0;36m 47\u001b[m lhs_rows=65.848k\u001b[m model_cost=26.01M\u001b[m output_rows=65.848k\u001b[m plan_cost=39.1796M\u001b[m rhs_rows=65.848k\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time \u001b[m\u001b[0;33m 21153397 ns 21080711 ns \u001b[m\u001b[0;36m 31\u001b[m lhs_rows=610\u001b[m model_cost=26.047M\u001b[m output_rows=610\u001b[m plan_cost=26.169M\u001b[m rhs_rows=610\u001b[m\n", + "\u001b[m\u001b[0;32mOperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time \u001b[m\u001b[0;33m 22172263 ns 21945666 ns \u001b[m\u001b[0;36m 31\u001b[m lhs_rows=500\u001b[m model_cost=26M\u001b[m output_rows=250k\u001b[m plan_cost=26.1M\u001b[m rhs_rows=500\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "!cd ~/c/iu9-sql-compiler/ && ./build-release/bin/benchmarks --benchmark_filter='OperatorCostMatched*' --benchmark_out=/tmp/operator-cost-matched.json --benchmark_out_format=json" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1c313f56", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'context': {'date': '2026-06-09T23:41:04+03:00',\n", + " 'host_name': 'nixos',\n", + " 'executable': './build-release/bin/benchmarks',\n", + " 'num_cpus': 8,\n", + " 'mhz_per_cpu': 4200,\n", + " 'cpu_scaling_enabled': True,\n", + " 'caches': [{'type': 'Data', 'level': 1, 'size': 49152, 'num_sharing': 2},\n", + " {'type': 'Instruction', 'level': 1, 'size': 32768, 'num_sharing': 2},\n", + " {'type': 'Unified', 'level': 2, 'size': 1310720, 'num_sharing': 2},\n", + " {'type': 'Unified', 'level': 3, 'size': 8388608, 'num_sharing': 8}],\n", + " 'load_avg': [1.09375, 0.795898, 1.04199],\n", + " 'library_version': 'v1.9.0',\n", + " 'library_build_type': 'release',\n", + " 'json_schema_version': 1},\n", + " 'benchmarks': [{'name': 'OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time',\n", + " 'family_index': 0,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/SeqScan/6400/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2231,\n", + " 'real_time': 309954.099962947,\n", + " 'cpu_time': 306992.77498879423,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 640000.0,\n", + " 'output_rows': 6400.0,\n", + " 'plan_cost': 640000.0,\n", + " 'rows': 6400.0},\n", + " {'name': 'OperatorCostMatched/target_cost:640000/Filter/6400/real_time',\n", + " 'family_index': 1,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/Filter/6400/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1738,\n", + " 'real_time': 407352.46718984947,\n", + " 'cpu_time': 405144.89355581126,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 640000.0,\n", + " 'output_rows': 3328.0,\n", + " 'plan_cost': 1280000.0,\n", + " 'rows': 6400.0},\n", + " {'name': 'OperatorCostMatched/target_cost:640000/Projection/29091/real_time',\n", + " 'family_index': 2,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/Projection/29091/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 197,\n", + " 'real_time': 3320695.1267884923,\n", + " 'cpu_time': 3294022.7258883254,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 640002.0,\n", + " 'output_rows': 29091.0,\n", + " 'plan_cost': 3549102.0,\n", + " 'rows': 29091.0},\n", + " {'name': 'OperatorCostMatched/target_cost:640000/Sort/4476/real_time',\n", + " 'family_index': 3,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/Sort/4476/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 971,\n", + " 'real_time': 720258.5479141482,\n", + " 'cpu_time': 716717.8671472706,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 640068.0,\n", + " 'output_rows': 4476.0,\n", + " 'plan_cost': 1087668.0,\n", + " 'rows': 4476.0},\n", + " {'name': 'OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time',\n", + " 'family_index': 4,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/Aggregation/1255/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2013,\n", + " 'real_time': 348253.45255807874,\n", + " 'cpu_time': 347048.28315946314,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 640050.0,\n", + " 'output_rows': 1255.0,\n", + " 'plan_cost': 765550.0,\n", + " 'rows': 1255.0},\n", + " {'name': 'OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time',\n", + " 'family_index': 5,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/HashJoin/1620/1620/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 2845,\n", + " 'real_time': 246863.73918773918,\n", + " 'cpu_time': 245901.98066783816,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 1620.0,\n", + " 'model_cost': 639900.0,\n", + " 'output_rows': 1620.0,\n", + " 'plan_cost': 963900.0,\n", + " 'rhs_rows': 1620.0},\n", + " {'name': 'OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time',\n", + " 'family_index': 6,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/NestedLoopJoin/96/96/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1046,\n", + " 'real_time': 697765.7963460343,\n", + " 'cpu_time': 693467.9225621414,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 96.0,\n", + " 'model_cost': 645120.0,\n", + " 'output_rows': 96.0,\n", + " 'plan_cost': 664320.0,\n", + " 'rhs_rows': 96.0},\n", + " {'name': 'OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time',\n", + " 'family_index': 7,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:640000/NestedLoopCrossJoin/78/78/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1705,\n", + " 'real_time': 399167.8293283668,\n", + " 'cpu_time': 395552.58651026443,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 78.0,\n", + " 'model_cost': 632736.0,\n", + " 'output_rows': 6084.0,\n", + " 'plan_cost': 648336.0,\n", + " 'rhs_rows': 78.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time',\n", + " 'family_index': 8,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/SeqScan/10000/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1982,\n", + " 'real_time': 380590.1458114292,\n", + " 'cpu_time': 377408.647325934,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1000000.0,\n", + " 'output_rows': 10000.0,\n", + " 'plan_cost': 1000000.0,\n", + " 'rows': 10000.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/Filter/10000/real_time',\n", + " 'family_index': 9,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/Filter/10000/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1094,\n", + " 'real_time': 657861.3775062938,\n", + " 'cpu_time': 654417.0466179174,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1000000.0,\n", + " 'output_rows': 5120.0,\n", + " 'plan_cost': 2000000.0,\n", + " 'rows': 10000.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/Projection/45455/real_time',\n", + " 'family_index': 10,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/Projection/45455/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 129,\n", + " 'real_time': 5492012.674357135,\n", + " 'cpu_time': 5446973.124030996,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1000010.0,\n", + " 'output_rows': 45455.0,\n", + " 'plan_cost': 5545510.0,\n", + " 'rows': 45455.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/Sort/6993/real_time',\n", + " 'family_index': 11,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/Sort/6993/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 598,\n", + " 'real_time': 1165643.4515340838,\n", + " 'cpu_time': 1159923.8896321051,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 999999.0,\n", + " 'output_rows': 6993.0,\n", + " 'plan_cost': 1699299.0,\n", + " 'rows': 6993.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time',\n", + " 'family_index': 12,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/Aggregation/1961/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1293,\n", + " 'real_time': 531581.1461662803,\n", + " 'cpu_time': 529216.385150812,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 1000110.0,\n", + " 'output_rows': 1961.0,\n", + " 'plan_cost': 1196210.0,\n", + " 'rows': 1961.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time',\n", + " 'family_index': 13,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/HashJoin/2532/2532/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 1798,\n", + " 'real_time': 396404.3103330183,\n", + " 'cpu_time': 394342.85817575123,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 2532.0,\n", + " 'model_cost': 1000140.0,\n", + " 'output_rows': 2532.0,\n", + " 'plan_cost': 1506540.0,\n", + " 'rhs_rows': 2532.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time',\n", + " 'family_index': 14,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/NestedLoopJoin/120/120/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 711,\n", + " 'real_time': 982848.244728049,\n", + " 'cpu_time': 978044.4992967627,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 120.0,\n", + " 'model_cost': 1008000.0,\n", + " 'output_rows': 120.0,\n", + " 'plan_cost': 1032000.0,\n", + " 'rhs_rows': 120.0},\n", + " {'name': 'OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time',\n", + " 'family_index': 15,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:1000000/NestedLoopCrossJoin/98/98/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 945,\n", + " 'real_time': 592520.8222362454,\n", + " 'cpu_time': 588477.2994708995,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 98.0,\n", + " 'model_cost': 998816.0,\n", + " 'output_rows': 9604.0,\n", + " 'plan_cost': 1018416.0,\n", + " 'rhs_rows': 98.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time',\n", + " 'family_index': 16,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/SeqScan/32400/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 602,\n", + " 'real_time': 1145653.0216108216,\n", + " 'cpu_time': 1137799.9152823885,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 3240000.0,\n", + " 'output_rows': 32400.0,\n", + " 'plan_cost': 3240000.0,\n", + " 'rows': 32400.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/Filter/32400/real_time',\n", + " 'family_index': 17,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/Filter/32400/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 333,\n", + " 'real_time': 2130490.6636592583,\n", + " 'cpu_time': 2118602.5795795773,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 3240000.0,\n", + " 'output_rows': 16384.0,\n", + " 'plan_cost': 6480000.0,\n", + " 'rows': 32400.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/Projection/147273/real_time',\n", + " 'family_index': 18,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/Projection/147273/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 37,\n", + " 'real_time': 19355047.134701412,\n", + " 'cpu_time': 19193728.13513518,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 3240006.0,\n", + " 'output_rows': 147273.0,\n", + " 'plan_cost': 17967306.0,\n", + " 'rows': 147273.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/Sort/19636/real_time',\n", + " 'family_index': 19,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/Sort/19636/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 172,\n", + " 'real_time': 4048476.3313691285,\n", + " 'cpu_time': 4028778.8023255784,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 3239940.0,\n", + " 'output_rows': 19636.0,\n", + " 'plan_cost': 5203540.0,\n", + " 'rows': 19636.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time',\n", + " 'family_index': 20,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/Aggregation/6353/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 389,\n", + " 'real_time': 1831354.6143687665,\n", + " 'cpu_time': 1822599.7814909993,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 3240030.0,\n", + " 'output_rows': 6353.0,\n", + " 'plan_cost': 3875330.0,\n", + " 'rows': 6353.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time',\n", + " 'family_index': 21,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/HashJoin/8203/8203/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 539,\n", + " 'real_time': 1297606.244909617,\n", + " 'cpu_time': 1289387.3710575104,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 8203.0,\n", + " 'model_cost': 3240185.0,\n", + " 'output_rows': 8203.0,\n", + " 'plan_cost': 4880785.0,\n", + " 'rhs_rows': 8203.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time',\n", + " 'family_index': 22,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/NestedLoopJoin/215/215/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 249,\n", + " 'real_time': 2799041.2047922043,\n", + " 'cpu_time': 2788075.1244980027,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 215.0,\n", + " 'model_cost': 3235750.0,\n", + " 'output_rows': 215.0,\n", + " 'plan_cost': 3278750.0,\n", + " 'rhs_rows': 215.0},\n", + " {'name': 'OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time',\n", + " 'family_index': 23,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:3240000/NestedLoopCrossJoin/177/177/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 377,\n", + " 'real_time': 1823391.5596992883,\n", + " 'cpu_time': 1805262.946949611,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 177.0,\n", + " 'model_cost': 3258216.0,\n", + " 'output_rows': 31329.0,\n", + " 'plan_cost': 3293616.0,\n", + " 'rhs_rows': 177.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time',\n", + " 'family_index': 24,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/SeqScan/67600/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 206,\n", + " 'real_time': 3559424.834906856,\n", + " 'cpu_time': 3523441.9902912695,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 6760000.0,\n", + " 'output_rows': 67600.0,\n", + " 'plan_cost': 6760000.0,\n", + " 'rows': 67600.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/Filter/67600/real_time',\n", + " 'family_index': 25,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/Filter/67600/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 134,\n", + " 'real_time': 4628038.888062184,\n", + " 'cpu_time': 4602719.089552247,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 6760000.0,\n", + " 'output_rows': 33808.0,\n", + " 'plan_cost': 13520000.0,\n", + " 'rows': 67600.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/Projection/307273/real_time',\n", + " 'family_index': 26,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/Projection/307273/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 19,\n", + " 'real_time': 38239999.21013111,\n", + " 'cpu_time': 37998344.315789424,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 6760006.0,\n", + " 'output_rows': 307273.0,\n", + " 'plan_cost': 37487306.0,\n", + " 'rows': 307273.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/Sort/38409/real_time',\n", + " 'family_index': 27,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/Sort/38409/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 72,\n", + " 'real_time': 10169495.055177119,\n", + " 'cpu_time': 10119187.347222218,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 6759984.0,\n", + " 'output_rows': 38409.0,\n", + " 'plan_cost': 10600884.0,\n", + " 'rows': 38409.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time',\n", + " 'family_index': 28,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/Aggregation/13255/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 174,\n", + " 'real_time': 4172917.143655834,\n", + " 'cpu_time': 4152985.063218387,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 6760050.0,\n", + " 'output_rows': 13255.0,\n", + " 'plan_cost': 8085550.0,\n", + " 'rows': 13255.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time',\n", + " 'family_index': 29,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/HashJoin/17114/17114/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 204,\n", + " 'real_time': 2975623.8137077373,\n", + " 'cpu_time': 2955758.84803921,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 17114.0,\n", + " 'model_cost': 6760030.0,\n", + " 'output_rows': 17114.0,\n", + " 'plan_cost': 10182830.0,\n", + " 'rhs_rows': 17114.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time',\n", + " 'family_index': 30,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/NestedLoopJoin/311/311/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 121,\n", + " 'real_time': 5785900.01658852,\n", + " 'cpu_time': 5761547.933884307,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 311.0,\n", + " 'model_cost': 6770470.0,\n", + " 'output_rows': 311.0,\n", + " 'plan_cost': 6832670.0,\n", + " 'rhs_rows': 311.0},\n", + " {'name': 'OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time',\n", + " 'family_index': 31,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:6760000/NestedLoopCrossJoin/255/255/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 131,\n", + " 'real_time': 5343956.488694726,\n", + " 'cpu_time': 5282505.534351162,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 255.0,\n", + " 'model_cost': 6762600.0,\n", + " 'output_rows': 65025.0,\n", + " 'plan_cost': 6813600.0,\n", + " 'rhs_rows': 255.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time',\n", + " 'family_index': 32,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/SeqScan/122500/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 94,\n", + " 'real_time': 7067010.638569085,\n", + " 'cpu_time': 7008813.021276589,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 12250000.0,\n", + " 'output_rows': 122500.0,\n", + " 'plan_cost': 12250000.0,\n", + " 'rows': 122500.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/Filter/122500/real_time',\n", + " 'family_index': 33,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/Filter/122500/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 83,\n", + " 'real_time': 8754703.24080011,\n", + " 'cpu_time': 8704814.783132503,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 12250000.0,\n", + " 'output_rows': 61440.0,\n", + " 'plan_cost': 24500000.0,\n", + " 'rows': 122500.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/Projection/556818/real_time',\n", + " 'family_index': 34,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/Projection/556818/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 10,\n", + " 'real_time': 74966987.99950537,\n", + " 'cpu_time': 74476933.0000004,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 12249996.0,\n", + " 'output_rows': 556818.0,\n", + " 'plan_cost': 67931796.0,\n", + " 'rows': 556818.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/Sort/65536/real_time',\n", + " 'family_index': 35,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/Sort/65536/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 35,\n", + " 'real_time': 19754016.400215086,\n", + " 'cpu_time': 19632989.22857157,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 12255232.0,\n", + " 'output_rows': 65536.0,\n", + " 'plan_cost': 18808832.0,\n", + " 'rows': 65536.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time',\n", + " 'family_index': 36,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/Aggregation/24020/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 70,\n", + " 'real_time': 10917471.60036383,\n", + " 'cpu_time': 10837185.385714322,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 12250200.0,\n", + " 'output_rows': 24020.0,\n", + " 'plan_cost': 14652200.0,\n", + " 'rows': 24020.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time',\n", + " 'family_index': 37,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/HashJoin/31013/31013/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 106,\n", + " 'real_time': 6093818.783037418,\n", + " 'cpu_time': 6031365.8490565615,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 31013.0,\n", + " 'model_cost': 12250135.0,\n", + " 'output_rows': 31013.0,\n", + " 'plan_cost': 18452735.0,\n", + " 'rhs_rows': 31013.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time',\n", + " 'family_index': 38,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/NestedLoopJoin/418/418/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 70,\n", + " 'real_time': 10007415.528525598,\n", + " 'cpu_time': 9965044.400000015,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 418.0,\n", + " 'model_cost': 12230680.0,\n", + " 'output_rows': 418.0,\n", + " 'plan_cost': 12314280.0,\n", + " 'rhs_rows': 418.0},\n", + " {'name': 'OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time',\n", + " 'family_index': 39,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:12250000/NestedLoopCrossJoin/343/343/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 70,\n", + " 'real_time': 10467055.214186465,\n", + " 'cpu_time': 10353758.299999984,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 343.0,\n", + " 'model_cost': 12235496.0,\n", + " 'output_rows': 117649.0,\n", + " 'plan_cost': 12304096.0,\n", + " 'rhs_rows': 343.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time',\n", + " 'family_index': 40,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/SeqScan/260100/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 42,\n", + " 'real_time': 16761908.238376714,\n", + " 'cpu_time': 16627440.904761907,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 26010000.0,\n", + " 'output_rows': 260100.0,\n", + " 'plan_cost': 26010000.0,\n", + " 'rows': 260100.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/Filter/260100/real_time',\n", + " 'family_index': 41,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/Filter/260100/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 36,\n", + " 'real_time': 19593865.166017268,\n", + " 'cpu_time': 19459568.388888985,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 26010000.0,\n", + " 'output_rows': 130052.0,\n", + " 'plan_cost': 52020000.0,\n", + " 'rows': 260100.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time',\n", + " 'family_index': 42,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/Projection/1182273/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 5,\n", + " 'real_time': 159555687.20237353,\n", + " 'cpu_time': 158424515.1999994,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 26010006.0,\n", + " 'output_rows': 1182273.0,\n", + " 'plan_cost': 144237306.0,\n", + " 'rows': 1182273.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/Sort/131364/real_time',\n", + " 'family_index': 43,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/Sort/131364/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 13,\n", + " 'real_time': 56860475.53899698,\n", + " 'cpu_time': 56499320.46153886,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 26010072.0,\n", + " 'output_rows': 131364.0,\n", + " 'plan_cost': 39146472.0,\n", + " 'rows': 131364.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time',\n", + " 'family_index': 44,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/Aggregation/51000/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 21,\n", + " 'real_time': 34235903.47630948,\n", + " 'cpu_time': 33978390.333333306,\n", + " 'time_unit': 'ns',\n", + " 'model_cost': 26010000.0,\n", + " 'output_rows': 51000.0,\n", + " 'plan_cost': 31110000.0,\n", + " 'rows': 51000.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time',\n", + " 'family_index': 45,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/HashJoin/65848/65848/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 47,\n", + " 'real_time': 15134376.276809564,\n", + " 'cpu_time': 15002296.74468074,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 65848.0,\n", + " 'model_cost': 26009960.0,\n", + " 'output_rows': 65848.0,\n", + " 'plan_cost': 39179560.0,\n", + " 'rhs_rows': 65848.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time',\n", + " 'family_index': 46,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/NestedLoopJoin/610/610/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 31,\n", + " 'real_time': 21153396.581329647,\n", + " 'cpu_time': 21080711.03225805,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 610.0,\n", + " 'model_cost': 26047000.0,\n", + " 'output_rows': 610.0,\n", + " 'plan_cost': 26169000.0,\n", + " 'rhs_rows': 610.0},\n", + " {'name': 'OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time',\n", + " 'family_index': 47,\n", + " 'per_family_instance_index': 0,\n", + " 'run_name': 'OperatorCostMatched/target_cost:26010000/NestedLoopCrossJoin/500/500/real_time',\n", + " 'run_type': 'iteration',\n", + " 'repetitions': 1,\n", + " 'repetition_index': 0,\n", + " 'threads': 1,\n", + " 'iterations': 31,\n", + " 'real_time': 22172262.903393038,\n", + " 'cpu_time': 21945665.74193559,\n", + " 'time_unit': 'ns',\n", + " 'lhs_rows': 500.0,\n", + " 'model_cost': 26000000.0,\n", + " 'output_rows': 250000.0,\n", + " 'plan_cost': 26100000.0,\n", + " 'rhs_rows': 500.0}]}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import json\n", + "results = None\n", + "with open('/tmp/operator-cost-matched.json') as f:\n", + " results = json.load(f)\n", + "\n", + "results" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "operator-cost-matched-dataframe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
target_costoperatorleft_rowsright_rowsreal_time_msmodel_costplan_costmatching_error
4640000Aggregation1255NaN0.348253640050.0765550.07.812500e-05
1640000Filter6400NaN0.407352640000.01280000.00.000000e+00
5640000HashJoin16201620.00.246864639900.0963900.01.562500e-04
7640000NestedLoopCrossJoin7878.00.399168632736.0648336.01.135000e-02
6640000NestedLoopJoin9696.00.697766645120.0664320.08.000000e-03
2640000Projection29091NaN3.320695640002.03549102.03.125000e-06
0640000SeqScan6400NaN0.309954640000.0640000.00.000000e+00
3640000Sort4476NaN0.720259640068.01087668.01.062500e-04
121000000Aggregation1961NaN0.5315811000110.01196210.01.100000e-04
91000000Filter10000NaN0.6578611000000.02000000.00.000000e+00
131000000HashJoin25322532.00.3964041000140.01506540.01.400000e-04
151000000NestedLoopCrossJoin9898.00.592521998816.01018416.01.184000e-03
141000000NestedLoopJoin120120.00.9828481008000.01032000.08.000000e-03
101000000Projection45455NaN5.4920131000010.05545510.01.000000e-05
81000000SeqScan10000NaN0.3805901000000.01000000.00.000000e+00
111000000Sort6993NaN1.165643999999.01699299.01.000000e-06
203240000Aggregation6353NaN1.8313553240030.03875330.09.259259e-06
173240000Filter32400NaN2.1304913240000.06480000.00.000000e+00
213240000HashJoin82038203.01.2976063240185.04880785.05.709877e-05
233240000NestedLoopCrossJoin177177.01.8233923258216.03293616.05.622222e-03
223240000NestedLoopJoin215215.02.7990413235750.03278750.01.311728e-03
183240000Projection147273NaN19.3550473240006.017967306.01.851852e-06
163240000SeqScan32400NaN1.1456533240000.03240000.00.000000e+00
193240000Sort19636NaN4.0484763239940.05203540.01.851852e-05
286760000Aggregation13255NaN4.1729176760050.08085550.07.396450e-06
256760000Filter67600NaN4.6280396760000.013520000.00.000000e+00
296760000HashJoin1711417114.02.9756246760030.010182830.04.437870e-06
316760000NestedLoopCrossJoin255255.05.3439566762600.06813600.03.846154e-04
306760000NestedLoopJoin311311.05.7859006770470.06832670.01.548817e-03
266760000Projection307273NaN38.2399996760006.037487306.08.875740e-07
246760000SeqScan67600NaN3.5594256760000.06760000.00.000000e+00
276760000Sort38409NaN10.1694956759984.010600884.02.366864e-06
3612250000Aggregation24020NaN10.91747212250200.014652200.01.632653e-05
3312250000Filter122500NaN8.75470312250000.024500000.00.000000e+00
3712250000HashJoin3101331013.06.09381912250135.018452735.01.102041e-05
3912250000NestedLoopCrossJoin343343.010.46705512235496.012304096.01.184000e-03
3812250000NestedLoopJoin418418.010.00741612230680.012314280.01.577143e-03
3412250000Projection556818NaN74.96698812249996.067931796.03.265306e-07
3212250000SeqScan122500NaN7.06701112250000.012250000.00.000000e+00
3512250000Sort65536NaN19.75401612255232.018808832.04.271020e-04
4426010000Aggregation51000NaN34.23590326010000.031110000.00.000000e+00
4126010000Filter260100NaN19.59386526010000.052020000.00.000000e+00
4526010000HashJoin6584865848.015.13437626009960.039179560.01.537870e-06
4726010000NestedLoopCrossJoin500500.022.17226326000000.026100000.03.844675e-04
4626010000NestedLoopJoin610610.021.15339726047000.026169000.01.422530e-03
4226010000Projection1182273NaN159.55568726010006.0144237306.02.306805e-07
4026010000SeqScan260100NaN16.76190826010000.026010000.00.000000e+00
4326010000Sort131364NaN56.86047626010072.039146472.02.768166e-06
\n", + "
" + ], + "text/plain": [ + " target_cost operator left_rows right_rows real_time_ms \\\n", + "4 640000 Aggregation 1255 NaN 0.348253 \n", + "1 640000 Filter 6400 NaN 0.407352 \n", + "5 640000 HashJoin 1620 1620.0 0.246864 \n", + "7 640000 NestedLoopCrossJoin 78 78.0 0.399168 \n", + "6 640000 NestedLoopJoin 96 96.0 0.697766 \n", + "2 640000 Projection 29091 NaN 3.320695 \n", + "0 640000 SeqScan 6400 NaN 0.309954 \n", + "3 640000 Sort 4476 NaN 0.720259 \n", + "12 1000000 Aggregation 1961 NaN 0.531581 \n", + "9 1000000 Filter 10000 NaN 0.657861 \n", + "13 1000000 HashJoin 2532 2532.0 0.396404 \n", + "15 1000000 NestedLoopCrossJoin 98 98.0 0.592521 \n", + "14 1000000 NestedLoopJoin 120 120.0 0.982848 \n", + "10 1000000 Projection 45455 NaN 5.492013 \n", + "8 1000000 SeqScan 10000 NaN 0.380590 \n", + "11 1000000 Sort 6993 NaN 1.165643 \n", + "20 3240000 Aggregation 6353 NaN 1.831355 \n", + "17 3240000 Filter 32400 NaN 2.130491 \n", + "21 3240000 HashJoin 8203 8203.0 1.297606 \n", + "23 3240000 NestedLoopCrossJoin 177 177.0 1.823392 \n", + "22 3240000 NestedLoopJoin 215 215.0 2.799041 \n", + "18 3240000 Projection 147273 NaN 19.355047 \n", + "16 3240000 SeqScan 32400 NaN 1.145653 \n", + "19 3240000 Sort 19636 NaN 4.048476 \n", + "28 6760000 Aggregation 13255 NaN 4.172917 \n", + "25 6760000 Filter 67600 NaN 4.628039 \n", + "29 6760000 HashJoin 17114 17114.0 2.975624 \n", + "31 6760000 NestedLoopCrossJoin 255 255.0 5.343956 \n", + "30 6760000 NestedLoopJoin 311 311.0 5.785900 \n", + "26 6760000 Projection 307273 NaN 38.239999 \n", + "24 6760000 SeqScan 67600 NaN 3.559425 \n", + "27 6760000 Sort 38409 NaN 10.169495 \n", + "36 12250000 Aggregation 24020 NaN 10.917472 \n", + "33 12250000 Filter 122500 NaN 8.754703 \n", + "37 12250000 HashJoin 31013 31013.0 6.093819 \n", + "39 12250000 NestedLoopCrossJoin 343 343.0 10.467055 \n", + "38 12250000 NestedLoopJoin 418 418.0 10.007416 \n", + "34 12250000 Projection 556818 NaN 74.966988 \n", + "32 12250000 SeqScan 122500 NaN 7.067011 \n", + "35 12250000 Sort 65536 NaN 19.754016 \n", + "44 26010000 Aggregation 51000 NaN 34.235903 \n", + "41 26010000 Filter 260100 NaN 19.593865 \n", + "45 26010000 HashJoin 65848 65848.0 15.134376 \n", + "47 26010000 NestedLoopCrossJoin 500 500.0 22.172263 \n", + "46 26010000 NestedLoopJoin 610 610.0 21.153397 \n", + "42 26010000 Projection 1182273 NaN 159.555687 \n", + "40 26010000 SeqScan 260100 NaN 16.761908 \n", + "43 26010000 Sort 131364 NaN 56.860476 \n", + "\n", + " model_cost plan_cost matching_error \n", + "4 640050.0 765550.0 7.812500e-05 \n", + "1 640000.0 1280000.0 0.000000e+00 \n", + "5 639900.0 963900.0 1.562500e-04 \n", + "7 632736.0 648336.0 1.135000e-02 \n", + "6 645120.0 664320.0 8.000000e-03 \n", + "2 640002.0 3549102.0 3.125000e-06 \n", + "0 640000.0 640000.0 0.000000e+00 \n", + "3 640068.0 1087668.0 1.062500e-04 \n", + "12 1000110.0 1196210.0 1.100000e-04 \n", + "9 1000000.0 2000000.0 0.000000e+00 \n", + "13 1000140.0 1506540.0 1.400000e-04 \n", + "15 998816.0 1018416.0 1.184000e-03 \n", + "14 1008000.0 1032000.0 8.000000e-03 \n", + "10 1000010.0 5545510.0 1.000000e-05 \n", + "8 1000000.0 1000000.0 0.000000e+00 \n", + "11 999999.0 1699299.0 1.000000e-06 \n", + "20 3240030.0 3875330.0 9.259259e-06 \n", + "17 3240000.0 6480000.0 0.000000e+00 \n", + "21 3240185.0 4880785.0 5.709877e-05 \n", + "23 3258216.0 3293616.0 5.622222e-03 \n", + "22 3235750.0 3278750.0 1.311728e-03 \n", + "18 3240006.0 17967306.0 1.851852e-06 \n", + "16 3240000.0 3240000.0 0.000000e+00 \n", + "19 3239940.0 5203540.0 1.851852e-05 \n", + "28 6760050.0 8085550.0 7.396450e-06 \n", + "25 6760000.0 13520000.0 0.000000e+00 \n", + "29 6760030.0 10182830.0 4.437870e-06 \n", + "31 6762600.0 6813600.0 3.846154e-04 \n", + "30 6770470.0 6832670.0 1.548817e-03 \n", + "26 6760006.0 37487306.0 8.875740e-07 \n", + "24 6760000.0 6760000.0 0.000000e+00 \n", + "27 6759984.0 10600884.0 2.366864e-06 \n", + "36 12250200.0 14652200.0 1.632653e-05 \n", + "33 12250000.0 24500000.0 0.000000e+00 \n", + "37 12250135.0 18452735.0 1.102041e-05 \n", + "39 12235496.0 12304096.0 1.184000e-03 \n", + "38 12230680.0 12314280.0 1.577143e-03 \n", + "34 12249996.0 67931796.0 3.265306e-07 \n", + "32 12250000.0 12250000.0 0.000000e+00 \n", + "35 12255232.0 18808832.0 4.271020e-04 \n", + "44 26010000.0 31110000.0 0.000000e+00 \n", + "41 26010000.0 52020000.0 0.000000e+00 \n", + "45 26009960.0 39179560.0 1.537870e-06 \n", + "47 26000000.0 26100000.0 3.844675e-04 \n", + "46 26047000.0 26169000.0 1.422530e-03 \n", + "42 26010006.0 144237306.0 2.306805e-07 \n", + "40 26010000.0 26010000.0 0.000000e+00 \n", + "43 26010072.0 39146472.0 2.768166e-06 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "matched_df = pd.DataFrame(results['benchmarks'])\n", + "matched_df = matched_df[matched_df['run_type'] == 'iteration'].copy()\n", + "\n", + "parts = matched_df['name'].str.split('/')\n", + "matched_df['target_cost'] = parts.str[1].str.removeprefix('target_cost:').astype(int)\n", + "matched_df['operator'] = parts.str[2]\n", + "matched_df['left_rows'] = parts.str[3].astype(int)\n", + "matched_df['right_rows'] = parts.apply(lambda p: int(p[4]) if len(p) == 6 else None)\n", + "matched_df['real_time_ms'] = matched_df['real_time'] / 1e6\n", + "\n", + "matched_df['matching_error'] = (matched_df['model_cost'] - matched_df['target_cost']).abs() / matched_df['target_cost']\n", + "assert (matched_df['matching_error'] < 0.02).all()\n", + "\n", + "matched_df[['target_cost', 'operator', 'left_rows', 'right_rows', 'real_time_ms', 'model_cost', 'plan_cost', 'matching_error']].sort_values(['target_cost', 'operator'])" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "operator-cost-matched-plot", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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nJzM0aNHKVasmC6peBE5jcvb21uXVAC4ublx69atF25XCCFEwZP6+BGHf13HgV9Wk/ooBYCydYOo2yUUu0IyPk8IQ5LE4gWYmJszePHq59a7duYka6eOe269diPHUaxshRy1m1sNGjRg3rx5mJqa4u7urjdA29raWq+uUirLrlbZlcOTuwTjx4+nXbt2mZZZWFjkyUDvnMb19OBzjUaDVqt96faFEEIYnlKKv6J3smt5OPfv3gbArZQvDUL741bK18DRCSFAEosXotFocnSb1atSZWycCmUauP1vts6F8KpUOd+mvrO2tqZkyZI5qluuXDmuXLnC1atXdXcHTp8+TUJCAmXLls1ynSpVqnD27Nls26hYsSLXrl3j3LlzWd61MDMzIz392VPvvkhcQggh3hzXz/1F5JLvuXH+yXg8W+fC1O8Whm+d+i899lAIkXckschHRkbGvBPWn/WzJmdbp0Fo/wIzn3ajRo2oWLEi3bp1Y/bs2bpB0oGBgVSrVi3LdcaMGUOLFi3w8PCgY8eOGBkZcfz4cU6cOMHnn39OYGAg9evXp3379syaNYuSJUvy119/odFoaNKkCd7e3iQlJbFt2zYqVaqElZUVVlZWLx2XEEKI11/indvs+jGcv/ZEAWBqbkGN1h2o2rItpma5v4svhMhfMl1CPitVsw6tho7CxqmQXrmtcyFaDR2V78+xyA2NRkNERASOjo7Ur1+fRo0a4ePjw8qVK7NdJzg4mA0bNrBlyxaqV69OrVq1mDVrFl5eXro6a9asoXr16nTp0oVy5crxySef6O5S1KlTh//85z906tSJwoULM3369DyJSwghxOsrNSWFPauWs+ij/+iSivKBjeg9+1tqte8sSYUQBZRGKaUMHYShJSYmYm9vT0JCAnZ2dnrLUlJSiImJoXjx4li8xCwTWm06/5w5RVJ8HDYOjhQtW77A3KnIjW+//ZaJEydy7do1Q4ciXrG8ei8IIUR2lFbL6V072L1iMUlx9wAoWqY8DUL74eKTs269Qoi89azr5KdJV6hXxMjIGI/yFQ0dxku5evUqGzdupHz58oYORQghxBvm2l+niFz8Azf/Pg+AfREX6nfvTakadWQchRCvCUksRI5VqVKFokWLEh4ebuhQhBBCvCESbsWyc3k45/btBsDM0pKabTtRpWkrTMzMDBydECI3JLEQOXb79m1D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StSI7t27q2eeeaZMWdu2bdUbb7xR4fqvvfaaatu2bZmyp59+WvXs2bPGYmwoqlvXFYmMjFQTJ0683aE1KDdbzyNHjlR///vf1TvvvKOio6NrMMKGo7p1/euvvypPT0+VmZlZG+E1GNWt548++kiFh4eXKfvss89UaGhojcXY0ABq2bJl111Hroe3rir1XBG5FlZfdeq6MVwP5YlFDTCZTOzZs4chQ4aUKR8yZAjbt2+vcJvffvut3Pp33XUXu3fvpqSkpMZire9upq6vZbVaycvLw9vbuyZCbBButp5nzJjBiRMneOedd2o6xAbjZur6xx9/pGvXrnz44YeEhITQunVrXn31VYqKimoj5HrpZuq5d+/enD17lhUrVqCUIi0tjSVLlnDffffVRsiNhlwP7UOuhTWrsVwPHewdQEOUkZGBxWIhICCgTHlAQACpqakVbpOamlrh+mazmYyMDIKCgmos3vrsZur6Wh9//DEFBQWMGDGiJkJsEG6mno8dO8Ybb7zBli1bcHCQj5qqupm6PnnyJFu3bsXJyYlly5aRkZHBs88+y8WLF6WfRSVupp579+7N3LlzGTlyJMXFxZjNZoYOHcp///vf2gi50ZDroX3ItbDmNKbroTyxqEEajabMa6VUubIbrV9RuSivunV92fz583n33XdZuHAh/v7+NRVeg1HVerZYLIwePZqJEyfSunXr2gqvQanOOW21WtFoNMydO5fu3btz7733MmXKFGbOnClPLW6gOvWcmJjICy+8wNtvv82ePXtYuXIlSUlJPPPMM7URaqMi18PaJdfCmtPYrocNO22yE19fX3Q6XblvvdLT08t9C3NZYGBghes7ODjg4+NTY7HWdzdT15ctXLiQJ554gsWLFzNo0KCaDLPeq2495+XlsXv3buLi4nj++eeB0ptfpRQODg6sXr2aO++8s1Zir29u5pwOCgoiJCQET09PW1m7du1QSnH27FkiIiJqNOb66GbqefLkyfTp04e//vWvAHTs2BFXV1f69u3Le++9J9+k3yZyPaxdci2sWY3teihPLGqAo6MjXbp0Yc2aNWXK16xZQ+/evSvcplevXuXWX716NV27dkWv19dYrPXdzdQ1lH47M378eObNmyfto6uguvXs4eHB/v37iY+Pt/0888wztGnThvj4eHr06FFbodc7N3NO9+nTh/Pnz5Ofn28rO3r0KFqtltDQ0BqNt766mXouLCxEqy172dTpdMCVb9TFrZPrYe2Ra2HNa3TXQ/v0GW/4FixYoPR6vZo+fbpKTExUL774onJ1dVWnTp1SSin1xhtvqLFjx9rWP3nypHJxcVEvvfSSSkxMVNOnT1d6vV4tWbLEXodQb1S3rufNm6ccHBzU1KlTVUpKiu0nOzvbXodQL1S3nq/VkEfBuN2qW9d5eXkqNDRUPfzww+rgwYNq06ZNKiIiQj355JP2OoR6obr1PGPGDOXg4KC++OILdeLECbV161bVtWtX1b17d3sdQr2Ql5en4uLiVFxcnALUlClTVFxcnEpOTlZKyfXwdqluPcu18OZVt66v1ZCvh5JY1KCpU6eqsLAw5ejoqDp37qw2bdpkW/b444+r2NjYMutv3LhRxcTEKEdHR9W8eXP15Zdf1nLE9Vd16jo2NlYB5X4ef/zx2g+8nqnuOX21hvxBWhOqW9eHDh1SgwYNUs7Ozio0NFS9/PLLqrCwsJajrn+qW8+fffaZioyMVM7OziooKEg99thj6uzZs7Ucdf2yYcOG637myvXw9qhuPcu18ObdzDl9tYZ8PdQoJc9vhRBCCCGEELdG+lgIIYQQQgghbpkkFkIIIYQQQohbJomFEEIIIYQQ4pZJYiGEEEIIIYS4ZZJYCCGEEEIIIW6ZJBZCCCGEEEKIWyaJhRBCCCGEEOKWSWIhhBBCCCFEPbV582buv/9+goOD0Wg0LF++vNr7WLVqFT179sTd3R0/Pz8eeughkpKSqr0fSSyEEOU0b96cTz/91N5h2EW/fv2YN2+e7fXNfkjfrJkzZ+Ll5VVr7yfqphv9DZ46dQqNRkN8fDwAGzduRKPRkJ2dXek2cm7duocffpgpU6bYOwwhyigoKCA6OprPP//8prY/efIkw4YN48477yQ+Pp5Vq1aRkZHB8OHDq70vSSyEEA1adW6mfv75Z1JTUxk1alTNBtXA1efE9N1336VTp072DuOGmjZtSkpKClFRUfYOpd66mUTr7bff5v333yc3N7dmghLiJtxzzz289957lSYCJpOJ1157jZCQEFxdXenRowcbN260Ld+7dy8Wi4X33nuPli1b0rlzZ1599VUSEhIoKSmpViySWAghxCWfffYZEyZMQKuVj8ZrKaUwm821+p4mk6lW3+9WjB8/nnfffbfW3k+n0xEYGIiDg0OtvaeAjh070rx5c+bOnWvvUISosgkTJrBt2zYWLFjAvn37eOSRR7j77rs5duwYAF27dkWn0zFjxgwsFgs5OTnMmTOHIUOGoNfrq/VecvUUopHp378/zz//PM8//zxeXl74+Pjw97//HaVUpdtMmTKFDh064OrqStOmTXn22WfJz8+3Lb/8zd+qVato164dbm5u3H333aSkpFQ5rm+//Zb27dtjMBgICgri+eefty07ffo0w4YNw83NDQ8PD0aMGEFaWppteUJCAgMGDMDd3R0PDw+6dOnC7t272bhxIxMmTCAnJweNRoNGo6n05i8jI4O1a9cydOjQ68a5f/9+7rzzTpydnfHx8eFPf/pTmbq40bHcqC6r4kYxjB8/ngceeICJEyfi7++Ph4cHTz/9dJkbdaUUH374IeHh4Tg7OxMdHc2SJUtsyy83rVm1ahVdu3bFYDCwZcsWTpw4wbBhwwgICMDNzY1u3bqxdu1a23b9+/cnOTmZl156yVbnly1dutRWL82bN+fjjz8uc1zNmzfnvffeY/z48Xh6evLUU09VePxWq5UPPviAVq1aYTAYaNasGe+//36V62fjxo10794dV1dXvLy86NOnD8nJycycOZOJEyeSkJBgi33mzJnV+t1Ux48//kjXrl1xcnLC19e33LeNhYWF/PGPf8Td3Z1mzZoxbdo027Jrm0JVZObMmTRr1gwXFxcefPBBMjMzrxuPyWTi+eefJygoCCcnJ5o3b87kyZNty7Ozs/nTn/5EQEAATk5OREVF8fPPPwOQmZnJo48+SmhoKC4uLnTo0IH58+eX2X///v154YUXeO211/D29iYwMLBKyVhtfzZ88cUXRERE4OTkREBAAA8//HCZeIYOHVru2ISoq06cOMH8+fNZvHgxffv2pWXLlrz66qvccccdzJgxAyj97F29ejV/+9vfMBgMeHl5cfbsWRYsWFD9N1RCiEYlNjZWubm5qb/85S/q8OHD6rvvvlMuLi5q2rRptnXCwsLUJ598Ynv9ySefqPXr16uTJ0+qdevWqTZt2qj/+7//sy2fMWOG0uv1atCgQWrXrl1qz549ql27dmr06NFViumLL75QTk5O6tNPP1VHjhxRO3futL2/1WpVMTEx6o477lC7d+9WO3bsUJ07d1axsbG27du3b6/GjBmjDh06pI4ePaoWLVqk4uPjldFo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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(8, 5))\n", + "for op, grp in matched_df.groupby('operator'):\n", + " grp = grp.sort_values('plan_cost')\n", + " ax.plot(grp['plan_cost'], grp['real_time_ms'], marker='o', label=op)\n", + "\n", + "ax.set_xlabel('plan_cost (local operator cost + child scan costs)')\n", + "ax.set_ylabel('real_time (ms)')\n", + "ax.set_title('Measured time vs plan_cost for local-cost-matched operators')\n", + "ax.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "operator-cost-matched-heatmaps", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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RkZGNHfuXDp79iwdOnSIfvnlF/L395edPwBycXGhli1b0tGjR+no0aNUqVIlsrCwoLi4ONmx/P39SVdXlxo0aED79u2jM2fOUP/+/RVaOx8/fkyGhoZUvnx52rNnDx07doxatGhBJUqUUOlu465duwgANW/enI4ePUr79u2jatWqka6uLl25ckWWTtrK6+zsTJMmTaKzZ8/SypUrZZ/xt12kFixYQAKBgAYOHEgnT56kw4cPU506dcjIyIgeP34sS9evXz/S0dEhFxcXWrRoEV28eJHOnj1LRETr16+nRYsW0fHjxykwMJC2b99OHh4eVK5cOdmxPn78SGPGjJHrmhkUFETx8fFERDR06FACQKNHj6YzZ87Qhg0byMbGhooXLy7XWu3l5UWWlpZUvHhxWrNmDQUEBFBgYGCu1+u356HKe02keGf12bNnZGJiQqVLlyZfX186deoU9ejRgwDQkiVLZOnCwsLIysqKSpQoQdu2bSM/Pz/q06cPubi4KNwFmzt3LmlpaVFiYqLcsVXJQwICAggAFS9enDp06EAnTpygnTt3UpkyZcjU1JRev34t974puzvRr18/hTsspUqVUnr3SpXP+Vvu7u7UuXNnheVNmzalnj17Fulz9fLyIisrK3J0dKTVq1fT2bNnaezYsQSARo0apZB+yZIlJBQK5a41IkmLvrGxMY0fP15hm7xQdldy4cKFBIB69OhBp06dIl9fXypVqhSZmZnRixcvZOn++usvAkBdunShkydP0q5du6hs2bLk7Oz8Q94fZd+jESNG0N69e+nSpUt08uRJGjRoEAmFQrnvhjp5cG5UyfNTU1OpcuXKZGRkRMuXL6dz587RjBkzSFtbm1q3bi3b1/Xr10kgENDPP/9Mfn5+5O/vT3///Tf16dOHiCR3gKWfz/Tp02V53cePH4mIyNfXlwDQkydP5GJUp05ARLRv3z4CQA8ePJBbfvXqVQIg95k7OzuTk5MTlShRgrZu3Up+fn7Uq1cvhbs0ynooBAYG0oQJE+jgwYMUGBhIR44coY4dO5KBgYFct3npd9TFxYV69epFp06doj179lCJEiXI1dWVRCKRyp+XKnlCVFQUOTk5kY2NDW3YsIHOnDlDo0ePJgByd/327NlDAGjMmDF07tw5unDhAm3YsIHGjh0r24/0u7N27VrZ5xUVFUVE2efR0s+4ePHi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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "matched_pivot = matched_df.set_index(['target_cost', 'operator'])[['cpu_time', 'model_cost', 'plan_cost']]\n", + "matched_costs = sorted(matched_df['target_cost'].unique())\n", + "\n", + "ncols = 2\n", + "nrows = (len(matched_costs) + ncols - 1) // ncols\n", + "fig, axes = plt.subplots(nrows, ncols, figsize=(5 * ncols, 4.5 * nrows), squeeze=False)\n", + "\n", + "for idx, target_cost in enumerate(matched_costs):\n", + " ax = axes[idx // ncols][idx % ncols]\n", + " sub = matched_pivot.xs(target_cost, level='target_cost')\n", + " ops = sub.index.tolist()\n", + " m = len(ops)\n", + "\n", + " mat = np.zeros((m, m))\n", + " for i, a in enumerate(ops):\n", + " for j, b in enumerate(ops):\n", + " if i != j:\n", + " mat[i, j] = (np.log(sub.loc[a, 'cpu_time']) - np.log(sub.loc[b, 'cpu_time']) -\n", + " (np.log(sub.loc[a, 'plan_cost']) - np.log(sub.loc[b, 'plan_cost'])))\n", + "\n", + " vmax = np.abs(mat).max() or 1.0\n", + " im = ax.imshow(mat, cmap='coolwarm', vmin=-vmax, vmax=vmax)\n", + " ax.set_xticks(range(m)); ax.set_xticklabels(ops, rotation=45, ha='right', fontsize=8)\n", + " ax.set_yticks(range(m)); ax.set_yticklabels(ops, fontsize=8)\n", + " ax.set_title(f'target_cost = {target_cost:,}', fontsize=10)\n", + " plt.colorbar(im, ax=ax, shrink=0.8)\n", + " for i in range(m):\n", + " for j in range(m):\n", + " ax.text(j, i, f'{mat[i,j]:.2f}', ha='center', va='center', fontsize=7,\n", + " color='black' if abs(mat[i,j]) < vmax * 0.6 else 'white')\n", + "\n", + "for idx in range(len(matched_costs), nrows * ncols):\n", + " axes[idx // ncols][idx % ncols].set_visible(False)\n", + "\n", + "fig.suptitle('Local-cost-matched operators: log(cpu(a)/cpu(b)) - log(plan_cost(a)/plan_cost(b)) [0 = perfect, +/-ln2 about 0.69 = 2x off]', fontsize=12)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "operator-cost-matched-comparison-table", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
target_cost6400001000000324000067600001225000026010000
ab
AggregationFilter0.3590.3020.3640.4110.7331.071
HashJoin0.5750.5250.5770.5710.8171.048
NestedLoopCrossJoin-0.297-0.267-0.153-0.412-0.1290.262
NestedLoopJoin-0.834-0.762-0.592-0.496-0.0900.304
Projection-0.717-0.798-0.820-0.680-0.394-0.006
SeqScan-0.0560.1590.292-0.0150.2570.536
Sort-0.374-0.434-0.498-0.620-0.344-0.279
FilterAggregation-0.359-0.302-0.364-0.411-0.733-1.071
HashJoin0.2160.2230.2130.1590.083-0.023
NestedLoopCrossJoin-0.656-0.569-0.517-0.823-0.862-0.810
NestedLoopJoin-1.193-1.063-0.956-0.907-0.823-0.767
Projection-1.076-1.099-1.184-1.091-1.127-1.077
SeqScan-0.416-0.143-0.071-0.426-0.476-0.536
Sort-0.733-0.735-0.862-1.031-1.078-1.350
HashJoinAggregation-0.575-0.525-0.577-0.571-0.817-1.048
Filter-0.216-0.223-0.213-0.159-0.0830.023
NestedLoopCrossJoin-0.872-0.792-0.730-0.982-0.946-0.787
NestedLoopJoin-1.409-1.287-1.169-1.066-0.907-0.744
Projection-1.291-1.322-1.397-1.250-1.210-1.054
SeqScan-0.631-0.366-0.285-0.585-0.560-0.513
Sort-0.949-0.958-1.075-1.190-1.161-1.327
NestedLoopCrossJoinAggregation0.2970.2670.1530.4120.129-0.262
Filter0.6560.5690.5170.8230.8620.810
HashJoin0.8720.7920.7300.9820.9460.787
NestedLoopJoin-0.537-0.495-0.439-0.0840.0390.043
Projection-0.420-0.531-0.667-0.268-0.265-0.267
SeqScan0.2410.4260.4450.3970.3860.274
Sort-0.077-0.167-0.345-0.208-0.215-0.540
NestedLoopJoinAggregation0.8340.7620.5920.4960.090-0.304
Filter1.1931.0630.9560.9070.8230.767
HashJoin1.4091.2871.1691.0660.9070.744
NestedLoopCrossJoin0.5370.4950.4390.084-0.039-0.043
Projection0.118-0.036-0.228-0.184-0.304-0.310
SeqScan0.7780.9210.8840.4810.3470.231
Sort0.4600.3280.094-0.124-0.255-0.583
ProjectionAggregation0.7170.7980.8200.6800.3940.006
Filter1.0761.0991.1841.0911.1271.077
HashJoin1.2911.3221.3971.2501.2101.054
NestedLoopCrossJoin0.4200.5310.6670.2680.2650.267
NestedLoopJoin-0.1180.0360.2280.1840.3040.310
SeqScan0.6600.9561.1130.6650.6500.541
Sort0.3430.3640.3220.0600.049-0.273
SeqScanAggregation0.056-0.159-0.2920.015-0.257-0.536
Filter0.4160.1430.0710.4260.4760.536
HashJoin0.6310.3660.2850.5850.5600.513
NestedLoopCrossJoin-0.241-0.426-0.445-0.397-0.386-0.274
NestedLoopJoin-0.778-0.921-0.884-0.481-0.347-0.231
Projection-0.660-0.956-1.113-0.665-0.650-0.541
Sort-0.318-0.593-0.791-0.605-0.601-0.814
SortAggregation0.3740.4340.4980.6200.3440.279
Filter0.7330.7350.8621.0311.0781.350
HashJoin0.9490.9581.0751.1901.1611.327
NestedLoopCrossJoin0.0770.1670.3450.2080.2150.540
NestedLoopJoin-0.460-0.328-0.0940.1240.2550.583
Projection-0.343-0.364-0.322-0.060-0.0490.273
SeqScan0.3180.5930.7910.6050.6010.814
\n", + "
" + ], + "text/plain": [ + "target_cost 640000 1000000 3240000 \\\n", + "a b \n", + "Aggregation Filter 0.359 0.302 0.364 \n", + " HashJoin 0.575 0.525 0.577 \n", + " NestedLoopCrossJoin -0.297 -0.267 -0.153 \n", + " NestedLoopJoin -0.834 -0.762 -0.592 \n", + " Projection -0.717 -0.798 -0.820 \n", + " SeqScan -0.056 0.159 0.292 \n", + " Sort -0.374 -0.434 -0.498 \n", + "Filter Aggregation -0.359 -0.302 -0.364 \n", + " HashJoin 0.216 0.223 0.213 \n", + " NestedLoopCrossJoin -0.656 -0.569 -0.517 \n", + " NestedLoopJoin -1.193 -1.063 -0.956 \n", + " Projection -1.076 -1.099 -1.184 \n", + " SeqScan -0.416 -0.143 -0.071 \n", + " Sort -0.733 -0.735 -0.862 \n", + "HashJoin Aggregation -0.575 -0.525 -0.577 \n", + " Filter -0.216 -0.223 -0.213 \n", + " NestedLoopCrossJoin -0.872 -0.792 -0.730 \n", + " NestedLoopJoin -1.409 -1.287 -1.169 \n", + " Projection -1.291 -1.322 -1.397 \n", + " SeqScan -0.631 -0.366 -0.285 \n", + " Sort -0.949 -0.958 -1.075 \n", + "NestedLoopCrossJoin Aggregation 0.297 0.267 0.153 \n", + " Filter 0.656 0.569 0.517 \n", + " HashJoin 0.872 0.792 0.730 \n", + " NestedLoopJoin -0.537 -0.495 -0.439 \n", + " Projection -0.420 -0.531 -0.667 \n", + " SeqScan 0.241 0.426 0.445 \n", + " Sort -0.077 -0.167 -0.345 \n", + "NestedLoopJoin Aggregation 0.834 0.762 0.592 \n", + " Filter 1.193 1.063 0.956 \n", + " HashJoin 1.409 1.287 1.169 \n", + " NestedLoopCrossJoin 0.537 0.495 0.439 \n", + " Projection 0.118 -0.036 -0.228 \n", + " SeqScan 0.778 0.921 0.884 \n", + " Sort 0.460 0.328 0.094 \n", + "Projection Aggregation 0.717 0.798 0.820 \n", + " Filter 1.076 1.099 1.184 \n", + " HashJoin 1.291 1.322 1.397 \n", + " NestedLoopCrossJoin 0.420 0.531 0.667 \n", + " NestedLoopJoin -0.118 0.036 0.228 \n", + " SeqScan 0.660 0.956 1.113 \n", + " Sort 0.343 0.364 0.322 \n", + "SeqScan Aggregation 0.056 -0.159 -0.292 \n", + " Filter 0.416 0.143 0.071 \n", + " HashJoin 0.631 0.366 0.285 \n", + " NestedLoopCrossJoin -0.241 -0.426 -0.445 \n", + " NestedLoopJoin -0.778 -0.921 -0.884 \n", + " Projection -0.660 -0.956 -1.113 \n", + " Sort -0.318 -0.593 -0.791 \n", + "Sort Aggregation 0.374 0.434 0.498 \n", + " Filter 0.733 0.735 0.862 \n", + " HashJoin 0.949 0.958 1.075 \n", + " NestedLoopCrossJoin 0.077 0.167 0.345 \n", + " NestedLoopJoin -0.460 -0.328 -0.094 \n", + " Projection -0.343 -0.364 -0.322 \n", + " SeqScan 0.318 0.593 0.791 \n", + "\n", + "target_cost 6760000 12250000 26010000 \n", + "a b \n", + "Aggregation Filter 0.411 0.733 1.071 \n", + " HashJoin 0.571 0.817 1.048 \n", + " NestedLoopCrossJoin -0.412 -0.129 0.262 \n", + " NestedLoopJoin -0.496 -0.090 0.304 \n", + " Projection -0.680 -0.394 -0.006 \n", + " SeqScan -0.015 0.257 0.536 \n", + " Sort -0.620 -0.344 -0.279 \n", + "Filter Aggregation -0.411 -0.733 -1.071 \n", + " HashJoin 0.159 0.083 -0.023 \n", + " NestedLoopCrossJoin -0.823 -0.862 -0.810 \n", + " NestedLoopJoin -0.907 -0.823 -0.767 \n", + " Projection -1.091 -1.127 -1.077 \n", + " SeqScan -0.426 -0.476 -0.536 \n", + " Sort -1.031 -1.078 -1.350 \n", + "HashJoin Aggregation -0.571 -0.817 -1.048 \n", + " Filter -0.159 -0.083 0.023 \n", + " NestedLoopCrossJoin -0.982 -0.946 -0.787 \n", + " NestedLoopJoin -1.066 -0.907 -0.744 \n", + " Projection -1.250 -1.210 -1.054 \n", + " SeqScan -0.585 -0.560 -0.513 \n", + " Sort -1.190 -1.161 -1.327 \n", + "NestedLoopCrossJoin Aggregation 0.412 0.129 -0.262 \n", + " Filter 0.823 0.862 0.810 \n", + " HashJoin 0.982 0.946 0.787 \n", + " NestedLoopJoin -0.084 0.039 0.043 \n", + " Projection -0.268 -0.265 -0.267 \n", + " SeqScan 0.397 0.386 0.274 \n", + " Sort -0.208 -0.215 -0.540 \n", + "NestedLoopJoin Aggregation 0.496 0.090 -0.304 \n", + " Filter 0.907 0.823 0.767 \n", + " HashJoin 1.066 0.907 0.744 \n", + " NestedLoopCrossJoin 0.084 -0.039 -0.043 \n", + " Projection -0.184 -0.304 -0.310 \n", + " SeqScan 0.481 0.347 0.231 \n", + " Sort -0.124 -0.255 -0.583 \n", + "Projection Aggregation 0.680 0.394 0.006 \n", + " Filter 1.091 1.127 1.077 \n", + " HashJoin 1.250 1.210 1.054 \n", + " NestedLoopCrossJoin 0.268 0.265 0.267 \n", + " NestedLoopJoin 0.184 0.304 0.310 \n", + " SeqScan 0.665 0.650 0.541 \n", + " Sort 0.060 0.049 -0.273 \n", + "SeqScan Aggregation 0.015 -0.257 -0.536 \n", + " Filter 0.426 0.476 0.536 \n", + " HashJoin 0.585 0.560 0.513 \n", + " NestedLoopCrossJoin -0.397 -0.386 -0.274 \n", + " NestedLoopJoin -0.481 -0.347 -0.231 \n", + " Projection -0.665 -0.650 -0.541 \n", + " Sort -0.605 -0.601 -0.814 \n", + "Sort Aggregation 0.620 0.344 0.279 \n", + " Filter 1.031 1.078 1.350 \n", + " HashJoin 1.190 1.161 1.327 \n", + " NestedLoopCrossJoin 0.208 0.215 0.540 \n", + " NestedLoopJoin 0.124 0.255 0.583 \n", + " Projection -0.060 -0.049 0.273 \n", + " SeqScan 0.605 0.601 0.814 " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "matched_rows = []\n", + "for target_cost in matched_costs:\n", + " sub = matched_pivot.xs(target_cost, level='target_cost')\n", + " ops = sub.index.tolist()\n", + " for a in ops:\n", + " for b in ops:\n", + " if a != b:\n", + " delta = (np.log(sub.loc[a, 'cpu_time']) - np.log(sub.loc[b, 'cpu_time']) -\n", + " (np.log(sub.loc[a, 'plan_cost']) - np.log(sub.loc[b, 'plan_cost'])))\n", + " matched_rows.append({'target_cost': target_cost, 'a': a, 'b': b, 'delta': delta})\n", + "\n", + "matched_comparison = pd.DataFrame(matched_rows)\n", + "matched_comparison.pivot_table(index=['a', 'b'], columns='target_cost', values='delta').round(3)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/research/research.pdf b/research/research.pdf new file mode 100644 index 0000000..e293216 Binary files /dev/null and b/research/research.pdf differ diff --git a/research/rule-lineage-random-1000-samples.csv b/research/rule-lineage-random-1000-samples.csv new file mode 100644 index 0000000..8956547 --- /dev/null +++ b/research/rule-lineage-random-1000-samples.csv @@ -0,0 +1,3204 @@ +seed,plan_cost,naive_plan_cost,exec_us,rows,lineage,query +0,2196,3466,192,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.price AS c2289 +FROM regions AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id;" +1,56100,56100,254,55,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.region_id AS c3748 +FROM customers AS t0 +WHERE t0.region_id = 7;" +2,110500,110500,305,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id, t0.id +FROM customers AS t0 +ORDER BY t0.id DESC, t0.region_id DESC;" +3,610,610,143,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM departments AS t0;" +4,574,574,193,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, SUM(t0.id) +FROM markets AS t0 +WHERE t0.id IS NULL +GROUP BY t0.id +ORDER BY t0.id DESC;" +5,35285800,35285800,55187,3,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.region, markets.id AS c7685 +FROM markets CROSS JOIN orders CROSS JOIN departments +GROUP BY markets.region, markets.id;" +6,111709,440694,314,4,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t1.id, t0.id AS c6716, t0.region_id +FROM customers AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN departments AS t2 ON t0.id = t2.id +WHERE t1.department_id <= 11;" +7,2188,5724,281,1,transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT departments.id +FROM departments RIGHT JOIN employees ON departments.id = employees.id +WHERE (employees.id != 33 AND employees.department_id IN (34, 74)) +GROUP BY departments.id +ORDER BY departments.id DESC;" +8,552500,552500,1173,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id, orders.id +FROM orders +WHERE (orders.id = 117 AND orders.id > 60) +ORDER BY orders.customer_id DESC, orders.id DESC;" +9,1351928,3939128,2497,2,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 2; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.customer_id AS c3674 +FROM orders AS t0 CROSS JOIN departments AS t1 +WHERE (t1.id >= 4 AND (t0.id IN (130) OR t1.id IS NULL));" +10,3639,6932,228,3,transformation 5 FilterPushdownThroughJoin: 3; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 2,"SELECT regions.id +FROM books RIGHT JOIN employees ON books.id = employees.id JOIN regions ON books.id = regions.id +WHERE books.price >= 6;" +11,5889,16616,226,22,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id +FROM users FULL JOIN employees ON users.id = employees.id +ORDER BY employees.department_id DESC;" +12,676416,1550366,1214,3,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT orders.id AS c2380, markets.region, markets.note +FROM markets JOIN orders ON markets.id = orders.id;" +13,2390300,2390300,2959,15000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT markets.region, orders.customer_id AS c8705, markets.note +FROM markets CROSS JOIN orders;" +14,2076,2076,135,10,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.department_id, t0.id AS c1827 +FROM employees AS t0 +WHERE (t0.department_id BETWEEN 5 AND 34 OR t0.id NOT IN (2, 68, 69));" +15,422,422,229,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.price +FROM books +WHERE (books.id < 2 AND (books.price NOT BETWEEN 41 AND 55 OR books.price IN (57, 63, 77, 77)));" +16,5405,16132,184,22,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.age AS c4948 +FROM users AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id;" +17,14805,29510,354,4,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age, COUNT(departments.id), COUNT(*) +FROM regions JOIN users ON regions.id = users.id CROSS JOIN departments +WHERE (users.age >= 52 OR regions.id IN (4, 9)) +GROUP BY users.age;" +18,102500,102500,291,436,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id +FROM customers +WHERE customers.region_id != 8 +ORDER BY customers.region_id ASC;" +19,2736,4463,264,2,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t0.id +FROM books AS t0 RIGHT JOIN regions AS t1 ON t0.id = t1.id JOIN markets AS t2 ON t1.id = t2.id +WHERE t1.id < 3 +ORDER BY t0.id ASC;" +20,31196040,49896040,20899,85,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT orders.id AS c5497, orders.customer_id, departments.id AS c8484 +FROM departments CROSS JOIN users CROSS JOIN orders +WHERE orders.id = 82;" +21,1220,1220,123,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions;" +22,3740,3740,220,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.department_id +FROM employees AS t0 +GROUP BY t0.department_id +ORDER BY t0.department_id ASC;" +23,109562,155822,402,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id +FROM customers LEFT JOIN books ON customers.id = books.id +GROUP BY customers.region_id +ORDER BY customers.region_id ASC;" +24,39522,56902,652,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.id, COUNT(t0.id) AS c2763, COUNT(t2.id) +FROM regions AS t0 CROSS JOIN users AS t1 LEFT JOIN markets AS t2 ON t0.id = t2.id +GROUP BY t2.id;" +25,68652,155432,263,3,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id +FROM books JOIN customers ON books.id = customers.id +ORDER BY customers.id DESC;" +26,1417306,4786826,4875,5489,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT customers.region_id, employees.department_id +FROM employees FULL JOIN orders ON employees.id = orders.id FULL JOIN customers ON employees.id = customers.id +ORDER BY customers.region_id ASC;" +27,1318590,6466259,4170,2,transformation 0 JoinCommutativity: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT orders.customer_id, users.id +FROM users RIGHT JOIN orders ON users.id = orders.id CROSS JOIN departments +WHERE orders.customer_id IN (34, 321) +GROUP BY orders.customer_id, users.id;" +28,1425,1425,156,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id AS c2364, SUM(departments.id) +FROM departments +GROUP BY departments.id +ORDER BY departments.id ASC;" +29,110161,226916,320,3,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT customers.id, markets.id +FROM customers FULL JOIN departments ON customers.id = departments.id JOIN markets ON customers.id = markets.id;" +30,19986,32788,418,51,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT markets.id AS c2619 +FROM markets CROSS JOIN users CROSS JOIN books +WHERE markets.region != 'AMERICA';" +31,3125,3125,190,8,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, SUM(t0.id) +FROM users AS t0 +WHERE ((t0.id NOT IN (5) AND t0.age != 12) AND t0.age NOT BETWEEN 33 AND 64) +GROUP BY t0.id;" +32,3740,3740,166,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id AS c8338, COUNT(t0.department_id) +FROM employees AS t0 +GROUP BY t0.id +ORDER BY t0.id DESC;" +33,878460,2705500,1374,1000,transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id, customers.region_id +FROM departments RIGHT JOIN orders ON departments.id = orders.id CROSS JOIN customers +WHERE departments.id BETWEEN 4 AND 96 +ORDER BY customers.region_id DESC;" +34,22645472,27634772,76328,3,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note AS c1833, markets.region, SUM(books.id) AS c1917, COUNT(*) +FROM orders CROSS JOIN markets CROSS JOIN books +WHERE ((orders.id != 89 OR markets.note IS NOT NULL) OR (markets.id IN (22) AND books.id NOT BETWEEN 1 AND 3)) +GROUP BY markets.note, markets.region +ORDER BY markets.region DESC;" +35,7686500,1755811500,23921,50000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id, orders.id +FROM orders CROSS JOIN regions JOIN customers ON regions.id = customers.id +ORDER BY orders.id DESC, regions.id DESC;" +36,8426,8426,329,51,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT books.price AS c7084 +FROM books CROSS JOIN users;" +37,1916300,525797800,3304,1591,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id +FROM customers CROSS JOIN markets FULL JOIN orders ON markets.id = orders.id +WHERE orders.id < 95;" +38,1310710,4010688,2615,2,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 6,"SELECT t1.department_id, COUNT(*) +FROM regions AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN orders AS t2 ON t0.id = t2.id +WHERE ((t1.id = 3 OR t2.id = 185) OR (t1.id < 3 AND t2.id IS NULL)) +GROUP BY t1.department_id +ORDER BY t1.department_id DESC;" +39,433,433,401,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.note, t0.region +FROM markets AS t0 +WHERE t0.note IS NULL +ORDER BY t0.note DESC, t0.id DESC;" +40,615800,615800,692,500,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id, t1.region_id AS c2127 +FROM books AS t0 CROSS JOIN customers AS t1 +GROUP BY t1.id, t1.region_id;" +41,917612117,1196362017,1308879,50000,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id, orders.id AS c1616 +FROM orders CROSS JOIN customers +WHERE ((orders.id >= 3 OR customers.region_id != 41) OR orders.id IN (73)) +GROUP BY customers.region_id, orders.id;" +42,744958,1705422,1361,3,transformation 0 JoinCommutativity: 1; transformation 9 FilterLiftThroughJoin: 2; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 2,"SELECT t1.price, t0.region_id, t2.customer_id AS c2615 +FROM customers AS t0 JOIN books AS t1 ON t0.id = t1.id JOIN orders AS t2 ON t0.id = t2.id +WHERE t2.id IS NOT NULL;" +43,422,422,293,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.region, markets.id AS c9574 +FROM markets +WHERE ((markets.note IS NOT NULL OR markets.region != 'AMERICA') AND (markets.region IN ('EUROPE', 'EUROPE') AND markets.note IN ('North, South', 'North, South')));" +44,72448,648028,243,6,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id AS c8372, t1.age AS c1550 +FROM customers AS t0 JOIN users AS t1 ON t0.id = t1.id +WHERE t1.id > 11 +ORDER BY t1.id ASC, t1.age DESC;" +45,4560,11225,172,0,transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.department_id AS c7018, t0.id AS c6686 +FROM regions AS t0 JOIN employees AS t1 ON t0.id = t1.id +WHERE t0.id >= 10 +GROUP BY t1.department_id, t0.id;" +46,1122,1122,164,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM regions AS t0 +WHERE ((t0.id IS NOT NULL AND t0.id != 5) AND (t0.id > 8 AND t0.id > 9));" +47,110844,436233,343,1,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id +FROM customers FULL JOIN employees ON customers.id = employees.id +WHERE employees.department_id = 32 +ORDER BY employees.id ASC;" +48,1443652,175655822,3485,11,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT customers.region_id AS c1201, SUM(customers.region_id) +FROM orders FULL JOIN customers ON orders.id = customers.id FULL JOIN markets ON orders.id = markets.id +GROUP BY customers.region_id;" +49,560554,560554,1253,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id, orders.id +FROM orders +WHERE ((orders.id = 130 OR orders.id <= 96) AND orders.id = 97) +ORDER BY orders.customer_id DESC, orders.id DESC;" +50,1239,1972,489,3,transformation 5 FilterPushdownThroughJoin: 1; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT markets.id +FROM markets RIGHT JOIN departments ON markets.id = departments.id +WHERE markets.id < 45;" +51,610,610,140,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM departments AS t0;" +52,3345275,5714300,8172,5000,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 3; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, COUNT(*) +FROM books CROSS JOIN orders +WHERE books.price NOT IN (45, 55, 55, 66) +GROUP BY orders.id;" +53,30725,62340,348,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.id, COUNT(employees.id) AS c5902 +FROM employees CROSS JOIN users JOIN markets ON users.id = markets.id +GROUP BY users.id +ORDER BY users.id ASC;" +54,5100,5100,178,15,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age, users.id AS c207, SUM(users.id) +FROM users +WHERE (users.age > 27 OR (users.age <= 123 AND users.age > 1)) +GROUP BY users.age, users.id +ORDER BY users.id ASC;" +55,1342,1342,128,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.department_id AS c7732 +FROM employees AS t0;" +56,40022332,53882332,79727,3,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id, SUM(orders.customer_id) AS c2549, COUNT(*) AS c1539 +FROM books CROSS JOIN employees CROSS JOIN orders +WHERE employees.id < 68 +GROUP BY books.id;" +57,881250,881250,1505,3001,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.id AS c9114 +FROM orders +WHERE (orders.customer_id > 214 OR orders.customer_id BETWEEN 75 AND 84);" +58,4073600,1927083100,3920,110,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id, customers.region_id, COUNT(*), SUM(orders.customer_id) +FROM employees CROSS JOIN customers FULL JOIN orders ON customers.id = orders.id +WHERE customers.id <= 149 +GROUP BY employees.department_id, customers.region_id;" +59,175500,175500,469,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id, customers.region_id, COUNT(customers.region_id) +FROM customers +GROUP BY customers.id, customers.region_id;" +60,3577,5702,185,17,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id +FROM markets AS t0 FULL JOIN users AS t1 ON t0.id = t1.id +ORDER BY t0.id ASC;" +61,1763,2493,272,5,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.region, t1.id, t1.note +FROM departments AS t0 CROSS JOIN markets AS t1 +WHERE ((t0.id < 43 OR t0.id >= 2) AND t1.note = 'Old World') +ORDER BY t1.note DESC, t1.id ASC, t1.region ASC;" +62,69881,156916,323,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id AS c7718 +FROM departments LEFT JOIN books ON departments.id = books.id JOIN customers ON departments.id = customers.id;" +63,4771,9075,197,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.age AS c5029, COUNT(*) AS c2096, SUM(users.age) +FROM users FULL JOIN departments ON users.id = departments.id +GROUP BY users.age +ORDER BY users.age ASC;" +64,1494000,175610500,1709,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id AS c6827, orders.id, customers.id +FROM customers LEFT JOIN orders ON customers.id = orders.id +ORDER BY customers.region_id ASC, customers.id ASC, orders.id DESC;" +65,1558,2113,203,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.id AS c8656, markets.region +FROM markets FULL JOIN departments ON markets.id = departments.id +WHERE departments.id = 60 +GROUP BY markets.id, markets.region;" +66,563,563,234,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.note AS c5953, SUM(markets.id), SUM(markets.id) +FROM markets +WHERE markets.region = 'EUROPE' +GROUP BY markets.note;" +67,61000,61000,262,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT customers.region_id, customers.id AS c651 +FROM customers;" +68,1311707,6465260,5389,5001,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT users.id, regions.id AS c5610, orders.id AS c9918 +FROM users FULL JOIN orders ON users.id = orders.id FULL JOIN regions ON orders.id = regions.id +ORDER BY users.id DESC;" +69,356822,548611,2595,2380,transformation 0 JoinCommutativity: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id AS c499 +FROM books AS t0 FULL JOIN users AS t1 ON t0.id = t1.id CROSS JOIN customers AS t2 +WHERE ((t0.price > 66 OR t0.price NOT IN (55, 77, 93)) OR t2.id < 93) +ORDER BY t0.id ASC;" +70,574,574,156,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region, markets.note +FROM markets +WHERE markets.id IN (2, 31) +GROUP BY markets.region, markets.note +ORDER BY markets.note ASC, markets.region ASC;" +71,5300,5300,256,5,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id AS c3175, SUM(departments.id), COUNT(books.price) +FROM books CROSS JOIN departments +GROUP BY departments.id;" +72,788,788,138,4,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM departments AS t0 +WHERE t0.id >= 2 +ORDER BY t0.id ASC;" +73,67449,155422,437,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT customers.id, customers.region_id +FROM customers JOIN markets ON customers.id = markets.id +WHERE ((markets.id = 3 AND markets.region != 'AMERICA') AND (customers.id < 17 AND customers.region_id IS NULL));" +74,109996,226972,330,3,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT books.id AS c9785 +FROM customers RIGHT JOIN departments ON customers.id = departments.id RIGHT JOIN books ON departments.id = books.id +WHERE books.price >= 55;" +75,15960,15960,395,110,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.department_id AS c5193, t1.id +FROM regions AS t0 CROSS JOIN employees AS t1;" +76,6261,16988,208,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id AS c3431 +FROM users AS t0 LEFT JOIN employees AS t1 ON t0.id = t1.id +WHERE ((t1.id != 67 AND t0.id IS NOT NULL) OR (t0.age NOT BETWEEN 33 AND 123 OR t0.age >= 22));" +77,60475000,315425000,1366,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.region_id AS c1177, t0.id AS c3278, COUNT(*), COUNT(t0.id) AS c3474 +FROM orders AS t0 CROSS JOIN customers AS t1 +WHERE (t0.id = 64 AND t0.customer_id > 397) +GROUP BY t1.region_id, t0.id;" +78,61000,61000,263,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.region_id +FROM customers AS t0;" +79,2310,2310,601,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c7963, t0.age AS c5437 +FROM users AS t0 +WHERE t0.age IN (5, 11, 63);" +80,3730,6250,275,30,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT regions.id AS c2137 +FROM regions CROSS JOIN markets +WHERE markets.note != 'AMERICA';" +81,3837,9622,233,3,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region AS c3832, users.age, departments.id +FROM users LEFT JOIN departments ON users.id = departments.id RIGHT JOIN markets ON users.id = markets.id +WHERE departments.id <= 5;" +82,1833,1833,147,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age AS c4898, users.id AS c4014 +FROM users +WHERE ((users.id = 4 OR users.id BETWEEN 11 AND 16) AND (users.age = 22 AND users.id <= 57)) +ORDER BY users.age DESC;" +83,3478,5692,185,6,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.age AS c499, t1.price AS c7644, t0.id +FROM users AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id +WHERE t0.id < 7;" +84,3124,4574,510,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT books.price, regions.id, markets.id AS c8988 +FROM markets LEFT JOIN regions ON markets.id = regions.id LEFT JOIN books ON regions.id = books.id +WHERE ((books.price BETWEEN 6 AND 77 OR markets.id NOT IN (1, 2, 2, 3)) OR books.price NOT IN (55));" +85,175500,175500,331,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.region_id +FROM customers AS t0 +GROUP BY t0.id, t0.region_id;" +86,610000,610000,1135,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id +FROM orders;" +87,3256,3256,218,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id, employees.id AS c6314 +FROM employees +GROUP BY employees.department_id, employees.id;" +88,3612,6540,207,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT departments.id AS c2839, COUNT(*) +FROM employees RIGHT JOIN departments ON employees.id = departments.id +GROUP BY departments.id +ORDER BY departments.id DESC;" +89,822,822,176,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region AS c5477, t0.id AS c2794, COUNT(*) AS c9570 +FROM markets AS t0 +GROUP BY t0.region, t0.id;" +90,135000,135000,585,500,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id, customers.id AS c6247, COUNT(*) AS c8943 +FROM customers +WHERE customers.region_id <= 40 +GROUP BY customers.region_id, customers.id;" +91,610,610,181,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id +FROM departments;" +92,12280,12280,358,3,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region, markets.id +FROM markets CROSS JOIN regions +GROUP BY markets.region, markets.id +ORDER BY markets.region ASC;" +93,675822,1550366,1009,3,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT t0.id, t1.id AS c1156, t0.customer_id +FROM orders AS t0 JOIN books AS t1 ON t0.id = t1.id;" +94,4252,22032,305,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 2,"SELECT t1.price +FROM departments AS t0 CROSS JOIN books AS t1 RIGHT JOIN users AS t2 ON t1.id = t2.id +WHERE (t2.id BETWEEN 45 AND 86 AND t1.id IN (2));" +95,2646,7432,516,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 3; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.id +FROM departments AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE ((t1.department_id IN (1, 5, 12, 34) AND t1.id IN (69)) AND t1.department_id <= 1);" +96,684299,6455219,1022,16,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id AS c5555, t0.id +FROM orders AS t0 JOIN users AS t1 ON t0.id = t1.id +GROUP BY t1.id, t0.id;" +97,1274,1274,178,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id, SUM(regions.id), COUNT(regions.id) AS c9550 +FROM regions +WHERE ((regions.id < 2 AND regions.id = 32) AND (regions.id <= 2 AND regions.id != 3)) +GROUP BY regions.id +ORDER BY regions.id ASC;" +98,1232,2482,250,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 5; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT books.id +FROM books CROSS JOIN departments +WHERE ((books.price IS NULL AND departments.id = 1) AND (books.price IS NULL OR books.price NOT IN (55, 77)));" +99,58943,401263,262,0,transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id, SUM(customers.id), COUNT(customers.id) AS c8662 +FROM regions JOIN customers ON regions.id = customers.id +WHERE customers.region_id = 63 +GROUP BY customers.id;" +100,3009,3009,138,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age, users.id AS c7470 +FROM users +ORDER BY users.id ASC;" +101,4056,6624,461,50,transformation 0 JoinCommutativity: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.id, departments.id AS c1258, markets.region +FROM employees LEFT JOIN markets ON employees.id = markets.id CROSS JOIN departments +WHERE ((markets.id IN (3) AND markets.region = 'EUROPE') OR employees.id < 69);" +102,610000,610000,1304,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.customer_id, t0.id AS c8685 +FROM orders AS t0;" +103,686541,6467000,1121,9,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT users.age AS c405 +FROM users JOIN orders ON users.id = orders.id RIGHT JOIN regions ON orders.id = regions.id +GROUP BY users.age +ORDER BY users.age DESC;" +104,1222,1222,196,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT employees.id +FROM employees +WHERE employees.id = 14;" +105,1306776,1551752,1464,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT markets.id, orders.customer_id, COUNT(*) +FROM orders RIGHT JOIN markets ON orders.id = markets.id LEFT JOIN books ON markets.id = books.id +GROUP BY markets.id, orders.customer_id;" +106,10392,18447,326,25,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price AS c2329, markets.note +FROM users CROSS JOIN books JOIN markets ON books.id = markets.id +WHERE (markets.region = 'EUROPE' OR (users.age NOT IN (5, 22, 64, 123) AND users.age < 22)) +ORDER BY markets.note ASC;" +107,1274,1274,159,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id +FROM regions +WHERE regions.id IN (5) +GROUP BY regions.id +ORDER BY regions.id ASC;" +108,102500,102500,239,51,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id AS c9166, customers.region_id +FROM customers +WHERE customers.id < 52 +ORDER BY customers.region_id ASC, customers.id DESC;" +109,5405,16132,208,22,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id, users.age, employees.department_id +FROM employees FULL JOIN users ON employees.id = users.id;" +110,3233,16153,330,0,transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id, employees.id, COUNT(users.id) AS c6401, COUNT(*) +FROM employees FULL JOIN users ON employees.id = users.id +WHERE ((employees.department_id = 31 AND employees.department_id NOT IN (6, 59, 72)) AND (employees.department_id NOT IN (1, 5, 6, 32) AND users.id = 5)) +GROUP BY employees.department_id, employees.id;" +111,541846,541846,2403,28,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id +FROM orders +WHERE ((orders.id IN (97, 99, 184, 198) OR orders.id BETWEEN 31 AND 58) AND (orders.customer_id IS NOT NULL AND orders.id BETWEEN 3 AND 65));" +112,1305844,1550574,3017,2,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT markets.id, markets.note, COUNT(orders.id), SUM(orders.customer_id) +FROM markets RIGHT JOIN orders ON markets.id = orders.id +WHERE ((orders.id < 188 AND orders.customer_id >= 193) OR (orders.customer_id BETWEEN 239 AND 436 OR markets.id = 1)) +GROUP BY markets.id, markets.note +ORDER BY markets.id DESC, markets.note DESC;" +113,422,422,175,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.region, t0.note AS c6295 +FROM markets AS t0 +WHERE t0.id < 2;" +114,61000,61000,226,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM customers AS t0;" +115,3922,10020,204,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id AS c2831, t0.id, t0.department_id +FROM employees AS t0 RIGHT JOIN regions AS t1 ON t0.id = t1.id;" +116,676779,1551363,1099,1,transformation 0 JoinCommutativity: 1; transformation 9 FilterLiftThroughJoin: 2; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.customer_id +FROM markets AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id JOIN orders AS t2 ON t1.id = t2.id +WHERE t2.customer_id > 244 +ORDER BY t2.customer_id DESC;" +117,744,744,354,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c6139 +FROM departments AS t0 +WHERE t0.id >= 5;" +118,3722,9763,328,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 5; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT books.id AS c118 +FROM departments RIGHT JOIN users ON departments.id = users.id FULL JOIN books ON departments.id = books.id +WHERE ((users.id > 3 AND books.price = 77) AND users.age > 443) +GROUP BY books.id;" +119,676308,2251048,1306,5,transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, COUNT(departments.id) +FROM departments JOIN orders ON departments.id = orders.id +WHERE departments.id < 36 +GROUP BY orders.id;" +120,1377824,4786342,1550,11,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id, employees.department_id +FROM employees JOIN customers ON employees.id = customers.id LEFT JOIN orders ON employees.id = orders.id;" +121,1222,1222,130,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT employees.id +FROM employees +WHERE ((employees.id > 68 OR employees.department_id = 5) AND (employees.id BETWEEN 2 AND 4 AND employees.department_id IN (3, 5, 5)));" +122,3184,5382,344,1,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 2; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note, employees.id +FROM employees LEFT JOIN markets ON employees.id = markets.id JOIN departments ON employees.id = departments.id +WHERE ((departments.id != 36 OR markets.region IS NULL) AND departments.id = 4);" +123,544,544,168,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.note, t0.region +FROM markets AS t0 +WHERE t0.id NOT BETWEEN 2 AND 21;" +124,59693,155433,332,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT books.price AS c5178 +FROM books RIGHT JOIN customers ON books.id = customers.id +WHERE customers.id = 158 +ORDER BY books.price DESC;" +125,1222,1222,152,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT employees.id AS c9929, employees.department_id +FROM employees +WHERE (employees.department_id = 35 AND employees.department_id < 32);" +126,3107,4593,277,0,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT books.price, books.id +FROM books FULL JOIN markets ON books.id = markets.id FULL JOIN regions ON books.id = regions.id +WHERE (markets.region != 'EUROPE' AND (regions.id IS NULL AND books.price > 66)) +GROUP BY books.price, books.id;" +127,81500,81500,286,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id +FROM customers AS t0 +WHERE t0.id IN (16, 32, 137) +ORDER BY t0.region_id DESC;" +128,1288,1288,144,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id +FROM regions +WHERE regions.id BETWEEN 9 AND 10 +ORDER BY regions.id DESC;" +129,1306842,1551818,1376,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT orders.customer_id, COUNT(orders.id) AS c1702 +FROM markets LEFT JOIN books ON markets.id = books.id LEFT JOIN orders ON markets.id = orders.id +GROUP BY orders.customer_id +ORDER BY orders.customer_id DESC;" +130,1308015,2255110,1379,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id, orders.id AS c452, orders.customer_id +FROM regions JOIN departments ON regions.id = departments.id LEFT JOIN orders ON departments.id = orders.id;" +131,1308440,4002960,2135,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id AS c9060, COUNT(orders.customer_id), COUNT(orders.id) +FROM orders FULL JOIN regions ON orders.id = regions.id +GROUP BY regions.id;" +132,2187,5572,196,0,transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id AS c4692, t1.department_id AS c2149, t1.id +FROM departments AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id +WHERE (t1.id IN (32, 66, 68, 80) AND (t1.id <= 26 OR t1.department_id <= 5));" +133,678014,4351342,1007,11,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT t1.id AS c8251, t0.customer_id, t0.id AS c2451 +FROM orders AS t0 JOIN employees AS t1 ON t0.id = t1.id;" +134,17111100,17111100,23355,55000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id AS c6356 +FROM employees CROSS JOIN orders +ORDER BY orders.customer_id ASC;" +135,1311707,6465260,2119,11,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT users.id, regions.id AS c3154 +FROM orders FULL JOIN users ON orders.id = users.id RIGHT JOIN regions ON orders.id = regions.id +ORDER BY users.id DESC, regions.id DESC;" +136,7798,11608,602,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT books.price +FROM regions CROSS JOIN markets LEFT JOIN books ON regions.id = books.id +GROUP BY books.price +ORDER BY books.price DESC;" +137,1425,1425,186,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, SUM(departments.id), COUNT(*) AS c6998 +FROM departments +GROUP BY departments.id;" +138,2960,2960,198,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id AS c7258 +FROM regions +GROUP BY regions.id;" +139,563,563,179,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.id +FROM markets +WHERE ((markets.note = 'Old World' OR markets.id >= 2) OR (markets.region = 'AMERICA' AND markets.region = 'EUROPE')) +GROUP BY markets.id;" +140,422,422,162,3,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.price, t0.id AS c3105 +FROM books AS t0 +WHERE t0.id IS NOT NULL;" +141,2932,4903,508,0,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id AS c8895, t1.note AS c8854, SUM(t1.id) +FROM books AS t0 RIGHT JOIN markets AS t1 ON t0.id = t1.id RIGHT JOIN employees AS t2 ON t0.id = t2.id +WHERE t1.id > 24 +GROUP BY t1.id, t1.note;" +142,75995,661132,331,17,transformation 10 ProjectionPushdownThroughJoin: 7; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 4; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.region_id AS c1630 +FROM users AS t0 JOIN customers AS t1 ON t0.id = t1.id LEFT JOIN employees AS t2 ON t0.id = t2.id;" +143,422,422,171,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.price +FROM books AS t0 +WHERE t0.price <= 55;" +144,7022,9744,298,10,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.department_id AS c3966, SUM(t1.id) AS c1392, COUNT(*) AS c3892 +FROM books AS t0 CROSS JOIN employees AS t1 +WHERE t1.department_id != 12 +GROUP BY t1.department_id;" +145,2159,3832,197,2,transformation 5 FilterPushdownThroughJoin: 1; transformation 7 FilterToJoinPredicate: 1; transformation 9 FilterLiftThroughJoin: 2; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT t1.department_id AS c6376, t1.id AS c3596 +FROM books AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id +WHERE ((t0.id < 1 OR t1.id IS NOT NULL) AND (t0.id != 3 AND t1.department_id <= 35));" +146,1590,1590,127,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id +FROM departments +GROUP BY departments.id +ORDER BY departments.id DESC;" +147,9987,12750,392,110,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id +FROM departments FULL JOIN employees ON departments.id = employees.id CROSS JOIN regions;" +148,3730,6250,284,20,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT books.id, books.price AS c5525, regions.id +FROM books CROSS JOIN regions +WHERE books.price < 69;" +149,102500,102500,393,422,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id AS c6318 +FROM customers AS t0 +WHERE t0.id > 78 +ORDER BY t0.region_id DESC;" +150,810500,1130500,1199,1040,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t1.id AS c921 +FROM regions AS t0 CROSS JOIN customers AS t1 +WHERE t1.region_id IN (5, 9, 51) +GROUP BY t0.id, t1.id;" +151,1310829,4014888,2354,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT regions.id, users.age, orders.id +FROM regions RIGHT JOIN users ON regions.id = users.id FULL JOIN orders ON users.id = orders.id +WHERE ((orders.customer_id BETWEEN 283 AND 359 AND users.id IN (10, 17, 17, 39)) OR (users.id IN (14, 16, 64, 75) AND users.id >= 81)) +ORDER BY orders.id ASC, regions.id DESC, users.age ASC;" +152,12160,12160,302,55,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, employees.department_id, employees.id +FROM departments CROSS JOIN employees +ORDER BY employees.id ASC;" +153,239300,239300,1299,1500,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.id, t0.region_id, t0.id +FROM customers AS t0 CROSS JOIN books AS t1;" +154,3226,3226,193,10,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id, SUM(regions.id) AS c8443 +FROM regions +WHERE (regions.id >= 1 OR (regions.id >= 8 OR regions.id = 5)) +GROUP BY regions.id +ORDER BY regions.id ASC;" +155,1308138,1554232,1515,2,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT markets.region +FROM markets LEFT JOIN orders ON markets.id = orders.id JOIN employees ON markets.id = employees.id +GROUP BY markets.region;" +156,1307140,4001220,1831,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.customer_id, t1.id AS c2544, t0.id AS c7170 +FROM regions AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id;" +157,126891,675750,1165,4840,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.region_id, t2.id +FROM users AS t0 RIGHT JOIN customers AS t1 ON t0.id = t1.id CROSS JOIN regions AS t2 +WHERE t0.id IS NULL;" +158,4574,12322,266,0,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t1.id, t0.id AS c3888, t2.id AS c5161 +FROM employees AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id JOIN books AS t2 ON t0.id = t2.id +WHERE t0.id IS NULL;" +159,1307045,2251640,1502,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT orders.customer_id AS c3770, departments.id AS c4567, COUNT(*) AS c2771 +FROM departments LEFT JOIN orders ON departments.id = orders.id +WHERE ((orders.customer_id NOT BETWEEN 115 AND 380 OR departments.id IS NULL) OR orders.id = 174) +GROUP BY orders.customer_id, departments.id;" +160,610000,610000,1315,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.customer_id AS c1926 +FROM orders AS t0;" +161,91868,1210558,389,33,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t1.region AS c961, t2.id +FROM employees AS t0 CROSS JOIN markets AS t1 JOIN customers AS t2 ON t1.id = t2.id;" +162,1466,1466,137,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT employees.id, employees.department_id +FROM employees +WHERE ((employees.id NOT IN (5, 67, 68) AND employees.department_id = 28) OR (employees.id > 4 AND employees.department_id > 32));" +163,122820,1849979,435,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.region, COUNT(*), COUNT(*) +FROM users CROSS JOIN markets LEFT JOIN customers ON markets.id = customers.id +WHERE users.age >= 1 +GROUP BY markets.region;" +164,433,433,203,3,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.id, markets.note AS c529, markets.region +FROM markets +WHERE (markets.region = 'EUROPE' OR markets.id <= 91) +ORDER BY markets.region ASC, markets.id ASC, markets.note ASC;" +165,822,822,182,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id, COUNT(books.price), SUM(books.id) +FROM books +GROUP BY books.id;" +166,2328,2328,243,9,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id +FROM regions +WHERE regions.id NOT IN (2, 95) +ORDER BY regions.id ASC;" +167,52196,52196,305,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM customers AS t0 +WHERE ((t0.region_id > 32 AND t0.id = 78) AND (t0.id >= 20 OR t0.id IS NULL));" +168,110368,157908,560,1,transformation 0 JoinCommutativity: 2; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.region AS c6909, t1.note, SUM(t2.id) +FROM customers AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id CROSS JOIN departments AS t2 +WHERE ((t0.id = 89 OR t1.id BETWEEN 49 AND 71) AND t0.id IS NOT NULL) +GROUP BY t1.region, t1.note +ORDER BY t1.region ASC;" +169,1392,2113,295,2,transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id +FROM departments RIGHT JOIN markets ON departments.id = markets.id +WHERE departments.id != 1 +GROUP BY departments.id;" +170,1306995,2251590,3048,505,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.id AS c5737, t0.customer_id +FROM orders AS t0 LEFT JOIN departments AS t1 ON t0.id = t1.id +GROUP BY t1.id, t0.customer_id +ORDER BY t0.customer_id DESC, t1.id ASC;" +171,24626,37006,524,110,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id AS c3715 +FROM regions CROSS JOIN employees FULL JOIN books ON regions.id = books.id;" +172,111497,436875,382,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id, t0.department_id AS c3676, t1.region_id +FROM employees AS t0 FULL JOIN customers AS t1 ON t0.id = t1.id +WHERE t0.department_id >= 57 +ORDER BY t0.id DESC, t1.region_id ASC;" +173,27106700,27106700,22994,85000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id +FROM users CROSS JOIN orders +ORDER BY orders.customer_id DESC;" +174,2475,2475,139,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.id, users.age +FROM users +WHERE users.id IN (3, 11, 12) +ORDER BY users.age DESC, users.id DESC;" +175,2273,3832,203,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.id, employees.id, employees.department_id +FROM employees RIGHT JOIN books ON employees.id = books.id +WHERE books.price = 77;" +176,563,563,268,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price, SUM(books.price) AS c5357 +FROM books +WHERE books.price >= 66 +GROUP BY books.price;" +177,3692,3692,175,14,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.id, users.age AS c4847 +FROM users +WHERE (users.age < 16 OR users.age < 71) +ORDER BY users.id ASC, users.age DESC;" +178,3009,3009,347,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age AS c7380, users.id +FROM users +ORDER BY users.id DESC;" +179,221288,221288,659,500,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id, customers.region_id +FROM customers +WHERE ((customers.id != 141 OR customers.region_id != 10) OR (customers.id NOT IN (197) OR customers.region_id IN (8))) +GROUP BY customers.id, customers.region_id;" +180,225000,225000,394,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id AS c95, customers.region_id +FROM customers +GROUP BY customers.id, customers.region_id +ORDER BY customers.region_id DESC;" +181,3199300,3199300,3855,500,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id AS c397, customers.id AS c5003, SUM(books.id) +FROM customers CROSS JOIN books CROSS JOIN departments +GROUP BY customers.region_id, customers.id;" +182,970500,175675500,1144,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id AS c6022, t0.id +FROM orders AS t0 JOIN customers AS t1 ON t0.id = t1.id +GROUP BY t1.id, t0.id;" +183,65900,65900,256,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id, COUNT(customers.id), COUNT(customers.id) +FROM customers +WHERE customers.id = 24 +GROUP BY customers.id;" +184,171800,297800,1278,500,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT books.price AS c5308, customers.region_id AS c1694 +FROM books CROSS JOIN customers +WHERE books.price <= 55;" +185,610,610,132,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c9527 +FROM departments AS t0;" +186,5081,14820,190,18,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id +FROM regions FULL JOIN users ON regions.id = users.id;" +187,5791,15530,209,8,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id, regions.id, users.age AS c4755 +FROM users LEFT JOIN regions ON users.id = regions.id +WHERE ((regions.id != 31 AND users.age <= 64) OR users.id >= 66) +ORDER BY users.age DESC;" +188,3600,7573,313,0,transformation 0 JoinCommutativity: 2; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.price AS c5573, COUNT(*) +FROM employees AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE (t2.price > 55 AND t0.id BETWEEN 66 AND 69) +GROUP BY t2.price;" +189,57522,155422,250,0,transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT markets.id AS c7660 +FROM markets JOIN customers ON markets.id = customers.id +WHERE (markets.note IS NULL AND (customers.id = 198 AND markets.region IN ('AMERICA', 'EUROPE')));" +190,1590,1590,222,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id +FROM departments +GROUP BY departments.id +ORDER BY departments.id DESC;" +191,14474,21594,411,55,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 3; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id, employees.id +FROM departments JOIN regions ON departments.id = regions.id CROSS JOIN employees +WHERE employees.id IS NOT NULL +GROUP BY departments.id, employees.id +ORDER BY departments.id DESC, employees.id ASC;" +192,1252,1658,322,3,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT books.id, markets.region +FROM markets CROSS JOIN books +WHERE markets.region = 'EUROPE';" +193,114435,414820,343,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.region_id, regions.id +FROM users JOIN regions ON users.id = regions.id LEFT JOIN customers ON regions.id = customers.id;" +194,2490882,5620982,22803,25000,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t2.id +FROM customers AS t0 CROSS JOIN markets AS t1 CROSS JOIN users AS t2 +WHERE ((t1.id IN (3) OR t2.age != 1) OR (t1.id != 2 OR t2.id > 61));" +195,1307400,1553466,1893,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t0.id +FROM regions AS t0 FULL JOIN books AS t1 ON t0.id = t1.id LEFT JOIN orders AS t2 ON t1.id = t2.id;" +196,617630,618900,807,30,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id, books.id, SUM(regions.id), SUM(customers.region_id) +FROM books JOIN regions ON books.id = regions.id CROSS JOIN customers +GROUP BY customers.region_id, books.id;" +197,70476,158466,310,3,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.price AS c8028, t2.id +FROM books AS t0 JOIN customers AS t1 ON t0.id = t1.id JOIN regions AS t2 ON t1.id = t2.id;" +198,2074,2074,158,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.age AS c2991 +FROM users AS t0;" +199,16360,30140,267,30,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t1.id, t0.id +FROM regions AS t0 CROSS JOIN users AS t1 +WHERE (t1.age BETWEEN 33 AND 33 OR t1.id IN (12, 49, 95));" +200,2428,2428,159,6,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id +FROM employees +WHERE ((employees.id > 66 OR employees.id IN (33)) OR employees.department_id IN (6, 12, 12, 50)) +ORDER BY employees.department_id DESC;" +201,610000,610000,1481,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id +FROM orders;" +202,4879,16712,225,6,transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.id AS c3304, users.id AS c2088 +FROM users JOIN employees ON users.id = employees.id +WHERE ((users.age IN (42, 64) OR users.id < 16) AND users.id != 99) +GROUP BY employees.id, users.id;" +203,822,822,256,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id, books.price +FROM books +GROUP BY books.id, books.price;" +204,69372,155822,327,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c8592, t1.region +FROM customers AS t0 JOIN markets AS t1 ON t0.id = t1.id +GROUP BY t0.id, t1.region;" +205,2016100,9975800,3442,1,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id AS c8725, SUM(t1.id), COUNT(*) +FROM users AS t0 CROSS JOIN orders AS t1 +WHERE (t0.age IN (33, 64) AND t1.id = 156) +GROUP BY t1.id;" +206,61000,61000,471,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c4257 +FROM customers AS t0;" +207,1236600,1931301600,1356,0,transformation 0 JoinCommutativity: 1; transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t1.customer_id, t0.department_id +FROM employees AS t0 CROSS JOIN orders AS t1 JOIN customers AS t2 ON t1.id = t2.id +WHERE t0.department_id IS NULL;" +208,2388,3776,246,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price AS c3720, employees.department_id, books.id +FROM employees RIGHT JOIN books ON employees.id = books.id;" +209,69533,156493,311,0,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.region, t1.id, COUNT(t2.id) +FROM books AS t0 JOIN customers AS t1 ON t0.id = t1.id FULL JOIN markets AS t2 ON t0.id = t2.id +WHERE t0.id < 1 +GROUP BY t2.region, t1.id;" +210,2390300,2390300,3375,15000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT orders.id, books.id AS c7933, orders.customer_id +FROM books CROSS JOIN orders;" +211,12462200,2984422200,8506,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t2.region_id, t2.id, t0.customer_id +FROM orders AS t0 CROSS JOIN users AS t1 LEFT JOIN customers AS t2 ON t1.id = t2.id +WHERE t2.region_id >= 23;" +212,1306062,1550822,1381,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT orders.id +FROM books LEFT JOIN orders ON books.id = orders.id +GROUP BY orders.id;" +213,1620,1620,151,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM regions AS t0 +WHERE t0.id IN (4, 4, 9, 88) +ORDER BY t0.id ASC;" +214,28496600,51596600,97726,247665,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT orders.id, orders.customer_id, employees.id +FROM orders CROSS JOIN employees CROSS JOIN departments +WHERE orders.customer_id >= 52;" +215,805000,805000,1080,29,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.customer_id +FROM orders AS t0 +WHERE t0.id < 30;" +216,744,744,164,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id +FROM departments +WHERE departments.id IN (1, 3, 4, 60);" +217,72665,437525,314,3,transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t1.region_id +FROM employees AS t0 LEFT JOIN customers AS t1 ON t0.id = t1.id +WHERE t1.region_id >= 8 +GROUP BY t0.id, t1.region_id;" +218,1331,1916,204,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price AS c5412, books.id, departments.id AS c9618 +FROM departments LEFT JOIN books ON departments.id = books.id;" +219,5705600,9485600,43305,45000,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.note, t0.customer_id AS c4395 +FROM orders AS t0 CROSS JOIN markets AS t1 CROSS JOIN books AS t2 +WHERE t1.region IS NOT NULL;" +220,17063620,1931305120,30660,45001,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id, orders.id, employees.id +FROM employees CROSS JOIN orders RIGHT JOIN customers ON employees.id = customers.id +WHERE (customers.id IN (181) OR employees.id BETWEEN 3 AND 92) +ORDER BY customers.region_id ASC;" +221,1133,1133,141,0,transformation 2 FilterSplit: 1; transformation 3 FilterMerge: 1; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c9777 +FROM regions AS t0 +WHERE ((t0.id <= 5 OR t0.id IN (4, 12)) AND (t0.id = 9 AND t0.id NOT IN (2, 4, 5, 89))) +ORDER BY t0.id ASC;" +222,3764,3764,183,11,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.department_id AS c2878, COUNT(t0.id), COUNT(*) AS c2633 +FROM employees AS t0 +WHERE ((t0.department_id IS NOT NULL OR t0.department_id IS NOT NULL) OR (t0.id > 26 OR t0.id = 52)) +GROUP BY t0.id, t0.department_id;" +223,7431100,7431100,16252,55000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t0.id +FROM orders AS t0 CROSS JOIN employees AS t1;" +224,1342,1342,139,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT employees.department_id +FROM employees;" +225,790056,4786342,1089,11,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id, customers.id AS c4775, employees.department_id AS c6986 +FROM employees LEFT JOIN customers ON employees.id = customers.id JOIN orders ON employees.id = orders.id;" +226,2374,3644,253,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.id, regions.id, books.price AS c1520 +FROM books LEFT JOIN regions ON books.id = regions.id +WHERE ((regions.id < 10 OR books.price = 77) OR books.price > 60);" +227,1305662,1550422,1986,9,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id, books.price, orders.customer_id AS c7568 +FROM orders FULL JOIN books ON orders.id = books.id +WHERE orders.customer_id = 63;" +228,100831,285106,925,1000,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t1.id AS c1443, t2.region_id AS c2237 +FROM employees AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id CROSS JOIN customers AS t2 +WHERE ((t1.id < 3 OR t1.region = 'AMERICA') AND (t2.region_id IS NULL OR t1.note != 'North, South'));" +229,111140,228445,393,0,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT departments.id +FROM departments LEFT JOIN customers ON departments.id = customers.id CROSS JOIN markets +WHERE markets.region IS NULL +ORDER BY departments.id ASC;" +230,1309060,2258260,2139,5000,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id, users.age AS c5139, orders.id +FROM departments JOIN users ON departments.id = users.id FULL JOIN orders ON users.id = orders.id;" +231,610,610,147,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id +FROM departments;" +232,116575,651154,322,16,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT customers.id, COUNT(*) AS c3789 +FROM customers RIGHT JOIN users ON customers.id = users.id +GROUP BY customers.id +ORDER BY customers.id DESC;" +233,109845,317650,643,1353,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id +FROM departments JOIN markets ON departments.id = markets.id CROSS JOIN customers +WHERE (customers.region_id NOT IN (10, 10) AND (customers.id <= 80 OR departments.id >= 1));" +234,2226,3466,165,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.note +FROM markets AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id;" +235,68524,155544,239,1,transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT t1.region_id AS c5511 +FROM books AS t0 JOIN customers AS t1 ON t0.id = t1.id +WHERE (t0.price NOT BETWEEN 66 AND 94 OR (t0.id > 3 AND t1.region_id = 10));" +236,1311747,4366616,1376,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t1.department_id AS c2338, t0.customer_id +FROM orders AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN users AS t2 ON t1.id = t2.id +ORDER BY t1.department_id ASC;" +237,134541,669120,1866,5010,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id +FROM users FULL JOIN customers ON users.id = customers.id CROSS JOIN regions;" +238,68974,436222,236,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 1; transformation 6 InToOrChain: 1; transformation 9 FilterLiftThroughJoin: 2; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT customers.region_id +FROM customers LEFT JOIN employees ON customers.id = employees.id +WHERE (employees.id IN (38) AND customers.region_id != 5);" +239,2690,2690,170,6,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id +FROM employees +WHERE employees.id > 5 +GROUP BY employees.id +ORDER BY employees.id DESC;" +240,95524,95524,484,3,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.price AS c6143, SUM(t2.id) +FROM books AS t0 CROSS JOIN users AS t1 CROSS JOIN departments AS t2 +GROUP BY t0.price;" +241,3759405,3875500,4325,25000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id, customers.region_id, departments.id +FROM customers RIGHT JOIN departments ON customers.id = departments.id CROSS JOIN orders;" +242,3038,5320,370,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT books.id +FROM books CROSS JOIN employees +WHERE (employees.id != 33 AND (employees.id < 1 AND employees.department_id < 12));" +243,678835,4353843,959,0,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 1; transformation 9 FilterLiftThroughJoin: 2; transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id, books.price AS c4475, books.id AS c4655 +FROM orders JOIN employees ON orders.id = employees.id RIGHT JOIN books ON employees.id = books.id +WHERE ((orders.id >= 13 AND orders.customer_id != 233) OR (orders.id > 39 AND orders.customer_id <= 359)) +ORDER BY books.id DESC;" +244,4057,12322,431,0,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 4; transformation 5 FilterPushdownThroughJoin: 2; transformation 9 FilterLiftThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT regions.id +FROM regions RIGHT JOIN employees ON regions.id = employees.id LEFT JOIN markets ON employees.id = markets.id +WHERE (regions.id NOT BETWEEN 2 AND 8 AND markets.note = 'Fast lane');" +245,433,433,280,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.note, markets.region, markets.id +FROM markets +WHERE ((markets.region != 'AMERICA' AND markets.region = 'AMERICA') OR markets.id < 2) +ORDER BY markets.note DESC, markets.id ASC, markets.region DESC;" +246,1244,1244,150,9,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id AS c713 +FROM regions +WHERE regions.id BETWEEN 2 AND 45;" +247,670078,1550848,1189,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c7208 +FROM orders AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id +WHERE t0.customer_id NOT IN (244, 365) +GROUP BY t0.id +ORDER BY t0.id ASC;" +248,1311645,6455252,3403,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, COUNT(*), SUM(t0.id) AS c7387 +FROM users AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t1.customer_id = 494 AND t1.customer_id != 145) OR (t0.age > 53 OR t1.customer_id IS NOT NULL)) +GROUP BY t1.id;" +249,3488,5692,177,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region, users.age +FROM markets LEFT JOIN users ON markets.id = users.id +WHERE users.id < 17;" +250,110640,401220,258,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id, customers.region_id +FROM regions LEFT JOIN customers ON regions.id = customers.id;" +251,1307530,4001775,1809,42,implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id, regions.id, orders.id AS c3346 +FROM regions FULL JOIN orders ON regions.id = orders.id +WHERE orders.id <= 42 +ORDER BY regions.id ASC;" +252,4641438,7310933,6345,39,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT orders.id AS c8852, orders.customer_id, books.price AS c8813 +FROM books CROSS JOIN orders FULL JOIN departments ON orders.id = departments.id +WHERE ((orders.customer_id = 163 OR orders.id IN (69, 96, 152)) AND (orders.id IS NOT NULL AND books.id IS NOT NULL)) +ORDER BY orders.id DESC;" +253,81634,3916834,362,40,transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 1; transformation 9 FilterLiftThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT customers.id +FROM regions CROSS JOIN employees JOIN customers ON employees.id = customers.id +WHERE (employees.id IS NOT NULL AND customers.id >= 65);" +254,2418,3776,166,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.id AS c3977, employees.department_id +FROM markets FULL JOIN employees ON markets.id = employees.id;" +255,1975000,1975000,2178,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, orders.customer_id AS c7286 +FROM orders +GROUP BY orders.id, orders.customer_id;" +256,5474,7496,426,9,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t1.id AS c9218 +FROM markets AS t0 CROSS JOIN employees AS t1 +WHERE t1.department_id IN (5, 12, 33) +GROUP BY t0.id, t1.id;" +257,2652,3922,247,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.price, t0.id AS c7828 +FROM regions AS t0 JOIN books AS t1 ON t0.id = t1.id +GROUP BY t1.price, t0.id;" +258,75739,651158,303,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT customers.region_id +FROM customers JOIN users ON customers.id = users.id RIGHT JOIN markets ON customers.id = markets.id +GROUP BY customers.region_id +ORDER BY customers.region_id DESC;" +259,110500,110500,248,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id +FROM customers +ORDER BY customers.id ASC;" +260,72814,72814,233,118,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT customers.region_id, customers.id +FROM customers +WHERE ((customers.region_id IS NULL OR customers.id >= 198) AND customers.region_id <= 4);" +261,109628,155888,306,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT customers.region_id AS c4278, books.id +FROM books LEFT JOIN customers ON books.id = customers.id +GROUP BY customers.region_id, books.id +ORDER BY books.id ASC;" +262,15960,15960,259,110,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.department_id AS c5186, t0.id, t1.id +FROM regions AS t0 CROSS JOIN employees AS t1;" +263,3077,6483,228,2,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT markets.id, departments.id, regions.id +FROM regions LEFT JOIN departments ON regions.id = departments.id JOIN markets ON regions.id = markets.id +WHERE markets.note != 'Old World' +ORDER BY markets.id ASC, regions.id DESC;" +264,112407,160636,399,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT users.id AS c6920, markets.region AS c5238, users.age AS c1104 +FROM markets RIGHT JOIN users ON markets.id = users.id RIGHT JOIN customers ON markets.id = customers.id;" +265,5081,14820,202,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, t0.id AS c2693 +FROM users AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id;" +266,59400,59400,235,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id AS c604, customers.region_id +FROM customers +WHERE customers.id = 135 +ORDER BY customers.id DESC, customers.region_id DESC;" +267,422,422,127,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.id +FROM books +WHERE books.price IS NULL;" +268,76971400,76971400,140145,11,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id, SUM(orders.customer_id) +FROM orders CROSS JOIN books CROSS JOIN employees +GROUP BY employees.department_id;" +269,366,366,310,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT books.price AS c8205 +FROM books;" +270,610000,610000,1415,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT orders.id AS c7922 +FROM orders;" +271,534702,2250622,1096,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT orders.id AS c8902, departments.id AS c4368, orders.customer_id +FROM orders JOIN departments ON orders.id = departments.id +WHERE ((orders.customer_id = 483 AND departments.id > 4) AND orders.id != 24);" +272,850563,1550563,1892,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id, orders.id, COUNT(*), COUNT(*) +FROM orders RIGHT JOIN books ON orders.id = books.id +WHERE books.id != 1 +GROUP BY orders.customer_id, orders.id;" +273,3256,3256,150,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, SUM(t0.department_id), COUNT(t0.department_id) +FROM employees AS t0 +GROUP BY t0.id;" +274,2066,2066,132,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c7269, t0.age +FROM users AS t0 +WHERE t0.age IN (33, 33);" +275,545750,545750,1554,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.id AS c6877 +FROM orders +WHERE ((orders.customer_id <= 56 OR orders.customer_id != 343) AND (orders.customer_id IN (52, 86) AND orders.customer_id >= 224));" +276,3536700,3536700,2615,11,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age AS c7960, SUM(users.id) +FROM customers CROSS JOIN users +GROUP BY users.age +ORDER BY users.age ASC;" +277,774,774,191,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id +FROM departments AS t0 +WHERE ((t0.id IN (2, 10, 59) OR t0.id IS NULL) AND (t0.id != 5 AND t0.id IS NOT NULL)) +GROUP BY t0.id +ORDER BY t0.id DESC;" +278,4334,8638,211,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id AS c8751 +FROM users AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id +WHERE (t0.age IS NULL OR (t1.id != 5 OR t1.id < 14));" +279,676493,4001263,1252,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 9 FilterLiftThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id, orders.id, COUNT(*), SUM(orders.customer_id) +FROM orders RIGHT JOIN regions ON orders.id = regions.id +WHERE ((regions.id IN (8, 9, 22) AND regions.id <= 6) AND orders.id IN (39, 49, 104, 106)) +GROUP BY regions.id, orders.id;" +280,679538,6451822,1025,0,transformation 5 FilterPushdownThroughJoin: 1; transformation 7 FilterToJoinPredicate: 1; transformation 9 FilterLiftThroughJoin: 2; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT orders.customer_id +FROM users FULL JOIN orders ON users.id = orders.id +WHERE ((users.age > 33 AND orders.customer_id < 84) AND (users.id = 15 OR orders.id IN (68, 128, 128, 198)));" +281,433,433,167,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.note, t0.region +FROM markets AS t0 +WHERE ((t0.note = 'unknown' AND t0.id != 1) AND t0.id > 67) +ORDER BY t0.id ASC, t0.note DESC;" +282,2588310,13612010,1854,9,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT employees.id, orders.customer_id AS c5471, books.price +FROM books CROSS JOIN orders LEFT JOIN employees ON orders.id = employees.id +WHERE employees.id > 66;" +283,68793,521733,358,0,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 2; transformation 3 FilterMerge: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 2; transformation 9 FilterLiftThroughJoin: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id, markets.id, books.price AS c2723 +FROM books CROSS JOIN customers FULL JOIN markets ON customers.id = markets.id +WHERE ((markets.id > 55 AND customers.region_id NOT IN (8, 9, 10)) AND markets.region IN ('AMERICA', 'EUROPE')) +ORDER BY books.id DESC, markets.id ASC;" +284,822,822,143,2,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region AS c1153, SUM(t0.id) AS c3569 +FROM markets AS t0 +GROUP BY t0.region +ORDER BY t0.region ASC;" +285,5364,10836,332,0,transformation 0 JoinCommutativity: 2; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id AS c5654, users.id +FROM markets FULL JOIN employees ON markets.id = employees.id CROSS JOIN users +WHERE ((markets.note = 'Fast lane' AND employees.department_id NOT BETWEEN 11 AND 37) AND (users.id != 4 AND users.age <= 443));" +286,109668,225774,393,3,implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.id, COUNT(customers.id) AS c434, COUNT(*) +FROM customers FULL JOIN departments ON customers.id = departments.id +WHERE departments.id BETWEEN 2 AND 4 +GROUP BY customers.id +ORDER BY customers.id DESC;" +287,3181,5072,278,7,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t2.id, t1.id, t0.id +FROM books AS t0 JOIN departments AS t1 ON t0.id = t1.id RIGHT JOIN regions AS t2 ON t1.id = t2.id +WHERE t0.id IS NULL;" +288,5252,15332,178,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id AS c1673 +FROM users JOIN regions ON users.id = regions.id +WHERE (users.age IN (33, 47, 98) OR users.id > 54);" +289,1883,2493,414,3,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, books.price AS c551 +FROM departments CROSS JOIN books +WHERE departments.id = 5 +ORDER BY departments.id DESC;" +290,679184,4353832,1026,2,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 1; transformation 9 FilterLiftThroughJoin: 2; transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.region, t1.department_id, t0.customer_id AS c8493 +FROM orders AS t0 JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN markets AS t2 ON t1.id = t2.id +WHERE t0.customer_id < 239;" +291,12321160,68844660,4406,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, COUNT(orders.id), SUM(regions.id) +FROM users CROSS JOIN orders JOIN regions ON orders.id = regions.id +GROUP BY orders.id;" +292,888,888,175,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.region, t0.id, SUM(t0.id), COUNT(*) +FROM markets AS t0 +GROUP BY t0.region, t0.id +ORDER BY t0.region ASC, t0.id DESC;" +293,2292,5692,281,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.age, t1.id +FROM users AS t0 FULL JOIN books AS t1 ON t0.id = t1.id +WHERE (t0.age = 18 AND (t0.age != 123 AND t1.id <= 89));" +294,848,848,165,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price AS c6589, books.id, COUNT(*) +FROM books +WHERE ((books.price > 77 OR books.id < 3) OR (books.price IN (49, 55, 55, 77) OR books.price IN (26, 66, 66, 68))) +GROUP BY books.price, books.id;" +295,4370,9075,190,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.id, SUM(users.age), COUNT(users.id) +FROM users JOIN departments ON users.id = departments.id +GROUP BY users.id;" +296,432,432,144,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region, t0.id AS c2775, t0.note +FROM markets AS t0 +ORDER BY t0.id DESC;" +297,685683,6467110,1084,22,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.department_id +FROM orders AS t0 JOIN users AS t1 ON t0.id = t1.id FULL JOIN employees AS t2 ON t0.id = t2.id +WHERE ((t2.id NOT BETWEEN 4 AND 6 OR t1.age != 21) OR t0.id <= 61);" +298,988,988,153,4,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id +FROM departments +WHERE ((departments.id NOT IN (4, 4, 5, 7) OR departments.id NOT IN (2, 5)) OR (departments.id NOT BETWEEN 3 AND 27 OR departments.id IS NULL));" +299,2455,3843,262,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT books.price, employees.id AS c7128 +FROM employees RIGHT JOIN books ON employees.id = books.id +WHERE ((books.id > 68 OR books.id >= 52) AND (employees.department_id < 6 OR employees.department_id != 1)) +ORDER BY books.price DESC;" +300,1126398,2251983,2203,3,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT orders.id AS c9121 +FROM orders LEFT JOIN departments ON orders.id = departments.id RIGHT JOIN books ON departments.id = books.id +WHERE orders.customer_id <= 497 +ORDER BY orders.id ASC;" +301,3967,6092,289,3,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id, COUNT(markets.id), SUM(markets.id) +FROM users JOIN markets ON users.id = markets.id +GROUP BY users.id;" +302,74482,410460,402,10,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT regions.id, customers.region_id, employees.id +FROM regions FULL JOIN employees ON regions.id = employees.id JOIN customers ON regions.id = customers.id +ORDER BY customers.region_id ASC, employees.id ASC, regions.id ASC;" +303,1832,1832,166,8,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT employees.id, employees.department_id +FROM employees +WHERE ((employees.id <= 33 OR employees.id = 3) OR (employees.department_id NOT IN (5) AND employees.id = 67));" +304,1342,1342,138,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT employees.department_id AS c3770 +FROM employees;" +305,1822,1822,173,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id AS c2143, COUNT(*), COUNT(*) +FROM regions +WHERE regions.id IN (3, 10, 89) +GROUP BY regions.id;" +306,3097,6272,186,5,transformation 0 JoinCommutativity: 1; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.id, employees.department_id, SUM(employees.id), SUM(employees.department_id) AS c8963 +FROM employees JOIN departments ON employees.id = departments.id +WHERE (departments.id <= 3 OR (employees.id = 68 OR employees.id <= 6)) +GROUP BY employees.id, employees.department_id;" +307,1307657,1553663,2492,4,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t0.note AS c4138 +FROM markets AS t0 JOIN regions AS t1 ON t0.id = t1.id RIGHT JOIN orders AS t2 ON t0.id = t2.id +WHERE ((t2.customer_id = 298 OR t0.region = 'AMERICA') OR t0.region != 'AMERICA') +GROUP BY t0.note;" +308,1092,1092,179,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id, COUNT(*) AS c9125 +FROM departments +WHERE (departments.id > 56 OR departments.id = 4) +GROUP BY departments.id +ORDER BY departments.id DESC;" +309,563,563,166,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c7541, COUNT(t0.id) +FROM markets AS t0 +WHERE (t0.note != 'Fast lane' AND (t0.id = 3 OR t0.note = 'Old World')) +GROUP BY t0.id;" +310,797661,7099420,1142,7,transformation 0 JoinCommutativity: 1; transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.id, t2.id, SUM(t2.id) +FROM customers AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id LEFT JOIN orders AS t2 ON t0.id = t2.id +WHERE (t2.customer_id < 243 OR (t2.id >= 38 AND t2.customer_id = 302)) +GROUP BY t1.id, t2.id +ORDER BY t2.id ASC;" +311,675695,1550433,1021,2,transformation 5 FilterPushdownThroughJoin: 1; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region, t1.customer_id AS c7183 +FROM markets AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t0.id <= 3 OR t0.note = 'Fast lane') AND (t0.note != 'North, South' OR t0.note = 'Fast lane')) +ORDER BY t0.region ASC;" +312,1308302,2255560,1399,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t1.id, t0.id +FROM departments AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id JOIN employees AS t2 ON t1.id = t2.id;" +313,678803,2255816,988,2,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id, orders.id AS c4699, orders.customer_id +FROM departments JOIN orders ON departments.id = orders.id LEFT JOIN employees ON departments.id = employees.id +WHERE employees.department_id NOT BETWEEN 5 AND 33;" +314,2840,2840,185,13,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.age, t0.id +FROM users AS t0 +WHERE (t0.age > 5 AND t0.id != 2) +GROUP BY t0.age, t0.id;" +315,2638,5548,220,5,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM regions AS t0 JOIN departments AS t1 ON t0.id = t1.id +WHERE t1.id IS NOT NULL +GROUP BY t0.id;" +316,822,822,162,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id +FROM books +GROUP BY books.id;" +317,1305606,1550366,1945,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id AS c7996, books.price +FROM books FULL JOIN orders ON books.id = orders.id;" +318,4668,7332,236,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT books.price, books.id +FROM books FULL JOIN employees ON books.id = employees.id RIGHT JOIN regions ON employees.id = regions.id +GROUP BY books.price, books.id;" +319,563,563,148,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id AS c9150, books.price, COUNT(books.id) +FROM books +WHERE ((books.price IN (76, 77) AND books.id BETWEEN 1 AND 96) OR books.price IN (55, 66, 77)) +GROUP BY books.id, books.price;" +320,70514,436342,469,11,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT customers.region_id, employees.id +FROM customers JOIN employees ON customers.id = employees.id;" +321,495125,702625,868,10,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c6071, COUNT(*), COUNT(*) AS c7241 +FROM regions AS t0 CROSS JOIN customers AS t1 +WHERE ((t1.id >= 59 OR t0.id IS NULL) AND t1.id = 126) +GROUP BY t0.id;" +322,111676,403466,462,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t1.region_id, t1.id, t2.price +FROM regions AS t0 RIGHT JOIN customers AS t1 ON t0.id = t1.id LEFT JOIN books AS t2 ON t0.id = t2.id;" +323,112483,650703,353,3,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t0.region_id +FROM customers AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id RIGHT JOIN markets AS t2 ON t1.id = t2.id +WHERE t1.id < 14 +ORDER BY t0.region_id ASC;" +324,678284,2255244,928,3,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id AS c4187, regions.id +FROM departments JOIN orders ON departments.id = orders.id LEFT JOIN regions ON departments.id = regions.id +WHERE regions.id < 4;" +325,1881,5932,317,4,transformation 0 JoinCommutativity: 2; transformation 2 FilterSplit: 4; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note AS c7216, books.price +FROM departments CROSS JOIN markets LEFT JOIN books ON markets.id = books.id +WHERE ((departments.id != 2 AND markets.note = 'Old World') AND (departments.id BETWEEN 1 AND 5 OR departments.id >= 41));" +326,2074,2074,160,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.age +FROM users AS t0;" +327,6154,6154,269,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.age +FROM users AS t0 +GROUP BY t0.age +ORDER BY t0.age ASC;" +328,14356396,44723198,27321,54310,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.department_id +FROM orders CROSS JOIN employees LEFT JOIN regions ON employees.id = regions.id +WHERE ((employees.id <= 25 OR orders.id NOT BETWEEN 47 AND 184) OR orders.id IS NULL);" +329,3541,5858,367,3,transformation 5 FilterPushdownThroughJoin: 1; transformation 10 ProjectionPushdownThroughJoin: 4; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 3; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM markets AS t0 JOIN users AS t1 ON t0.id = t1.id +WHERE (t0.id != 2 OR t0.id != 1) +ORDER BY t0.id ASC;" +330,104084,158984,1037,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 5; transformation 7 FilterToJoinPredicate: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT books.price, employees.id, COUNT(*) +FROM customers LEFT JOIN books ON customers.id = books.id FULL JOIN employees ON customers.id = employees.id +WHERE ((customers.id > 96 AND employees.department_id != 32) AND (employees.id > 5 OR books.id IS NOT NULL)) +GROUP BY books.price, employees.id +ORDER BY books.price DESC, employees.id DESC;" +331,15545500,1755762000,24523,50490,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.region_id, t2.id +FROM orders AS t0 CROSS JOIN regions AS t1 FULL JOIN customers AS t2 ON t1.id = t2.id;" +332,1610,1610,139,10,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id AS c7728 +FROM regions +WHERE regions.id IS NOT NULL;" +333,1415505,2476425,3742,6,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT departments.id, COUNT(*) +FROM orders FULL JOIN departments ON orders.id = departments.id LEFT JOIN customers ON departments.id = customers.id +GROUP BY departments.id;" +334,1775,1775,152,10,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id AS c1480 +FROM regions +WHERE regions.id IS NOT NULL +ORDER BY regions.id DESC;" +335,433,433,131,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price, books.id +FROM books +WHERE (books.price < 44 AND (books.price IN (55, 55, 66, 66) AND books.price != 77)) +ORDER BY books.price DESC, books.id DESC;" +336,1222,1222,133,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.department_id +FROM employees AS t0 +WHERE ((t0.department_id = 12 AND t0.department_id < 35) AND (t0.id IN (4) OR t0.department_id != 31));" +337,2432,2432,210,10,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.id, users.age +FROM users +WHERE users.age IN (1, 21, 33, 64);" +338,1732,1732,138,9,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM regions AS t0 +WHERE ((t0.id NOT BETWEEN 1 AND 15 OR t0.id < 10) AND (t0.id <= 40 OR t0.id != 3));" +339,744,744,142,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id +FROM departments +WHERE ((departments.id <= 98 OR departments.id > 1) AND (departments.id IS NULL OR departments.id IN (3, 3, 30, 91)));" +340,109333,155563,539,13,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id AS c4276, markets.region AS c7002, COUNT(*), COUNT(*) AS c3968 +FROM customers LEFT JOIN markets ON customers.id = markets.id +WHERE (markets.note = 'Fast lane' OR customers.id IS NOT NULL) +GROUP BY customers.region_id, markets.region;" +341,112731,161092,479,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.id, SUM(customers.region_id) +FROM markets JOIN users ON markets.id = users.id RIGHT JOIN customers ON users.id = customers.id +GROUP BY customers.id;" +342,1222,1222,145,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT employees.department_id, employees.id +FROM employees +WHERE ((employees.id >= 66 OR employees.id != 2) AND (employees.id > 69 AND employees.department_id = 33));" +343,3009,3009,139,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age +FROM users +ORDER BY users.age ASC;" +344,2101,3522,188,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.price AS c605, t0.id AS c1651, t1.id +FROM regions AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id +WHERE (t1.id != 3 OR t1.id = 3);" +345,109172,155432,298,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT books.id, customers.id AS c5626 +FROM books LEFT JOIN customers ON books.id = customers.id +ORDER BY customers.id DESC;" +346,10087,29195,369,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.region, t2.id, COUNT(t1.id) +FROM employees AS t0 CROSS JOIN markets AS t1 FULL JOIN regions AS t2 ON t1.id = t2.id +WHERE t0.department_id = 36 +GROUP BY t1.region, t2.id;" +347,679596,6452676,958,0,transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT users.age AS c6973 +FROM users RIGHT JOIN orders ON users.id = orders.id +WHERE users.id > 94;" +348,347600,525100,2006,1187,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id, t0.id +FROM departments AS t0 CROSS JOIN customers AS t1 +WHERE ((t1.id BETWEEN 28 AND 146 AND t1.id IN (46, 50, 104, 146)) OR (t0.id < 4 AND t1.region_id NOT IN (2, 2, 10, 12))) +ORDER BY t1.id DESC;" +349,2832,5592,209,5,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT regions.id, departments.id AS c6050, COUNT(*), COUNT(*) +FROM regions RIGHT JOIN departments ON regions.id = departments.id +WHERE departments.id IS NOT NULL +GROUP BY regions.id, departments.id +ORDER BY departments.id ASC;" +350,3612,6375,203,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id, departments.id AS c3106, COUNT(*) AS c7893 +FROM departments RIGHT JOIN employees ON departments.id = employees.id +GROUP BY employees.department_id, departments.id;" +351,7680023,32852932,14639,261,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.customer_id, t2.id AS c6006, t2.age +FROM orders AS t0 CROSS JOIN departments AS t1 FULL JOIN users AS t2 ON t1.id = t2.id +WHERE (t1.id >= 3 AND (t0.id < 55 OR t0.id < 88));" +352,2676728,5464128,4305,80,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t1.customer_id, t1.id, t0.id +FROM departments AS t0 CROSS JOIN orders AS t1 +WHERE (t1.customer_id IN (426) OR (t1.customer_id IS NULL OR t0.id >= 57));" +353,61000,61000,214,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.region_id +FROM customers AS t0;" +354,2372,2372,196,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.department_id, t0.id +FROM employees AS t0 +WHERE (t0.department_id >= 31 AND (t0.department_id > 34 OR t0.id < 33)) +GROUP BY t0.department_id, t0.id +ORDER BY t0.department_id DESC;" +355,62200,62200,258,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT customers.id, customers.region_id +FROM customers +WHERE customers.id IN (153, 183);" +356,3413,5326,234,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT employees.department_id, departments.id +FROM books LEFT JOIN employees ON books.id = employees.id FULL JOIN departments ON employees.id = departments.id;" +357,1940000,1940000,2422,4914,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id AS c1456, orders.customer_id, COUNT(*), COUNT(orders.customer_id) +FROM orders +WHERE (orders.id > 86 OR orders.customer_id IS NULL) +GROUP BY orders.id, orders.customer_id;" +358,2525,2525,167,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.department_id, t0.id AS c8666, COUNT(*) AS c1170, COUNT(*) +FROM employees AS t0 +WHERE t0.id IS NULL +GROUP BY t0.department_id, t0.id;" +359,2960,2960,174,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id, COUNT(regions.id) AS c1004, COUNT(regions.id) AS c9498 +FROM regions +GROUP BY regions.id;" +360,366,366,122,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.note AS c1949, t0.region +FROM markets AS t0;" +361,130976600,130976600,208219,11,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t2.department_id, COUNT(t2.id), SUM(t0.id) +FROM departments AS t0 CROSS JOIN orders AS t1 CROSS JOIN employees AS t2 +GROUP BY t2.department_id;" +362,785550822,785550822,807862,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.id AS c6682, markets.note AS c3493 +FROM orders CROSS JOIN customers RIGHT JOIN markets ON customers.id = markets.id +GROUP BY markets.id, markets.note;" +363,433,433,178,3,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price +FROM books +WHERE books.price IS NOT NULL +ORDER BY books.price DESC;" +364,69900,69900,225,44,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id AS c2372 +FROM customers +WHERE customers.region_id IN (4, 89) +ORDER BY customers.region_id DESC;" +365,1373762,1705422,1959,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id AS c4468, t1.customer_id, t0.region +FROM markets AS t0 FULL JOIN orders AS t1 ON t0.id = t1.id JOIN customers AS t2 ON t0.id = t2.id +WHERE t0.id <= 17;" +366,2310,2310,171,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.id, users.age +FROM users +WHERE (users.id = 15 OR (users.id != 3 AND users.id IS NULL));" +367,2074,2074,172,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.age +FROM users AS t0;" +368,6428,12062,276,49,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.id, t1.age +FROM books AS t0 CROSS JOIN users AS t1 +WHERE (t1.id = 96 OR (t1.age != 18 OR t0.price <= 61));" +369,850563,1550563,1020,0,transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c4185, SUM(t0.id) +FROM markets AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t0.id BETWEEN 1 AND 7 OR t0.note != 'North, South') AND (t0.note != 'Old World' AND t0.note IN ('ASIA', 'Old World', 'unknown'))) +GROUP BY t0.id;" +370,422,422,147,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.note AS c2495, markets.id, markets.region +FROM markets +WHERE ((markets.note = 'North, South' AND markets.note != 'North, South') OR markets.note != 'Fast lane');" +371,15960,15960,285,110,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT regions.id +FROM regions CROSS JOIN employees;" +372,1293750,1293750,1728,3214,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM orders AS t0 +WHERE (t0.id <= 95 OR t0.customer_id BETWEEN 55 AND 369) +ORDER BY t0.id DESC;" +373,175500,175500,352,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id, COUNT(customers.region_id) +FROM customers +GROUP BY customers.id;" +374,4689550,6653000,3011,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id AS c9907, COUNT(*) AS c8627 +FROM departments CROSS JOIN orders +WHERE ((orders.id >= 79 OR orders.customer_id IN (63, 185, 226, 480)) AND departments.id IS NULL) +GROUP BY orders.id +ORDER BY orders.id ASC;" +375,957500,957500,1491,4999,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.customer_id AS c3627, t0.id AS c1197 +FROM orders AS t0 +WHERE (t0.customer_id <= 56 OR t0.id > 1);" +376,5325,14288,305,9,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t1.id, t0.id, t1.department_id +FROM regions AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id FULL JOIN departments AS t2 ON t0.id = t2.id +WHERE t0.id NOT IN (3);" +377,77608,77608,570,341,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id, customers.region_id, SUM(customers.region_id), SUM(customers.id) +FROM customers +WHERE ((customers.region_id != 9 OR customers.id = 20) AND (customers.region_id >= 3 AND customers.id > 5)) +GROUP BY customers.id, customers.region_id +ORDER BY customers.id DESC, customers.region_id ASC;" +378,822,822,363,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.region, markets.id AS c5516 +FROM markets +GROUP BY markets.region, markets.id;" +379,1342,1342,687,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT employees.department_id, employees.id +FROM employees;" +380,1692500,1692500,4349,4329,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id +FROM orders +WHERE (orders.id < 86 OR (orders.customer_id IN (21, 71) OR orders.customer_id NOT BETWEEN 371 AND 436)) +ORDER BY orders.customer_id ASC;" +381,6120,10354,289,34,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.age AS c6869, t0.id +FROM users AS t0 CROSS JOIN markets AS t1 +WHERE t1.note != 'Old World';" +382,848,848,182,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c3548, t0.note, SUM(t0.id) AS c5991, COUNT(*) +FROM markets AS t0 +WHERE ((t0.note IN ('Fast lane', 'Fast lane', 'Old World') OR t0.region = 'AMERICA') OR t0.id NOT IN (1, 2, 3)) +GROUP BY t0.id, t0.note;" +383,7457700,14597700,5603,8500,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT customers.id, customers.region_id +FROM regions CROSS JOIN customers CROSS JOIN users +WHERE regions.id < 2;" +384,20830,28390,456,9,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.id, books.id, COUNT(markets.id), COUNT(markets.id) +FROM regions CROSS JOIN books CROSS JOIN markets +WHERE regions.id != 5 +GROUP BY markets.id, books.id;" +385,2432,2432,160,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c9611, t0.age AS c4657 +FROM users AS t0 +WHERE ((t0.id >= 3 OR t0.id IS NULL) AND t0.age IS NULL);" +386,1307748,1553776,1822,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT markets.note, markets.region AS c394 +FROM markets LEFT JOIN orders ON markets.id = orders.id LEFT JOIN employees ON orders.id = employees.id;" +387,1692,3832,245,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT employees.id AS c1393 +FROM employees LEFT JOIN books ON employees.id = books.id +WHERE ((employees.id IS NULL AND books.id IS NOT NULL) AND (books.price = 55 AND employees.department_id = 12));" +388,3650500,3650500,11085,25000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT orders.customer_id AS c3285 +FROM orders CROSS JOIN departments;" +389,677641,1553832,987,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 10 ProjectionPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 3; implementation 6 ImplementHashJoin: 2,"SELECT employees.id AS c6990 +FROM orders LEFT JOIN markets ON orders.id = markets.id JOIN employees ON orders.id = employees.id +WHERE markets.id < 3;" +390,113850,160080,1873,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note AS c1438, customers.region_id, markets.id AS c7869 +FROM customers LEFT JOIN markets ON customers.id = markets.id CROSS JOIN regions;" +391,1306995,2251590,1424,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT orders.customer_id AS c6961, departments.id +FROM departments LEFT JOIN orders ON departments.id = orders.id +GROUP BY orders.customer_id, departments.id +ORDER BY departments.id DESC;" +392,43248,62366,679,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id AS c8803, SUM(users.id), SUM(employees.id) +FROM users CROSS JOIN employees LEFT JOIN markets ON employees.id = markets.id +WHERE ((markets.id >= 18 OR users.id = 4) OR employees.id >= 2) +GROUP BY employees.department_id +ORDER BY employees.department_id ASC;" +393,93800000,291050000,224105,384231,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 2; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT orders.customer_id, customers.id +FROM orders CROSS JOIN customers +WHERE (orders.id IS NOT NULL AND (orders.customer_id <= 190 AND customers.region_id IN (1, 5, 8, 9)));" +394,1832,1832,189,8,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM employees AS t0 +WHERE t0.department_id NOT IN (6, 11, 32, 94);" +395,2390300,2390300,10768,15000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT orders.id, orders.customer_id, books.price AS c2061 +FROM orders CROSS JOIN books;" +396,422,422,131,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.region, markets.id, markets.note +FROM markets +WHERE markets.region IS NULL;" +397,175500,175500,363,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.region_id, SUM(t0.region_id) +FROM customers AS t0 +GROUP BY t0.id, t0.region_id;" +398,69916,401976,271,10,transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT regions.id, customers.id, customers.region_id +FROM regions JOIN customers ON regions.id = customers.id +WHERE (customers.id NOT BETWEEN 18 AND 150 OR regions.id IN (1, 9, 10, 63));" +399,1092,1092,246,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id AS c2818, COUNT(departments.id), COUNT(departments.id) +FROM departments +WHERE departments.id <= 1 +GROUP BY departments.id +ORDER BY departments.id DESC;" +400,2226,3466,284,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note AS c7054, markets.region, regions.id +FROM markets FULL JOIN regions ON markets.id = regions.id;" +401,112878,438256,453,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id, customers.id +FROM customers FULL JOIN employees ON customers.id = employees.id +GROUP BY customers.region_id, customers.id;" +402,1456314,175572814,2560,2194,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id, orders.customer_id AS c5129, customers.id AS c8733 +FROM customers FULL JOIN orders ON customers.id = orders.id +WHERE (orders.customer_id > 283 AND (orders.customer_id IS NOT NULL OR orders.customer_id IS NULL));" +403,1246000,1246000,1346,44,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, COUNT(*), COUNT(t0.customer_id) AS c7688 +FROM orders AS t0 +WHERE t0.customer_id IN (15, 185, 359, 461) +GROUP BY t0.id;" +404,1122,1122,134,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id AS c6964 +FROM regions +WHERE (regions.id = 86 AND (regions.id IN (5, 9) AND regions.id < 9));" +405,610,610,130,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id +FROM departments;" +406,6451,16190,217,9,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.id AS c2861, t0.id +FROM regions AS t0 LEFT JOIN users AS t1 ON t0.id = t1.id +WHERE t1.id IS NOT NULL +GROUP BY t1.id, t0.id +ORDER BY t0.id DESC;" +407,111830,405275,331,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT customers.id, customers.region_id, departments.id AS c3112 +FROM customers FULL JOIN regions ON customers.id = regions.id JOIN departments ON regions.id = departments.id +ORDER BY departments.id ASC, customers.id DESC, customers.region_id ASC;" +408,4788,10132,363,7,transformation 0 JoinCommutativity: 2; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id AS c868, t2.region +FROM users AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id CROSS JOIN markets AS t2 +WHERE ((t1.id <= 8 OR t0.age IN (64)) AND (t2.id = 1 OR t1.id = 3));" +409,113508,650692,318,1,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age AS c3766, customers.id AS c5291, markets.id +FROM users LEFT JOIN customers ON users.id = customers.id LEFT JOIN markets ON customers.id = markets.id +WHERE markets.id NOT IN (1, 1, 1, 3);" +410,422,422,152,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.region, t0.note, t0.id AS c8799 +FROM markets AS t0 +WHERE (t0.id IN (1) OR (t0.note IN ('AMERICA', 'Fast lane', 'North, South', 'unknown') AND t0.note != 'Old World'));" +411,822,822,137,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.price, t0.id, COUNT(*), SUM(t0.id) AS c6022 +FROM books AS t0 +GROUP BY t0.price, t0.id;" +412,3645,5814,228,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age, books.price +FROM books LEFT JOIN users ON books.id = users.id +WHERE books.price NOT BETWEEN 66 AND 66;" +413,2398822,2721522,2134,1500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.id, books.price, COUNT(users.age) +FROM users CROSS JOIN customers RIGHT JOIN books ON users.id = books.id +GROUP BY customers.id, books.price;" +414,4551000,8751000,2981,920,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT regions.id +FROM orders CROSS JOIN regions +WHERE orders.id <= 92;" +415,6102,12200,244,6,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t0.id, t1.id +FROM employees AS t0 JOIN regions AS t1 ON t0.id = t1.id +GROUP BY t0.id, t1.id +ORDER BY t0.id DESC;" +416,1122,1362,330,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.note, books.id, markets.id +FROM books RIGHT JOIN markets ON books.id = markets.id +ORDER BY books.id DESC;" +417,785942,1705822,1166,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.price AS c5776, t0.id AS c5445, COUNT(t1.price) +FROM customers AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id JOIN orders AS t2 ON t1.id = t2.id +GROUP BY t1.price, t0.id;" +418,1306128,1550888,1442,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.id, t0.customer_id, COUNT(*) +FROM orders AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id +GROUP BY t1.id, t0.customer_id +ORDER BY t0.customer_id DESC;" +419,112961,233260,333,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t0.id AS c6883, t2.id +FROM departments AS t0 LEFT JOIN customers AS t1 ON t0.id = t1.id JOIN users AS t2 ON t1.id = t2.id;" +420,894875,894875,1916,2330,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM orders AS t0 +WHERE (t0.customer_id >= 270 AND (t0.customer_id IN (108, 315, 364) OR t0.id > 11)) +ORDER BY t0.id ASC;" +421,4298,6932,201,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT markets.note, markets.region +FROM markets FULL JOIN regions ON markets.id = regions.id JOIN employees ON regions.id = employees.id +WHERE ((regions.id > 39 AND employees.department_id IS NOT NULL) OR (markets.region IS NOT NULL AND markets.id != 47));" +422,932,1363,208,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id AS c7037, t0.region AS c4401, t0.id AS c405 +FROM markets AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id +WHERE t0.note = 'Old World' +ORDER BY t0.id DESC;" +423,2690,2690,255,15,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT markets.note, departments.id, markets.id +FROM departments CROSS JOIN markets;" +424,682435,4014820,964,10,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 2,"SELECT orders.customer_id AS c6591, users.age AS c6962, users.id AS c7318 +FROM users JOIN regions ON users.id = regions.id JOIN orders ON users.id = orders.id;" +425,112495,647074,334,501,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, t1.age, t0.region_id AS c768 +FROM customers AS t0 LEFT JOIN users AS t1 ON t0.id = t1.id;" +426,652500,652500,1144,102,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id +FROM orders +WHERE orders.id BETWEEN 43 AND 144;" +427,2676,2676,168,7,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.age, users.id AS c2712 +FROM users +WHERE users.id >= 11;" +428,1252,1658,481,3,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT markets.id +FROM books CROSS JOIN markets +WHERE books.id IN (2);" +429,72226,160616,507,3,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id, COUNT(*) +FROM customers AS t0 JOIN books AS t1 ON t0.id = t1.id CROSS JOIN departments AS t2 +WHERE (t2.id <= 1 OR t1.id IS NULL) +GROUP BY t0.region_id;" +430,1377,2113,260,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id AS c2823, t1.id AS c3548, SUM(t1.price), COUNT(*) AS c5225 +FROM departments AS t0 RIGHT JOIN books AS t1 ON t0.id = t1.id +WHERE t1.price > 55 +GROUP BY t0.id, t1.id;" +431,1305662,1550422,1240,2,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.id, orders.id, books.price AS c8011 +FROM books LEFT JOIN orders ON books.id = orders.id +WHERE ((books.id > 1 OR books.price BETWEEN 66 AND 77) OR (books.price <= 66 AND orders.id = 187));" +432,14852,48496,400,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id, books.id, COUNT(users.id) +FROM users CROSS JOIN books FULL JOIN employees ON users.id = employees.id +WHERE (books.id > 3 AND (users.age IS NOT NULL OR employees.id BETWEEN 30 AND 49)) +GROUP BY users.id, books.id;" +433,3028,3028,320,9,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.age +FROM users AS t0 +WHERE t0.age >= 64 +ORDER BY t0.id DESC;" +434,68783,155433,264,0,transformation 0 JoinCommutativity: 1; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.note +FROM customers AS t0 JOIN markets AS t1 ON t0.id = t1.id +WHERE (t1.note IS NULL OR t0.id = 123) +ORDER BY t1.note DESC;" +435,3042,3042,218,9,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.id +FROM users +WHERE (users.id IS NULL OR users.age IN (5, 12, 33, 64));" +436,3547,14722,183,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT t1.id +FROM users AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id +WHERE t1.id = 8;" +437,3432,4918,250,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT t1.id AS c9930, SUM(t0.id), COUNT(t1.id) AS c171 +FROM markets AS t0 FULL JOIN books AS t1 ON t0.id = t1.id RIGHT JOIN regions AS t2 ON t1.id = t2.id +GROUP BY t1.id +ORDER BY t1.id DESC;" +438,61346,61346,258,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT customers.id +FROM customers +WHERE ((customers.region_id NOT BETWEEN 2 AND 19 AND customers.id < 36) AND customers.id > 39);" +439,1306015,2250610,1289,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id AS c4906, orders.customer_id +FROM orders RIGHT JOIN departments ON orders.id = departments.id;" +440,225000,225000,369,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id, COUNT(*), COUNT(*) AS c6478 +FROM customers +GROUP BY customers.id +ORDER BY customers.id ASC;" +441,1220,1220,149,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions;" +442,3633,5692,185,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region AS c3227, users.age +FROM users FULL JOIN markets ON users.id = markets.id +WHERE (users.id IN (7, 15, 96) AND (users.age IN (33) OR markets.note != 'North, South'));" +443,366,366,149,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT books.price AS c5763 +FROM books;" +444,1278,1518,206,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.price, t1.id +FROM markets AS t0 FULL JOIN books AS t1 ON t0.id = t1.id +WHERE ((t1.price >= 55 AND t0.id IS NULL) OR (t0.note != 'North, South' OR t0.region != 'EUROPE')) +ORDER BY t1.price DESC;" +445,3073124,4408024,2652,5000,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t2.id, t1.region_id, t1.id AS c770 +FROM regions AS t0 CROSS JOIN customers AS t1 CROSS JOIN books AS t2 +WHERE (t2.id > 34 OR t2.id IN (2)) +ORDER BY t1.id ASC;" +446,111332,436710,455,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, t1.region_id +FROM employees AS t0 FULL JOIN customers AS t1 ON t0.id = t1.id +WHERE t0.id != 14;" +447,2320,3126,261,2,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.region, COUNT(t0.id), COUNT(*) AS c7336 +FROM books AS t0 CROSS JOIN markets AS t1 +WHERE (t1.id IS NOT NULL OR (t0.id <= 2 AND t0.price IS NOT NULL)) +GROUP BY t1.region +ORDER BY t1.region DESC;" +448,2778,4232,391,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT books.id, SUM(books.price) +FROM employees FULL JOIN books ON employees.id = books.id +GROUP BY books.id;" +449,1363,1363,160,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, COUNT(*) AS c6748 +FROM employees AS t0 +WHERE ((t0.department_id <= 31 OR t0.id BETWEEN 5 AND 49) AND (t0.department_id IS NULL AND t0.department_id BETWEEN 5 AND 31)) +GROUP BY t0.id;" +450,5219,5219,162,16,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.id +FROM users +GROUP BY users.id;" +451,633,633,194,4,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id +FROM departments +WHERE ((departments.id BETWEEN 4 AND 4 OR departments.id <= 4) AND departments.id <= 47) +ORDER BY departments.id DESC;" +452,1310637,6454068,1484,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id AS c3185 +FROM orders RIGHT JOIN users ON orders.id = users.id +WHERE (users.age IN (17, 34, 64) OR (users.id < 12 AND orders.id IS NULL)) +GROUP BY users.id +ORDER BY users.id ASC;" +453,1220,1220,129,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions;" +454,1311205,6455219,1428,16,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, COUNT(*) +FROM orders AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id +GROUP BY t1.id;" +455,3072,4706,208,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT employees.id AS c8673 +FROM books LEFT JOIN employees ON books.id = employees.id JOIN markets ON books.id = markets.id;" +456,44507,154580,433,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.department_id, t0.age +FROM users AS t0 CROSS JOIN employees AS t1 RIGHT JOIN regions AS t2 ON t0.id = t2.id +WHERE ((t0.id IN (13, 79) OR t2.id >= 44) AND t2.id IS NULL) +ORDER BY t0.age DESC;" +457,957500,957500,1329,4999,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.id +FROM orders +WHERE (orders.id != 33 OR orders.customer_id > 461);" +458,2074,2074,152,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.age, t0.id AS c1193 +FROM users AS t0;" +459,685169,19762099,1076,55,transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM departments AS t0 CROSS JOIN employees AS t1 RIGHT JOIN orders AS t2 ON t1.id = t2.id +WHERE t1.id <= 91 +ORDER BY t0.id DESC;" +460,682089,6453009,897,17,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.customer_id, t0.age, t1.id +FROM users AS t0 JOIN orders AS t1 ON t0.id = t1.id +ORDER BY t1.customer_id ASC;" +461,1308264,4003644,2072,0,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT regions.id AS c8461 +FROM orders LEFT JOIN regions ON orders.id = regions.id RIGHT JOIN books ON regions.id = books.id +WHERE books.id NOT BETWEEN 1 AND 14;" +462,60649,409922,390,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 9 FilterLiftThroughJoin: 2; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 2,"SELECT regions.id, employees.department_id AS c5017, customers.region_id +FROM customers JOIN regions ON customers.id = regions.id RIGHT JOIN employees ON regions.id = employees.id +WHERE (employees.id < 54 AND customers.id = 67);" +463,2074,2074,149,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT users.id AS c7578 +FROM users;" +464,1361,1916,208,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id, markets.id, markets.region +FROM departments LEFT JOIN markets ON departments.id = markets.id;" +465,3256,3256,419,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.department_id AS c3122, COUNT(t0.id) AS c9921 +FROM employees AS t0 +GROUP BY t0.department_id;" +466,61000,61000,241,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM customers AS t0;" +467,5889,16616,256,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.age AS c4980 +FROM users AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id +ORDER BY t0.age DESC;" +468,3664,3664,167,9,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM regions AS t0 +WHERE (t0.id > 5 OR (t0.id NOT IN (1, 3, 9, 96) OR t0.id NOT IN (1, 5))) +GROUP BY t0.id;" +469,676872,1550822,939,3,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c3150, COUNT(t1.customer_id) +FROM markets AS t0 JOIN orders AS t1 ON t0.id = t1.id +GROUP BY t0.id;" +470,2228125,2228125,2822,5000,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, COUNT(t0.id), COUNT(t0.id) +FROM orders AS t0 +WHERE ((t0.customer_id IS NOT NULL OR t0.customer_id < 310) OR t0.id <= 5) +GROUP BY t0.id;" +471,348050982,1135550932,92238,1500,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id +FROM customers CROSS JOIN orders JOIN departments ON orders.id = departments.id +WHERE departments.id NOT IN (3, 4, 33, 52) +ORDER BY customers.region_id DESC;" +472,1363,1363,159,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.id +FROM employees +WHERE (employees.department_id != 19 AND employees.id IN (2, 5)) +GROUP BY employees.id;" +473,6650300,6650300,8463,3,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.price +FROM orders AS t0 CROSS JOIN books AS t1 +GROUP BY t1.price;" +474,822,822,164,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price AS c6021, books.id AS c591 +FROM books +GROUP BY books.price, books.id;" +475,563,563,186,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price, SUM(books.price) AS c2521 +FROM books +WHERE books.price = 66 +GROUP BY books.price;" +476,2690,2690,298,15,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT markets.region, markets.note +FROM markets CROSS JOIN departments;" +477,3322,5213,237,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT books.id, COUNT(books.price) +FROM regions LEFT JOIN books ON regions.id = books.id FULL JOIN departments ON regions.id = departments.id +WHERE books.id IS NOT NULL +GROUP BY books.id;" +478,1377,2113,241,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT books.id, books.price AS c3187, COUNT(books.price), SUM(departments.id) +FROM departments JOIN books ON departments.id = books.id +WHERE books.id <= 98 +GROUP BY books.id, books.price;" +479,3364,8164,334,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT regions.id, departments.id +FROM departments CROSS JOIN regions +WHERE (departments.id >= 3 AND departments.id > 5);" +480,2979,5692,171,2,transformation 5 FilterPushdownThroughJoin: 1; transformation 6 InToOrChain: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT markets.id, markets.region, users.age +FROM markets JOIN users ON markets.id = users.id +WHERE (markets.note != 'ASIA' AND markets.id IN (1, 3, 89));" +481,4960163,9590890,14129,30000,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id AS c5950, t2.id AS c7759 +FROM users AS t0 LEFT JOIN employees AS t1 ON t0.id = t1.id CROSS JOIN orders AS t2 +WHERE t1.id IS NOT NULL;" +482,942989,2566838,2592,5000,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.department_id AS c6048, markets.region, markets.note +FROM markets LEFT JOIN employees ON markets.id = employees.id CROSS JOIN orders +WHERE (markets.id <= 2 AND (employees.id = 68 OR markets.region != 'AMERICA'));" +483,1307418,2252623,1534,3,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.id, COUNT(*) AS c2015 +FROM orders AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE t1.id = 3 +GROUP BY t2.id;" +484,8367,19707,314,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.department_id +FROM departments AS t0 JOIN employees AS t1 ON t0.id = t1.id CROSS JOIN users AS t2 +WHERE t1.id BETWEEN 43 AND 48 +ORDER BY t1.department_id DESC;" +485,744,744,158,5,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id +FROM departments +WHERE departments.id <= 96;" +486,1120,1120,156,5,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id AS c4502 +FROM departments +WHERE ((departments.id IS NOT NULL OR departments.id <= 2) OR (departments.id > 3 OR departments.id IN (1, 5))) +ORDER BY departments.id ASC;" +487,7676202,8350932,11659,15000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id AS c1414, t2.id AS c4044 +FROM orders AS t0 CROSS JOIN departments AS t1 RIGHT JOIN books AS t2 ON t1.id = t2.id +ORDER BY t1.id DESC;" +488,805000,805000,1406,4992,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id +FROM orders +WHERE orders.customer_id != 133;" +489,2140,2140,184,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id AS c266 +FROM regions +WHERE ((regions.id = 63 AND regions.id IN (1, 36)) OR (regions.id NOT IN (7) AND regions.id IS NULL)) +GROUP BY regions.id;" +490,5689,9112,367,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT markets.region AS c2686, users.age AS c2053, employees.department_id +FROM users RIGHT JOIN markets ON users.id = markets.id RIGHT JOIN employees ON markets.id = employees.id +ORDER BY users.age DESC, employees.department_id DESC, markets.region DESC;" +491,2824,10532,229,4,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 1; transformation 6 InToOrChain: 1; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT employees.id, regions.id AS c47 +FROM regions LEFT JOIN employees ON regions.id = employees.id +WHERE employees.id NOT IN (3, 6, 6, 75);" +492,69422,405122,358,0,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 5; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT regions.id AS c9972 +FROM customers LEFT JOIN regions ON customers.id = regions.id JOIN departments ON regions.id = departments.id +WHERE (departments.id IN (8, 31, 37) AND regions.id = 1);" +493,3644,5703,230,1,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.age AS c4963, t0.note +FROM markets AS t0 FULL JOIN users AS t1 ON t0.id = t1.id +WHERE t0.region != 'AMERICA' +ORDER BY t0.note ASC;" +494,3647,5538,301,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT regions.id, books.price, SUM(books.price) +FROM departments FULL JOIN books ON departments.id = books.id FULL JOIN regions ON books.id = regions.id +GROUP BY regions.id, books.price +ORDER BY books.price ASC;" +495,110392,226982,468,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT t2.region +FROM departments AS t0 RIGHT JOIN customers AS t1 ON t0.id = t1.id LEFT JOIN markets AS t2 ON t0.id = t2.id +ORDER BY t2.region ASC;" +496,6093,18632,221,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT employees.department_id, books.price +FROM users LEFT JOIN employees ON users.id = employees.id LEFT JOIN books ON users.id = books.id +ORDER BY books.price ASC;" +497,4370,9075,188,5,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.age, t1.id, SUM(t0.id) AS c2929 +FROM departments AS t0 JOIN users AS t1 ON t0.id = t1.id +GROUP BY t1.age, t1.id;" +498,2024,3522,169,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 1; transformation 6 InToOrChain: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT regions.id AS c3913, markets.region +FROM regions JOIN markets ON regions.id = markets.id +WHERE ((markets.note = 'Fast lane' AND markets.id IN (1, 3, 51)) OR markets.region IN ('AMERICA', 'EUROPE', 'EUROPE'));" +499,1640,1640,244,4,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, COUNT(*), COUNT(departments.id) +FROM departments +WHERE ((departments.id IN (2, 58, 99) OR departments.id >= 2) OR departments.id >= 2) +GROUP BY departments.id;" +500,3039,8394,362,4,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 2; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id, users.age AS c9399, departments.id +FROM users JOIN departments ON users.id = departments.id +WHERE (users.age NOT IN (21, 22, 123) AND (departments.id < 4 OR users.id <= 15));" +501,4641630,7311075,7972,15000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id, t0.id, t0.note +FROM markets AS t0 CROSS JOIN orders AS t1 LEFT JOIN departments AS t2 ON t1.id = t2.id +ORDER BY t1.id ASC, t0.id DESC, t0.note DESC;" +502,910557480,3235555219,1646785,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.id AS c3228, t0.id +FROM orders AS t0 CROSS JOIN customers AS t1 FULL JOIN users AS t2 ON t0.id = t2.id +GROUP BY t2.id, t0.id;" +503,1048,1048,174,5,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, SUM(t0.id), COUNT(*) +FROM departments AS t0 +WHERE t0.id IS NOT NULL +GROUP BY t0.id;" +504,8676000,12876000,4179,6910,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.customer_id +FROM orders AS t0 CROSS JOIN regions AS t1 +WHERE t0.customer_id <= 74 +ORDER BY t0.id ASC, t0.customer_id DESC;" +505,588,588,156,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.id, markets.region, markets.note +FROM markets +WHERE ((markets.id <= 3 OR markets.note != 'unknown') OR markets.id IN (2, 68)) +ORDER BY markets.note ASC;" +506,5558,5558,346,33,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT markets.region, employees.id AS c412, employees.department_id +FROM markets CROSS JOIN employees;" +507,3612,6540,214,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.id, COUNT(departments.id), COUNT(employees.department_id) +FROM departments RIGHT JOIN employees ON departments.id = employees.id +GROUP BY employees.id +ORDER BY employees.id DESC;" +508,4300,12463,243,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.department_id, SUM(t2.price) AS c7329 +FROM regions AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN books AS t2 ON t1.id = t2.id +WHERE (t1.id BETWEEN 3 AND 14 OR t1.department_id IN (1, 3, 33)) +GROUP BY t1.department_id;" +509,107641,416025,432,8,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT users.id, users.age, COUNT(*), COUNT(users.age) +FROM regions RIGHT JOIN customers ON regions.id = customers.id RIGHT JOIN users ON regions.id = users.id +WHERE customers.id != 8 +GROUP BY users.id, users.age;" +510,8645,29395,414,18,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.note, employees.id AS c4181 +FROM markets CROSS JOIN regions LEFT JOIN employees ON regions.id = employees.id +WHERE employees.department_id < 54 +ORDER BY markets.note DESC;" +511,1833,1833,172,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.age +FROM users AS t0 +WHERE ((t0.id = 3 AND t0.id IS NULL) AND t0.id BETWEEN 6 AND 95) +ORDER BY t0.age DESC;" +512,3009,3009,153,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c74, t0.age +FROM users AS t0 +ORDER BY t0.id ASC, t0.age DESC;" +513,1342,1342,123,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.department_id AS c4862, t0.id +FROM employees AS t0;" +514,1480714,175597214,2113,153,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.region_id AS c5690, customers.id, orders.customer_id AS c2293 +FROM customers RIGHT JOIN orders ON customers.id = orders.id +WHERE ((orders.id IS NULL OR customers.id = 97) OR orders.customer_id <= 14);" +515,822,822,151,2,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.region, COUNT(markets.id), COUNT(*) +FROM markets +GROUP BY markets.region;" +516,4314,6876,225,3,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 2; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT regions.id AS c4858, markets.note, markets.id +FROM employees LEFT JOIN markets ON employees.id = markets.id JOIN regions ON markets.id = regions.id;" +517,5471,15210,204,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.age AS c8314, t1.id, t0.id +FROM regions AS t0 FULL JOIN users AS t1 ON t0.id = t1.id +WHERE t0.id IS NOT NULL;" +518,1306180,2250775,2519,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT orders.customer_id +FROM orders LEFT JOIN departments ON orders.id = departments.id +ORDER BY orders.customer_id DESC;" +519,3794,7253,267,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.id, t0.id +FROM users AS t0 JOIN books AS t1 ON t0.id = t1.id LEFT JOIN departments AS t2 ON t0.id = t2.id +WHERE t1.id IS NULL +ORDER BY t2.id DESC, t0.id DESC;" +520,422,422,151,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.price +FROM books +WHERE (books.price != 55 OR (books.price BETWEEN 66 AND 66 AND books.id < 1));" +521,422,422,143,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.id AS c5535, books.price +FROM books +WHERE ((books.id < 3 OR books.id IS NULL) AND books.id IS NOT NULL);" +522,1342,1342,131,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM employees AS t0;" +523,10286,11526,343,170,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, t2.id +FROM markets AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id CROSS JOIN users AS t2;" +524,544,544,131,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.price +FROM books +WHERE books.price NOT IN (77);" +525,152440,477818,448,187,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id AS c7276, users.age, employees.department_id +FROM customers RIGHT JOIN employees ON customers.id = employees.id CROSS JOIN users +ORDER BY employees.department_id ASC;" +526,2320,2320,131,11,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM employees AS t0 +WHERE ((t0.id != 2 AND t0.department_id < 3) OR (t0.department_id NOT IN (1, 33) OR t0.department_id NOT IN (31)));" +527,20391700,27531700,21735,16,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.id, users.age, COUNT(*) AS c3042, COUNT(users.age) +FROM orders CROSS JOIN users +WHERE orders.id IS NOT NULL +GROUP BY users.id, users.age;" +528,563,563,181,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.region AS c5933 +FROM markets AS t0 +WHERE ((t0.id != 1 AND t0.region = 'AMERICA') AND (t0.id IS NOT NULL OR t0.region = 'EUROPE')) +GROUP BY t0.id, t0.region;" +529,1415835,2476590,3712,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT orders.id, orders.customer_id, COUNT(*) +FROM departments FULL JOIN orders ON departments.id = orders.id FULL JOIN customers ON orders.id = customers.id +GROUP BY orders.id, orders.customer_id +ORDER BY orders.id DESC, orders.customer_id DESC;" +530,920550000,920550000,1297662,2500000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id AS c9140, customers.region_id, customers.id +FROM customers CROSS JOIN orders +ORDER BY customers.region_id ASC, customers.id ASC, orders.customer_id DESC;" +531,570413,2255572,1095,0,transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id, orders.id AS c4291, employees.department_id +FROM departments JOIN orders ON departments.id = orders.id LEFT JOIN employees ON orders.id = employees.id +WHERE orders.id IN (99);" +532,633,633,143,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM departments AS t0 +WHERE (t0.id < 2 AND (t0.id IN (1, 1, 5) OR t0.id >= 5)) +ORDER BY t0.id ASC;" +533,7775500,7775500,7589,25000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c1841, t1.id +FROM departments AS t0 CROSS JOIN orders AS t1 +ORDER BY t0.id ASC;" +534,5903,17315,226,6,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.age AS c2013, COUNT(*) AS c86, COUNT(employees.department_id) +FROM employees JOIN users ON employees.id = users.id +WHERE users.id != 10 +GROUP BY users.age +ORDER BY users.age ASC;" +535,1220,1220,126,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM regions AS t0;" +536,16891862,17771532,23633,55000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT orders.customer_id +FROM employees CROSS JOIN orders FULL JOIN markets ON orders.id = markets.id +ORDER BY orders.customer_id ASC;" +537,121968,668932,851,5,transformation 0 JoinCommutativity: 2; transformation 2 FilterSplit: 5; transformation 5 FilterPushdownThroughJoin: 4; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.region_id, t0.age, SUM(t0.id), COUNT(t2.id) AS c2707 +FROM users AS t0 LEFT JOIN customers AS t1 ON t0.id = t1.id CROSS JOIN regions AS t2 +WHERE ((t1.region_id >= 6 OR t0.age != 22) AND (t0.age >= 64 AND t0.age IN (22, 64))) +GROUP BY t1.region_id, t0.age;" +538,932,1363,195,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT books.id, markets.region +FROM markets RIGHT JOIN books ON markets.id = books.id +WHERE markets.region = 'AMERICA' +ORDER BY markets.region DESC, books.id DESC;" +539,11604,23544,239,22,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT regions.id +FROM employees CROSS JOIN regions +WHERE regions.id NOT BETWEEN 2 AND 9;" +540,678650,1554258,1074,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.price +FROM orders AS t0 JOIN books AS t1 ON t0.id = t1.id FULL JOIN employees AS t2 ON t0.id = t2.id +WHERE ((t0.customer_id > 224 AND t2.id NOT IN (2, 66, 67)) OR t0.customer_id < 119) +GROUP BY t1.price;" +541,7022,9744,836,11,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id, COUNT(t1.department_id) AS c9704, COUNT(t1.department_id) +FROM books AS t0 CROSS JOIN employees AS t1 +WHERE t0.id <= 2 +GROUP BY t1.id;" +542,115640,650219,432,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id AS c2902 +FROM customers LEFT JOIN users ON customers.id = users.id +GROUP BY customers.region_id;" +543,3449,5392,225,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT books.price AS c9867, employees.department_id +FROM departments RIGHT JOIN books ON departments.id = books.id FULL JOIN employees ON departments.id = employees.id +ORDER BY employees.department_id DESC, books.price DESC;" +544,3332,5282,327,15,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.note AS c8307, departments.id, books.price AS c1104 +FROM markets JOIN books ON markets.id = books.id CROSS JOIN departments +WHERE (departments.id >= 24 OR (markets.id BETWEEN 1 AND 2 OR books.id <= 96)) +ORDER BY departments.id DESC, books.price ASC, markets.note DESC;" +545,574,574,419,2,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.price AS c637, t0.id AS c2803 +FROM books AS t0 +WHERE t0.id IN (1, 2, 24) +GROUP BY t0.price, t0.id +ORDER BY t0.id ASC, t0.price ASC;" +546,366,366,131,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.note, t0.id AS c5805 +FROM markets AS t0;" +547,159512,159512,395,329,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id, customers.region_id, COUNT(customers.id), COUNT(customers.id) AS c8210 +FROM customers +WHERE (customers.id < 119 OR (customers.region_id <= 6 AND customers.region_id != 10)) +GROUP BY customers.id, customers.region_id;" +548,433,433,143,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id AS c7062, books.price AS c5593 +FROM books +WHERE books.price <= 66 +ORDER BY books.price DESC;" +549,515777,1057204,2573,7000,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id AS c529 +FROM employees RIGHT JOIN users ON employees.id = users.id CROSS JOIN customers +WHERE ((employees.department_id != 65 AND customers.region_id >= 8) OR users.id < 15);" +550,112495,647074,363,501,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id +FROM users FULL JOIN customers ON users.id = customers.id;" +551,125278,1217026,451,33,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id AS c8412, t1.price AS c8263, COUNT(t1.id) +FROM employees AS t0 CROSS JOIN books AS t1 LEFT JOIN customers AS t2 ON t0.id = t2.id +GROUP BY t0.id, t1.price;" +552,61000,61000,264,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT customers.id +FROM customers;" +553,2585,3973,222,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.department_id, t0.price, COUNT(*) AS c1800 +FROM books AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id +WHERE (t1.department_id IS NOT NULL AND (t0.price IS NOT NULL OR t0.id IN (3))) +GROUP BY t1.department_id, t0.price;" +554,686187,175725622,1888,0,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 2,"SELECT customers.id +FROM orders JOIN customers ON orders.id = customers.id RIGHT JOIN departments ON customers.id = departments.id +WHERE (orders.id IN (22, 50, 61, 149) AND orders.id IS NULL);" +555,563,563,171,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.note +FROM markets AS t0 +WHERE (t0.id < 1 AND (t0.region = 'AMERICA' AND t0.id BETWEEN 2 AND 3)) +GROUP BY t0.note;" +556,23036435700,23036435700,24496249,10,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id +FROM users CROSS JOIN customers CROSS JOIN orders +GROUP BY customers.region_id;" +557,110500,110500,396,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id, customers.id +FROM customers +ORDER BY customers.region_id ASC;" +558,3577,5636,167,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.region +FROM markets AS t0 FULL JOIN users AS t1 ON t0.id = t1.id;" +559,1220,1220,126,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions;" +560,672828,12055320,1633,27,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 5; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT employees.department_id, employees.id AS c3726, books.id +FROM books CROSS JOIN employees RIGHT JOIN orders ON employees.id = orders.id +WHERE (employees.department_id NOT IN (1, 35, 39, 88) AND (orders.id IS NOT NULL AND orders.customer_id != 133));" +561,111672,438832,491,2,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t2.note AS c4598, t0.department_id AS c1378, t0.id AS c1288 +FROM employees AS t0 FULL JOIN customers AS t1 ON t0.id = t1.id FULL JOIN markets AS t2 ON t0.id = t2.id +WHERE t2.region = 'AMERICA';" +562,1310198,6455692,1863,0,transformation 0 JoinCommutativity: 2; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.age, t2.price +FROM orders AS t0 LEFT JOIN users AS t1 ON t0.id = t1.id JOIN books AS t2 ON t1.id = t2.id +WHERE ((t1.age = 123 AND t2.id IS NULL) OR (t0.customer_id = 354 AND t0.customer_id = 252));" +563,3110874,5210874,2149,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, COUNT(*), COUNT(*) +FROM markets AS t0 CROSS JOIN orders AS t1 RIGHT JOIN books AS t2 ON t1.id = t2.id +WHERE t2.price <= 42 +GROUP BY t0.id +ORDER BY t0.id DESC;" +564,671553,4351363,1635,0,transformation 5 FilterPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c6853 +FROM employees AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t1.id IS NULL AND t1.customer_id < 172) AND t0.id != 67) +GROUP BY t0.id;" +565,89730,161130,646,765,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT departments.id, users.id AS c5132 +FROM users CROSS JOIN regions CROSS JOIN departments +WHERE regions.id <= 9;" +566,68272,155422,286,2,transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT markets.id, markets.region, markets.note AS c6111 +FROM markets RIGHT JOIN customers ON markets.id = customers.id +WHERE markets.id IN (2, 3, 23, 41);" +567,610000,610000,1279,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id +FROM orders;" +568,366,366,122,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT books.price, books.id AS c5367 +FROM books;" +569,8406,23335,962,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.age +FROM books AS t0 CROSS JOIN departments AS t1 FULL JOIN users AS t2 ON t0.id = t2.id +WHERE ((t1.id IS NOT NULL OR t2.id = 4) AND (t0.price BETWEEN 66 AND 77 OR t0.price IS NULL)) +GROUP BY t2.age +ORDER BY t2.age ASC;" +570,3764,3764,176,11,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.department_id AS c7134, t0.id AS c6407, SUM(t0.department_id), COUNT(*) +FROM employees AS t0 +WHERE (t0.id IN (1, 42, 51) OR (t0.department_id >= 3 OR t0.department_id <= 34)) +GROUP BY t0.department_id, t0.id;" +571,366,366,125,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT markets.id, markets.note AS c5448, markets.region +FROM markets;" +572,2085,2085,151,9,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id +FROM regions +WHERE ((regions.id IN (7, 90) AND regions.id != 9) OR regions.id NOT IN (2, 2, 29)) +ORDER BY regions.id DESC;" +573,260498,522188,1486,1500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, t2.price, COUNT(*) +FROM markets AS t0 CROSS JOIN customers AS t1 JOIN books AS t2 ON t0.id = t2.id +GROUP BY t1.id, t2.price +ORDER BY t2.price ASC, t1.id ASC;" +574,253000,463000,399,0,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.region_id, t0.id +FROM customers AS t0 CROSS JOIN departments AS t1 +WHERE t1.id < 1;" +575,2337,3832,213,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.price +FROM books AS t0 JOIN employees AS t1 ON t0.id = t1.id +WHERE t1.id >= 33;" +576,118847,653426,419,51,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price, customers.id AS c8754, users.age AS c3420 +FROM users LEFT JOIN customers ON users.id = customers.id CROSS JOIN books;" +577,1308754,6451833,1921,6,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.customer_id, t1.id, t0.id AS c4362 +FROM orders AS t0 FULL JOIN users AS t1 ON t0.id = t1.id +WHERE t1.age = 64 +ORDER BY t1.id DESC, t0.customer_id ASC;" +578,2525500,4625500,12855,25000,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT departments.id, orders.id +FROM departments CROSS JOIN orders +WHERE departments.id != 45;" +579,5041,15375,514,9,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id, regions.id +FROM regions JOIN users ON regions.id = users.id +WHERE users.id <= 8 +ORDER BY users.id DESC, regions.id DESC;" +580,3134,4880,300,20,transformation 0 JoinCommutativity: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id, markets.note AS c9073 +FROM markets FULL JOIN regions ON markets.id = regions.id CROSS JOIN books +WHERE books.price IN (66, 77, 77);" +581,110137,157124,401,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT t1.id +FROM books AS t0 FULL JOIN customers AS t1 ON t0.id = t1.id RIGHT JOIN departments AS t2 ON t0.id = t2.id +WHERE t1.region_id IN (9, 64) +GROUP BY t1.id +ORDER BY t1.id DESC;" +582,61000,61000,241,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT customers.region_id AS c8490 +FROM customers;" +583,3759405,3875500,347832,2500000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, t2.id, t2.customer_id +FROM customers AS t0 LEFT JOIN departments AS t1 ON t0.id = t1.id CROSS JOIN orders AS t2;" +584,69714,156902,422,6,transformation 10 ProjectionPushdownThroughJoin: 7; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 4; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT markets.region +FROM markets JOIN customers ON markets.id = customers.id CROSS JOIN books +WHERE ((books.id IN (2, 3, 3, 63) AND markets.region != 'ASIA') OR (customers.region_id IS NOT NULL AND markets.id > 81));" +585,92456,92456,284,313,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT customers.region_id, customers.id AS c9727 +FROM customers +WHERE ((customers.region_id IN (6) OR customers.id IS NULL) OR (customers.id = 66 OR customers.region_id BETWEEN 5 AND 60));" +586,4783,7022,267,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT t0.region, t1.price, SUM(t1.id), SUM(t2.age) +FROM markets AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id RIGHT JOIN users AS t2 ON t1.id = t2.id +GROUP BY t0.region, t1.price;" +587,1317032,1561792,29744,85000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id +FROM orders FULL JOIN books ON orders.id = books.id CROSS JOIN users +ORDER BY users.id DESC;" +588,87300,1807800,399,50,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t2.region_id AS c6587, t1.id AS c973, t0.id +FROM regions AS t0 CROSS JOIN departments AS t1 JOIN customers AS t2 ON t1.id = t2.id;" +589,1033750,1033750,1719,5000,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id +FROM orders +WHERE (orders.customer_id >= 10 OR (orders.id >= 28 OR orders.id != 171));" +590,544,544,150,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.id AS c8304 +FROM books +WHERE ((books.price >= 77 OR books.id != 1) OR books.id > 1);" +591,2620,4452,252,0,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT markets.region, markets.note, regions.id AS c1044 +FROM regions RIGHT JOIN markets ON regions.id = markets.id RIGHT JOIN books ON regions.id = books.id +WHERE markets.id = 45;" +592,2676,2676,167,16,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.id, users.age +FROM users +WHERE users.id != 12;" +593,1306015,2250610,2076,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id AS c6161 +FROM orders FULL JOIN departments ON orders.id = departments.id;" +594,1963,1963,148,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.id, SUM(users.age) +FROM users +WHERE users.age = 18 +GROUP BY users.id;" +595,1308995,6452074,2266,5001,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age, orders.customer_id AS c5702 +FROM orders FULL JOIN users ON orders.id = users.id;" +596,68894,225744,283,4,transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT customers.id, departments.id +FROM departments LEFT JOIN customers ON departments.id = customers.id +WHERE customers.region_id < 10;" +597,1425,1425,450,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, COUNT(*) +FROM departments AS t0 +GROUP BY t0.id;" +598,5219,5219,206,16,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age AS c5631, users.id, COUNT(users.id) +FROM users +GROUP BY users.age, users.id;" +599,9921,24850,440,6,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT departments.id AS c5260, COUNT(books.id) AS c2969, SUM(departments.id) +FROM books CROSS JOIN departments RIGHT JOIN users ON books.id = users.id +GROUP BY departments.id;" +600,27106700,27106700,26255,85000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age, orders.id +FROM orders CROSS JOIN users +ORDER BY orders.id ASC, users.age ASC;" +601,366,366,119,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.price +FROM books AS t0;" +602,4771,9075,244,16,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id, COUNT(*), SUM(departments.id) +FROM departments FULL JOIN users ON departments.id = users.id +GROUP BY users.id +ORDER BY users.id ASC;" +603,698250,698250,2540,4964,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id AS c3643 +FROM orders +WHERE ((orders.id >= 36 OR orders.customer_id IN (32, 70, 315)) AND orders.id != 113);" +604,1342,1342,210,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT employees.department_id AS c8290, employees.id +FROM employees;" +605,1610,1610,165,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions +WHERE regions.id <= 1;" +606,1306757,1552138,3197,4809,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT orders.id AS c7144, books.id, orders.customer_id +FROM books FULL JOIN departments ON books.id = departments.id RIGHT JOIN orders ON books.id = orders.id +WHERE (orders.id IS NULL OR (books.price = 55 OR orders.id >= 193)) +ORDER BY orders.id DESC, orders.customer_id DESC;" +607,120050,410630,1115,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.id, COUNT(t2.price) +FROM customers AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +GROUP BY t2.id +ORDER BY t2.id ASC;" +608,2030,2030,146,7,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id +FROM employees +WHERE ((employees.department_id >= 6 AND employees.department_id > 34) OR employees.id < 22) +ORDER BY employees.department_id ASC;" +609,421490,421730,839,1500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.note, books.id AS c2320, customers.region_id AS c7927 +FROM markets RIGHT JOIN books ON markets.id = books.id CROSS JOIN customers +ORDER BY books.id ASC, customers.region_id DESC, markets.note DESC;" +610,2192,5122,171,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 9 FilterLiftThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT regions.id, departments.id +FROM regions LEFT JOIN departments ON regions.id = departments.id +WHERE (regions.id < 5 AND departments.id = 97);" +611,1421966,7100219,2935,11,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT customers.region_id AS c888 +FROM orders FULL JOIN users ON orders.id = users.id LEFT JOIN customers ON orders.id = customers.id +GROUP BY customers.region_id;" +612,1342,1342,147,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT employees.id +FROM employees;" +613,2585,5110,191,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id +FROM departments AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id;" +614,2257,3832,231,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT markets.id, markets.region +FROM employees JOIN markets ON employees.id = markets.id +WHERE markets.note != 'EUROPE';" +615,2960,2960,162,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM regions AS t0 +GROUP BY t0.id;" +616,3290,3290,164,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age, users.id +FROM users +WHERE users.id IN (3, 8, 11) +GROUP BY users.age, users.id +ORDER BY users.age DESC;" +617,6476,20350,243,6,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t0.age +FROM users AS t0 JOIN employees AS t1 ON t0.id = t1.id FULL JOIN departments AS t2 ON t0.id = t2.id;" +618,2378,5713,275,4,transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id +FROM departments AS t0 JOIN employees AS t1 ON t0.id = t1.id +WHERE t0.id BETWEEN 2 AND 95 +GROUP BY t1.id;" +619,935433,1705433,1969,1,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 2; enforcer 0 SortEnforcer: 1,"SELECT customers.id AS c7618, orders.customer_id +FROM books LEFT JOIN orders ON books.id = orders.id LEFT JOIN customers ON books.id = customers.id +WHERE books.id < 2 +ORDER BY customers.id ASC;" +620,1660,1660,147,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id AS c894 +FROM regions +ORDER BY regions.id DESC;" +621,1307580,4001660,3319,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT regions.id, orders.customer_id AS c8455 +FROM orders LEFT JOIN regions ON orders.id = regions.id +ORDER BY regions.id ASC;" +622,2658,3948,185,3,transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id, books.price AS c9301, COUNT(books.price) +FROM books JOIN regions ON books.id = regions.id +WHERE (regions.id NOT IN (3) OR books.id <= 3) +GROUP BY books.id, books.price;" +623,2425,2425,146,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, SUM(t0.id) AS c6358, COUNT(*) +FROM regions AS t0 +WHERE t0.id IS NULL +GROUP BY t0.id;" +624,422,422,509,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.price, t0.id AS c4296 +FROM books AS t0 +WHERE (t0.id < 2 OR (t0.id = 81 OR t0.price IN (21, 30)));" +625,3774,5833,242,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.age, markets.id +FROM markets RIGHT JOIN users ON markets.id = users.id +WHERE markets.note IS NOT NULL +GROUP BY users.age, markets.id;" +626,112495,647074,307,501,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.age +FROM customers AS t0 FULL JOIN users AS t1 ON t0.id = t1.id;" +627,72910,647310,229,9,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT t0.id, t1.age AS c4295, t0.region_id +FROM customers AS t0 LEFT JOIN users AS t1 ON t0.id = t1.id +WHERE ((t1.id IN (4, 8) AND t1.id > 35) OR (t1.id <= 8 AND t0.region_id > 2));" +628,109136,155366,391,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region, markets.note, customers.region_id +FROM customers LEFT JOIN markets ON customers.id = markets.id;" +629,1975000,1975000,2290,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, orders.customer_id, COUNT(*) +FROM orders +GROUP BY orders.id, orders.customer_id;" +630,1361,1916,176,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note AS c3373, markets.region +FROM departments RIGHT JOIN markets ON departments.id = markets.id;" +631,1309320,4003400,1610,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t0.id, t1.id +FROM orders AS t0 RIGHT JOIN regions AS t1 ON t0.id = t1.id +GROUP BY t0.id, t1.id +ORDER BY t0.id ASC, t1.id DESC;" +632,9998,13520,356,0,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.region, t1.note +FROM employees AS t0 CROSS JOIN markets AS t1 +WHERE t1.id NOT BETWEEN 1 AND 83 +GROUP BY t1.region, t1.note +ORDER BY t1.region DESC, t1.note DESC;" +633,610000,610000,1334,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT orders.id +FROM orders;" +634,1875,1875,156,11,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id +FROM employees +WHERE employees.id != 49 +ORDER BY employees.department_id DESC;" +635,679260,4002960,999,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id, SUM(orders.id) AS c5946, COUNT(orders.id) +FROM orders JOIN regions ON orders.id = regions.id +GROUP BY regions.id;" +636,422,422,184,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c1836, t0.price +FROM books AS t0 +WHERE t0.price > 66;" +637,2083,5583,289,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, employees.department_id AS c5334, employees.id +FROM employees JOIN departments ON employees.id = departments.id +WHERE ((departments.id > 3 OR employees.id IN (5)) AND (employees.department_id IS NOT NULL AND employees.id IN (4, 4, 96))) +ORDER BY employees.id ASC, employees.department_id ASC, departments.id ASC;" +638,445582248,445582248,398994,55000,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.id, orders.id +FROM employees CROSS JOIN users CROSS JOIN orders +GROUP BY employees.id, orders.id;" +639,7453072,65202822,2907,10,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t0.id, t1.id AS c9098 +FROM regions AS t0 CROSS JOIN orders AS t1 LEFT JOIN users AS t2 ON t1.id = t2.id +WHERE t2.age IN (88);" +640,4420,4420,171,8,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.age, SUM(t0.age) AS c7571 +FROM users AS t0 +WHERE t0.id < 13 +GROUP BY t0.age +ORDER BY t0.age ASC;" +641,1734,1734,444,9,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.region, t0.id, t0.price +FROM books AS t0 CROSS JOIN markets AS t1;" +642,4298,6932,230,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT markets.region +FROM markets RIGHT JOIN regions ON markets.id = regions.id LEFT JOIN employees ON regions.id = employees.id +WHERE (employees.department_id >= 25 OR (regions.id != 8 AND markets.note = 'unknown'));" +643,6213,17132,248,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT t0.id AS c6788, t2.id +FROM regions AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id FULL JOIN markets AS t2 ON t0.id = t2.id +ORDER BY t0.id ASC, t2.id DESC;" +644,678796,4352076,896,10,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT employees.id, employees.department_id +FROM orders LEFT JOIN employees ON orders.id = employees.id +WHERE ((employees.id NOT IN (2, 4, 20) AND employees.department_id > 3) OR (orders.id NOT IN (150, 198) AND employees.department_id NOT IN (3)));" +645,1048,1048,228,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, SUM(departments.id) AS c664 +FROM departments +WHERE departments.id < 3 +GROUP BY departments.id;" +646,31399500,59194500,89761,190000,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT markets.region +FROM orders CROSS JOIN users CROSS JOIN markets +WHERE (users.id < 5 OR markets.region != 'EUROPE');" +647,682544,6455844,1136,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id +FROM orders JOIN users ON orders.id = users.id RIGHT JOIN books ON orders.id = books.id +WHERE books.price BETWEEN 1 AND 6 +GROUP BY orders.customer_id +ORDER BY orders.customer_id DESC;" +648,3917,22032,339,2,transformation 1 JoinAssociativity: 2; transformation 2 FilterSplit: 2; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT departments.id, users.age AS c8349, markets.region AS c8908 +FROM departments CROSS JOIN markets RIGHT JOIN users ON markets.id = users.id +WHERE ((markets.region = 'AMERICA' AND markets.note = 'North, South') AND (departments.id NOT BETWEEN 1 AND 3 OR departments.id IN (38, 78, 83)));" +649,3053,5816,232,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id +FROM employees RIGHT JOIN departments ON employees.id = departments.id +WHERE ((employees.department_id = 1 OR departments.id >= 99) OR (employees.id != 40 AND departments.id <= 85));" +650,2558,5738,280,4,transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id AS c5479, employees.id AS c3162, departments.id +FROM departments LEFT JOIN employees ON departments.id = employees.id +WHERE ((departments.id IN (2) OR departments.id NOT IN (51)) AND departments.id <= 4) +ORDER BY employees.department_id DESC, employees.id DESC;" +651,622,622,147,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id AS c1103 +FROM departments +WHERE ((departments.id <= 1 OR departments.id != 1) AND (departments.id = 50 OR departments.id = 4));" +652,6154,6154,254,16,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id, COUNT(users.age) AS c4429 +FROM users +GROUP BY users.id +ORDER BY users.id ASC;" +653,424011,426070,2060,8500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.region +FROM users FULL JOIN markets ON users.id = markets.id CROSS JOIN customers +ORDER BY markets.region ASC;" +654,1308980,4005925,3762,510,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT regions.id AS c4375, orders.customer_id AS c4532 +FROM regions FULL JOIN orders ON regions.id = orders.id LEFT JOIN departments ON orders.id = departments.id +GROUP BY regions.id, orders.customer_id;" +655,1342,1342,148,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.department_id AS c8831 +FROM employees AS t0;" +656,4261,6566,233,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t1.note AS c9009 +FROM users AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id JOIN books AS t2 ON t0.id = t2.id;" +657,684100,4007800,1066,50,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t2.id AS c86, t0.id AS c971 +FROM orders AS t0 JOIN regions AS t1 ON t0.id = t1.id CROSS JOIN departments AS t2;" +658,433,433,135,0,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region AS c1228, t0.note, t0.id +FROM markets AS t0 +WHERE (t0.id IN (76) AND (t0.id <= 2 AND t0.region != 'AMERICA')) +ORDER BY t0.note ASC, t0.id ASC, t0.region ASC;" +659,10312,30045,345,7,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.id +FROM markets AS t0 CROSS JOIN regions AS t1 FULL JOIN employees AS t2 ON t1.id = t2.id +WHERE t0.id > 2 +GROUP BY t2.id;" +660,2960,2960,168,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, SUM(t0.id) +FROM regions AS t0 +GROUP BY t0.id;" +661,1366,1366,143,4,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c6502 +FROM regions AS t0 +WHERE (t0.id <= 4 AND (t0.id >= 3 OR t0.id <= 6));" +662,3421000,3421000,6445,25500,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2,"SELECT t0.id, t2.id, t1.price AS c6596 +FROM customers AS t0 CROSS JOIN books AS t1 CROSS JOIN users AS t2;" +663,75082,442664,404,27,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id AS c1872 +FROM customers JOIN employees ON customers.id = employees.id CROSS JOIN books +WHERE employees.department_id >= 5 +ORDER BY customers.id ASC;" +664,1311267,6464820,2817,5001,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT orders.id, regions.id AS c6643, users.id +FROM users FULL JOIN orders ON users.id = orders.id FULL JOIN regions ON orders.id = regions.id;" +665,6079,9502,273,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.note, COUNT(t2.id), COUNT(*) AS c2705 +FROM employees AS t0 FULL JOIN markets AS t1 ON t0.id = t1.id LEFT JOIN users AS t2 ON t1.id = t2.id +GROUP BY t1.note;" +666,4550,11225,224,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id, t1.id AS c8913, COUNT(*) AS c1414 +FROM regions AS t0 JOIN employees AS t1 ON t0.id = t1.id +WHERE t1.department_id <= 3 +GROUP BY t0.id, t1.id;" +667,1775,1775,167,7,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id AS c2245 +FROM regions +WHERE regions.id > 3 +ORDER BY regions.id DESC;" +668,68586,155366,317,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT books.price, books.id, customers.region_id +FROM customers JOIN books ON customers.id = books.id;" +669,1308995,6452074,1967,5001,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.customer_id +FROM users AS t0 FULL JOIN orders AS t1 ON t0.id = t1.id;" +670,1386309,7099068,1898,16,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id AS c9255, users.age, COUNT(*), COUNT(customers.id) +FROM orders RIGHT JOIN users ON orders.id = users.id JOIN customers ON orders.id = customers.id +WHERE customers.id != 40 +GROUP BY users.id, users.age;" +671,4657,10060,215,11,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id, employees.id AS c3646 +FROM departments FULL JOIN regions ON departments.id = regions.id RIGHT JOIN employees ON departments.id = employees.id;" +672,118952,118952,280,357,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id +FROM customers AS t0 +WHERE (t0.id < 13 OR t0.region_id BETWEEN 4 AND 53) +ORDER BY t0.region_id DESC;" +673,16893483,44722363,23746,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id +FROM orders AS t0 CROSS JOIN employees AS t1 LEFT JOIN regions AS t2 ON t0.id = t2.id +WHERE ((t0.id > 126 AND t2.id = 3) OR t0.customer_id = 248) +GROUP BY t1.id;" +674,797300,797300,1370,1500,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id, books.price, COUNT(customers.region_id), COUNT(*) +FROM customers CROSS JOIN books +GROUP BY customers.id, books.price +ORDER BY customers.id ASC, books.price DESC;" +675,1325000,1325000,1421,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id, orders.id AS c1243 +FROM orders +ORDER BY orders.id DESC, orders.customer_id DESC;" +676,5252,5252,178,11,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age, users.id, COUNT(users.age), COUNT(*) +FROM users +WHERE users.age NOT BETWEEN 61 AND 64 +GROUP BY users.age, users.id;" +677,637302,175655422,1118,0,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 3; transformation 9 FilterLiftThroughJoin: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 2,"SELECT books.id +FROM customers JOIN orders ON customers.id = orders.id JOIN books ON customers.id = books.id +WHERE orders.customer_id = 89;" +678,1975000,1975000,2350,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c1600, SUM(t0.customer_id) +FROM orders AS t0 +GROUP BY t0.id;" +679,6711,18875,234,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT t1.age AS c5457, t0.id, t2.id AS c6810 +FROM regions AS t0 LEFT JOIN users AS t1 ON t0.id = t1.id RIGHT JOIN departments AS t2 ON t1.id = t2.id +ORDER BY t1.age DESC, t2.id DESC, t0.id DESC;" +680,39066,56446,395,51,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.id, t0.age +FROM users AS t0 CROSS JOIN regions AS t1 RIGHT JOIN markets AS t2 ON t1.id = t2.id;" +681,1914392,3124292,2550,3067,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 5; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id +FROM orders CROSS JOIN books +WHERE ((orders.customer_id < 310 AND books.id >= 3) AND (books.price IN (77) OR books.id < 3)) +GROUP BY orders.id +ORDER BY orders.id DESC;" +682,111672,438832,513,10,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t2.id, t1.region_id AS c4049 +FROM employees AS t0 FULL JOIN customers AS t1 ON t0.id = t1.id LEFT JOIN markets AS t2 ON t0.id = t2.id +WHERE ((t2.note = 'Old World' OR t0.id >= 2) AND t1.id <= 131);" +683,3184,5024,313,3,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.price, SUM(t1.id), SUM(t1.id) +FROM books AS t0 CROSS JOIN departments AS t1 +WHERE t1.id NOT IN (5, 8, 17, 45) +GROUP BY t0.price;" +684,2682,3922,217,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.region AS c7251 +FROM regions FULL JOIN markets ON regions.id = markets.id +GROUP BY markets.region;" +685,113430,648009,451,501,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.region_id, t0.id, t1.age AS c7926 +FROM customers AS t0 LEFT JOIN users AS t1 ON t0.id = t1.id +ORDER BY t0.region_id ASC, t0.id ASC;" +686,671327,1551972,1261,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region AS c1187, markets.id, orders.id +FROM markets RIGHT JOIN departments ON markets.id = departments.id LEFT JOIN orders ON markets.id = orders.id +WHERE orders.customer_id BETWEEN 324 AND 365;" +687,2422,6822,282,9,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 3; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT regions.id, departments.id +FROM departments CROSS JOIN regions +WHERE ((regions.id >= 2 AND departments.id IN (2, 3, 5)) AND (departments.id != 1 AND departments.id IN (4, 4, 5)));" +688,3510,5393,248,2,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT t1.id +FROM markets AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id FULL JOIN employees AS t2 ON t0.id = t2.id +WHERE (t1.id < 3 OR (t1.id <= 1 AND t2.id IN (3, 6, 33, 85))) +ORDER BY t1.id ASC;" +689,5828,12772,274,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t2.id AS c3, t0.id +FROM departments AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id FULL JOIN users AS t2 ON t0.id = t2.id +WHERE t2.age = 51;" +690,1122700,1122700,2265,8500,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT users.id, customers.id +FROM users CROSS JOIN customers;" +691,110296,226916,340,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT customers.region_id AS c2257, customers.id AS c5252 +FROM departments LEFT JOIN customers ON departments.id = customers.id JOIN books ON customers.id = books.id;" +692,633,633,281,3,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id +FROM departments +WHERE (departments.id != 5 AND departments.id > 1) +ORDER BY departments.id ASC;" +693,2358,3843,176,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.region AS c9465, markets.id +FROM employees JOIN markets ON employees.id = markets.id +WHERE employees.id > 67 +ORDER BY markets.region DESC, markets.id ASC;" +694,6686,18875,247,5,transformation 0 JoinCommutativity: 2; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT regions.id AS c1390, users.age AS c184 +FROM users RIGHT JOIN regions ON users.id = regions.id JOIN departments ON users.id = departments.id +ORDER BY users.age DESC;" +695,2424,3973,237,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.note, employees.department_id AS c9332, SUM(employees.id) AS c3951 +FROM employees RIGHT JOIN markets ON employees.id = markets.id +WHERE markets.region = 'AMERICA' +GROUP BY markets.note, employees.department_id;" +696,1342,1342,145,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT employees.department_id, employees.id +FROM employees;" +697,366,366,130,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.price +FROM books AS t0;" +698,3898,16153,220,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id, COUNT(employees.department_id) AS c7934, COUNT(*) AS c8534 +FROM users LEFT JOIN employees ON users.id = employees.id +WHERE ((employees.id IS NULL AND employees.id >= 66) AND employees.id NOT IN (2, 6, 33, 68)) +GROUP BY employees.department_id;" +699,3922,10460,181,6,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id +FROM employees JOIN regions ON employees.id = regions.id +ORDER BY regions.id ASC;" +700,1922,1922,170,5,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.id, COUNT(employees.department_id) AS c6900 +FROM employees +WHERE ((employees.id < 5 AND employees.id <= 6) OR (employees.id = 67 AND employees.department_id >= 31)) +GROUP BY employees.id;" +701,3315,5079,445,3,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id, t0.note AS c5073 +FROM markets AS t0 LEFT JOIN employees AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE t0.region = 'EUROPE' +ORDER BY t1.id DESC, t0.note DESC;" +702,1425,1425,166,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, COUNT(*) AS c4016, COUNT(t0.id) +FROM departments AS t0 +GROUP BY t0.id;" +703,2690000,2690000,1857,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.customer_id AS c8628, SUM(t0.customer_id) +FROM orders AS t0 +GROUP BY t0.customer_id +ORDER BY t0.customer_id DESC;" +704,690944,68842944,1034,51,transformation 0 JoinCommutativity: 1; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT users.id +FROM orders CROSS JOIN users RIGHT JOIN regions ON orders.id = regions.id +WHERE orders.customer_id BETWEEN 56 AND 310;" +705,1331,1916,185,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, t0.price AS c5080, t1.id +FROM books AS t0 LEFT JOIN departments AS t1 ON t0.id = t1.id;" +706,677417,1553644,1000,2,transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; transformation 10 ProjectionPushdownThroughJoin: 6; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 3; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region, orders.customer_id AS c9499, orders.id AS c2512 +FROM markets JOIN orders ON markets.id = orders.id JOIN regions ON orders.id = regions.id +WHERE markets.note NOT IN ('AMERICA', 'Old World');" +707,1293750,1293750,2125,5000,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id, orders.id +FROM orders +WHERE (orders.customer_id IS NOT NULL OR (orders.customer_id > 52 AND orders.customer_id <= 48)) +ORDER BY orders.id ASC, orders.customer_id DESC;" +708,2277500,2277500,2754,4991,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.customer_id, t0.id +FROM orders AS t0 +WHERE (t0.customer_id > 257 OR t0.customer_id NOT IN (88, 404)) +GROUP BY t0.customer_id, t0.id;" +709,69108,156778,387,1,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t2.id +FROM markets AS t0 JOIN books AS t1 ON t0.id = t1.id RIGHT JOIN customers AS t2 ON t0.id = t2.id +WHERE ((t0.id IS NOT NULL OR t2.region_id < 10) AND t0.region NOT IN ('AMERICA')) +GROUP BY t2.id;" +710,678892,6452676,986,13,transformation 5 FilterPushdownThroughJoin: 1; transformation 10 ProjectionPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1,"SELECT t1.id, t1.customer_id, t0.id +FROM users AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +WHERE t0.id <= 13;" +711,17610163,22240890,39324,5500,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT orders.customer_id, users.age +FROM users LEFT JOIN employees ON users.id = employees.id CROSS JOIN orders +WHERE users.age IS NOT NULL +GROUP BY orders.customer_id, users.age +ORDER BY orders.customer_id DESC, users.age DESC;" +712,3092,16012,211,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t1.id AS c8042, t0.id +FROM users AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id +WHERE ((t0.age BETWEEN 4 AND 64 AND t1.department_id < 3) AND (t0.age = 33 AND t1.department_id IN (2, 5, 31, 48)));" +713,1361,1916,266,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.id, markets.note AS c3697 +FROM markets RIGHT JOIN departments ON markets.id = departments.id;" +714,65900,65900,279,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id AS c1736, customers.region_id AS c1547, COUNT(customers.id) AS c74 +FROM customers +WHERE customers.id = 166 +GROUP BY customers.id, customers.region_id;" +715,115207,660260,563,501,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t0.id, t2.id, t1.id AS c5704 +FROM customers AS t0 FULL JOIN users AS t1 ON t0.id = t1.id FULL JOIN regions AS t2 ON t0.id = t2.id +ORDER BY t2.id ASC, t0.id DESC;" +716,4024,4024,167,15,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age +FROM users +WHERE (users.age >= 443 OR users.id NOT BETWEEN 13 AND 14) +ORDER BY users.age DESC;" +717,763,763,154,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id +FROM departments +WHERE (departments.id IS NOT NULL AND (departments.id BETWEEN 4 AND 37 OR departments.id >= 4)) +GROUP BY departments.id;" +718,26362,26362,270,187,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT users.age, users.id +FROM employees CROSS JOIN users;" +719,1494000,175610500,2992,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id AS c3382, t1.customer_id, t0.region_id AS c6097 +FROM customers AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +ORDER BY t1.customer_id DESC;" +720,683600,5753350,1064,15,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t2.id, t1.price, t1.id +FROM departments AS t0 CROSS JOIN books AS t1 JOIN orders AS t2 ON t1.id = t2.id +ORDER BY t1.price DESC, t1.id DESC;" +721,1660,1660,131,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c5795 +FROM regions AS t0 +ORDER BY t0.id DESC;" +722,113611,234075,442,14,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT departments.id, users.age AS c5964, COUNT(users.age) +FROM departments RIGHT JOIN customers ON departments.id = customers.id LEFT JOIN users ON customers.id = users.id +GROUP BY departments.id, users.age;" +723,112808,233272,542,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t1.id AS c5700, t2.age, t2.id +FROM departments AS t0 FULL JOIN customers AS t1 ON t0.id = t1.id FULL JOIN users AS t2 ON t1.id = t2.id +WHERE (t1.id != 184 AND (t1.region_id IS NOT NULL OR t1.region_id >= 82));" +724,3178,5124,322,30,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id, markets.note +FROM markets FULL JOIN regions ON markets.id = regions.id CROSS JOIN books +WHERE books.id < 37;" +725,1822,1822,145,0,transformation 2 FilterSplit: 1; transformation 3 FilterMerge: 1; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id AS c8444 +FROM users AS t0 +WHERE ((t0.age IN (10, 33, 64, 88) AND t0.age IN (21, 33, 92)) AND (t0.id IS NULL AND t0.age < 64));" +726,22101000,22101000,22796,50000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, regions.id AS c2828, COUNT(*) +FROM orders CROSS JOIN regions +GROUP BY orders.id, regions.id;" +727,4022,5942,337,9,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.id AS c2746, books.price, departments.id +FROM departments CROSS JOIN markets RIGHT JOIN books ON departments.id = books.id +ORDER BY markets.id DESC;" +728,2074,2074,155,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT users.id +FROM users;" +729,1308299,4005244,1463,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT orders.customer_id AS c7513 +FROM orders RIGHT JOIN regions ON orders.id = regions.id FULL JOIN departments ON regions.id = departments.id +WHERE orders.id IS NULL;" +730,102133,155563,488,356,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id, t1.id, COUNT(t0.id), COUNT(t0.id) +FROM customers AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id +WHERE t0.region_id >= 4 +GROUP BY t0.id, t1.id;" +731,868050300,1498050300,984190,2500000,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT orders.id, customers.id +FROM customers CROSS JOIN orders CROSS JOIN markets +WHERE markets.region != 'AMERICA';" +732,83440,423880,351,0,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id +FROM customers JOIN regions ON customers.id = regions.id CROSS JOIN employees +WHERE employees.department_id IS NULL +ORDER BY regions.id ASC;" +733,76799,650219,275,11,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age, COUNT(customers.region_id), SUM(users.age) AS c9006 +FROM users JOIN customers ON users.id = customers.id +GROUP BY users.age;" +734,1308580,4005110,1832,5,transformation 0 JoinCommutativity: 2; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id +FROM orders LEFT JOIN regions ON orders.id = regions.id JOIN departments ON regions.id = departments.id;" +735,2235,3445,264,0,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, books.id AS c2211, books.price +FROM departments CROSS JOIN books +WHERE books.price IS NULL +ORDER BY books.price DESC, departments.id ASC;" +736,69042,155822,271,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id AS c7269, t1.price AS c9813 +FROM customers AS t0 JOIN books AS t1 ON t0.id = t1.id +GROUP BY t1.id, t1.price;" +737,1342,1342,158,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.department_id, t0.id AS c7129 +FROM employees AS t0;" +738,74400,74400,297,282,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.region_id +FROM customers AS t0 +WHERE ((t0.region_id NOT IN (8, 10) AND t0.id BETWEEN 50 AND 141) OR (t0.region_id > 6 AND t0.region_id != 1));" +739,805000,805000,1274,4999,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id, orders.id +FROM orders +WHERE orders.id != 185;" +740,3581,5472,245,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.id, COUNT(t0.id) AS c1028, SUM(t2.id) +FROM books AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id RIGHT JOIN departments AS t2 ON t0.id = t2.id +GROUP BY t1.id;" +741,433,433,172,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id AS c935, books.price +FROM books +WHERE ((books.price BETWEEN 26 AND 66 AND books.price < 66) AND (books.price BETWEEN 66 AND 91 AND books.price IS NOT NULL)) +ORDER BY books.id ASC, books.price DESC;" +742,109192,155422,483,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note +FROM markets FULL JOIN customers ON markets.id = customers.id +WHERE markets.id < 1;" +743,4529,7294,364,6,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.id +FROM markets AS t0 LEFT JOIN users AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE ((t0.note = 'Fast lane' AND t1.id != 6) OR t0.note != 'Old World');" +744,850433,1550433,1943,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.region, t1.note +FROM orders AS t0 RIGHT JOIN markets AS t1 ON t0.id = t1.id +WHERE t1.id != 3 +ORDER BY t1.region ASC, t1.note ASC;" +745,652500,652500,1159,35,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.id AS c7997 +FROM orders +WHERE orders.customer_id BETWEEN 477 AND 480;" +746,2797,5560,173,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id, departments.id +FROM departments LEFT JOIN employees ON departments.id = employees.id;" +747,110640,401220,327,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.region_id AS c5955, customers.id AS c1522 +FROM customers LEFT JOIN regions ON customers.id = regions.id;" +748,744,744,131,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT departments.id AS c497 +FROM departments +WHERE departments.id >= 5;" +749,6821,16560,306,12,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.age AS c5325, COUNT(*), SUM(users.age) +FROM users FULL JOIN regions ON users.id = regions.id +GROUP BY users.age +ORDER BY users.age ASC;" +750,69552,401122,228,0,transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT customers.id AS c5621, regions.id, customers.region_id +FROM regions JOIN customers ON regions.id = customers.id +WHERE (customers.id = 154 OR regions.id = 74);" +751,404006,2238756,903,55,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, customers.id, SUM(employees.department_id) +FROM customers CROSS JOIN departments JOIN employees ON customers.id = employees.id +GROUP BY departments.id, customers.id;" +752,5505,15620,308,4,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id AS c7811, t0.id +FROM books AS t0 CROSS JOIN departments AS t1 RIGHT JOIN employees AS t2 ON t0.id = t2.id +WHERE t2.id > 33;" +753,168180,648180,491,0,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 3; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id AS c1319 +FROM customers AS t0 CROSS JOIN regions AS t1 +WHERE ((t0.region_id NOT IN (6) OR t1.id IN (8)) AND (t0.id = 53 AND t0.id > 175)) +GROUP BY t0.region_id;" +754,6154,6154,166,16,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id AS c8201 +FROM users +GROUP BY users.id +ORDER BY users.id DESC;" +755,22333,33348,410,5,transformation 0 JoinCommutativity: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id, COUNT(t1.id) AS c3083 +FROM regions AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id CROSS JOIN users AS t2 +WHERE (t2.id < 15 OR (t2.id BETWEEN 5 AND 78 OR t0.id IN (9))) +GROUP BY t0.id;" +756,2292,5692,251,0,transformation 0 JoinCommutativity: 1; transformation 3 FilterMerge: 1; transformation 5 FilterPushdownThroughJoin: 4; transformation 6 InToOrChain: 3; transformation 7 FilterToJoinPredicate: 2; transformation 9 FilterLiftThroughJoin: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT users.id AS c8257, markets.note AS c8225, markets.region AS c1435 +FROM markets JOIN users ON markets.id = users.id +WHERE ((markets.region IN ('unknown') OR users.age BETWEEN 5 AND 33) AND (users.id IN (13) AND markets.id IN (96)));" +757,8530,8530,298,55,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.id +FROM departments AS t0 CROSS JOIN employees AS t1;" +758,968798,1507498,2197,1500,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id, regions.id AS c2521 +FROM customers CROSS JOIN regions +WHERE ((regions.id >= 8 OR regions.id IN (10, 11, 23)) OR (customers.region_id > 10 AND customers.region_id > 93)) +ORDER BY customers.region_id DESC;" +759,152102,152102,426,500,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id AS c6281, customers.id +FROM customers +WHERE ((customers.id >= 24 OR customers.id IN (10, 19, 31, 38)) OR (customers.id >= 31 OR customers.id != 127)) +ORDER BY customers.id ASC;" +760,80500,80500,305,500,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT customers.region_id AS c9204 +FROM customers +WHERE customers.region_id <= 59;" +761,1875,1875,172,11,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id, employees.id AS c1116 +FROM employees +WHERE employees.id != 13 +ORDER BY employees.department_id DESC, employees.id DESC;" +762,734412,175936222,1099,0,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 2,"SELECT t0.id AS c87, t2.id, t1.region_id +FROM orders AS t0 LEFT JOIN customers AS t1 ON t0.id = t1.id JOIN employees AS t2 ON t0.id = t2.id +WHERE (t2.id BETWEEN 5 AND 67 AND t1.region_id = 1);" +763,11939,46176,287,33,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price, users.id, employees.id +FROM books CROSS JOIN employees LEFT JOIN users ON employees.id = users.id;" +764,5864,15252,292,21,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price, employees.id +FROM departments CROSS JOIN books RIGHT JOIN employees ON departments.id = employees.id;" +765,2522,2522,163,12,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.age AS c3911, t0.id, COUNT(t0.age) +FROM users AS t0 +WHERE ((t0.age IS NOT NULL AND t0.age > 18) AND (t0.id < 2 OR t0.age IS NOT NULL)) +GROUP BY t0.age, t0.id;" +766,2072,5583,250,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.department_id +FROM employees AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id +WHERE (t1.id <= 79 AND t0.department_id = 6) +ORDER BY t0.department_id DESC;" +767,1956040,3862300,685207,2499996,transformation 0 JoinCommutativity: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id +FROM customers LEFT JOIN books ON customers.id = books.id CROSS JOIN orders +WHERE ((books.id < 2 OR orders.id NOT IN (36, 91, 181, 188)) OR customers.id != 3);" +768,432,432,147,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.note +FROM markets +ORDER BY markets.note DESC;" +769,774,774,177,1,transformation 2 FilterSplit: 1; transformation 3 FilterMerge: 1; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id, COUNT(departments.id), COUNT(departments.id) AS c9545 +FROM departments +WHERE ((departments.id IN (1, 4, 5, 74) AND departments.id > 4) AND (departments.id BETWEEN 4 AND 19 OR departments.id IN (2, 3, 49))) +GROUP BY departments.id +ORDER BY departments.id DESC;" +770,5550,9750,302,0,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT regions.id +FROM departments CROSS JOIN regions +WHERE regions.id IS NULL;" +771,68894,225744,255,5,transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT customers.id +FROM departments LEFT JOIN customers ON departments.id = customers.id +WHERE customers.region_id >= 4;" +772,1460000,1460000,2111,2996,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, orders.customer_id, SUM(orders.customer_id) AS c6858 +FROM orders +WHERE orders.customer_id >= 207 +GROUP BY orders.id, orders.customer_id;" +773,2074,2074,152,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT users.id, users.age AS c2495 +FROM users;" +774,2311,5692,183,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age, users.id AS c8300 +FROM books LEFT JOIN users ON books.id = users.id +WHERE users.age = 5;" +775,254300,380300,669,387,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id +FROM books CROSS JOIN customers +WHERE customers.id <= 129 +ORDER BY customers.region_id DESC;" +776,2196,3466,174,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id AS c6446, books.price AS c1473, books.id AS c4530 +FROM regions FULL JOIN books ON regions.id = books.id;" +777,1822,1822,131,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.age, users.id +FROM users +WHERE users.age = 86;" +778,610000,610000,1117,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.customer_id +FROM orders AS t0;" +779,3318,6933,241,4,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t1.id, t2.note +FROM departments AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id LEFT JOIN markets AS t2 ON t0.id = t2.id +WHERE t0.id NOT IN (2, 2, 2, 63) +ORDER BY t2.note DESC;" +780,1975000,1975000,1807,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.customer_id, COUNT(orders.id), COUNT(*) AS c1649 +FROM orders +GROUP BY orders.customer_id;" +781,1342,1342,138,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT employees.id, employees.department_id +FROM employees;" +782,70978,230548,348,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id, departments.id AS c1329, COUNT(customers.region_id) +FROM customers RIGHT JOIN departments ON customers.id = departments.id LEFT JOIN regions ON departments.id = regions.id +WHERE customers.region_id >= 60 +GROUP BY customers.region_id, departments.id;" +783,819128,7170288,747,44,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 2; transformation 7 FilterToJoinPredicate: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT employees.id, customers.region_id, users.id +FROM employees CROSS JOIN customers LEFT JOIN users ON employees.id = users.id +WHERE ((customers.id < 45 OR users.age IN (33, 64, 64, 443)) AND users.id IN (4, 11, 15, 73));" +784,1236,1972,175,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, t0.price +FROM books AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id +WHERE t0.price IN (77);" +785,136783,671362,1650,5511,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id +FROM customers LEFT JOIN users ON customers.id = users.id CROSS JOIN employees;" +786,585048,1550433,1412,0,transformation 5 FilterPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.price, t0.id, t1.customer_id +FROM books AS t0 JOIN orders AS t1 ON t0.id = t1.id +WHERE ((t1.id >= 39 AND t1.customer_id BETWEEN 319 AND 371) AND t0.price = 66) +ORDER BY t0.id DESC, t0.price ASC;" +787,1308995,6452074,2794,5001,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id, users.id AS c419, orders.id +FROM users RIGHT JOIN orders ON users.id = orders.id;" +788,6599,17548,265,15,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT users.id +FROM users LEFT JOIN regions ON users.id = regions.id LEFT JOIN books ON regions.id = books.id +WHERE ((users.age <= 18 AND users.id < 6) OR (books.price NOT IN (9, 55, 77, 77) OR users.age NOT IN (11, 18))) +GROUP BY users.id;" +789,2454,3842,203,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id, t0.price +FROM books AS t0 LEFT JOIN employees AS t1 ON t0.id = t1.id +ORDER BY t0.price DESC;" +790,1308138,1554232,4448,12,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.department_id, COUNT(t1.id), COUNT(t1.id) AS c7952 +FROM markets AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN orders AS t2 ON t1.id = t2.id +GROUP BY t1.department_id;" +791,2908,4903,359,1,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.department_id AS c4790, t2.id, COUNT(t0.id) AS c1515, COUNT(t0.department_id) +FROM employees AS t0 JOIN markets AS t1 ON t0.id = t1.id RIGHT JOIN books AS t2 ON t0.id = t2.id +WHERE t1.note = 'North, South' +GROUP BY t0.department_id, t2.id;" +792,1586400,16287420,4020,10,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 2,"SELECT t1.region, t2.customer_id AS c3350, t1.note +FROM regions AS t0 CROSS JOIN markets AS t1 CROSS JOIN orders AS t2 +WHERE (t1.region IN ('EUROPE') AND t2.id IN (117));" +793,4779,10724,374,51,transformation 0 JoinCommutativity: 2; transformation 2 FilterSplit: 2; transformation 5 FilterPushdownThroughJoin: 4; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.price, t0.region +FROM markets AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id CROSS JOIN users AS t2 +WHERE ((t1.price = 77 OR t1.price < 77) AND (t0.id IN (2) OR t0.region != 'EUROPE')) +ORDER BY t0.region ASC, t1.price ASC;" +794,2004,3214,265,0,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.id, t1.region AS c4531, t1.note +FROM departments AS t0 CROSS JOIN markets AS t1 +WHERE t0.id IS NULL;" +795,5833,16866,231,11,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.age, t1.department_id AS c5680, t1.id +FROM users AS t0 RIGHT JOIN employees AS t1 ON t0.id = t1.id +WHERE (t1.department_id IS NOT NULL OR t1.department_id IS NULL);" +796,6057,18566,247,3,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT markets.note, users.id +FROM users FULL JOIN employees ON users.id = employees.id JOIN markets ON users.id = markets.id;" +797,114500,405080,1485,1500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price +FROM regions RIGHT JOIN customers ON regions.id = customers.id CROSS JOIN books;" +798,2474,3832,411,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.note AS c2770, t0.region AS c8751 +FROM markets AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id +WHERE (t1.department_id BETWEEN 5 AND 6 OR t1.department_id <= 11);" +799,1660,1660,189,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id +FROM regions +ORDER BY regions.id ASC;" +800,26882,26882,638,153,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2,"SELECT users.age AS c9040, users.id, markets.note AS c4935 +FROM users CROSS JOIN markets CROSS JOIN books;" +801,1342,1342,165,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.department_id +FROM employees AS t0;" +802,4536,7186,242,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT t2.age, t1.note, t1.id +FROM departments AS t0 RIGHT JOIN markets AS t1 ON t0.id = t1.id FULL JOIN users AS t2 ON t1.id = t2.id;" +803,1640,1640,173,4,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, COUNT(departments.id), COUNT(*) AS c7527 +FROM departments +WHERE departments.id NOT IN (1, 65) +GROUP BY departments.id;" +804,4744,8768,317,48,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT books.price AS c5825, users.id, books.id +FROM users CROSS JOIN books +WHERE (users.id < 17 AND books.price != 83);" +805,27408,37348,347,7,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.department_id, employees.id AS c1965, SUM(employees.department_id) +FROM employees CROSS JOIN regions +WHERE ((employees.department_id IN (64) OR employees.department_id >= 11) OR (regions.id = 32 AND employees.department_id != 5)) +GROUP BY employees.department_id, employees.id +ORDER BY employees.id ASC, employees.department_id DESC;" +806,1062,1493,237,1,transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id +FROM books AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id +WHERE t1.region != 'AMERICA' +GROUP BY t0.id +ORDER BY t0.id ASC;" +807,317000,937000,1960,4960,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t1.id, t0.region_id AS c3707, t0.id +FROM customers AS t0 CROSS JOIN regions AS t1 +WHERE t0.id NOT IN (33, 42, 91, 200);" +808,678304,6451944,985,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 9 FilterLiftThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT t0.age, t1.id, t0.id +FROM users AS t0 RIGHT JOIN orders AS t1 ON t0.id = t1.id +WHERE (t0.id < 4 AND (t1.id >= 151 AND t0.id IS NULL));" +809,1220,1220,150,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM regions AS t0;" +810,174981,303070,1785,1500,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.id, users.id, users.age +FROM users LEFT JOIN books ON users.id = books.id CROSS JOIN customers +WHERE books.price <= 95;" +811,633,633,149,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id +FROM departments +WHERE departments.id = 13 +ORDER BY departments.id DESC;" +812,1660,1660,136,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id +FROM regions +ORDER BY regions.id ASC;" +813,366,366,118,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT books.price AS c8764, books.id +FROM books;" +814,70867,230432,352,1,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.region_id, t2.id, t1.id +FROM departments AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id JOIN customers AS t2 ON t1.id = t2.id +WHERE ((t1.id = 8 OR t2.region_id > 57) OR t1.id IS NULL) +ORDER BY t2.region_id DESC, t2.id DESC;" +815,563,563,172,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.note +FROM markets AS t0 +WHERE ((t0.id > 7 AND t0.region != 'EUROPE') AND t0.id < 2) +GROUP BY t0.note;" +816,80500,80500,223,500,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT customers.id +FROM customers +WHERE customers.region_id != 78;" +817,840200,840200,1459,31,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c573, t0.customer_id +FROM orders AS t0 +WHERE (t0.customer_id IN (178, 239) OR t0.customer_id = 84) +ORDER BY t0.customer_id DESC;" +818,4254550,7107050,7951,5098,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT orders.id, orders.customer_id +FROM orders CROSS JOIN regions +WHERE ((orders.customer_id = 337 OR regions.id = 86) OR (regions.id = 10 AND orders.id != 156));" +819,109515,225610,511,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, t1.id, t1.region_id +FROM departments AS t0 RIGHT JOIN customers AS t1 ON t0.id = t1.id;" +820,1034588,1199375,3651,4960,transformation 2 FilterSplit: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, orders.customer_id, COUNT(orders.id) AS c5012, SUM(orders.customer_id) +FROM orders +WHERE ((orders.id != 129 OR orders.customer_id BETWEEN 72 AND 352) AND orders.customer_id NOT IN (191, 289, 289, 404)) +GROUP BY orders.id, orders.customer_id;" +821,5866,12760,247,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT departments.id, users.id AS c1842, users.age +FROM users FULL JOIN departments ON users.id = departments.id RIGHT JOIN regions ON departments.id = regions.id;" +822,2960,2960,203,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, COUNT(t0.id), SUM(t0.id) +FROM regions AS t0 +GROUP BY t0.id;" +823,318040,693140,1917,1,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id, COUNT(employees.department_id) +FROM employees CROSS JOIN customers +WHERE (customers.id != 157 AND (customers.id NOT BETWEEN 13 AND 55 AND employees.department_id = 34)) +GROUP BY employees.department_id;" +824,4927,22342,314,3,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.note, t0.id, t2.id +FROM departments AS t0 CROSS JOIN markets AS t1 JOIN users AS t2 ON t0.id = t2.id +WHERE t0.id IN (1, 33) +ORDER BY t0.id DESC, t1.note ASC;" +825,4099,6092,224,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT users.age, markets.note, COUNT(markets.id) +FROM markets LEFT JOIN users ON markets.id = users.id +GROUP BY users.age, markets.note +ORDER BY users.age ASC;" +826,3473,5326,262,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT employees.department_id, markets.note AS c1405, departments.id +FROM markets LEFT JOIN employees ON markets.id = employees.id LEFT JOIN departments ON employees.id = departments.id;" +827,2347,3832,230,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.id, employees.id +FROM markets JOIN employees ON markets.id = employees.id +WHERE employees.department_id IS NOT NULL;" +828,1048,1048,249,4,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id, COUNT(*) AS c4059, SUM(departments.id) AS c8530 +FROM departments +WHERE ((departments.id > 3 OR departments.id IN (2)) OR departments.id IN (3)) +GROUP BY departments.id;" +829,422,422,164,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.id, markets.region, markets.note +FROM markets +WHERE (markets.note != 'AMERICA' AND markets.region = 'AMERICA');" +830,3256,3256,151,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM employees AS t0 +GROUP BY t0.id;" +831,1975000,1975000,1793,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id +FROM orders +GROUP BY orders.id;" +832,677748,1553466,960,3,transformation 0 JoinCommutativity: 1; transformation 10 ProjectionPushdownThroughJoin: 6; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 3; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id AS c5347, markets.id AS c3605, orders.id +FROM orders JOIN markets ON orders.id = markets.id JOIN regions ON markets.id = regions.id;" +833,8121,22900,335,15,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.note, t0.id +FROM markets AS t0 CROSS JOIN departments AS t1 LEFT JOIN users AS t2 ON t1.id = t2.id +ORDER BY t0.note ASC;" +834,744100,744100,1011,5500,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT customers.id, employees.id, customers.region_id +FROM employees CROSS JOIN customers;" +835,3413,6866,221,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT employees.id, books.price, departments.id +FROM employees FULL JOIN departments ON employees.id = departments.id JOIN books ON departments.id = books.id;" +836,1278,1518,229,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT books.id, markets.note +FROM books JOIN markets ON books.id = markets.id +WHERE (books.id > 1 OR markets.note != 'unknown') +ORDER BY books.id ASC, markets.note ASC;" +837,3786,5144,300,9,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price, markets.id, books.id AS c9062 +FROM markets LEFT JOIN employees ON markets.id = employees.id CROSS JOIN books;" +838,1313696,1558426,19341,85000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.region, markets.id AS c8264 +FROM orders FULL JOIN markets ON orders.id = markets.id CROSS JOIN users;" +839,112105,405110,489,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT regions.id +FROM customers LEFT JOIN regions ON customers.id = regions.id FULL JOIN departments ON regions.id = departments.id;" +840,1220,1220,149,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions;" +841,111333,403533,398,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT markets.note, customers.id, regions.id +FROM regions FULL JOIN customers ON regions.id = customers.id JOIN markets ON customers.id = markets.id +WHERE ((customers.region_id = 41 OR regions.id IS NULL) AND (customers.id IN (32, 65, 97, 184) OR markets.id <= 13)) +ORDER BY markets.note DESC, customers.id ASC, regions.id DESC;" +842,1822,1822,165,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.age, t0.id AS c599 +FROM users AS t0 +WHERE (t0.id IN (5) AND t0.age < 88);" +843,15036300,31386300,34689,149880,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT books.id, orders.id, books.price +FROM books CROSS JOIN orders CROSS JOIN regions +WHERE orders.id NOT BETWEEN 50 AND 53;" +844,2988,4903,368,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 4; transformation 7 FilterToJoinPredicate: 1; transformation 11 OuterJoinToInner: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT books.price, COUNT(employees.department_id) AS c475 +FROM employees RIGHT JOIN books ON employees.id = books.id RIGHT JOIN markets ON books.id = markets.id +WHERE (employees.id < 3 AND (employees.department_id IS NULL OR markets.note = 'unknown')) +GROUP BY books.price;" +845,422,422,183,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.note, markets.region AS c5409 +FROM markets +WHERE markets.id IS NULL;" +846,54514,54514,277,26,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT customers.region_id, customers.id +FROM customers +WHERE (customers.region_id = 6 AND customers.id NOT BETWEEN 29 AND 200);" +847,2046000,2046000,1327,10,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.region_id, COUNT(customers.region_id) +FROM regions CROSS JOIN customers +GROUP BY customers.region_id;" +848,67736,89716,348,16,transformation 0 JoinCommutativity: 1; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, t1.age AS c2807 +FROM regions AS t0 CROSS JOIN users AS t1 +WHERE ((t0.id NOT IN (10) OR t1.age = 21) OR t1.id != 15) +GROUP BY t1.id, t1.age +ORDER BY t1.age ASC;" +849,422,422,126,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.id AS c2855, books.price AS c862 +FROM books +WHERE books.id IN (3);" +850,3381,5636,175,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT users.age, books.price, books.id +FROM users JOIN books ON users.id = books.id;" +851,1319042,4017922,1570,10,transformation 0 JoinCommutativity: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT orders.id +FROM regions LEFT JOIN orders ON regions.id = orders.id CROSS JOIN departments +WHERE ((departments.id <= 8 AND orders.id <= 71) OR regions.id != 7) +GROUP BY orders.id +ORDER BY orders.id DESC;" +852,5210,29176,464,15,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id AS c5743, regions.id AS c3685 +FROM books CROSS JOIN employees RIGHT JOIN regions ON employees.id = regions.id +WHERE (employees.department_id != 5 AND employees.id IS NOT NULL);" +853,3730,6250,272,10,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t1.id, t0.id AS c1844 +FROM markets AS t0 CROSS JOIN regions AS t1 +WHERE t0.region != 'AMERICA';" +854,681154,6452074,926,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT users.id AS c8433, orders.id, users.age AS c5978 +FROM orders JOIN users ON orders.id = users.id;" +855,822,822,148,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.note, t0.region +FROM markets AS t0 +GROUP BY t0.note, t0.region;" +856,1222,1222,220,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT employees.department_id +FROM employees +WHERE (employees.department_id > 35 AND employees.id = 2);" +857,5837,15576,238,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id AS c3241, users.id, users.age +FROM regions LEFT JOIN users ON regions.id = users.id +WHERE ((users.age != 64 OR users.age >= 33) OR regions.id > 9);" +858,113694,234290,520,393,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT customers.id +FROM customers FULL JOIN departments ON customers.id = departments.id FULL JOIN users ON customers.id = users.id +WHERE ((users.id != 8 OR departments.id IS NOT NULL) OR customers.id > 122) +GROUP BY customers.id;" +859,4668,7332,274,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT regions.id, books.id, COUNT(*) AS c2626 +FROM employees LEFT JOIN books ON employees.id = books.id RIGHT JOIN regions ON employees.id = regions.id +GROUP BY regions.id, books.id;" +860,788,788,190,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT departments.id +FROM departments +WHERE departments.id <= 2 +ORDER BY departments.id ASC;" +861,54051700,54051700,46969,500,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.customer_id +FROM users AS t0 CROSS JOIN orders AS t1 +GROUP BY t1.customer_id +ORDER BY t1.customer_id DESC;" +862,1308778,4353954,1550,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t0.department_id +FROM employees AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id RIGHT JOIN markets AS t2 ON t1.id = t2.id +WHERE ((t2.note != 'Old World' OR t0.department_id <= 12) OR (t0.department_id != 35 OR t1.id NOT BETWEEN 4 AND 78));" +863,1319320,18364979,1814,32,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id, markets.id +FROM markets CROSS JOIN users LEFT JOIN orders ON markets.id = orders.id +WHERE markets.id > 1 +GROUP BY users.id, markets.id;" +864,3589,5844,218,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, t1.price AS c2144 +FROM users AS t0 JOIN books AS t1 ON t0.id = t1.id +WHERE (t0.age IS NULL OR t0.age = 21) +GROUP BY t1.id, t1.price +ORDER BY t1.id ASC, t1.price DESC;" +865,12026,12026,322,33,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price, employees.id AS c7767, COUNT(books.price) AS c6906, COUNT(*) +FROM books CROSS JOIN employees +GROUP BY books.price, employees.id;" +866,1875,1875,156,11,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.department_id, t0.id +FROM employees AS t0 +WHERE t0.department_id IS NOT NULL +ORDER BY t0.department_id ASC, t0.id DESC;" +867,2475,2475,146,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age AS c9309, users.id +FROM users +WHERE users.age IN (45, 75, 88) +ORDER BY users.id DESC, users.age DESC;" +868,3377,8272,260,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 9 FilterLiftThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT users.id +FROM departments FULL JOIN users ON departments.id = users.id +WHERE (departments.id >= 3 AND (users.age = 11 OR users.id > 15));" +869,110128,401488,277,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.id AS c2844 +FROM regions LEFT JOIN customers ON regions.id = customers.id +WHERE regions.id IN (6, 9, 32, 41);" +870,3042,3042,142,16,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.age +FROM users +WHERE (users.age IN (3, 50, 69, 95) OR users.id != 13);" +871,111030,401610,321,1,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id AS c476, customers.region_id AS c8704, customers.id +FROM customers RIGHT JOIN regions ON customers.id = regions.id +WHERE (customers.region_id >= 66 OR regions.id = 6);" +872,97942,671362,480,187,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT users.age, users.id AS c6703 +FROM users JOIN customers ON users.id = customers.id CROSS JOIN employees;" +873,366,366,125,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT markets.id, markets.region +FROM markets;" +874,6031,12760,219,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t2.id AS c1600, t1.age AS c5206 +FROM departments AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id LEFT JOIN regions AS t2 ON t0.id = t2.id;" +875,678740,1552690,1047,15,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT markets.id +FROM markets JOIN orders ON markets.id = orders.id CROSS JOIN departments;" +876,677111,1551916,1030,5,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.id, orders.customer_id, books.price +FROM books JOIN orders ON books.id = orders.id FULL JOIN departments ON orders.id = departments.id;" +877,1220,1220,128,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions;" +878,3215,5194,317,3,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT markets.note AS c9740, departments.id +FROM markets JOIN regions ON markets.id = regions.id LEFT JOIN departments ON markets.id = departments.id +WHERE (markets.note != 'North, South' OR markets.region IS NOT NULL);" +879,683000,683000,1259,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.customer_id +FROM orders AS t0 +WHERE t0.id IN (24, 100, 169);" +880,110500,110500,284,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id +FROM customers +ORDER BY customers.id DESC;" +881,4422165,2185552525,29179,55000,transformation 0 JoinCommutativity: 1; transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, employees.id AS c6235, SUM(employees.department_id) +FROM orders CROSS JOIN customers RIGHT JOIN employees ON customers.id = employees.id +WHERE customers.id <= 83 +GROUP BY orders.id, employees.id;" +882,610000,610000,1363,5000,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.customer_id AS c4872, t0.id AS c6904 +FROM orders AS t0;" +883,228608,3911463,755,0,transformation 0 JoinCommutativity: 1; transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT customers.id AS c9594, departments.id +FROM customers CROSS JOIN users RIGHT JOIN departments ON users.id = departments.id +WHERE customers.region_id = 58 +GROUP BY customers.id, departments.id;" +884,748788,4401488,1078,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 2,"SELECT regions.id AS c6190 +FROM customers JOIN regions ON customers.id = regions.id JOIN orders ON customers.id = orders.id +WHERE orders.id IN (23, 82, 167, 183);" +885,4876,15210,221,15,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id, t1.age AS c5455, t1.id AS c7095 +FROM regions AS t0 RIGHT JOIN users AS t1 ON t0.id = t1.id +WHERE t1.age >= 11;" +886,1732,1732,163,3,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM regions AS t0 +WHERE (t0.id IS NULL OR (t0.id > 7 AND t0.id IS NOT NULL));" +887,5293,12168,184,6,transformation 7 FilterToJoinPredicate: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.id AS c2880, employees.department_id, COUNT(regions.id) AS c3583, SUM(employees.department_id) +FROM regions JOIN employees ON regions.id = employees.id +WHERE ((employees.department_id >= 22 OR regions.id IS NOT NULL) OR (employees.id >= 2 AND employees.id < 66)) +GROUP BY employees.id, employees.department_id;" +888,1220,1220,135,10,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id AS c6441 +FROM regions;" +889,8756,12584,373,12,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.id AS c1119, t1.note AS c6695, COUNT(*) AS c7579, COUNT(*) +FROM employees AS t0 CROSS JOIN markets AS t1 LEFT JOIN books AS t2 ON t0.id = t2.id +GROUP BY t2.id, t1.note;" +890,563,563,192,1,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id, t0.note, COUNT(*) AS c5980 +FROM markets AS t0 +WHERE (t0.region != 'AMERICA' AND (t0.note NOT IN ('AMERICA') OR t0.region != 'AMERICA')) +GROUP BY t0.id, t0.note;" +891,9222,17622,267,20,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT t2.region, t1.id +FROM regions AS t0 CROSS JOIN departments AS t1 LEFT JOIN markets AS t2 ON t1.id = t2.id +WHERE ((t2.note NOT IN ('North, South', 'North, South') OR t2.region IS NOT NULL) AND (t2.id IN (1, 3) AND t2.region IN ('AMERICA', 'EUROPE')));" +892,366,366,122,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT markets.id, markets.note +FROM markets;" +893,945706300,945706300,794257,7500000,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2,"SELECT t0.region, t2.customer_id +FROM markets AS t0 CROSS JOIN customers AS t1 CROSS JOIN orders AS t2;" +894,4121,8425,210,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT users.id, users.age, departments.id +FROM users RIGHT JOIN departments ON users.id = departments.id +ORDER BY users.id DESC, departments.id DESC;" +895,422822,424210,1309,5500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id, customers.region_id, customers.id +FROM books RIGHT JOIN employees ON books.id = employees.id CROSS JOIN customers +ORDER BY employees.department_id DESC;" +896,110500,110500,300,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id +FROM customers AS t0 +ORDER BY t0.region_id DESC;" +897,422,422,162,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT books.id +FROM books +WHERE (books.id = 3 OR books.price = 77);" +898,1092,1972,321,0,transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.id, t1.price, t1.id AS c28 +FROM departments AS t0 LEFT JOIN books AS t1 ON t0.id = t1.id +WHERE ((t0.id >= 4 OR t0.id IN (42, 48)) AND (t0.id BETWEEN 3 AND 56 AND t1.id = 2));" +899,61000,61000,244,500,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT customers.region_id +FROM customers;" +900,2647,7063,365,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 5; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.id AS c9569 +FROM employees JOIN departments ON employees.id = departments.id FULL JOIN books ON employees.id = books.id +WHERE ((books.price BETWEEN 55 AND 77 OR books.id IN (2, 2)) AND (books.id = 2 AND employees.id = 1)) +GROUP BY employees.id +ORDER BY employees.id ASC;" +901,1056,1296,206,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.id, books.price +FROM markets RIGHT JOIN books ON markets.id = books.id;" +902,15960,15960,288,110,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.department_id AS c4915 +FROM regions AS t0 CROSS JOIN employees AS t1;" +903,91125,91125,393,196,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.region_id, t0.id AS c4393 +FROM customers AS t0 +WHERE t0.region_id BETWEEN 2 AND 5 +GROUP BY t0.region_id, t0.id;" +904,14262,47796,546,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.id AS c1939 +FROM markets CROSS JOIN users FULL JOIN employees ON markets.id = employees.id +WHERE ((markets.id < 2 OR users.id < 33) AND users.age = 91);" +905,1188612,2251972,2182,0,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 5; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT departments.id, markets.region AS c1481, orders.customer_id +FROM orders LEFT JOIN departments ON orders.id = departments.id FULL JOIN markets ON orders.id = markets.id +WHERE ((orders.id = 16 OR orders.customer_id != 311) AND markets.note IN ('ASIA'));" +906,822,822,157,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.region, markets.note, COUNT(*), COUNT(markets.id) +FROM markets +GROUP BY markets.region, markets.note;" +907,115472,1107972,422,20,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id AS c6763, t0.id +FROM regions AS t0 CROSS JOIN books AS t1 LEFT JOIN customers AS t2 ON t0.id = t2.id +WHERE t1.id IN (1, 1, 3, 97) +GROUP BY t1.id, t0.id;" +908,16034,17304,337,44,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t0.price, t2.department_id AS c7096, COUNT(*) AS c2148 +FROM books AS t0 FULL JOIN regions AS t1 ON t0.id = t1.id CROSS JOIN employees AS t2 +GROUP BY t0.price, t2.department_id +ORDER BY t2.department_id DESC, t0.price DESC;" +909,1610,1610,194,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT regions.id +FROM regions +WHERE regions.id >= 66;" +910,2309766,6019600,3273,5000,transformation 0 JoinCommutativity: 1; transformation 2 FilterSplit: 1; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id +FROM users AS t0 JOIN regions AS t1 ON t0.id = t1.id CROSS JOIN orders AS t2 +WHERE (t0.id < 11 AND t1.id = 7);" +911,1307140,4001220,1382,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT regions.id +FROM regions LEFT JOIN orders ON regions.id = orders.id;" +912,72243,160692,335,1,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.id AS c8092 +FROM markets JOIN customers ON markets.id = customers.id LEFT JOIN users ON markets.id = users.id +WHERE (users.id = 17 OR markets.region != 'AMERICA');" +913,366,366,123,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT books.price, books.id +FROM books;" +914,366,366,124,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT markets.region AS c7952 +FROM markets;" +915,433,433,145,3,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.id, markets.note +FROM markets +WHERE (markets.note IS NOT NULL OR markets.id IN (1, 3, 3)) +ORDER BY markets.id ASC;" +916,5055,12333,307,2,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT employees.department_id, markets.note, markets.id +FROM employees LEFT JOIN regions ON employees.id = regions.id FULL JOIN markets ON regions.id = markets.id +WHERE (markets.region = 'AMERICA' OR (markets.note = 'North, South' AND markets.region != 'unknown')) +ORDER BY markets.note ASC, markets.id ASC;" +917,419320,1259700,1857,1501,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT books.id, customers.id, COUNT(books.id) AS c1217, SUM(customers.id) +FROM customers CROSS JOIN books RIGHT JOIN regions ON books.id = regions.id +GROUP BY books.id, customers.id +ORDER BY customers.id ASC;" +918,26767433,27192133,32958,17,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.price, t0.age, t0.id +FROM users AS t0 CROSS JOIN orders AS t1 FULL JOIN books AS t2 ON t1.id = t2.id +WHERE t2.price = 77 +ORDER BY t0.id ASC;" +919,64542,156983,335,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 5; transformation 6 InToOrChain: 1; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT departments.id +FROM markets LEFT JOIN departments ON markets.id = departments.id FULL JOIN customers ON markets.id = customers.id +WHERE ((markets.note = 'ASIA' AND markets.region = 'EUROPE') AND customers.region_id IN (2, 7)) +ORDER BY departments.id DESC;" +920,1311328,4366665,1984,17,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT t2.age, t0.customer_id AS c4744 +FROM orders AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id JOIN users AS t2 ON t0.id = t2.id +WHERE t0.customer_id IS NOT NULL +ORDER BY t2.age ASC, t0.customer_id ASC;" +921,1309378,4353256,3596,511,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.customer_id, t1.id, COUNT(t1.department_id), SUM(t0.id) AS c8528 +FROM orders AS t0 LEFT JOIN employees AS t1 ON t0.id = t1.id +GROUP BY t0.customer_id, t1.id;" +922,3009,3009,141,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age +FROM users +ORDER BY users.age DESC;" +923,2690,2690,249,15,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.note AS c7666 +FROM departments AS t0 CROSS JOIN markets AS t1;" +924,677986,4353954,1037,2,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.customer_id, books.price AS c537 +FROM orders JOIN employees ON orders.id = employees.id JOIN books ON employees.id = books.id +WHERE (employees.department_id NOT IN (1, 5, 6, 14) OR (books.price NOT IN (55, 66, 93) OR employees.id > 3));" +925,432,432,211,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id AS c1708, books.price AS c6613 +FROM books +ORDER BY books.id ASC, books.price ASC;" +926,114285,414844,412,0,implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT users.age, regions.id AS c873 +FROM regions JOIN users ON regions.id = users.id FULL JOIN customers ON regions.id = customers.id +WHERE regions.id IN (9, 9);" +927,6045,6630,272,30,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.price AS c9588, t0.id, t2.id +FROM departments AS t0 JOIN books AS t1 ON t0.id = t1.id CROSS JOIN regions AS t2;" +928,2226,3466,216,10,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.id +FROM regions AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id;" +929,227750,227750,490,404,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT customers.region_id, customers.id AS c650, COUNT(customers.region_id) +FROM customers +WHERE ((customers.region_id NOT IN (1, 9, 46) OR customers.id = 28) OR (customers.id NOT IN (135) AND customers.id BETWEEN 140 AND 147)) +GROUP BY customers.region_id, customers.id +ORDER BY customers.region_id ASC;" +930,71454,440694,454,3,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT departments.id, customers.id AS c5398 +FROM customers LEFT JOIN employees ON customers.id = employees.id LEFT JOIN departments ON employees.id = departments.id +WHERE employees.department_id < 6;" +931,11673302,17248202,24718,50000,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, orders.customer_id, regions.id AS c3962 +FROM regions CROSS JOIN orders +WHERE (orders.customer_id != 353 OR (regions.id != 58 OR orders.customer_id = 281)) +ORDER BY orders.id DESC, orders.customer_id ASC, regions.id DESC;" +932,1603000,1751193000,2148,10,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 6 ImplementHashJoin: 1,"SELECT customers.id, orders.id, customers.region_id +FROM customers CROSS JOIN regions JOIN orders ON customers.id = orders.id +WHERE orders.customer_id IN (97, 268);" +933,3667,5498,271,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 4,"SELECT t1.note, t1.id, COUNT(t2.id) AS c841 +FROM departments AS t0 LEFT JOIN markets AS t1 ON t0.id = t1.id JOIN regions AS t2 ON t0.id = t2.id +WHERE (t2.id IS NULL OR (t1.id NOT BETWEEN 1 AND 3 AND t1.note != 'Old World')) +GROUP BY t1.note, t1.id;" +934,4370,8492,308,33,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t1.price, t1.id, t0.department_id AS c8063 +FROM employees AS t0 CROSS JOIN books AS t1 +WHERE (t0.department_id >= 1 OR (t1.price <= 77 OR t0.department_id NOT BETWEEN 18 AND 33));" +935,9698,24047,378,51,transformation 0 JoinCommutativity: 2; transformation 6 InToOrChain: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t2.id +FROM users AS t0 FULL JOIN employees AS t1 ON t0.id = t1.id CROSS JOIN books AS t2 +WHERE ((t2.price IN (13, 66) AND t1.department_id > 53) OR (t0.id > 4 OR t0.id != 58)) +ORDER BY t2.id ASC;" +936,1775,1775,170,7,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id +FROM regions +WHERE regions.id <= 7 +ORDER BY regions.id DESC;" +937,1306862,2251982,1792,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT orders.id AS c9311, books.price, orders.customer_id +FROM departments LEFT JOIN orders ON departments.id = orders.id LEFT JOIN books ON orders.id = books.id +ORDER BY orders.id ASC, books.price ASC, orders.customer_id ASC;" +938,4212,8516,207,5,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age, departments.id, users.id +FROM users RIGHT JOIN departments ON users.id = departments.id +WHERE (departments.id != 3 OR (users.id < 5 AND users.id != 17));" +939,633,633,166,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id +FROM departments AS t0 +WHERE t0.id = 5 +ORDER BY t0.id ASC;" +940,892,1352,289,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1,"SELECT t0.note AS c4575 +FROM markets AS t0 JOIN books AS t1 ON t0.id = t1.id +WHERE ((t0.id >= 1 AND t0.region IS NULL) AND t1.price != 66);" +941,1307140,4001220,2081,5000,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT orders.id, orders.customer_id, regions.id +FROM regions RIGHT JOIN orders ON regions.id = orders.id;" +942,3922,10020,172,15,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.department_id, regions.id, employees.id +FROM employees FULL JOIN regions ON employees.id = regions.id;" +943,1317110,12057028,2701,4997,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT t1.id, t1.note +FROM employees AS t0 CROSS JOIN markets AS t1 FULL JOIN orders AS t2 ON t1.id = t2.id +WHERE (t1.region IS NULL OR t1.region = 'unknown');" +944,805000,805000,1368,2978,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id AS c1298, orders.id +FROM orders +WHERE orders.customer_id <= 300;" +945,1309930,6453009,3132,5001,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT users.age, orders.customer_id, orders.id +FROM orders LEFT JOIN users ON orders.id = users.id +ORDER BY users.age ASC, orders.customer_id DESC, orders.id ASC;" +946,422,422,168,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.region, markets.id +FROM markets +WHERE (markets.region = 'AMERICA' AND (markets.note IS NULL AND markets.id <= 21));" +947,422,422,135,2,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.region +FROM markets +WHERE markets.region != 'EUROPE';" +948,1308242,4003532,1324,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 5,"SELECT orders.customer_id +FROM orders RIGHT JOIN regions ON orders.id = regions.id RIGHT JOIN books ON orders.id = books.id +ORDER BY orders.customer_id DESC;" +949,1309963,6453042,2038,47,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age AS c940, orders.id +FROM orders FULL JOIN users ON orders.id = users.id +WHERE (orders.customer_id IN (84, 108, 233, 290) OR users.age > 64);" +950,1826,1826,149,11,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT employees.department_id AS c483 +FROM employees +ORDER BY employees.department_id DESC;" +951,422,422,139,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT markets.id, markets.note, markets.region AS c2312 +FROM markets +WHERE (markets.id = 1 AND markets.region = 'EUROPE');" +952,563,563,174,1,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.id, SUM(books.price) AS c4950 +FROM books +WHERE books.id > 2 +GROUP BY books.id;" +953,1875,1875,153,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.department_id AS c600 +FROM employees AS t0 +WHERE t0.id IS NULL +ORDER BY t0.department_id DESC;" +954,822,822,386,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT books.price, books.id AS c2022 +FROM books +GROUP BY books.price, books.id;" +955,4594,14044,222,0,transformation 0 JoinCommutativity: 2; transformation 5 FilterPushdownThroughJoin: 4; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id, employees.id AS c9494, departments.id AS c9282 +FROM regions RIGHT JOIN employees ON regions.id = employees.id JOIN departments ON employees.id = departments.id +WHERE employees.department_id >= 33;" +956,113102,410020,348,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 3,"SELECT customers.region_id AS c272, regions.id +FROM regions RIGHT JOIN customers ON regions.id = customers.id RIGHT JOIN employees ON customers.id = employees.id;" +957,2383,5263,190,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id, t1.id, COUNT(*) AS c5678 +FROM departments AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id +WHERE (t1.id >= 8 AND (t0.id IS NULL AND t1.id NOT BETWEEN 3 AND 10)) +GROUP BY t0.id, t1.id;" +958,113219,647798,312,6,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.age, users.id AS c4390, customers.id AS c5582 +FROM users LEFT JOIN customers ON users.id = customers.id +WHERE (users.age <= 22 OR customers.region_id = 1);" +959,2388,3776,175,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT books.price AS c3041 +FROM employees JOIN books ON employees.id = books.id;" +960,2690,2690,311,15,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t1.id, t1.price +FROM departments AS t0 CROSS JOIN books AS t1;" +961,5785,9268,261,7,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; enforcer 0 SortEnforcer: 4,"SELECT employees.id AS c7515 +FROM books RIGHT JOIN users ON books.id = users.id FULL JOIN employees ON users.id = employees.id +WHERE (employees.department_id >= 12 OR books.price > 55) +ORDER BY employees.id DESC;" +962,3993,8698,189,0,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id AS c6095, COUNT(*) +FROM users AS t0 JOIN departments AS t1 ON t0.id = t1.id +WHERE t0.age IS NULL +GROUP BY t1.id;" +963,417240,1257520,798,1500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT markets.note, markets.id +FROM markets CROSS JOIN customers LEFT JOIN regions ON customers.id = regions.id;" +964,4729,8792,367,1,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 1; transformation 10 ProjectionPushdownThroughJoin: 3; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 2; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT users.id, users.age +FROM regions FULL JOIN markets ON regions.id = markets.id JOIN users ON regions.id = users.id +WHERE markets.note = 'Old World';" +965,2074,2074,172,17,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.age +FROM users AS t0;" +966,1705750,1705750,1999,4998,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, orders.customer_id +FROM orders +WHERE ((orders.id < 52 OR orders.id NOT IN (71, 82)) OR orders.id = 92) +ORDER BY orders.id ASC, orders.customer_id DESC;" +967,74906,661500,338,6,transformation 0 JoinCommutativity: 2; transformation 1 JoinAssociativity: 2; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 2,"SELECT employees.id +FROM customers JOIN users ON customers.id = users.id LEFT JOIN employees ON users.id = employees.id +WHERE employees.department_id <= 31;" +968,563,563,188,1,transformation 2 FilterSplit: 1; transformation 3 FilterMerge: 1; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.region, markets.note, COUNT(markets.id) AS c1532 +FROM markets +WHERE ((markets.region IN ('AMERICA', 'EUROPE') OR markets.region NOT IN ('AMERICA')) AND (markets.note IN ('AMERICA', 'AMERICA', 'North, South', 'unknown') AND markets.id <= 2)) +GROUP BY markets.region, markets.note;" +969,70410,401610,299,1,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT customers.id, customers.region_id +FROM customers JOIN regions ON customers.id = regions.id +WHERE ((customers.region_id IN (5) AND customers.region_id > 5) OR customers.region_id <= 2);" +970,69760,401610,288,10,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1,"SELECT t0.id, t1.id AS c5601 +FROM customers AS t0 LEFT JOIN regions AS t1 ON t0.id = t1.id +WHERE t1.id != 53;" +971,1307948,4351826,1402,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.department_id, t1.customer_id AS c5553, t0.id +FROM employees AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id +ORDER BY t0.id DESC, t1.customer_id DESC, t0.department_id DESC;" +972,1398700,2124700,13080,5500,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t2.id +FROM customers AS t0 CROSS JOIN markets AS t1 CROSS JOIN employees AS t2 +WHERE t1.note = 'North, South';" +973,366,366,123,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT books.id, books.price AS c662 +FROM books;" +974,2610,3998,194,11,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.id, books.id +FROM books RIGHT JOIN employees ON books.id = employees.id +WHERE ((books.id IS NULL OR employees.id IS NOT NULL) OR books.id BETWEEN 2 AND 14) +ORDER BY employees.id DESC, books.id ASC;" +975,6650300,6650300,4507,5000,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT orders.id, COUNT(books.id), COUNT(books.id) +FROM books CROSS JOIN orders +GROUP BY orders.id;" +976,3323,10063,227,0,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 3; transformation 9 FilterLiftThroughJoin: 3; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT employees.department_id, employees.id AS c8812, COUNT(*), COUNT(*) +FROM employees RIGHT JOIN regions ON employees.id = regions.id +WHERE (regions.id >= 9 AND (employees.id < 2 AND employees.department_id >= 31)) +GROUP BY employees.department_id, employees.id;" +977,1306838,1551778,1363,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT t1.customer_id AS c4052, t0.id AS c3598, COUNT(*), COUNT(*) AS c317 +FROM books AS t0 LEFT JOIN orders AS t1 ON t0.id = t1.id LEFT JOIN markets AS t2 ON t1.id = t2.id +WHERE ((t2.id NOT IN (61) OR t2.note IN ('Fast lane', 'Fast lane')) OR t0.price NOT IN (55, 55, 66, 77)) +GROUP BY t1.customer_id, t0.id;" +978,5325,7850,315,15,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT regions.id +FROM departments LEFT JOIN regions ON departments.id = regions.id CROSS JOIN books +ORDER BY regions.id ASC;" +979,6925,11125,263,50,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id, t0.id +FROM departments AS t0 CROSS JOIN regions AS t1 +WHERE t0.id <= 5 +ORDER BY t0.id DESC, t1.id DESC;" +980,109202,155432,376,500,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; enforcer 0 SortEnforcer: 3,"SELECT markets.region +FROM markets RIGHT JOIN customers ON markets.id = customers.id +ORDER BY markets.region ASC;" +981,366,366,128,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id, t0.price +FROM books AS t0;" +982,7846500,7846500,14033,25000,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2; enforcer 0 SortEnforcer: 1,"SELECT t1.id, t0.id, t2.id +FROM customers AS t0 CROSS JOIN regions AS t1 CROSS JOIN departments AS t2 +ORDER BY t0.id ASC, t2.id DESC;" +983,5923,9192,258,3,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 2; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 5,"SELECT markets.id +FROM regions RIGHT JOIN markets ON regions.id = markets.id JOIN users ON regions.id = users.id +GROUP BY markets.id +ORDER BY markets.id ASC;" +984,74556,452315,349,2,transformation 0 JoinCommutativity: 3; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t2.id AS c3607, COUNT(t0.region_id) +FROM customers AS t0 JOIN employees AS t1 ON t0.id = t1.id RIGHT JOIN users AS t2 ON t1.id = t2.id +WHERE t1.id >= 5 +GROUP BY t2.id;" +985,3018,5998,236,5,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t1.id, t0.department_id, COUNT(*) +FROM employees AS t0 RIGHT JOIN departments AS t1 ON t0.id = t1.id +WHERE t0.department_id <= 12 +GROUP BY t1.id, t0.department_id;" +986,210758,210758,487,500,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t0.id AS c6190 +FROM customers AS t0 +WHERE ((t0.id IN (2, 24, 27, 41) OR t0.id NOT IN (75, 109)) OR t0.region_id IN (1, 5, 5)) +GROUP BY t0.id;" +987,15668,45964,305,4,transformation 0 JoinCommutativity: 1; implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1; implementation 7 ImplementMergeJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 3,"SELECT t0.id, COUNT(*) +FROM books AS t0 CROSS JOIN users AS t1 RIGHT JOIN regions AS t2 ON t0.id = t2.id +GROUP BY t0.id +ORDER BY t0.id ASC;" +988,68692,155892,325,2,transformation 0 JoinCommutativity: 1; transformation 5 FilterPushdownThroughJoin: 2; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 2; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 2,"SELECT t0.region_id +FROM customers AS t0 JOIN markets AS t1 ON t0.id = t1.id +WHERE (t1.note != 'Fast lane' OR (t1.note NOT IN ('Fast lane') AND t1.region != 'unknown')) +GROUP BY t0.region_id +ORDER BY t0.region_id DESC;" +989,610,610,137,5,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT t0.id +FROM departments AS t0;" +990,11510800,11510800,37851,75000,implementation 0 ImplementTable: 3; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 2,"SELECT books.price, orders.id, departments.id +FROM books CROSS JOIN orders CROSS JOIN departments;" +991,2132,2132,140,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT users.age +FROM users +WHERE ((users.id NOT BETWEEN 13 AND 16 OR users.id = 34) AND users.id BETWEEN 14 AND 15) +ORDER BY users.age ASC;" +992,1098532,1098532,2037,5000,transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT orders.customer_id AS c1752 +FROM orders +WHERE ((orders.id BETWEEN 69 AND 86 OR orders.customer_id > 243) OR (orders.customer_id NOT IN (224) OR orders.id IS NOT NULL));" +993,99532,99532,341,396,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT t0.region_id +FROM customers AS t0 +WHERE ((t0.region_id BETWEEN 3 AND 5 OR t0.id IS NULL) OR t0.id >= 148);" +994,1133,1133,142,0,transformation 2 FilterSplit: 1; transformation 3 FilterMerge: 1; transformation 6 InToOrChain: 1; implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1; enforcer 0 SortEnforcer: 1,"SELECT regions.id AS c494 +FROM regions +WHERE ((regions.id = 100 OR regions.id NOT IN (5)) AND regions.id IN (29)) +ORDER BY regions.id ASC;" +995,366,366,146,3,implementation 0 ImplementTable: 1; implementation 3 ImplementProjection: 1,"SELECT markets.id, markets.region +FROM markets;" +996,1900450,2614450,2199,418,transformation 0 JoinCommutativity: 1; transformation 8 CrossJoinToJoin: 1; implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT t1.id, t1.region_id AS c505, COUNT(*) +FROM users AS t0 CROSS JOIN customers AS t1 +WHERE t1.id >= 83 +GROUP BY t1.id, t1.region_id;" +997,12910,12910,509,85,implementation 0 ImplementTable: 2; implementation 3 ImplementProjection: 1; implementation 5 ImplementCrossJoin: 1,"SELECT t0.id, t1.id, t0.age AS c6537 +FROM users AS t0 CROSS JOIN departments AS t1;" +998,2798,2798,157,0,implementation 0 ImplementTable: 1; implementation 1 ImplementFilter: 1; implementation 3 ImplementProjection: 1,"SELECT users.id AS c9941 +FROM users +WHERE ((users.id >= 12 OR users.age < 22) AND (users.age IS NULL OR users.id IS NULL));" +999,70213,403663,443,3,transformation 0 JoinCommutativity: 4; transformation 1 JoinAssociativity: 3; transformation 5 FilterPushdownThroughJoin: 3; transformation 6 InToOrChain: 1; transformation 7 FilterToJoinPredicate: 2; transformation 9 FilterLiftThroughJoin: 4; transformation 11 OuterJoinToInner: 1; implementation 0 ImplementTable: 3; implementation 1 ImplementFilter: 2; implementation 3 ImplementProjection: 1; implementation 4 ImplementJoin: 1; implementation 6 ImplementHashJoin: 1; implementation 8 ImplementAggregation: 1; enforcer 0 SortEnforcer: 1,"SELECT markets.note +FROM customers RIGHT JOIN regions ON customers.id = regions.id LEFT JOIN markets ON customers.id = markets.id +WHERE ((markets.id != 52 OR customers.id IN (129, 173)) AND (markets.region != 'ASIA' AND customers.region_id > 1)) +GROUP BY markets.note;" diff --git a/research/rule-lineage-random-1000-summary.csv b/research/rule-lineage-random-1000-summary.csv new file mode 100644 index 0000000..fdb56bc --- /dev/null +++ b/research/rule-lineage-random-1000-summary.csv @@ -0,0 +1,21 @@ +kind,rule_id,rule_name,total_lineage_occurrences,queries_with_rule,query_frequency_percent,max_occurrences_in_one_query +transformation,0,JoinCommutativity,594,517,51.70,4 +transformation,1,JoinAssociativity,75,29,2.90,4 +transformation,2,FilterSplit,70,42,4.20,5 +transformation,3,FilterMerge,7,7,0.70,1 +transformation,5,FilterPushdownThroughJoin,469,191,19.10,5 +transformation,6,InToOrChain,158,153,15.30,3 +transformation,7,FilterToJoinPredicate,27,23,2.30,2 +transformation,8,CrossJoinToJoin,82,82,8.20,1 +transformation,9,FilterLiftThroughJoin,40,19,1.90,4 +transformation,10,ProjectionPushdownThroughJoin,106,30,3.00,7 +transformation,11,OuterJoinToInner,101,91,9.10,2 +implementation,0,ImplementTable,1977,1000,100.00,3 +implementation,1,ImplementFilter,531,487,48.70,2 +implementation,3,ImplementProjection,1038,1000,100.00,4 +implementation,4,ImplementJoin,156,153,15.30,2 +implementation,5,ImplementCrossJoin,157,145,14.50,2 +implementation,6,ImplementHashJoin,171,162,16.20,2 +implementation,7,ImplementMergeJoin,493,394,39.40,2 +implementation,8,ImplementAggregation,319,319,31.90,1 +enforcer,0,SortEnforcer,1494,706,70.60,6 diff --git a/research/run.py b/research/run.py new file mode 100644 index 0000000..c6c559b --- /dev/null +++ b/research/run.py @@ -0,0 +1,23 @@ +import extractor, ms_sql_server_extractor + +from ms_sql_server_extractor import MsSqlServerExtractor + +extractor_obj = MsSqlServerExtractor( + dataset="/home/st/c/iu9-sql-compiler/research/datasets/mysql-data/imdb", + host="localhost", + port=1433, + user="sa", + password="Password123!", + max_rows=10_000, +) + +query = """ +SELECT p.primaryName, t.averageRating +FROM dbo.TitlePrincipals tp +JOIN dbo.Principals p ON p.principalId = tp.principalId +JOIN dbo.Titles t ON t.titleId = tp.titleId +WHERE t.averageRating IS NOT NULL +""" + +plan = extractor_obj.extract(query) +print(plan) \ No newline at end of file diff --git a/research/show_rules.sql b/research/show_rules.sql new file mode 100644 index 0000000..b832053 --- /dev/null +++ b/research/show_rules.sql @@ -0,0 +1,13 @@ +USE master; +GO + +DBCC TRACEON(3604); +GO + +PRINT '=== ENABLED RULES ==='; +DBCC SHOWONRULES; +GO + +PRINT '=== DISABLED RULES ==='; +DBCC SHOWOFFRULES; +GO diff --git a/research/ssb-dbgen b/research/ssb-dbgen new file mode 160000 index 0000000..90141f6 --- /dev/null +++ b/research/ssb-dbgen @@ -0,0 +1 @@ +Subproject commit 90141f692abfed592627462996a12eb242d2f053 diff --git a/research/test_converter.py b/research/test_converter.py new file mode 100644 index 0000000..5e83a6d --- /dev/null +++ b/research/test_converter.py @@ -0,0 +1,530 @@ +#!/usr/bin/env python3 + +import pytest + +from ms_sql_server_extractor import MsSqlServerExtractor +from converter import convert + +NS = "http://schemas.microsoft.com/sqlserver/2004/07/showplan" + + +def showplan(relop: str) -> str: + return f""" + + + + + + + {relop} + + + + + + + """ + + +@pytest.fixture(scope="session") +def extractor(): + return MsSqlServerExtractor( + dataset="./datasets", + host="localhost", + port=1433, + user="sa", + password="Password123!", + ) + + +def extract_plan(extractor, sql: str) -> str: + """Extract plan with OPTION (RECOMPILE) so literal values appear in the XML instead of @N placeholders.""" + return extractor.extract(sql + " OPTION (RECOMPILE)") + + +def test_seq_scan(extractor): + plan = extract_plan(extractor, "SELECT * FROM Titles") + result = convert(plan) + assert result == "(SeqScan Titles)" + + +def test_aliased_seq_scan_from_xml(): + plan = showplan(""" + + + + + + """) + + result = convert(plan) + + assert result == "(SeqScan Titles t)" + + +def test_filter_eq(extractor): + plan = extract_plan(extractor, "SELECT * FROM Titles WHERE Titles.isAdult = 1") + result = convert(plan) + assert result == "(PhysicalFilter (= (attr Titles isAdult) 1) (SeqScan Titles))" + + +def test_aliased_seq_scan_filter_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == "(PhysicalFilter (= (attr t isAdult) 1) (SeqScan Titles t))" + + +def test_filter_gt(extractor): + plan = extract_plan(extractor, "SELECT * FROM Titles WHERE Titles.titleId > 5000") + result = convert(plan) + assert result == "(IndexSeek (> (attr Titles titleId) 5000) Titles)" + + +def test_aliased_index_seek_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == "(IndexSeek (> (attr t titleId) 5000) Titles t)" + + +def test_index_seek_without_seek_predicates_uses_predicate_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == "(IndexSeek (= (attr t0 region_id) 7) customers t0)" + + +def test_index_seek_prefix_eq_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == "(IndexSeek (= (attr t0 region_id) 7) customers t0)" + + +def test_index_seek_bounded_range_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == ( + "(IndexSeek (and (> (attr customers region_id) 3)" + " (< (attr customers region_id) 8)) customers)" + ) + + +def test_index_seek_split_ranges_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == ( + "(IndexSeek (or (< (attr customers region_id) 8)" + " (> (attr customers region_id) 8)) customers)" + ) + + +def test_nested_loops_outer_reference_index_seek_is_skipped_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """) + + with pytest.raises(NotImplementedError, match="outer-reference index seek"): + convert(plan) + + +def test_filter_lt(extractor): + plan = extract_plan(extractor, "SELECT * FROM Titles WHERE Titles.titleId < 5000") + result = convert(plan) + assert result == "(IndexSeek (< (attr Titles titleId) 5000) Titles)" + + +def test_filter_and(extractor): + plan = extract_plan(extractor, "SELECT * FROM Titles WHERE Titles.isAdult = 1 AND Titles.titleId > 5000") + result = convert(plan) + assert result == "(PhysicalFilter (= (attr Titles isAdult) 1) (IndexSeek (> (attr Titles titleId) 5000) Titles))" + + +@pytest.mark.skip(reason="IS NULL not supported in target plan format (BinaryOp lacks kIsNull)") +def test_filter_or(extractor): + plan = extract_plan(extractor, "SELECT * FROM Titles WHERE Titles.isAdult = 0 OR Titles.endYear IS NULL") + result = convert(plan) + assert result == "" + + +def test_filter_not(extractor): + plan = extract_plan(extractor, "SELECT * FROM Titles WHERE NOT Titles.isAdult = 1") + result = convert(plan) + assert result == "(PhysicalFilter (!= (attr Titles isAdult) 1) (SeqScan Titles))" + + +def test_cross_join(extractor): + plan = extract_plan(extractor, "SELECT * FROM TitleTypes CROSS JOIN Genres") + result = convert(plan) + assert result == "(NestedLoopCrossJoin (SeqScan TitleTypes) (SeqScan Genres))" + + +def test_inner_join(extractor): + plan = extract_plan(extractor, + "SELECT * FROM TitlePrincipals JOIN Principals ON TitlePrincipals.principalId = Principals.principalId" + ) + result = convert(plan) + assert result == "(HashJoin Inner (= (attr Principals principalId) (attr TitlePrincipals principalId)) (SeqScan Principals) (SeqScan TitlePrincipals))" + + +def test_left_join(extractor): + plan = extract_plan(extractor, + "SELECT * FROM Titles LEFT OUTER JOIN Episodes ON Titles.titleId = Episodes.episodeId" + ) + result = convert(plan) + assert result == "(MergeJoin Left (= (attr Titles titleId) (attr Episodes episodeId)) (SeqScan Titles) (SeqScan Episodes))" + + +def test_join_with_filter(extractor): + plan = extract_plan(extractor, + "SELECT * FROM TitlePrincipals JOIN Principals ON TitlePrincipals.principalId = Principals.principalId WHERE TitlePrincipals.ordinal > 1" + ) + result = convert(plan) + assert result == "(HashJoin Inner (= (attr Principals principalId) (attr TitlePrincipals principalId)) (SeqScan Principals) (PhysicalFilter (> (attr TitlePrincipals ordinal) 1) (SeqScan TitlePrincipals)))" + + +def test_join_compound_predicate(extractor): + plan = extract_plan(extractor, + "SELECT * FROM TitlePrincipals JOIN Principals ON TitlePrincipals.principalId = Principals.principalId AND TitlePrincipals.ordinal = 1" + ) + result = convert(plan) + assert result == "(HashJoin Inner (= (attr TitlePrincipals principalId) (attr Principals principalId)) (PhysicalFilter (= (attr TitlePrincipals ordinal) 1) (SeqScan TitlePrincipals)) (SeqScan Principals))" + + +# ─── XML-driven tests for the new operators ────────────────────────────────── +# The XML shapes below are inferred from the ShowPlanXML schema; they MUST be +# re-validated against live MS SQL output and captured as ms-server-plans/*.xml +# fixtures (see the plan's Verification step). + +def test_string_const_filter_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == '(PhysicalFilter (= (attr m region) (str "AMERICA")) (SeqScan markets m))' + + +def test_sort_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == "(Sort (keys users.id Asc users.age Desc) (SeqScan users))" + + +def test_stream_aggregate_drops_enforcer_sort_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == ( + "(HashAggregate (group_by (attr users id))" + " (aggs (SUM (attr users age)) (COUNT *)) (SeqScan users))" + ) + + +def test_hash_aggregate_from_xml(): + plan = showplan(""" + + + + + + + + + + + + + + + + + + + + + + + """) + + result = convert(plan) + + assert result == ( + "(HashAggregate (group_by (attr users id))" + " (aggs (COUNT (attr users age))) (SeqScan users))" + ) + + +def test_compute_scalar_passthrough_from_xml(): + plan = showplan(""" + + + + + + + + """) + + result = convert(plan) + + assert result == "(SeqScan users)" diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index be6ee2a..5b39178 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -15,14 +15,57 @@ add_library(libsql logic/parser/PostgreSQLParserBase.cpp logic/parser/parser.cpp logic/parser/visitor.cpp + models/parser/expression.cpp + models/parser/join_type.cpp models/parser/relational_algebra_ast.cpp logic/result/error.cpp logic/result/result.cpp + logic/executor/plan.cpp + logic/executor/plan_serializer.cpp logic/executor/executor.cpp logic/executor/sequential_scan.cpp models/executor/tuple.cpp logic/executor/materialization.cpp logic/executor/llvm.cpp + logic/optimizer/optimizer.cpp + logic/optimizer/reachability.cpp + logic/optimizer/cardinality.cpp + logic/optimizer/schema_catalog.cpp + logic/optimizer/winner_key.cpp + logic/optimizer/sort_enforcer.cpp + logic/optimizer/group.cpp + logic/optimizer/memo.cpp + logic/optimizer/rules.cpp + logic/optimizer/rules_applier.cpp + logic/optimizer/rule.cpp + logic/transformation_rules/predicate_utils.cpp + logic/transformation_rules/join_commutativity.cpp + logic/transformation_rules/join_associativity.cpp + logic/transformation_rules/filter_split.cpp + logic/transformation_rules/filter_merge.cpp + logic/transformation_rules/filter_pushdown_through_projection.cpp + logic/transformation_rules/filter_pushdown_through_join.cpp + logic/transformation_rules/in_to_or_chain.cpp + logic/transformation_rules/filter_to_join_predicate.cpp + logic/transformation_rules/cross_join_to_join.cpp + logic/transformation_rules/filter_lift_through_join.cpp + logic/transformation_rules/projection_pushdown_through_join.cpp + logic/transformation_rules/outer_join_to_inner.cpp + logic/transformation_rules/aggregation_pushdown_through_join.cpp + logic/transformation_rules/aggregation_join_transpose.cpp + logic/implementation_rules/implement_table.cpp + logic/implementation_rules/implement_filter.cpp + logic/implementation_rules/implement_index_seek.cpp + logic/implementation_rules/implement_projection.cpp + logic/implementation_rules/implement_join.cpp + logic/implementation_rules/implement_cross_join.cpp + logic/implementation_rules/implement_hash_join.cpp + logic/implementation_rules/implement_merge_join.cpp + logic/implementation_rules/implement_aggregation.cpp + logic/optimizer/properties/sort_order.cpp + logic/optimizer/properties/sort_property.cpp + logic/optimizer/properties/property_set.cpp + utils/output_dot_plans.cpp ) add_library(stewkk::libsql ALIAS libsql) target_compile_features(libsql PUBLIC cxx_std_23) @@ -43,14 +86,19 @@ target_include_directories( ) target_compile_options(libsql PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(libsql PRIVATE ${BASE_LINK_FLAGS}) -llvm_map_components_to_libnames(llvm_libs Support Core IRReader OrcJIT X86CodeGen X86AsmParser X86Desc X86Info) target_link_directories(libsql PUBLIC ${LLVM_LIBRARY_DIRS}) +if(TARGET LLVM) + set(llvm_libs LLVM) +else() + llvm_map_components_to_libnames(llvm_libs Support Core IRReader OrcJIT X86CodeGen X86AsmParser X86Desc X86Info) +endif() target_link_libraries(libsql PRIVATE antlr4_static ${llvm_libs} Boost::asio Boost::thread Boost::filesystem + Boost::scope ) add_executable(sql @@ -65,4 +113,7 @@ set_target_properties(sql PROPERTIES target_compile_options(sql PRIVATE ${BASE_COMPILE_FLAGS}) target_link_libraries(sql PRIVATE libsql + Boost::asio + Boost::thread + Boost::filesystem ) diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index 322a550..5df06ca 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -1,7 +1,59 @@ #include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + namespace stewkk::sql { +namespace { + +Value MakeBooleanValue(bool value) { + return Value{false, static_cast(value)}; +} + +} // namespace + +template + requires std::invocable + && std::same_as, Ret> +Value ApplyIntegersOperator(Value lhs, Value rhs) { + if (lhs.is_null || rhs.is_null) { + return Value{true}; + } + return Value{false, Op{}(lhs.value.int_value, rhs.value.int_value)}; +} + +template + requires std::invocable + && std::same_as, bool> +Value ApplyBooleanOperator(Value lhs, Value rhs) { + if (lhs.is_null || rhs.is_null) { + return Value{true}; + } + return MakeBooleanValue(Op{}(lhs.value.bool_value, rhs.value.bool_value)); +} + +struct IntPow { + int64_t operator()(int64_t base, int64_t exp) const { + return static_cast(std::pow(base, exp)); + } +}; + Type GetExpressionType(const Expression& expr, const AttributesInfo& available_attrs) { struct ExpressionTypeVisitor { Type operator()(const BinaryExpression& binop) const { @@ -18,6 +70,19 @@ Type GetExpressionType(const Expression& expr, const AttributesInfo& available_a } return Type::kInt; } + if (std::ranges::contains(std::vector{BinaryOp::kAnd, BinaryOp::kOr}, binop.binop)) { + if (lhs_type != Type::kBool) { + throw std::logic_error{"types mismatch"}; + } + return Type::kBool; + } + if (lhs_type == Type::kString + && !std::ranges::contains(std::vector{BinaryOp::kEq, BinaryOp::kNotEq, + BinaryOp::kLt, BinaryOp::kLe, + BinaryOp::kGt, BinaryOp::kGe}, + binop.binop)) { + throw std::logic_error{"strings support only comparison operators"}; + } return Type::kBool; } Type operator()(const UnaryExpression& unop) const { @@ -28,17 +93,46 @@ Type GetExpressionType(const Expression& expr, const AttributesInfo& available_a } return Type::kInt; } + if (unop.op == UnaryOp::kIsNull) { + return Type::kBool; + } if (child_type != Type::kBool) { throw std::logic_error{"types mismatch"}; } return Type::kBool; } + Type operator()(const InExpression& in) const { + auto lhs_type = std::visit(*this, *in.lhs); + for (const auto& value : in.values) { + if (const auto* lit = std::get_if(&value); + lit && (*lit == Literal::kNull || *lit == Literal::kUnknown)) { + continue; + } + auto value_type = std::visit(*this, value); + if (value_type != lhs_type) { + throw std::logic_error{"types mismatch"}; + } + } + return Type::kBool; + } + Type operator()(const AggregateExpression& aggregate) const { + if (aggregate.function == AggregateFunction::kCount) { + return Type::kInt; + } + if (aggregate.is_star) { + throw std::logic_error{"star aggregate has no scalar type"}; + } + return std::visit(*this, *aggregate.argument); + } Type operator()(const IntConst& iconst) const { return Type::kInt; } + Type operator()(const StringConst& sconst) const { + return Type::kString; + } Type operator()(const Literal& literal) const { // NOTE: we are using switch because compiler will remind about adding - // type of new literal here + switch (literal) { case Literal::kNull: return Type::kBool; @@ -69,8 +163,8 @@ Type GetExpressionType(const Expression& expr, const AttributesInfo& available_a Type GetExpressionTypeUnchecked(const Expression& expr, const AttributesInfo& available_attrs) { struct ExpressionTypeVisitor { Type operator()(const BinaryExpression& binop) const { - if (std::ranges::contains(std::vector{BinaryOp::kPlus, BinaryOp::kMinus, BinaryOp::kDiv, - BinaryOp::kMod, BinaryOp::kPow}, + if (std::ranges::contains(std::vector{BinaryOp::kPlus, BinaryOp::kMinus, BinaryOp::kMul, + BinaryOp::kDiv, BinaryOp::kMod, BinaryOp::kPow}, binop.binop)) { return Type::kInt; } @@ -82,9 +176,18 @@ Type GetExpressionTypeUnchecked(const Expression& expr, const AttributesInfo& av } return Type::kBool; } + Type operator()(const InExpression& in) const { + return Type::kBool; + } + Type operator()(const AggregateExpression& aggregate) const { + return Type::kInt; + } Type operator()(const IntConst& iconst) const { return Type::kInt; } + Type operator()(const StringConst& sconst) const { + return Type::kString; + } Type operator()(const Literal& literal) const { switch (literal) { case Literal::kNull: @@ -113,13 +216,16 @@ Type GetExpressionTypeUnchecked(const Expression& expr, const AttributesInfo& av return std::visit(ExpressionTypeVisitor{available_attrs}, expr); } -AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const Projection& proj) { +AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const PhysicalProjection& proj) { AttributesInfo result_attributes; result_attributes.reserve(proj.expressions.size()); - for (const auto& target : proj.expressions) { + for (const auto& [index, target] : proj.expressions | std::views::enumerate) { AttributeInfo projection_result; projection_result.type = GetExpressionType(target, attrs); - if (const Attribute* attr = std::get_if(&target)) { + if (index < static_cast(proj.aliases.size()) && proj.aliases[index]) { + projection_result.name = *proj.aliases[index]; + projection_result.table = ""; + } else if (const Attribute* attr = std::get_if(&target)) { projection_result.name = std::move(attr->name); projection_result.table = std::move(attr->table); } @@ -130,44 +236,82 @@ AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const P Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, const Expression& expr) { struct ExpressionVisitor { + bool ValuesEqual(Value lhs, Value rhs, Type type) { + switch (type) { + case Type::kInt: + return lhs.value.int_value == rhs.value.int_value; + case Type::kBool: + return lhs.value.bool_value == rhs.value.bool_value; + case Type::kString: + return lhs.value.string_id == rhs.value.string_id; + } + } + + Value CompareValues(Value lhs, Value rhs, BinaryOp op, Type type) { + if (lhs.is_null || rhs.is_null) { + return Value{true}; + } + int cmp = 0; + switch (type) { + case Type::kInt: + cmp = (lhs.value.int_value > rhs.value.int_value) + - (lhs.value.int_value < rhs.value.int_value); + break; + case Type::kBool: + cmp = (lhs.value.bool_value > rhs.value.bool_value) + - (lhs.value.bool_value < rhs.value.bool_value); + break; + case Type::kString: { + const auto& l = GetInternedString(lhs.value.string_id); + const auto& r = GetInternedString(rhs.value.string_id); + cmp = (l > r) - (l < r); + break; + } + } + switch (op) { + case BinaryOp::kGt: + return MakeBooleanValue(cmp > 0); + case BinaryOp::kLt: + return MakeBooleanValue(cmp < 0); + case BinaryOp::kLe: + return MakeBooleanValue(cmp <= 0); + case BinaryOp::kGe: + return MakeBooleanValue(cmp >= 0); + case BinaryOp::kNotEq: + return MakeBooleanValue(cmp != 0); + case BinaryOp::kEq: + return MakeBooleanValue(cmp == 0); + default: + std::unreachable(); + } + } + Value operator()(const BinaryExpression& expr) { auto lhs = std::visit(*this, *expr.lhs); auto rhs = std::visit(*this, *expr.rhs); switch (expr.binop) { case BinaryOp::kGt: - if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); - } - return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kLt: - if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); - } - return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kLe: - if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); - } - return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kGe: - if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); - } - return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kNotEq: - if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); - } - return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kEq: - if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); - } - return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); - case BinaryOp::kOr: - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); - case BinaryOp::kAnd: - return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); + return CompareValues(std::move(lhs), std::move(rhs), expr.binop, + GetExpressionTypeUnchecked(*expr.lhs, source_attrs)); + case BinaryOp::kOr: { + bool lhs_true = !lhs.is_null && lhs.value.bool_value; + bool rhs_true = !rhs.is_null && rhs.value.bool_value; + if (lhs_true || rhs_true) return MakeBooleanValue(true); + if (lhs.is_null || rhs.is_null) return Value{true}; + return MakeBooleanValue(false); + } + case BinaryOp::kAnd: { + bool lhs_false = !lhs.is_null && !lhs.value.bool_value; + bool rhs_false = !rhs.is_null && !rhs.value.bool_value; + if (lhs_false || rhs_false) return MakeBooleanValue(false); + if (lhs.is_null || rhs.is_null) return Value{true}; + return MakeBooleanValue(true); + } case BinaryOp::kPlus: return ApplyIntegersOperator, int64_t>(std::move(lhs), std::move(rhs)); case BinaryOp::kMinus: @@ -191,16 +335,45 @@ Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, co return Value{true}; } if (child.value.bool_value) { - return Value{false, false}; + return MakeBooleanValue(false); } - return Value{false, true}; + return MakeBooleanValue(true); case UnaryOp::kMinus: if (child.is_null) { return Value{true}; } return Value{false, -child.value.int_value}; + case UnaryOp::kIsNull: + return MakeBooleanValue(child.is_null); } } + Value operator()(const InExpression& expr) { + auto lhs = std::visit(*this, *expr.lhs); + if (lhs.is_null) { + return Value{true}; + } + + auto lhs_type = GetExpressionTypeUnchecked(*expr.lhs, source_attrs); + bool saw_null = false; + for (const auto& value_expr : expr.values) { + auto value = std::visit(*this, value_expr); + if (value.is_null) { + saw_null = true; + continue; + } + if (ValuesEqual(lhs, value, lhs_type)) { + return MakeBooleanValue(!expr.negated); + } + } + + if (saw_null) { + return Value{true}; + } + return MakeBooleanValue(expr.negated); + } + Value operator()(const AggregateExpression&) { + throw std::logic_error{"aggregate expressions cannot be evaluated as scalar expressions"}; + } Value operator()(const Attribute& expr) { auto it = std::find_if(source_attrs.begin(), source_attrs.end(), [&expr](const AttributeInfo& attr_info) { @@ -213,15 +386,18 @@ Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, co Value operator()(const IntConst& expr) { return Value{false, expr}; } + Value operator()(const StringConst& expr) { + return Value{false, {.string_id = InternString(expr)}}; + } Value operator()(const Literal& expr) { switch (expr) { case Literal::kNull: return Value{true}; case Literal::kTrue: { - return Value{false, true}; + return MakeBooleanValue(true); } case Literal::kFalse: { - return Value{false, false}; + return MakeBooleanValue(false); } case Literal::kUnknown: { return Value{true}; @@ -291,6 +467,58 @@ Tuple ConcatTuples(const Tuple& lhs, const Tuple& rhs) { return joined_tuple; } +bool ContainsStringExpression(const Expression& expr, const AttributesInfo& attrs) { + struct Visitor { + bool operator()(const BinaryExpression& expr) const { + return std::visit(*this, *expr.lhs) || std::visit(*this, *expr.rhs); + } + bool operator()(const UnaryExpression& expr) const { + return std::visit(*this, *expr.child); + } + bool operator()(const InExpression& expr) const { + return std::visit(*this, *expr.lhs) + || std::ranges::any_of(expr.values, [&](const Expression& value) { + return std::visit(*this, value); + }); + } + bool operator()(const AggregateExpression& expr) const { + return !expr.is_star && expr.argument && std::visit(*this, *expr.argument); + } + bool operator()(const Attribute& attr) const { + auto it = std::find_if(attrs.begin(), attrs.end(), [&](const AttributeInfo& attr_info) { + return attr_info.name == attr.name && attr_info.table == attr.table; + }); + return it != attrs.end() && it->type == Type::kString; + } + bool operator()(const StringConst&) const { return true; } + bool operator()(const IntConst&) const { return false; } + bool operator()(const Literal&) const { return false; } + + const AttributesInfo& attrs; + }; + return std::visit(Visitor{attrs}, expr); +} + +bool ContainsInExpression(const Expression& expr) { + struct Visitor { + bool operator()(const BinaryExpression& expr) const { + return std::visit(*this, *expr.lhs) || std::visit(*this, *expr.rhs); + } + bool operator()(const UnaryExpression& expr) const { + return std::visit(*this, *expr.child); + } + bool operator()(const InExpression&) const { return true; } + bool operator()(const AggregateExpression& expr) const { + return !expr.is_star && expr.argument && std::visit(*this, *expr.argument); + } + bool operator()(const Attribute&) const { return false; } + bool operator()(const StringConst&) const { return false; } + bool operator()(const IntConst&) const { return false; } + bool operator()(const Literal&) const { return false; } + }; + return std::visit(Visitor{}, expr); +} + boost::asio::awaitable InterpretedExpressionExecutor::GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs) { struct Executor { Value operator()(const Tuple& source, const AttributesInfo& source_attrs) { @@ -303,6 +531,12 @@ boost::asio::awaitable InterpretedExpressionExecutor::GetExpress } boost::asio::awaitable JitCompiledExpressionExecutor::GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs) { + if (ContainsInExpression(expr)) { + throw std::logic_error{"IN expressions are not supported by JIT"}; + } + if (ContainsStringExpression(expr, attrs)) { + throw std::logic_error{"string expressions are not supported by JIT"}; + } struct Executor { Value operator()(const Tuple& source, const AttributesInfo& source_attrs) { Value result; @@ -322,6 +556,12 @@ JitCompiledExpressionExecutor::JitCompiledExpressionExecutor(boost::asio::any_io InterpretedExpressionExecutor::InterpretedExpressionExecutor(boost::asio::any_io_executor executor) {} boost::asio::awaitable CachedJitCompiledExpressionExecutor::GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs) { + if (ContainsInExpression(expr)) { + throw std::logic_error{"IN expressions are not supported by JIT"}; + } + if (ContainsStringExpression(expr, attrs)) { + throw std::logic_error{"string expressions are not supported by JIT"}; + } auto expr_str = ToString(expr); if (auto it = cache_.find(expr_str); it != cache_.end()) { co_return it->second; @@ -345,4 +585,1126 @@ boost::asio::awaitable CachedJitCompiledExpressionExecutor::GetE CachedJitCompiledExpressionExecutor::CachedJitCompiledExpressionExecutor(boost::asio::any_io_executor executor) : compiler_(executor) {} +template +Executor::Executor(SequentialScan seq_scan, boost::asio::any_io_executor executor) + : sequential_scan_(std::move(seq_scan)), expression_executor_(executor) {} + +template +Executor::Executor(SequentialScan seq_scan, IndexScan index_scan, boost::asio::any_io_executor executor) + : sequential_scan_(std::move(seq_scan)), index_scan_(std::move(index_scan)), expression_executor_(executor) {} + +template +boost::asio::awaitable> Executor::Execute(const PhysicalPlanNode& op) { + auto exec = co_await boost::asio::this_coro::executor; + auto [attr_chan, tuples_chan] = co_await GetChannels(); + auto task = SpawnExecutor(exec, op, attr_chan, tuples_chan); + + + + + std::exception_ptr eptr; + AttributesInfo attrs; + Tuples result; + try { + attrs = co_await attr_chan.async_receive(boost::asio::use_awaitable); + Log("Received attrs in root"); + for (;;) { + auto buf = co_await ReceiveTuples(tuples_chan); + if (buf.empty()) { + break; + } + Log("Received {} tuples in root", buf.size()); + std::move(buf.begin(), buf.end(), std::back_inserter(result)); + } + } catch (...) { + eptr = std::current_exception(); + } + try { + co_await task(boost::asio::use_awaitable); + } catch (...) { + eptr = std::current_exception(); + } + if (eptr) { + std::rethrow_exception(eptr); + } + Log("Total {} tuples in root", result.size()); + co_return Ok(Relation{std::move(attrs), std::move(result)}); +} + +template +boost::asio::awaitable Executor::Execute(const PhysicalPlanNode& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { + auto close_on_fail = boost::scope::make_scope_fail([&] { + attr_chan.close(); + tuples_chan.close(); + }); + struct ExecuteVisitor{ + boost::asio::awaitable operator()(const SeqScan& seq_scan) const { + return executor.ExecuteSeqScan(seq_scan, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const PhysicalProjection& projection) const { + // NOTE: We are using multiset relational algebra projection (i.e. not + return executor.ExecuteProjection(projection, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const PhysicalFilter& filter) const { + return executor.ExecuteFilter(filter, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const NestedLoopJoin& join) const { + return executor.ExecuteJoin(join, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const NestedLoopCrossJoin& cross_join) const { + return executor.ExecuteCrossJoin(cross_join, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const HashJoin& join) const { + return executor.ExecuteHashJoin(join, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const PhysicalAggregation& agg) const { + return executor.ExecuteHashAggregate(agg, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const PhysicalStreamAggregation& agg) const { + return executor.ExecuteStreamAggregate(agg, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const PhysicalPartialAggregation& agg) const { + return executor.ExecuteHashAggregate( + PhysicalAggregation{agg.source, agg.group_by, agg.aggregates}, + attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const PhysicalFinalAggregation& agg) const { + return executor.ExecuteHashAggregate( + PhysicalAggregation{agg.source, agg.group_by, agg.aggregates}, + attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const MergeJoin& join) const { + return executor.ExecuteMergeJoin(join, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const IndexSeek& seek) const { + return executor.ExecuteIndexSeek(seek, attr_chan, tuples_chan); + } + boost::asio::awaitable operator()(const PhysicalSort& sort) const { + return executor.ExecuteSort(sort, attr_chan, tuples_chan); + } + + AttributesInfoChannel& attr_chan; + TuplesChannel& tuples_chan; + Executor& executor; + }; + co_await std::visit(ExecuteVisitor{attr_chan, tuples_chan, *this}, op.node); + co_return; +} + +template +boost::asio::awaitable Executor::ExecuteSeqScan( + const SeqScan& seq_scan, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { + co_await sequential_scan_(seq_scan.table, std::string{OutputTable(seq_scan)}, attr_chan, tuples_chan); +} + +template +boost::asio::awaitable Executor::ExecuteIndexSeek( + const IndexSeek& seek, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { + // FIXME: make index_scan_ required! + if (!index_scan_) { + throw std::runtime_error("IndexSeek execution requested, but no index scanner is configured"); + } + co_await index_scan_(seek.table, seek.alias ? *seek.alias : seek.table, seek.predicate, + seek.index_column, attr_chan, tuples_chan); +} + +template +boost::asio::awaitable Executor::ExecuteSort( + const PhysicalSort& sort, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { + // FIXME: that's in-memory sort + auto close_on_fail = boost::scope::make_scope_fail( + [&] { attr_chan.close(); tuples_chan.close(); }); + auto exec = co_await boost::asio::this_coro::executor; + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + auto task = SpawnExecutor(exec, *sort.source, in_attrs_chan, in_tuples_chan); + + auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + attr_chan.close(); + + Tuples all_tuples; + for (;;) { + auto buf = co_await ReceiveTuples(in_tuples_chan); + if (buf.empty()) break; + std::move(buf.begin(), buf.end(), std::back_inserter(all_tuples)); + } + + std::vector> key_indices; + for (const auto& key : sort.keys.keys) { + auto it = std::find_if(attrs.begin(), attrs.end(), + [&](const AttributeInfo& a) { + return a.name == key.column && (key.table.empty() || a.table == key.table); + }); + if (it == attrs.end()) + throw std::runtime_error{"sort key column not found: " + key.table + "." + key.column}; + key_indices.push_back({static_cast(it - attrs.begin()), key.dir}); + } + + std::sort(all_tuples.begin(), all_tuples.end(), + [&](const Tuple& a, const Tuple& b) { + for (const auto& [idx, dir] : key_indices) { + const auto& attr = attrs[idx]; + const auto& va = a[idx]; + const auto& vb = b[idx]; + if (va.is_null && vb.is_null) continue; + if (va.is_null) return false; + if (vb.is_null) return true; + bool less = false; + bool greater = false; + switch (attr.type) { + case Type::kInt: + less = va.value.int_value < vb.value.int_value; + greater = va.value.int_value > vb.value.int_value; + break; + case Type::kBool: + less = va.value.bool_value < vb.value.bool_value; + greater = va.value.bool_value > vb.value.bool_value; + break; + case Type::kString: { + const auto& sa = GetInternedString(va.value.string_id); + const auto& sb = GetInternedString(vb.value.string_id); + less = sa < sb; + greater = sa > sb; + break; + } + } + if (less || greater) return dir == Direction::kAsc ? less : greater; + } + return false; + }); + + if (!all_tuples.empty()) + co_await tuples_chan.async_send(boost::system::error_code{}, + std::move(all_tuples), boost::asio::use_awaitable); + co_await task(boost::asio::use_awaitable); + tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteProjection(const PhysicalProjection& proj, + AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) { + Log("Executing projection"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { out_attr_chan.close(); out_tuples_chan.close(); }); + auto exec = co_await boost::asio::this_coro::executor; + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + auto task = SpawnExecutor(exec, *proj.source, in_attrs_chan, in_tuples_chan); + + auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + auto attrs_after = GetAttributesAfterProjection(attrs, proj); + co_await out_attr_chan.async_send(boost::system::error_code{}, attrs_after, + boost::asio::use_awaitable); + out_attr_chan.close(); + + std::vector executors; + executors.reserve(proj.expressions.size()); + for (const auto& expr : proj.expressions) { + executors.push_back(co_await expression_executor_.GetExpressionExecutor(expr, attrs)); + } + + for (;;) { + auto buf = co_await ReceiveTuples(in_tuples_chan); + if (buf.empty()) { + break; + } + Log("Received {} tuples in projection", buf.size()); + buf = buf | std::views::transform([&](const auto& tuple) { + return ApplyProjection(tuple, attrs, executors); + }) | std::ranges::to(); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + } + co_await task(boost::asio::use_awaitable); + out_tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteFilter(const PhysicalFilter& filter, + AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) { + Log("Executing filter"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { out_attr_chan.close(); out_tuples_chan.close(); }); + auto exec = co_await boost::asio::this_coro::executor; + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + auto task = SpawnExecutor(exec, *filter.source, in_attrs_chan, in_tuples_chan); + + auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + Log("Filter received attrs"); + + if (GetExpressionType(filter.predicate, attrs) != Type::kBool) { + throw std::logic_error{"filter expr should return bool"}; + } + + Log("Filter sending attrs"); + co_await out_attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + out_attr_chan.close(); + Log("Filter sent attrs"); + + auto filter_executor = co_await expression_executor_.GetExpressionExecutor(filter.predicate, attrs); + + Tuples output_buf; + output_buf.reserve(kBufSize); + for (;;) { + auto input_buf = co_await ReceiveTuples(in_tuples_chan); + if (input_buf.empty()) { + break; + } + Log("Received {} tuples in filter", input_buf.size()); + auto filtered_view = input_buf | std::views::filter([&](const auto& tuple) { + return ApplyFilter(tuple, attrs, filter_executor); + }) | std::views::as_rvalue; + for (auto&& tuple : filtered_view) { + output_buf.push_back(std::move(tuple)); + if (output_buf.size() == kBufSize) { + Log("Sending {} tuples in filter", output_buf.size()); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), + boost::asio::use_awaitable); + output_buf.clear(); + } + } + } + Log("{} tuples left in output_buf", output_buf.size()); + if (!output_buf.empty()) { + Log("Sending {} tuples in filter", output_buf.size()); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), + boost::asio::use_awaitable); + } + co_await task(boost::asio::use_awaitable); + out_tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteCrossJoin(const NestedLoopCrossJoin& cross_join, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) { + Log("Executing cross join"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { attr_chan.close(); tuples_chan.close(); }); + auto exec = co_await boost::asio::this_coro::executor; + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + auto lhs_task = SpawnExecutor(exec, *cross_join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + auto rhs_task = SpawnExecutor(exec, *cross_join.rhs, rhs_attrs_chan, rhs_tuples_chan); + + auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); + Log("Cross join received attrs"); + Log("Cross join sending attrs"); + co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + attr_chan.close(); + Log("Cross join sent attrs"); + + auto reader = co_await MaterializeChannel(lhs_tuples_chan); + Log("Materialized tuples in cross join"); + + for (;;) { + auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); + if (buf_rhs.empty()) { + break; + } + Log("Received {} tuples in cross join as rhs", buf_rhs.size()); + + reader.Rewind(); + for (;;) { + auto buf_lhs = reader.Read(); + if (buf_lhs.empty()) { + break; + } + Log("Read {} tuples back from materialized form", buf_lhs.size()); + for (const auto& tuple_lhs : buf_lhs) { + Tuples buf_joined; + buf_joined.reserve(kBufSize); + // NOTE: not optimal if rhs is a small table (<< kBufSize tuples) + for (const auto& tuple_rhs : buf_rhs) { + auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); + buf_joined.push_back(std::move(joined_tuple)); + } + Log("Sending {} tuples from cross join", buf_joined.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_joined), + boost::asio::use_awaitable); + } + } + } + co_await lhs_task(boost::asio::use_awaitable); + co_await rhs_task(boost::asio::use_awaitable); + tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteJoin(const NestedLoopJoin& join, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) { + Log("Executing join"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { attr_chan.close(); tuples_chan.close(); }); + if (join.type == JoinType::kLeft) { + std::swap(*join.lhs, *join.rhs); + } + auto exec = co_await boost::asio::this_coro::executor; + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + auto lhs_task = SpawnExecutor(exec, *join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + auto rhs_task = SpawnExecutor(exec, *join.rhs, rhs_attrs_chan, rhs_tuples_chan); + + auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); + Log("Join received attrs"); + Log("Join sending attrs"); + co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + attr_chan.close(); + Log("Join sent attrs"); + + auto qual_executor = co_await expression_executor_.GetExpressionExecutor(join.qual, attrs); + + if (join.type == JoinType::kFull) { + std::vector lhs_all; + { + auto full_reader = co_await MaterializeChannel(lhs_tuples_chan); + full_reader.Rewind(); + for (;;) { + auto buf = full_reader.Read(); + if (buf.empty()) break; + std::move(buf.begin(), buf.end(), std::back_inserter(lhs_all)); + } + } + std::vector lhs_matched(lhs_all.size(), false); + + for (;;) { + auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); + if (buf_rhs.empty()) break; + Log("Received {} tuples in full join as rhs", buf_rhs.size()); + + for (const auto& tuple_rhs : buf_rhs) { + bool rhs_matched = false; + Tuples buf_res; + buf_res.reserve(kBufSize); + for (size_t i = 0; i < lhs_all.size(); ++i) { + auto joined = ConcatTuples(lhs_all[i], tuple_rhs); + auto qual_result = qual_executor(joined, attrs); + if (!qual_result.is_null && qual_result.value.bool_value) { + buf_res.push_back(std::move(joined)); + lhs_matched[i] = true; + rhs_matched = true; + if (buf_res.size() == kBufSize) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + buf_res.clear(); + buf_res.reserve(kBufSize); + } + } + } + if (!rhs_matched) { + auto rhs_size = tuple_rhs.size(); + auto lhs_size = attrs.size() - rhs_size; + buf_res.push_back(ConcatTuples(Tuple(lhs_size, Value{true}), tuple_rhs)); + } + if (!buf_res.empty()) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + } + } + } + + Tuples buf_unmatched; + buf_unmatched.reserve(kBufSize); + for (size_t i = 0; i < lhs_all.size(); ++i) { + if (!lhs_matched[i]) { + auto lhs_size = lhs_all[i].size(); + auto rhs_size = attrs.size() - lhs_size; + buf_unmatched.push_back(ConcatTuples(lhs_all[i], Tuple(rhs_size, Value{true}))); + if (buf_unmatched.size() == kBufSize) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_unmatched), + boost::asio::use_awaitable); + buf_unmatched.clear(); + buf_unmatched.reserve(kBufSize); + } + } + } + if (!buf_unmatched.empty()) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_unmatched), + boost::asio::use_awaitable); + } + + co_await lhs_task(boost::asio::use_awaitable); + co_await rhs_task(boost::asio::use_awaitable); + tuples_chan.close(); + co_return; + } + + auto reader = co_await MaterializeChannel(lhs_tuples_chan); + for (;;) { + auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); + if (buf_rhs.empty()) { + break; + } + Log("Received {} tuples in join as rhs", buf_rhs.size()); + + std::vector used(buf_rhs.size(), false); + reader.Rewind(); + for (;;) { + auto buf_lhs = reader.Read(); + if (buf_lhs.empty()) { + break; + } + Log("Read {} tuples back from materialized form", buf_lhs.size()); + + for (const auto& tuple_lhs : buf_lhs) { + Tuples buf_res; + buf_res.reserve(kBufSize); + for (const auto& [rhs_index, tuple_rhs] : buf_rhs | std::views::enumerate) { + auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); + auto qual_expr_res = qual_executor(joined_tuple, attrs); + if (qual_expr_res.value.bool_value) { + buf_res.push_back(std::move(joined_tuple)); + used[rhs_index] = true; + } + } + if (!buf_res.empty()) { + Log("Sending {} tuples from join", buf_res.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + } + } + } + + if (join.type == JoinType::kRight || join.type == JoinType::kLeft) { + Tuples buf_res; + buf_res.reserve(kBufSize); + for (auto [rhs_index, is_used] : used | std::views::enumerate) { + if (is_used) { + continue; + } + + auto rhs_tuple = std::move(buf_rhs[rhs_index]); + + auto lhs_size = attrs.size() - rhs_tuple.size(); + Tuple lhs_tuple(lhs_size, Value{true}); + + auto joined_tuple = ConcatTuples(lhs_tuple, rhs_tuple); + buf_res.push_back(std::move(joined_tuple)); + } + if (!buf_res.empty()) { + Log("Sending {} tuples from join", buf_res.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + } + } + } + co_await lhs_task(boost::asio::use_awaitable); + co_await rhs_task(boost::asio::use_awaitable); + tuples_chan.close(); +} + +namespace { + +bool AttrMatches(const AttributeInfo& ai, const Attribute& a) { + return ai.name == a.name && (a.table.empty() || ai.table == a.table); +} + +bool HasAttr(const AttributesInfo& attrs, const Attribute& a) { + return std::ranges::any_of(attrs, [&](const AttributeInfo& ai) { + return AttrMatches(ai, a); + }); +} + +size_t FindAttrIndexFlexible(const AttributesInfo& attrs, const Attribute& a) { + auto it = std::find_if(attrs.begin(), attrs.end(), [&](const AttributeInfo& ai) { + return AttrMatches(ai, a); + }); + if (it == attrs.end()) { + throw std::runtime_error{"merge join key attribute not found: " + a.table + "." + a.name}; + } + return static_cast(it - attrs.begin()); +} + +struct ExecutorJoinKeys { + Attribute lhs; + Attribute rhs; +}; + +ExecutorJoinKeys ResolveExecutorJoinKeys(const Expression& qual, + const AttributesInfo& lhs_attrs, + const AttributesInfo& rhs_attrs) { + const auto* bin = std::get_if(&qual); + if (!bin || bin->binop != BinaryOp::kEq) { + throw std::logic_error{"MergeJoin qual must be an equality"}; + } + const auto* a = std::get_if(bin->lhs.get()); + const auto* b = std::get_if(bin->rhs.get()); + if (!a || !b) { + throw std::logic_error{"MergeJoin qual must be `attr = attr`"}; + } + + const bool a_lhs = HasAttr(lhs_attrs, *a); + const bool a_rhs = HasAttr(rhs_attrs, *a); + const bool b_lhs = HasAttr(lhs_attrs, *b); + const bool b_rhs = HasAttr(rhs_attrs, *b); + if (a_lhs && !a_rhs && b_rhs && !b_lhs) { + return {*a, *b}; + } + if (b_lhs && !b_rhs && a_rhs && !a_lhs) { + return {*b, *a}; + } + throw std::runtime_error{"MergeJoin qual cannot be mapped unambiguously to inputs"}; +} + +boost::asio::awaitable CollectAllTuples(TuplesChannel& tuples_chan) { + Tuples result; + for (;;) { + auto buf = co_await ReceiveTuples(tuples_chan); + if (buf.empty()) break; + std::move(buf.begin(), buf.end(), std::back_inserter(result)); + } + co_return result; +} + +int CompareNonNullValues(const Value& lhs, const Value& rhs, Type type) { + switch (type) { + case Type::kInt: + return (lhs.value.int_value > rhs.value.int_value) + - (lhs.value.int_value < rhs.value.int_value); + case Type::kBool: + return (lhs.value.bool_value > rhs.value.bool_value) + - (lhs.value.bool_value < rhs.value.bool_value); + case Type::kString: { + const auto& l = GetInternedString(lhs.value.string_id); + const auto& r = GetInternedString(rhs.value.string_id); + return (l > r) - (l < r); + } + } + std::unreachable(); +} + +} // namespace + + + + + + +template +boost::asio::awaitable Executor::ExecuteHashJoin( + const HashJoin& join, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { + Log("Executing hash join"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { attr_chan.close(); tuples_chan.close(); }); + if (join.type != JoinType::kInner) { + throw std::logic_error{"HashJoin executor supports only Inner joins"}; + } + auto exec = co_await boost::asio::this_coro::executor; + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + auto lhs_task = SpawnExecutor(exec, *join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + auto rhs_task = SpawnExecutor(exec, *join.rhs, rhs_attrs_chan, rhs_tuples_chan); + + auto lhs_attrs = co_await lhs_attrs_chan.async_receive(boost::asio::use_awaitable); + auto rhs_attrs = co_await rhs_attrs_chan.async_receive(boost::asio::use_awaitable); + auto keys = ResolveExecutorJoinKeys(join.qual, lhs_attrs, rhs_attrs); + size_t lhs_key_idx = FindAttrIndexFlexible(lhs_attrs, keys.lhs); + size_t rhs_key_idx = FindAttrIndexFlexible(rhs_attrs, keys.rhs); + + AttributesInfo out_attrs = lhs_attrs; + std::ranges::copy(rhs_attrs, std::back_inserter(out_attrs)); + if (GetExpressionType(join.qual, out_attrs) != Type::kBool) { + throw std::logic_error{"hash join qual should return bool"}; + } + auto qual_executor = co_await expression_executor_.GetExpressionExecutor(join.qual, out_attrs); + + co_await attr_chan.async_send(boost::system::error_code{}, out_attrs, + boost::asio::use_awaitable); + attr_chan.close(); + + + + std::unordered_multimap build; + for (;;) { + auto buf = co_await ReceiveTuples(lhs_tuples_chan); + if (buf.empty()) break; + Log("HashJoin build received {} tuples", buf.size()); + for (auto& t : buf) { + const Value& k = t[lhs_key_idx]; + if (k.is_null) continue; + build.emplace(k.value.int_value, std::move(t)); + } + } + Log("HashJoin build phase done; {} entries", build.size()); + + + Tuples out_buf; + out_buf.reserve(kBufSize); + for (;;) { + auto buf = co_await ReceiveTuples(rhs_tuples_chan); + if (buf.empty()) break; + Log("HashJoin probe received {} tuples", buf.size()); + for (const auto& rt : buf) { + const Value& k = rt[rhs_key_idx]; + if (k.is_null) continue; + auto range = build.equal_range(k.value.int_value); + for (auto it = range.first; it != range.second; ++it) { + auto joined_tuple = ConcatTuples(it->second, rt); + if (!ApplyFilter(joined_tuple, out_attrs, qual_executor)) { + continue; + } + out_buf.push_back(std::move(joined_tuple)); + if (out_buf.size() == kBufSize) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + out_buf.clear(); + out_buf.reserve(kBufSize); + } + } + } + } + if (!out_buf.empty()) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + } + co_await lhs_task(boost::asio::use_awaitable); + co_await rhs_task(boost::asio::use_awaitable); + tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteMergeJoin( + const MergeJoin& join, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { + Log("Executing merge join"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { attr_chan.close(); tuples_chan.close(); }); + + auto exec = co_await boost::asio::this_coro::executor; + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + auto lhs_task = SpawnExecutor(exec, *join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + auto rhs_task = SpawnExecutor(exec, *join.rhs, rhs_attrs_chan, rhs_tuples_chan); + + auto lhs_attrs = co_await lhs_attrs_chan.async_receive(boost::asio::use_awaitable); + auto rhs_attrs = co_await rhs_attrs_chan.async_receive(boost::asio::use_awaitable); + auto keys = ResolveExecutorJoinKeys(join.qual, lhs_attrs, rhs_attrs); + const size_t lhs_key_idx = FindAttrIndexFlexible(lhs_attrs, keys.lhs); + const size_t rhs_key_idx = FindAttrIndexFlexible(rhs_attrs, keys.rhs); + if (lhs_attrs[lhs_key_idx].type != rhs_attrs[rhs_key_idx].type) { + throw std::logic_error{"MergeJoin key types mismatch"}; + } + const Type key_type = lhs_attrs[lhs_key_idx].type; + + AttributesInfo out_attrs = lhs_attrs; + std::ranges::copy(rhs_attrs, std::back_inserter(out_attrs)); + co_await attr_chan.async_send(boost::system::error_code{}, out_attrs, + boost::asio::use_awaitable); + attr_chan.close(); + + auto lhs = co_await CollectAllTuples(lhs_tuples_chan); + auto rhs = co_await CollectAllTuples(rhs_tuples_chan); + + Tuples out_buf; + out_buf.reserve(kBufSize); + auto emit = [&](Tuple tuple) -> boost::asio::awaitable { + out_buf.push_back(std::move(tuple)); + if (out_buf.size() == kBufSize) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + out_buf.clear(); + out_buf.reserve(kBufSize); + } + }; + auto flush = [&]() -> boost::asio::awaitable { + if (!out_buf.empty()) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + out_buf.clear(); + out_buf.reserve(kBufSize); + } + }; + + auto same_lhs_key = [&](size_t a, size_t b) { + const auto& av = lhs[a][lhs_key_idx]; + const auto& bv = lhs[b][lhs_key_idx]; + return !av.is_null && !bv.is_null && CompareNonNullValues(av, bv, key_type) == 0; + }; + auto same_rhs_key = [&](size_t a, size_t b) { + const auto& av = rhs[a][rhs_key_idx]; + const auto& bv = rhs[b][rhs_key_idx]; + return !av.is_null && !bv.is_null && CompareNonNullValues(av, bv, key_type) == 0; + }; + + if (join.type == JoinType::kRight) { + size_t i = 0; + size_t j = 0; + while (j < rhs.size()) { + const auto& rhs_key = rhs[j][rhs_key_idx]; + size_t rhs_end = j + 1; + while (rhs_end < rhs.size() && same_rhs_key(j, rhs_end)) ++rhs_end; + + if (rhs_key.is_null) { + for (size_t r = j; r < rhs_end; ++r) { + co_await emit(ConcatTuples(Tuple(lhs_attrs.size(), Value{true}), rhs[r])); + } + j = rhs_end; + continue; + } + + while (i < lhs.size()) { + const auto& lhs_key = lhs[i][lhs_key_idx]; + if (lhs_key.is_null || CompareNonNullValues(lhs_key, rhs_key, key_type) >= 0) break; + ++i; + } + + if (i < lhs.size() && !lhs[i][lhs_key_idx].is_null + && CompareNonNullValues(lhs[i][lhs_key_idx], rhs_key, key_type) == 0) { + size_t lhs_end = i + 1; + while (lhs_end < lhs.size() && same_lhs_key(i, lhs_end)) ++lhs_end; + for (size_t l = i; l < lhs_end; ++l) { + for (size_t r = j; r < rhs_end; ++r) { + co_await emit(ConcatTuples(lhs[l], rhs[r])); + } + } + i = lhs_end; + } else { + for (size_t r = j; r < rhs_end; ++r) { + co_await emit(ConcatTuples(Tuple(lhs_attrs.size(), Value{true}), rhs[r])); + } + } + j = rhs_end; + } + } else { + std::vector rhs_used(rhs.size(), false); + size_t i = 0; + size_t j = 0; + while (i < lhs.size()) { + const auto& lhs_key = lhs[i][lhs_key_idx]; + size_t lhs_end = i + 1; + while (lhs_end < lhs.size() && same_lhs_key(i, lhs_end)) ++lhs_end; + + if (lhs_key.is_null) { + if (join.type == JoinType::kLeft || join.type == JoinType::kFull) { + for (size_t l = i; l < lhs_end; ++l) { + co_await emit(ConcatTuples(lhs[l], Tuple(rhs_attrs.size(), Value{true}))); + } + } + i = lhs_end; + continue; + } + + while (j < rhs.size()) { + const auto& rhs_key = rhs[j][rhs_key_idx]; + if (rhs_key.is_null || CompareNonNullValues(rhs_key, lhs_key, key_type) >= 0) break; + ++j; + } + + if (j < rhs.size() && !rhs[j][rhs_key_idx].is_null + && CompareNonNullValues(rhs[j][rhs_key_idx], lhs_key, key_type) == 0) { + size_t rhs_end = j + 1; + while (rhs_end < rhs.size() && same_rhs_key(j, rhs_end)) ++rhs_end; + for (size_t l = i; l < lhs_end; ++l) { + for (size_t r = j; r < rhs_end; ++r) { + rhs_used[r] = true; + co_await emit(ConcatTuples(lhs[l], rhs[r])); + } + } + j = rhs_end; + } else if (join.type == JoinType::kLeft || join.type == JoinType::kFull) { + for (size_t l = i; l < lhs_end; ++l) { + co_await emit(ConcatTuples(lhs[l], Tuple(rhs_attrs.size(), Value{true}))); + } + } + i = lhs_end; + } + + if (join.type == JoinType::kFull) { + for (size_t r = 0; r < rhs.size(); ++r) { + if (!rhs_used[r]) { + co_await emit(ConcatTuples(Tuple(lhs_attrs.size(), Value{true}), rhs[r])); + } + } + } + } + + co_await flush(); + co_await lhs_task(boost::asio::use_awaitable); + co_await rhs_task(boost::asio::use_awaitable); + tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteHashAggregate( + PhysicalAggregation agg, AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) { + Log("Executing hash aggregate"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { out_attr_chan.close(); out_tuples_chan.close(); }); + auto exec = co_await boost::asio::this_coro::executor; + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + auto task = SpawnExecutor(exec, *agg.source, in_attrs_chan, in_tuples_chan); + + auto in_attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + + + AttributesInfo out_attrs; + for (const auto& expr : agg.group_by) { + if (const auto* attr = std::get_if(&expr)) { + auto it = std::find_if(in_attrs.begin(), in_attrs.end(), [&](const AttributeInfo& ai) { + return ai.table == attr->table && ai.name == attr->name; + }); + if (it != in_attrs.end()) { + out_attrs.push_back(*it); + } + } + } + for (size_t i = 0; i < agg.aggregates.size(); ++i) { + const auto& agg_expr = std::get(agg.aggregates[i]); + Type t = (agg_expr.function == AggregateFunction::kCount) + ? Type::kInt + : (agg_expr.is_star ? Type::kInt : GetExpressionTypeUnchecked(*agg_expr.argument, in_attrs)); + out_attrs.push_back(AttributeInfo{"", std::format("__agg{}", i), t}); + } + co_await out_attr_chan.async_send(boost::system::error_code{}, out_attrs, + boost::asio::use_awaitable); + out_attr_chan.close(); + + + + + struct GroupState { + std::vector accumulators; + std::vector any_non_null; + }; + struct TupleKeyHash { + size_t operator()(const std::vector& key) const { + size_t seed = 0; + for (const auto& v : key) { + boost::hash_combine(seed, v.is_null); + if (!v.is_null) boost::hash_combine(seed, v.value.int_value); + } + return seed; + } + }; + std::unordered_map, GroupState, TupleKeyHash> groups; + + + auto do_scalar = [&](const Expression& expr, const Tuple& tuple) -> Value { + return CalcExpression(tuple, in_attrs, expr); + }; + + const bool scalar_agg = agg.group_by.empty(); + + auto init_state = [&]() -> GroupState { + GroupState s; + s.accumulators.resize(agg.aggregates.size(), 0); + s.any_non_null.resize(agg.aggregates.size(), false); + return s; + }; + + for (;;) { + auto buf = co_await ReceiveTuples(in_tuples_chan); + if (buf.empty()) break; + for (const auto& tuple : buf) { + std::vector key; + for (const auto& expr : agg.group_by) { + key.push_back(do_scalar(expr, tuple)); + } + auto [it, inserted] = groups.emplace(key, GroupState{}); + if (inserted) it->second = init_state(); + auto& state = it->second; + for (size_t i = 0; i < agg.aggregates.size(); ++i) { + const auto& agg_expr = std::get(agg.aggregates[i]); + if (agg_expr.function == AggregateFunction::kCount) { + if (agg_expr.is_star) { + state.accumulators[i]++; + } else { + auto v = do_scalar(*agg_expr.argument, tuple); + if (!v.is_null) state.accumulators[i]++; + } + } else { + auto v = do_scalar(*agg_expr.argument, tuple); + if (!v.is_null) { + state.accumulators[i] += v.value.int_value; + state.any_non_null[i] = true; + } + } + } + } + } + + + if (scalar_agg && groups.empty()) { + groups.emplace(std::vector{}, init_state()); + } + + Tuples out_buf; + out_buf.reserve(kBufSize); + for (auto& [key, state] : groups) { + Tuple tuple = key; + for (size_t i = 0; i < agg.aggregates.size(); ++i) { + const auto& agg_expr = std::get(agg.aggregates[i]); + if (agg_expr.function == AggregateFunction::kSum && !state.any_non_null[i]) { + tuple.push_back(Value{true}); + } else { + tuple.push_back(Value{false, {.int_value = state.accumulators[i]}}); + } + } + out_buf.push_back(std::move(tuple)); + if (out_buf.size() == kBufSize) { + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + out_buf.clear(); + out_buf.reserve(kBufSize); + } + } + if (!out_buf.empty()) { + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + } + co_await task(boost::asio::use_awaitable); + out_tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteStreamAggregate( + const PhysicalStreamAggregation& agg, AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) { + Log("Executing stream aggregate"); + auto close_on_fail = boost::scope::make_scope_fail( + [&] { out_attr_chan.close(); out_tuples_chan.close(); }); + auto exec = co_await boost::asio::this_coro::executor; + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + auto task = SpawnExecutor(exec, *agg.source, in_attrs_chan, in_tuples_chan); + + auto in_attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + + AttributesInfo out_attrs; + for (const auto& expr : agg.group_by) { + if (const auto* attr = std::get_if(&expr)) { + auto it = std::find_if(in_attrs.begin(), in_attrs.end(), [&](const AttributeInfo& ai) { + return ai.table == attr->table && ai.name == attr->name; + }); + if (it != in_attrs.end()) { + out_attrs.push_back(*it); + } + } + } + for (size_t i = 0; i < agg.aggregates.size(); ++i) { + const auto& agg_expr = std::get(agg.aggregates[i]); + Type t = (agg_expr.function == AggregateFunction::kCount) + ? Type::kInt + : (agg_expr.is_star ? Type::kInt : GetExpressionTypeUnchecked(*agg_expr.argument, in_attrs)); + out_attrs.push_back(AttributeInfo{"", std::format("__agg{}", i), t}); + } + co_await out_attr_chan.async_send(boost::system::error_code{}, out_attrs, + boost::asio::use_awaitable); + out_attr_chan.close(); + + struct GroupState { + std::vector accumulators; + std::vector any_non_null; + }; + + auto do_scalar = [&](const Expression& expr, const Tuple& tuple) -> Value { + return CalcExpression(tuple, in_attrs, expr); + }; + auto init_state = [&]() -> GroupState { + GroupState s; + s.accumulators.resize(agg.aggregates.size(), 0); + s.any_non_null.resize(agg.aggregates.size(), false); + return s; + }; + auto update_state = [&](GroupState& state, const Tuple& tuple) { + for (size_t i = 0; i < agg.aggregates.size(); ++i) { + const auto& agg_expr = std::get(agg.aggregates[i]); + if (agg_expr.function == AggregateFunction::kCount) { + if (agg_expr.is_star) { + state.accumulators[i]++; + } else { + auto v = do_scalar(*agg_expr.argument, tuple); + if (!v.is_null) state.accumulators[i]++; + } + } else { + auto v = do_scalar(*agg_expr.argument, tuple); + if (!v.is_null) { + state.accumulators[i] += v.value.int_value; + state.any_non_null[i] = true; + } + } + } + }; + + Tuples out_buf; + out_buf.reserve(kBufSize); + auto emit_group = [&](const std::vector& key, const GroupState& state) + -> boost::asio::awaitable { + Tuple tuple = key; + for (size_t i = 0; i < agg.aggregates.size(); ++i) { + const auto& agg_expr = std::get(agg.aggregates[i]); + if (agg_expr.function == AggregateFunction::kSum && !state.any_non_null[i]) { + tuple.push_back(Value{true}); + } else { + tuple.push_back(Value{false, {.int_value = state.accumulators[i]}}); + } + } + out_buf.push_back(std::move(tuple)); + if (out_buf.size() == kBufSize) { + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + out_buf.clear(); + out_buf.reserve(kBufSize); + } + }; + auto flush_output = [&]() -> boost::asio::awaitable { + if (!out_buf.empty()) { + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(out_buf), + boost::asio::use_awaitable); + out_buf.clear(); + out_buf.reserve(kBufSize); + } + }; + + std::vector current_key; + GroupState current_state = init_state(); + bool has_current = false; + + for (;;) { + auto buf = co_await ReceiveTuples(in_tuples_chan); + if (buf.empty()) break; + for (const auto& tuple : buf) { + std::vector key; + key.reserve(agg.group_by.size()); + for (const auto& expr : agg.group_by) { + key.push_back(do_scalar(expr, tuple)); + } + if (!has_current) { + current_key = std::move(key); + current_state = init_state(); + has_current = true; + } else if (key != current_key) { + co_await emit_group(current_key, current_state); + current_key = std::move(key); + current_state = init_state(); + } + update_state(current_state, tuple); + } + } + + if (has_current) { + co_await emit_group(current_key, current_state); + } else if (agg.group_by.empty()) { + co_await emit_group(std::vector{}, init_state()); + } + co_await flush_output(); + co_await task(boost::asio::use_awaitable); + out_tuples_chan.close(); +} + +template +boost::asio::experimental::promise +Executor::SpawnExecutor(boost::asio::any_io_executor exec, + const PhysicalPlanNode& op, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuple_chan) { + return boost::asio::co_spawn(exec, Execute(op, attr_chan, tuple_chan), + boost::asio::experimental::use_promise); +} + +template class Executor; +template class Executor; +template class Executor; + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index 73e182e..2f9abac 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -2,19 +2,73 @@ #include #include +#include #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include +#include using ::testing::Eq; namespace stewkk::sql { +namespace { + +PhysicalPlanNode ToPhysicalPlan(const Operator& op) { + return std::visit(utils::Overloaded{ + [](const Table& t) -> PhysicalPlanNode { + return SeqScan{.table = t.name, .alias = t.alias}; + }, + [](const Projection& p) -> PhysicalPlanNode { + return PhysicalProjection{ + .source = std::make_shared(ToPhysicalPlan(*p.source)), + .expressions = p.expressions, + .aliases = p.aliases, + }; + }, + [](const Filter& f) -> PhysicalPlanNode { + return PhysicalFilter{ + .source = std::make_shared(ToPhysicalPlan(*f.source)), + .predicate = f.expr, + }; + }, + [](const Aggregation& a) -> PhysicalPlanNode { + return PhysicalAggregation{ + .source = std::make_shared(ToPhysicalPlan(*a.source)), + .group_by = a.group_by, + .aggregates = a.aggregates, + }; + }, + [](const CrossJoin& j) -> PhysicalPlanNode { + return NestedLoopCrossJoin{ + .lhs = std::make_shared(ToPhysicalPlan(*j.lhs)), + .rhs = std::make_shared(ToPhysicalPlan(*j.rhs)), + }; + }, + [](const Join& j) -> PhysicalPlanNode { + return NestedLoopJoin{ + .lhs = std::make_shared(ToPhysicalPlan(*j.lhs)), + .rhs = std::make_shared(ToPhysicalPlan(*j.rhs)), + .type = j.type, + .qual = j.qual, + }; + }, + }, op); +} + +} // namespace + const static std::string kProjectDir = std::getenv("PWD"); namespace { @@ -26,6 +80,24 @@ std::string ReadFromFile(std::filesystem::path path) { return stream.str(); } +boost::asio::awaitable> RunIndexSeekTestQuery(std::string dir) { + PhysicalPlanNode op = IndexSeek{ + "users", + std::nullopt, + BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kGe, + std::make_shared(IntConst{8}), + }, + }; + CsvDirSequentialScanner seq_scan{dir}; + CsvDirIndexedScanner index_scan{dir}; + Executor executor(std::move(seq_scan), std::move(index_scan), + co_await boost::asio::this_coro::executor); + + co_return co_await executor.Execute(op); +} + } // namespace template @@ -39,7 +111,7 @@ TEST(ExecutorTest, SimpleSelect) { ctx, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM users;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -65,7 +137,7 @@ TEST(ExecutorTest, SimpleSelectWithParallelism) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM users;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -85,13 +157,427 @@ TEST(ExecutorTest, SimpleSelectWithParallelism) { pool.join(); } +TEST(ExecutorTest, IndexSeekBuildsAndUsesSortedIntIndex) { + auto dir = std::filesystem::temp_directory_path() / "iu9_sql_index_seek_test"; + std::filesystem::remove_all(dir); + std::filesystem::create_directories(dir); + { + std::ofstream csv{dir / "users.csv"}; + csv << "id:int,age:int\n" + << "10,22\n" + << "1,33\n" + << "8,64\n" + << "8,70\n"; + std::ofstream meta{dir / "indexes.meta"}; + meta << "users id sorted users.id.sorted.idx\n"; + } + + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + RunIndexSeekTestQuery(dir.string()), + [dir](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"users", "id", Type::kInt}, {"users", "age", Type::kInt}})); + ASSERT_THAT(got.value().tuples, + Eq(Tuples{ + Tuple{Value{false, 8}, Value{false, 64}}, + Tuple{Value{false, 8}, Value{false, 70}}, + Tuple{Value{false, 10}, Value{false, 22}}, + })); + ASSERT_TRUE(std::filesystem::exists(dir / "users.id.sorted.idx")); + std::filesystem::remove_all(dir); + }); + + ctx.run(); +} + +TEST(ExecutorTest, OptimizedIndexSeekPlanExecutes) { + auto dir = std::filesystem::temp_directory_path() / "iu9_sql_optimized_index_seek_test"; + std::filesystem::remove_all(dir); + std::filesystem::create_directories(dir); + { + std::ofstream csv{dir / "users.csv"}; + csv << "id:int,age:int\n" + << "10,22\n" + << "1,33\n" + << "8,64\n" + << "8,70\n"; + std::ofstream meta{dir / "indexes.meta"}; + meta << "users id sorted users.id.sorted.idx\n"; + } + + std::stringstream sql{"SELECT * FROM users WHERE users.id >= 8 AND users.age < 70;"}; + auto parsed = GetAST(sql).value(); + Optimizer optimizer(parsed.op, MakeMainRules(LoadIndexCatalogFromCsvDir(dir)), + CardinalityEstimates({{"users", 4}}), + LoadSchemaFromCsvDir(dir)); + auto plan = optimizer.Optimize(); + + boost::asio::io_context ctx; + CsvDirSequentialScanner seq_scan{dir.string()}; + CsvDirIndexedScanner index_scan{dir.string()}; + Executor executor(std::move(seq_scan), std::move(index_scan), ctx.get_executor()); + auto fut = boost::asio::co_spawn(ctx, executor.Execute(plan), boost::asio::use_future); + ctx.run(); + auto got = fut.get(); + + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"users", "id", Type::kInt}, {"users", "age", Type::kInt}})); + ASSERT_THAT(got.value().tuples, + Eq(Tuples{ + Tuple{Value{false, 8}, Value{false, 64}}, + Tuple{Value{false, 10}, Value{false, 22}}, + })); + std::filesystem::remove_all(dir); +} + +TEST(ExecutorTest, MergeJoinFullOuterWithDuplicatesAndNulls) { + auto dir = std::filesystem::temp_directory_path() / "iu9_sql_merge_join_test"; + std::filesystem::remove_all(dir); + std::filesystem::create_directories(dir); + { + std::ofstream a{dir / "a.csv"}; + a << "id:int,v:int\n" + << "1,10\n" + << "1,11\n" + << "2,20\n" + << "NULL,99\n"; + std::ofstream b{dir / "b.csv"}; + b << "id:int,w:int\n" + << "1,100\n" + << "1,101\n" + << "3,300\n" + << "NULL,999\n"; + } + + Expression qual = BinaryExpression{ + std::make_shared(Attribute{"a", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"b", "id"}), + }; + PhysicalPlanNode plan = MergeJoin{ + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"a"}), + SortOrder{{SortKey{"a", "id", Direction::kAsc}}}}), + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"b"}), + SortOrder{{SortKey{"b", "id", Direction::kAsc}}}}), + JoinType::kFull, + qual, + }; + + boost::asio::io_context ctx; + CsvDirSequentialScanner seq_scan{dir.string()}; + Executor executor(std::move(seq_scan), ctx.get_executor()); + auto fut = boost::asio::co_spawn(ctx, executor.Execute(plan), boost::asio::use_future); + ctx.run(); + auto got = fut.get(); + + ASSERT_THAT(got.value().tuples, + Eq(Tuples{ + Tuple{Value{false, 1}, Value{false, 10}, Value{false, 1}, Value{false, 100}}, + Tuple{Value{false, 1}, Value{false, 10}, Value{false, 1}, Value{false, 101}}, + Tuple{Value{false, 1}, Value{false, 11}, Value{false, 1}, Value{false, 100}}, + Tuple{Value{false, 1}, Value{false, 11}, Value{false, 1}, Value{false, 101}}, + Tuple{Value{false, 2}, Value{false, 20}, Value{true}, Value{true}}, + Tuple{Value{true}, Value{false, 99}, Value{true}, Value{true}}, + Tuple{Value{true}, Value{true}, Value{false, 3}, Value{false, 300}}, + Tuple{Value{true}, Value{true}, Value{true}, Value{false, 999}}, + })); + std::filesystem::remove_all(dir); +} + +TEST(ExecutorTest, MergeJoinStringKeys) { + auto dir = std::filesystem::temp_directory_path() / "iu9_sql_merge_join_string_test"; + std::filesystem::remove_all(dir); + std::filesystem::create_directories(dir); + { + std::ofstream lhs{dir / "lhs.csv"}; + lhs << "name:string,v:int\n" + << "alpha,1\n" + << "beta,2\n"; + std::ofstream rhs{dir / "rhs.csv"}; + rhs << "name:string,w:int\n" + << "alpha,10\n" + << "gamma,30\n"; + } + + Expression qual = BinaryExpression{ + std::make_shared(Attribute{"lhs", "name"}), + BinaryOp::kEq, + std::make_shared(Attribute{"rhs", "name"}), + }; + PhysicalPlanNode plan = MergeJoin{ + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"lhs"}), + SortOrder{{SortKey{"lhs", "name", Direction::kAsc}}}}), + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"rhs"}), + SortOrder{{SortKey{"rhs", "name", Direction::kAsc}}}}), + JoinType::kInner, + qual, + }; + + boost::asio::io_context ctx; + CsvDirSequentialScanner seq_scan{dir.string()}; + Executor executor(std::move(seq_scan), ctx.get_executor()); + auto fut = boost::asio::co_spawn(ctx, executor.Execute(plan), boost::asio::use_future); + ctx.run(); + auto got = fut.get(); + + ASSERT_THAT(got.value().tuples.size(), Eq(1u)); + ASSERT_THAT(GetInternedString(got.value().tuples[0][0].value.string_id), Eq("alpha")); + ASSERT_THAT(GetInternedString(got.value().tuples[0][2].value.string_id), Eq("alpha")); + ASSERT_THAT(got.value().tuples[0][1], Eq(Value{false, 1})); + ASSERT_THAT(got.value().tuples[0][3], Eq(Value{false, 10})); + std::filesystem::remove_all(dir); +} + +TEST(ExecutorTest, StringFilterProjectionAndCsvQuotes) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT m.region, m.note FROM markets AS m WHERE m.region = 'AMERICA';"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"m", "region", Type::kString}, {"m", "note", Type::kString}})); + ASSERT_THAT(got.value().tuples.size(), Eq(2)); + ASSERT_THAT(GetInternedString(got.value().tuples[0][0].value.string_id), Eq("AMERICA")); + ASSERT_THAT(GetInternedString(got.value().tuples[0][1].value.string_id), Eq("North, South")); + ASSERT_THAT(GetInternedString(got.value().tuples[1][1].value.string_id), Eq("Fast lane")); + }); + + ctx.run(); +} + +TEST(ExecutorTest, StringInFilter) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT m.id FROM markets AS m WHERE m.region IN ('AMERICA', 'ASIA');"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"m", "id", Type::kInt}})); + ASSERT_THAT(got.value().tuples, Eq(Tuples{{Value{false, 1}}, {Value{false, 3}}})); + }); + + ctx.run(); +} + +TEST(ExecutorTest, StringOrderedFilter) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT m.id FROM markets AS m WHERE m.region >= 'AMERICA';"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"m", "id", Type::kInt}})); + ASSERT_THAT(got.value().tuples, Eq(Tuples{{Value{false, 1}}, {Value{false, 2}}, {Value{false, 3}}})); + }); + + ctx.run(); +} + +TEST(ExecutorTest, StringNotInFilter) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT m.id FROM markets AS m WHERE m.region NOT IN ('AMERICA', 'ASIA');"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"m", "id", Type::kInt}})); + ASSERT_THAT(got.value().tuples, Eq(Tuples{{Value{false, 2}}})); + }); + + ctx.run(); +} + +TEST(ExecutorTest, IntegerBetweenFilter) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT users.id FROM users WHERE users.age BETWEEN 5 AND 11;"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"users", "id", Type::kInt}})); + ASSERT_THAT(got.value().tuples, Eq(Tuples{{Value{false, 4}}, {Value{false, 6}}})); + }); + + ctx.run(); +} + +TEST(ExecutorTest, InNullSemantics) { + AttributesInfo attrs{{"t", "x", Type::kInt}}; + Tuple tuple{Value{false, 2}}; + + Expression positive = InExpression{ + std::make_shared(Attribute{"t", "x"}), + {IntConst{1}, Literal::kNull}, + false, + }; + Expression negative = InExpression{ + std::make_shared(Attribute{"t", "x"}), + {IntConst{1}, Literal::kNull}, + true, + }; + Expression matched = InExpression{ + std::make_shared(Attribute{"t", "x"}), + {IntConst{2}, Literal::kNull}, + false, + }; + Expression null_lhs = InExpression{ + std::make_shared(Literal::kNull), + {IntConst{1}, IntConst{2}}, + false, + }; + + ASSERT_THAT(CalcExpression(tuple, attrs, positive).is_null, Eq(true)); + ASSERT_THAT(CalcExpression(tuple, attrs, negative).is_null, Eq(true)); + ASSERT_THAT(CalcExpression(tuple, attrs, matched), Eq(Value{false, true})); + ASSERT_THAT(CalcExpression(tuple, attrs, null_lhs).is_null, Eq(true)); +} + +TEST(ExecutorTest, StringOrderedComparison) { + AttributesInfo attrs{{"t", "x", Type::kString}}; + Tuple tuple{Value{false, {.string_id = InternString("MFGR#2223")}}}; + + Expression lower = BinaryExpression{ + std::make_shared(Attribute{"t", "x"}), + BinaryOp::kGe, + std::make_shared(StringConst{"MFGR#2221"}), + }; + Expression upper = BinaryExpression{ + std::make_shared(Attribute{"t", "x"}), + BinaryOp::kLe, + std::make_shared(StringConst{"MFGR#2228"}), + }; + + ASSERT_THAT(CalcExpression(tuple, attrs, lower), + Eq(Value{false, {.bool_value = true}})); + ASSERT_THAT(CalcExpression(tuple, attrs, upper), + Eq(Value{false, {.bool_value = true}})); +} + +TEST(ExecutorTest, JitRejectsStringExpressions) { + boost::asio::io_context ctx; + bool rejected = false; + boost::asio::co_spawn( + ctx, + [&rejected]() -> boost::asio::awaitable { + std::stringstream s{"SELECT m.id FROM markets AS m WHERE m.region = 'AMERICA';"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + + try { + (void) co_await executor.Execute(op); + } catch (const std::logic_error& e) { + rejected = std::string_view{e.what()}.find("string expressions are not supported by JIT") + != std::string_view::npos; + } + }(), + [](std::exception_ptr p) { + if (p) std::rethrow_exception(p); + }); + + ctx.run(); + ASSERT_THAT(rejected, Eq(true)); +} + +TEST(ExecutorTest, JitRejectsInExpressions) { + boost::asio::io_context ctx; + bool rejected = false; + boost::asio::co_spawn( + ctx, + [&rejected]() -> boost::asio::awaitable { + std::stringstream s{"SELECT users.id FROM users WHERE users.age IN (1, 2);"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + + try { + (void) co_await executor.Execute(op); + } catch (const std::logic_error& e) { + rejected = std::string_view{e.what()}.find("IN expressions are not supported by JIT") + != std::string_view::npos; + } + }(), + [](std::exception_ptr p) { + if (p) std::rethrow_exception(p); + }); + + ctx.run(); + ASSERT_THAT(rejected, Eq(true)); +} + TYPED_TEST_P(ExecutorTest, Projection) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT users.id FROM users;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -117,7 +603,7 @@ TYPED_TEST_P(ExecutorTest, Filter) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT users.id FROM users WHERE users.age < 10;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -143,7 +629,7 @@ TYPED_TEST_P(ExecutorTest, FilterMany) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT users.id FROM users WHERE users.age > 10;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -169,7 +655,7 @@ TYPED_TEST_P(ExecutorTest, CrossJoin) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM users, books WHERE users.age < 10;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -199,7 +685,7 @@ TYPED_TEST_P(ExecutorTest, InnerJoin) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM employees JOIN departments ON employees.department_id = departments.id;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -228,7 +714,7 @@ TYPED_TEST_P(ExecutorTest, LeftJoin) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM employees LEFT OUTER JOIN departments ON employees.department_id = departments.id;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -257,7 +743,7 @@ TYPED_TEST_P(ExecutorTest, RightJoin) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -286,7 +772,7 @@ TYPED_TEST_P(ExecutorTest, ComplexJoin) { pool, []() -> boost::asio::awaitable> { std::stringstream s{"SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2 < 30;"}; - Operator op = GetAST(s).value(); + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); @@ -308,8 +794,143 @@ TYPED_TEST_P(ExecutorTest, ComplexJoin) { pool.join(); } +TEST(ExecutorTest, InnerJoinLargeRhsBug) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + Expression qual{BinaryExpression{ + .lhs = std::make_shared(Attribute{"employees", "id"}), + .binop = BinaryOp::kGt, + .rhs = std::make_shared(IntConst{0}), + }}; + PhysicalPlanNode op = NestedLoopJoin{ + .lhs = std::make_shared(SeqScan{.table = "employees"}), + .rhs = std::make_shared(SeqScan{.table = "departments_4000"}), + .type = JoinType::kInner, + .qual = std::move(qual), + }; + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + ASSERT_THAT(got.value().tuples.size(), Eq(44000u)); + }); + pool.join(); +} + REGISTER_TYPED_TEST_SUITE_P(ExecutorTest, Projection, Filter, FilterMany, CrossJoin, InnerJoin, LeftJoin, RightJoin, ComplexJoin); using ExecutorTypes = ::testing::Types; INSTANTIATE_TYPED_TEST_SUITE_P(TypedExecutorTest, ExecutorTest, ExecutorTypes); +TEST(ExecutorTest, ScalarCountStar) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT COUNT(*) FROM users;"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + auto got = co_await executor.Execute(op); + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + ASSERT_THAT(got.value().tuples.size(), Eq(1u)); + ASSERT_THAT(got.value().tuples[0][0], Eq(Value{false, {.int_value = 17}})); + }); + ctx.run(); +} + +TEST(ExecutorTest, ScalarSum) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT SUM(users.age) FROM users;"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + auto got = co_await executor.Execute(op); + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + ASSERT_THAT(got.value().tuples.size(), Eq(1u)); + ASSERT_THAT(got.value().tuples[0][0], Eq(Value{false, {.int_value = 1182}})); + }); + ctx.run(); +} + +TEST(ExecutorTest, GroupByCountStar) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT users.age, COUNT(*) FROM users GROUP BY users.age;"}; + PhysicalPlanNode op = ToPhysicalPlan(GetAST(s).value().op); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + auto got = co_await executor.Execute(op); + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().tuples.size(), Eq(11u)); + + int64_t total = 0; + for (const auto& t : got.value().tuples) { + total += t[1].value.int_value; + } + ASSERT_THAT(total, Eq(17)); + }); + ctx.run(); +} + +TEST(ExecutorTest, StreamGroupByCountStar) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT users.age, COUNT(*) FROM users GROUP BY users.age;"}; + auto ast = GetAST(s).value().op; + const auto& projection = std::get(ast); + const auto& agg = std::get(*projection.source); + PhysicalPlanNode op = PhysicalStreamAggregation{ + .source = std::make_shared(PhysicalSort{ + .source = std::make_shared(ToPhysicalPlan(*agg.source)), + .keys = SortOrder{{SortKey{"users", "age", Direction::kAsc}}}, + }), + .group_by = agg.group_by, + .aggregates = agg.aggregates, + }; + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor( + std::move(seq_scan), co_await boost::asio::this_coro::executor); + auto got = co_await executor.Execute(op); + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + ASSERT_THAT(got.value().tuples.size(), Eq(11u)); + + int64_t total = 0; + for (const auto& t : got.value().tuples) { + total += t[1].value.int_value; + } + ASSERT_THAT(total, Eq(17)); + }); + ctx.run(); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/llvm.cpp b/src/stewkk/sql/logic/executor/llvm.cpp index c807ff0..a83e292 100644 --- a/src/stewkk/sql/logic/executor/llvm.cpp +++ b/src/stewkk/sql/logic/executor/llvm.cpp @@ -204,16 +204,23 @@ llvm::Function* JITCompiler::GenerateIR( { auto* trunc_lhs = builder.CreateTrunc(value_lhs, builder.getInt1Ty(), "i64_to_i1_trunc"); auto* trunc_rhs = builder.CreateTrunc(value_rhs, builder.getInt1Ty(), "i64_to_i1_trunc"); - res_value = builder.CreateLogicalOr(trunc_lhs, trunc_rhs); - res_value = builder.CreateZExt(res_value, builder.getInt64Ty(), "i1_to_i64_zext"); + auto* lhs_true = builder.CreateAnd(builder.CreateNot(is_null_lhs), trunc_lhs); + auto* rhs_true = builder.CreateAnd(builder.CreateNot(is_null_rhs), trunc_rhs); + auto* has_true = builder.CreateOr(lhs_true, rhs_true); + is_null = builder.CreateAnd(is_null, builder.CreateNot(has_true)); + res_value = builder.CreateZExt(has_true, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kAnd: { auto* trunc_lhs = builder.CreateTrunc(value_lhs, builder.getInt1Ty(), "i64_to_i1_trunc"); auto* trunc_rhs = builder.CreateTrunc(value_rhs, builder.getInt1Ty(), "i64_to_i1_trunc"); - res_value = builder.CreateLogicalAnd(trunc_lhs, trunc_rhs); - res_value = builder.CreateZExt(res_value, builder.getInt64Ty(), "i1_to_i64_zext"); + auto* lhs_false = builder.CreateAnd(builder.CreateNot(is_null_lhs), builder.CreateNot(trunc_lhs)); + auto* rhs_false = builder.CreateAnd(builder.CreateNot(is_null_rhs), builder.CreateNot(trunc_rhs)); + auto* has_false = builder.CreateOr(lhs_false, rhs_false); + is_null = builder.CreateAnd(is_null, builder.CreateNot(has_false)); + auto* both_true = builder.CreateNot(has_false); + res_value = builder.CreateZExt(both_true, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kPlus: @@ -257,32 +264,58 @@ llvm::Function* JITCompiler::GenerateIR( auto* logical_not = builder.CreateZExt(is_zero, value->getType(), "logical_not_result"); + llvm::Value* not_null_struct = llvm::UndefValue::get(value_type); + not_null_struct = builder.CreateInsertValue(not_null_struct, builder.getInt8(0), {0}); + not_null_struct = builder.CreateInsertValue(not_null_struct, logical_not, {1}); + auto* select = builder.CreateSelect( is_null, llvm::ConstantStruct::get(static_cast(value_type), {builder.getInt8(1), builder.getInt64(0)}), - logical_not); + not_null_struct); return select; } case UnaryOp::kMinus: { auto is_null = CheckNull(child); auto value = LoadValue(child); - + auto* negated = builder.CreateNeg(value); - + + llvm::Value* not_null_struct = llvm::UndefValue::get(value_type); + not_null_struct = builder.CreateInsertValue(not_null_struct, builder.getInt8(0), {0}); + not_null_struct = builder.CreateInsertValue(not_null_struct, negated, {1}); + auto* select = builder.CreateSelect( is_null, llvm::ConstantStruct::get(static_cast(value_type), {builder.getInt8(1), builder.getInt64(0)}), - negated); + not_null_struct); return select; } + case UnaryOp::kIsNull: { + auto* is_null = CheckNull(child); + auto* res_value + = builder.CreateZExt(is_null, builder.getInt64Ty(), "isnull_value"); + llvm::Value* result = llvm::UndefValue::get(value_type); + result = builder.CreateInsertValue(result, builder.getInt8(0), {0}); + result = builder.CreateInsertValue(result, res_value, {1}); + return result; + } } } llvm::Value* operator()(const IntConst& expr) { return llvm::ConstantStruct::get(static_cast(value_type), {builder.getInt8(0), builder.getInt64(expr)}); } + llvm::Value* operator()(const StringConst&) { + throw std::logic_error{"string expressions are not supported by JIT"}; + } + llvm::Value* operator()(const InExpression&) { + throw std::logic_error{"IN expressions are not supported by JIT"}; + } + llvm::Value* operator()(const AggregateExpression&) { + throw std::logic_error{"aggregate expressions are not supported by JIT"}; + } llvm::Value* operator()(const Literal& expr) { switch (expr) { case Literal::kNull: diff --git a/src/stewkk/sql/logic/executor/materialization.cpp b/src/stewkk/sql/logic/executor/materialization.cpp index 3dcbfe0..78f4890 100644 --- a/src/stewkk/sql/logic/executor/materialization.cpp +++ b/src/stewkk/sql/logic/executor/materialization.cpp @@ -1,23 +1,56 @@ #include +#include +#include +#include +#include + #include // TODO: implement async file io with asio (and benchmark it) namespace stewkk::sql { +namespace { + +constexpr std::size_t kDiskValueSize = sizeof(uint8_t) + sizeof(int64_t); + +void AppendValue(std::vector& buf, const Value& value) { + const int64_t payload = value.is_null ? 0 : value.value.int_value; + buf.push_back(static_cast(value.is_null)); + const auto* payload_bytes = reinterpret_cast(&payload); + buf.insert(buf.end(), payload_bytes, payload_bytes + sizeof(payload)); +} + +Value ReadValue(const char* buf) { + int64_t payload; + std::memcpy(&payload, buf + sizeof(uint8_t), sizeof(payload)); + return Value{static_cast(buf[0]), {.int_value = payload}}; +} + +} // namespace + DiskFileWriter::DiskFileWriter() : path_(fs::temp_directory_path() / fs::unique_path("%%%%.tmp")), f_(path_, std::ios::binary) {} void DiskFileWriter::Write(const Tuples& tuples) { - tuple_size_ = tuples.front().size(); + if (tuples.empty()) { + return; + } + if (tuple_size_ == 0) { + tuple_size_ = tuples.front().size(); + } + std::vector buf; + buf.reserve(tuples.size() * tuple_size_ * kDiskValueSize); for (const auto& tuple : tuples) { - std::size_t tuple_size = tuple.size(); - // NOTE: better to store tuples in continious buffer and than flush to - // disk - f_.write(reinterpret_cast(tuple.data()), - tuple.size() * sizeof(Tuple::value_type)); + if (tuple.size() != tuple_size_) { + throw std::runtime_error{"cannot materialize tuples with different sizes"}; + } + for (const auto& value : tuple) { + AppendValue(buf, value); + } } + f_.write(buf.data(), static_cast(buf.size())); } DiskFileReader DiskFileWriter::GetDiskFileReader() && { @@ -34,16 +67,28 @@ DiskFileReader::~DiskFileReader() { fs::remove(path_); } +void DiskFileReader::Rewind() { + f_.clear(); + f_.seekg(0, std::ios::beg); +} + Tuples DiskFileReader::Read() { Tuples buf; buf.reserve(kBufSize); + std::vector tuple_buf(tuple_size_ * kDiskValueSize); while (buf.size() < kBufSize) { Tuple tuple; - tuple.resize(tuple_size_); - f_.read(reinterpret_cast(tuple.data()), tuple_size_*sizeof(Tuple::value_type)); + tuple.reserve(tuple_size_); + f_.read(tuple_buf.data(), static_cast(tuple_buf.size())); if (f_.gcount() == 0) { break; } + if (f_.gcount() != static_cast(tuple_buf.size())) { + throw std::runtime_error{"truncated materialized tuple"}; + } + for (std::size_t offset = 0; offset < tuple_buf.size(); offset += kDiskValueSize) { + tuple.push_back(ReadValue(tuple_buf.data() + offset)); + } buf.push_back(std::move(tuple)); } return buf; diff --git a/src/stewkk/sql/logic/executor/plan.cpp b/src/stewkk/sql/logic/executor/plan.cpp new file mode 100644 index 0000000..55c82d0 --- /dev/null +++ b/src/stewkk/sql/logic/executor/plan.cpp @@ -0,0 +1,62 @@ +#include + +namespace stewkk::sql { + +bool PhysicalPlanNode::operator==(const PhysicalPlanNode& other) const { + return node == other.node; +} + +std::string_view OutputTable(const SeqScan& scan) { + return scan.alias ? std::string_view{*scan.alias} : std::string_view{scan.table}; +} + +bool PhysicalProjection::operator==(const PhysicalProjection& other) const { + return *source == *other.source && expressions == other.expressions && aliases == other.aliases; +} + +bool PhysicalFilter::operator==(const PhysicalFilter& other) const { + return *source == *other.source && predicate == other.predicate; +} + +bool NestedLoopJoin::operator==(const NestedLoopJoin& other) const { + return *lhs == *other.lhs && *rhs == *other.rhs && type == other.type && qual == other.qual; +} + +bool NestedLoopCrossJoin::operator==(const NestedLoopCrossJoin& other) const { + return *lhs == *other.lhs && *rhs == *other.rhs; +} + +bool HashJoin::operator==(const HashJoin& other) const { + return *lhs == *other.lhs && *rhs == *other.rhs && type == other.type && qual == other.qual; +} + +bool MergeJoin::operator==(const MergeJoin& other) const { + return *lhs == *other.lhs && *rhs == *other.rhs && type == other.type && qual == other.qual; +} + +bool IndexSeek::operator==(const IndexSeek& other) const { + return table == other.table && alias == other.alias && predicate == other.predicate + && index_column == other.index_column; +} + +bool PhysicalSort::operator==(const PhysicalSort& other) const { + return *source == *other.source && keys == other.keys; +} + +bool PhysicalAggregation::operator==(const PhysicalAggregation& other) const { + return *source == *other.source && group_by == other.group_by && aggregates == other.aggregates; +} + +bool PhysicalStreamAggregation::operator==(const PhysicalStreamAggregation& other) const { + return *source == *other.source && group_by == other.group_by && aggregates == other.aggregates; +} + +bool PhysicalPartialAggregation::operator==(const PhysicalPartialAggregation& other) const { + return *source == *other.source && group_by == other.group_by && aggregates == other.aggregates; +} + +bool PhysicalFinalAggregation::operator==(const PhysicalFinalAggregation& other) const { + return *source == *other.source && group_by == other.group_by && aggregates == other.aggregates; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/plan_serializer.cpp b/src/stewkk/sql/logic/executor/plan_serializer.cpp new file mode 100644 index 0000000..deff5ae --- /dev/null +++ b/src/stewkk/sql/logic/executor/plan_serializer.cpp @@ -0,0 +1,846 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +namespace { + +std::string SerializeExpr(const Expression& expr); +std::string SerializeNode(const PhysicalPlanNode& node); + +std::string QuoteString(std::string_view value) { + std::string out; + out.reserve(value.size() + 2); + out.push_back('"'); + for (char c : value) { + switch (c) { + case '\\': out += "\\\\"; break; + case '"': out += "\\\""; break; + case '\n': out += "\\n"; break; + case '\t': out += "\\t"; break; + default: out.push_back(c); break; + } + } + out.push_back('"'); + return out; +} + +std::string UnquoteString(std::string_view value) { + if (value.size() < 2 || value.front() != '"' || value.back() != '"') { + throw std::runtime_error("expected quoted string"); + } + std::string out; + for (size_t i = 1; i + 1 < value.size(); ++i) { + if (value[i] != '\\') { + out.push_back(value[i]); + continue; + } + if (i + 1 >= value.size() - 1) { + throw std::runtime_error("unterminated string escape"); + } + char escaped = value[++i]; + switch (escaped) { + case '\\': out.push_back('\\'); break; + case '"': out.push_back('"'); break; + case 'n': out.push_back('\n'); break; + case 't': out.push_back('\t'); break; + default: throw std::runtime_error(std::format("unsupported string escape: \\{}", escaped)); + } + } + return out; +} + +std::string SerializeJoinType(JoinType t) { + switch (t) { + case JoinType::kInner: return "Inner"; + case JoinType::kFull: return "Full"; + case JoinType::kLeft: return "Left"; + case JoinType::kRight: return "Right"; + } +} + +std::string SerializeBinaryOp(BinaryOp op) { + switch (op) { + case BinaryOp::kGt: return ">"; + case BinaryOp::kLt: return "<"; + case BinaryOp::kLe: return "<="; + case BinaryOp::kGe: return ">="; + case BinaryOp::kNotEq: return "!="; + case BinaryOp::kEq: return "="; + case BinaryOp::kOr: return "or"; + case BinaryOp::kAnd: return "and"; + case BinaryOp::kPlus: return "+"; + case BinaryOp::kMinus: return "-"; + case BinaryOp::kMul: return "*"; + case BinaryOp::kDiv: return "/"; + case BinaryOp::kMod: return "%"; + case BinaryOp::kPow: return "^"; + } +} + +std::string SerializeUnaryOp(UnaryOp op) { + switch (op) { + case UnaryOp::kNot: return "not"; + case UnaryOp::kMinus: return "uminus"; + case UnaryOp::kIsNull: return "isnull"; + } +} + +std::string SerializeExpr(const Expression& expr) { + struct Visitor { + std::string operator()(const BinaryExpression& e) const { + return std::format("({} {} {})", SerializeBinaryOp(e.binop), + SerializeExpr(*e.lhs), SerializeExpr(*e.rhs)); + } + std::string operator()(const Attribute& a) const { + return std::format("(attr {} {})", a.table.empty() ? "-" : a.table, a.name); + } + std::string operator()(IntConst n) const { + return std::to_string(n); + } + std::string operator()(const StringConst& s) const { + return std::format("(str {})", QuoteString(s)); + } + std::string operator()(const UnaryExpression& e) const { + return std::format("({} {})", SerializeUnaryOp(e.op), SerializeExpr(*e.child)); + } + std::string operator()(const InExpression& e) const { + std::string values; + for (const auto& value : e.values) { + if (!values.empty()) values += ' '; + values += SerializeExpr(value); + } + return std::format("({} {} (values {}))", + e.negated ? "notin" : "in", SerializeExpr(*e.lhs), values); + } + std::string operator()(const AggregateExpression& e) const { + if (e.is_star) { + return std::format("({} *)", ToString(e.function)); + } + return std::format("({} {})", ToString(e.function), SerializeExpr(*e.argument)); + } + std::string operator()(Literal l) const { + switch (l) { + case Literal::kNull: return "NULL"; + case Literal::kTrue: return "TRUE"; + case Literal::kFalse: return "FALSE"; + case Literal::kUnknown: return "UNKNOWN"; + } + } + }; + return std::visit(Visitor{}, expr); +} + +std::string SerializeNode(const PhysicalPlanNode& node) { + struct Visitor { + std::string operator()(const SeqScan& n) const { + if (n.alias) { + return std::format("(SeqScan {} {})", n.table, *n.alias); + } + return std::format("(SeqScan {})", n.table); + } + std::string operator()(const PhysicalFilter& n) const { + return std::format("(PhysicalFilter {} {})", + SerializeExpr(n.predicate), SerializeNode(*n.source)); + } + std::string operator()(const PhysicalProjection& n) const { + std::string exprs; + for (const auto& e : n.expressions) { + if (!exprs.empty()) exprs += ' '; + exprs += SerializeExpr(e); + } + if (!n.aliases.empty()) { + std::string aliases; + for (size_t i = 0; i < n.expressions.size(); ++i) { + if (!aliases.empty()) aliases += ' '; + if (i < n.aliases.size() && n.aliases[i]) { + aliases += QuoteString(*n.aliases[i]); + } else { + aliases += "-"; + } + } + return std::format("(PhysicalProjection (exprs {}) (aliases {}) {})", + exprs, aliases, SerializeNode(*n.source)); + } + return std::format("(PhysicalProjection (exprs {}) {})", exprs, + SerializeNode(*n.source)); + } + std::string operator()(const NestedLoopCrossJoin& n) const { + return std::format("(NestedLoopCrossJoin {} {})", + SerializeNode(*n.lhs), SerializeNode(*n.rhs)); + } + std::string operator()(const NestedLoopJoin& n) const { + return std::format("(NestedLoopJoin {} {} {} {})", + SerializeJoinType(n.type), SerializeExpr(n.qual), + SerializeNode(*n.lhs), SerializeNode(*n.rhs)); + } + std::string operator()(const IndexSeek& n) const { + if (n.alias) { + return std::format("(IndexSeek {} {} {})", SerializeExpr(n.predicate), n.table, *n.alias); + } + return std::format("(IndexSeek {} {})", SerializeExpr(n.predicate), n.table); + } + std::string operator()(const HashJoin& n) const { + return std::format("(HashJoin {} {} {} {})", + SerializeJoinType(n.type), SerializeExpr(n.qual), + SerializeNode(*n.lhs), SerializeNode(*n.rhs)); + } + std::string operator()(const MergeJoin& n) const { + return std::format("(MergeJoin {} {} {} {})", + SerializeJoinType(n.type), SerializeExpr(n.qual), + SerializeNode(*n.lhs), SerializeNode(*n.rhs)); + } + std::string operator()(const PhysicalSort& n) const { + std::string keys; + for (const auto& k : n.keys.keys) { + if (!keys.empty()) keys += ' '; + keys += k.table; + keys += '.'; + keys += k.column; + keys += k.dir == Direction::kAsc ? " Asc" : " Desc"; + } + return std::format("(Sort (keys {}) {})", keys, SerializeNode(*n.source)); + } + std::string operator()(const PhysicalAggregation& n) const { + std::string group_by_str; + for (const auto& e : n.group_by) { + if (!group_by_str.empty()) group_by_str += ' '; + group_by_str += SerializeExpr(e); + } + std::string aggs_str; + for (const auto& e : n.aggregates) { + if (!aggs_str.empty()) aggs_str += ' '; + aggs_str += SerializeExpr(e); + } + return std::format("(HashAggregate (group_by {}) (aggs {}) {})", + group_by_str, aggs_str, SerializeNode(*n.source)); + } + std::string operator()(const PhysicalStreamAggregation& n) const { + std::string group_by_str; + for (const auto& e : n.group_by) { + if (!group_by_str.empty()) group_by_str += ' '; + group_by_str += SerializeExpr(e); + } + std::string aggs_str; + for (const auto& e : n.aggregates) { + if (!aggs_str.empty()) aggs_str += ' '; + aggs_str += SerializeExpr(e); + } + return std::format("(StreamAggregate (group_by {}) (aggs {}) {})", + group_by_str, aggs_str, SerializeNode(*n.source)); + } + std::string operator()(const PhysicalPartialAggregation& n) const { + std::string group_by_str; + for (const auto& e : n.group_by) { + if (!group_by_str.empty()) group_by_str += ' '; + group_by_str += SerializeExpr(e); + } + std::string aggs_str; + for (const auto& e : n.aggregates) { + if (!aggs_str.empty()) aggs_str += ' '; + aggs_str += SerializeExpr(e); + } + return std::format("(PartialAggregate (group_by {}) (aggs {}) {})", + group_by_str, aggs_str, SerializeNode(*n.source)); + } + std::string operator()(const PhysicalFinalAggregation& n) const { + std::string group_by_str; + for (const auto& e : n.group_by) { + if (!group_by_str.empty()) group_by_str += ' '; + group_by_str += SerializeExpr(e); + } + std::string aggs_str; + for (const auto& e : n.aggregates) { + if (!aggs_str.empty()) aggs_str += ' '; + aggs_str += SerializeExpr(e); + } + return std::format("(FinalAggregate (group_by {}) (aggs {}) {})", + group_by_str, aggs_str, SerializeNode(*n.source)); + } + }; + return std::visit(Visitor{}, node.node); +} + +enum class TokenKind { LParen, RParen, Atom }; + +struct Token { + TokenKind kind; + std::string text; +}; + +std::vector Tokenize(std::string_view input) { + std::vector tokens; + size_t i = 0; + while (i < input.size()) { + if (std::isspace(static_cast(input[i]))) { ++i; continue; } + if (input[i] == '(') { tokens.push_back({TokenKind::LParen, "("}); ++i; continue; } + if (input[i] == ')') { tokens.push_back({TokenKind::RParen, ")"}); ++i; continue; } + if (input[i] == '"') { + size_t j = i + 1; + bool escaped = false; + while (j < input.size()) { + if (escaped) { + escaped = false; + } else if (input[j] == '\\') { + escaped = true; + } else if (input[j] == '"') { + ++j; + break; + } + ++j; + } + if (j > input.size() || input[j - 1] != '"') + throw std::runtime_error("unterminated quoted string"); + tokens.push_back({TokenKind::Atom, std::string(input.substr(i, j - i))}); + i = j; + continue; + } + size_t j = i; + while (j < input.size() + && !std::isspace(static_cast(input[j])) + && input[j] != '(' && input[j] != ')') + ++j; + tokens.push_back({TokenKind::Atom, std::string(input.substr(i, j - i))}); + i = j; + } + return tokens; +} + +struct ParseState { + const std::vector& tokens; + size_t pos = 0; + + const Token& Peek() const { + if (pos >= tokens.size()) + throw std::runtime_error("unexpected end of input"); + return tokens[pos]; + } + + const Token& Consume() { + if (pos >= tokens.size()) + throw std::runtime_error("unexpected end of input"); + return tokens[pos++]; + } + + void ExpectLParen() { + const auto& t = Consume(); + if (t.kind != TokenKind::LParen) + throw std::runtime_error(std::format("expected '(' but got '{}'", t.text)); + } + + void ExpectRParen() { + const auto& t = Consume(); + if (t.kind != TokenKind::RParen) + throw std::runtime_error(std::format("expected ')' but got '{}'", t.text)); + } + + std::string ExpectAtom() { + const auto& t = Consume(); + if (t.kind != TokenKind::Atom) + throw std::runtime_error(std::format("expected atom but got '{}'", t.text)); + return t.text; + } +}; + +const std::unordered_map kBinaryOps = { + {">", BinaryOp::kGt}, {"<", BinaryOp::kLt}, {"<=", BinaryOp::kLe}, + {">=", BinaryOp::kGe}, {"!=", BinaryOp::kNotEq}, {"=", BinaryOp::kEq}, + {"or", BinaryOp::kOr}, {"and", BinaryOp::kAnd}, {"+", BinaryOp::kPlus}, + {"-", BinaryOp::kMinus}, {"*", BinaryOp::kMul}, {"/", BinaryOp::kDiv}, + {"%", BinaryOp::kMod}, {"^", BinaryOp::kPow}, +}; + +const std::unordered_map kUnaryOps = { + {"not", UnaryOp::kNot}, {"uminus", UnaryOp::kMinus}, {"isnull", UnaryOp::kIsNull}, +}; + +const std::unordered_map kJoinTypes = { + {"Inner", JoinType::kInner}, {"Full", JoinType::kFull}, + {"Left", JoinType::kLeft}, {"Right", JoinType::kRight}, +}; + +const std::unordered_map kLiterals = { + {"NULL", Literal::kNull}, {"TRUE", Literal::kTrue}, + {"FALSE", Literal::kFalse}, {"UNKNOWN", Literal::kUnknown}, +}; + +const std::unordered_map kAggregateFunctions = { + {"SUM", AggregateFunction::kSum}, + {"COUNT", AggregateFunction::kCount}, +}; + +Expression ParseExpr(ParseState& s); +PhysicalPlanNode ParseNode(ParseState& s); + +Expression ParseExpr(ParseState& s) { + const auto& t = s.Peek(); + + if (t.kind == TokenKind::Atom) { + auto text = s.ExpectAtom(); + try { + size_t idx; + IntConst n = std::stoll(text, &idx); + if (idx == text.size()) return n; + } catch (...) {} + if (auto it = kLiterals.find(text); it != kLiterals.end()) return it->second; + throw std::runtime_error(std::format("unexpected atom in expression: '{}'", text)); + } + + if (t.kind == TokenKind::LParen) { + s.ExpectLParen(); + auto head = s.ExpectAtom(); + + if (head == "attr") { + auto table = s.ExpectAtom(); + auto name = s.ExpectAtom(); + s.ExpectRParen(); + return Attribute{table == "-" ? "" : std::move(table), std::move(name)}; + } + if (head == "str") { + auto value = UnquoteString(s.ExpectAtom()); + s.ExpectRParen(); + return StringConst{std::move(value)}; + } + if (head == "in" || head == "notin") { + auto lhs = ParseExpr(s); + s.ExpectLParen(); + auto values_head = s.ExpectAtom(); + if (values_head != "values") { + throw std::runtime_error(std::format("expected values but got '{}'", values_head)); + } + std::vector values; + while (s.Peek().kind != TokenKind::RParen) { + values.push_back(ParseExpr(s)); + } + s.ExpectRParen(); + s.ExpectRParen(); + return InExpression{ + std::make_shared(std::move(lhs)), + std::move(values), + head == "notin", + }; + } + if (auto it = kAggregateFunctions.find(head); it != kAggregateFunctions.end()) { + if (s.Peek().kind == TokenKind::Atom && s.Peek().text == "*") { + s.ExpectAtom(); + s.ExpectRParen(); + return AggregateExpression{it->second, nullptr, true}; + } + auto arg = ParseExpr(s); + s.ExpectRParen(); + return AggregateExpression{ + it->second, + std::make_shared(std::move(arg)), + false, + }; + } + if (auto it = kBinaryOps.find(head); it != kBinaryOps.end()) { + auto lhs = ParseExpr(s); + auto rhs = ParseExpr(s); + s.ExpectRParen(); + return BinaryExpression{ + std::make_shared(std::move(lhs)), + it->second, + std::make_shared(std::move(rhs)), + }; + } + if (auto it = kUnaryOps.find(head); it != kUnaryOps.end()) { + auto child = ParseExpr(s); + s.ExpectRParen(); + return UnaryExpression{it->second, std::make_shared(std::move(child))}; + } + throw std::runtime_error(std::format("unknown expression head: '{}'", head)); + } + + throw std::runtime_error("expected expression"); +} + +PhysicalPlanNode ParseNode(ParseState& s) { + s.ExpectLParen(); + auto head = s.ExpectAtom(); + + if (head == "SeqScan") { + auto table = s.ExpectAtom(); + std::optional alias; + if (s.Peek().kind != TokenKind::RParen) { + alias = s.ExpectAtom(); + } + s.ExpectRParen(); + return SeqScan{std::move(table), std::move(alias)}; + } + if (head == "PhysicalFilter") { + auto pred = ParseExpr(s); + auto source = ParseNode(s); + s.ExpectRParen(); + return PhysicalFilter{ + std::make_shared(std::move(source)), + std::move(pred), + }; + } + if (head == "PhysicalProjection") { + s.ExpectLParen(); + auto kw = s.ExpectAtom(); + if (kw != "exprs") + throw std::runtime_error(std::format("expected 'exprs' but got '{}'", kw)); + std::vector exprs; + while (s.Peek().kind != TokenKind::RParen) + exprs.push_back(ParseExpr(s)); + s.ExpectRParen(); + std::vector> aliases; + if (s.Peek().kind == TokenKind::LParen + && s.pos + 1 < s.tokens.size() + && s.tokens[s.pos + 1].kind == TokenKind::Atom + && s.tokens[s.pos + 1].text == "aliases") { + s.ExpectLParen(); + auto aliases_kw = s.ExpectAtom(); + if (aliases_kw != "aliases") + throw std::runtime_error(std::format("expected 'aliases' but got '{}'", aliases_kw)); + while (s.Peek().kind != TokenKind::RParen) { + auto alias = s.ExpectAtom(); + if (alias == "-") { + aliases.push_back(std::nullopt); + } else { + aliases.push_back(UnquoteString(alias)); + } + } + s.ExpectRParen(); + } + auto source = ParseNode(s); + s.ExpectRParen(); + return PhysicalProjection{ + std::make_shared(std::move(source)), + std::move(exprs), + std::move(aliases), + }; + } + if (head == "NestedLoopCrossJoin") { + auto lhs = ParseNode(s); + auto rhs = ParseNode(s); + s.ExpectRParen(); + return NestedLoopCrossJoin{ + std::make_shared(std::move(lhs)), + std::make_shared(std::move(rhs)), + }; + } + auto ParseJoinNode = [&]() -> JoinT { + auto type_str = s.ExpectAtom(); + auto it = kJoinTypes.find(type_str); + if (it == kJoinTypes.end()) + throw std::runtime_error(std::format("unknown join type: '{}'", type_str)); + auto qual = ParseExpr(s); + auto lhs = ParseNode(s); + auto rhs = ParseNode(s); + s.ExpectRParen(); + return JoinT{ + std::make_shared(std::move(lhs)), + std::make_shared(std::move(rhs)), + it->second, + std::move(qual), + }; + }; + + if (head == "IndexSeek") { + auto pred = ParseExpr(s); + auto table = s.ExpectAtom(); + std::optional alias; + if (s.Peek().kind != TokenKind::RParen) { + alias = s.ExpectAtom(); + } + s.ExpectRParen(); + return IndexSeek{std::move(table), std::move(alias), std::move(pred)}; + } + if (head == "NestedLoopJoin") return ParseJoinNode.template operator()(); + if (head == "HashJoin") return ParseJoinNode.template operator()(); + if (head == "MergeJoin") return ParseJoinNode.template operator()(); + if (head == "Sort") { + s.ExpectLParen(); + auto kw = s.ExpectAtom(); + if (kw != "keys") + throw std::runtime_error(std::format("expected 'keys' but got '{}'", kw)); + std::vector keys; + while (s.Peek().kind != TokenKind::RParen) { + auto qualified = s.ExpectAtom(); + auto dot = qualified.find('.'); + if (dot == std::string::npos) + throw std::runtime_error("sort key must be table.column, got: " + qualified); + std::string table = qualified.substr(0, dot); + std::string col = qualified.substr(dot + 1); + auto dir_str = s.ExpectAtom(); + Direction dir = dir_str == "Asc" ? Direction::kAsc : Direction::kDesc; + keys.push_back({std::move(table), std::move(col), dir}); + } + s.ExpectRParen(); + auto source = ParseNode(s); + s.ExpectRParen(); + return PhysicalSort{ + std::make_shared(std::move(source)), + SortOrder{std::move(keys)}, + }; + } + + if (head == "HashAggregate" || head == "StreamAggregate") { + s.ExpectLParen(); + auto kw1 = s.ExpectAtom(); + if (kw1 != "group_by") + throw std::runtime_error(std::format("expected 'group_by' but got '{}'", kw1)); + std::vector group_by; + while (s.Peek().kind != TokenKind::RParen) + group_by.push_back(ParseExpr(s)); + s.ExpectRParen(); + s.ExpectLParen(); + auto kw2 = s.ExpectAtom(); + if (kw2 != "aggs") + throw std::runtime_error(std::format("expected 'aggs' but got '{}'", kw2)); + std::vector aggregates; + while (s.Peek().kind != TokenKind::RParen) + aggregates.push_back(ParseExpr(s)); + s.ExpectRParen(); + auto source = ParseNode(s); + s.ExpectRParen(); + if (head == "StreamAggregate") { + return PhysicalStreamAggregation{ + std::make_shared(std::move(source)), + std::move(group_by), + std::move(aggregates), + }; + } + return PhysicalAggregation{ + std::make_shared(std::move(source)), + std::move(group_by), + std::move(aggregates), + }; + } + + throw std::runtime_error(std::format("unknown plan node: '{}'", head)); +} + +std::string EscapeLabel(std::string_view s) { + std::string out; + out.reserve(s.size()); + for (char c : s) { + if (c == '\n') { + out += "\\n"; + continue; + } + if (c == '"' || c == '\\') out += '\\'; + out += c; + } + return out; +} + +struct DotBuilder { + std::ostringstream os; + int next_id = 0; + + int Emit(std::string label, const std::optional& metadata) { + if (metadata) { + label += std::format("\ncard={}\ncost={}", metadata->cardinality, + metadata->local_cost); + } + int id = next_id++; + os << std::format(" n{} [label=\"{}\"]\n", id, EscapeLabel(label)); + return id; + } + + void EmitEdge(int from, int to) { + os << std::format(" n{} -> n{}\n", from, to); + } + + int EmitNode(const PhysicalPlanNode& node) { + return std::visit([&](const auto& n) { return EmitAlternative(n, node.metadata); }, + node.node); + } + + int EmitAlternative(const SeqScan& n, const std::optional& metadata) { + if (n.alias) { + return Emit(std::format("SeqScan\n{} AS {}", n.table, *n.alias), metadata); + } + return Emit(std::format("SeqScan\n{}", n.table), metadata); + } + + int EmitAlternative(const PhysicalFilter& n, + const std::optional& metadata) { + int src = EmitNode(*n.source); + int id = Emit(std::format("σ {}", ToString(n.predicate)), metadata); + EmitEdge(src, id); + return id; + } + + int EmitAlternative(const PhysicalProjection& n, + const std::optional& metadata) { + int src = EmitNode(*n.source); + auto exprs = n.expressions + | std::views::transform([](const Expression& e) { return ToString(e); }) + | std::views::join_with(std::string(", ")) + | std::ranges::to(); + int id = Emit(std::format("π {}", exprs), metadata); + EmitEdge(src, id); + return id; + } + + int EmitAlternative(const NestedLoopCrossJoin& n, + const std::optional& metadata) { + int lhs = EmitNode(*n.lhs); + int rhs = EmitNode(*n.rhs); + int id = Emit("×", metadata); + EmitEdge(lhs, id); + EmitEdge(rhs, id); + return id; + } + + int EmitAlternative(const NestedLoopJoin& n, + const std::optional& metadata) { + int lhs = EmitNode(*n.lhs); + int rhs = EmitNode(*n.rhs); + int id = Emit(std::format("NL {}\nON {}", ToString(n.type), ToString(n.qual)), + metadata); + EmitEdge(lhs, id); + EmitEdge(rhs, id); + return id; + } + + int EmitAlternative(const IndexSeek& n, const std::optional& metadata) { + return Emit(std::format("IndexSeek\n{}\n{}", n.table, ToString(n.predicate)), + metadata); + } + + int EmitAlternative(const HashJoin& n, const std::optional& metadata) { + int lhs = EmitNode(*n.lhs); + int rhs = EmitNode(*n.rhs); + int id = Emit(std::format("Hash {}\nON {}", ToString(n.type), ToString(n.qual)), + metadata); + EmitEdge(lhs, id); + EmitEdge(rhs, id); + return id; + } + + int EmitAlternative(const MergeJoin& n, const std::optional& metadata) { + int lhs = EmitNode(*n.lhs); + int rhs = EmitNode(*n.rhs); + int id = Emit(std::format("Merge {}\nON {}", ToString(n.type), ToString(n.qual)), + metadata); + EmitEdge(lhs, id); + EmitEdge(rhs, id); + return id; + } + + int EmitAlternative(const PhysicalSort& n, + const std::optional& metadata) { + int src = EmitNode(*n.source); + std::string keys; + for (const auto& k : n.keys.keys) { + if (!keys.empty()) keys += ", "; + keys += k.table; + keys += '.'; + keys += k.column; + keys += k.dir == Direction::kAsc ? " asc" : " desc"; + } + int id = Emit(std::format("Sort\n{}", keys), metadata); + EmitEdge(src, id); + return id; + } + + int EmitAlternative(const PhysicalAggregation& n, + const std::optional& metadata) { + int src = EmitNode(*n.source); + std::string aggs; + for (const auto& e : n.aggregates) { + if (!aggs.empty()) aggs += ", "; + aggs += ToString(e); + } + std::string group_by; + for (const auto& e : n.group_by) { + if (!group_by.empty()) group_by += ", "; + group_by += ToString(e); + } + int id = Emit(std::format("HashAgg\nGROUP BY {}\n{}", group_by, aggs), + metadata); + EmitEdge(src, id); + return id; + } + + int EmitAlternative(const PhysicalStreamAggregation& n, + const std::optional& metadata) { + int src = EmitNode(*n.source); + std::string aggs; + for (const auto& e : n.aggregates) { + if (!aggs.empty()) aggs += ", "; + aggs += ToString(e); + } + std::string group_by; + for (const auto& e : n.group_by) { + if (!group_by.empty()) group_by += ", "; + group_by += ToString(e); + } + int id = Emit(std::format("StreamAgg\nGROUP BY {}\n{}", group_by, aggs), + metadata); + EmitEdge(src, id); + return id; + } + + int EmitAlternative(const PhysicalPartialAggregation& n, + const std::optional& metadata) { + int src = EmitNode(*n.source); + std::string aggs; + for (const auto& e : n.aggregates) { + if (!aggs.empty()) aggs += ", "; + aggs += ToString(e); + } + std::string group_by; + for (const auto& e : n.group_by) { + if (!group_by.empty()) group_by += ", "; + group_by += ToString(e); + } + int id = Emit(std::format("PartialAgg\nGROUP BY {}\n{}", group_by, aggs), + metadata); + EmitEdge(src, id); + return id; + } + + int EmitAlternative(const PhysicalFinalAggregation& n, + const std::optional& metadata) { + int src = EmitNode(*n.source); + std::string aggs; + for (const auto& e : n.aggregates) { + if (!aggs.empty()) aggs += ", "; + aggs += ToString(e); + } + std::string group_by; + for (const auto& e : n.group_by) { + if (!group_by.empty()) group_by += ", "; + group_by += ToString(e); + } + int id = Emit(std::format("FinalAgg\nGROUP BY {}\n{}", group_by, aggs), + metadata); + EmitEdge(src, id); + return id; + } +}; + +} // namespace + +std::string Serialize(const PhysicalPlanNode& node) { + return SerializeNode(node); +} + +PhysicalPlanNode Deserialize(std::string_view text) { + auto tokens = Tokenize(text); + ParseState s{tokens}; + return ParseNode(s); +} + +std::string SerializeDot(const PhysicalPlanNode& node) { + DotBuilder b; + b.EmitNode(node); + return std::format("digraph G {{ rankdir=BT;\n{}}}\n", b.os.str()); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/plan_serializer_test.cpp b/src/stewkk/sql/logic/executor/plan_serializer_test.cpp new file mode 100644 index 0000000..2867e5d --- /dev/null +++ b/src/stewkk/sql/logic/executor/plan_serializer_test.cpp @@ -0,0 +1,273 @@ +#include + +#include + +using ::testing::Eq; + +namespace stewkk::sql { + +namespace { + +PhysicalPlanNode RoundTrip(const PhysicalPlanNode& node) { + return Deserialize(Serialize(node)); +} + +} // namespace + +TEST(PlanSerializerTest, SeqScan) { + PhysicalPlanNode plan = SeqScan{"users"}; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + EXPECT_THAT(Serialize(plan), Eq("(SeqScan users)")); +} + +TEST(PlanSerializerTest, SeqScanAlias) { + PhysicalPlanNode plan = SeqScan{"customer", "c"}; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + EXPECT_THAT(Serialize(plan), Eq("(SeqScan customer c)")); +} + +TEST(PlanSerializerTest, PhysicalFilter) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"users"}), + BinaryExpression{ + std::make_shared(Attribute{"users", "age"}), + BinaryOp::kLt, + std::make_shared(IntConst{10}), + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, PhysicalProjection) { + PhysicalPlanNode plan = PhysicalProjection{ + std::make_shared(SeqScan{"users"}), + {Attribute{"users", "id"}, Attribute{"users", "age"}}, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, PhysicalProjectionEmpty) { + PhysicalPlanNode plan = PhysicalProjection{ + std::make_shared(SeqScan{"t"}), + {}, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, NestedLoopCrossJoin) { + PhysicalPlanNode plan = NestedLoopCrossJoin{ + std::make_shared(SeqScan{"users"}), + std::make_shared(SeqScan{"books"}), + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, NestedLoopJoinInner) { + PhysicalPlanNode plan = NestedLoopJoin{ + std::make_shared(SeqScan{"a"}), + std::make_shared(SeqScan{"b"}), + JoinType::kInner, + BinaryExpression{ + std::make_shared(Attribute{"a", "x"}), + BinaryOp::kEq, + std::make_shared(Attribute{"b", "x"}), + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, NestedLoopJoinAllTypes) { + for (auto type : {JoinType::kInner, JoinType::kFull, JoinType::kLeft, JoinType::kRight}) { + PhysicalPlanNode plan = NestedLoopJoin{ + std::make_shared(SeqScan{"a"}), + std::make_shared(SeqScan{"b"}), + type, + Literal::kTrue, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + } +} + +TEST(PlanSerializerTest, ExprIntConst) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + IntConst{-42}, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, ExprStringConst) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + StringConst{"Bob's Market \"North\""}, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + EXPECT_THAT(Serialize(plan), Eq("(PhysicalFilter (str \"Bob's Market \\\"North\\\"\") (SeqScan t))")); +} + +TEST(PlanSerializerTest, ExprInList) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + InExpression{ + std::make_shared(Attribute{"t", "region"}), + {StringConst{"AMERICA"}, StringConst{"ASIA"}, Literal::kNull}, + false, + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + EXPECT_THAT(Serialize(plan), + Eq("(PhysicalFilter (in (attr t region) (values (str \"AMERICA\") (str \"ASIA\") NULL)) (SeqScan t))")); +} + +TEST(PlanSerializerTest, ExprNotInList) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + InExpression{ + std::make_shared(Attribute{"t", "id"}), + {IntConst{1}, IntConst{2}}, + true, + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + EXPECT_THAT(Serialize(plan), Eq("(PhysicalFilter (notin (attr t id) (values 1 2)) (SeqScan t))")); +} + +TEST(PlanSerializerTest, ExprLiterals) { + for (auto lit : {Literal::kNull, Literal::kTrue, Literal::kFalse, Literal::kUnknown}) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + lit, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + } +} + +TEST(PlanSerializerTest, ExprUnaryNot) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + UnaryExpression{UnaryOp::kNot, std::make_shared(Attribute{"t", "x"})}, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, ExprUnaryMinus) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + UnaryExpression{UnaryOp::kMinus, std::make_shared(IntConst{5})}, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, ExprAllBinaryOps) { + for (auto op : {BinaryOp::kGt, BinaryOp::kLt, BinaryOp::kLe, BinaryOp::kGe, + BinaryOp::kNotEq, BinaryOp::kEq, BinaryOp::kOr, BinaryOp::kAnd, + BinaryOp::kPlus, BinaryOp::kMinus, BinaryOp::kMul, BinaryOp::kDiv, + BinaryOp::kMod, BinaryOp::kPow}) { + PhysicalPlanNode plan = PhysicalFilter{ + std::make_shared(SeqScan{"t"}), + BinaryExpression{ + std::make_shared(IntConst{1}), + op, + std::make_shared(IntConst{2}), + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + } +} + +TEST(PlanSerializerTest, DeepNestedPlan) { + PhysicalPlanNode plan = PhysicalProjection{ + std::make_shared(PhysicalFilter{ + std::make_shared(NestedLoopJoin{ + std::make_shared(SeqScan{"employees"}), + std::make_shared(SeqScan{"departments"}), + JoinType::kLeft, + BinaryExpression{ + std::make_shared(Attribute{"employees", "dept_id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"departments", "id"}), + }, + }), + BinaryExpression{ + std::make_shared(Attribute{"departments", "id"}), + BinaryOp::kGt, + std::make_shared(IntConst{3}), + }, + }), + {Attribute{"employees", "id"}, Attribute{"departments", "id"}}, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, IndexSeek) { + PhysicalPlanNode plan = IndexSeek{ + "users", + std::nullopt, + BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kGt, + std::make_shared(IntConst{42}), + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); + EXPECT_THAT(Serialize(plan), Eq("(IndexSeek (> (attr users id) 42) users)")); +} + +TEST(PlanSerializerTest, HashJoin) { + PhysicalPlanNode plan = HashJoin{ + std::make_shared(SeqScan{"a"}), + std::make_shared(SeqScan{"b"}), + JoinType::kInner, + BinaryExpression{ + std::make_shared(Attribute{"a", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"b", "id"}), + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerTest, MergeJoin) { + PhysicalPlanNode plan = MergeJoin{ + std::make_shared(SeqScan{"a"}), + std::make_shared(SeqScan{"b"}), + JoinType::kLeft, + BinaryExpression{ + std::make_shared(Attribute{"a", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"b", "id"}), + }, + }; + EXPECT_THAT(RoundTrip(plan), Eq(plan)); +} + +TEST(PlanSerializerDotTest, SmokeTest) { + PhysicalPlanNode plan = NestedLoopJoin{ + std::make_shared(SeqScan{"a"}), + std::make_shared(SeqScan{"b"}), + JoinType::kInner, + BinaryExpression{ + std::make_shared(Attribute{"a", "x"}), + BinaryOp::kEq, + std::make_shared(Attribute{"b", "x"}), + }, + }; + auto dot = SerializeDot(plan); + EXPECT_THAT(dot, ::testing::HasSubstr("digraph G")); + EXPECT_THAT(dot, ::testing::HasSubstr("rankdir=BT")); + EXPECT_THAT(dot, ::testing::HasSubstr("SeqScan")); + EXPECT_THAT(dot, ::testing::HasSubstr("ON a.x = b.x")); + EXPECT_THAT(dot, ::testing::HasSubstr("n0 -> n2")); + EXPECT_THAT(dot, ::testing::HasSubstr("n1 -> n2")); +} + +TEST(PlanSerializerDotTest, RendersMetadataWhenPresent) { + PhysicalPlanNode plan = SeqScan{"users"}; + plan.metadata = PlanNodeMetadata{.cardinality = 42, .local_cost = 4200}; + + auto dot = SerializeDot(plan); + + EXPECT_THAT(dot, ::testing::HasSubstr("SeqScan\\nusers\\ncard=42\\ncost=4200")); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp index 26dc178..c78105d 100644 --- a/src/stewkk/sql/logic/executor/sequential_scan.cpp +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -1,54 +1,336 @@ #include +#include +#include +#include +#include #include +#include #include #include +#include +#include +#include +#include +#include #include +#include +#include #include #include +#include namespace stewkk::sql { namespace { +constexpr std::array kIntSortedIndexMagic{'I', '9', 'I', 'D', 'X', '0', '0', '1'}; + +struct IndexMeta { + std::string table; + std::string column; + std::string type; + std::string file; +}; + +struct IntIndexEntry { + int64_t key; + uint64_t row_offset; +}; + Type GetTypeFromString(const std::string& s) { if (s == "int") { return Type::kInt; } + if (s == "string") { + return Type::kString; + } std::unreachable(); } -Value BuildValueFromString(Type type, const std::string& table, const std::string attr_name, const std::string& value) { +Value BuildValueFromString(Type type, const std::string& value) { if (value == "NULL") { return Value{true}; } switch (type) { case Type::kInt: return Value{false, std::stoi(value)}; + case Type::kString: + return Value{false, {.string_id = InternString(value)}}; default: std::unreachable(); } } +std::vector ParseCsvLine(const std::string& line) { + auto input = std::string_view{line}; + if (!input.empty() && input.back() == '\r') { + input.remove_suffix(1); + } + std::vector fields; + std::string field; + bool in_quotes = false; + for (size_t i = 0; i < input.size(); ++i) { + char c = input[i]; + if (in_quotes) { + if (c == '"') { + if (i + 1 < input.size() && input[i + 1] == '"') { + field.push_back('"'); + ++i; + } else { + in_quotes = false; + } + } else { + field.push_back(c); + } + } else if (c == '"') { + in_quotes = true; + } else if (c == ',') { + fields.push_back(std::move(field)); + field.clear(); + } else { + field.push_back(c); + } + } + fields.push_back(std::move(field)); + return fields; +} + Tuple ParseTuple(const std::string& line, - const AttributesInfo& attributes, - const std::string& table_name) { - return line | std::views::split(',') | std::views::enumerate - | std::views::transform([&attributes, &table_name](const auto& attr) { - const auto& [index, value_range] = attr; - auto value = value_range | std::ranges::to(); + const AttributesInfo& attributes) { + auto fields = ParseCsvLine(line); + if (fields.size() != attributes.size()) { + throw std::runtime_error{std::format("CSV row has {} fields, expected {}", fields.size(), attributes.size())}; + } + return fields | std::views::enumerate + | std::views::transform([&attributes](const auto& attr) { + const auto& [index, value] = attr; const auto& [_, attr_name, type] = attributes[index]; - return BuildValueFromString(type, table_name, attr_name, value); + return BuildValueFromString(type, value); }) - | std::ranges::to(); + | std::ranges::to(); +} + +AttributesInfo ReadAttributes(std::istream& input, const std::string& output_table_name) { + std::string line; + if (!std::getline(input, line)) { + throw std::runtime_error{"CSV file is empty"}; + } + return line | std::views::split(',') | std::views::transform([&output_table_name](const auto& attr) { + auto mid = std::find(attr.begin(), attr.end(), ':'); + auto attr_name = std::string{attr.begin(), mid}; + auto attr_type = GetTypeFromString(std::string{mid + 1, attr.end()}); + return AttributeInfo{output_table_name, std::move(attr_name), attr_type}; + }) + | std::ranges::to(); +} + +std::vector ReadIndexMeta(const std::filesystem::path& path) { + std::ifstream input{path}; + if (!input) { + throw std::runtime_error{std::format("index metadata file not found: {}", path.string())}; + } + + std::vector result; + std::string line; + while (std::getline(input, line)) { + auto first = line.find_first_not_of(" \t\r\n"); + if (first == std::string::npos || line[first] == '#') { + continue; + } + + std::istringstream fields{line}; + IndexMeta meta; + if (!(fields >> meta.table >> meta.column >> meta.type >> meta.file)) { + throw std::runtime_error{std::format("malformed index metadata line: {}", line)}; + } + result.push_back(std::move(meta)); + } + return result; +} + +std::optional FindIndexMeta(const std::filesystem::path& dir, + const std::string& table, + const std::string& column) { + for (auto meta : ReadIndexMeta(dir / "indexes.meta")) { + if (meta.table == table && meta.column == column && meta.type == "sorted") { + return meta; + } + } + return std::nullopt; +} + +BinaryOp InvertComparison(BinaryOp op) { + switch (op) { + case BinaryOp::kGt: + return BinaryOp::kLt; + case BinaryOp::kLt: + return BinaryOp::kGt; + case BinaryOp::kLe: + return BinaryOp::kGe; + case BinaryOp::kGe: + return BinaryOp::kLe; + case BinaryOp::kEq: + return BinaryOp::kEq; + default: + throw std::runtime_error{"unsupported index seek operator"}; + } +} + +struct SeekCondition { + std::string column; + BinaryOp op; + int64_t key; +}; + +std::optional ExtractSeekCondition(const Expression& predicate, + const std::string& table_name, + const std::string& output_table_name) { + const auto* binary = std::get_if(&predicate); + if (!binary) { + return std::nullopt; + } + if (binary->binop == BinaryOp::kAnd) { + if (auto lhs = ExtractSeekCondition(*binary->lhs, table_name, output_table_name)) { + return lhs; + } + return ExtractSeekCondition(*binary->rhs, table_name, output_table_name); + } + if (!std::ranges::contains(std::vector{BinaryOp::kEq, BinaryOp::kLt, BinaryOp::kLe, + BinaryOp::kGt, BinaryOp::kGe}, binary->binop)) { + return std::nullopt; + } + + auto attr_matches = [&](const Attribute& attr) { + return attr.table.empty() || attr.table == table_name || attr.table == output_table_name; + }; + if (const auto* attr = std::get_if(binary->lhs.get()); + attr && attr_matches(*attr)) { + if (const auto* key = std::get_if(binary->rhs.get())) { + return SeekCondition{attr->name, binary->binop, *key}; + } + } + if (const auto* attr = std::get_if(binary->rhs.get()); + attr && attr_matches(*attr)) { + if (const auto* key = std::get_if(binary->lhs.get())) { + return SeekCondition{attr->name, InvertComparison(binary->binop), *key}; + } + } + return std::nullopt; +} + +std::vector ReadIndexEntries(const std::filesystem::path& path) { + std::ifstream input{path, std::ios::binary}; + if (!input) { + throw std::runtime_error{std::format("index file not found: {}", path.string())}; + } + + std::array magic{}; + uint64_t count = 0; + input.read(magic.data(), magic.size()); + input.read(reinterpret_cast(&count), sizeof(count)); + if (!input || magic != kIntSortedIndexMagic) { + throw std::runtime_error{std::format("invalid int sorted index file: {}", path.string())}; + } + + std::vector entries(count); + input.read(reinterpret_cast(entries.data()), + static_cast(entries.size() * sizeof(IntIndexEntry))); + if (!input) { + throw std::runtime_error{std::format("truncated index file: {}", path.string())}; + } + return entries; +} + +void WriteIndexEntries(const std::filesystem::path& path, const std::vector& entries) { + std::ofstream output{path, std::ios::binary | std::ios::trunc}; + if (!output) { + throw std::runtime_error{std::format("cannot write index file: {}", path.string())}; + } + + uint64_t count = entries.size(); + output.write(kIntSortedIndexMagic.data(), kIntSortedIndexMagic.size()); + output.write(reinterpret_cast(&count), sizeof(count)); + output.write(reinterpret_cast(entries.data()), + static_cast(entries.size() * sizeof(IntIndexEntry))); +} + +void BuildIntSortedIndex(const std::filesystem::path& csv_path, + const std::filesystem::path& index_path, + const std::string& output_table_name, + const std::string& column) { + std::ifstream input{csv_path}; + if (!input) { + throw std::runtime_error{std::format("CSV file not found: {}", csv_path.string())}; + } + + auto attrs = ReadAttributes(input, output_table_name); + auto it = std::find_if(attrs.begin(), attrs.end(), [&](const AttributeInfo& attr) { + return attr.name == column; + }); + if (it == attrs.end()) { + throw std::runtime_error{std::format("indexed column not found: {}", column)}; + } + if (it->type != Type::kInt) { + throw std::runtime_error{std::format("only int indexes are supported: {}", column)}; + } + size_t column_index = static_cast(it - attrs.begin()); + + std::vector entries; + std::string line; + while (input) { + auto row_offset = input.tellg(); + if (!std::getline(input, line)) { + break; + } + auto tuple = ParseTuple(line, attrs); + const auto& key = tuple[column_index]; + if (!key.is_null) { + entries.push_back(IntIndexEntry{ + .key = key.value.int_value, + .row_offset = static_cast(row_offset), + }); + } + } + + std::ranges::sort(entries, [](const IntIndexEntry& lhs, const IntIndexEntry& rhs) { + return std::tie(lhs.key, lhs.row_offset) < std::tie(rhs.key, rhs.row_offset); + }); + WriteIndexEntries(index_path, entries); +} + +std::pair::const_iterator, std::vector::const_iterator> +FindIndexRange(const std::vector& entries, const SeekCondition& condition) { + auto lower = [&](int64_t key) { + return std::ranges::lower_bound(entries, key, {}, &IntIndexEntry::key); + }; + auto upper = [&](int64_t key) { + return std::ranges::upper_bound(entries, key, {}, &IntIndexEntry::key); + }; + + switch (condition.op) { + case BinaryOp::kEq: + return {lower(condition.key), upper(condition.key)}; + case BinaryOp::kLt: + return {entries.begin(), lower(condition.key)}; + case BinaryOp::kLe: + return {entries.begin(), upper(condition.key)}; + case BinaryOp::kGt: + return {upper(condition.key), entries.end()}; + case BinaryOp::kGe: + return {lower(condition.key), entries.end()}; + default: + throw std::runtime_error{"unsupported index seek operator"}; + } } } // namespace boost::asio::awaitable> CsvDirSequentialScanner::operator()( - const std::string& table_name, AttributesInfoChannel& attrs_chan, + const std::string& table_name, const std::string& output_table_name, + AttributesInfoChannel& attrs_chan, TuplesChannel& tuples_chan) const { #ifdef DEBUG std::clog << "Executing sequential scan\n"; @@ -56,14 +338,7 @@ boost::asio::awaitable> CsvDirSequentialScanner::operator()( auto path = std::format("{}/{}.csv", dir, table_name); std::ifstream input{std::move(path)}; std::string line; - std::getline(input, line); - auto attributes = line | std::views::split(',') | std::views::transform([&table_name](const auto& attr) { - auto mid = std::find(attr.begin(), attr.end(), ':'); - auto attr_name = std::string{attr.begin(), mid}; - auto attr_type = GetTypeFromString(std::string{mid + 1, attr.end()}); - return AttributeInfo{table_name, std::move(attr_name), attr_type}; - }) - | std::ranges::to(); + auto attributes = ReadAttributes(input, output_table_name); co_await attrs_chan.async_send(boost::system::error_code{}, attributes, boost::asio::use_awaitable); @@ -72,7 +347,7 @@ boost::asio::awaitable> CsvDirSequentialScanner::operator()( Tuples buf; buf.reserve(kBufSize); while (std::getline(input, line)) { - auto tuple = ParseTuple(line, attributes, table_name); + auto tuple = ParseTuple(line, attributes); buf.emplace_back(std::move(tuple)); #ifdef DEBUG std::clog << std::format("buf size is {}\n", buf.size()); @@ -104,4 +379,86 @@ boost::asio::awaitable> CsvDirSequentialScanner::operator()( co_return Ok(); } +boost::asio::awaitable> CsvDirIndexedScanner::operator()( + const std::string& table_name, const std::string& output_table_name, + const Expression& predicate, + const std::optional& index_column, + AttributesInfoChannel& attrs_chan, + TuplesChannel& tuples_chan) const { +#ifdef DEBUG + std::clog << "Executing index seek\n"; +#endif + auto condition = ExtractSeekCondition(predicate, table_name, output_table_name); + if (index_column && (!condition || condition->column != *index_column)) { + std::vector conjuncts; + CollectConjuncts(predicate, conjuncts); + condition = std::nullopt; + for (const auto& conjunct : conjuncts) { + auto candidate = ExtractSeekCondition(conjunct, table_name, output_table_name); + if (candidate && candidate->column == *index_column) { + condition = std::move(candidate); + break; + } + } + } + if (!condition) { + throw std::runtime_error{"IndexSeek predicate must be a comparison between one indexed int column and an int constant"}; + } + + std::filesystem::path data_dir{dir}; + auto meta = FindIndexMeta(data_dir, table_name, condition->column); + if (!meta) { + throw std::runtime_error{std::format("no sorted int index declared for {}.{}", table_name, condition->column)}; + } + + auto csv_path = data_dir / std::format("{}.csv", table_name); + auto index_path = data_dir / meta->file; + if (!std::filesystem::exists(index_path)) { + BuildIntSortedIndex(csv_path, index_path, output_table_name, condition->column); + } + auto entries = ReadIndexEntries(index_path); + + std::ifstream input{csv_path}; + if (!input) { + throw std::runtime_error{std::format("CSV file not found: {}", csv_path.string())}; + } + auto attributes = ReadAttributes(input, output_table_name); + auto filter = InterpretedExpressionExecutor{co_await boost::asio::this_coro::executor}; + auto residual = co_await filter.GetExpressionExecutor(predicate, attributes); + + co_await attrs_chan.async_send(boost::system::error_code{}, attributes, + boost::asio::use_awaitable); + attrs_chan.close(); + + auto [begin, end] = FindIndexRange(entries, *condition); + Tuples buf; + buf.reserve(kBufSize); + std::string line; + for (auto it = begin; it != end; ++it) { + input.clear(); + input.seekg(static_cast(it->row_offset)); + if (!std::getline(input, line)) { + throw std::runtime_error{"failed to read indexed CSV row"}; + } + auto tuple = ParseTuple(line, attributes); + if (!ApplyFilter(tuple, attributes, residual)) { + continue; + } + buf.emplace_back(std::move(tuple)); + if (buf.size() == kBufSize) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + buf.clear(); + } + } + + if (!buf.empty()) { + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + } + tuples_chan.close(); + + co_return Ok(); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_aggregation.cpp b/src/stewkk/sql/logic/implementation_rules/implement_aggregation.cpp new file mode 100644 index 0000000..cf8a586 --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_aggregation.cpp @@ -0,0 +1,37 @@ +#include + +#include + +namespace stewkk::sql { + +bool ImplementAggregation::IsApplicable(utils::NotNull expr) { + return std::holds_alternative(expr->root_operator) + || std::holds_alternative(expr->root_operator) + || std::holds_alternative(expr->root_operator); +} + +std::vector> ImplementAggregation::Apply(utils::NotNull expr, Memo&) { + if (auto* partial = std::get_if(&expr->root_operator)) { + return {expr->group->AddPhysicalExpr( + physical::PartialAggregation{partial->source, partial->group_by, partial->aggregates})}; + } + if (auto* final = std::get_if(&expr->root_operator)) { + return {expr->group->AddPhysicalExpr( + physical::FinalAggregation{final->source, final->group_by, final->aggregates})}; + } + + auto& agg = std::get(expr->root_operator); + std::vector> result{ + expr->group->AddPhysicalExpr( + physical::Aggregation{agg.source, agg.group_by, agg.aggregates})}; + if (!agg.group_by.empty() + && std::ranges::all_of(agg.group_by, [](const Expression& group_expr) { + return std::holds_alternative(group_expr); + })) { + result.push_back(expr->group->AddPhysicalExpr( + physical::StreamAggregation{agg.source, agg.group_by, agg.aggregates})); + } + return result; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_cross_join.cpp b/src/stewkk/sql/logic/implementation_rules/implement_cross_join.cpp new file mode 100644 index 0000000..d517ee3 --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_cross_join.cpp @@ -0,0 +1,14 @@ +#include + +namespace stewkk::sql { + +bool ImplementCrossJoin::IsApplicable(utils::NotNull expr) { + return std::holds_alternative(expr->root_operator); +} + +std::vector> ImplementCrossJoin::Apply(utils::NotNull expr, Memo&) { + auto& cj = std::get(expr->root_operator); + return {expr->group->AddPhysicalExpr(physical::NestedLoopCrossJoin{cj.lhs, cj.rhs})}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_filter.cpp b/src/stewkk/sql/logic/implementation_rules/implement_filter.cpp new file mode 100644 index 0000000..38baaaa --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_filter.cpp @@ -0,0 +1,14 @@ +#include + +namespace stewkk::sql { + +bool ImplementFilter::IsApplicable(utils::NotNull expr) { + return std::holds_alternative(expr->root_operator); +} + +std::vector> ImplementFilter::Apply(utils::NotNull expr, Memo&) { + auto& filter = std::get(expr->root_operator); + return {expr->group->AddPhysicalExpr(physical::Filter{filter.source, filter.predicate})}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_hash_join.cpp b/src/stewkk/sql/logic/implementation_rules/implement_hash_join.cpp new file mode 100644 index 0000000..59fb501 --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_hash_join.cpp @@ -0,0 +1,40 @@ +#include + +#include +#include + +#include + +namespace stewkk::sql { + +namespace { + +bool IsSimpleEquiJoin(const Expression& qual) { + const auto* bin = std::get_if(&qual); + if (!bin || bin->binop != BinaryOp::kEq) return false; + return std::holds_alternative(*bin->lhs) + && std::holds_alternative(*bin->rhs); +} + +bool HasEquiJoinConjunct(const Expression& qual) { + std::vector conjuncts; + CollectConjuncts(qual, conjuncts); + return std::ranges::any_of(conjuncts, IsSimpleEquiJoin); +} + +} // namespace + +bool ImplementHashJoin::IsApplicable(utils::NotNull expr) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& join = std::get(expr->root_operator); + if (join.type != JoinType::kInner) return false; + return HasEquiJoinConjunct(join.qual); +} + +std::vector> ImplementHashJoin::Apply(utils::NotNull expr, Memo&) { + auto& join = std::get(expr->root_operator); + return {expr->group->AddPhysicalExpr( + physical::HashJoin{join.lhs, join.rhs, join.type, join.qual})}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_index_seek.cpp b/src/stewkk/sql/logic/implementation_rules/implement_index_seek.cpp new file mode 100644 index 0000000..6769399 --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_index_seek.cpp @@ -0,0 +1,130 @@ +#include + +#include +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +namespace { + +bool IsSupportedComparison(BinaryOp op) { + return std::ranges::contains(std::vector{BinaryOp::kEq, BinaryOp::kLt, BinaryOp::kLe, + BinaryOp::kGt, BinaryOp::kGe}, op); +} + +const logical::Table* SourceTable(utils::NotNull group) { + auto exprs = group->GetLogicalExprs(); + if (exprs.size() != 1) { + return nullptr; + } + return std::get_if(&exprs.front()->root_operator); +} + +bool AttrMatchesTable(const Attribute& attr, const logical::Table& table) { + auto visible = std::string_view{VisibleName(table)}; + return attr.table.empty() || attr.table == table.name || attr.table == visible; +} + +bool IsIndexedComparison(const Expression& expr, const logical::Table& table, + const IndexCatalog& indexes) { + const auto* binary = std::get_if(&expr); + if (!binary || !IsSupportedComparison(binary->binop)) { + return false; + } + + if (const auto* attr = std::get_if(binary->lhs.get()); + attr && AttrMatchesTable(*attr, table) && std::holds_alternative(*binary->rhs)) { + return indexes.HasSortedIndex(table.name, attr->name); + } + if (const auto* attr = std::get_if(binary->rhs.get()); + attr && AttrMatchesTable(*attr, table) && std::holds_alternative(*binary->lhs)) { + return indexes.HasSortedIndex(table.name, attr->name); + } + return false; +} + +void CollectIndexedColumns(const Expression& expr, const logical::Table& table, + const IndexCatalog& indexes, std::vector& columns) { + const auto* binary = std::get_if(&expr); + if (!binary) { + return; + } + if (binary->binop == BinaryOp::kAnd) { + CollectIndexedColumns(*binary->lhs, table, indexes, columns); + CollectIndexedColumns(*binary->rhs, table, indexes, columns); + return; + } + if (!IsSupportedComparison(binary->binop)) { + return; + } + + auto add_if_indexed = [&](const Attribute& attr, const Expression& other) { + if (!AttrMatchesTable(attr, table) || !std::holds_alternative(other)) { + return; + } + if (!indexes.HasSortedIndex(table.name, attr.name)) { + return; + } + if (!std::ranges::contains(columns, attr.name)) { + columns.push_back(attr.name); + } + }; + + if (const auto* attr = std::get_if(binary->lhs.get())) { + add_if_indexed(*attr, *binary->rhs); + } + if (const auto* attr = std::get_if(binary->rhs.get())) { + add_if_indexed(*attr, *binary->lhs); + } +} + +} // namespace + +ImplementIndexSeek::ImplementIndexSeek(IndexCatalog indexes) + : indexes_(std::move(indexes)) {} + +bool HasCompatibleIndexSeek(const logical::Filter& filter, const IndexCatalog& indexes) { + const auto* table = SourceTable(filter.source); + if (!table) { + return false; + } + + std::vector conjuncts; + CollectConjuncts(filter.predicate, conjuncts); + return std::ranges::any_of(conjuncts, [&](const Expression& conjunct) { + return IsIndexedComparison(conjunct, *table, indexes); + }); +} + +bool ImplementIndexSeek::IsApplicable(utils::NotNull expr) { + if (!std::holds_alternative(expr->root_operator)) { + return false; + } + return HasCompatibleIndexSeek(std::get(expr->root_operator), indexes_); +} + +std::vector> ImplementIndexSeek::Apply(utils::NotNull expr, Memo&) { + auto& filter = std::get(expr->root_operator); + const auto* table = SourceTable(filter.source); + std::vector columns; + CollectIndexedColumns(filter.predicate, *table, indexes_, columns); + + std::vector> result; + result.reserve(columns.size()); + for (const auto& column : columns) { + result.push_back(expr->group->AddPhysicalExpr(physical::IndexSeek{ + .table = table->name, + .alias = table->alias, + .predicate = filter.predicate, + .index_column = column, + })); + } + return result; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_join.cpp b/src/stewkk/sql/logic/implementation_rules/implement_join.cpp new file mode 100644 index 0000000..89ff20c --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_join.cpp @@ -0,0 +1,15 @@ +#include + +namespace stewkk::sql { + +bool ImplementJoin::IsApplicable(utils::NotNull expr) { + return std::holds_alternative(expr->root_operator); +} + +std::vector> ImplementJoin::Apply(utils::NotNull expr, Memo&) { + auto& join = std::get(expr->root_operator); + return {expr->group->AddPhysicalExpr( + physical::NestedLoopJoin{join.lhs, join.rhs, join.type, join.qual})}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_merge_join.cpp b/src/stewkk/sql/logic/implementation_rules/implement_merge_join.cpp new file mode 100644 index 0000000..130e1bf --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_merge_join.cpp @@ -0,0 +1,28 @@ +#include + +namespace stewkk::sql { + +namespace { + +bool IsSimpleEquiJoin(const Expression& qual) { + const auto* bin = std::get_if(&qual); + if (!bin || bin->binop != BinaryOp::kEq) return false; + return std::holds_alternative(*bin->lhs) + && std::holds_alternative(*bin->rhs); +} + +} // namespace + +bool ImplementMergeJoin::IsApplicable(utils::NotNull expr) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& join = std::get(expr->root_operator); + return IsSimpleEquiJoin(join.qual); +} + +std::vector> ImplementMergeJoin::Apply(utils::NotNull expr, Memo&) { + auto& join = std::get(expr->root_operator); + return {expr->group->AddPhysicalExpr( + physical::MergeJoin{join.lhs, join.rhs, join.type, join.qual})}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_projection.cpp b/src/stewkk/sql/logic/implementation_rules/implement_projection.cpp new file mode 100644 index 0000000..26d6891 --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_projection.cpp @@ -0,0 +1,15 @@ +#include + +namespace stewkk::sql { + +bool ImplementProjection::IsApplicable(utils::NotNull expr) { + return std::holds_alternative(expr->root_operator); +} + +std::vector> ImplementProjection::Apply(utils::NotNull expr, Memo&) { + auto& proj = std::get(expr->root_operator); + return {expr->group->AddPhysicalExpr( + physical::Projection{proj.source, proj.expressions, proj.aliases})}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/implementation_rules/implement_table.cpp b/src/stewkk/sql/logic/implementation_rules/implement_table.cpp new file mode 100644 index 0000000..c12f35d --- /dev/null +++ b/src/stewkk/sql/logic/implementation_rules/implement_table.cpp @@ -0,0 +1,14 @@ +#include + +namespace stewkk::sql { + +bool ImplementTable::IsApplicable(utils::NotNull expr) { + return std::holds_alternative(expr->root_operator); +} + +std::vector> ImplementTable::Apply(utils::NotNull expr, Memo&) { + auto& table = std::get(expr->root_operator); + return {expr->group->AddPhysicalExpr(physical::SeqScan{table.name, table.alias})}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/cardinality.cpp b/src/stewkk/sql/logic/optimizer/cardinality.cpp new file mode 100644 index 0000000..4f4ed0e --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/cardinality.cpp @@ -0,0 +1,139 @@ +#include + +#include + +#include + +namespace stewkk::sql { + +namespace { + +constexpr double kEqualitySelectivity = 0.10; +constexpr double kRangeSelectivity = 0.50; +constexpr double kUnknownSelectivity = 0.50; + +bool IsConstant(const Expression& expr) { + return std::holds_alternative(expr) + || std::holds_alternative(expr) + || std::holds_alternative(expr); +} + +double EstimateFilterSelectivity(const Expression& predicate) { + return std::visit(utils::Overloaded{ + [](const BinaryExpression& b) { + const auto lhs = EstimateFilterSelectivity(*b.lhs); + const auto rhs = EstimateFilterSelectivity(*b.rhs); + switch (b.binop) { + case BinaryOp::kAnd: + return lhs * rhs; + case BinaryOp::kOr: + return lhs + rhs - lhs * rhs; + case BinaryOp::kEq: + return (std::holds_alternative(*b.lhs) && IsConstant(*b.rhs)) + || (IsConstant(*b.lhs) && std::holds_alternative(*b.rhs)) + ? kEqualitySelectivity + : kUnknownSelectivity; + case BinaryOp::kGt: + case BinaryOp::kLt: + case BinaryOp::kLe: + case BinaryOp::kGe: + return kRangeSelectivity; + default: + return kUnknownSelectivity; + } + }, + [](const UnaryExpression& u) { + return u.op == UnaryOp::kNot + ? 1.0 - EstimateFilterSelectivity(*u.child) + : kUnknownSelectivity; + }, + [](const InExpression& in) { + const auto selectivity = std::min(0.50, in.values.size() * kEqualitySelectivity); + return in.negated ? 1.0 - selectivity : selectivity; + }, + [](const auto&) { return kUnknownSelectivity; }, + }, predicate); +} + +double JoinSelectivity(const Expression& qual, int64_t lhs_card, int64_t rhs_card) { + const auto* b = std::get_if(&qual); + if (!b) return 1.0; + if (b->binop == BinaryOp::kAnd) { + return JoinSelectivity(*b->lhs, lhs_card, rhs_card) + * JoinSelectivity(*b->rhs, lhs_card, rhs_card); + } + if (b->binop == BinaryOp::kEq + && std::holds_alternative(*b->lhs) + && std::holds_alternative(*b->rhs)) { + auto m = std::max(1, std::max(lhs_card, rhs_card)); + return 1.0 / static_cast(m); + } + return 1.0; +} + +} // namespace + +CardinalityEstimates::CardinalityEstimates(std::unordered_map table_sizes) + : table_sizes_(std::move(table_sizes)) {} + +int64_t CardinalityEstimates::GetCardinality(utils::NotNull group) { + if (auto it = cache_.find(group.get()); it != cache_.end()) { + return it->second; + } + auto cardinality = GetCardinality(group->GetLogicalExprs().front()->root_operator); + cache_[group.get()] = cardinality; + return cardinality; +} + +int64_t CardinalityEstimates::GetCardinality(const LogicalOperator& op) { + return std::visit(utils::Overloaded{ + [this](const logical::Table& t) -> int64_t { + if (auto it = table_sizes_.find(t.name); it != table_sizes_.end()) { + return it->second; + } + return 10; + }, + [this](const logical::Filter& f) -> int64_t { + auto source_cardinality = GetCardinality(f.source); + auto filtered = static_cast( + source_cardinality * EstimateFilterSelectivity(f.predicate)); + return std::max(1, filtered); + }, + [this](const logical::Projection& p) -> int64_t { + return GetCardinality(p.source); + }, + [this](const logical::Aggregation& a) -> int64_t { + auto source_cardinality = GetCardinality(a.source); + if (a.group_by.empty()) { + return 1; + } + return source_cardinality; + }, + [this](const logical::PartialAggregation& a) -> int64_t { + auto source_cardinality = GetCardinality(a.source); + if (a.group_by.empty()) { + return 1; + } + return source_cardinality; + }, + [this](const logical::FinalAggregation& a) -> int64_t { + auto source_cardinality = GetCardinality(a.source); + if (a.group_by.empty()) { + return 1; + } + return source_cardinality; + }, + [this](const logical::CrossJoin& j) -> int64_t { + return GetCardinality(j.lhs) * GetCardinality(j.rhs); + }, + [this](const logical::Join& j) -> int64_t { + auto lhs_c = GetCardinality(j.lhs); + auto rhs_c = GetCardinality(j.rhs); + auto out = static_cast(lhs_c) * static_cast(rhs_c) + * JoinSelectivity(j.qual, lhs_c, rhs_c); + return std::max(1, static_cast(out)); + }, + }, op); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/group.cpp b/src/stewkk/sql/logic/optimizer/group.cpp new file mode 100644 index 0000000..9d63af6 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/group.cpp @@ -0,0 +1,25 @@ +#include + +namespace stewkk::sql { + +utils::NotNull Group::AddLogicalExpr(LogicalOperator root_operator) { + return &logical_exprs_.emplace_back(std::move(root_operator), this); +} + +utils::NotNull Group::AddPhysicalExpr(PhysicalOperator root_operator, bool is_enforcer) { + return &physical_exprs_.emplace_back(std::move(root_operator), this, is_enforcer); +} + +Group::LogicalExprs Group::GetLogicalExprs() { + return std::views::transform(logical_exprs_, ToNotNull{}); +} + +Group::PhysicalExprs Group::GetPhysicalExprs() { + return std::views::transform(physical_exprs_, ToNotNullPhysical{}); +} + +size_t Group::GetId() const { + return id_; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/memo.cpp b/src/stewkk/sql/logic/optimizer/memo.cpp new file mode 100644 index 0000000..8393763 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/memo.cpp @@ -0,0 +1,165 @@ +#include +#include + +namespace stewkk::sql { + +namespace { + +std::string ToKey(const LogicalOperator& op) { + return std::visit(utils::Overloaded{ + [](const logical::Table& t) { + return "Table(" + t.name + "," + std::string{VisibleName(t)} + ")"; + }, + [](const logical::Filter& f) { + return "Filter(" + ToString(f.predicate) + "," + std::to_string(f.source->GetId()) + ")"; + }, + [](const logical::Projection& p) { + std::string exprs; + for (const auto& e : p.expressions) { + exprs += ToString(e) + ","; + } + std::string aliases; + for (const auto& alias : p.aliases) { + aliases += alias.value_or("") + ","; + } + return "Projection(" + exprs + aliases + std::to_string(p.source->GetId()) + ")"; + }, + [](const logical::Aggregation& a) { + std::string group_by; + for (const auto& e : a.group_by) { + group_by += ToString(e) + ","; + } + std::string aggregates; + for (const auto& e : a.aggregates) { + aggregates += ToString(e) + ","; + } + return "Aggregation(" + group_by + ";" + aggregates + ";" + + std::to_string(a.source->GetId()) + ")"; + }, + [](const logical::PartialAggregation& a) { + std::string group_by; + for (const auto& e : a.group_by) { + group_by += ToString(e) + ","; + } + std::string aggregates; + for (const auto& e : a.aggregates) { + aggregates += ToString(e) + ","; + } + return "PartialAggregation(" + group_by + ";" + aggregates + ";" + + std::to_string(a.source->GetId()) + ")"; + }, + [](const logical::FinalAggregation& a) { + std::string group_by; + for (const auto& e : a.group_by) { + group_by += ToString(e) + ","; + } + std::string aggregates; + for (const auto& e : a.aggregates) { + aggregates += ToString(e) + ","; + } + return "FinalAggregation(" + group_by + ";" + aggregates + ";" + + std::to_string(a.source->GetId()) + ")"; + }, + [](const logical::CrossJoin& j) { + return "CrossJoin(" + std::to_string(j.lhs->GetId()) + "," + std::to_string(j.rhs->GetId()) + ")"; + }, + [](const logical::Join& j) { + return "Join(" + ToString(j.type) + "," + ToString(j.qual) + "," + + std::to_string(j.lhs->GetId()) + "," + std::to_string(j.rhs->GetId()) + ")"; + }, + }, op); +} + +} // namespace + +size_t Memo::GroupCount() const { + return groups_.size(); +} + +Memo::ScopedLogicalProvenance::ScopedLogicalProvenance( + Memo& memo, LogicalProvenance provenance) + : memo_(memo), previous_(memo.current_provenance_) { + memo_.current_provenance_ = provenance; +} + +Memo::ScopedLogicalProvenance::~ScopedLogicalProvenance() { + memo_.current_provenance_ = previous_; +} + +LogicalExpr* Memo::GetGroup(LogicalOperator root_operator) const { + auto key = ToKey(root_operator); + return GetGroup(key); +} + +LogicalExpr* Memo::GetGroup(const std::string& key) const { + auto it = expr_index_.find(key); + if (it != expr_index_.end()) { + return it->second; + } + return nullptr; +} + +utils::NotNull Memo::AddGroup(LogicalOperator root_operator) { + auto key = ToKey(root_operator); + if (auto g = GetGroup(key); g) { + return g; + } + auto& group = groups_.emplace_back(Group(groups_.size())); + auto expr = group.AddLogicalExpr(std::move(root_operator)); + SetProvenanceIfNew(expr); + expr_index_[key] = expr; + return expr; +} + +utils::NotNull Memo::AddLogicalExprToGroup(utils::NotNull group, + LogicalOperator root_operator) { + auto key = ToKey(root_operator); + if (auto g = GetGroup(key); g) { + return g; + } + auto expr = group->AddLogicalExpr(std::move(root_operator)); + SetProvenanceIfNew(expr); + expr_index_[key] = expr; + return expr; +} + +void Memo::SetProvenanceIfNew(utils::NotNull expr) { + if (!current_provenance_) return; + expr->provenance = LogicalExpr::Provenance{ + .rule_id = current_provenance_->rule_id, + .rule_name = current_provenance_->rule_name, + .source = current_provenance_->source, + }; +} + +utils::NotNull Memo::Populate(const Operator& op) { + return std::visit(utils::Overloaded{ + [this](const Table& t) { + return AddGroup(logical::Table{t.name, t.alias}); + }, + [this](const Filter& f) { + auto source = Populate(*f.source); + return AddGroup(logical::Filter{source->group, f.expr}); + }, + [this](const Projection& p) { + auto source = Populate(*p.source); + return AddGroup(logical::Projection{source->group, p.expressions, p.aliases}); + }, + [this](const Aggregation& a) { + auto source = Populate(*a.source); + return AddGroup(logical::Aggregation{source->group, a.group_by, a.aggregates}); + }, + [this](const CrossJoin& j) { + auto lhs = Populate(*j.lhs); + auto rhs = Populate(*j.rhs); + return AddGroup(logical::CrossJoin{lhs->group, rhs->group}); + }, + [this](const Join& j) { + auto lhs = Populate(*j.lhs); + auto rhs = Populate(*j.rhs); + return AddGroup(logical::Join{lhs->group, rhs->group, j.type, j.qual}); + }, + }, op); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/memo_test.cpp b/src/stewkk/sql/logic/optimizer/memo_test.cpp new file mode 100644 index 0000000..0dc6af7 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/memo_test.cpp @@ -0,0 +1,45 @@ +#include + +#include + +#include +#include + +namespace stewkk::sql { + +TEST(MemoTest, AddGroup) { + Memo memo; + EXPECT_EQ(memo.GroupCount(), 0); + + auto expr = memo.AddGroup(logical::Table{"t"}); + EXPECT_EQ(memo.GroupCount(), 1); + + auto exprs = expr->group->GetLogicalExprs(); + ASSERT_EQ(exprs.size(), 1); + EXPECT_TRUE(std::holds_alternative(exprs[0]->root_operator)); + EXPECT_EQ(std::get(exprs[0]->root_operator).name, "t"); +} + +TEST(MemoTest, AddSameGroupSecondReturnsIt) { + Memo memo; + auto first = memo.AddGroup(logical::Table{"t"}); + auto second = memo.AddGroup(logical::Table{"t"}); + + EXPECT_EQ(first->group, second->group); + EXPECT_EQ(memo.GroupCount(), 1); +} + +TEST(MemoTest, PopulateWithWholeQuery) { + std::stringstream s{"SELECT * FROM users, orders;"}; + Operator op = GetAST(s).value().op; + Memo memo; + + auto root = memo.Populate(op); + + ASSERT_EQ(memo.GroupCount(), 3); + const auto& cross_join = std::get(root->root_operator); + EXPECT_EQ(std::get(cross_join.lhs->GetLogicalExprs()[0]->root_operator).name, "users"); + EXPECT_EQ(std::get(cross_join.rhs->GetLogicalExprs()[0]->root_operator).name, "orders"); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/optimizer.cpp b/src/stewkk/sql/logic/optimizer/optimizer.cpp new file mode 100644 index 0000000..b0ab890 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/optimizer.cpp @@ -0,0 +1,898 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +namespace { + +static const std::vector> kEnforcers = [] { + std::vector> v; + v.push_back(std::make_unique()); + return v; +}(); + +struct JoinKeys { + Attribute lhs; + Attribute rhs; +}; + +bool AttrMatchesSchema(const Attribute& attr, const Attribute& schema_attr) { + return attr.name == schema_attr.name + && (attr.table.empty() || attr.table == schema_attr.table); +} + +bool AttrInSchema(const Attribute& attr, const Schema& schema) { + return std::ranges::any_of(schema, [&](const Attribute& schema_attr) { + return AttrMatchesSchema(attr, schema_attr); + }); +} + +std::optional ResolveSimpleJoinKeys(const Expression& qual, + utils::NotNull lhs, + utils::NotNull rhs, + SchemaCatalog& schema) { + const auto* bin = std::get_if(&qual); + if (!bin || bin->binop != BinaryOp::kEq) return std::nullopt; + const auto* a = std::get_if(bin->lhs.get()); + const auto* b = std::get_if(bin->rhs.get()); + if (!a || !b) return std::nullopt; + + auto lhs_schema = schema.GetSchema(lhs); + auto rhs_schema = schema.GetSchema(rhs); + + const bool a_lhs = AttrInSchema(*a, lhs_schema); + const bool a_rhs = AttrInSchema(*a, rhs_schema); + const bool b_lhs = AttrInSchema(*b, lhs_schema); + const bool b_rhs = AttrInSchema(*b, rhs_schema); + + if (a_lhs && !a_rhs && b_rhs && !b_lhs) { + return JoinKeys{*a, *b}; + } + if (b_lhs && !b_rhs && a_rhs && !a_lhs) { + return JoinKeys{*b, *a}; + } + return std::nullopt; +} + +std::optional ResolveJoinKeys(const Expression& qual, + utils::NotNull lhs, + utils::NotNull rhs, + SchemaCatalog& schema) { + std::vector conjuncts; + CollectConjuncts(qual, conjuncts); + for (const auto& conjunct : conjuncts) { + auto keys = ResolveSimpleJoinKeys(conjunct, lhs, rhs, schema); + if (keys) return keys; + } + return std::nullopt; +} + +PropertySet SortOn(const Attribute& attr) { + return PropertySet{SortProperty{SortOrder{{SortKey{attr.table, attr.name, Direction::kAsc}}}}}; +} + +PropertySet SortOn(const std::string& table, const std::string& column) { + return PropertySet{SortProperty{SortOrder{{SortKey{table, column, Direction::kAsc}}}}}; +} + +PropertySet SortOnGroupBy(const std::vector& group_by) { + SortOrder order; + for (const auto& expr : group_by) { + const auto* attr = std::get_if(&expr); + if (!attr) return PropertySet::Any(); + order.keys.push_back(SortKey{attr->table, attr->name, Direction::kAsc}); + } + if (order.keys.empty()) return PropertySet::Any(); + return PropertySet{SortProperty{std::move(order)}}; +} + +PropertySet RequiredInputProps(utils::NotNull expr, + PropertySet required, size_t child_index, + SchemaCatalog& schema) { + return std::visit(utils::Overloaded{ + [&](const physical::SeqScan&) { return PropertySet::Any(); }, + [&](const physical::IndexSeek&) { return PropertySet::Any(); }, + [&](const physical::Filter&) { return required; }, + [&](const physical::Projection&) { return required; }, + [&](const physical::NestedLoopJoin&) { return PropertySet::Any(); }, + [&](const physical::NestedLoopCrossJoin&) { return PropertySet::Any(); }, + [&](const physical::HashJoin&) -> PropertySet { + return PropertySet::Any(); + }, + [&](const physical::MergeJoin& j) -> PropertySet { + auto keys = ResolveJoinKeys(j.qual, j.lhs, j.rhs, schema); + if (!keys) return PropertySet::Any(); + return SortOn(child_index == 0 ? keys->lhs : keys->rhs); + }, + [&](const physical::Sort&) { return PropertySet::Any(); }, + [&](const physical::Aggregation&) { return PropertySet::Any(); }, + [&](const physical::StreamAggregation& a) { return SortOnGroupBy(a.group_by); }, + [&](const physical::PartialAggregation&) { return PropertySet::Any(); }, + [&](const physical::FinalAggregation&) { return PropertySet::Any(); }, + }, expr->root_operator); +} + +PropertySet DeriveOutputProps(utils::NotNull expr, + const std::vector& child_delivered, + SchemaCatalog& schema) { + return std::visit(utils::Overloaded{ + [&](const physical::SeqScan&) { return PropertySet::Any(); }, + [&](const physical::IndexSeek& s) { + return SortOn(s.alias.value_or(s.table), s.index_column); + }, + [&](const physical::Filter&) { return child_delivered[0]; }, + [&](const physical::Projection&) { return child_delivered[0]; }, + [&](const physical::NestedLoopJoin&) { return PropertySet::Any(); }, + [&](const physical::NestedLoopCrossJoin&) { return PropertySet::Any(); }, + [&](const physical::HashJoin&) { return PropertySet::Any(); }, + [&](const physical::MergeJoin& j) -> PropertySet { + auto keys = ResolveJoinKeys(j.qual, j.lhs, j.rhs, schema); + if (!keys) return PropertySet::Any(); + return SortOn(j.type == JoinType::kRight ? keys->rhs : keys->lhs); + }, + [&](const physical::Sort& s) { return PropertySet{SortProperty{s.keys}}; }, + [&](const physical::Aggregation&) { return PropertySet::Any(); }, + [&](const physical::StreamAggregation& a) { return SortOnGroupBy(a.group_by); }, + [&](const physical::PartialAggregation&) { return PropertySet::Any(); }, + [&](const physical::FinalAggregation&) { return PropertySet::Any(); }, + }, expr->root_operator); +} + +int64_t HashJoinCost(utils::NotNull build, utils::NotNull probe, + utils::NotNull output, CardinalityEstimates& cardinality, + SchemaCatalog& schema) { + constexpr int64_t kHashBuild = 100; + constexpr int64_t kHashProbe = 35; + constexpr int64_t kTupleCopy = 10; + return kHashBuild * cardinality.GetCardinality(build) * schema.GetWidth(build) + + kHashProbe * cardinality.GetCardinality(probe) + + kTupleCopy * cardinality.GetCardinality(output) * schema.GetWidth(output); +} + +int64_t MergeJoinCost(utils::NotNull lhs, utils::NotNull rhs, + utils::NotNull output, CardinalityEstimates& cardinality, + SchemaCatalog& schema) { + constexpr int64_t kMergeRead = 18; + constexpr int64_t kTupleCopy = 10; + return kMergeRead * (cardinality.GetCardinality(lhs) + cardinality.GetCardinality(rhs)) + + kTupleCopy * cardinality.GetCardinality(output) * schema.GetWidth(output); +} + +int64_t LowerBoundLocalCost(utils::NotNull expr, CardinalityEstimates& cardinality, + SchemaCatalog& schema) { + return std::visit(utils::Overloaded{ + [&](const logical::Table&) -> int64_t { + return 100 * cardinality.GetCardinality(expr->group); + }, + [&](const logical::Filter&) -> int64_t { + return 100 * cardinality.GetCardinality(expr->group); + }, + [&](const logical::Projection&) -> int64_t { + return 22 * cardinality.GetCardinality(expr->group); + }, + [&](const logical::Aggregation& a) -> int64_t { + return 510 * cardinality.GetCardinality(a.source); + }, + [&](const logical::PartialAggregation& a) -> int64_t { + return 510 * cardinality.GetCardinality(a.source); + }, + [&](const logical::FinalAggregation& a) -> int64_t { + return 510 * cardinality.GetCardinality(a.source); + }, + [&](const logical::CrossJoin& j) -> int64_t { + return 104 * cardinality.GetCardinality(j.lhs) * cardinality.GetCardinality(j.rhs); + }, + [&](const logical::Join& j) -> int64_t { + auto n_l = cardinality.GetCardinality(j.lhs); + auto n_r = cardinality.GetCardinality(j.rhs); + std::vector alternatives{ + HashJoinCost(j.lhs, j.rhs, expr->group, cardinality, schema), + HashJoinCost(j.rhs, j.lhs, expr->group, cardinality, schema), + 70 * n_l * n_r, + }; + if (ResolveJoinKeys(j.qual, j.lhs, j.rhs, schema)) { + alternatives.push_back(MergeJoinCost(j.lhs, j.rhs, expr->group, cardinality, schema)); + } + return *std::ranges::min_element(alternatives); + }, + }, expr->root_operator); +} + +int64_t CalcCost(utils::NotNull expr, CardinalityEstimates& cardinality, + SchemaCatalog& schema) { + return std::visit(utils::Overloaded{ + [&](const physical::SeqScan&) -> int64_t { + return 100 * cardinality.GetCardinality(expr->group); + }, + [&](const physical::IndexSeek&) -> int64_t { + auto out = cardinality.GetCardinality(expr->group); + return 200 * out + 100 * static_cast( + std::bit_width(static_cast(std::max(1, out)))); + }, + [&](const physical::Filter&) -> int64_t { + return 100 * cardinality.GetCardinality(expr->group); + }, + [&](const physical::Projection&) -> int64_t { + return 22 * cardinality.GetCardinality(expr->group); + }, + [&](const physical::NestedLoopJoin& j) -> int64_t { + auto n_l = cardinality.GetCardinality(j.lhs); + auto n_r = cardinality.GetCardinality(j.rhs); + return 70 * n_l * n_r; + }, + [&](const physical::NestedLoopCrossJoin& j) -> int64_t { + auto n_l = cardinality.GetCardinality(j.lhs); + auto n_r = cardinality.GetCardinality(j.rhs); + return 104 * n_l * n_r; + }, + [&](const physical::HashJoin& j) -> int64_t { + return HashJoinCost(j.lhs, j.rhs, expr->group, cardinality, schema); + }, + [&](const physical::MergeJoin& j) -> int64_t { + if (!ResolveJoinKeys(j.qual, j.lhs, j.rhs, schema)) { + return std::numeric_limits::max() / 4; + } + return MergeJoinCost(j.lhs, j.rhs, expr->group, cardinality, schema); + }, + [&](const physical::Sort& s) -> int64_t { + auto n = cardinality.GetCardinality(s.input); + return 11 * (n > 1 ? n * static_cast(std::bit_width(static_cast(n))) : n); + }, + [&](const physical::Aggregation& a) -> int64_t { + return 510 * cardinality.GetCardinality(a.source); + }, + [&](const physical::StreamAggregation& a) -> int64_t { + return 130 * cardinality.GetCardinality(a.source); + }, + [&](const physical::PartialAggregation& a) -> int64_t { + return 510 * cardinality.GetCardinality(a.source); + }, + [&](const physical::FinalAggregation& a) -> int64_t { + return 510 * cardinality.GetCardinality(a.source); + }, + }, expr->root_operator); +} + +char RuleLineageKindCode(RuleLineageKind kind) { + switch (kind) { + case RuleLineageKind::kTransformation: + return 't'; + case RuleLineageKind::kImplementation: + return 'i'; + case RuleLineageKind::kEnforcer: + return 'e'; + } + return '?'; +} + +int RuleLineageKindOrder(RuleLineageKind kind) { + switch (kind) { + case RuleLineageKind::kTransformation: + return 0; + case RuleLineageKind::kImplementation: + return 1; + case RuleLineageKind::kEnforcer: + return 2; + } + return 3; +} + +void AddRuleLineageStat(std::unordered_map& stats, + RuleLineageKind kind, size_t rule_id, + std::string_view rule_name) { + std::string key; + key.reserve(rule_name.size() + 32); + key.push_back(RuleLineageKindCode(kind)); + key.push_back(':'); + key += std::to_string(rule_id); + key.push_back(':'); + key += rule_name; + + auto [it, _] = stats.emplace( + std::move(key), + RuleLineageStat{ + .kind = kind, + .rule_id = rule_id, + .rule_name = rule_name, + .count = 0, + }); + ++it->second.count; +} + +} // namespace + +std::vector> GetChildren(utils::NotNull expr) { + return std::visit(utils::Overloaded{ + [](const logical::Table&) -> std::vector> { + return {}; + }, + [](const logical::Filter& f) -> std::vector> { + return {f.source}; + }, + [](const logical::Projection& p) -> std::vector> { + return {p.source}; + }, + [](const logical::Aggregation& a) -> std::vector> { + return {a.source}; + }, + [](const logical::PartialAggregation& a) -> std::vector> { + return {a.source}; + }, + [](const logical::FinalAggregation& a) -> std::vector> { + return {a.source}; + }, + [](const logical::CrossJoin& j) -> std::vector> { + return {j.lhs, j.rhs}; + }, + [](const logical::Join& j) -> std::vector> { + return {j.lhs, j.rhs}; + }, + }, expr->root_operator); +} + +std::vector> GetChildren(utils::NotNull expr) { + return std::visit(utils::Overloaded{ + [](const physical::SeqScan&) -> std::vector> { + return {}; + }, + [](const physical::IndexSeek&) -> std::vector> { + return {}; + }, + [](const physical::Filter& f) -> std::vector> { + return {f.source}; + }, + [](const physical::Projection& p) -> std::vector> { + return {p.source}; + }, + [](const physical::NestedLoopJoin& j) -> std::vector> { + return {j.lhs, j.rhs}; + }, + [](const physical::NestedLoopCrossJoin& j) -> std::vector> { + return {j.lhs, j.rhs}; + }, + [](const physical::HashJoin& j) -> std::vector> { + return {j.lhs, j.rhs}; + }, + [](const physical::MergeJoin& j) -> std::vector> { + return {j.lhs, j.rhs}; + }, + [](const physical::Sort& s) -> std::vector> { + return {s.input}; + }, + [](const physical::Aggregation& a) -> std::vector> { + return {a.source}; + }, + [](const physical::StreamAggregation& a) -> std::vector> { + return {a.source}; + }, + [](const physical::PartialAggregation& a) -> std::vector> { + return {a.source}; + }, + [](const physical::FinalAggregation& a) -> std::vector> { + return {a.source}; + }, + }, expr->root_operator); +} + +template +Optimizer::Optimizer( + const Operator& expr, Rules&& rules, + CardinalityEstimates cardinality, SchemaCatalog schema, PropertySet required, + ConstraintCatalog constraints) + : memo_(), rules_applier_(std::move(rules)), root_(memo_.Populate(expr)), + cardinality_(std::move(cardinality)), schema_(std::move(schema)), + constraints_(std::move(constraints)), + global_required_(std::move(required)) { +} + +template +int64_t Optimizer::LowerBoundCost(utils::NotNull group) { + if (auto it = lower_bounds_.find(group.get()); it != lower_bounds_.end()) { + return it->second; + } + + int64_t best = std::numeric_limits::max(); + for (auto expr : group->GetLogicalExprs()) { + int64_t local = LowerBoundLocalCost(expr, cardinality_, schema_); + int64_t children = 0; + bool overflow = false; + for (auto child : GetChildren(expr)) { + auto cb = LowerBoundCost(child); + if (children > std::numeric_limits::max() - cb) { overflow = true; break; } + children += cb; + } + if (overflow) continue; + if (local > std::numeric_limits::max() - children) continue; + best = std::min(best, local + children); + } + lower_bounds_[group.get()] = best; + return best; +} + +template +bool Optimizer::IsExplored(utils::NotNull group) const { + return explored_groups_.contains(group.get()); +} + +template +void Optimizer::SetExplored(utils::NotNull group) { + explored_groups_.insert(group); +} + +template +bool Optimizer::MarkOptimizeGroupRequested( + const WinnerKey& key, Limit limit) { + auto it = optimize_group_limits_.find(key); + if (it == optimize_group_limits_.end()) { + optimize_group_limits_.emplace(key, limit); + return true; + } + + const auto& seen_limit = it->second; + if (!seen_limit) return false; + if (!limit) { + it->second = std::nullopt; + return true; + } + if (*seen_limit >= *limit) return false; + + it->second = limit; + return true; +} + +template +void Optimizer::OptimizeInputs( + utils::NotNull expr, PropertySet required, + std::vector child_delivered, int64_t accum, Limit limit, size_t child_index) { + + + WinnerKey self_key{expr->group.get(), required}; + if (auto it = winner_.find(self_key); it != winner_.end()) { + limit = limit ? Limit{std::min(*limit, it->second.cost)} : Limit{it->second.cost}; + } + if (limit && accum >= *limit) return; + auto children = GetChildren(expr); + if (child_index >= children.size()) { + auto delivered = DeriveOutputProps(expr, child_delivered, schema_); + if (!delivered.Satisfies(required)) return; + WinnerKey key{expr->group.get(), required}; + if (!winner_.contains(key) || accum < winner_.at(key).cost) { + Log("New best plan for group {} with cost {}", expr->group->GetId(), accum); + winner_[key] = WinnerEntry{accum, expr.get(), delivered}; + } + return; + } + + auto child = children[child_index]; + auto child_required = RequiredInputProps(expr, required, child_index, schema_); + + tasks_.emplace([this, expr, child, child_index, required, child_delivered, accum, limit, child_required]() mutable { + WinnerKey child_key{child.get(), child_required}; + auto child_it = winner_.find(child_key); + if (child_it == winner_.end()) return; + auto new_accum = accum + child_it->second.cost; + if (limit && new_accum >= *limit) return; + child_delivered.push_back(child_it->second.delivered); + OptimizeInputs(expr, required, std::move(child_delivered), new_accum, limit, child_index + 1); + }); + + int64_t children_lb = 0; + for (size_t i = child_index + 1; i < children.size(); i++) { + auto cb = LowerBoundCost(children[i]); + if (children_lb > std::numeric_limits::max() - cb) { children_lb = std::numeric_limits::max(); break; } + children_lb += cb; + } + Limit child_limit = limit ? Limit{*limit - accum - children_lb} : std::nullopt; + tasks_.emplace([this, child, child_required, child_limit]() { + OptimizeGroup(child, child_required, child_limit); + }); +} + +template +void Optimizer::ApplyRule( + TransformationRuleId rule, utils::NotNull expr, Limit limit) { + Log("Applying transformation rule {} to group {}", rule.value, expr->group->GetId()); + RuleContext ctx{schema_, constraints_}; + auto new_expr = rules_applier_.Apply(rule, expr, memo_, ctx); + tasks_.emplace([this, new_expr, limit]() { ExploreExpression(new_expr, limit); }); + + + + if (auto it = group_parents_.find(new_expr->group.get()); it != group_parents_.end()) { + for (auto* parent : it->second) { + tasks_.emplace([this, parent, limit]() { + TryRules(utils::NotNull{parent}, limit); + }); + } + } +} + +template +void Optimizer::TryRules( + utils::NotNull expr, Limit limit) { + for (size_t rule = 0; rule < NTransformation; rule++) { + RuleContext ctx{schema_, constraints_}; + if (!rules_applier_.IsApplicable(TransformationRuleId{rule}, expr, ctx)) { + continue; + } + tasks_.emplace([this, expr, rule, limit]() { + ApplyRule(TransformationRuleId{rule}, expr, limit); + }); + } + + for (size_t rule = 0; rule < NImplementation; rule++) { + if (!rules_applier_.IsApplicable(ImplementationRuleId{rule}, expr)) { + continue; + } + tasks_.emplace([this, expr, rule]() { + Log("Applying implementation rule {} to group {}", rule, expr->group->GetId()); + auto new_exprs = rules_applier_.Apply(ImplementationRuleId{rule}, expr, memo_); + for (auto new_expr : new_exprs) { + auto lc = CalcCost(new_expr, cardinality_, schema_); + Log("Local cost for group {} expression: {}", new_expr->group->GetId(), lc); + local_cost_[new_expr.get()] = lc; + } + }); + } +} + +template +void Optimizer::ExploreExpression( + utils::NotNull expr, Limit limit) { + Log("Exploring expression in group {}", expr->group->GetId()); + TryRules(expr, limit); + if (!explored_exprs_.insert(expr.get()).second) return; + + for (auto child : GetChildren(expr)) { + group_parents_[child.get()].push_back(expr.get()); + if (IsExplored(child)) { + continue; + } + tasks_.emplace([this, child, limit]() { + ExploreGroup(child, limit); + }); + } +} + +template +void Optimizer::ExploreGroup( + utils::NotNull group, Limit limit) { + Log("Exploring group {}", group->GetId()); + SetExplored(group); + for (auto expr : group->GetLogicalExprs()) { + tasks_.emplace([this, expr, limit]() { + ExploreExpression(expr, limit); + }); + } +} + +template +void Optimizer::OptimizeGroup( + utils::NotNull group, PropertySet required, Limit limit) { + Log("Optimizing group {}", group->GetId()); + WinnerKey key{group.get(), required}; + if (winner_.contains(key)) return; + + if (IsExplored(group) && limit && LowerBoundCost(group) >= *limit) return; + + if (!IsExplored(group)) { + tasks_.emplace([this, group, required, limit]() { + OptimizeGroup(group, required, limit); + }); + tasks_.emplace([this, group, limit]() { ExploreGroup(group, limit); }); + return; + } + + if (!MarkOptimizeGroupRequested(key, limit)) { + Log("Skipping duplicate optimization request for group {}", group->GetId()); + return; + } + + + + + for (auto phys_expr : group->GetPhysicalExprs()) { + if (phys_expr->is_enforcer) continue; + auto lc = local_cost_[phys_expr.get()]; + if (limit && lc >= *limit) continue; + tasks_.emplace([this, phys_expr, required, lc, limit]() { + OptimizeInputs(phys_expr, required, {}, lc, limit); + }); + } + + if (!enforcers_added_.contains(key)) { + enforcers_added_.insert(key); + for (const auto& enforcer : kEnforcers) { + auto op = enforcer->TryBuild(group, required, schema_); + if (!op) continue; + auto enf_expr = group->AddPhysicalExpr(*op, true); + enf_expr->provenance = PhysicalExpr::Provenance{ + .kind = PhysicalExpr::ProvenanceKind::kEnforcer, + .rule_id = 0, + .rule_name = "SortEnforcer", + .source = nullptr, + }; + auto lc = CalcCost(enf_expr, cardinality_, schema_); + Log("Enforcer local cost for group {}: {}", group->GetId(), lc); + local_cost_[enf_expr.get()] = lc; + if (!limit || lc < *limit) { + tasks_.emplace([this, enf_expr, required, lc, limit]() { + OptimizeInputs(enf_expr, required, {}, lc, limit); + }); + } + } + } +} + +template +PhysicalPlanNode Optimizer::BuildOptimalPlan(Group* group, PropertySet required) { + Log("Building optimal plan for group {}", group->GetId()); + WinnerKey key{group, required}; + auto it = winner_.find(key); + if (it == winner_.end() || !it->second.plan) { + throw std::runtime_error{"no optimal plan for group"}; + } + auto* best_expr = it->second.plan; + utils::NotNull best_expr_nn{best_expr}; + auto plan = std::visit( + utils::Overloaded{ + [](const physical::SeqScan& op) -> PhysicalPlanNode { + return SeqScan{.table = op.table, .alias = op.alias}; + }, + [](const physical::IndexSeek& op) -> PhysicalPlanNode { + return IndexSeek{ + .table = op.table, + .alias = op.alias, + .predicate = op.predicate, + .index_column = op.index_column, + }; + }, + [this, best_expr_nn, required](const physical::Projection& op) -> PhysicalPlanNode { + return PhysicalProjection{ + .source = std::make_shared( + BuildOptimalPlan(op.source.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .expressions = op.expressions, + .aliases = op.aliases, + }; + }, + [this, best_expr_nn, required](const physical::Filter& op) -> PhysicalPlanNode { + return PhysicalFilter{ + .source = std::make_shared( + BuildOptimalPlan(op.source.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .predicate = op.predicate, + }; + }, + [this, best_expr_nn, required](const physical::NestedLoopJoin& op) -> PhysicalPlanNode { + return NestedLoopJoin{ + .lhs = std::make_shared( + BuildOptimalPlan(op.lhs.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .rhs = std::make_shared( + BuildOptimalPlan(op.rhs.get(), RequiredInputProps(best_expr_nn, required, 1, schema_))), + .type = op.type, + .qual = op.qual, + }; + }, + [this, best_expr_nn, required](const physical::NestedLoopCrossJoin& op) -> PhysicalPlanNode { + return NestedLoopCrossJoin{ + .lhs = std::make_shared( + BuildOptimalPlan(op.lhs.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .rhs = std::make_shared( + BuildOptimalPlan(op.rhs.get(), RequiredInputProps(best_expr_nn, required, 1, schema_))), + }; + }, + [this, best_expr_nn, required](const physical::HashJoin& op) -> PhysicalPlanNode { + return HashJoin{ + .lhs = std::make_shared( + BuildOptimalPlan(op.lhs.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .rhs = std::make_shared( + BuildOptimalPlan(op.rhs.get(), RequiredInputProps(best_expr_nn, required, 1, schema_))), + .type = op.type, + .qual = op.qual, + }; + }, + [this, best_expr_nn, required](const physical::MergeJoin& op) -> PhysicalPlanNode { + return MergeJoin{ + .lhs = std::make_shared( + BuildOptimalPlan(op.lhs.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .rhs = std::make_shared( + BuildOptimalPlan(op.rhs.get(), RequiredInputProps(best_expr_nn, required, 1, schema_))), + .type = op.type, + .qual = op.qual, + }; + }, + [this, best_expr_nn, required](const physical::Sort& op) -> PhysicalPlanNode { + return PhysicalSort{ + .source = std::make_shared( + BuildOptimalPlan(op.input.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .keys = op.keys, + }; + }, + [this, best_expr_nn, required](const physical::Aggregation& op) -> PhysicalPlanNode { + return PhysicalAggregation{ + .source = std::make_shared( + BuildOptimalPlan(op.source.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .group_by = op.group_by, + .aggregates = op.aggregates, + }; + }, + [this, best_expr_nn, required](const physical::StreamAggregation& op) -> PhysicalPlanNode { + return PhysicalStreamAggregation{ + .source = std::make_shared( + BuildOptimalPlan(op.source.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .group_by = op.group_by, + .aggregates = op.aggregates, + }; + }, + [this, best_expr_nn, required](const physical::PartialAggregation& op) -> PhysicalPlanNode { + return PhysicalPartialAggregation{ + .source = std::make_shared( + BuildOptimalPlan(op.source.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .group_by = op.group_by, + .aggregates = op.aggregates, + }; + }, + [this, best_expr_nn, required](const physical::FinalAggregation& op) -> PhysicalPlanNode { + return PhysicalFinalAggregation{ + .source = std::make_shared( + BuildOptimalPlan(op.source.get(), RequiredInputProps(best_expr_nn, required, 0, schema_))), + .group_by = op.group_by, + .aggregates = op.aggregates, + }; + }, + }, + best_expr->root_operator); + plan.metadata = PlanNodeMetadata{ + .cardinality = cardinality_.GetCardinality(best_expr->group), + .local_cost = local_cost_.at(best_expr), + }; + return plan; +} + +template +void Optimizer::CollectLogicalRuleLineage( + LogicalExpr* expr, std::unordered_map& stats, + std::unordered_set& visited_logical) { + if (!expr || !visited_logical.insert(expr).second) return; + + if (!expr->provenance) return; + AddRuleLineageStat(stats, RuleLineageKind::kTransformation, + expr->provenance->rule_id, expr->provenance->rule_name); + CollectLogicalRuleLineage(expr->provenance->source, stats, visited_logical); +} + +template +void Optimizer::CollectSelectedPlanRuleLineage( + Group* group, PropertySet required, + std::unordered_map& stats, + std::unordered_set& visited_physical, + std::unordered_set& visited_logical) { + WinnerKey key{group, required}; + auto it = winner_.find(key); + if (it == winner_.end() || !it->second.plan) { + throw std::runtime_error{"no optimal plan for group"}; + } + + auto* best_expr = it->second.plan; + const bool first_visit = visited_physical.insert(best_expr).second; + if (first_visit && best_expr->provenance) { + const auto& provenance = *best_expr->provenance; + auto kind = provenance.kind == PhysicalExpr::ProvenanceKind::kEnforcer + ? RuleLineageKind::kEnforcer + : RuleLineageKind::kImplementation; + AddRuleLineageStat(stats, kind, provenance.rule_id, provenance.rule_name); + CollectLogicalRuleLineage(provenance.source, stats, visited_logical); + } + + utils::NotNull best_expr_nn{best_expr}; + auto children = GetChildren(best_expr_nn); + for (size_t i = 0; i < children.size(); ++i) { + CollectSelectedPlanRuleLineage( + children[i].get(), RequiredInputProps(best_expr_nn, required, i, schema_), + stats, visited_physical, visited_logical); + } +} + +template +std::vector Optimizer::GetSelectedPlanRuleLineage() { + std::unordered_map stats; + std::unordered_set visited_physical; + std::unordered_set visited_logical; + CollectSelectedPlanRuleLineage( + root_->group.get(), global_required_, stats, visited_physical, visited_logical); + + std::vector result; + result.reserve(stats.size()); + for (const auto& [_, stat] : stats) { + result.push_back(stat); + } + std::ranges::sort(result, [](const RuleLineageStat& lhs, const RuleLineageStat& rhs) { + if (RuleLineageKindOrder(lhs.kind) != RuleLineageKindOrder(rhs.kind)) { + return RuleLineageKindOrder(lhs.kind) < RuleLineageKindOrder(rhs.kind); + } + return lhs.rule_id < rhs.rule_id; + }); + return result; +} + +template +void Optimizer::RunSearch(Limit limit) { + Log("Starting optimization"); + tasks_.emplace([this, limit]() { OptimizeGroup(root_->group, global_required_, limit); }); + std::size_t executed_tasks = 0; + while (!tasks_.empty()) { + auto next_task = std::move(tasks_.top()); + tasks_.pop(); + next_task(); + ++executed_tasks; + if (executed_tasks % 100000 == 0) { + Log("Optimizer progress: tasks={} pending={} groups={} explored_groups={} explored_exprs={} winners={}", + executed_tasks, tasks_.size(), memo_.GroupCount(), explored_groups_.size(), + explored_exprs_.size(), winner_.size()); + } + } + Log("Optimizer search complete: tasks={} groups={} explored_groups={} explored_exprs={} winners={}", + executed_tasks, memo_.GroupCount(), explored_groups_.size(), explored_exprs_.size(), + winner_.size()); +} + +template +PhysicalPlanNode Optimizer::Optimize() { + const auto started = std::chrono::steady_clock::now(); + RunSearch(std::numeric_limits::max()); + Log("Optimization complete, building plan"); + auto plan = BuildOptimalPlan(root_->group.get(), global_required_); + const auto runtime = std::chrono::duration_cast( + std::chrono::steady_clock::now() - started); + Log("Optimization finished in {} us, chosen plan cost {}", runtime.count(), GetBestCost()); + return plan; +} + +template +PhysicalPlanNode Optimizer::OptimizeExhaustive() { + const auto started = std::chrono::steady_clock::now(); + RunSearch(std::nullopt); + auto plan = BuildOptimalPlan(root_->group.get(), global_required_); + const auto runtime = std::chrono::duration_cast( + std::chrono::steady_clock::now() - started); + Log("Exhaustive optimization finished in {} us, chosen plan cost {}", runtime.count(), + GetBestCost()); + return plan; +} + +template +std::int64_t Optimizer::GetBestCost() const { + WinnerKey key{root_->group.get(), global_required_}; + auto it = winner_.find(key); + if (it == winner_.end()) { + throw std::runtime_error{"no optimal plan cost"}; + } + return it->second.cost; +} + +template +utils::NotNull Optimizer::GetRootGroup() const { + return root_->group; +} + +template class Optimizer<14, 9>; +template class Optimizer<0, 6>; + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/optimizer_test.cpp b/src/stewkk/sql/logic/optimizer/optimizer_test.cpp new file mode 100644 index 0000000..a04f77f --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/optimizer_test.cpp @@ -0,0 +1,777 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using ::testing::Eq; +using ::testing::Gt; +using ::testing::IsTrue; +using ::testing::IsFalse; +using ::testing::HasSubstr; + +namespace stewkk::sql { + +// FIXME: branch and bound +// FIXME: сделать API в виде DoStep(), которое возвращает какой-то внутренний стейт оптимизатора +// FIXME: применение правила (по крайней мере трансформации), должно создавать несколько выражений + +int64_t EstimateCardinality( + std::string_view sql, std::unordered_map table_sizes) { + std::stringstream s{std::string{sql}}; + Memo memo; + auto root = memo.Populate(GetAST(s).value().op); + CardinalityEstimates cardinality{std::move(table_sizes)}; + return cardinality.GetCardinality(root->group); +} + +SchemaCatalog MakeTestSchema() { + return SchemaCatalog({ + {"users", {Attribute{"users", "id"}, Attribute{"users", "age"}, + Attribute{"users", "name"}}}, + {"orders", {Attribute{"orders", "id"}, Attribute{"orders", "user_id"}, + Attribute{"orders", "customer_id"}, Attribute{"orders", "total"}}}, + {"customers", {Attribute{"customers", "id"}, Attribute{"customers", "region_id"}}}, + {"regions", {Attribute{"regions", "id"}}}, + {"departments", {Attribute{"departments", "id"}}}, + {"lineorder", {Attribute{"lineorder", "suppkey"}, Attribute{"lineorder", "value"}}}, + {"supplier", {Attribute{"supplier", "id"}, Attribute{"supplier", "region"}}}, + }); +} + +size_t RuleLineageCount(const std::vector& stats, + RuleLineageKind kind, std::string_view rule_name) { + for (const auto& stat : stats) { + if (stat.kind == kind && stat.rule_name == rule_name) { + return stat.count; + } + } + return 0; +} + +TEST(CardinalityEstimatesTest, AppliesFilterHeuristics) { + ASSERT_THAT(EstimateCardinality("SELECT * FROM users WHERE users.id = 1;", {{"users", 1000}}), + Eq(100)); + ASSERT_THAT( + EstimateCardinality("SELECT * FROM users WHERE users.age BETWEEN 18 AND 30;", + {{"users", 1000}}), + Eq(250)); + ASSERT_THAT( + EstimateCardinality("SELECT * FROM users WHERE users.id IN (1, 2, 3);", + {{"users", 1000}}), + Eq(300)); +} + +TEST(SchemaCatalogTest, DerivesJoinWidth) { + std::stringstream s{"SELECT * FROM users JOIN orders ON users.id = orders.user_id;"}; + Memo memo; + auto root = memo.Populate(GetAST(s).value().op); + SchemaCatalog schema({ + {"users", {Attribute{"users", "id"}, Attribute{"users", "name"}}}, + {"orders", {Attribute{"orders", "id"}, Attribute{"orders", "user_id"}, + Attribute{"orders", "total"}}}, + }); + + ASSERT_THAT(schema.GetWidth(root->group), Eq(5)); +} + +TEST(SchemaCatalogTest, MissingTableSchemaThrows) { + std::stringstream s{"SELECT * FROM users;"}; + Memo memo; + auto root = memo.Populate(GetAST(s).value().op); + SchemaCatalog schema; + + ASSERT_THROW(schema.GetWidth(root->group), std::runtime_error); +} + +TEST(ConstraintCatalogTest, LoadsUniqueAndForeignKeyMetadata) { + auto dir = std::filesystem::temp_directory_path() / "iu9_sql_constraints_test"; + std::filesystem::create_directories(dir); + { + std::ofstream out{dir / "constraints.meta"}; + out << "unique departments id\n"; + out << "foreign_key users department_id departments id\n"; + } + + auto catalog = LoadConstraintCatalogFromCsvDir(dir); + + EXPECT_TRUE(catalog.IsUnique(Attribute{"departments", "id"})); + EXPECT_TRUE(catalog.HasForeignKey(Attribute{"users", "department_id"}, + Attribute{"departments", "id"})); + std::filesystem::remove_all(dir); +} + +TEST(OptimizerTest, Simple) { + std::stringstream s{"SELECT * FROM users;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(), {}, MakeTestSchema()); + + auto got = optimizer.Optimize(); + + ASSERT_THAT(SerializeDot(got), + Eq("digraph G { rankdir=BT;\n" + " n0 [label=\"SeqScan\\nusers\\ncard=10\\ncost=1000\"]\n" + "}\n")); + ASSERT_THAT(optimizer.GetBestCost(), Eq(1000)); +} + +TEST(OptimizerTest, JoinCommutativity) { + std::stringstream s{"SELECT * FROM users JOIN orders ON users.id = orders.user_id;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({ + {"users", 10000}, + {"orders", 100}, + }), MakeTestSchema()); + + + auto got = optimizer.Optimize(); + + ASSERT_THAT(Serialize(got), Eq("(HashJoin Inner (= (attr users id) (attr orders user_id)) (SeqScan orders) (SeqScan users))")); +} + +TEST(OptimizerTest, MultiwayJoinOCR) { + std::stringstream s{ + "SELECT orders.id, customers.id, regions.id FROM orders " + "JOIN customers ON orders.customer_id = customers.id " + "JOIN regions ON customers.region_id = regions.id;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({ + {"regions", 10}, + {"customers", 500}, + {"orders", 5000}, + }), MakeTestSchema()); + + auto got = optimizer.Optimize(); + + ASSERT_THAT( + Serialize(got), + Eq("(PhysicalProjection (exprs (attr orders id) (attr customers id) (attr regions id))" + " (HashJoin Inner (= (attr orders customer_id) (attr customers id))" + " (HashJoin Inner (= (attr customers region_id) (attr regions id))" + " (SeqScan regions) (SeqScan customers)) (SeqScan orders)))")); +} + +TEST(OptimizerTest, MultiwayJoinROC) { + std::stringstream s{ + "SELECT orders.id, customers.id, regions.id FROM regions " + "JOIN customers ON customers.region_id = regions.id " + "JOIN orders ON orders.customer_id = customers.id;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({ + {"regions", 10}, + {"customers", 500}, + {"orders", 5000}, + }), MakeTestSchema()); + + auto got = optimizer.Optimize(); + + ASSERT_THAT( + Serialize(got), + Eq("(PhysicalProjection (exprs (attr orders id) (attr customers id) (attr regions id))" + " (HashJoin Inner (= (attr orders customer_id) (attr customers id))" + " (HashJoin Inner (= (attr customers region_id) (attr regions id))" + " (SeqScan regions) (SeqScan customers)) (SeqScan orders)))")); +} + +TEST(OptimizerTest, OrderBy) { + std::stringstream s{"SELECT * FROM users ORDER BY users.id;"}; + auto parsed = GetAST(s).value(); + SchemaCatalog schema({{"users", {Attribute{"users", "id"}, Attribute{"users", "age"}}}}); + PropertySet required = parsed.required_order + ? PropertySet{SortProperty{*parsed.required_order}} + : PropertySet::Any(); + Optimizer optimizer(parsed.op, MakeMainRules(), {}, std::move(schema), std::move(required)); + + auto got = optimizer.Optimize(); + + ASSERT_THAT(Serialize(got), Eq("(Sort (keys users.id Asc) (SeqScan users))")); +} + +TEST(OptimizerTest, UsesStreamAggregateWhenOrderCanBeProvided) { + std::stringstream s{"SELECT users.id, COUNT(*) FROM users GROUP BY users.id ORDER BY users.id;"}; + auto parsed = GetAST(s).value(); + PropertySet required{SortProperty{*parsed.required_order}}; + Optimizer optimizer(parsed.op, MakeMainRules(), CardinalityEstimates({{"users", 1000}}), + MakeTestSchema(), std::move(required)); + + auto got = optimizer.Optimize(); + + ASSERT_THAT( + Serialize(got), + HasSubstr("(StreamAggregate (group_by (attr users id)) (aggs (COUNT *))" + " (Sort (keys users.id Asc) (SeqScan users)))")); +} + +TEST(OptimizerTest, OrderBySortsAfterCrossJoin) { + std::stringstream s{ + "SELECT * FROM departments CROSS JOIN orders ORDER BY departments.id;"}; + auto parsed = GetAST(s).value(); + SchemaCatalog schema({ + {"departments", {Attribute{"departments", "id"}}}, + {"orders", {Attribute{"orders", "id"}}}, + }); + PropertySet required{SortProperty{*parsed.required_order}}; + Optimizer optimizer(parsed.op, MakeMainRules(), {}, std::move(schema), std::move(required)); + + auto got = optimizer.Optimize(); + + ASSERT_THAT( + Serialize(got), + Eq("(Sort (keys departments.id Asc)" + " (NestedLoopCrossJoin (SeqScan departments) (SeqScan orders)))")); +} + +TEST(OptimizerTest, OrderBySortsAfterNestedLoopJoin) { + std::stringstream s{ + "SELECT * FROM departments JOIN orders ON departments.id < orders.id " + "ORDER BY departments.id;"}; + auto parsed = GetAST(s).value(); + SchemaCatalog schema({ + {"departments", {Attribute{"departments", "id"}}}, + {"orders", {Attribute{"orders", "id"}}}, + }); + PropertySet required{SortProperty{*parsed.required_order}}; + Optimizer optimizer(parsed.op, MakeMainRules(), {}, std::move(schema), std::move(required)); + + auto got = optimizer.Optimize(); + + ASSERT_THAT( + Serialize(got), + HasSubstr("(Sort (keys departments.id Asc) (NestedLoopJoin Inner")); +} + +TEST(OptimizerTest, AliasedJoinOptimizesWithAliasQualifiedAttrs) { + std::stringstream s{"SELECT c.id FROM customers AS c JOIN orders AS o ON c.id = o.customer_id WHERE c.region_id = 1;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({ + {"customers", 500}, + {"orders", 5000}, + }), MakeTestSchema()); + + auto got = optimizer.Optimize(); + auto serialized = Serialize(got); + + ASSERT_THAT(serialized, HasSubstr("(SeqScan customers c)")); + ASSERT_THAT(serialized, HasSubstr("(SeqScan orders o)")); + ASSERT_THAT(serialized, HasSubstr("(attr c id)")); + ASSERT_THAT(serialized, HasSubstr("(attr o customer_id)")); +} + +TEST(OptimizerTest, PushesSelectiveFilterIntoHashBuildSide) { + std::stringstream s{ + "SELECT * FROM lineorder AS lo " + "JOIN supplier AS s ON lo.suppkey = s.id " + "WHERE s.region = 'AMERICA';"}; + Operator op = GetAST(s).value().op; + SchemaCatalog schema({ + {"lineorder", {Attribute{"lineorder", "suppkey"}, Attribute{"lineorder", "value"}}}, + {"supplier", {Attribute{"supplier", "id"}, Attribute{"supplier", "region"}}}, + }); + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({ + {"lineorder", 6000}, + {"supplier", 100}, + }), std::move(schema)); + + auto got = optimizer.Optimize(); + + ASSERT_THAT( + Serialize(got), + Eq("(HashJoin Inner (= (attr lo suppkey) (attr s id))" + " (PhysicalFilter (= (attr s region) (str \"AMERICA\")) (SeqScan supplier s))" + " (SeqScan lineorder lo))")); +} + +TEST(OptimizerTest, TracksSelectedPlanRuleLineage) { + std::stringstream s{ + "SELECT * FROM lineorder AS lo " + "JOIN supplier AS s ON lo.suppkey = s.id " + "WHERE s.region = 'AMERICA';"}; + Operator op = GetAST(s).value().op; + SchemaCatalog schema({ + {"lineorder", {Attribute{"lineorder", "suppkey"}, Attribute{"lineorder", "value"}}}, + {"supplier", {Attribute{"supplier", "id"}, Attribute{"supplier", "region"}}}, + }); + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({ + {"lineorder", 6000}, + {"supplier", 100}, + }), std::move(schema)); + + auto got = optimizer.Optimize(); + auto stats = optimizer.GetSelectedPlanRuleLineage(); + + ASSERT_THAT(Serialize(got), + HasSubstr("(PhysicalFilter (= (attr s region) (str \"AMERICA\")) " + "(SeqScan supplier s))")); + EXPECT_THAT(RuleLineageCount(stats, RuleLineageKind::kTransformation, + "FilterPushdownThroughJoin"), Gt(0)); + EXPECT_THAT(RuleLineageCount(stats, RuleLineageKind::kImplementation, + "ImplementTable"), Eq(2)); + EXPECT_THAT(RuleLineageCount(stats, RuleLineageKind::kImplementation, + "ImplementFilter"), Eq(1)); + EXPECT_THAT(RuleLineageCount(stats, RuleLineageKind::kImplementation, + "ImplementHashJoin"), Eq(1)); +} + +TEST(OptimizerTest, TracksSelectedPlanEnforcerLineage) { + std::stringstream s{"SELECT * FROM users ORDER BY users.id;"}; + auto parsed = GetAST(s).value(); + SchemaCatalog schema({{"users", {Attribute{"users", "id"}, Attribute{"users", "age"}}}}); + PropertySet required{SortProperty{*parsed.required_order}}; + Optimizer optimizer(parsed.op, MakeMainRules(), {}, std::move(schema), std::move(required)); + + auto got = optimizer.Optimize(); + auto stats = optimizer.GetSelectedPlanRuleLineage(); + + ASSERT_THAT(Serialize(got), Eq("(Sort (keys users.id Asc) (SeqScan users))")); + EXPECT_THAT(RuleLineageCount(stats, RuleLineageKind::kEnforcer, "SortEnforcer"), Eq(1)); + EXPECT_THAT(RuleLineageCount(stats, RuleLineageKind::kImplementation, + "ImplementTable"), Eq(1)); +} + +TEST(OptimizerTest, UsesHashJoinForConjunctiveEquiJoinPredicate) { + std::stringstream s{ + "SELECT * FROM lineorder AS lo " + "JOIN supplier AS s ON lo.suppkey = s.id AND s.region = 'AMERICA';"}; + Operator op = GetAST(s).value().op; + SchemaCatalog schema({ + {"lineorder", {Attribute{"lineorder", "suppkey"}, Attribute{"lineorder", "value"}}}, + {"supplier", {Attribute{"supplier", "id"}, Attribute{"supplier", "region"}}}, + }); + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({ + {"lineorder", 6000}, + {"supplier", 100}, + }), std::move(schema)); + + auto got = optimizer.Optimize(); + + ASSERT_THAT( + Serialize(got), + Eq("(HashJoin Inner (and (= (attr lo suppkey) (attr s id))" + " (= (attr s region) (str \"AMERICA\")))" + " (SeqScan supplier s) (SeqScan lineorder lo))")); +} + +TEST(OptimizerTest, UsesIndexSeekForIndexedIntegerPredicate) { + std::stringstream s{"SELECT * FROM users WHERE users.id = 8;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(IndexCatalog({ + IndexInfo{.table = "users", .column = "id", .type = "sorted", .file = "users.id.sorted.idx"}, + })), CardinalityEstimates({{"users", 1000}}), MakeTestSchema()); + + auto got = optimizer.Optimize(); + + ASSERT_THAT(Serialize(got), Eq("(IndexSeek (= (attr users id) 8) users)")); +} + +TEST(OptimizerTest, KeepsSequentialFilterWhenIndexMetadataIsAbsent) { + std::stringstream s{"SELECT * FROM users WHERE users.id = 8;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(), CardinalityEstimates({{"users", 1000}}), + MakeTestSchema()); + + auto got = optimizer.Optimize(); + + ASSERT_THAT(Serialize(got), + Eq("(PhysicalFilter (= (attr users id) 8) (SeqScan users))")); +} + +TEST(OptimizerTest, IndexSeekSupportsAliasAndFullResidualPredicate) { + std::stringstream s{"SELECT * FROM users AS u WHERE u.id >= 8 AND u.age < 70;"}; + Operator op = GetAST(s).value().op; + Optimizer optimizer(op, MakeMainRules(IndexCatalog({ + IndexInfo{.table = "users", .column = "id", .type = "sorted", .file = "users.id.sorted.idx"}, + })), CardinalityEstimates({{"users", 1000}}), MakeTestSchema()); + + auto got = optimizer.Optimize(); + + ASSERT_THAT(Serialize(got), + Eq("(IndexSeek (and (>= (attr u id) 8) (< (attr u age) 70)) users u)")); +} + +TEST(OptimizerTest, IndexSeekDeliversSortedIndexOrder) { + std::stringstream s{"SELECT * FROM users WHERE users.id >= 8 ORDER BY users.id;"}; + auto parsed = GetAST(s).value(); + Optimizer optimizer(parsed.op, MakeMainRules(IndexCatalog({ + IndexInfo{.table = "users", .column = "id", .type = "sorted", .file = "users.id.sorted.idx"}, + })), CardinalityEstimates({{"users", 1000}}), MakeTestSchema(), + PropertySet{SortProperty{*parsed.required_order}}); + + auto got = optimizer.Optimize(); + + ASSERT_THAT(Serialize(got), Eq("(IndexSeek (>= (attr users id) 8) users)")); +} + +TEST(OptimizerTest, IndexSeekCanChooseOrderCompatibleIndexedConjunct) { + std::stringstream s{ + "SELECT * FROM users AS u WHERE u.id >= 8 AND u.age < 70 ORDER BY u.age;"}; + auto parsed = GetAST(s).value(); + Optimizer optimizer(parsed.op, MakeMainRules(IndexCatalog({ + IndexInfo{.table = "users", .column = "id", .type = "sorted", .file = "users.id.sorted.idx"}, + IndexInfo{.table = "users", .column = "age", .type = "sorted", .file = "users.age.sorted.idx"}, + })), CardinalityEstimates({{"users", 1000}}), MakeTestSchema(), + PropertySet{SortProperty{*parsed.required_order}}); + + auto got = optimizer.Optimize(); + + ASSERT_THAT(Serialize(got), + Eq("(IndexSeek (and (>= (attr u id) 8) (< (attr u age) 70)) users u)")); +} + +TEST(OptimizerTest, NaiveRulesDisableLogicalTransformations) { + std::stringstream s{ + "SELECT * FROM lineorder AS lo " + "JOIN supplier AS s ON lo.suppkey = s.id " + "WHERE s.region = 'AMERICA';"}; + Operator op = GetAST(s).value().op; + SchemaCatalog schema({ + {"lineorder", {Attribute{"lineorder", "suppkey"}, Attribute{"lineorder", "value"}}}, + {"supplier", {Attribute{"supplier", "id"}, Attribute{"supplier", "region"}}}, + }); + CardinalityEstimates cardinality({ + {"lineorder", 6000}, + {"supplier", 100}, + }); + Optimizer optimizer(op, MakeMainRules(), cardinality, schema); + auto optimized = optimizer.Optimize(); + + Optimizer naive_optimizer(op, MakeNaiveRules(), std::move(cardinality), std::move(schema)); + auto naive = naive_optimizer.Optimize(); + + ASSERT_THAT(Serialize(optimized), + HasSubstr("(PhysicalFilter (= (attr s region) (str \"AMERICA\")) " + "(SeqScan supplier s))")); + ASSERT_THAT(Serialize(naive), + Eq("(PhysicalFilter (= (attr s region) (str \"AMERICA\"))" + " (NestedLoopJoin Inner (= (attr lo suppkey) (attr s id))" + " (SeqScan lineorder lo) (SeqScan supplier s)))")); + ASSERT_THAT(naive_optimizer.GetBestCost(), Gt(optimizer.GetBestCost())); +} + +TEST(ReachabilityTest, SeqScanReachable) { + std::stringstream s{"SELECT * FROM users;"}; + auto result = IsPlanReachable(s, SeqScan{"users"}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); + ASSERT_THAT(result.closest_distance, Eq(0)); +} + +TEST(ReachabilityTest, SeqScanWrongTable) { + std::stringstream s{"SELECT * FROM users;"}; + auto result = IsPlanReachable(s, SeqScan{"orders"}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsFalse()); + ASSERT_THAT(result.mismatch, HasSubstr("users")); + ASSERT_THAT(result.closest_distance, Eq(1)); +} + +TEST(ReachabilityTest, IndexSeekReachableWithIndexCatalog) { + std::stringstream s{"SELECT * FROM users WHERE users.id >= 8;"}; + Expression predicate = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kGe, + std::make_shared(IntConst{8})}; + auto result = IsPlanReachable( + s, IndexSeek{"users", std::nullopt, predicate}, {}, MakeTestSchema(), + IndexCatalog({ + IndexInfo{.table = "users", .column = "id", .type = "sorted", + .file = "users.id.sorted.idx"}, + })); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, WrongOperatorType) { + std::stringstream s{"SELECT * FROM users;"}; + Expression qual = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"orders", "user_id"})}; + auto result = IsPlanReachable(s, HashJoin{ + std::make_shared(SeqScan{"users"}), + std::make_shared(SeqScan{"orders"}), + JoinType::kInner, + qual}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsFalse()); +} + +TEST(ReachabilityTest, BothJoinOrdersReachable) { + std::stringstream s1{"SELECT * FROM users JOIN orders ON users.id = orders.user_id;"}; + std::stringstream s2{"SELECT * FROM users JOIN orders ON users.id = orders.user_id;"}; + Expression qual = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"orders", "user_id"})}; + CardinalityEstimates cardinality({{"users", 10000}, {"orders", 100}}); + + auto optimal = IsPlanReachable(s1, NestedLoopJoin{ + std::make_shared(SeqScan{"orders"}), + std::make_shared(SeqScan{"users"}), + JoinType::kInner, qual}, cardinality, MakeTestSchema()); + ASSERT_THAT(optimal.reachable, IsTrue()); + + auto suboptimal = IsPlanReachable(s2, NestedLoopJoin{ + std::make_shared(SeqScan{"users"}), + std::make_shared(SeqScan{"orders"}), + JoinType::kInner, qual}, cardinality, MakeTestSchema()); + ASSERT_THAT(suboptimal.reachable, IsTrue()); +} + +TEST(ReachabilityTest, HashJoinReachable) { + std::stringstream s{"SELECT * FROM users JOIN orders ON users.id = orders.user_id;"}; + Expression qual = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"orders", "user_id"})}; + auto result = IsPlanReachable(s, HashJoin{ + std::make_shared(SeqScan{"orders"}), + std::make_shared(SeqScan{"users"}), + JoinType::kInner, qual}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, MergeJoinReachableWithSortedInputs) { + std::stringstream s{"SELECT * FROM users JOIN orders ON users.id = orders.user_id;"}; + Expression qual = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"orders", "user_id"})}; + SchemaCatalog schema({ + {"users", {Attribute{"users", "id"}}}, + {"orders", {Attribute{"orders", "user_id"}}}, + }); + auto result = IsPlanReachable(s, MergeJoin{ + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"users"}), + SortOrder{{SortKey{"users", "id", Direction::kAsc}}}}), + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"orders"}), + SortOrder{{SortKey{"orders", "user_id", Direction::kAsc}}}}), + JoinType::kInner, qual}, {}, std::move(schema)); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, MergeJoinCanSatisfyOrderByJoinKey) { + std::stringstream s{ + "SELECT * FROM users JOIN orders ON users.id = orders.user_id ORDER BY users.id;"}; + Expression qual = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"orders", "user_id"})}; + SchemaCatalog schema({ + {"users", {Attribute{"users", "id"}}}, + {"orders", {Attribute{"orders", "user_id"}}}, + }); + auto result = IsPlanReachable(s, MergeJoin{ + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"users"}), + SortOrder{{SortKey{"users", "id", Direction::kAsc}}}}), + std::make_shared(PhysicalSort{ + std::make_shared(SeqScan{"orders"}), + SortOrder{{SortKey{"orders", "user_id", Direction::kAsc}}}}), + JoinType::kInner, qual}, {}, std::move(schema)); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, WrongJoinQual) { + std::stringstream s{"SELECT * FROM users JOIN orders ON users.id = orders.user_id;"}; + Expression wrong_qual = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(Attribute{"orders", "id"})}; + auto result = IsPlanReachable(s, NestedLoopJoin{ + std::make_shared(SeqScan{"orders"}), + std::make_shared(SeqScan{"users"}), + JoinType::kInner, wrong_qual}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsFalse()); + ASSERT_THAT(result.mismatch, HasSubstr("qual")); +} + +TEST(ReachabilityTest, InExpandsToOrChain) { + std::stringstream s{"SELECT * FROM users WHERE users.id IN (1, 2, 3);"}; + Expression eq1 = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(IntConst{1})}; + Expression eq2 = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(IntConst{2})}; + Expression eq3 = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(IntConst{3})}; + Expression or_chain = BinaryExpression{ + std::make_shared(BinaryExpression{ + std::make_shared(std::move(eq1)), + BinaryOp::kOr, + std::make_shared(std::move(eq2))}), + BinaryOp::kOr, + std::make_shared(std::move(eq3))}; + auto result = IsPlanReachable(s, PhysicalFilter{ + std::make_shared(SeqScan{"users"}), or_chain}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, NotInExpandedAndChainIgnoresComparisonOrder) { + std::stringstream s{"SELECT * FROM users WHERE users.id NOT IN (1, 2, 3);"}; + Expression ne3 = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kNotEq, + std::make_shared(IntConst{3})}; + Expression ne2 = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kNotEq, + std::make_shared(IntConst{2})}; + Expression ne1 = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kNotEq, + std::make_shared(IntConst{1})}; + Expression and_chain = BinaryExpression{ + std::make_shared(BinaryExpression{ + std::make_shared(std::move(ne3)), + BinaryOp::kAnd, + std::make_shared(std::move(ne2))}), + BinaryOp::kAnd, + std::make_shared(std::move(ne1))}; + auto result = IsPlanReachable(s, PhysicalFilter{ + std::make_shared(SeqScan{"users"}), and_chain}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, NotBetweenMatchesExpandedRangeDisjunction) { + std::stringstream s{"SELECT * FROM users WHERE users.id NOT BETWEEN 1 AND 3;"}; + Expression lt = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kLt, + std::make_shared(IntConst{1})}; + Expression gt = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kGt, + std::make_shared(IntConst{3})}; + Expression range = BinaryExpression{ + std::make_shared(std::move(lt)), + BinaryOp::kOr, + std::make_shared(std::move(gt))}; + + auto result = IsPlanReachable(s, PhysicalFilter{ + std::make_shared(SeqScan{"users"}), range}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, NegatedConjunctionMatchesDeMorganForm) { + std::stringstream s{"SELECT * FROM users WHERE NOT (users.id = 1 AND users.age >= 30);"}; + Expression id_ne = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kNotEq, + std::make_shared(IntConst{1})}; + Expression age_lt = BinaryExpression{ + std::make_shared(Attribute{"users", "age"}), + BinaryOp::kLt, + std::make_shared(IntConst{30})}; + Expression demorgan = BinaryExpression{ + std::make_shared(std::move(age_lt)), + BinaryOp::kOr, + std::make_shared(std::move(id_ne))}; + + auto result = IsPlanReachable(s, PhysicalFilter{ + std::make_shared(SeqScan{"users"}), demorgan}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, ReversedComparisonOperandsMatch) { + std::stringstream s{"SELECT * FROM users WHERE 1 < users.id;"}; + Expression gt = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kGt, + std::make_shared(IntConst{1})}; + + auto result = IsPlanReachable(s, PhysicalFilter{ + std::make_shared(SeqScan{"users"}), gt}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + +TEST(ReachabilityTest, JoinQualNormalizesInAndNotBetween) { + std::stringstream s{ + "SELECT * FROM users CROSS JOIN orders " + "WHERE users.id IN (22) AND orders.id NOT BETWEEN 1 AND 3;"}; + Expression users_eq = BinaryExpression{ + std::make_shared(Attribute{"users", "id"}), + BinaryOp::kEq, + std::make_shared(IntConst{22})}; + Expression orders_lt = BinaryExpression{ + std::make_shared(Attribute{"orders", "id"}), + BinaryOp::kLt, + std::make_shared(IntConst{1})}; + Expression orders_gt = BinaryExpression{ + std::make_shared(Attribute{"orders", "id"}), + BinaryOp::kGt, + std::make_shared(IntConst{3})}; + Expression orders_range = BinaryExpression{ + std::make_shared(std::move(orders_lt)), + BinaryOp::kOr, + std::make_shared(std::move(orders_gt))}; + Expression qual = BinaryExpression{ + std::make_shared(std::move(users_eq)), + BinaryOp::kAnd, + std::make_shared(std::move(orders_range))}; + + auto result = IsPlanReachable(s, NestedLoopJoin{ + std::make_shared(SeqScan{"users"}), + std::make_shared(SeqScan{"orders"}), + JoinType::kInner, + qual}, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + + + +TEST(ReachabilityTest, OrderByReachableViaSortEnforcer) { + std::stringstream s{"SELECT users.id FROM users ORDER BY users.id;"}; + PhysicalSort target{ + std::make_shared(PhysicalProjection{ + std::make_shared(SeqScan{"users"}), + {Attribute{"users", "id"}}, + {}}), + SortOrder{{SortKey{"users", "id", Direction::kAsc}}}}; + auto result = IsPlanReachable(s, target, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsTrue()); +} + + + +TEST(ReachabilityTest, OrderByWrongDirectionNotReachable) { + std::stringstream s{"SELECT users.id FROM users ORDER BY users.id ASC;"}; + PhysicalSort target{ + std::make_shared(PhysicalProjection{ + std::make_shared(SeqScan{"users"}), + {Attribute{"users", "id"}}, + {}}), + SortOrder{{SortKey{"users", "id", Direction::kDesc}}}}; + auto result = IsPlanReachable(s, target, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsFalse()); + ASSERT_THAT(result.closest_distance, Eq(1)); +} + + + +TEST(ReachabilityTest, SortNotReachableWithoutOrderBy) { + std::stringstream s{"SELECT users.id FROM users;"}; + PhysicalSort target{ + std::make_shared(PhysicalProjection{ + std::make_shared(SeqScan{"users"}), + {Attribute{"users", "id"}}, + {}}), + SortOrder{{SortKey{"users", "id", Direction::kAsc}}}}; + auto result = IsPlanReachable(s, target, {}, MakeTestSchema()); + ASSERT_THAT(result.reachable, IsFalse()); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/properties/properties_test.cpp b/src/stewkk/sql/logic/optimizer/properties/properties_test.cpp new file mode 100644 index 0000000..4d61bb4 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/properties/properties_test.cpp @@ -0,0 +1,68 @@ +#include + +#include +#include +#include + +using ::testing::IsTrue; +using ::testing::IsFalse; + +namespace stewkk::sql { + +TEST(SortOrderTest, EmptyRequiredAlwaysSatisfied) { + SortOrder delivered{{{"t", "a", Direction::kAsc}, {"t", "b", Direction::kAsc}}}; + SortOrder required{}; + ASSERT_THAT(delivered.Satisfies(required), IsTrue()); +} + +TEST(SortOrderTest, ExactMatchSatisfied) { + SortOrder delivered{{{"t", "a", Direction::kAsc}, {"t", "b", Direction::kDesc}}}; + SortOrder required{{{"t", "a", Direction::kAsc}, {"t", "b", Direction::kDesc}}}; + ASSERT_THAT(delivered.Satisfies(required), IsTrue()); +} + +TEST(SortOrderTest, LongerDeliveredSatisfiesPrefix) { + SortOrder delivered{{{"t", "a", Direction::kAsc}, {"t", "b", Direction::kAsc}, {"t", "c", Direction::kAsc}}}; + SortOrder required{{{"t", "a", Direction::kAsc}, {"t", "b", Direction::kAsc}}}; + ASSERT_THAT(delivered.Satisfies(required), IsTrue()); +} + +TEST(SortOrderTest, ShorterDeliveredDoesNotSatisfy) { + SortOrder delivered{{{"t", "a", Direction::kAsc}}}; + SortOrder required{{{"t", "a", Direction::kAsc}, {"t", "b", Direction::kAsc}}}; + ASSERT_THAT(delivered.Satisfies(required), IsFalse()); +} + +TEST(SortOrderTest, WrongDirectionDoesNotSatisfy) { + SortOrder delivered{{{"t", "a", Direction::kAsc}}}; + SortOrder required{{{"t", "a", Direction::kDesc}}}; + ASSERT_THAT(delivered.Satisfies(required), IsFalse()); +} + +TEST(SortOrderTest, WrongColumnDoesNotSatisfy) { + SortOrder delivered{{{"t", "b", Direction::kAsc}}}; + SortOrder required{{{"t", "a", Direction::kAsc}}}; + ASSERT_THAT(delivered.Satisfies(required), IsFalse()); +} + +TEST(PropertySetTest, AnyAlwaysSatisfied) { + PropertySet delivered{SortProperty{SortOrder{{{"t", "a", Direction::kAsc}}}}}; + ASSERT_THAT(delivered.Satisfies(PropertySet::Any()), IsTrue()); +} + +TEST(PropertySetTest, AnySatisfiesAny) { + ASSERT_THAT(PropertySet::Any().Satisfies(PropertySet::Any()), IsTrue()); +} + +TEST(PropertySetTest, AnyDoesNotSatisfySortRequirement) { + PropertySet required{SortProperty{SortOrder{{{"t", "a", Direction::kAsc}}}}}; + ASSERT_THAT(PropertySet::Any().Satisfies(required), IsFalse()); +} + +TEST(PropertySetTest, SortSatisfiesSortPrefix) { + PropertySet delivered{SortProperty{SortOrder{{{"t", "a", Direction::kAsc}, {"t", "b", Direction::kAsc}}}}}; + PropertySet required{SortProperty{SortOrder{{{"t", "a", Direction::kAsc}}}}}; + ASSERT_THAT(delivered.Satisfies(required), IsTrue()); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/properties/property_set.cpp b/src/stewkk/sql/logic/optimizer/properties/property_set.cpp new file mode 100644 index 0000000..3286b0c --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/properties/property_set.cpp @@ -0,0 +1,74 @@ +#include + +#include +#include +#include + +#include + +namespace stewkk::sql { + +PropertySet::PropertySet(const PropertySet& other) { + props_.reserve(other.props_.size()); + for (const auto& p : other.props_) props_.push_back(p->Clone()); +} + +PropertySet& PropertySet::operator=(const PropertySet& other) { + if (this == &other) return *this; + std::vector> copy; + copy.reserve(other.props_.size()); + for (const auto& p : other.props_) copy.push_back(p->Clone()); + props_ = std::move(copy); + return *this; +} + +PropertySet PropertySet::Any() { return {}; } + +const std::vector>& PropertySet::Items() const noexcept { return props_; } + +void PropertySet::Normalize() { + std::ranges::sort(props_, {}, [](const std::unique_ptr& p) { + const Property& prop = *p; + return std::type_index(typeid(prop)); + }); +} + +bool PropertySet::Satisfies(const PropertySet& required) const { + for (const auto& req : required.props_) { + const Property& req_ref = *req; + auto it = std::ranges::find_if(props_, [&](const std::unique_ptr& d) { + const Property& d_ref = *d; + return typeid(d_ref) == typeid(req_ref); + }); + if (it == props_.end()) return false; + if (!(*it)->Satisfies(*req)) return false; + } + return true; +} + +bool PropertySet::operator==(const PropertySet& other) const { + if (props_.size() != other.props_.size()) return false; + for (std::size_t i = 0; i < props_.size(); ++i) { + const Property& a = *props_[i]; + const Property& b = *other.props_[i]; + if (typeid(a) != typeid(b)) return false; + if (!a.Equals(b)) return false; + } + return true; +} + +} // namespace stewkk::sql + +namespace std { + +size_t hash::operator()(const stewkk::sql::PropertySet& ps) const noexcept { + size_t h = 0; + for (const auto& p : ps.Items()) { + const stewkk::sql::Property& prop = *p; + boost::hash_combine(h, std::type_index(typeid(prop)).hash_code()); + boost::hash_combine(h, prop.Hash()); + } + return h; +} + +} // namespace std diff --git a/src/stewkk/sql/logic/optimizer/properties/sort_order.cpp b/src/stewkk/sql/logic/optimizer/properties/sort_order.cpp new file mode 100644 index 0000000..5c51bed --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/properties/sort_order.cpp @@ -0,0 +1,38 @@ +#include + +#include + +namespace stewkk::sql { + +bool SortOrder::Satisfies(const SortOrder& required) const { + if (required.keys.size() > keys.size()) { + return false; + } + for (size_t i = 0; i < required.keys.size(); ++i) { + if (keys[i] != required.keys[i]) { + return false; + } + } + return true; +} + +} // namespace stewkk::sql + +namespace std { + +size_t hash::operator()(const stewkk::sql::SortKey& k) const noexcept { + size_t h = hash{}(k.table); + boost::hash_combine(h, hash{}(k.column)); + boost::hash_combine(h, static_cast(k.dir)); + return h; +} + +size_t hash::operator()(const stewkk::sql::SortOrder& o) const noexcept { + size_t h = 0; + for (const auto& key : o.keys) { + boost::hash_combine(h, hash{}(key)); + } + return h; +} + +} // namespace std diff --git a/src/stewkk/sql/logic/optimizer/properties/sort_property.cpp b/src/stewkk/sql/logic/optimizer/properties/sort_property.cpp new file mode 100644 index 0000000..b0d671b --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/properties/sort_property.cpp @@ -0,0 +1,23 @@ +#include + +#include + +namespace stewkk::sql { + +std::unique_ptr SortProperty::Clone() const { + return std::make_unique(*this); +} + +bool SortProperty::SatisfiesTyped(const SortProperty& required) const { + return order.Satisfies(required.order); +} + +bool SortProperty::EqualsTyped(const SortProperty& other) const { + return order == other.order; +} + +std::size_t SortProperty::Hash() const { + return std::hash{}(order); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/reachability.cpp b/src/stewkk/sql/logic/optimizer/reachability.cpp new file mode 100644 index 0000000..3c5fcfb --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/reachability.cpp @@ -0,0 +1,433 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +namespace { + +constexpr int kInfiniteDistance = std::numeric_limits::max() / 4; + +struct InternalMatch { + bool ok; + int depth; + std::string reason; +}; + +InternalMatch MatchGroup(Group* group, const PhysicalPlanNode& target, int depth); + +InternalMatch TryMatchExpr(utils::NotNull pe, + const PhysicalPlanNode& target, int depth) { + return std::visit(utils::Overloaded{ + [&](const physical::SeqScan& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected SeqScan"}; + if (op.table != t->table) + return {false, depth + 1, + std::format("SeqScan table '{}' != '{}'", op.table, t->table)}; + if (op.alias != t->alias) + return {false, depth + 1, + std::format("SeqScan alias '{}' != '{}'", + op.alias.value_or(""), t->alias.value_or(""))}; + return {true, depth + 1, {}}; + }, + [&](const physical::IndexSeek& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected IndexSeek"}; + if (op.table != t->table) + return {false, depth + 1, + std::format("IndexSeek table '{}' != '{}'", op.table, t->table)}; + if (op.alias != t->alias) + return {false, depth + 1, + std::format("IndexSeek alias '{}' != '{}'", + op.alias.value_or(""), t->alias.value_or(""))}; + if (!EquivalentPredicate(op.predicate, t->predicate)) + return {false, depth + 1, + std::format("IndexSeek predicate '{}' != '{}'", + ToString(op.predicate), ToString(t->predicate))}; + return {true, depth + 1, {}}; + }, + [&](const physical::Filter& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected Filter"}; + if (!EquivalentPredicate(op.predicate, t->predicate)) + return {false, depth + 1, + std::format("Filter predicate '{}' != '{}'", + ToString(op.predicate), ToString(t->predicate))}; + auto child = MatchGroup(op.source.get(), *t->source, depth + 1); + if (!child.ok) child.reason = "Filter.source: " + child.reason; + return child; + }, + [&](const physical::Projection& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected Projection"}; + if (op.expressions != t->expressions) + return {false, depth + 1, "Projection expressions mismatch"}; + auto child = MatchGroup(op.source.get(), *t->source, depth + 1); + if (!child.ok) child.reason = "Projection.source: " + child.reason; + return child; + }, + [&](const physical::NestedLoopJoin& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected NestedLoopJoin"}; + if (op.type != t->type) + return {false, depth + 1, "NestedLoopJoin join type mismatch"}; + if (!EquivalentPredicate(op.qual, t->qual)) + return {false, depth + 1, + std::format("NestedLoopJoin qual '{}' != '{}'", + ToString(op.qual), ToString(t->qual))}; + auto lhs = MatchGroup(op.lhs.get(), *t->lhs, depth + 1); + if (!lhs.ok) { lhs.reason = "NestedLoopJoin.lhs: " + lhs.reason; return lhs; } + auto rhs = MatchGroup(op.rhs.get(), *t->rhs, depth + 1); + if (!rhs.ok) { rhs.reason = "NestedLoopJoin.rhs: " + rhs.reason; return rhs; } + return {true, std::max(lhs.depth, rhs.depth), {}}; + }, + [&](const physical::NestedLoopCrossJoin& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected NestedLoopCrossJoin"}; + auto lhs = MatchGroup(op.lhs.get(), *t->lhs, depth + 1); + if (!lhs.ok) { lhs.reason = "NestedLoopCrossJoin.lhs: " + lhs.reason; return lhs; } + auto rhs = MatchGroup(op.rhs.get(), *t->rhs, depth + 1); + if (!rhs.ok) { rhs.reason = "NestedLoopCrossJoin.rhs: " + rhs.reason; return rhs; } + return {true, std::max(lhs.depth, rhs.depth), {}}; + }, + [&](const physical::HashJoin& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected HashJoin"}; + if (op.type != t->type) + return {false, depth + 1, "HashJoin join type mismatch"}; + if (!EquivalentPredicate(op.qual, t->qual)) + return {false, depth + 1, + std::format("HashJoin qual '{}' != '{}'", + ToString(op.qual), ToString(t->qual))}; + auto lhs = MatchGroup(op.lhs.get(), *t->lhs, depth + 1); + if (!lhs.ok) { lhs.reason = "HashJoin.lhs: " + lhs.reason; return lhs; } + auto rhs = MatchGroup(op.rhs.get(), *t->rhs, depth + 1); + if (!rhs.ok) { rhs.reason = "HashJoin.rhs: " + rhs.reason; return rhs; } + return {true, std::max(lhs.depth, rhs.depth), {}}; + }, + [&](const physical::MergeJoin& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected MergeJoin"}; + if (op.type != t->type) + return {false, depth + 1, "MergeJoin join type mismatch"}; + if (!EquivalentPredicate(op.qual, t->qual)) + return {false, depth + 1, + std::format("MergeJoin qual '{}' != '{}'", + ToString(op.qual), ToString(t->qual))}; + auto lhs = MatchGroup(op.lhs.get(), *t->lhs, depth + 1); + if (!lhs.ok) { lhs.reason = "MergeJoin.lhs: " + lhs.reason; return lhs; } + auto rhs = MatchGroup(op.rhs.get(), *t->rhs, depth + 1); + if (!rhs.ok) { rhs.reason = "MergeJoin.rhs: " + rhs.reason; return rhs; } + return {true, std::max(lhs.depth, rhs.depth), {}}; + }, + [&](const physical::Sort& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected Sort"}; + if (op.keys != t->keys) + return {false, depth + 1, "Sort keys mismatch"}; + auto child = MatchGroup(op.input.get(), *t->source, depth + 1); + if (!child.ok) child.reason = "Sort.input: " + child.reason; + return child; + }, + [&](const physical::Aggregation& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected HashAggregate"}; + if (op.group_by != t->group_by) + return {false, depth + 1, "Aggregation group_by mismatch"}; + if (op.aggregates != t->aggregates) + return {false, depth + 1, "Aggregation aggregates mismatch"}; + auto child = MatchGroup(op.source.get(), *t->source, depth + 1); + if (!child.ok) child.reason = "Aggregation.source: " + child.reason; + return child; + }, + [&](const physical::StreamAggregation& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected StreamAggregate"}; + if (op.group_by != t->group_by) + return {false, depth + 1, "StreamAggregation group_by mismatch"}; + if (op.aggregates != t->aggregates) + return {false, depth + 1, "StreamAggregation aggregates mismatch"}; + auto child = MatchGroup(op.source.get(), *t->source, depth + 1); + if (!child.ok) child.reason = "StreamAggregation.source: " + child.reason; + return child; + }, + [&](const physical::PartialAggregation& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected PartialAggregate"}; + if (op.group_by != t->group_by) + return {false, depth + 1, "PartialAggregation group_by mismatch"}; + if (op.aggregates != t->aggregates) + return {false, depth + 1, "PartialAggregation aggregates mismatch"}; + auto child = MatchGroup(op.source.get(), *t->source, depth + 1); + if (!child.ok) child.reason = "PartialAggregation.source: " + child.reason; + return child; + }, + [&](const physical::FinalAggregation& op) -> InternalMatch { + const auto* t = std::get_if(&target.node); + if (!t) return {false, depth, "type mismatch: expected FinalAggregate"}; + if (op.group_by != t->group_by) + return {false, depth + 1, "FinalAggregation group_by mismatch"}; + if (op.aggregates != t->aggregates) + return {false, depth + 1, "FinalAggregation aggregates mismatch"}; + auto child = MatchGroup(op.source.get(), *t->source, depth + 1); + if (!child.ok) child.reason = "FinalAggregation.source: " + child.reason; + return child; + }, + }, pe->root_operator); +} + +InternalMatch MatchGroup(Group* group, const PhysicalPlanNode& target, int depth) { + InternalMatch best{false, -1, + std::format("group {}: no physical expressions generated", group->GetId())}; + for (auto pe : group->GetPhysicalExprs()) { + auto r = TryMatchExpr(pe, target, depth); + if (r.ok) return r; + if (r.depth > best.depth) best = r; + } + return best; +} + +std::vector Children(const PhysicalExpr& expr) { + return std::visit(utils::Overloaded{ + [](const physical::SeqScan&) { return std::vector{}; }, + [](const physical::IndexSeek&) { return std::vector{}; }, + [](const physical::Filter& op) { return std::vector{op.source.get()}; }, + [](const physical::Projection& op) { return std::vector{op.source.get()}; }, + [](const physical::NestedLoopJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const physical::NestedLoopCrossJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const physical::HashJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const physical::MergeJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const physical::Sort& op) { return std::vector{op.input.get()}; }, + [](const physical::Aggregation& op) { return std::vector{op.source.get()}; }, + [](const physical::StreamAggregation& op) { return std::vector{op.source.get()}; }, + [](const physical::PartialAggregation& op) { return std::vector{op.source.get()}; }, + [](const physical::FinalAggregation& op) { return std::vector{op.source.get()}; }, + }, expr.root_operator); +} + +std::vector Children(const PhysicalPlanNode& node) { + return std::visit(utils::Overloaded{ + [](const SeqScan&) { return std::vector{}; }, + [](const IndexSeek&) { return std::vector{}; }, + [](const PhysicalFilter& op) { return std::vector{op.source.get()}; }, + [](const PhysicalProjection& op) { return std::vector{op.source.get()}; }, + [](const NestedLoopJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const NestedLoopCrossJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const HashJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const MergeJoin& op) { + return std::vector{op.lhs.get(), op.rhs.get()}; + }, + [](const PhysicalSort& op) { return std::vector{op.source.get()}; }, + [](const PhysicalAggregation& op) { return std::vector{op.source.get()}; }, + [](const PhysicalStreamAggregation& op) { return std::vector{op.source.get()}; }, + [](const PhysicalPartialAggregation& op) { return std::vector{op.source.get()}; }, + [](const PhysicalFinalAggregation& op) { return std::vector{op.source.get()}; }, + }, node.node); +} + +int TargetTreeSize(const PhysicalPlanNode& node) { + int size = 1; + for (const auto* child : Children(node)) { + size += TargetTreeSize(*child); + } + return size; +} + +struct PairHash { + size_t operator()(const std::pair& p) const noexcept { + size_t h = std::hash{}(p.first); + h ^= std::hash{}(p.second) + 0x9e3779b9 + (h << 6) + (h >> 2); + return h; + } +}; + +class TreeDistance { + public: + int Distance(Group* group, const PhysicalPlanNode& target) { + auto key = std::pair{group, &target}; + if (auto it = distance_memo_.find(key); it != distance_memo_.end()) { + return it->second; + } + if (!distance_visiting_.insert(key).second) { + return kInfiniteDistance; + } + + int best = kInfiniteDistance; + for (auto pe : group->GetPhysicalExprs()) { + best = std::min(best, Distance(*pe, target)); + } + distance_visiting_.erase(key); + distance_memo_.emplace(key, best); + return best; + } + + private: + int MinTreeSize(Group* group) { + if (auto it = size_memo_.find(group); it != size_memo_.end()) { + return it->second; + } + if (!size_visiting_.insert(group).second) { + return kInfiniteDistance; + } + + int best = kInfiniteDistance; + for (auto pe : group->GetPhysicalExprs()) { + int size = 1; + for (auto* child : Children(*pe)) { + int child_size = MinTreeSize(child); + if (child_size >= kInfiniteDistance) { + size = kInfiniteDistance; + break; + } + size += child_size; + } + best = std::min(best, size); + } + + size_visiting_.erase(group); + size_memo_.emplace(group, best); + return best; + } + + int Distance(const PhysicalExpr& expr, const PhysicalPlanNode& target) { + const auto source_children = Children(expr); + const auto target_children = Children(target); + return LabelCost(expr, target) + ChildrenDistance(source_children, target_children); + } + + int ChildrenDistance(const std::vector& source_children, + const std::vector& target_children) { + std::vector> dp( + source_children.size() + 1, std::vector(target_children.size() + 1, 0)); + + for (size_t i = 1; i <= source_children.size(); ++i) { + dp[i][0] = dp[i - 1][0] + MinTreeSize(source_children[i - 1]); + } + for (size_t j = 1; j <= target_children.size(); ++j) { + dp[0][j] = dp[0][j - 1] + TargetTreeSize(*target_children[j - 1]); + } + + for (size_t i = 1; i <= source_children.size(); ++i) { + for (size_t j = 1; j <= target_children.size(); ++j) { + const int del = dp[i - 1][j] + MinTreeSize(source_children[i - 1]); + const int ins = dp[i][j - 1] + TargetTreeSize(*target_children[j - 1]); + const int sub = dp[i - 1][j - 1] + + Distance(source_children[i - 1], *target_children[j - 1]); + dp[i][j] = std::min({del, ins, sub}); + } + } + return dp[source_children.size()][target_children.size()]; + } + + static int LabelCost(const PhysicalExpr& expr, const PhysicalPlanNode& target) { + return std::visit(utils::Overloaded{ + [](const physical::SeqScan& op, const SeqScan& t) { + return op.table == t.table && op.alias == t.alias ? 0 : 1; + }, + [](const physical::IndexSeek& op, const IndexSeek& t) { + return op.table == t.table && op.alias == t.alias + && EquivalentPredicate(op.predicate, t.predicate) + ? 0 + : 1; + }, + [](const physical::Filter& op, const PhysicalFilter& t) { + return EquivalentPredicate(op.predicate, t.predicate) ? 0 : 1; + }, + [](const physical::Projection& op, const PhysicalProjection& t) { + return op.expressions == t.expressions ? 0 : 1; + }, + [](const physical::NestedLoopJoin& op, const NestedLoopJoin& t) { + return op.type == t.type && EquivalentPredicate(op.qual, t.qual) ? 0 : 1; + }, + [](const physical::NestedLoopCrossJoin&, const NestedLoopCrossJoin&) { + return 0; + }, + [](const physical::HashJoin& op, const HashJoin& t) { + return op.type == t.type && EquivalentPredicate(op.qual, t.qual) ? 0 : 1; + }, + [](const physical::MergeJoin& op, const MergeJoin& t) { + return op.type == t.type && EquivalentPredicate(op.qual, t.qual) ? 0 : 1; + }, + [](const physical::Sort& op, const PhysicalSort& t) { + return op.keys == t.keys ? 0 : 1; + }, + [](const physical::Aggregation& op, const PhysicalAggregation& t) { + return op.group_by == t.group_by && op.aggregates == t.aggregates ? 0 : 1; + }, + [](const physical::StreamAggregation& op, const PhysicalStreamAggregation& t) { + return op.group_by == t.group_by && op.aggregates == t.aggregates ? 0 : 1; + }, + [](const physical::PartialAggregation& op, const PhysicalPartialAggregation& t) { + return op.group_by == t.group_by && op.aggregates == t.aggregates ? 0 : 1; + }, + [](const physical::FinalAggregation& op, const PhysicalFinalAggregation& t) { + return op.group_by == t.group_by && op.aggregates == t.aggregates ? 0 : 1; + }, + [](const auto&, const auto&) { + return 1; + }, + }, expr.root_operator, target.node); + } + + std::unordered_map, int, PairHash> distance_memo_; + std::unordered_set, PairHash> distance_visiting_; + std::unordered_map size_memo_; + std::unordered_set size_visiting_; +}; + + +} // namespace + +MatchResult IsReachable(utils::NotNull root, const PhysicalPlanNode& target) { + auto r = MatchGroup(root.get(), target, 0); + TreeDistance distance; + return {r.ok, r.reason, distance.Distance(root.get(), target)}; +} + +MatchResult IsPlanReachable(std::istream& sql, const PhysicalPlanNode& target, + CardinalityEstimates cardinality, SchemaCatalog schema, + IndexCatalog indexes) { + auto parsed = GetAST(sql).value(); + PropertySet required = parsed.required_order + ? PropertySet{SortProperty{*parsed.required_order}} + : PropertySet::Any(); + Optimizer optimizer(parsed.op, MakeMainRules(std::move(indexes)), std::move(cardinality), + std::move(schema), std::move(required)); + auto plan = optimizer.OptimizeExhaustive(); + auto result = IsReachable(optimizer.GetRootGroup(), target); + if (!result.reachable) { + OutputDot(plan, target); + } + return result; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/rule.cpp b/src/stewkk/sql/logic/optimizer/rule.cpp new file mode 100644 index 0000000..e3237ad --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/rule.cpp @@ -0,0 +1,12 @@ +#include + +#include + +namespace stewkk::sql { + +utils::NotNull TransformationRule::Apply(utils::NotNull expr, + Memo& memo, RuleContext& ctx) { + return memo.AddLogicalExprToGroup(expr->group, ApplyImpl(expr, memo, ctx)); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/rule_test.cpp b/src/stewkk/sql/logic/optimizer/rule_test.cpp new file mode 100644 index 0000000..a4e7f8e --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/rule_test.cpp @@ -0,0 +1,304 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +namespace { + +Expression Eq(Attribute lhs, Attribute rhs) { + return BinaryExpression{ + std::make_shared(std::move(lhs)), + BinaryOp::kEq, + std::make_shared(std::move(rhs))}; +} + +Expression EqConst(Attribute lhs, IntConst rhs) { + return BinaryExpression{ + std::make_shared(std::move(lhs)), + BinaryOp::kEq, + std::make_shared(rhs)}; +} + +Expression Sum(Attribute attr) { + return AggregateExpression{ + AggregateFunction::kSum, + std::make_shared(std::move(attr)), + false}; +} + +} // namespace + +class JoinCommutativityTest : public ::testing::Test { + protected: + void SetUp() override { + a = memo.AddGroup(logical::Table{"a"})->group; + b = memo.AddGroup(logical::Table{"b"})->group; + } + + Memo memo; + Group* a = nullptr; + Group* b = nullptr; + JoinCommutativity rule; + SchemaCatalog schema; + ConstraintCatalog constraints; +}; + +TEST_F(JoinCommutativityTest, ReturnsSwappedJoin) { + auto join_group = memo.AddGroup(logical::Join{a, b, JoinType::kInner, Literal::kTrue})->group; + auto expr = join_group->GetLogicalExprs()[0]; + + RuleContext ctx{schema, constraints}; + auto result = rule.Apply(expr, memo, ctx); + + const auto& join = std::get(result->root_operator); + EXPECT_EQ(join.lhs.get(), b); + EXPECT_EQ(join.rhs.get(), a); +} + +TEST_F(JoinCommutativityTest, AddsNewJoinIntoGroup) { + auto join_group = memo.AddGroup(logical::Join{a, b, JoinType::kInner, Literal::kTrue})->group; + auto expr = join_group->GetLogicalExprs()[0]; + + RuleContext ctx{schema, constraints}; + rule.Apply(expr, memo, ctx); + + EXPECT_EQ(join_group->GetLogicalExprs().size(), 2u); + const auto& new_join = std::get(join_group->GetLogicalExprs()[1]->root_operator); + EXPECT_EQ(new_join.lhs.get(), b); + EXPECT_EQ(new_join.rhs.get(), a); +} + +class JoinAssociativityTest : public ::testing::Test { + protected: + void SetUp() override { + a = memo.AddGroup(logical::Table{"a"})->group; + b = memo.AddGroup(logical::Table{"b"})->group; + c = memo.AddGroup(logical::Table{"c"})->group; + ab = memo.AddGroup(logical::Join{a, b, JoinType::kInner, Literal::kTrue})->group; + abc = memo.AddGroup(logical::Join{ab, c, JoinType::kInner, Literal::kFalse})->group; + } + + Memo memo; + Group* a = nullptr; + Group* b = nullptr; + Group* c = nullptr; + Group* ab = nullptr; + Group* abc = nullptr; + JoinAssociativity rule; + SchemaCatalog schema; + ConstraintCatalog constraints; +}; + +TEST_F(JoinAssociativityTest, CreatesNewGroup) { + RuleContext ctx{schema, constraints}; + rule.Apply(abc->GetLogicalExprs()[0], memo, ctx); + + EXPECT_EQ(memo.GroupCount(), 6u); +} + +TEST_F(JoinAssociativityTest, ReturnsCorrectExpression) { + RuleContext ctx{schema, constraints}; + auto result = rule.Apply(abc->GetLogicalExprs()[0], memo, ctx); + + const auto& outer = std::get(result->root_operator); + EXPECT_EQ(outer.lhs.get(), a); + EXPECT_EQ(outer.type, JoinType::kInner); + const auto& inner = std::get(outer.rhs->GetLogicalExprs()[0]->root_operator); + EXPECT_EQ(inner.lhs.get(), b); + EXPECT_EQ(inner.rhs.get(), c); + EXPECT_EQ(inner.qual, Expression{Literal::kFalse}); + EXPECT_EQ(outer.qual, Expression{Literal::kTrue}); +} + +TEST_F(JoinAssociativityTest, DoesNotApplyWhenInnerJoinIsOuter) { + auto outer_ab = memo.AddGroup(logical::Join{a, b, JoinType::kFull, Literal::kTrue})->group; + auto expr = memo.AddGroup( + logical::Join{outer_ab, c, JoinType::kInner, Literal::kTrue}); + + RuleContext ctx{schema, constraints}; + EXPECT_FALSE(rule.IsApplicable(expr, ctx)); +} + +TEST_F(JoinAssociativityTest, DoesNotApplyWhenOuterJoinIsOuter) { + auto expr = memo.AddGroup( + logical::Join{ab, c, JoinType::kFull, Literal::kTrue}); + + RuleContext ctx{schema, constraints}; + EXPECT_FALSE(rule.IsApplicable(expr, ctx)); +} + +TEST(TransformationRulesTest, MovesFilterIntoInnerJoinPredicate) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto join = memo.AddGroup(logical::Join{a, b, JoinType::kInner, Literal::kTrue})->group; + auto filter = memo.AddGroup(logical::Filter{join, Eq(Attribute{"a", "id"}, Attribute{"b", "id"})}); + SchemaCatalog schema; + ConstraintCatalog constraints; + RuleContext ctx{schema, constraints}; + FilterToJoinPredicate rule; + + auto result = rule.Apply(filter, memo, ctx); + + const auto& got = std::get(result->root_operator); + EXPECT_EQ(got.type, JoinType::kInner); + EXPECT_EQ(got.qual, Eq(Attribute{"a", "id"}, Attribute{"b", "id"})); +} + +TEST(TransformationRulesTest, ConvertsFilteredCrossJoinToInnerJoin) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto cross = memo.AddGroup(logical::CrossJoin{a, b})->group; + auto pred = Eq(Attribute{"a", "id"}, Attribute{"b", "id"}); + auto filter = memo.AddGroup(logical::Filter{cross, pred}); + SchemaCatalog schema; + ConstraintCatalog constraints; + RuleContext ctx{schema, constraints}; + CrossJoinToJoin rule; + + auto result = rule.Apply(filter, memo, ctx); + + const auto& got = std::get(result->root_operator); + EXPECT_EQ(got.type, JoinType::kInner); + EXPECT_EQ(got.qual, pred); +} + +TEST(TransformationRulesTest, ConvertsNullRejectedLeftJoinToInnerJoin) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto join = memo.AddGroup(logical::Join{ + a, b, JoinType::kLeft, Eq(Attribute{"a", "id"}, Attribute{"b", "aid"})})->group; + auto filter = memo.AddGroup(logical::Filter{join, EqConst(Attribute{"b", "aid"}, 7)}); + SchemaCatalog schema; + ConstraintCatalog constraints; + RuleContext ctx{schema, constraints}; + OuterJoinToInner rule; + + auto result = rule.Apply(filter, memo, ctx); + + const auto& got_filter = std::get(result->root_operator); + const auto& got_join = std::get( + got_filter.source->GetLogicalExprs()[0]->root_operator); + EXPECT_EQ(got_join.type, JoinType::kInner); +} + +TEST(TransformationRulesTest, PushesProjectionBelowJoinInputs) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto join = memo.AddGroup(logical::Join{ + a, b, JoinType::kInner, Eq(Attribute{"a", "id"}, Attribute{"b", "aid"})})->group; + auto projection = memo.AddGroup(logical::Projection{ + join, {Attribute{"a", "x"}}, {}}); + SchemaCatalog schema({ + {"a", {Attribute{"a", "id"}, Attribute{"a", "x"}, Attribute{"a", "unused"}}}, + {"b", {Attribute{"b", "aid"}, Attribute{"b", "unused"}}}, + }); + ConstraintCatalog constraints; + RuleContext ctx{schema, constraints}; + ProjectionPushdownThroughJoin rule; + + auto result = rule.Apply(projection, memo, ctx); + + const auto& got_projection = std::get(result->root_operator); + const auto& got_join = std::get( + got_projection.source->GetLogicalExprs()[0]->root_operator); + EXPECT_TRUE(std::holds_alternative( + got_join.lhs->GetLogicalExprs()[0]->root_operator)); + EXPECT_TRUE(std::holds_alternative( + got_join.rhs->GetLogicalExprs()[0]->root_operator)); +} + +TEST(TransformationRulesTest, PushesPartialAggregationBelowJoin) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto join = memo.AddGroup(logical::Join{ + a, b, JoinType::kInner, Eq(Attribute{"a", "id"}, Attribute{"b", "aid"})})->group; + auto agg = memo.AddGroup(logical::Aggregation{ + join, {Attribute{"a", "id"}}, {Sum(Attribute{"a", "x"})}}); + SchemaCatalog schema; + ConstraintCatalog constraints; + RuleContext ctx{schema, constraints}; + AggregationPushdownThroughJoin rule; + + auto result = rule.Apply(agg, memo, ctx); + + const auto& final = std::get(result->root_operator); + const auto& new_join = std::get( + final.source->GetLogicalExprs()[0]->root_operator); + EXPECT_TRUE(std::holds_alternative( + new_join.lhs->GetLogicalExprs()[0]->root_operator)); +} + +TEST(TransformationRulesTest, PartialAggregationBelowJoinKeepsOnlyLeftGroupBy) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto join = memo.AddGroup(logical::Join{ + a, b, JoinType::kInner, Eq(Attribute{"a", "id"}, Attribute{"b", "aid"})})->group; + auto agg = memo.AddGroup(logical::Aggregation{ + join, + {Attribute{"a", "region"}, Attribute{"b", "brand"}}, + {Sum(Attribute{"a", "x"})}}); + SchemaCatalog schema; + ConstraintCatalog constraints; + RuleContext ctx{schema, constraints}; + AggregationPushdownThroughJoin rule; + + auto result = rule.Apply(agg, memo, ctx); + + const auto& final = std::get(result->root_operator); + const auto& new_join = std::get( + final.source->GetLogicalExprs()[0]->root_operator); + const auto& partial = std::get( + new_join.lhs->GetLogicalExprs()[0]->root_operator); + EXPECT_THAT(partial.group_by, ::testing::ElementsAre( + Expression{Attribute{"a", "region"}}, + Expression{Attribute{"a", "id"}})); + EXPECT_THAT(final.group_by, ::testing::ElementsAre( + Expression{Attribute{"a", "region"}}, + Expression{Attribute{"b", "brand"}})); +} + +TEST(TransformationRulesTest, TransposesAggregationAndJoinWithConstraints) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto join = memo.AddGroup(logical::Join{ + a, b, JoinType::kInner, Eq(Attribute{"a", "id"}, Attribute{"b", "id"})})->group; + auto agg = memo.AddGroup(logical::Aggregation{ + join, {Attribute{"a", "id"}}, {Sum(Attribute{"a", "x"})}}); + SchemaCatalog schema; + ConstraintCatalog constraints( + {UniqueKeyInfo{.table = "b", .column = "id"}}, + {ForeignKeyInfo{.from_table = "a", .from_column = "id", + .to_table = "b", .to_column = "id"}}); + RuleContext ctx{schema, constraints}; + AggregationJoinTranspose rule; + + auto result = rule.Apply(agg, memo, ctx); + + const auto& projection = std::get(result->root_operator); + const auto& new_join = std::get( + projection.source->GetLogicalExprs()[0]->root_operator); + EXPECT_TRUE(std::holds_alternative( + new_join.lhs->GetLogicalExprs()[0]->root_operator)); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/rules.cpp b/src/stewkk/sql/logic/optimizer/rules.cpp new file mode 100644 index 0000000..48dc4d5 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/rules.cpp @@ -0,0 +1,89 @@ +#include + +#include + +namespace stewkk::sql { + +Rules<14, 9> MakeMainRules(IndexCatalog indexes) { + return { + .transformation_rules = { + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + }, + .implementation_rules = { + std::make_unique(), + std::make_unique(), + std::make_unique(std::move(indexes)), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + }, + .transformation_rule_names = { + "JoinCommutativity", + "JoinAssociativity", + "FilterSplit", + "FilterMerge", + "FilterPushdownThroughProjection", + "FilterPushdownThroughJoin", + "InToOrChain", + "FilterToJoinPredicate", + "CrossJoinToJoin", + "FilterLiftThroughJoin", + "ProjectionPushdownThroughJoin", + "OuterJoinToInner", + "AggregationPushdownThroughJoin", + "AggregationJoinTranspose", + }, + .implementation_rule_names = { + "ImplementTable", + "ImplementFilter", + "ImplementIndexSeek", + "ImplementProjection", + "ImplementJoin", + "ImplementCrossJoin", + "ImplementHashJoin", + "ImplementMergeJoin", + "ImplementAggregation", + }, + }; +} + +Rules<0, 6> MakeNaiveRules() { + return { + .transformation_rules = {}, + .implementation_rules = { + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + std::make_unique(), + }, + .transformation_rule_names = {}, + .implementation_rule_names = { + "ImplementTable", + "ImplementFilter", + "ImplementProjection", + "ImplementJoin", + "ImplementCrossJoin", + "ImplementAggregation", + }, + }; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/rules_applier.cpp b/src/stewkk/sql/logic/optimizer/rules_applier.cpp new file mode 100644 index 0000000..1b81376 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/rules_applier.cpp @@ -0,0 +1,67 @@ +#include + +namespace stewkk::sql { + +template +RulesApplier::RulesApplier(Rules rules) + : rules_(std::move(rules)) {} + +template +bool RulesApplier::IsApplicable( + TransformationRuleId rule, utils::NotNull expr, RuleContext& ctx) { + return !applied_transformation_rules_[expr.get()][rule.value] && + rules_.transformation_rules[rule.value]->IsApplicable(expr, ctx); +} + +template +utils::NotNull RulesApplier::Apply( + TransformationRuleId rule, utils::NotNull expr, Memo& memo, RuleContext& ctx) { + applied_transformation_rules_[expr.get()][rule.value] = 1; + Memo::ScopedLogicalProvenance provenance{ + memo, + Memo::LogicalProvenance{ + .rule_id = rule.value, + .rule_name = rules_.transformation_rule_names[rule.value], + .source = expr.get(), + }, + }; + return rules_.transformation_rules[rule.value]->Apply(expr, memo, ctx); +} + +template +bool RulesApplier::IsApplicable(ImplementationRuleId rule, utils::NotNull expr) { + return !applied_implementation_rules_[expr.get()][rule.value] && + rules_.implementation_rules[rule.value]->IsApplicable(expr); +} + +template +std::vector> RulesApplier::Apply(ImplementationRuleId rule, utils::NotNull expr, Memo& memo) { + applied_implementation_rules_[expr.get()][rule.value] = 1; + auto result = rules_.implementation_rules[rule.value]->Apply(expr, memo); + for (auto physical_expr : result) { + physical_expr->provenance = PhysicalExpr::Provenance{ + .kind = PhysicalExpr::ProvenanceKind::kImplementation, + .rule_id = rule.value, + .rule_name = rules_.implementation_rule_names[rule.value], + .source = expr.get(), + }; + } + return result; +} + +template +std::string_view RulesApplier::GetTransformationRuleName( + TransformationRuleId rule) const { + return rules_.transformation_rule_names[rule.value]; +} + +template +std::string_view RulesApplier::GetImplementationRuleName( + ImplementationRuleId rule) const { + return rules_.implementation_rule_names[rule.value]; +} + +template class RulesApplier<14, 9>; +template class RulesApplier<0, 6>; + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/rules_applier_test.cpp b/src/stewkk/sql/logic/optimizer/rules_applier_test.cpp new file mode 100644 index 0000000..699b4c3 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/rules_applier_test.cpp @@ -0,0 +1,29 @@ +#include + +#include + +namespace stewkk::sql { + +TEST(RulesApplierTest, ChecksThatRuleAlreadyApplied) { + Memo memo; + auto a = memo.AddGroup(logical::Table{"a"})->group; + auto b = memo.AddGroup(logical::Table{"b"})->group; + auto join_group = memo.AddGroup(logical::Join{a, b, JoinType::kInner, Literal::kTrue})->group; + auto expr = join_group->GetLogicalExprs()[0]; + + RulesApplier applier(MakeMainRules()); + constexpr TransformationRuleId kJoinCommutativity{0}; + SchemaCatalog schema; + ConstraintCatalog constraints; + RuleContext ctx{schema, constraints}; + + EXPECT_TRUE(applier.IsApplicable(kJoinCommutativity, expr, ctx)); + applier.Apply(kJoinCommutativity, expr, memo, ctx); + EXPECT_FALSE(applier.IsApplicable(kJoinCommutativity, expr, ctx)); +} + +TEST(RulesApplierTest, ApplyImplementationRule) { + // FIXME +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/schema_catalog.cpp b/src/stewkk/sql/logic/optimizer/schema_catalog.cpp new file mode 100644 index 0000000..54ffb2c --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/schema_catalog.cpp @@ -0,0 +1,264 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace stewkk::sql { + +IndexCatalog::IndexCatalog(std::vector indexes) + : indexes_(std::move(indexes)) {} + +bool IndexCatalog::HasSortedIndex(const std::string& table, const std::string& column) const { + return std::ranges::any_of(indexes_, [&](const IndexInfo& index) { + return index.table == table && index.column == column && index.type == "sorted"; + }); +} + +ConstraintCatalog::ConstraintCatalog(std::vector unique_keys, + std::vector foreign_keys) + : unique_keys_(std::move(unique_keys)), foreign_keys_(std::move(foreign_keys)) {} + +bool ConstraintCatalog::IsUnique(const Attribute& attr) const { + return std::ranges::any_of(unique_keys_, [&](const UniqueKeyInfo& key) { + return key.table == attr.table && key.column == attr.name; + }); +} + +bool ConstraintCatalog::HasForeignKey(const Attribute& from, const Attribute& to) const { + return std::ranges::any_of(foreign_keys_, [&](const ForeignKeyInfo& fk) { + return fk.from_table == from.table && fk.from_column == from.name + && fk.to_table == to.table && fk.to_column == to.name; + }); +} + +SchemaCatalog::SchemaCatalog(std::unordered_map tables) + : tables_(std::move(tables)) {} + +Schema SchemaCatalog::GetSchema(utils::NotNull group) { + if (auto it = cache_.find(group.get()); it != cache_.end()) { + return it->second; + } + auto schema = Derive(group->GetLogicalExprs().front()->root_operator); + cache_[group.get()] = schema; + return schema; +} + +std::int64_t SchemaCatalog::GetWidth(utils::NotNull group) { + return std::max(1, GetSchema(group).size()); +} + +std::optional SchemaCatalog::ResolveBaseAttribute(const Attribute& attr, + utils::NotNull group) { + auto schema = GetSchema(group); + auto it = std::ranges::find_if(schema, [&](const Attribute& schema_attr) { + return schema_attr.name == attr.name + && (attr.table.empty() || attr.table == schema_attr.table); + }); + if (it == schema.end()) { + return std::nullopt; + } + return *it; +} + +// TODO: refactor to remove duplicate logic: both executor and optimizer derive +// attributes +Schema SchemaCatalog::Derive(const LogicalOperator& op) { + return std::visit(utils::Overloaded{ + [this](const logical::Table& t) -> Schema { + auto it = tables_.find(t.name); + if (it == tables_.end()) { + throw std::runtime_error{std::format("missing schema for table '{}'", t.name)}; + } + auto schema = it->second; + for (auto& attr : schema) { + attr.table = std::string{VisibleName(t)}; + } + return schema; + }, + [this](const logical::Filter& f) -> Schema { + return GetSchema(f.source); + }, + [](const logical::Projection& p) -> Schema { + Schema out; + for (size_t i = 0; i < p.expressions.size(); ++i) { + if (i < p.aliases.size() && p.aliases[i]) { + out.push_back(Attribute{"", *p.aliases[i]}); + } else if (const auto* a = std::get_if(&p.expressions[i])) { + out.push_back(*a); + } + } + return out; + }, + [this](const logical::Aggregation& a) -> Schema { + GetSchema(a.source); + Schema out; + for (const auto& expr : a.group_by) { + if (const auto* attr = std::get_if(&expr)) { + out.push_back(*attr); + } + } + for (size_t i = 0; i < a.aggregates.size(); ++i) { + out.push_back(Attribute{"", std::format("__agg{}", i)}); + } + return out; + }, + [this](const logical::PartialAggregation& a) -> Schema { + GetSchema(a.source); + Schema out; + for (const auto& expr : a.group_by) { + if (const auto* attr = std::get_if(&expr)) { + out.push_back(*attr); + } + } + for (size_t i = 0; i < a.aggregates.size(); ++i) { + out.push_back(Attribute{"", std::format("__agg{}", i)}); + } + return out; + }, + [this](const logical::FinalAggregation& a) -> Schema { + GetSchema(a.source); + Schema out; + for (const auto& expr : a.group_by) { + if (const auto* attr = std::get_if(&expr)) { + out.push_back(*attr); + } + } + for (size_t i = 0; i < a.aggregates.size(); ++i) { + out.push_back(Attribute{"", std::format("__agg{}", i)}); + } + return out; + }, + [this](const logical::CrossJoin& j) -> Schema { + auto l = GetSchema(j.lhs); + auto r = GetSchema(j.rhs); + l.insert(l.end(), r.begin(), r.end()); + return l; + }, + [this](const logical::Join& j) -> Schema { + auto l = GetSchema(j.lhs); + auto r = GetSchema(j.rhs); + l.insert(l.end(), r.begin(), r.end()); + return l; + }, + }, op); +} + +SchemaCatalog LoadSchemaFromCsvDir(const std::filesystem::path& dir) { + std::unordered_map tables; + if (!std::filesystem::is_directory(dir)) { + return SchemaCatalog{}; + } + + static const std::regex kBench{R"(_\d+$)"}; + for (const auto& entry : std::filesystem::directory_iterator{dir}) { + if (entry.path().extension() != ".csv") continue; + auto stem = entry.path().stem().string(); + if (std::regex_search(stem, kBench)) continue; + + std::ifstream in{entry.path()}; + std::string header; + if (!std::getline(in, header)) continue; + + Schema schema; + for (const auto& part : header | std::views::split(',')) { + std::string token{part.begin(), part.end()}; + auto colon = token.find(':'); + if (colon == std::string::npos) continue; + schema.push_back(Attribute{stem, token.substr(0, colon)}); + } + tables.emplace(std::move(stem), std::move(schema)); + } + return SchemaCatalog{std::move(tables)}; +} + +IndexCatalog LoadIndexCatalogFromCsvDir(const std::filesystem::path& dir) { + std::vector indexes; + std::ifstream input{dir / "indexes.meta"}; + if (!input) { + return IndexCatalog{}; + } + + std::string line; + while (std::getline(input, line)) { + auto first = line.find_first_not_of(" \t\r\n"); + if (first == std::string::npos || line[first] == '#') { + continue; + } + + std::istringstream fields{line}; + IndexInfo index; + if (fields >> index.table >> index.column >> index.type >> index.file) { + indexes.push_back(std::move(index)); + } + } + return IndexCatalog{std::move(indexes)}; +} + +ConstraintCatalog LoadConstraintCatalogFromCsvDir(const std::filesystem::path& dir) { + std::vector unique_keys; + std::vector foreign_keys; + std::ifstream input{dir / "constraints.meta"}; + if (!input) { + return ConstraintCatalog{}; + } + + std::string line; + while (std::getline(input, line)) { + auto first = line.find_first_not_of(" \t\r\n"); + if (first == std::string::npos || line[first] == '#') { + continue; + } + + std::istringstream fields{line}; + std::string kind; + fields >> kind; + if (kind == "unique") { + UniqueKeyInfo key; + if (fields >> key.table >> key.column) { + unique_keys.push_back(std::move(key)); + } + } else if (kind == "foreign_key") { + ForeignKeyInfo fk; + if (fields >> fk.from_table >> fk.from_column >> fk.to_table >> fk.to_column) { + foreign_keys.push_back(std::move(fk)); + } + } + } + return ConstraintCatalog{std::move(unique_keys), std::move(foreign_keys)}; +} + +std::unordered_map LoadTableSizesFromCsvDir( + const std::filesystem::path& dir) { + std::unordered_map sizes; + if (!std::filesystem::is_directory(dir)) { + return sizes; + } + + static const std::regex kBench{R"(_\d+$)"}; + for (const auto& entry : std::filesystem::directory_iterator{dir}) { + if (entry.path().extension() != ".csv") continue; + auto stem = entry.path().stem().string(); + if (std::regex_search(stem, kBench)) continue; + + std::ifstream in{entry.path()}; + std::string line; + if (!std::getline(in, line)) continue; // skip header + + std::int64_t rows = 0; + while (std::getline(in, line)) { + if (!line.empty()) ++rows; + } + sizes.emplace(std::move(stem), rows); + } + return sizes; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/sort_enforcer.cpp b/src/stewkk/sql/logic/optimizer/sort_enforcer.cpp new file mode 100644 index 0000000..d5c0060 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/sort_enforcer.cpp @@ -0,0 +1,28 @@ +#include + +#include +#include + +namespace stewkk::sql { + +std::optional SortEnforcer::TryBuild( + utils::NotNull group, + const PropertySet& required, + SchemaCatalog& schema) const { + const auto* req = required.Get(); + if (!req) return std::nullopt; + auto sch = schema.GetSchema(group); + for (const auto& sk : req->order.keys) { + bool found = false; + for (const auto& a : sch) { + if (a.name == sk.column && (sk.table.empty() || a.table == sk.table)) { + found = true; + break; + } + } + if (!found) return std::nullopt; + } + return PhysicalOperator{physical::Sort{group, req->order}}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/optimizer/winner_key.cpp b/src/stewkk/sql/logic/optimizer/winner_key.cpp new file mode 100644 index 0000000..3861905 --- /dev/null +++ b/src/stewkk/sql/logic/optimizer/winner_key.cpp @@ -0,0 +1,15 @@ +#include + +#include + +#include + +namespace std { + +size_t hash::operator()(const stewkk::sql::WinnerKey& k) const noexcept { + size_t h = std::hash{}(k.group); + boost::hash_combine(h, std::hash{}(k.required)); + return h; +} + +} // namespace std diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 0615f49..f7deaeb 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -11,7 +11,7 @@ namespace stewkk::sql { -Result GetAST(std::istream& in) { +Result GetAST(std::istream& in) { antlr4::ANTLRInputStream antlr_input(in); codegen::PostgreSQLLexer lexer(&antlr_input); @@ -44,11 +44,12 @@ Result GetAST(std::istream& in) { return std::unexpected(ex); } - if (Operator* op = std::any_cast(&res)) { - return std::move(*op); + Operator op = Table{kEmptyTableName}; + if (Operator* p = std::any_cast(&res)) { + op = std::move(*p); } - return Table{kEmptyTableName}; + return ParsedQuery{std::move(op), visitor.GetRequiredOrder()}; } std::string GetDotRepresentation(const Expression& expr) { @@ -60,12 +61,21 @@ std::string GetDotRepresentation(const Expression& expr) { std::string operator()(const UnaryExpression& expr) { return std::format("({} {})",ToString(expr.op), std::visit(DotFormatter{}, *expr.child)); } + std::string operator()(const InExpression& expr) { + return ToString(Expression{expr}); + } + std::string operator()(const AggregateExpression& expr) { + return ToString(Expression{expr}); + } std::string operator()(const Attribute& expr) { return ToString(expr); } std::string operator()(const IntConst& expr) { return std::to_string(expr); } + std::string operator()(const StringConst& expr) { + return ToString(Expression{expr}); + } std::string operator()(const Literal& expr) { return ToString(expr); } @@ -89,8 +99,20 @@ std::string GetDotRepresentation(const Operator& op) { auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); return {node, std::format("\"{}\"\n\"{}\" -> \"{}\"\n{}", node, source_node, node, rest)}; } + std::pair operator()(const Aggregation& op) { + auto groups = op.group_by + | std::views::transform([](const Expression& expr) { return ToString(expr); }) + | std::views::join_with(',') | std::ranges::to(); + auto aggregates = op.aggregates + | std::views::transform([](const Expression& expr) { return ToString(expr); }) + | std::views::join_with(',') | std::ranges::to(); + auto node = std::format("γ group_by=[{}] aggregates=[{}]", groups, aggregates); + auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); + return {node, std::format("\"{}\"\n\"{}\" -> \"{}\"\n{}", node, source_node, node, rest)}; + } std::pair operator()(const Table& op) { - auto node = std::format("{}", op.name); + auto node = op.alias ? std::format("{} AS {}", op.name, *op.alias) + : std::format("{}", op.name); return {node, std::format("\"{}\"", node)}; } std::pair operator()(const CrossJoin& op) { diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index bdb12f9..8e2d7ce 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -2,12 +2,16 @@ #include #include +#include #include #include +#include using ::testing::Eq; using ::testing::IsTrue; +using ::testing::IsFalse; +using ::testing::Optional; using ::testing::VariantWith; namespace stewkk::sql { @@ -29,7 +33,7 @@ std::string ReadFromFile(std::filesystem::path path) { TEST(ParserTest, SelectAllFromSingleTable) { std::stringstream s{"SELECT * FROM users;"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith(Table{"users"})); } @@ -37,7 +41,7 @@ TEST(ParserTest, SelectAllFromSingleTable) { TEST(ParserTest, SelectSingleColumnFromSingleTable) { std::stringstream s{"SELECT users.id FROM users;"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith(Projection{std::vector{{Attribute{"users", "id"}}}, std::make_shared(Table{"users"})})); @@ -46,7 +50,7 @@ TEST(ParserTest, SelectSingleColumnFromSingleTable) { TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { std::stringstream s{"SELECT users.id, users.email, users.phone FROM users;"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith(Projection{ @@ -57,7 +61,7 @@ TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { TEST(ParserTest, SelectWithWhereClause) { std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith(Projection{ std::vector{Attribute{"users", "id"}}, @@ -68,9 +72,156 @@ TEST(ParserTest, SelectWithWhereClause) { std::make_shared(Table{"users"})})})); } +TEST(ParserTest, SelectWithTableAlias) { + std::stringstream s{"SELECT u.id FROM users u WHERE u.age > 18;"}; + + Operator got = GetAST(s).value().op; + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"u", "id"}}, + std::make_shared( + Filter{Expression{BinaryExpression{ + std::make_shared(Attribute{"u", "age"}), + BinaryOp::kGt, std::make_shared(IntConst{18})}}, + std::make_shared(Table{"users", "u"})})})); +} + +TEST(ParserTest, SelectWithTableAsAlias) { + std::stringstream s{"SELECT u.id FROM users AS u;"}; + + Operator got = GetAST(s).value().op; + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"u", "id"}}, + std::make_shared(Table{"users", "u"})})); +} + +TEST(ParserTest, AliasColumnListRejected) { + std::stringstream s{"SELECT u.id FROM users AS u(id);"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); +} + +TEST(ParserTest, SelectWithStringLiteral) { + std::stringstream s{"SELECT users.id FROM users WHERE users.name = 'Bob''s Market';"}; + + Operator got = GetAST(s).value().op; + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"users", "id"}}, + std::make_shared( + Filter{Expression{BinaryExpression{ + std::make_shared(Attribute{"users", "name"}), + BinaryOp::kEq, + std::make_shared(StringConst{"Bob's Market"})}}, + std::make_shared(Table{"users"})})})); +} + +TEST(ParserTest, SelectWithBetween) { + std::stringstream s{"SELECT users.id FROM users WHERE users.age BETWEEN 18 AND 30;"}; + + Operator got = GetAST(s).value().op; + + auto ge = BinaryExpression{ + std::make_shared(Attribute{"users", "age"}), + BinaryOp::kGe, + std::make_shared(IntConst{18}), + }; + auto le = BinaryExpression{ + std::make_shared(Attribute{"users", "age"}), + BinaryOp::kLe, + std::make_shared(IntConst{30}), + }; + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"users", "id"}}, + std::make_shared( + Filter{Expression{BinaryExpression{ + std::make_shared(std::move(ge)), + BinaryOp::kAnd, + std::make_shared(std::move(le))}}, + std::make_shared(Table{"users"})})})); +} + +TEST(ParserTest, SelectWithNotBetween) { + std::stringstream s{"SELECT users.id FROM users WHERE users.age NOT BETWEEN 18 AND 30;"}; + + Operator got = GetAST(s).value().op; + + auto ge = BinaryExpression{ + std::make_shared(Attribute{"users", "age"}), + BinaryOp::kGe, + std::make_shared(IntConst{18}), + }; + auto le = BinaryExpression{ + std::make_shared(Attribute{"users", "age"}), + BinaryOp::kLe, + std::make_shared(IntConst{30}), + }; + auto between = BinaryExpression{ + std::make_shared(std::move(ge)), + BinaryOp::kAnd, + std::make_shared(std::move(le)), + }; + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"users", "id"}}, + std::make_shared( + Filter{Expression{UnaryExpression{ + UnaryOp::kNot, + std::make_shared(std::move(between))}}, + std::make_shared(Table{"users"})})})); +} + +TEST(ParserTest, SelectWithInList) { + std::stringstream s{"SELECT m.id FROM markets AS m WHERE m.region IN ('AMERICA', 'ASIA');"}; + + Operator got = GetAST(s).value().op; + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"m", "id"}}, + std::make_shared( + Filter{Expression{InExpression{ + std::make_shared(Attribute{"m", "region"}), + {StringConst{"AMERICA"}, StringConst{"ASIA"}}, + false}}, + std::make_shared(Table{"markets", "m"})})})); +} + +TEST(ParserTest, SelectWithNotInList) { + std::stringstream s{"SELECT m.id FROM markets AS m WHERE m.region NOT IN ('AMERICA', 'ASIA');"}; + + Operator got = GetAST(s).value().op; + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"m", "id"}}, + std::make_shared( + Filter{Expression{InExpression{ + std::make_shared(Attribute{"m", "region"}), + {StringConst{"AMERICA"}, StringConst{"ASIA"}}, + true}}, + std::make_shared(Table{"markets", "m"})})})); +} + +TEST(ParserTest, SelectWithBetweenSymmetricRejected) { + std::stringstream s{"SELECT users.id FROM users WHERE users.age BETWEEN SYMMETRIC 18 AND 30;"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); +} + +TEST(ParserTest, SelectWithInSubqueryRejected) { + std::stringstream s{"SELECT users.id FROM users WHERE users.age IN (SELECT users.age FROM users);"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); +} + TEST(ParserTest, GetDotRepresentation) { std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; - Operator op = GetAST(s).value(); + Operator op = GetAST(s).value().op; auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected.dot"); auto got = GetDotRepresentation(op); @@ -81,7 +232,7 @@ TEST(ParserTest, GetDotRepresentation) { TEST(ParserTest, SelectWithBooleanExpression) { std::stringstream s{"SELECT TRUE AND NULL OR FALSE AND NOT NULL;"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith(Projection{ {BinaryExpression{ @@ -104,7 +255,7 @@ TEST(ParserTest, SelectWithBooleanExpression) { TEST(ParserTest, SelectWithArithmeticalOperations) { std::stringstream s{"SELECT 1+2-3;"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT( got, VariantWith(Projection{{BinaryExpression{ @@ -122,7 +273,7 @@ TEST(ParserTest, SelectWithArithmeticalOperations) { TEST(ParserTest, GetDotRepresentationOfArithmeticalExpression) { std::stringstream s{"SELECT 1+2-3+4+5-6;"}; - Operator op = GetAST(s).value(); + Operator op = GetAST(s).value().op; auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected_arithmetical.dot"); auto got = GetDotRepresentation(op); @@ -151,7 +302,7 @@ TEST(ParserTest, NotSupportedError) { TEST(ParserTest, EmptyQuery) { std::stringstream s{""}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith
(Table{"_EMPTY_TABLE_"})); } @@ -159,7 +310,7 @@ TEST(ParserTest, EmptyQuery) { TEST(ParserTest, SelectWithParens) { std::stringstream s{"((SELECT * FROM users));"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith
(Table{"users"})); } @@ -167,7 +318,7 @@ TEST(ParserTest, SelectWithParens) { TEST(ParserTest, EmptySelect) { std::stringstream s{"SELECT;"}; - Operator got = GetAST(s).value(); + Operator got = GetAST(s).value().op; ASSERT_THAT(got, VariantWith
(Table{"_EMPTY_TABLE_"})); } @@ -175,7 +326,7 @@ TEST(ParserTest, EmptySelect) { TEST(ParserTest, SelectWithJoinDot) { std::stringstream s{"SELECT * FROM users, books;"}; auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected_join.dot"); - Operator op = GetAST(s).value(); + Operator op = GetAST(s).value().op; auto got = GetDotRepresentation(op); @@ -185,16 +336,192 @@ TEST(ParserTest, SelectWithJoinDot) { TEST(ParserTest, SelectWithOuterJoinDot) { std::stringstream s{"SELECT * FROM users LEFT OUTER JOIN books ON users.book = books.id;"}; auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected_outer_join.dot"); - Operator op = GetAST(s).value(); + Operator op = GetAST(s).value().op; auto got = GetDotRepresentation(op); ASSERT_THAT(got, Eq(expected)); } -/* -** ORDER BY -** aggregations: SELECT kind, sum(len) AS total FROM films GROUP BY kind; - */ +TEST(ParserTest, OrderByAscDefault) { + std::stringstream s{"SELECT * FROM users ORDER BY users.id;"}; + + auto parsed = GetAST(s).value(); + + ASSERT_THAT(parsed.op, VariantWith
(Table{"users"})); + ASSERT_THAT(parsed.required_order, Optional(SortOrder{{SortKey{"users", "id", Direction::kAsc}}})); +} + +TEST(ParserTest, OrderByDesc) { + std::stringstream s{"SELECT * FROM users ORDER BY users.id DESC;"}; + + auto parsed = GetAST(s).value(); + + ASSERT_THAT(parsed.required_order, Optional(SortOrder{{SortKey{"users", "id", Direction::kDesc}}})); +} + +TEST(ParserTest, OrderByMultiKey) { + std::stringstream s{"SELECT * FROM users ORDER BY users.id ASC, users.age DESC;"}; + + auto parsed = GetAST(s).value(); + + ASSERT_THAT(parsed.required_order, Optional(SortOrder{{ + SortKey{"users", "id", Direction::kAsc}, + SortKey{"users", "age", Direction::kDesc}, + }})); +} + +TEST(ParserTest, OrderByNoOrderByClause) { + std::stringstream s{"SELECT * FROM users;"}; + + auto parsed = GetAST(s).value(); + + ASSERT_THAT(parsed.required_order.has_value(), IsFalse()); +} + +TEST(ParserTest, OrderByIntOrdinalRejected) { + std::stringstream s{"SELECT * FROM users ORDER BY 1;"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); +} + +TEST(ParserTest, OrderByUnqualifiedColumnAccepted) { + std::stringstream s{"SELECT * FROM users ORDER BY id;"}; + + auto parsed = GetAST(s).value(); + + ASSERT_THAT(parsed.required_order, Optional(SortOrder{{SortKey{"", "id", Direction::kAsc}}})); +} + +TEST(ParserTest, OrderByNullsFirstRejected) { + std::stringstream s{"SELECT * FROM users ORDER BY users.id NULLS FIRST;"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); +} + +TEST(ParserTest, SelectWithSumAggregateAlias) { + std::stringstream s{ + "SELECT SUM(lineorder.lo_extendedprice * lineorder.lo_discount) AS revenue " + "FROM lineorder;"}; + + Operator got = GetAST(s).value().op; + + Expression revenue_arg = BinaryExpression{ + std::make_shared(Attribute{"lineorder", "lo_extendedprice"}), + BinaryOp::kMul, + std::make_shared(Attribute{"lineorder", "lo_discount"}), + }; + Expression revenue = AggregateExpression{ + AggregateFunction::kSum, + std::make_shared(revenue_arg), + false, + }; + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"", "__agg0"}}, + std::make_shared(Aggregation{ + {}, + std::vector{revenue}, + std::make_shared(Table{"lineorder"}), + }), + std::vector>{"revenue"}, + })); +} + +TEST(ParserTest, SelectWithGroupByAndAggregate) { + std::stringstream s{ + "SELECT date.d_year, SUM(lineorder.lo_revenue) AS revenue " + "FROM lineorder JOIN date ON lineorder.lo_orderdate = date.d_datekey " + "GROUP BY date.d_year;"}; + + Operator got = GetAST(s).value().op; + + Expression year = Attribute{"date", "d_year"}; + Expression revenue = AggregateExpression{ + AggregateFunction::kSum, + std::make_shared(Attribute{"lineorder", "lo_revenue"}), + false, + }; + Expression join_qual = BinaryExpression{ + std::make_shared(Attribute{"lineorder", "lo_orderdate"}), + BinaryOp::kEq, + std::make_shared(Attribute{"date", "d_datekey"}), + }; + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{year, Attribute{"", "__agg0"}}, + std::make_shared(Aggregation{ + std::vector{year}, + std::vector{revenue}, + std::make_shared(Join{ + JoinType::kInner, + join_qual, + std::make_shared(Table{"lineorder"}), + std::make_shared(Table{"date"}), + }), + }), + std::vector>{std::nullopt, "revenue"}, + })); +} + +TEST(ParserTest, SelectWithCountStar) { + std::stringstream s{"SELECT COUNT(*) FROM lineorder;"}; + + Operator got = GetAST(s).value().op; + + Expression count_star = AggregateExpression{ + AggregateFunction::kCount, + nullptr, + true, + }; + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{Attribute{"", "__agg0"}}, + std::make_shared(Aggregation{ + {}, + std::vector{count_star}, + std::make_shared(Table{"lineorder"}), + }), + })); +} + +TEST(ParserTest, UnsupportedAggregateModifiersRejected) { + for (const char* sql : { + "SELECT SUM(DISTINCT lineorder.lo_revenue) FROM lineorder;", + "SELECT SUM(ALL lineorder.lo_revenue) FROM lineorder;", + "SELECT SUM(lineorder.lo_revenue ORDER BY lineorder.lo_revenue) FROM lineorder;", + "SELECT SUM(lineorder.lo_revenue) FILTER (WHERE lineorder.lo_discount > 0) FROM lineorder;", + "SELECT SUM(lineorder.lo_revenue) OVER () FROM lineorder;", + }) { + std::stringstream s{sql}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); + } +} + +TEST(ParserTest, UnsupportedGroupByFormsRejected) { + std::stringstream s{ + "SELECT date.d_year, SUM(lineorder.lo_revenue) FROM lineorder " + "GROUP BY ROLLUP(date.d_year);"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); +} + +TEST(ParserTest, HavingRejected) { + std::stringstream s{ + "SELECT SUM(lineorder.lo_revenue) FROM lineorder " + "HAVING SUM(lineorder.lo_revenue) > 0;"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); +} } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index 49b348f..cf7f5e1 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -1,14 +1,41 @@ #include +#include +#include +#include #include #include #include +#include namespace stewkk::sql { namespace { +struct ParsedTarget { + Expression expression; + std::optional alias; +}; + +std::string UnquoteStandardString(std::string_view text) { + if (text.size() < 2 || text.front() != '\'' || text.back() != '\'') { + throw Error{ErrorType::kQueryNotSupported, "only standard single-quoted strings are supported"}; + } + + std::string result; + result.reserve(text.size() - 2); + for (size_t i = 1; i + 1 < text.size(); ++i) { + if (text[i] == '\'' && i + 1 < text.size() - 1 && text[i + 1] == '\'') { + result.push_back('\''); + ++i; + continue; + } + result.push_back(text[i]); + } + return result; +} + Operator GetOperatorWithChild(Operator&& op, Operator&& child) { struct ChildSetter { Operator operator() (Table&& parent) { @@ -22,6 +49,10 @@ Operator GetOperatorWithChild(Operator&& op, Operator&& child) { filter.source = std::make_shared(std::move(child)); return filter; } + Operator operator() (Aggregation&& aggregation) { + aggregation.source = std::make_shared(std::move(child)); + return aggregation; + } Operator operator() (CrossJoin&& cross_join) { std::unreachable(); } @@ -34,6 +65,121 @@ Operator GetOperatorWithChild(Operator&& op, Operator&& child) { return std::visit(ChildSetter{std::move(child)}, std::move(op)); } +std::string ToLower(std::string s) { + std::ranges::transform(s, s.begin(), [](unsigned char c) { + return static_cast(std::tolower(c)); + }); + return s; +} + +bool ContainsAggregate(const Expression& expr) { + struct Visitor { + bool operator()(const BinaryExpression& expr) const { + return ContainsAggregate(*expr.lhs) || ContainsAggregate(*expr.rhs); + } + bool operator()(const UnaryExpression& expr) const { + return ContainsAggregate(*expr.child); + } + bool operator()(const InExpression& expr) const { + return ContainsAggregate(*expr.lhs) + || std::ranges::any_of(expr.values, [](const Expression& value) { + return ContainsAggregate(value); + }); + } + bool operator()(const AggregateExpression&) const { + return true; + } + bool operator()(const Attribute&) const { return false; } + bool operator()(const IntConst&) const { return false; } + bool operator()(const StringConst&) const { return false; } + bool operator()(const Literal&) const { return false; } + }; + return std::visit(Visitor{}, expr); +} + +void CollectAggregates(const Expression& expr, std::vector& out) { + struct Visitor { + void operator()(const BinaryExpression& expr) const { + CollectAggregates(*expr.lhs, out); + CollectAggregates(*expr.rhs, out); + } + void operator()(const UnaryExpression& expr) const { + CollectAggregates(*expr.child, out); + } + void operator()(const InExpression& expr) const { + CollectAggregates(*expr.lhs, out); + for (const auto& value : expr.values) { + CollectAggregates(value, out); + } + } + void operator()(const AggregateExpression& expr) const { + out.push_back(Expression{expr}); + } + void operator()(const Attribute&) const {} + void operator()(const IntConst&) const {} + void operator()(const StringConst&) const {} + void operator()(const Literal&) const {} + + std::vector& out; + }; + std::visit(Visitor{out}, expr); +} + +std::vector CollectAggregates(const std::vector& expressions) { + std::vector result; + for (const auto& expr : expressions) { + CollectAggregates(expr, result); + } + return result; +} + +Expression ReplaceAggregatesWithAttributes(const Expression& expr, size_t& counter); + +Expression ReplaceAggregatesWithAttributes(const Expression& expr, size_t& counter) { + struct Visitor { + Expression operator()(const BinaryExpression& e) const { + return BinaryExpression{ + std::make_shared(ReplaceAggregatesWithAttributes(*e.lhs, counter)), + e.binop, + std::make_shared(ReplaceAggregatesWithAttributes(*e.rhs, counter)), + }; + } + Expression operator()(const UnaryExpression& e) const { + return UnaryExpression{ + e.op, + std::make_shared(ReplaceAggregatesWithAttributes(*e.child, counter)), + }; + } + Expression operator()(const InExpression& e) const { + auto new_lhs = std::make_shared(ReplaceAggregatesWithAttributes(*e.lhs, counter)); + std::vector new_values; + for (const auto& v : e.values) { + new_values.push_back(ReplaceAggregatesWithAttributes(v, counter)); + } + return InExpression{std::move(new_lhs), std::move(new_values), e.negated}; + } + Expression operator()(const AggregateExpression&) const { + return Attribute{"", std::format("__agg{}", counter++)}; + } + Expression operator()(const Attribute& a) const { return a; } + Expression operator()(const IntConst& c) const { return c; } + Expression operator()(const StringConst& s) const { return s; } + Expression operator()(const Literal& l) const { return l; } + + size_t& counter; + }; + return std::visit(Visitor{counter}, expr); +} + +std::vector ReplaceAggregatesWithAttributes(const std::vector& exprs) { + std::vector result; + size_t counter = 0; + for (const auto& expr : exprs) { + result.push_back(ReplaceAggregatesWithAttributes(expr, counter)); + } + return result; +} + } // namespace std::any Visitor::visitStmt(codegen::PostgreSQLParser::StmtContext *ctx) { @@ -145,8 +291,15 @@ std::any Visitor::visitSimple_select_intersect(codegen::PostgreSQLParser::Simple } std::any Visitor::visitTarget_label(codegen::PostgreSQLParser::Target_labelContext *ctx) { - // TODO: AS (rename operation) - return visit(ctx->a_expr()); + ParsedTarget target{ + .expression = std::any_cast(visit(ctx->a_expr())), + }; + if (ctx->colLabel()) { + target.alias = ctx->colLabel()->getText(); + } else if (ctx->bareColLabel()) { + target.alias = ctx->bareColLabel()->getText(); + } + return target; } std::any Visitor::visitTarget_star(codegen::PostgreSQLParser::Target_starContext* ctx) { @@ -155,21 +308,52 @@ std::any Visitor::visitTarget_star(codegen::PostgreSQLParser::Target_starContext std::any Visitor::visitTarget_list(codegen::PostgreSQLParser::Target_listContext* ctx) { const auto& targets = ctx->target_el(); - auto target_expressions + auto parsed_targets = targets | std::views::transform([this](const auto& target) { return visit(target); }) | std::views::filter( [](const std::any& expr) { return expr.has_value(); }) | std::views::transform( - [](std::any expr) { return std::any_cast(expr); }) + [](std::any expr) { return std::any_cast(expr); }) | std::ranges::to(); - if (!target_expressions.empty()) { - return Operator{Projection{std::move(target_expressions), nullptr}}; + if (!parsed_targets.empty()) { + std::vector target_expressions; + std::vector> aliases; + target_expressions.reserve(parsed_targets.size()); + aliases.reserve(parsed_targets.size()); + for (auto& target : parsed_targets) { + aliases.push_back(std::move(target.alias)); + target_expressions.push_back(std::move(target.expression)); + } + if (std::ranges::none_of(aliases, [](const auto& alias) { return alias.has_value(); })) { + aliases.clear(); + } + return Operator{Projection{std::move(target_expressions), nullptr, std::move(aliases)}}; } return {}; } +std::any Visitor::visitGroup_clause(codegen::PostgreSQLParser::Group_clauseContext *ctx) { + return visit(ctx->group_by_list()); +} + +std::any Visitor::visitGroup_by_list(codegen::PostgreSQLParser::Group_by_listContext *ctx) { + std::vector result; + for (auto* item : ctx->group_by_item()) { + result.push_back(std::any_cast(visit(item))); + } + return result; +} + +std::any Visitor::visitGroup_by_item(codegen::PostgreSQLParser::Group_by_itemContext *ctx) { + if (!ctx->a_expr()) { + throw Error{ErrorType::kQueryNotSupported, + "ROLLUP, CUBE, GROUPING SETS and empty GROUP BY items are not supported"}; + } + return visit(ctx->a_expr()); +} + std::any Visitor::visitFrom_clause(codegen::PostgreSQLParser::From_clauseContext *ctx) { return visit(ctx->from_list()); } @@ -187,86 +371,121 @@ std::any Visitor::visitFrom_list(codegen::PostgreSQLParser::From_listContext *ct return result; } -std::any Visitor::visitTable_ref(codegen::PostgreSQLParser::Table_refContext *ctx) { +namespace { + +using TableRefCtx = codegen::PostgreSQLParser::Table_refContext; +using ChildIt = std::vector::const_iterator; + +Operator BuildTableRef(Visitor* v, TableRefCtx* ctx); + +Expression MakeBinary(Expression lhs, BinaryOp op, Expression rhs) { + return BinaryExpression{ + std::make_shared(std::move(lhs)), + op, + std::make_shared(std::move(rhs)), + }; +} + +Expression MakeUnary(UnaryOp op, Expression child) { + return UnaryExpression{op, std::make_shared(std::move(child))}; +} + +std::pair ExtractAtom(Visitor* v, TableRefCtx* ctx) { if (ctx->xmltable()) { throw Error{ErrorType::kQueryNotSupported, "xmltable is not supported"}; } if (ctx->func_table()) { - // NOTE: may want to support throw Error{ErrorType::kQueryNotSupported, "func_table is not supported"}; } if (ctx->LATERAL_P()) { throw Error{ErrorType::kQueryNotSupported, "LATERAL clause is not supported"}; } + if (ctx->tablesample_clause()) { + throw Error{ErrorType::kQueryNotSupported, "tablesample_clause is not supported"}; + } - auto children = ctx->children; - auto children_it = children.begin(); - + const auto& children = ctx->children; + auto it = children.cbegin(); Operator res; if (ctx->relation_expr()) { - auto table = std::any_cast(visit(ctx->relation_expr())); - children_it++; - res = Table{std::move(table)}; - } - if (ctx->select_with_parens()) { - res = std::any_cast(visit(ctx->select_with_parens())); - children_it++; - children_it++; - } - auto table_refs = ctx->table_ref(); - auto table_ref_it = table_refs.begin(); - if (ctx->OPEN_PAREN()) { - res = std::any_cast(visit(*table_ref_it)); - children_it++; - table_ref_it++; - } - - if (ctx->alias_clause()) { - throw Error{ErrorType::kQueryNotSupported, "alias_clause is not supported"}; - } - if (ctx->tablesample_clause()) { - throw Error{ErrorType::kQueryNotSupported, "tablesample_clause is not supported"}; + auto table = Table{std::any_cast(v->visit(ctx->relation_expr()))}; + if (auto* alias = ctx->alias_clause()) { + if (alias->name_list()) { + throw Error{ErrorType::kQueryNotSupported, "alias column lists are not supported"}; + } + table.alias = alias->colid()->getText(); + } + res = std::move(table); + ++it; + if (ctx->alias_clause()) { + ++it; + } + } else if (ctx->select_with_parens()) { + if (ctx->alias_clause()) { + throw Error{ErrorType::kQueryNotSupported, "aliases on subqueries are not supported"}; + } + res = std::any_cast(v->visit(ctx->select_with_parens())); + ++it; + } else if (ctx->OPEN_PAREN()) { + if (ctx->alias_clause()) { + throw Error{ErrorType::kQueryNotSupported, "aliases on parenthesized table refs are not supported"}; + } + res = BuildTableRef(v, ctx->table_ref(0)); + it += 3; } + return {std::move(res), it}; +} - for (; children_it != children.end(); children_it++) { - auto text = (*children_it)->getText(); +Operator ContinueChain(Visitor* v, Operator lhs, TableRefCtx* ctx, ChildIt it) { + const auto end = ctx->children.cend(); + while (it != end) { + const auto text = (*it)->getText(); if (text == "CROSS") { - // CROSS JOIN table_ref - children_it += 2; - auto rhs = std::any_cast(visit(*children_it)); - res = CrossJoin{ - std::make_shared(std::move(res)), - std::make_shared(std::move(rhs)), + it += 2; + auto* rhs_ctx = dynamic_cast(*it); + ++it; + auto [rhs_atom, rhs_post] = ExtractAtom(v, rhs_ctx); + lhs = CrossJoin{ + std::make_shared(std::move(lhs)), + std::make_shared(std::move(rhs_atom)), }; + lhs = ContinueChain(v, std::move(lhs), rhs_ctx, rhs_post); } else if (text == "NATURAL") { throw Error{ErrorType::kQueryNotSupported, "NATURAL clause is not supported"}; } else { - Operator rhs; - auto join_type = JoinType::kInner; - Expression qual_expression; + JoinType jt = JoinType::kInner; if (text == "JOIN") { - // JOIN table_ref join_qual - children_it++; - rhs = std::any_cast(visit(*children_it)); - children_it++; - qual_expression = std::any_cast(visit(*children_it)); + ++it; } else { - // join_type JOIN table_ref join_qual - join_type = std::any_cast(visit(*children_it)); - children_it += 2; - rhs = std::any_cast(visit(*children_it)); - children_it++; - qual_expression = std::any_cast(visit(*children_it)); + jt = std::any_cast(v->visit(*it)); + it += 2; } - res = Join{ - join_type, - std::move(qual_expression), - std::make_shared(std::move(res)), - std::make_shared(std::move(rhs)), + auto* rhs_ctx = dynamic_cast(*it); + ++it; + auto qual = std::any_cast(v->visit(*it)); + ++it; + auto [rhs_atom, rhs_post] = ExtractAtom(v, rhs_ctx); + lhs = Join{ + jt, + std::move(qual), + std::make_shared(std::move(lhs)), + std::make_shared(std::move(rhs_atom)), }; + lhs = ContinueChain(v, std::move(lhs), rhs_ctx, rhs_post); } } - return res; + return lhs; +} + +Operator BuildTableRef(Visitor* v, TableRefCtx* ctx) { + auto [atom, it] = ExtractAtom(v, ctx); + return ContinueChain(v, std::move(atom), ctx, it); +} + +} // namespace + +std::any Visitor::visitTable_ref(codegen::PostgreSQLParser::Table_refContext *ctx) { + return Operator{BuildTableRef(this, ctx)}; } std::any Visitor::visitJoin_type(codegen::PostgreSQLParser::Join_typeContext *ctx) { @@ -320,9 +539,15 @@ std::any Visitor::visitSimple_select_pramary( } // NOTE: all_clause_ is ignored - // TODO: support group_clause, having_clause + if (ctx->having_clause()) { + throw Error{ErrorType::kQueryNotSupported, "HAVING clause is not supported"}; + } + if (ctx->window_clause()) { + throw Error{ErrorType::kQueryNotSupported, "WINDOW clause is not supported"}; + } Operator result = Table{kEmptyTableName}; + std::optional projection; if (ctx->from_clause()) { result = std::any_cast(visit(ctx->from_clause())); @@ -336,11 +561,44 @@ std::any Visitor::visitSimple_select_pramary( if (ctx->target_list_()) { auto target_list_opt = visit(ctx->target_list_()); if (target_list_opt.has_value()) { - auto projection = std::any_cast(target_list_opt); - result = GetOperatorWithChild(std::move(projection), std::move(result)); + auto projection_op = std::any_cast(target_list_opt); + projection = std::get(std::move(projection_op)); } } + std::vector group_by; + if (ctx->group_clause()) { + group_by = std::any_cast>(visit(ctx->group_clause())); + } + + std::vector aggregates; + if (projection) { + aggregates = CollectAggregates(projection->expressions); + } + + if (!group_by.empty() || !aggregates.empty()) { + if (projection) { + projection->expressions = ReplaceAggregatesWithAttributes(projection->expressions); + } + result = Aggregation{ + std::move(group_by), + std::move(aggregates), + std::make_shared(std::move(result)), + }; + } + + if (projection) { + if (std::ranges::none_of(projection->aliases, + [](const auto& alias) { return alias.has_value(); })) { + projection->aliases.clear(); + } + result = Projection{ + std::move(projection->expressions), + std::make_shared(std::move(result)), + std::move(projection->aliases), + }; + } + return result; } @@ -368,11 +626,47 @@ std::any Visitor::visitSelect_no_parens(codegen::PostgreSQLParser::Select_no_par auto select_clause = std::any_cast(visit(ctx->select_clause())); - // TODO: remaining clauses + if (ctx->sort_clause_()) { + auto* sortby_list = ctx->sort_clause_()->sort_clause()->sortby_list(); + std::vector keys; + for (auto* sortby : sortby_list->sortby()) { + keys.push_back(std::any_cast(visit(sortby))); + } + required_order_ = SortOrder{std::move(keys)}; + } return select_clause; } +std::any Visitor::visitSortby(codegen::PostgreSQLParser::SortbyContext *ctx) { + if (ctx->USING()) { + throw Error{ErrorType::kQueryNotSupported, "USING in ORDER BY is not supported"}; + } + if (ctx->nulls_order_()) { + throw Error{ErrorType::kQueryNotSupported, "NULLS FIRST/LAST in ORDER BY is not supported"}; + } + + auto expr = std::any_cast(visit(ctx->a_expr())); + auto* attr = std::get_if(&expr); + if (!attr) { + throw Error{ErrorType::kQueryNotSupported, "ORDER BY expression must be a column reference"}; + } + + Direction dir = Direction::kAsc; + if (ctx->asc_desc_()) { + dir = std::any_cast(visit(ctx->asc_desc_())); + } + + return SortKey{attr->table, attr->name, dir}; +} + +std::any Visitor::visitAsc_desc_(codegen::PostgreSQLParser::Asc_desc_Context *ctx) { + if (ctx->DESC()) { + return Direction::kDesc; + } + return Direction::kAsc; +} + std::any Visitor::visitA_expr_qual(codegen::PostgreSQLParser::A_expr_qualContext *ctx) { if (ctx->qual_op()) { throw Error{ErrorType::kQueryNotSupported, "qualified operators are not supported"}; @@ -410,19 +704,47 @@ std::any Visitor::visitA_expr_and(codegen::PostgreSQLParser::A_expr_andContext * } std::any Visitor::visitA_expr_between(codegen::PostgreSQLParser::A_expr_betweenContext *ctx) { - if (ctx->BETWEEN()) { - // NOTE: may want to support - throw Error{ErrorType::kQueryNotSupported, "BETWEEN clause is not supported"}; + if (!ctx->BETWEEN()) { + return visit(ctx->a_expr_in(0)); + } + if (ctx->SYMMETRIC()) { + throw Error{ErrorType::kQueryNotSupported, "BETWEEN SYMMETRIC clause is not supported"}; } - return visit(ctx->a_expr_in(0)); + + auto exprs = ctx->a_expr_in(); + auto value = std::any_cast(visit(exprs[0])); + auto lower = std::any_cast(visit(exprs[1])); + auto upper = std::any_cast(visit(exprs[2])); + + auto ge = MakeBinary(value, BinaryOp::kGe, std::move(lower)); + auto le = MakeBinary(std::move(value), BinaryOp::kLe, std::move(upper)); + auto result = MakeBinary(std::move(ge), BinaryOp::kAnd, std::move(le)); + if (ctx->NOT()) { + result = MakeUnary(UnaryOp::kNot, std::move(result)); + } + return result; } std::any Visitor::visitA_expr_in(codegen::PostgreSQLParser::A_expr_inContext *ctx) { - if (ctx->IN_P()) { - // NOTE: may want to support - throw Error{ErrorType::kQueryNotSupported, "IN clause is not supported"}; + auto lhs = std::any_cast(visit(ctx->a_expr_unary_not())); + if (!ctx->IN_P()) { + return lhs; + } + + auto* in_list = dynamic_cast(ctx->in_expr()); + if (!in_list) { + throw Error{ErrorType::kQueryNotSupported, "IN subqueries are not supported"}; } - return visit(ctx->a_expr_unary_not()); + + std::vector values; + for (auto* expr : in_list->expr_list()->a_expr()) { + values.push_back(std::any_cast(visit(expr))); + } + return Expression{InExpression{ + std::make_shared(std::move(lhs)), + std::move(values), + ctx->NOT() != nullptr, + }}; } std::any Visitor::visitA_expr_unary_not(codegen::PostgreSQLParser::A_expr_unary_notContext *ctx) { @@ -436,10 +758,10 @@ std::any Visitor::visitA_expr_unary_not(codegen::PostgreSQLParser::A_expr_unary_ std::any Visitor::visitA_expr_isnull(codegen::PostgreSQLParser::A_expr_isnullContext *ctx) { auto result = std::any_cast(visit(ctx->a_expr_is_not())); if (ctx->ISNULL()) { - result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kNull)}; + result = UnaryExpression{UnaryOp::kIsNull, std::make_shared(std::move(result))}; } if (ctx->NOTNULL()) { - result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kNull)}; + result = UnaryExpression{UnaryOp::kIsNull, std::make_shared(std::move(result))}; result = UnaryExpression{UnaryOp::kNot, std::make_shared(std::move(result))}; } return result; @@ -452,7 +774,7 @@ std::any Visitor::visitA_expr_is_not(codegen::PostgreSQLParser::A_expr_is_notCon } if (ctx->NULL_P()) { - result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kNull)}; + result = UnaryExpression{UnaryOp::kIsNull, std::make_shared(std::move(result))}; } else if (ctx->TRUE_P()) { result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kTrue)}; } else if (ctx->FALSE_P()) { @@ -486,11 +808,19 @@ std::any Visitor::visitA_expr_like(codegen::PostgreSQLParser::A_expr_likeContext std::any Visitor::visitA_expr_qual_op(codegen::PostgreSQLParser::A_expr_qual_opContext *ctx) { const auto& exprs = ctx->a_expr_unary_qualop(); - if (exprs.size() > 1) { - // NOTE: may want to implement - throw Error{ErrorType::kQueryNotSupported, "qual_op is not supported"}; + if (exprs.size() == 1) { + return visit(exprs.front()); } - return visit(exprs.front()); + if (exprs.size() == 2 && ctx->qual_op(0)->getText() == "!=") { + auto lhs = std::any_cast(visit(exprs[0])); + auto rhs = std::any_cast(visit(exprs[1])); + return Expression{BinaryExpression{ + std::make_shared(std::move(lhs)), + BinaryOp::kNotEq, + std::make_shared(std::move(rhs)), + }}; + } + throw Error{ErrorType::kQueryNotSupported, "qual_op is not supported"}; } std::any Visitor::visitA_expr_unary_qualop(codegen::PostgreSQLParser::A_expr_unary_qualopContext *ctx) { @@ -622,10 +952,89 @@ std::any Visitor::visitC_expr_expr(codegen::PostgreSQLParser::C_expr_exprContext if (ctx->aexprconst()) { return visit(ctx->aexprconst()); } + if (ctx->a_expr_in_parens != nullptr) { + return visit(ctx->a_expr_in_parens); + } + if (ctx->func_expr()) { + return visit(ctx->func_expr()); + } // NOTE: may want to support throw Error{ErrorType::kQueryNotSupported, "c_expr is not fully supported"}; } +std::any Visitor::visitFunc_expr(codegen::PostgreSQLParser::Func_exprContext *ctx) { + if (ctx->within_group_clause()) { + throw Error{ErrorType::kQueryNotSupported, "WITHIN GROUP clause is not supported"}; + } + if (ctx->filter_clause()) { + throw Error{ErrorType::kQueryNotSupported, "aggregate FILTER clause is not supported"}; + } + if (ctx->over_clause()) { + throw Error{ErrorType::kQueryNotSupported, "window functions are not supported"}; + } + if (!ctx->func_application()) { + throw Error{ErrorType::kQueryNotSupported, "function expression is not supported"}; + } + return visit(ctx->func_application()); +} + +std::any Visitor::visitFunc_application(codegen::PostgreSQLParser::Func_applicationContext *ctx) { + if (ctx->VARIADIC()) { + throw Error{ErrorType::kQueryNotSupported, "VARIADIC function arguments are not supported"}; + } + if (ctx->ALL()) { + throw Error{ErrorType::kQueryNotSupported, "ALL in aggregate calls is not supported"}; + } + if (ctx->DISTINCT()) { + throw Error{ErrorType::kQueryNotSupported, "DISTINCT in aggregate calls is not supported"}; + } + if (ctx->sort_clause_()) { + throw Error{ErrorType::kQueryNotSupported, "ORDER BY in aggregate calls is not supported"}; + } + + auto name = ToLower(ctx->func_name()->getText()); + AggregateFunction function; + if (name == "sum") { + function = AggregateFunction::kSum; + } else if (name == "count") { + function = AggregateFunction::kCount; + } else { + throw Error{ErrorType::kQueryNotSupported, + std::format("function {} is not supported", ctx->func_name()->getText())}; + } + + if (ctx->STAR()) { + if (function != AggregateFunction::kCount) { + throw Error{ErrorType::kQueryNotSupported, "only COUNT(*) supports star arguments"}; + } + return Expression{AggregateExpression{function, nullptr, true}}; + } + + auto* args = ctx->func_arg_list(); + if (!args || args->func_arg_expr().size() != 1) { + throw Error{ErrorType::kQueryNotSupported, + std::format("{} requires exactly one argument", ToString(function))}; + } + + auto argument = std::any_cast(visit(args->func_arg_expr(0))); + if (ContainsAggregate(argument)) { + throw Error{ErrorType::kQueryNotSupported, "nested aggregate calls are not supported"}; + } + + return Expression{AggregateExpression{ + function, + std::make_shared(std::move(argument)), + false, + }}; +} + +std::any Visitor::visitFunc_arg_expr(codegen::PostgreSQLParser::Func_arg_exprContext *ctx) { + if (ctx->param_name()) { + throw Error{ErrorType::kQueryNotSupported, "named function arguments are not supported"}; + } + return visit(ctx->a_expr()); +} + std::any Visitor::visitC_expr_case(codegen::PostgreSQLParser::C_expr_caseContext *ctx) { // NOTE: may want to support throw Error{ErrorType::kQueryNotSupported, "CASE clause is not supported"}; @@ -636,7 +1045,15 @@ std::any Visitor::visitAexprconst(codegen::PostgreSQLParser::AexprconstContext * throw Error{ErrorType::kQueryNotSupported, "intervals are not supported"}; } if (ctx->sconst()) { - throw Error{ErrorType::kQueryNotSupported, "strings are not supported"}; + auto* sconst = ctx->sconst(); + if (sconst->uescape_()) { + throw Error{ErrorType::kQueryNotSupported, "UESCAPE strings are not supported"}; + } + auto* any = sconst->anysconst(); + if (!any->StringConstant()) { + throw Error{ErrorType::kQueryNotSupported, "only standard single-quoted strings are supported"}; + } + return Expression{StringConst{UnquoteStandardString(any->StringConstant()->getText())}}; } if (ctx->xconst()) { throw Error{ErrorType::kQueryNotSupported, "hex literals are not supported"}; diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp index 85b5a9b..cf24d9f 100644 --- a/src/stewkk/sql/logic/parser/visitor.hpp +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -1,6 +1,9 @@ #pragma once +#include + #include +#include namespace stewkk::sql { @@ -8,6 +11,8 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { public: explicit Visitor(codegen::PostgreSQLParser* parser); virtual std::any visit(antlr4::tree::ParseTree *tree) override; + + std::optional GetRequiredOrder() const { return required_order_; } virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; virtual std::any visitStmt(codegen::PostgreSQLParser::StmtContext *ctx) override; virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; @@ -20,6 +25,12 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { virtual std::any visitTarget_list(codegen::PostgreSQLParser::Target_listContext *ctx) override; virtual std::any visitTarget_label(codegen::PostgreSQLParser::Target_labelContext *ctx) override; virtual std::any visitTarget_star(codegen::PostgreSQLParser::Target_starContext *ctx) override; + virtual std::any visitGroup_clause(codegen::PostgreSQLParser::Group_clauseContext *ctx) override; + virtual std::any visitGroup_by_list(codegen::PostgreSQLParser::Group_by_listContext *ctx) override; + virtual std::any visitGroup_by_item(codegen::PostgreSQLParser::Group_by_itemContext *ctx) override; + virtual std::any visitFunc_expr(codegen::PostgreSQLParser::Func_exprContext *ctx) override; + virtual std::any visitFunc_application(codegen::PostgreSQLParser::Func_applicationContext *ctx) override; + virtual std::any visitFunc_arg_expr(codegen::PostgreSQLParser::Func_arg_exprContext *ctx) override; virtual std::any visitFrom_clause(codegen::PostgreSQLParser::From_clauseContext *ctx) override; virtual std::any visitWhere_clause(codegen::PostgreSQLParser::Where_clauseContext *ctx) override; virtual std::any visitSelect_with_parens(codegen::PostgreSQLParser::Select_with_parensContext *ctx) override; @@ -62,9 +73,12 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { virtual std::any visitQualified_name(codegen::PostgreSQLParser::Qualified_nameContext *ctx) override; virtual std::any visitJoin_type(codegen::PostgreSQLParser::Join_typeContext *ctx) override; virtual std::any visitJoin_qual(codegen::PostgreSQLParser::Join_qualContext *ctx) override; + virtual std::any visitSortby(codegen::PostgreSQLParser::SortbyContext *ctx) override; + virtual std::any visitAsc_desc_(codegen::PostgreSQLParser::Asc_desc_Context *ctx) override; private: codegen::PostgreSQLParser* parser_; + std::optional required_order_; }; diff --git a/src/stewkk/sql/logic/transformation_rules/aggregation_join_transpose.cpp b/src/stewkk/sql/logic/transformation_rules/aggregation_join_transpose.cpp new file mode 100644 index 0000000..6f8782f --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/aggregation_join_transpose.cpp @@ -0,0 +1,114 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +namespace { + +struct JoinKeys { + Attribute lhs; + Attribute rhs; +}; + +const logical::Join* FindInnerJoin(utils::NotNull source) { + for (auto inner_expr : source->GetLogicalExprs()) { + if (const auto* j = std::get_if(&inner_expr->root_operator); + j != nullptr && j->type == JoinType::kInner) { + return j; + } + } + return nullptr; +} + +std::optional EquiJoinKeys(const logical::Join& join) { + const auto* bin = std::get_if(&join.qual); + if (bin == nullptr || bin->binop != BinaryOp::kEq) return std::nullopt; + const auto* l = std::get_if(bin->lhs.get()); + const auto* r = std::get_if(bin->rhs.get()); + if (l == nullptr || r == nullptr) return std::nullopt; + auto lhs_tables = GroupTables(join.lhs); + auto rhs_tables = GroupTables(join.rhs); + if (lhs_tables.contains(l->table) && rhs_tables.contains(r->table)) { + return JoinKeys{*l, *r}; + } + if (lhs_tables.contains(r->table) && rhs_tables.contains(l->table)) { + return JoinKeys{*r, *l}; + } + return std::nullopt; +} + +bool ContainsExpr(const std::vector& exprs, const Expression& needle) { + return std::ranges::any_of(exprs, [&](const Expression& e) { return e == needle; }); +} + +bool AggregateInputsOnLeft(const logical::Aggregation& agg, + const std::unordered_set& lhs_tables) { + return std::ranges::all_of(agg.aggregates, [&](const Expression& expr) { + const auto* a = std::get_if(&expr); + return a != nullptr + && (a->is_star || (a->argument && ExprUsesOnlyTables(*a->argument, lhs_tables))); + }); +} + +bool GroupByOnLeft(const logical::Aggregation& agg, + const std::unordered_set& lhs_tables) { + return std::ranges::all_of(agg.group_by, [&](const Expression& expr) { + return ExprUsesOnlyTables(expr, lhs_tables); + }); +} + +std::vector OutputProjection(const logical::Aggregation& agg) { + std::vector out = agg.group_by; + for (size_t i = 0; i < agg.aggregates.size(); ++i) { + out.push_back(Attribute{"", std::format("__agg{}", i)}); + } + return out; +} + +bool CanApply(const logical::Aggregation& agg, RuleContext& ctx) { + const auto* join = FindInnerJoin(agg.source); + if (join == nullptr) return false; + auto keys = EquiJoinKeys(*join); + if (!keys) return false; + auto lhs_tables = GroupTables(join->lhs); + if (!AggregateInputsOnLeft(agg, lhs_tables) || !GroupByOnLeft(agg, lhs_tables)) { + return false; + } + if (!ContainsExpr(agg.group_by, Expression{keys->lhs})) { + return false; + } + return ctx.constraints.IsUnique(keys->rhs) + && ctx.constraints.HasForeignKey(keys->lhs, keys->rhs); +} + +} // namespace + +bool AggregationJoinTranspose::IsApplicable(utils::NotNull expr, + RuleContext& ctx) { + if (!std::holds_alternative(expr->root_operator)) return false; + return CanApply(std::get(expr->root_operator), ctx); +} + +LogicalOperator AggregationJoinTranspose::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext& ctx) { + const auto& agg = std::get(expr->root_operator); + if (!CanApply(agg, ctx)) { + throw std::runtime_error{"AggregationJoinTranspose requires unique/full inner equijoin"}; + } + const auto* join = FindInnerJoin(agg.source); + auto agg_lhs = memo.AddGroup( + logical::Aggregation{join->lhs, agg.group_by, agg.aggregates})->group; + auto new_join = memo.AddGroup( + logical::Join{agg_lhs, join->rhs, join->type, join->qual})->group; + return logical::Projection{new_join, OutputProjection(agg), {}}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/aggregation_pushdown_through_join.cpp b/src/stewkk/sql/logic/transformation_rules/aggregation_pushdown_through_join.cpp new file mode 100644 index 0000000..d584af7 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/aggregation_pushdown_through_join.cpp @@ -0,0 +1,136 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +namespace { + +struct JoinKeys { + Attribute lhs; + Attribute rhs; +}; + +const logical::Join* FindInnerJoin(utils::NotNull source) { + for (auto inner_expr : source->GetLogicalExprs()) { + if (const auto* j = std::get_if(&inner_expr->root_operator); + j != nullptr && j->type == JoinType::kInner) { + return j; + } + } + return nullptr; +} + +std::optional EquiJoinKeys(const logical::Join& join) { + const auto* bin = std::get_if(&join.qual); + if (bin == nullptr || bin->binop != BinaryOp::kEq) return std::nullopt; + const auto* l = std::get_if(bin->lhs.get()); + const auto* r = std::get_if(bin->rhs.get()); + if (l == nullptr || r == nullptr) return std::nullopt; + auto lhs_tables = GroupTables(join.lhs); + auto rhs_tables = GroupTables(join.rhs); + if (lhs_tables.contains(l->table) && rhs_tables.contains(r->table)) { + return JoinKeys{*l, *r}; + } + if (lhs_tables.contains(r->table) && rhs_tables.contains(l->table)) { + return JoinKeys{*r, *l}; + } + return std::nullopt; +} + +bool IsSupportedAggregate(const Expression& expr) { + return std::holds_alternative(expr); +} + +bool AggregatesUseOnlyLeft(const logical::Aggregation& agg, + const std::unordered_set& lhs_tables) { + return std::ranges::all_of(agg.aggregates, [&](const Expression& expr) { + if (!IsSupportedAggregate(expr)) return false; + const auto& a = std::get(expr); + return a.is_star || (a.argument && ExprUsesOnlyTables(*a.argument, lhs_tables)); + }); +} + +bool GroupByCanBeReconstructedAfterJoin(const logical::Aggregation& agg, + const std::unordered_set& lhs_tables, + const std::unordered_set& rhs_tables) { + return std::ranges::all_of(agg.group_by, [&](const Expression& expr) { + return ExprUsesOnlyTables(expr, lhs_tables) || ExprUsesOnlyTables(expr, rhs_tables); + }); +} + +bool ContainsExpr(const std::vector& exprs, const Expression& needle) { + return std::ranges::any_of(exprs, [&](const Expression& e) { return e == needle; }); +} + +std::vector PartialGroupBy(const logical::Aggregation& agg, + const std::unordered_set& lhs_tables, + const Attribute& join_key) { + std::vector out; + for (const auto& expr : agg.group_by) { + if (ExprUsesOnlyTables(expr, lhs_tables)) { + out.push_back(expr); + } + } + if (!ContainsExpr(out, Expression{join_key})) { + out.push_back(join_key); + } + return out; +} + +std::vector FinalAggregates(const std::vector& aggregates) { + std::vector out; + out.reserve(aggregates.size()); + for (size_t i = 0; i < aggregates.size(); ++i) { + out.push_back(AggregateExpression{ + AggregateFunction::kSum, + std::make_shared(Attribute{"", std::format("__agg{}", i)}), + false}); + } + return out; +} + +bool CanApply(const logical::Aggregation& agg) { + const auto* join = FindInnerJoin(agg.source); + if (join == nullptr || !EquiJoinKeys(*join)) return false; + auto lhs_tables = GroupTables(join->lhs); + auto rhs_tables = GroupTables(join->rhs); + return AggregatesUseOnlyLeft(agg, lhs_tables) + && GroupByCanBeReconstructedAfterJoin(agg, lhs_tables, rhs_tables); +} + +} // namespace + +bool AggregationPushdownThroughJoin::IsApplicable(utils::NotNull expr, + RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + return CanApply(std::get(expr->root_operator)); +} + +LogicalOperator AggregationPushdownThroughJoin::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext&) { + const auto& agg = std::get(expr->root_operator); + const auto* join = FindInnerJoin(agg.source); + auto keys = join == nullptr ? std::optional{} : EquiJoinKeys(*join); + if (join == nullptr || !keys) { + throw std::runtime_error{"AggregationPushdownThroughJoin requires an inner equijoin"}; + } + + auto lhs_tables = GroupTables(join->lhs); + auto partial_group_by = PartialGroupBy(agg, lhs_tables, keys->lhs); + + auto partial = memo.AddGroup( + logical::PartialAggregation{join->lhs, partial_group_by, agg.aggregates})->group; + auto new_join = memo.AddGroup( + logical::Join{partial, join->rhs, join->type, join->qual})->group; + return logical::FinalAggregation{new_join, agg.group_by, FinalAggregates(agg.aggregates)}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/cross_join_to_join.cpp b/src/stewkk/sql/logic/transformation_rules/cross_join_to_join.cpp new file mode 100644 index 0000000..82620f3 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/cross_join_to_join.cpp @@ -0,0 +1,36 @@ +#include + +#include + +namespace stewkk::sql { + +namespace { + +const logical::CrossJoin* FindCrossJoin(utils::NotNull source) { + for (auto inner_expr : source->GetLogicalExprs()) { + if (const auto* j = std::get_if(&inner_expr->root_operator)) { + return j; + } + } + return nullptr; +} + +} // namespace + +bool CrossJoinToJoin::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& filter = std::get(expr->root_operator); + return FindCrossJoin(filter.source) != nullptr; +} + +LogicalOperator CrossJoinToJoin::ApplyImpl(utils::NotNull expr, + Memo&, RuleContext&) { + const auto& filter = std::get(expr->root_operator); + const auto* cross = FindCrossJoin(filter.source); + if (cross == nullptr) { + throw std::runtime_error{"CrossJoinToJoin requires a cross join below filter"}; + } + return logical::Join{cross->lhs, cross->rhs, JoinType::kInner, filter.predicate}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/filter_lift_through_join.cpp b/src/stewkk/sql/logic/transformation_rules/filter_lift_through_join.cpp new file mode 100644 index 0000000..ae7e40c --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/filter_lift_through_join.cpp @@ -0,0 +1,57 @@ +#include + +#include +#include + +#include + +namespace stewkk::sql { + +namespace { + +struct LiftCandidate { + logical::Filter filter; + bool left; +}; + +std::optional FindCandidate(const logical::Join& join) { + const bool can_left = join.type == JoinType::kInner || join.type == JoinType::kLeft; + const bool can_right = join.type == JoinType::kInner || join.type == JoinType::kRight; + if (can_left) { + for (auto expr : join.lhs->GetLogicalExprs()) { + if (const auto* f = std::get_if(&expr->root_operator)) { + return LiftCandidate{*f, true}; + } + } + } + if (can_right) { + for (auto expr : join.rhs->GetLogicalExprs()) { + if (const auto* f = std::get_if(&expr->root_operator)) { + return LiftCandidate{*f, false}; + } + } + } + return std::nullopt; +} + +} // namespace + +bool FilterLiftThroughJoin::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + return FindCandidate(std::get(expr->root_operator)).has_value(); +} + +LogicalOperator FilterLiftThroughJoin::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext&) { + const auto& join = std::get(expr->root_operator); + auto candidate = FindCandidate(join); + if (!candidate) { + throw std::runtime_error{"FilterLiftThroughJoin requires a child filter"}; + } + auto new_lhs = candidate->left ? candidate->filter.source : join.lhs; + auto new_rhs = candidate->left ? join.rhs : candidate->filter.source; + auto new_join = memo.AddGroup(logical::Join{new_lhs, new_rhs, join.type, join.qual})->group; + return logical::Filter{new_join, candidate->filter.predicate}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/filter_merge.cpp b/src/stewkk/sql/logic/transformation_rules/filter_merge.cpp new file mode 100644 index 0000000..d30bd82 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/filter_merge.cpp @@ -0,0 +1,44 @@ +#include + +#include +#include + +#include + +namespace stewkk::sql { + +namespace { + +const logical::Filter* FindInnerFilter(utils::NotNull source) { + for (auto inner_expr : source->GetLogicalExprs()) { + if (const auto* f = std::get_if(&inner_expr->root_operator)) { + return f; + } + } + return nullptr; +} + +} // namespace + +bool FilterMerge::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& outer = std::get(expr->root_operator); + return FindInnerFilter(outer.source) != nullptr; +} + +LogicalOperator FilterMerge::ApplyImpl(utils::NotNull expr, Memo&, RuleContext&) { + const auto& outer = std::get(expr->root_operator); + const auto* inner = FindInnerFilter(outer.source); + if (inner == nullptr) { + throw std::runtime_error{"FilterMerge requires a nested filter"}; + } + + Expression merged = BinaryExpression{ + std::make_shared(outer.predicate), + BinaryOp::kAnd, + std::make_shared(inner->predicate), + }; + return logical::Filter{inner->source, std::move(merged)}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/filter_pushdown_through_join.cpp b/src/stewkk/sql/logic/transformation_rules/filter_pushdown_through_join.cpp new file mode 100644 index 0000000..d8d2bfb --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/filter_pushdown_through_join.cpp @@ -0,0 +1,121 @@ +#include + +#include +#include +#include +#include +#include + +#include + +namespace stewkk::sql { + +namespace { + +struct Sides { + bool push_left; + bool push_right; +}; + +Sides PushableSides(JoinType type) { + switch (type) { + case JoinType::kInner: return {true, true}; + case JoinType::kLeft: return {true, false}; + case JoinType::kRight: return {false, true}; + case JoinType::kFull: return {false, false}; + } + return {false, false}; +} + +struct Partition { + std::vector left; + std::vector right; + std::vector rest; +}; + +Partition PartitionConjuncts(const std::vector& conjs, + const std::unordered_set& l_tables, + const std::unordered_set& r_tables, + Sides sides) { + Partition out; + for (const auto& q : conjs) { + auto q_tables = ExprTables(q); + bool fits_left = !q_tables.empty() && std::all_of(q_tables.begin(), q_tables.end(), + [&](const auto& t) { return l_tables.contains(t); }); + bool fits_right = !q_tables.empty() && std::all_of(q_tables.begin(), q_tables.end(), + [&](const auto& t) { return r_tables.contains(t); }); + if (fits_left && sides.push_left) { + out.left.push_back(q); + } else if (fits_right && sides.push_right) { + out.right.push_back(q); + } else { + out.rest.push_back(q); + } + } + return out; +} + +const logical::Join* FindPushableJoin(utils::NotNull source, + const Expression& predicate) { + std::vector conjs; + CollectConjuncts(predicate, conjs); + if (conjs.empty()) return nullptr; + + for (auto inner_expr : source->GetLogicalExprs()) { + const auto* j = std::get_if(&inner_expr->root_operator); + if (j == nullptr) continue; + auto sides = PushableSides(j->type); + if (!sides.push_left && !sides.push_right) continue; + + auto l_tables = GroupTables(j->lhs); + auto r_tables = GroupTables(j->rhs); + auto parts = PartitionConjuncts(conjs, l_tables, r_tables, sides); + if (!parts.left.empty() || !parts.right.empty()) { + return j; + } + } + return nullptr; +} + +} // namespace + +bool FilterPushdownThroughJoin::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& f = std::get(expr->root_operator); + return FindPushableJoin(f.source, f.predicate) != nullptr; +} + +LogicalOperator FilterPushdownThroughJoin::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext&) { + const auto& f = std::get(expr->root_operator); + const auto* join = FindPushableJoin(f.source, f.predicate); + if (join == nullptr) { + throw std::runtime_error{"FilterPushdownThroughJoin requires a pushable join below"}; + } + + std::vector conjs; + CollectConjuncts(f.predicate, conjs); + auto sides = PushableSides(join->type); + auto l_tables = GroupTables(join->lhs); + auto r_tables = GroupTables(join->rhs); + auto parts = PartitionConjuncts(conjs, l_tables, r_tables, sides); + + utils::NotNull new_lhs = join->lhs; + if (!parts.left.empty()) { + new_lhs = memo.AddGroup(logical::Filter{join->lhs, AndConjuncts(parts.left)})->group; + } + utils::NotNull new_rhs = join->rhs; + if (!parts.right.empty()) { + new_rhs = memo.AddGroup(logical::Filter{join->rhs, AndConjuncts(parts.right)})->group; + } + + logical::Join new_join{new_lhs, new_rhs, join->type, join->qual}; + + if (parts.rest.empty()) { + return new_join; + } + auto new_join_group = memo.AddGroup(new_join)->group; + return logical::Filter{new_join_group, AndConjuncts(parts.rest)}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/filter_pushdown_through_projection.cpp b/src/stewkk/sql/logic/transformation_rules/filter_pushdown_through_projection.cpp new file mode 100644 index 0000000..91f2128 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/filter_pushdown_through_projection.cpp @@ -0,0 +1,58 @@ +#include + +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +namespace { + +bool ProjectionPassesThrough(const std::vector& proj_exprs, + const std::vector& needed) { + return std::all_of(needed.begin(), needed.end(), [&](const Attribute& a) { + return std::any_of(proj_exprs.begin(), proj_exprs.end(), [&](const Expression& e) { + const auto* pa = std::get_if(&e); + return pa != nullptr && *pa == a; + }); + }); +} + +const logical::Projection* FindPushableProjection(utils::NotNull source, + const Expression& predicate) { + std::vector needed; + CollectAttributes(predicate, needed); + for (auto inner_expr : source->GetLogicalExprs()) { + if (const auto* p = std::get_if(&inner_expr->root_operator)) { + if (ProjectionPassesThrough(p->expressions, needed)) { + return p; + } + } + } + return nullptr; +} + +} // namespace + +bool FilterPushdownThroughProjection::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& f = std::get(expr->root_operator); + return FindPushableProjection(f.source, f.predicate) != nullptr; +} + +LogicalOperator FilterPushdownThroughProjection::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext&) { + const auto& f = std::get(expr->root_operator); + const auto* proj = FindPushableProjection(f.source, f.predicate); + if (proj == nullptr) { + throw std::runtime_error{"FilterPushdownThroughProjection requires a passthrough projection"}; + } + + auto new_filter_group = memo.AddGroup(logical::Filter{proj->source, f.predicate})->group; + return logical::Projection{new_filter_group, proj->expressions, proj->aliases}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/filter_split.cpp b/src/stewkk/sql/logic/transformation_rules/filter_split.cpp new file mode 100644 index 0000000..47eb0f1 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/filter_split.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include + +#include + +namespace stewkk::sql { + +namespace { + +size_t ConjunctCount(const Expression& e) { + std::vector conjs; + CollectConjuncts(e, conjs); + return conjs.size(); +} + +} // namespace + +bool FilterSplit::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& f = std::get(expr->root_operator); + return ConjunctCount(f.predicate) >= 2; +} + +LogicalOperator FilterSplit::ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext&) { + const auto& f = std::get(expr->root_operator); + + std::vector conjs; + CollectConjuncts(f.predicate, conjs); + if (conjs.size() < 2) { + throw std::runtime_error{"FilterSplit requires a conjunction"}; + } + + Expression head = conjs.front(); + std::vector rest(conjs.begin() + 1, conjs.end()); + Expression rest_pred = AndConjuncts(rest); + + auto inner_group = memo.AddGroup(logical::Filter{f.source, std::move(rest_pred)})->group; + return logical::Filter{inner_group, std::move(head)}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/filter_to_join_predicate.cpp b/src/stewkk/sql/logic/transformation_rules/filter_to_join_predicate.cpp new file mode 100644 index 0000000..447caf6 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/filter_to_join_predicate.cpp @@ -0,0 +1,85 @@ +#include + +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +namespace { + +const logical::Join* FindInnerJoin(utils::NotNull source) { + for (auto inner_expr : source->GetLogicalExprs()) { + if (const auto* j = std::get_if(&inner_expr->root_operator); + j != nullptr && j->type == JoinType::kInner) { + return j; + } + } + return nullptr; +} + +bool UsesBothJoinSides(const Expression& expr, const logical::Join& join) { + const auto tables = ExprTables(expr); + const auto lhs_tables = GroupTables(join.lhs); + const auto rhs_tables = GroupTables(join.rhs); + + bool uses_lhs = false; + bool uses_rhs = false; + for (const auto& table : tables) { + uses_lhs = uses_lhs || lhs_tables.contains(table); + uses_rhs = uses_rhs || rhs_tables.contains(table); + } + return uses_lhs && uses_rhs; +} + +bool HasCrossSideConjunct(const Expression& predicate, const logical::Join& join) { + std::vector conjs; + CollectConjuncts(predicate, conjs); + return std::ranges::any_of(conjs, [&](const Expression& conj) { + return UsesBothJoinSides(conj, join); + }); +} + +} // namespace + +bool FilterToJoinPredicate::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& filter = std::get(expr->root_operator); + const auto* join = FindInnerJoin(filter.source); + return join != nullptr && HasCrossSideConjunct(filter.predicate, *join); +} + +LogicalOperator FilterToJoinPredicate::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext&) { + const auto& filter = std::get(expr->root_operator); + const auto* join = FindInnerJoin(filter.source); + if (join == nullptr) { + throw std::runtime_error{"FilterToJoinPredicate requires an inner join below filter"}; + } + + std::vector join_conjs; + std::vector rest_conjs; + CollectConjuncts(join->qual, join_conjs); + + std::vector filter_conjs; + CollectConjuncts(filter.predicate, filter_conjs); + for (const auto& conj : filter_conjs) { + if (UsesBothJoinSides(conj, *join)) { + join_conjs.push_back(conj); + } else { + rest_conjs.push_back(conj); + } + } + + auto new_join = logical::Join{join->lhs, join->rhs, join->type, AndConjuncts(join_conjs)}; + if (rest_conjs.empty()) { + return new_join; + } + auto new_join_group = memo.AddGroup(new_join)->group; + return logical::Filter{new_join_group, AndConjuncts(rest_conjs)}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/in_to_or_chain.cpp b/src/stewkk/sql/logic/transformation_rules/in_to_or_chain.cpp new file mode 100644 index 0000000..d25dfdb --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/in_to_or_chain.cpp @@ -0,0 +1,76 @@ +#include + +#include +#include + +#include + +namespace stewkk::sql { + +namespace { + +bool ContainsIn(const Expression& e) { + return std::visit(utils::Overloaded{ + [](const BinaryExpression& b) { return ContainsIn(*b.lhs) || ContainsIn(*b.rhs); }, + [](const UnaryExpression& u) { return ContainsIn(*u.child); }, + [](const InExpression&) { return true; }, + [](const AggregateExpression& a) { + return !a.is_star && a.argument && ContainsIn(*a.argument); + }, + [](const Attribute&) { return false; }, + [](const IntConst&) { return false; }, + [](const StringConst&) { return false; }, + [](const Literal&) { return false; }, + }, e); +} + +std::shared_ptr Share(Expression e) { + return std::make_shared(std::move(e)); +} + +Expression ExpandIn(const InExpression& in); + +Expression Expand(const Expression& e) { + return std::visit(utils::Overloaded{ + [](const BinaryExpression& b) -> Expression { + return BinaryExpression{Share(Expand(*b.lhs)), b.binop, Share(Expand(*b.rhs))}; + }, + [](const UnaryExpression& u) -> Expression { + return UnaryExpression{u.op, Share(Expand(*u.child))}; + }, + [](const InExpression& in) -> Expression { return ExpandIn(in); }, + [](const AggregateExpression& a) -> Expression { + if (a.is_star || !a.argument) return a; + return AggregateExpression{a.function, Share(Expand(*a.argument)), a.is_star}; + }, + [](const auto& leaf) -> Expression { return leaf; }, + }, e); +} + +Expression ExpandIn(const InExpression& in) { + Expression lhs = Expand(*in.lhs); + BinaryOp leaf_op = in.negated ? BinaryOp::kNotEq : BinaryOp::kEq; + BinaryOp join_op = in.negated ? BinaryOp::kAnd : BinaryOp::kOr; + + Expression acc = BinaryExpression{Share(lhs), leaf_op, Share(Expand(in.values.front()))}; + for (size_t i = 1; i < in.values.size(); ++i) { + Expression cmp = BinaryExpression{Share(lhs), leaf_op, Share(Expand(in.values[i]))}; + acc = BinaryExpression{Share(std::move(acc)), join_op, Share(std::move(cmp))}; + } + return acc; +} + +} // namespace + +bool InToOrChain::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& f = std::get(expr->root_operator); + return ContainsIn(f.predicate); +} + +LogicalOperator InToOrChain::ApplyImpl(utils::NotNull expr, Memo&, RuleContext&) { + const auto& f = std::get(expr->root_operator); + return logical::Filter{f.source, Expand(f.predicate)}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/join_associativity.cpp b/src/stewkk/sql/logic/transformation_rules/join_associativity.cpp new file mode 100644 index 0000000..6c7b961 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/join_associativity.cpp @@ -0,0 +1,54 @@ +#include + +#include +#include +#include + +#include + +namespace stewkk::sql { + +bool JoinAssociativity::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& outer = std::get(expr->root_operator); + if (outer.type != JoinType::kInner) return false; + for (auto inner_expr : outer.lhs->GetLogicalExprs()) { + const auto* inner = std::get_if(&inner_expr->root_operator); + if (inner != nullptr && inner->type == JoinType::kInner) return true; + } + return false; +} + +LogicalOperator JoinAssociativity::ApplyImpl(utils::NotNull expr, Memo& memo, RuleContext&) { + const auto& outer = std::get(expr->root_operator); + for (auto inner_expr : outer.lhs->GetLogicalExprs()) { + if (!std::holds_alternative(inner_expr->root_operator)) continue; + const auto& inner = std::get(inner_expr->root_operator); + if (outer.type != JoinType::kInner || inner.type != JoinType::kInner) continue; + + auto bc_tables = GroupTables(inner.rhs); + auto c_tables = GroupTables(outer.rhs); + bc_tables.insert(c_tables.begin(), c_tables.end()); + + std::vector conjs; + CollectConjuncts(inner.qual, conjs); + CollectConjuncts(outer.qual, conjs); + + std::vector inner_quals; + std::vector outer_quals; + for (auto& q : conjs) { + auto q_tables = ExprTables(q); + bool fits_inner = std::all_of(q_tables.begin(), q_tables.end(), + [&](const auto& t) { return bc_tables.contains(t); }); + (fits_inner ? inner_quals : outer_quals).push_back(std::move(q)); + } + + auto new_rhs = memo.AddGroup(logical::Join{ + inner.rhs, outer.rhs, outer.type, AndConjuncts(inner_quals)})->group; + // FIXME: should return more than one operators!!! + return logical::Join{inner.lhs, new_rhs, inner.type, AndConjuncts(outer_quals)}; + } + throw std::runtime_error{"cant perform JoinAssociativity"}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/join_commutativity.cpp b/src/stewkk/sql/logic/transformation_rules/join_commutativity.cpp new file mode 100644 index 0000000..0cc9dbb --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/join_commutativity.cpp @@ -0,0 +1,20 @@ +#include + +namespace stewkk::sql { + +bool JoinCommutativity::IsApplicable(utils::NotNull expr, RuleContext&) { + return std::holds_alternative(expr->root_operator); +} + +LogicalOperator JoinCommutativity::ApplyImpl(utils::NotNull expr, Memo&, RuleContext&) { + auto join = std::get(expr->root_operator); + std::swap(join.lhs, join.rhs); + if (join.type == JoinType::kLeft) { + join.type = JoinType::kRight; + } else if (join.type == JoinType::kRight) { + join.type = JoinType::kLeft; + } + return join; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/outer_join_to_inner.cpp b/src/stewkk/sql/logic/transformation_rules/outer_join_to_inner.cpp new file mode 100644 index 0000000..186b5d3 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/outer_join_to_inner.cpp @@ -0,0 +1,56 @@ +#include + +#include + +#include +#include + +namespace stewkk::sql { + +namespace { + +const logical::Join* FindReducibleJoin(utils::NotNull source, + const Expression& predicate) { + for (auto inner_expr : source->GetLogicalExprs()) { + const auto* join = std::get_if(&inner_expr->root_operator); + if (join == nullptr || join->type == JoinType::kInner) continue; + auto lhs_tables = GroupTables(join->lhs); + auto rhs_tables = GroupTables(join->rhs); + if (join->type == JoinType::kLeft + && IsNullRejectingForTables(predicate, rhs_tables)) { + return join; + } + if (join->type == JoinType::kRight + && IsNullRejectingForTables(predicate, lhs_tables)) { + return join; + } + if (join->type == JoinType::kFull + && IsNullRejectingForTables(predicate, lhs_tables) + && IsNullRejectingForTables(predicate, rhs_tables)) { + return join; + } + } + return nullptr; +} + +} // namespace + +bool OuterJoinToInner::IsApplicable(utils::NotNull expr, RuleContext&) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& filter = std::get(expr->root_operator); + return FindReducibleJoin(filter.source, filter.predicate) != nullptr; +} + +LogicalOperator OuterJoinToInner::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext&) { + const auto& filter = std::get(expr->root_operator); + const auto* join = FindReducibleJoin(filter.source, filter.predicate); + if (join == nullptr) { + throw std::runtime_error{"OuterJoinToInner requires a null-rejecting filter"}; + } + auto inner = memo.AddGroup( + logical::Join{join->lhs, join->rhs, JoinType::kInner, join->qual})->group; + return logical::Filter{inner, filter.predicate}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/predicate_utils.cpp b/src/stewkk/sql/logic/transformation_rules/predicate_utils.cpp new file mode 100644 index 0000000..35ae135 --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/predicate_utils.cpp @@ -0,0 +1,412 @@ +#include + +#include +#include +#include +#include +#include + +#include + +namespace stewkk::sql { + +bool IsTrue(const Expression& e) { + return std::holds_alternative(e) && std::get(e) == Literal::kTrue; +} + +void CollectConjuncts(const Expression& e, std::vector& out) { + if (IsTrue(e)) return; + if (const auto* b = std::get_if(&e); b && b->binop == BinaryOp::kAnd) { + CollectConjuncts(*b->lhs, out); + CollectConjuncts(*b->rhs, out); + return; + } + out.push_back(e); +} + +Expression AndConjuncts(const std::vector& conjs) { + if (conjs.empty()) return Literal::kTrue; + Expression acc = conjs[0]; + for (size_t i = 1; i < conjs.size(); i++) { + acc = BinaryExpression{ + std::make_shared(std::move(acc)), + BinaryOp::kAnd, + std::make_shared(conjs[i]), + }; + } + return acc; +} + +namespace { + +std::shared_ptr Share(Expression e) { + return std::make_shared(std::move(e)); +} + +std::string CanonicalKey(const Expression& e); +Expression NormalizePredicate(const Expression& e); +Expression NormalizeNegated(const Expression& e); + +std::string CanonicalKey(const BinaryExpression& e) { + return std::format("({} {} {})", static_cast(e.binop), + CanonicalKey(*e.lhs), CanonicalKey(*e.rhs)); +} + +std::string CanonicalKey(const UnaryExpression& e) { + return std::format("({} {})", static_cast(e.op), CanonicalKey(*e.child)); +} + +std::string CanonicalKey(const InExpression& e) { + std::string out = std::format("({} {}", e.negated ? "notin" : "in", CanonicalKey(*e.lhs)); + for (const auto& value : e.values) { + out += ' '; + out += CanonicalKey(value); + } + out += ')'; + return out; +} + +std::string CanonicalKey(const AggregateExpression& e) { + if (e.is_star) { + return std::format("({} *)", static_cast(e.function)); + } + return std::format("({} {})", static_cast(e.function), CanonicalKey(*e.argument)); +} + +std::string CanonicalKey(const Expression& e) { + return std::visit(utils::Overloaded{ + [](const BinaryExpression& b) { return CanonicalKey(b); }, + [](const Attribute& a) { return std::format("(attr {} {})", a.table, a.name); }, + [](const IntConst& i) { return std::format("(int {})", i); }, + [](const StringConst& s) { return std::format("(str {})", s); }, + [](const UnaryExpression& u) { return CanonicalKey(u); }, + [](const InExpression& i) { return CanonicalKey(i); }, + [](const AggregateExpression& a) { return CanonicalKey(a); }, + [](const Literal& l) { return std::format("(literal {})", static_cast(l)); }, + }, e); +} + +void CollectByOp(const Expression& e, BinaryOp op, std::vector& out) { + if (const auto* b = std::get_if(&e); b && b->binop == op) { + CollectByOp(*b->lhs, op, out); + CollectByOp(*b->rhs, op, out); + return; + } + out.push_back(NormalizePredicate(e)); +} + +Expression ChainByOp(std::vector exprs, BinaryOp op) { + std::ranges::sort(exprs, [](const Expression& lhs, const Expression& rhs) { + return CanonicalKey(lhs) < CanonicalKey(rhs); + }); + Expression acc = std::move(exprs.front()); + for (size_t i = 1; i < exprs.size(); ++i) { + acc = BinaryExpression{Share(std::move(acc)), op, Share(std::move(exprs[i]))}; + } + return acc; +} + +std::optional InvertComparison(BinaryOp op) { + switch (op) { + case BinaryOp::kGt: return BinaryOp::kLe; + case BinaryOp::kLt: return BinaryOp::kGe; + case BinaryOp::kLe: return BinaryOp::kGt; + case BinaryOp::kGe: return BinaryOp::kLt; + case BinaryOp::kNotEq: return BinaryOp::kEq; + case BinaryOp::kEq: return BinaryOp::kNotEq; + case BinaryOp::kOr: + case BinaryOp::kAnd: + case BinaryOp::kPlus: + case BinaryOp::kMinus: + case BinaryOp::kMul: + case BinaryOp::kDiv: + case BinaryOp::kMod: + case BinaryOp::kPow: + return std::nullopt; + } +} + +std::optional ReverseComparison(BinaryOp op) { + switch (op) { + case BinaryOp::kGt: return BinaryOp::kLt; + case BinaryOp::kLt: return BinaryOp::kGt; + case BinaryOp::kLe: return BinaryOp::kGe; + case BinaryOp::kGe: return BinaryOp::kLe; + case BinaryOp::kNotEq: return BinaryOp::kNotEq; + case BinaryOp::kEq: return BinaryOp::kEq; + case BinaryOp::kOr: + case BinaryOp::kAnd: + case BinaryOp::kPlus: + case BinaryOp::kMinus: + case BinaryOp::kMul: + case BinaryOp::kDiv: + case BinaryOp::kMod: + case BinaryOp::kPow: + return std::nullopt; + } +} + +std::optional InvertLiteral(Literal literal) { + switch (literal) { + case Literal::kTrue: return Literal::kFalse; + case Literal::kFalse: return Literal::kTrue; + case Literal::kNull: + case Literal::kUnknown: + return std::nullopt; + } +} + +Expression ExpandIn(const InExpression& in) { + Expression lhs = NormalizePredicate(*in.lhs); + std::vector values; + values.reserve(in.values.size()); + for (const auto& value : in.values) { + values.push_back(NormalizePredicate(value)); + } + std::ranges::sort(values, [](const Expression& lhs, const Expression& rhs) { + return CanonicalKey(lhs) < CanonicalKey(rhs); + }); + + if (values.empty()) { + return InExpression{Share(std::move(lhs)), {}, in.negated}; + } + + const BinaryOp leaf_op = in.negated ? BinaryOp::kNotEq : BinaryOp::kEq; + const BinaryOp join_op = in.negated ? BinaryOp::kAnd : BinaryOp::kOr; + + std::vector terms; + terms.reserve(values.size()); + for (auto& value : values) { + terms.push_back(BinaryExpression{Share(lhs), leaf_op, Share(std::move(value))}); + } + return ChainByOp(std::move(terms), join_op); +} + +Expression NormalizeBinary(const BinaryExpression& b) { + Expression lhs = NormalizePredicate(*b.lhs); + Expression rhs = NormalizePredicate(*b.rhs); + if (auto reversed = ReverseComparison(b.binop); + reversed && CanonicalKey(rhs) < CanonicalKey(lhs)) { + return BinaryExpression{Share(std::move(rhs)), *reversed, Share(std::move(lhs))}; + } + return BinaryExpression{Share(std::move(lhs)), b.binop, Share(std::move(rhs))}; +} + +Expression NormalizePredicate(const Expression& e) { + return std::visit(utils::Overloaded{ + [](const BinaryExpression& b) -> Expression { + if (b.binop == BinaryOp::kAnd || b.binop == BinaryOp::kOr) { + std::vector terms; + CollectByOp(*b.lhs, b.binop, terms); + CollectByOp(*b.rhs, b.binop, terms); + return ChainByOp(std::move(terms), b.binop); + } + return NormalizeBinary(b); + }, + [](const UnaryExpression& u) -> Expression { + if (u.op == UnaryOp::kNot) { + return NormalizeNegated(*u.child); + } + return UnaryExpression{u.op, Share(NormalizePredicate(*u.child))}; + }, + [](const InExpression& i) -> Expression { + return ExpandIn(i); + }, + [](const AggregateExpression& a) -> Expression { + if (a.is_star || !a.argument) return a; + return AggregateExpression{a.function, Share(NormalizePredicate(*a.argument)), a.is_star}; + }, + [](const auto& leaf) -> Expression { return leaf; }, + }, e); +} + +Expression NormalizeNegated(const Expression& e) { + return std::visit(utils::Overloaded{ + [](const BinaryExpression& b) -> Expression { + if (b.binop == BinaryOp::kAnd || b.binop == BinaryOp::kOr) { + const BinaryOp op = b.binop == BinaryOp::kAnd ? BinaryOp::kOr : BinaryOp::kAnd; + std::vector terms; + CollectByOp(NormalizeNegated(*b.lhs), op, terms); + CollectByOp(NormalizeNegated(*b.rhs), op, terms); + return ChainByOp(std::move(terms), op); + } + if (auto inverted = InvertComparison(b.binop)) { + return BinaryExpression{ + Share(NormalizePredicate(*b.lhs)), + *inverted, + Share(NormalizePredicate(*b.rhs)), + }; + } + return UnaryExpression{UnaryOp::kNot, Share(NormalizePredicate(Expression{b}))}; + }, + [](const UnaryExpression& u) -> Expression { + if (u.op == UnaryOp::kNot) { + return NormalizePredicate(*u.child); + } + return UnaryExpression{UnaryOp::kNot, Share(NormalizePredicate(Expression{u}))}; + }, + [](const InExpression& i) -> Expression { + std::vector values; + values.reserve(i.values.size()); + for (const auto& value : i.values) { + values.push_back(value); + } + return ExpandIn(InExpression{Share(*i.lhs), std::move(values), !i.negated}); + }, + [](const AggregateExpression& a) -> Expression { + return UnaryExpression{UnaryOp::kNot, Share(NormalizePredicate(Expression{a}))}; + }, + [](Literal l) -> Expression { + if (auto inverted = InvertLiteral(l)) return *inverted; + return UnaryExpression{UnaryOp::kNot, Share(Expression{l})}; + }, + [](const auto& leaf) -> Expression { + return UnaryExpression{UnaryOp::kNot, Share(Expression{leaf})}; + }, + }, e); +} + +} // namespace + +Expression CanonicalizePredicate(const Expression& e) { + return NormalizePredicate(e); +} + +bool EquivalentPredicate(const Expression& lhs, const Expression& rhs) { + return CanonicalizePredicate(lhs) == CanonicalizePredicate(rhs); +} + +void CollectAttrTables(const Expression& e, std::unordered_set& out) { + std::visit(utils::Overloaded{ + [&](const Attribute& a) { out.insert(a.table); }, + [&](const BinaryExpression& b) { + CollectAttrTables(*b.lhs, out); + CollectAttrTables(*b.rhs, out); + }, + [&](const UnaryExpression& u) { CollectAttrTables(*u.child, out); }, + [&](const InExpression& i) { + CollectAttrTables(*i.lhs, out); + for (const auto& value : i.values) { + CollectAttrTables(value, out); + } + }, + [&](const AggregateExpression& a) { + if (!a.is_star && a.argument) { + CollectAttrTables(*a.argument, out); + } + }, + [&](const IntConst&) {}, + [&](const StringConst&) {}, + [&](const Literal&) {}, + }, e); +} + +void CollectAttributes(const Expression& e, std::vector& out) { + std::visit(utils::Overloaded{ + [&](const Attribute& a) { out.push_back(a); }, + [&](const BinaryExpression& b) { + CollectAttributes(*b.lhs, out); + CollectAttributes(*b.rhs, out); + }, + [&](const UnaryExpression& u) { CollectAttributes(*u.child, out); }, + [&](const InExpression& i) { + CollectAttributes(*i.lhs, out); + for (const auto& value : i.values) { + CollectAttributes(value, out); + } + }, + [&](const AggregateExpression& a) { + if (!a.is_star && a.argument) { + CollectAttributes(*a.argument, out); + } + }, + [&](const IntConst&) {}, + [&](const StringConst&) {}, + [&](const Literal&) {}, + }, e); +} + +std::unordered_set ExprTables(const Expression& e) { + std::unordered_set out; + CollectAttrTables(e, out); + return out; +} + +bool ExprUsesOnlyTables(const Expression& e, const std::unordered_set& tables) { + auto expr_tables = ExprTables(e); + return std::all_of(expr_tables.begin(), expr_tables.end(), + [&](const auto& table) { return tables.contains(table); }); +} + +bool IsNullRejectingForTables(const Expression& e, + const std::unordered_set& tables) { + return std::visit(utils::Overloaded{ + [&](const Attribute& a) { return tables.contains(a.table); }, + [&](const BinaryExpression& b) { + if (b.binop == BinaryOp::kAnd) { + return IsNullRejectingForTables(*b.lhs, tables) + || IsNullRejectingForTables(*b.rhs, tables); + } + if (b.binop == BinaryOp::kOr) { + return IsNullRejectingForTables(*b.lhs, tables) + && IsNullRejectingForTables(*b.rhs, tables); + } + if (b.binop == BinaryOp::kEq || b.binop == BinaryOp::kNotEq + || b.binop == BinaryOp::kGt || b.binop == BinaryOp::kLt + || b.binop == BinaryOp::kGe || b.binop == BinaryOp::kLe) { + return IsNullRejectingForTables(*b.lhs, tables) + || IsNullRejectingForTables(*b.rhs, tables); + } + return false; + }, + [&](const UnaryExpression& u) { + if (u.op == UnaryOp::kIsNull) return false; + return IsNullRejectingForTables(*u.child, tables); + }, + [&](const InExpression& i) { + return IsNullRejectingForTables(*i.lhs, tables); + }, + [&](const AggregateExpression& a) { + return !a.is_star && a.argument && IsNullRejectingForTables(*a.argument, tables); + }, + [&](const IntConst&) { return false; }, + [&](const StringConst&) { return false; }, + [&](const Literal&) { return false; }, + }, e); +} + +namespace { + +void CollectGroupTables(utils::NotNull g, std::unordered_set& out, + std::unordered_set& seen) { + if (!seen.insert(g.get()).second) return; + auto exprs = g->GetLogicalExprs(); + if (exprs.empty()) return; + std::visit(utils::Overloaded{ + [&](const logical::Table& t) { out.insert(std::string{VisibleName(t)}); }, + [&](const logical::Filter& f) { CollectGroupTables(f.source, out, seen); }, + [&](const logical::Projection& p) { CollectGroupTables(p.source, out, seen); }, + [&](const logical::Aggregation& a) { CollectGroupTables(a.source, out, seen); }, + [&](const logical::PartialAggregation& a) { CollectGroupTables(a.source, out, seen); }, + [&](const logical::FinalAggregation& a) { CollectGroupTables(a.source, out, seen); }, + [&](const logical::CrossJoin& j) { + CollectGroupTables(j.lhs, out, seen); + CollectGroupTables(j.rhs, out, seen); + }, + [&](const logical::Join& j) { + CollectGroupTables(j.lhs, out, seen); + CollectGroupTables(j.rhs, out, seen); + }, + }, (*exprs.begin())->root_operator); +} + +} // namespace + +std::unordered_set GroupTables(utils::NotNull g) { + std::unordered_set out; + std::unordered_set seen; + CollectGroupTables(g, out, seen); + return out; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/transformation_rules/projection_pushdown_through_join.cpp b/src/stewkk/sql/logic/transformation_rules/projection_pushdown_through_join.cpp new file mode 100644 index 0000000..bfefdbb --- /dev/null +++ b/src/stewkk/sql/logic/transformation_rules/projection_pushdown_through_join.cpp @@ -0,0 +1,109 @@ +#include + +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +namespace { + +const logical::Join* FindJoin(utils::NotNull source) { + for (auto inner_expr : source->GetLogicalExprs()) { + if (const auto* j = std::get_if(&inner_expr->root_operator)) { + return j; + } + } + return nullptr; +} + +bool ContainsAttr(const std::vector& attrs, const Attribute& attr) { + return std::ranges::any_of(attrs, [&](const Attribute& a) { return a == attr; }); +} + +std::vector NeededForSide(const std::vector& needed, + const Schema& schema) { + std::vector out; + for (const auto& attr : needed) { + auto it = std::ranges::find_if(schema, [&](const Attribute& schema_attr) { + return schema_attr.name == attr.name + && (attr.table.empty() || attr.table == schema_attr.table); + }); + if (it != schema.end() && !ContainsAttr(out, *it)) { + out.push_back(*it); + } + } + return out; +} + +std::vector AttrExpressions(const std::vector& attrs) { + std::vector out; + out.reserve(attrs.size()); + for (const auto& attr : attrs) { + out.push_back(attr); + } + return out; +} + +bool CanPush(const logical::Projection& projection, const logical::Join& join, + RuleContext& ctx) { + std::vector needed; + for (const auto& expr : projection.expressions) { + CollectAttributes(expr, needed); + } + CollectAttributes(join.qual, needed); + + auto lhs_schema = ctx.schema.GetSchema(join.lhs); + auto rhs_schema = ctx.schema.GetSchema(join.rhs); + auto lhs_needed = NeededForSide(needed, lhs_schema); + auto rhs_needed = NeededForSide(needed, rhs_schema); + return (!lhs_needed.empty() && lhs_needed.size() < lhs_schema.size()) + || (!rhs_needed.empty() && rhs_needed.size() < rhs_schema.size()); +} + +} // namespace + +bool ProjectionPushdownThroughJoin::IsApplicable(utils::NotNull expr, + RuleContext& ctx) { + if (!std::holds_alternative(expr->root_operator)) return false; + const auto& projection = std::get(expr->root_operator); + const auto* join = FindJoin(projection.source); + return join != nullptr && CanPush(projection, *join, ctx); +} + +LogicalOperator ProjectionPushdownThroughJoin::ApplyImpl(utils::NotNull expr, + Memo& memo, RuleContext& ctx) { + const auto& projection = std::get(expr->root_operator); + const auto* join = FindJoin(projection.source); + if (join == nullptr) { + throw std::runtime_error{"ProjectionPushdownThroughJoin requires a join below"}; + } + + std::vector needed; + for (const auto& e : projection.expressions) { + CollectAttributes(e, needed); + } + CollectAttributes(join->qual, needed); + + auto lhs_schema = ctx.schema.GetSchema(join->lhs); + auto rhs_schema = ctx.schema.GetSchema(join->rhs); + auto lhs_needed = NeededForSide(needed, lhs_schema); + auto rhs_needed = NeededForSide(needed, rhs_schema); + + auto lhs = join->lhs; + if (!lhs_needed.empty() && lhs_needed.size() < lhs_schema.size()) { + lhs = memo.AddGroup(logical::Projection{join->lhs, AttrExpressions(lhs_needed), {}})->group; + } + auto rhs = join->rhs; + if (!rhs_needed.empty() && rhs_needed.size() < rhs_schema.size()) { + rhs = memo.AddGroup(logical::Projection{join->rhs, AttrExpressions(rhs_needed), {}})->group; + } + + auto new_join = memo.AddGroup(logical::Join{lhs, rhs, join->type, join->qual})->group; + return logical::Projection{new_join, projection.expressions, projection.aliases}; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/main.cpp b/src/stewkk/sql/main.cpp index 4d3a5ae..74ae0cd 100644 --- a/src/stewkk/sql/main.cpp +++ b/src/stewkk/sql/main.cpp @@ -1,7 +1,349 @@ +#include +#include +#include +#include +#include +#include #include +#include +#include +#include -int main() { - std::cout << "Hello\n"; +#include - return 0; +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stewkk::sql { + +namespace { + +constexpr int kOk = 0; +constexpr int kUsage = 64; +constexpr int kParseError = 1; +constexpr int kOptimizerError = 2; +constexpr int kRuntimeError = 3; +constexpr int kNotReachable = 4; + +struct Args { + std::string data_dir; + bool print_plan = false; + bool print_ast = false; + bool jit = false; + bool stats = false; + std::string check_reachable_path; +}; + +Args ParseArgs(int argc, char** argv) { + Args args; + for (int i = 1; i < argc; ++i) { + std::string a = argv[i]; + if (a == "--data-dir") { + if (i + 1 >= argc) { + std::cerr << "--data-dir requires an argument\n"; + std::exit(kUsage); + } + args.data_dir = argv[++i]; + } else if (a == "--print-plan") { + args.print_plan = true; + } else if (a == "--print-ast") { + args.print_ast = true; + } else if (a == "--jit") { + args.jit = true; + } else if (a == "--stats") { + args.stats = true; + } else if (a == "--check-reachable") { + if (i + 1 >= argc) { + std::cerr << "--check-reachable requires a plan file path\n"; + std::exit(kUsage); + } + args.check_reachable_path = argv[++i]; + } else { + std::cerr << "unknown argument: " << a << "\n"; + std::exit(kUsage); + } + } + if (args.data_dir.empty() && args.check_reachable_path.empty()) { + std::cerr << "usage: sql --data-dir [--print-plan] [--jit] [--stats] < query.sql\n" + << " sql --check-reachable < query.sql\n"; + std::exit(kUsage); + } + return args; +} + +std::string SerializeAst(const Operator& op) { + return std::visit([](auto&& node) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return node.alias ? "(Table " + node.name + " AS " + *node.alias + ")" + : "(Table " + node.name + ")"; + } else if constexpr (std::is_same_v) { + return "(Filter " + ToString(node.expr) + " " + SerializeAst(*node.source) + ")"; + } else if constexpr (std::is_same_v) { + std::string exprs; + for (size_t i = 0; i < node.expressions.size(); ++i) { + exprs += " " + ToString(node.expressions[i]); + if (i < node.aliases.size() && node.aliases[i]) { + exprs += " AS " + *node.aliases[i]; + } + } + return "(Projection" + exprs + " " + SerializeAst(*node.source) + ")"; + } else if constexpr (std::is_same_v) { + std::string group_by; + for (const auto& e : node.group_by) group_by += " " + ToString(e); + std::string aggregates; + for (const auto& e : node.aggregates) aggregates += " " + ToString(e); + return "(Aggregation (GroupBy" + group_by + ") (Aggregates" + aggregates + ") " + + SerializeAst(*node.source) + ")"; + } else if constexpr (std::is_same_v) { + return "(CrossJoin " + SerializeAst(*node.lhs) + " " + SerializeAst(*node.rhs) + ")"; + } else if constexpr (std::is_same_v) { + return "(Join " + ToString(node.type) + " " + ToString(node.qual) + + " " + SerializeAst(*node.lhs) + " " + SerializeAst(*node.rhs) + ")"; + } + return "?"; + }, op); +} + +std::string ValueToString(const Value& v, const AttributeInfo& attr) { + if (v.is_null) return "NULL"; + switch (attr.type) { + case Type::kInt: + return std::to_string(v.value.int_value); + case Type::kBool: + return v.value.bool_value ? "1" : "0"; + case Type::kString: + return GetInternedString(v.value.string_id); + } + return "?"; +} + +std::string TypeName(Type t) { + switch (t) { + case Type::kInt: + return "int"; + case Type::kBool: + return "bool"; + case Type::kString: + return "string"; + } + return "?"; +} + +void PrintRelation(const Relation& rel, bool preserve_order) { + std::ostringstream header; + for (size_t i = 0; i < rel.attributes.size(); ++i) { + const auto& a = rel.attributes[i]; + if (i) header << '\t'; + if (!a.table.empty() || !a.name.empty()) { + header << a.table << '.' << a.name; + } + header << ':' << TypeName(a.type); + } + + std::vector rows; + rows.reserve(rel.tuples.size()); + for (const auto& tuple : rel.tuples) { + std::ostringstream line; + for (size_t i = 0; i < tuple.size(); ++i) { + if (i) line << '\t'; + line << ValueToString(tuple[i], rel.attributes[i]); + } + rows.push_back(std::move(line).str()); + } + if (!preserve_order) std::sort(rows.begin(), rows.end()); + + std::cout << header.str() << '\n'; + for (const auto& r : rows) std::cout << r << '\n'; +} + +std::string_view RuleLineageKindName(RuleLineageKind kind) { + switch (kind) { + case RuleLineageKind::kTransformation: + return "transformation"; + case RuleLineageKind::kImplementation: + return "implementation"; + case RuleLineageKind::kEnforcer: + return "enforcer"; + } + return "unknown"; +} + +void PrintRuleLineageStats(const std::vector& stats) { + std::cerr << "Selected plan rule lineage:\n"; + if (stats.empty()) { + std::cerr << " \n"; + return; + } + for (const auto& stat : stats) { + std::cerr << " " << RuleLineageKindName(stat.kind) << ' ' + << stat.rule_id << ' ' << stat.rule_name + << ": " << stat.count << '\n'; + } +} + +boost::asio::awaitable> RunQuery(const std::string& data_dir, + const PhysicalPlanNode& plan) { + CsvDirSequentialScanner seq_scan{data_dir}; + CsvDirIndexedScanner index_scan{data_dir}; + Executor executor(std::move(seq_scan), std::move(index_scan), co_await boost::asio::this_coro::executor); + co_return co_await executor.Execute(plan); +} + +boost::asio::awaitable> RunQueryJit(const std::string& data_dir, + const PhysicalPlanNode& plan) { + CsvDirSequentialScanner seq_scan{data_dir}; + CsvDirIndexedScanner index_scan{data_dir}; + Executor executor( + std::move(seq_scan), std::move(index_scan), co_await boost::asio::this_coro::executor); + co_return co_await executor.Execute(plan); +} + +} // namespace + +} // namespace stewkk::sql + +int main(int argc, char** argv) { + using namespace stewkk::sql; + + auto args = ParseArgs(argc, argv); + + std::ostringstream sql_buf; + sql_buf << std::cin.rdbuf(); + std::stringstream sql_stream{sql_buf.str()}; + + if (!args.check_reachable_path.empty()) { + std::ifstream plan_in{args.check_reachable_path}; + if (!plan_in) { + std::cerr << "cannot open plan file: " << args.check_reachable_path << "\n"; + return kUsage; + } + std::ostringstream plan_buf; + plan_buf << plan_in.rdbuf(); + PhysicalPlanNode target; + try { + target = Deserialize(plan_buf.str()); + } catch (const std::exception& e) { + std::cerr << "plan parse error: " << e.what() << "\n"; + return kParseError; + } + MatchResult mr; + try { + SchemaCatalog schema = args.data_dir.empty() ? SchemaCatalog{} + : LoadSchemaFromCsvDir(args.data_dir); + IndexCatalog indexes = args.data_dir.empty() ? IndexCatalog{} + : LoadIndexCatalogFromCsvDir(args.data_dir); + mr = IsPlanReachable(sql_stream, target, {}, std::move(schema), std::move(indexes)); + } catch (const std::exception& e) { + std::cerr << "reachability error: " << e.what() << "\n"; + return kOptimizerError; + } + if (mr.reachable) { + std::cout << "REACHABLE distance=" << mr.closest_distance << "\n"; + return kOk; + } + std::cout << "NOT REACHABLE distance=" << mr.closest_distance << ": " << mr.mismatch << "\n"; + return kNotReachable; + } + + auto parsed_result = GetAST(sql_stream); + if (!parsed_result.has_value()) { + std::cerr << "parse error: " << What(parsed_result.error()) << "\n"; + return kParseError; + } + auto parsed = std::move(parsed_result).value(); + + if (args.print_ast) { + std::cerr << SerializeAst(parsed.op) << "\n"; + } + + PhysicalPlanNode plan; + PhysicalPlanNode naive_plan; + std::int64_t plan_cost = 0; + std::int64_t naive_plan_cost = 0; + try { + PropertySet required = parsed.required_order + ? PropertySet{SortProperty{*parsed.required_order}} + : PropertySet::Any(); + Optimizer optimizer(parsed.op, MakeMainRules(LoadIndexCatalogFromCsvDir(args.data_dir)), + CardinalityEstimates{LoadTableSizesFromCsvDir(args.data_dir)}, + LoadSchemaFromCsvDir(args.data_dir), std::move(required), + LoadConstraintCatalogFromCsvDir(args.data_dir)); + plan = optimizer.Optimize(); + plan_cost = optimizer.GetBestCost(); + if (args.stats) { + PrintRuleLineageStats(optimizer.GetSelectedPlanRuleLineage()); + } + + PropertySet naive_required = parsed.required_order + ? PropertySet{SortProperty{*parsed.required_order}} + : PropertySet::Any(); + Optimizer naive_optimizer(parsed.op, MakeNaiveRules(), + CardinalityEstimates{LoadTableSizesFromCsvDir(args.data_dir)}, + LoadSchemaFromCsvDir(args.data_dir), std::move(naive_required)); + naive_plan = naive_optimizer.Optimize(); + naive_plan_cost = naive_optimizer.GetBestCost(); + } catch (const std::exception& e) { + std::cerr << "optimizer error: " << e.what() << "\n"; + return kOptimizerError; + } + + OutputDot(plan, naive_plan); + + if (args.print_plan) { + std::cerr << Serialize(plan) << "\n"; + } + + Result result; + std::int64_t exec_us = 0; + try { + const auto exec_started = std::chrono::steady_clock::now(); + boost::asio::io_context ctx; + boost::asio::any_io_executor exec = ctx.get_executor(); + auto fut = args.jit + ? boost::asio::co_spawn(exec, RunQueryJit(args.data_dir, plan), + boost::asio::use_future) + : boost::asio::co_spawn(exec, RunQuery(args.data_dir, plan), + boost::asio::use_future); + ctx.run(); + result = fut.get(); + exec_us = std::chrono::duration_cast( + std::chrono::steady_clock::now() - exec_started).count(); + } catch (const std::exception& e) { + std::cerr << "runtime error: " << e.what() << "\n"; + return kRuntimeError; + } + + if (!result.has_value()) { + std::cerr << "runtime error: " << What(result.error()) << "\n"; + return kRuntimeError; + } + + PrintRelation(result.value(), parsed.required_order.has_value()); + + if (args.stats) { + std::cerr << "STATS plan_cost=" << plan_cost + << " naive_plan_cost=" << naive_plan_cost + << " exec_us=" << exec_us + << " rows=" << result.value().tuples.size() << "\n"; + } + return kOk; } diff --git a/src/stewkk/sql/models/executor/tuple.cpp b/src/stewkk/sql/models/executor/tuple.cpp index 463cbb3..5601a26 100644 --- a/src/stewkk/sql/models/executor/tuple.cpp +++ b/src/stewkk/sql/models/executor/tuple.cpp @@ -1,16 +1,42 @@ #include +#include +#include +#include #include #include +#include +#include namespace stewkk::sql { +namespace { + +std::mutex& StringPoolMutex() { + static std::mutex mutex; + return mutex; +} + +std::deque& StringPoolValues() { + static std::deque values; + return values; +} + +std::unordered_map& StringPoolIds() { + static std::unordered_map ids; + return ids; +} + +} // namespace + std::string ToString(Type type) { switch (type) { case Type::kInt: return "int"; case Type::kBool: return "bool"; + case Type::kString: + return "string"; } std::unreachable(); } @@ -29,6 +55,9 @@ std::string ToString(Value v, const AttributeInfo& attr) { if (attr.type == Type::kInt) { return std::format("{:<8}", v.value.int_value); } + if (attr.type == Type::kString) { + return std::format("{:<8}", GetInternedString(v.value.string_id)); + } return ToString(v.value.bool_value)+' '; } @@ -48,4 +77,26 @@ bool Value::operator==(const Value& other) const { return (is_null && other.is_null) || (!is_null && !other.is_null && value.int_value == other.value.int_value); } +int64_t InternString(std::string value) { + std::lock_guard lock{StringPoolMutex()}; + auto& ids = StringPoolIds(); + if (auto it = ids.find(value); it != ids.end()) { + return it->second; + } + auto& values = StringPoolValues(); + auto id = static_cast(values.size()); + values.push_back(std::move(value)); + ids.emplace(values.back(), id); + return id; +} + +const std::string& GetInternedString(int64_t id) { + std::lock_guard lock{StringPoolMutex()}; + auto& values = StringPoolValues(); + if (id < 0 || static_cast(id) >= values.size()) { + throw std::out_of_range{"string id is not interned"}; + } + return values[static_cast(id)]; +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/models/parser/expression.cpp b/src/stewkk/sql/models/parser/expression.cpp new file mode 100644 index 0000000..e539e9a --- /dev/null +++ b/src/stewkk/sql/models/parser/expression.cpp @@ -0,0 +1,144 @@ +#include + +#include +#include + +namespace stewkk::sql { + +bool BinaryExpression::operator==(const BinaryExpression& other) const { + return *lhs == *other.lhs && binop == other.binop && *rhs == *other.rhs; +} + +bool UnaryExpression::operator==(const UnaryExpression& other) const { + return op == other.op && *child == *other.child; +} + +bool InExpression::operator==(const InExpression& other) const { + return *lhs == *other.lhs && values == other.values && negated == other.negated; +} + +bool AggregateExpression::operator==(const AggregateExpression& other) const { + if (function != other.function || is_star != other.is_star) { + return false; + } + if (is_star) { + return true; + } + return argument && other.argument && *argument == *other.argument; +} + +std::string ToString(AggregateFunction function) { + switch (function) { + case AggregateFunction::kSum: + return "SUM"; + case AggregateFunction::kCount: + return "COUNT"; + } +} + +std::string ToString(const Attribute& attr) { + return std::format("{}.{}", attr.table, attr.name); +} + +std::string ToString(Literal literal) { + switch (literal) { + case Literal::kNull: + return "NULL"; + case Literal::kTrue: + return "TRUE"; + case Literal::kFalse: + return "FALSE"; + case Literal::kUnknown: + return "UNKNOWN"; + } +} + +std::string ToString(BinaryOp binop) { + switch (binop) { + case BinaryOp::kGt: + return ">"; + case BinaryOp::kOr: + return "or"; + case BinaryOp::kAnd: + return "and"; + case BinaryOp::kEq: + return "="; + case BinaryOp::kPlus: + return "+"; + case BinaryOp::kMinus: + return "-"; + case BinaryOp::kMul: + return "*"; + case BinaryOp::kDiv: + return "/"; + case BinaryOp::kMod: + return "%"; + case BinaryOp::kPow: + return "^"; + case BinaryOp::kLt: + return "<"; + case BinaryOp::kLe: + return "<="; + case BinaryOp::kGe: + return ">="; + case BinaryOp::kNotEq: + return "!="; + } +} + +std::string ToString(UnaryOp op) { + switch (op) { + case UnaryOp::kNot: + return "not"; + case UnaryOp::kMinus: + return "-"; + case UnaryOp::kIsNull: + return "isnull"; + } +} + +std::string ToString(const Expression& expr) { + struct Formatter { + std::string operator()(const BinaryExpression& expr) { + return std::format("{} {} {}", ToString(*expr.lhs), ToString(expr.binop), ToString(*expr.rhs)); + } + std::string operator()(const Attribute& expr) { + return ToString(expr); + } + std::string operator()(const IntConst& expr) { + return std::to_string(expr); + } + std::string operator()(const StringConst& expr) { + std::string escaped; + escaped.reserve(expr.size() + 2); + escaped.push_back('\''); + for (char c : expr) { + if (c == '\'') escaped += "''"; + else escaped.push_back(c); + } + escaped.push_back('\''); + return escaped; + } + std::string operator()(const UnaryExpression& expr) { + return std::format("{} {}", ToString(expr.op), ToString(*expr.child)); + } + std::string operator()(const InExpression& expr) { + auto values = expr.values | std::views::transform([](const Expression& v) { + return ToString(v); + }) | std::views::join_with(',') | std::ranges::to(); + return std::format("{} {}in ({})", ToString(*expr.lhs), expr.negated ? "not " : "", values); + } + std::string operator()(const AggregateExpression& expr) { + if (expr.is_star) { + return std::format("{}(*)", ToString(expr.function)); + } + return std::format("{}({})", ToString(expr.function), ToString(*expr.argument)); + } + std::string operator()(const Literal& expr) { + return ToString(expr); + } + }; + return std::visit(Formatter{}, expr); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/models/parser/join_type.cpp b/src/stewkk/sql/models/parser/join_type.cpp new file mode 100644 index 0000000..9d47aff --- /dev/null +++ b/src/stewkk/sql/models/parser/join_type.cpp @@ -0,0 +1,18 @@ +#include + +namespace stewkk::sql { + +std::string ToString(JoinType type) { + switch (type) { + case JoinType::kInner: + return "⋈"; + case JoinType::kFull: + return "⟗"; + case JoinType::kLeft: + return "⟕"; + case JoinType::kRight: + return "⟖"; + } +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp index 1518f35..24ce926 100644 --- a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp +++ b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp @@ -2,20 +2,28 @@ namespace stewkk::sql { +std::string_view VisibleName(const Table& table) { + return table.alias ? std::string_view{*table.alias} : std::string_view{table.name}; +} + bool Projection::operator==(const Projection& other) const { - return expressions == other.expressions && *source == *other.source; + auto normalize = [](const std::vector>& aliases) { + auto result = aliases; + while (!result.empty() && !result.back()) { + result.pop_back(); + } + return result; + }; + return expressions == other.expressions && *source == *other.source + && normalize(aliases) == normalize(other.aliases); } bool Filter::operator==(const Filter& other) const { return expr == other.expr && *source == *other.source; } -bool BinaryExpression::operator==(const BinaryExpression& other) const { - return *lhs == *other.lhs && binop == other.binop && *rhs == *other.rhs; -} - -bool UnaryExpression::operator==(const UnaryExpression& other) const { - return op == other.op && *child == *other.child; +bool Aggregation::operator==(const Aggregation& other) const { + return group_by == other.group_by && aggregates == other.aggregates && *source == *other.source; } bool CrossJoin::operator==(const CrossJoin& other) const { @@ -26,97 +34,4 @@ bool Join::operator==(const Join& other) const { return type == other.type && qual == other.qual && *lhs == *other.lhs && *rhs == *other.rhs; } -std::string ToString(BinaryOp binop) { - switch (binop) { - case BinaryOp::kGt: - return ">"; - case BinaryOp::kOr: - return "or"; - case BinaryOp::kAnd: - return "and"; - case BinaryOp::kEq: - return "="; - case BinaryOp::kPlus: - return "+"; - case BinaryOp::kMinus: - return "-"; - case BinaryOp::kMul: - return "*"; - case BinaryOp::kDiv: - return "/"; - case BinaryOp::kMod: - return "%"; - case BinaryOp::kPow: - return "^"; - case BinaryOp::kLt: - return "<"; - case BinaryOp::kLe: - return "<="; - case BinaryOp::kGe: - return ">="; - case BinaryOp::kNotEq: - return "!="; - } -} - -std::string ToString(const Attribute& attr) { - return std::format("{}.{}", attr.table, attr.name); -} - -std::string ToString(UnaryOp op) { - switch (op) { - case UnaryOp::kNot: - return "not"; - case UnaryOp::kMinus: - return "-"; - } -} - -std::string ToString(Literal literal) { - switch (literal) { - case Literal::kNull: - return "NULL"; - case Literal::kTrue: - return "TRUE"; - case Literal::kFalse: - return "FALSE"; - case Literal::kUnknown: - return "UNKNOWN"; - } -} - -std::string ToString(const Expression& expr) { - struct Formatter { - std::string operator()(const BinaryExpression& expr) { - return std::format("{} {} {}", ToString(*expr.lhs), ToString(expr.binop), ToString(*expr.rhs)); - } - std::string operator()(const Attribute& expr) { - return ToString(expr); - } - std::string operator()(const IntConst& expr) { - return std::to_string(expr); - } - std::string operator()(const UnaryExpression& expr) { - return std::format("{} {}", ToString(expr.op), ToString(*expr.child)); - } - std::string operator()(const Literal& expr) { - return ToString(expr); - } - }; - return std::visit(Formatter{}, expr); -} - -std::string ToString(JoinType type) { - switch (type) { - case JoinType::kInner: - return "⋈"; - case JoinType::kFull: - return "⟗"; - case JoinType::kLeft: - return "⟕"; - case JoinType::kRight: - return "⟖"; - } -} - } // namespace stewkk::sql diff --git a/src/stewkk/sql/utils/output_dot_plans.cpp b/src/stewkk/sql/utils/output_dot_plans.cpp new file mode 100644 index 0000000..d32cb31 --- /dev/null +++ b/src/stewkk/sql/utils/output_dot_plans.cpp @@ -0,0 +1,26 @@ +#include + +#include +#include +#include + +#include + +namespace stewkk::sql { + +void OutputDot(const PhysicalPlanNode& optimizer_plan, const std::optional other_plan) { + std::filesystem::create_directories(".plans"); + auto ts = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + { + std::ofstream dot_file{std::format(".plans/{}.dot", ts)}; + dot_file << SerializeDot(optimizer_plan); + } + if (other_plan) { + std::ofstream dot_file{std::format(".plans/{}.ref.dot", ts)}; + dot_file << SerializeDot(*other_plan); + } +} + +} // namespace stewkk::sql diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 65187f8..99ea0ac 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,6 +5,12 @@ add_executable(unittests) target_sources(unittests PRIVATE ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/parser/parser_test.cpp ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/executor/executor_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/executor/plan_serializer_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/optimizer/rule_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/optimizer/rules_applier_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/optimizer/optimizer_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/optimizer/memo_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/optimizer/properties/properties_test.cpp ) target_compile_features(unittests PRIVATE cxx_std_23) set_target_properties(unittests PROPERTIES @@ -20,12 +26,11 @@ target_include_directories( ) target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) -llvm_map_components_to_libnames(llvm_libs Support Core IRReader OrcJIT X86CodeGen X86AsmParser X86Desc X86Info) -target_link_directories(libsql PUBLIC ${LLVM_LIBRARY_DIRS}) -target_link_libraries(unittests PRIVATE gmock_main libsql antlr4_static +target_link_libraries(unittests PRIVATE + gmock_main + libsql Boost::asio Boost::thread Boost::filesystem - ${llvm_libs} ) gtest_discover_tests(unittests) diff --git a/test/static/executor/test_data/customers.csv b/test/static/executor/test_data/customers.csv new file mode 100644 index 0000000..4d48d7c --- /dev/null +++ b/test/static/executor/test_data/customers.csv @@ -0,0 +1,501 @@ +id:int,region_id:int +1,10 +2,6 +3,4 +4,6 +5,8 +6,8 +7,5 +8,4 +9,8 +10,2 +11,8 +12,9 +13,8 +14,5 +15,5 +16,10 +17,9 +18,8 +19,2 +20,2 +21,10 +22,9 +23,5 +24,8 +25,4 +26,7 +27,7 +28,4 +29,6 +30,10 +31,2 +32,8 +33,2 +34,6 +35,10 +36,9 +37,5 +38,10 +39,8 +40,2 +41,6 +42,10 +43,3 +44,1 +45,9 +46,8 +47,8 +48,2 +49,8 +50,5 +51,5 +52,8 +53,4 +54,1 +55,9 +56,10 +57,7 +58,2 +59,5 +60,10 +61,10 +62,5 +63,5 +64,7 +65,2 +66,3 +67,3 +68,6 +69,6 +70,3 +71,6 +72,6 +73,7 +74,9 +75,1 +76,1 +77,1 +78,5 +79,4 +80,2 +81,5 +82,4 +83,6 +84,1 +85,7 +86,4 +87,8 +88,5 +89,2 +90,10 +91,3 +92,2 +93,8 +94,8 +95,1 +96,10 +97,9 +98,5 +99,3 +100,7 +101,5 +102,10 +103,1 +104,7 +105,1 +106,3 +107,3 +108,5 +109,1 +110,7 +111,9 +112,8 +113,10 +114,5 +115,3 +116,1 +117,5 +118,8 +119,2 +120,1 +121,2 +122,6 +123,2 +124,8 +125,10 +126,8 +127,4 +128,5 +129,3 +130,5 +131,1 +132,9 +133,1 +134,9 +135,3 +136,9 +137,3 +138,3 +139,4 +140,7 +141,6 +142,8 +143,10 +144,8 +145,10 +146,6 +147,10 +148,7 +149,9 +150,2 +151,9 +152,8 +153,10 +154,9 +155,2 +156,6 +157,10 +158,8 +159,3 +160,4 +161,9 +162,5 +163,10 +164,5 +165,5 +166,4 +167,2 +168,9 +169,4 +170,1 +171,9 +172,8 +173,2 +174,8 +175,9 +176,2 +177,8 +178,10 +179,8 +180,5 +181,4 +182,6 +183,8 +184,4 +185,5 +186,4 +187,9 +188,8 +189,5 +190,8 +191,6 +192,1 +193,2 +194,2 +195,1 +196,4 +197,9 +198,2 +199,7 +200,2 +201,10 +202,5 +203,7 +204,1 +205,10 +206,6 +207,9 +208,9 +209,7 +210,9 +211,4 +212,7 +213,6 +214,2 +215,1 +216,6 +217,3 +218,7 +219,2 +220,3 +221,7 +222,3 +223,8 +224,6 +225,10 +226,8 +227,7 +228,1 +229,10 +230,6 +231,10 +232,2 +233,5 +234,7 +235,3 +236,3 +237,7 +238,9 +239,3 +240,4 +241,4 +242,2 +243,2 +244,3 +245,2 +246,4 +247,4 +248,3 +249,8 +250,10 +251,4 +252,5 +253,9 +254,2 +255,9 +256,6 +257,10 +258,5 +259,1 +260,9 +261,2 +262,7 +263,8 +264,7 +265,9 +266,8 +267,7 +268,6 +269,8 +270,5 +271,4 +272,1 +273,7 +274,4 +275,4 +276,4 +277,9 +278,3 +279,8 +280,4 +281,5 +282,4 +283,6 +284,4 +285,8 +286,7 +287,1 +288,10 +289,3 +290,2 +291,4 +292,9 +293,7 +294,9 +295,7 +296,2 +297,5 +298,5 +299,5 +300,9 +301,9 +302,8 +303,6 +304,7 +305,7 +306,1 +307,7 +308,4 +309,10 +310,3 +311,9 +312,6 +313,10 +314,8 +315,7 +316,5 +317,1 +318,7 +319,3 +320,5 +321,9 +322,3 +323,8 +324,3 +325,8 +326,8 +327,4 +328,1 +329,2 +330,9 +331,6 +332,7 +333,3 +334,7 +335,7 +336,8 +337,4 +338,3 +339,7 +340,2 +341,10 +342,7 +343,10 +344,1 +345,2 +346,9 +347,2 +348,3 +349,1 +350,1 +351,2 +352,8 +353,4 +354,2 +355,10 +356,8 +357,9 +358,3 +359,5 +360,10 +361,6 +362,7 +363,2 +364,4 +365,10 +366,2 +367,3 +368,5 +369,6 +370,4 +371,8 +372,8 +373,9 +374,2 +375,4 +376,1 +377,6 +378,1 +379,7 +380,3 +381,4 +382,5 +383,8 +384,2 +385,5 +386,10 +387,3 +388,8 +389,3 +390,2 +391,8 +392,1 +393,2 +394,6 +395,1 +396,6 +397,3 +398,2 +399,1 +400,5 +401,6 +402,10 +403,5 +404,4 +405,7 +406,9 +407,8 +408,3 +409,6 +410,10 +411,2 +412,1 +413,5 +414,6 +415,10 +416,1 +417,3 +418,9 +419,1 +420,8 +421,2 +422,7 +423,7 +424,7 +425,3 +426,7 +427,8 +428,10 +429,2 +430,8 +431,8 +432,10 +433,6 +434,3 +435,8 +436,8 +437,7 +438,2 +439,6 +440,4 +441,7 +442,10 +443,7 +444,7 +445,1 +446,7 +447,8 +448,1 +449,9 +450,9 +451,10 +452,8 +453,7 +454,3 +455,7 +456,8 +457,7 +458,2 +459,5 +460,10 +461,2 +462,1 +463,4 +464,2 +465,1 +466,9 +467,10 +468,6 +469,4 +470,2 +471,5 +472,9 +473,1 +474,2 +475,5 +476,10 +477,7 +478,9 +479,8 +480,6 +481,9 +482,10 +483,5 +484,4 +485,9 +486,9 +487,2 +488,1 +489,6 +490,2 +491,8 +492,7 +493,3 +494,5 +495,7 +496,9 +497,5 +498,9 +499,9 +500,5 diff --git a/test/static/executor/test_data/markets.csv b/test/static/executor/test_data/markets.csv new file mode 100644 index 0000000..59c1ab1 --- /dev/null +++ b/test/static/executor/test_data/markets.csv @@ -0,0 +1,4 @@ +id:int,region:string,note:string +1,AMERICA,"North, South" +2,EUROPE,Old World +3,AMERICA,Fast lane diff --git a/test/static/executor/test_data/orders.csv b/test/static/executor/test_data/orders.csv new file mode 100644 index 0000000..efe6f8c --- /dev/null +++ b/test/static/executor/test_data/orders.csv @@ -0,0 +1,5001 @@ +id:int,customer_id:int +1,447 +2,113 +3,23 +4,319 +5,190 +6,304 +7,358 +8,23 +9,410 +10,397 +11,163 +12,352 +13,165 +14,169 +15,271 +16,300 +17,446 +18,436 +19,34 +20,58 +21,191 +22,56 +23,401 +24,436 +25,432 +26,248 +27,233 +28,218 +29,371 +30,384 +31,388 +32,338 +33,248 +34,191 +35,441 +36,206 +37,483 +38,364 +39,29 +40,359 +41,107 +42,178 +43,426 +44,289 +45,418 +46,283 +47,207 +48,389 +49,28 +50,310 +51,405 +52,357 +53,488 +54,225 +55,268 +56,333 +57,467 +58,293 +59,279 +60,138 +61,337 +62,252 +63,253 +64,240 +65,311 +66,102 +67,108 +68,348 +69,73 +70,116 +71,369 +72,199 +73,354 +74,436 +75,496 +76,307 +77,84 +78,447 +79,239 +80,263 +81,113 +82,336 +83,397 +84,122 +85,227 +86,280 +87,381 +88,362 +89,82 +90,323 +91,134 +92,172 +93,224 +94,185 +95,124 +96,369 +97,353 +98,108 +99,48 +100,142 +101,324 +102,404 +103,128 +104,236 +105,302 +106,142 +107,257 +108,93 +109,341 +110,8 +111,386 +112,411 +113,393 +114,281 +115,145 +116,193 +117,390 +118,273 +119,418 +120,231 +121,199 +122,252 +123,314 +124,315 +125,95 +126,406 +127,194 +128,494 +129,81 +130,339 +131,239 +132,497 +133,319 +134,418 +135,480 +136,224 +137,126 +138,365 +139,498 +140,10 +141,70 +142,48 +143,244 +144,72 +145,260 +146,47 +147,175 +148,289 +149,371 +150,125 +151,243 +152,406 +153,289 +154,372 +155,278 +156,432 +157,14 +158,146 +159,74 +160,461 +161,133 +162,477 +163,109 +164,321 +165,380 +166,343 +167,294 +168,52 +169,365 +170,115 +171,88 +172,298 +173,108 +174,249 +175,340 +176,115 +177,192 +178,461 +179,290 +180,56 +181,405 +182,365 +183,329 +184,136 +185,481 +186,411 +187,373 +188,420 +189,214 +190,481 +191,293 +192,138 +193,226 +194,107 +195,47 +196,119 +197,130 +198,422 +199,270 +200,410 +201,191 +202,313 +203,322 +204,464 +205,286 +206,141 +207,252 +208,293 +209,9 +210,398 +211,458 +212,315 +213,352 +214,323 +215,88 +216,184 +217,126 +218,96 +219,367 +220,459 +221,418 +222,364 +223,421 +224,420 +225,419 +226,172 +227,6 +228,227 +229,122 +230,335 +231,303 +232,157 +233,38 +234,279 +235,460 +236,312 +237,74 +238,233 +239,204 +240,178 +241,335 +242,229 +243,495 +244,281 +245,172 +246,272 +247,29 +248,346 +249,403 +250,5 +251,460 +252,121 +253,464 +254,171 +255,455 +256,221 +257,206 +258,88 +259,71 +260,426 +261,233 +262,472 +263,165 +264,127 +265,429 +266,33 +267,235 +268,264 +269,12 +270,37 +271,347 +272,200 +273,104 +274,233 +275,260 +276,257 +277,120 +278,224 +279,235 +280,221 +281,20 +282,184 +283,45 +284,299 +285,36 +286,279 +287,346 +288,307 +289,272 +290,223 +291,274 +292,429 +293,236 +294,334 +295,419 +296,267 +297,285 +298,434 +299,329 +300,32 +301,181 +302,284 +303,249 +304,308 +305,135 +306,325 +307,452 +308,20 +309,491 +310,118 +311,28 +312,427 +313,264 +314,288 +315,348 +316,331 +317,292 +318,379 +319,247 +320,59 +321,322 +322,81 +323,400 +324,63 +325,382 +326,41 +327,10 +328,343 +329,262 +330,463 +331,440 +332,265 +333,448 +334,48 +335,321 +336,206 +337,102 +338,245 +339,291 +340,379 +341,119 +342,213 +343,25 +344,160 +345,153 +346,13 +347,467 +348,446 +349,440 +350,362 +351,54 +352,124 +353,312 +354,294 +355,49 +356,446 +357,94 +358,363 +359,238 +360,160 +361,201 +362,191 +363,89 +364,342 +365,109 +366,256 +367,380 +368,335 +369,151 +370,116 +371,206 +372,379 +373,320 +374,479 +375,461 +376,60 +377,220 +378,150 +379,299 +380,155 +381,206 +382,472 +383,489 +384,173 +385,192 +386,144 +387,61 +388,291 +389,436 +390,448 +391,294 +392,453 +393,214 +394,497 +395,286 +396,482 +397,113 +398,86 +399,104 +400,82 +401,116 +402,401 +403,60 +404,248 +405,111 +406,451 +407,463 +408,395 +409,193 +410,296 +411,296 +412,443 +413,196 +414,293 +415,251 +416,327 +417,464 +418,158 +419,15 +420,23 +421,295 +422,321 +423,159 +424,448 +425,204 +426,111 +427,414 +428,377 +429,441 +430,114 +431,314 +432,234 +433,80 +434,145 +435,154 +436,36 +437,199 +438,200 +439,438 +440,119 +441,272 +442,402 +443,102 +444,124 +445,414 +446,89 +447,33 +448,71 +449,217 +450,402 +451,84 +452,207 +453,489 +454,463 +455,79 +456,291 +457,343 +458,471 +459,275 +460,143 +461,143 +462,429 +463,314 +464,34 +465,336 +466,183 +467,440 +468,248 +469,237 +470,13 +471,403 +472,376 +473,364 +474,323 +475,157 +476,130 +477,394 +478,451 +479,344 +480,285 +481,23 +482,143 +483,295 +484,188 +485,461 +486,113 +487,402 +488,215 +489,35 +490,242 +491,392 +492,483 +493,294 +494,219 +495,210 +496,494 +497,233 +498,256 +499,419 +500,285 +501,82 +502,450 +503,127 +504,33 +505,276 +506,132 +507,46 +508,458 +509,484 +510,276 +511,166 +512,452 +513,2 +514,116 +515,498 +516,493 +517,369 +518,355 +519,359 +520,427 +521,372 +522,325 +523,447 +524,333 +525,26 +526,144 +527,442 +528,395 +529,78 +530,414 +531,494 +532,184 +533,359 +534,218 +535,77 +536,46 +537,239 +538,377 +539,277 +540,292 +541,249 +542,401 +543,176 +544,20 +545,102 +546,197 +547,138 +548,307 +549,436 +550,225 +551,143 +552,73 +553,59 +554,491 +555,124 +556,343 +557,98 +558,367 +559,137 +560,79 +561,304 +562,302 +563,140 +564,366 +565,499 +566,374 +567,181 +568,482 +569,81 +570,314 +571,350 +572,2 +573,34 +574,289 +575,241 +576,264 +577,50 +578,353 +579,21 +580,311 +581,141 +582,90 +583,195 +584,172 +585,16 +586,478 +587,156 +588,63 +589,123 +590,269 +591,450 +592,192 +593,121 +594,494 +595,113 +596,434 +597,167 +598,363 +599,388 +600,171 +601,453 +602,39 +603,290 +604,344 +605,212 +606,299 +607,253 +608,182 +609,479 +610,500 +611,350 +612,389 +613,193 +614,300 +615,438 +616,134 +617,334 +618,390 +619,484 +620,459 +621,201 +622,266 +623,132 +624,447 +625,53 +626,427 +627,430 +628,34 +629,94 +630,109 +631,89 +632,481 +633,175 +634,65 +635,404 +636,251 +637,52 +638,152 +639,495 +640,208 +641,83 +642,315 +643,276 +644,382 +645,318 +646,182 +647,284 +648,84 +649,213 +650,241 +651,165 +652,437 +653,77 +654,107 +655,164 +656,426 +657,299 +658,484 +659,66 +660,356 +661,103 +662,52 +663,219 +664,494 +665,50 +666,298 +667,49 +668,36 +669,247 +670,366 +671,406 +672,307 +673,35 +674,205 +675,155 +676,156 +677,220 +678,311 +679,292 +680,284 +681,300 +682,273 +683,360 +684,78 +685,203 +686,487 +687,463 +688,231 +689,243 +690,288 +691,282 +692,142 +693,406 +694,466 +695,73 +696,81 +697,316 +698,3 +699,395 +700,58 +701,245 +702,235 +703,388 +704,23 +705,457 +706,415 +707,492 +708,376 +709,112 +710,188 +711,163 +712,97 +713,97 +714,470 +715,361 +716,432 +717,188 +718,238 +719,165 +720,287 +721,26 +722,350 +723,328 +724,454 +725,147 +726,211 +727,177 +728,189 +729,357 +730,217 +731,374 +732,368 +733,178 +734,451 +735,58 +736,114 +737,246 +738,283 +739,188 +740,283 +741,20 +742,450 +743,390 +744,357 +745,17 +746,68 +747,17 +748,213 +749,65 +750,143 +751,18 +752,456 +753,399 +754,92 +755,488 +756,275 +757,200 +758,25 +759,253 +760,250 +761,155 +762,174 +763,253 +764,457 +765,225 +766,78 +767,172 +768,458 +769,327 +770,436 +771,383 +772,248 +773,417 +774,348 +775,497 +776,141 +777,132 +778,394 +779,471 +780,46 +781,17 +782,392 +783,140 +784,307 +785,216 +786,56 +787,213 +788,348 +789,39 +790,279 +791,30 +792,236 +793,335 +794,493 +795,399 +796,87 +797,486 +798,491 +799,400 +800,55 +801,431 +802,98 +803,118 +804,250 +805,347 +806,6 +807,64 +808,104 +809,239 +810,157 +811,500 +812,489 +813,309 +814,198 +815,70 +816,269 +817,191 +818,300 +819,241 +820,439 +821,107 +822,347 +823,223 +824,498 +825,359 +826,95 +827,415 +828,402 +829,223 +830,427 +831,138 +832,401 +833,309 +834,318 +835,403 +836,349 +837,444 +838,335 +839,209 +840,450 +841,200 +842,500 +843,271 +844,395 +845,24 +846,487 +847,399 +848,489 +849,25 +850,443 +851,259 +852,317 +853,226 +854,42 +855,362 +856,348 +857,11 +858,444 +859,472 +860,274 +861,114 +862,100 +863,254 +864,53 +865,290 +866,237 +867,328 +868,382 +869,206 +870,279 +871,393 +872,202 +873,437 +874,346 +875,403 +876,314 +877,268 +878,288 +879,147 +880,448 +881,13 +882,438 +883,459 +884,352 +885,54 +886,470 +887,301 +888,309 +889,107 +890,272 +891,45 +892,152 +893,408 +894,124 +895,406 +896,348 +897,318 +898,337 +899,43 +900,40 +901,51 +902,36 +903,470 +904,228 +905,307 +906,221 +907,3 +908,435 +909,53 +910,26 +911,212 +912,67 +913,263 +914,494 +915,400 +916,262 +917,320 +918,407 +919,336 +920,62 +921,334 +922,55 +923,480 +924,322 +925,161 +926,449 +927,389 +928,51 +929,369 +930,430 +931,95 +932,122 +933,304 +934,94 +935,384 +936,471 +937,77 +938,416 +939,393 +940,219 +941,200 +942,37 +943,242 +944,6 +945,420 +946,217 +947,78 +948,235 +949,293 +950,277 +951,35 +952,142 +953,10 +954,400 +955,99 +956,207 +957,325 +958,461 +959,483 +960,59 +961,292 +962,60 +963,297 +964,78 +965,447 +966,382 +967,303 +968,30 +969,377 +970,474 +971,215 +972,177 +973,459 +974,365 +975,475 +976,40 +977,296 +978,194 +979,362 +980,336 +981,204 +982,464 +983,54 +984,24 +985,417 +986,352 +987,355 +988,307 +989,237 +990,97 +991,321 +992,248 +993,495 +994,413 +995,235 +996,390 +997,361 +998,68 +999,8 +1000,350 +1001,368 +1002,174 +1003,201 +1004,336 +1005,98 +1006,151 +1007,464 +1008,105 +1009,446 +1010,138 +1011,248 +1012,165 +1013,217 +1014,35 +1015,362 +1016,350 +1017,312 +1018,367 +1019,264 +1020,354 +1021,72 +1022,342 +1023,248 +1024,372 +1025,71 +1026,17 +1027,239 +1028,192 +1029,192 +1030,259 +1031,59 +1032,17 +1033,81 +1034,500 +1035,115 +1036,442 +1037,82 +1038,463 +1039,123 +1040,493 +1041,69 +1042,87 +1043,459 +1044,126 +1045,14 +1046,19 +1047,283 +1048,435 +1049,54 +1050,332 +1051,78 +1052,193 +1053,170 +1054,211 +1055,260 +1056,498 +1057,417 +1058,468 +1059,385 +1060,110 +1061,351 +1062,412 +1063,454 +1064,134 +1065,371 +1066,39 +1067,357 +1068,71 +1069,298 +1070,213 +1071,326 +1072,433 +1073,489 +1074,153 +1075,376 +1076,56 +1077,342 +1078,407 +1079,138 +1080,395 +1081,304 +1082,110 +1083,43 +1084,478 +1085,398 +1086,264 +1087,375 +1088,185 +1089,31 +1090,328 +1091,449 +1092,190 +1093,278 +1094,465 +1095,99 +1096,197 +1097,314 +1098,303 +1099,333 +1100,379 +1101,70 +1102,58 +1103,243 +1104,204 +1105,89 +1106,259 +1107,343 +1108,163 +1109,379 +1110,215 +1111,232 +1112,89 +1113,144 +1114,105 +1115,349 +1116,154 +1117,357 +1118,173 +1119,134 +1120,120 +1121,421 +1122,369 +1123,472 +1124,257 +1125,312 +1126,395 +1127,166 +1128,381 +1129,299 +1130,57 +1131,326 +1132,34 +1133,126 +1134,360 +1135,134 +1136,230 +1137,357 +1138,99 +1139,201 +1140,351 +1141,497 +1142,402 +1143,462 +1144,476 +1145,382 +1146,50 +1147,68 +1148,425 +1149,462 +1150,253 +1151,234 +1152,301 +1153,262 +1154,53 +1155,418 +1156,392 +1157,5 +1158,262 +1159,195 +1160,243 +1161,136 +1162,99 +1163,201 +1164,344 +1165,170 +1166,94 +1167,478 +1168,106 +1169,485 +1170,35 +1171,125 +1172,161 +1173,206 +1174,136 +1175,354 +1176,406 +1177,423 +1178,14 +1179,13 +1180,474 +1181,473 +1182,160 +1183,34 +1184,454 +1185,437 +1186,82 +1187,141 +1188,75 +1189,393 +1190,40 +1191,87 +1192,68 +1193,438 +1194,275 +1195,132 +1196,75 +1197,220 +1198,374 +1199,401 +1200,422 +1201,167 +1202,399 +1203,71 +1204,474 +1205,412 +1206,338 +1207,4 +1208,6 +1209,448 +1210,52 +1211,248 +1212,191 +1213,208 +1214,315 +1215,404 +1216,51 +1217,278 +1218,401 +1219,171 +1220,93 +1221,377 +1222,50 +1223,252 +1224,15 +1225,482 +1226,207 +1227,351 +1228,370 +1229,422 +1230,227 +1231,300 +1232,326 +1233,41 +1234,436 +1235,91 +1236,252 +1237,422 +1238,360 +1239,360 +1240,94 +1241,97 +1242,336 +1243,15 +1244,358 +1245,336 +1246,234 +1247,59 +1248,363 +1249,218 +1250,425 +1251,328 +1252,337 +1253,429 +1254,447 +1255,237 +1256,226 +1257,137 +1258,162 +1259,451 +1260,217 +1261,486 +1262,160 +1263,376 +1264,500 +1265,173 +1266,293 +1267,102 +1268,194 +1269,83 +1270,313 +1271,388 +1272,500 +1273,52 +1274,212 +1275,307 +1276,109 +1277,238 +1278,307 +1279,299 +1280,447 +1281,42 +1282,78 +1283,87 +1284,484 +1285,277 +1286,372 +1287,151 +1288,95 +1289,310 +1290,98 +1291,261 +1292,275 +1293,239 +1294,101 +1295,236 +1296,265 +1297,263 +1298,229 +1299,113 +1300,399 +1301,6 +1302,364 +1303,121 +1304,424 +1305,196 +1306,438 +1307,72 +1308,6 +1309,435 +1310,178 +1311,325 +1312,487 +1313,207 +1314,90 +1315,179 +1316,208 +1317,487 +1318,386 +1319,498 +1320,245 +1321,4 +1322,386 +1323,76 +1324,163 +1325,410 +1326,81 +1327,490 +1328,3 +1329,427 +1330,94 +1331,363 +1332,335 +1333,364 +1334,489 +1335,307 +1336,147 +1337,64 +1338,385 +1339,284 +1340,96 +1341,344 +1342,171 +1343,303 +1344,183 +1345,386 +1346,235 +1347,260 +1348,172 +1349,479 +1350,72 +1351,408 +1352,247 +1353,242 +1354,86 +1355,419 +1356,133 +1357,48 +1358,317 +1359,198 +1360,287 +1361,350 +1362,268 +1363,373 +1364,56 +1365,379 +1366,317 +1367,411 +1368,16 +1369,173 +1370,138 +1371,221 +1372,418 +1373,8 +1374,342 +1375,79 +1376,171 +1377,320 +1378,120 +1379,120 +1380,219 +1381,474 +1382,176 +1383,104 +1384,114 +1385,282 +1386,218 +1387,292 +1388,121 +1389,476 +1390,488 +1391,346 +1392,280 +1393,202 +1394,306 +1395,257 +1396,326 +1397,490 +1398,348 +1399,107 +1400,367 +1401,499 +1402,320 +1403,69 +1404,6 +1405,497 +1406,42 +1407,55 +1408,332 +1409,142 +1410,447 +1411,210 +1412,106 +1413,89 +1414,115 +1415,139 +1416,457 +1417,116 +1418,51 +1419,83 +1420,410 +1421,215 +1422,288 +1423,216 +1424,287 +1425,236 +1426,192 +1427,377 +1428,387 +1429,369 +1430,350 +1431,104 +1432,157 +1433,354 +1434,252 +1435,169 +1436,181 +1437,259 +1438,166 +1439,208 +1440,461 +1441,329 +1442,132 +1443,194 +1444,71 +1445,51 +1446,234 +1447,199 +1448,180 +1449,198 +1450,294 +1451,320 +1452,442 +1453,144 +1454,165 +1455,445 +1456,391 +1457,431 +1458,4 +1459,39 +1460,273 +1461,294 +1462,379 +1463,435 +1464,428 +1465,367 +1466,346 +1467,363 +1468,358 +1469,240 +1470,236 +1471,299 +1472,303 +1473,233 +1474,255 +1475,41 +1476,56 +1477,91 +1478,321 +1479,11 +1480,71 +1481,357 +1482,377 +1483,1 +1484,63 +1485,265 +1486,43 +1487,307 +1488,214 +1489,84 +1490,339 +1491,199 +1492,443 +1493,296 +1494,141 +1495,493 +1496,280 +1497,432 +1498,44 +1499,452 +1500,149 +1501,26 +1502,214 +1503,308 +1504,277 +1505,54 +1506,416 +1507,301 +1508,256 +1509,100 +1510,61 +1511,395 +1512,492 +1513,366 +1514,214 +1515,125 +1516,499 +1517,140 +1518,433 +1519,451 +1520,302 +1521,29 +1522,276 +1523,327 +1524,210 +1525,484 +1526,42 +1527,451 +1528,467 +1529,435 +1530,64 +1531,84 +1532,55 +1533,35 +1534,180 +1535,431 +1536,276 +1537,219 +1538,325 +1539,209 +1540,88 +1541,206 +1542,334 +1543,318 +1544,424 +1545,140 +1546,143 +1547,389 +1548,61 +1549,20 +1550,157 +1551,372 +1552,434 +1553,317 +1554,132 +1555,484 +1556,43 +1557,430 +1558,283 +1559,21 +1560,322 +1561,445 +1562,419 +1563,466 +1564,302 +1565,19 +1566,10 +1567,471 +1568,256 +1569,182 +1570,184 +1571,226 +1572,332 +1573,362 +1574,470 +1575,429 +1576,407 +1577,87 +1578,176 +1579,106 +1580,329 +1581,129 +1582,363 +1583,248 +1584,473 +1585,414 +1586,7 +1587,294 +1588,428 +1589,182 +1590,4 +1591,365 +1592,66 +1593,103 +1594,94 +1595,193 +1596,293 +1597,414 +1598,20 +1599,426 +1600,492 +1601,231 +1602,371 +1603,493 +1604,259 +1605,203 +1606,392 +1607,486 +1608,262 +1609,48 +1610,97 +1611,237 +1612,6 +1613,485 +1614,416 +1615,448 +1616,3 +1617,167 +1618,34 +1619,220 +1620,197 +1621,24 +1622,245 +1623,17 +1624,122 +1625,241 +1626,122 +1627,203 +1628,265 +1629,496 +1630,468 +1631,171 +1632,390 +1633,422 +1634,172 +1635,389 +1636,108 +1637,333 +1638,485 +1639,221 +1640,174 +1641,27 +1642,267 +1643,486 +1644,96 +1645,5 +1646,33 +1647,171 +1648,255 +1649,99 +1650,403 +1651,197 +1652,293 +1653,260 +1654,112 +1655,413 +1656,366 +1657,373 +1658,164 +1659,425 +1660,191 +1661,468 +1662,127 +1663,418 +1664,498 +1665,71 +1666,185 +1667,254 +1668,24 +1669,436 +1670,190 +1671,337 +1672,20 +1673,177 +1674,16 +1675,344 +1676,450 +1677,58 +1678,446 +1679,257 +1680,431 +1681,448 +1682,394 +1683,283 +1684,29 +1685,291 +1686,354 +1687,113 +1688,379 +1689,167 +1690,382 +1691,413 +1692,83 +1693,417 +1694,259 +1695,11 +1696,460 +1697,234 +1698,218 +1699,200 +1700,163 +1701,203 +1702,120 +1703,409 +1704,437 +1705,235 +1706,370 +1707,147 +1708,226 +1709,249 +1710,489 +1711,208 +1712,312 +1713,330 +1714,407 +1715,236 +1716,374 +1717,237 +1718,35 +1719,107 +1720,84 +1721,274 +1722,189 +1723,338 +1724,274 +1725,94 +1726,494 +1727,352 +1728,307 +1729,401 +1730,381 +1731,3 +1732,238 +1733,393 +1734,308 +1735,101 +1736,363 +1737,323 +1738,340 +1739,253 +1740,479 +1741,429 +1742,116 +1743,26 +1744,88 +1745,190 +1746,33 +1747,321 +1748,238 +1749,466 +1750,39 +1751,361 +1752,195 +1753,217 +1754,433 +1755,360 +1756,500 +1757,188 +1758,236 +1759,234 +1760,312 +1761,479 +1762,384 +1763,183 +1764,192 +1765,244 +1766,234 +1767,192 +1768,220 +1769,30 +1770,457 +1771,217 +1772,143 +1773,272 +1774,313 +1775,192 +1776,195 +1777,283 +1778,287 +1779,3 +1780,4 +1781,373 +1782,260 +1783,238 +1784,448 +1785,477 +1786,460 +1787,359 +1788,198 +1789,478 +1790,36 +1791,429 +1792,487 +1793,317 +1794,11 +1795,494 +1796,211 +1797,44 +1798,111 +1799,402 +1800,290 +1801,384 +1802,42 +1803,300 +1804,161 +1805,242 +1806,333 +1807,45 +1808,110 +1809,60 +1810,20 +1811,337 +1812,319 +1813,18 +1814,488 +1815,310 +1816,250 +1817,474 +1818,201 +1819,472 +1820,331 +1821,410 +1822,182 +1823,61 +1824,427 +1825,152 +1826,496 +1827,346 +1828,264 +1829,242 +1830,370 +1831,400 +1832,338 +1833,369 +1834,314 +1835,276 +1836,273 +1837,494 +1838,29 +1839,241 +1840,220 +1841,59 +1842,234 +1843,56 +1844,303 +1845,368 +1846,309 +1847,494 +1848,76 +1849,495 +1850,258 +1851,156 +1852,284 +1853,124 +1854,225 +1855,312 +1856,273 +1857,138 +1858,423 +1859,27 +1860,376 +1861,312 +1862,71 +1863,423 +1864,478 +1865,177 +1866,356 +1867,303 +1868,485 +1869,86 +1870,407 +1871,219 +1872,96 +1873,394 +1874,323 +1875,111 +1876,354 +1877,488 +1878,391 +1879,185 +1880,492 +1881,301 +1882,209 +1883,496 +1884,368 +1885,241 +1886,263 +1887,203 +1888,432 +1889,185 +1890,210 +1891,13 +1892,135 +1893,1 +1894,111 +1895,62 +1896,254 +1897,114 +1898,375 +1899,46 +1900,420 +1901,353 +1902,94 +1903,316 +1904,246 +1905,403 +1906,469 +1907,283 +1908,144 +1909,34 +1910,256 +1911,420 +1912,414 +1913,236 +1914,289 +1915,250 +1916,135 +1917,500 +1918,138 +1919,358 +1920,406 +1921,164 +1922,176 +1923,137 +1924,365 +1925,166 +1926,329 +1927,186 +1928,19 +1929,314 +1930,461 +1931,467 +1932,366 +1933,322 +1934,26 +1935,493 +1936,481 +1937,14 +1938,277 +1939,114 +1940,218 +1941,204 +1942,274 +1943,499 +1944,299 +1945,177 +1946,297 +1947,191 +1948,210 +1949,139 +1950,417 +1951,95 +1952,380 +1953,208 +1954,233 +1955,65 +1956,76 +1957,496 +1958,143 +1959,87 +1960,383 +1961,332 +1962,497 +1963,343 +1964,332 +1965,1 +1966,254 +1967,362 +1968,395 +1969,85 +1970,84 +1971,191 +1972,158 +1973,70 +1974,259 +1975,499 +1976,151 +1977,371 +1978,211 +1979,118 +1980,24 +1981,85 +1982,102 +1983,12 +1984,157 +1985,207 +1986,220 +1987,194 +1988,118 +1989,269 +1990,262 +1991,191 +1992,380 +1993,309 +1994,29 +1995,36 +1996,336 +1997,59 +1998,230 +1999,201 +2000,56 +2001,258 +2002,146 +2003,313 +2004,185 +2005,35 +2006,368 +2007,230 +2008,391 +2009,182 +2010,359 +2011,182 +2012,317 +2013,152 +2014,260 +2015,368 +2016,423 +2017,369 +2018,97 +2019,355 +2020,229 +2021,66 +2022,252 +2023,311 +2024,482 +2025,187 +2026,130 +2027,486 +2028,252 +2029,268 +2030,175 +2031,317 +2032,22 +2033,360 +2034,372 +2035,10 +2036,40 +2037,416 +2038,158 +2039,200 +2040,76 +2041,399 +2042,241 +2043,487 +2044,279 +2045,55 +2046,242 +2047,86 +2048,92 +2049,407 +2050,222 +2051,460 +2052,381 +2053,140 +2054,480 +2055,437 +2056,126 +2057,222 +2058,67 +2059,335 +2060,69 +2061,355 +2062,191 +2063,456 +2064,262 +2065,127 +2066,303 +2067,253 +2068,79 +2069,159 +2070,263 +2071,308 +2072,131 +2073,109 +2074,46 +2075,491 +2076,499 +2077,218 +2078,227 +2079,156 +2080,43 +2081,100 +2082,227 +2083,424 +2084,197 +2085,278 +2086,104 +2087,377 +2088,362 +2089,460 +2090,14 +2091,278 +2092,265 +2093,108 +2094,420 +2095,451 +2096,360 +2097,143 +2098,123 +2099,364 +2100,91 +2101,280 +2102,40 +2103,391 +2104,222 +2105,98 +2106,82 +2107,336 +2108,376 +2109,22 +2110,227 +2111,9 +2112,79 +2113,422 +2114,486 +2115,472 +2116,240 +2117,276 +2118,190 +2119,210 +2120,194 +2121,272 +2122,211 +2123,183 +2124,4 +2125,306 +2126,35 +2127,187 +2128,349 +2129,249 +2130,498 +2131,55 +2132,344 +2133,69 +2134,14 +2135,186 +2136,352 +2137,49 +2138,138 +2139,337 +2140,245 +2141,196 +2142,170 +2143,210 +2144,401 +2145,2 +2146,154 +2147,328 +2148,2 +2149,307 +2150,178 +2151,129 +2152,279 +2153,340 +2154,379 +2155,395 +2156,179 +2157,420 +2158,435 +2159,416 +2160,102 +2161,283 +2162,326 +2163,355 +2164,272 +2165,356 +2166,30 +2167,411 +2168,164 +2169,191 +2170,236 +2171,316 +2172,180 +2173,112 +2174,304 +2175,291 +2176,300 +2177,362 +2178,374 +2179,472 +2180,380 +2181,155 +2182,354 +2183,418 +2184,113 +2185,377 +2186,220 +2187,98 +2188,379 +2189,405 +2190,315 +2191,30 +2192,240 +2193,301 +2194,160 +2195,275 +2196,460 +2197,390 +2198,347 +2199,382 +2200,359 +2201,361 +2202,389 +2203,3 +2204,451 +2205,383 +2206,385 +2207,365 +2208,278 +2209,471 +2210,281 +2211,490 +2212,475 +2213,271 +2214,360 +2215,159 +2216,361 +2217,401 +2218,477 +2219,10 +2220,64 +2221,458 +2222,100 +2223,149 +2224,111 +2225,116 +2226,20 +2227,449 +2228,145 +2229,371 +2230,48 +2231,188 +2232,233 +2233,376 +2234,57 +2235,175 +2236,343 +2237,309 +2238,485 +2239,22 +2240,217 +2241,79 +2242,420 +2243,26 +2244,426 +2245,42 +2246,362 +2247,479 +2248,166 +2249,372 +2250,110 +2251,168 +2252,236 +2253,286 +2254,316 +2255,265 +2256,236 +2257,417 +2258,327 +2259,83 +2260,246 +2261,153 +2262,31 +2263,193 +2264,103 +2265,479 +2266,313 +2267,214 +2268,301 +2269,311 +2270,283 +2271,407 +2272,10 +2273,243 +2274,118 +2275,477 +2276,352 +2277,150 +2278,346 +2279,82 +2280,484 +2281,232 +2282,2 +2283,480 +2284,477 +2285,464 +2286,340 +2287,307 +2288,283 +2289,421 +2290,127 +2291,316 +2292,237 +2293,106 +2294,373 +2295,281 +2296,466 +2297,184 +2298,409 +2299,17 +2300,436 +2301,260 +2302,176 +2303,179 +2304,174 +2305,427 +2306,436 +2307,228 +2308,38 +2309,2 +2310,459 +2311,375 +2312,76 +2313,252 +2314,336 +2315,362 +2316,256 +2317,156 +2318,443 +2319,333 +2320,59 +2321,119 +2322,9 +2323,182 +2324,95 +2325,401 +2326,371 +2327,336 +2328,118 +2329,209 +2330,272 +2331,101 +2332,163 +2333,387 +2334,128 +2335,277 +2336,403 +2337,244 +2338,500 +2339,3 +2340,266 +2341,113 +2342,88 +2343,422 +2344,249 +2345,225 +2346,356 +2347,433 +2348,420 +2349,455 +2350,50 +2351,311 +2352,125 +2353,263 +2354,391 +2355,99 +2356,407 +2357,324 +2358,78 +2359,348 +2360,173 +2361,368 +2362,70 +2363,324 +2364,443 +2365,319 +2366,490 +2367,314 +2368,386 +2369,312 +2370,417 +2371,50 +2372,201 +2373,2 +2374,139 +2375,176 +2376,112 +2377,147 +2378,410 +2379,347 +2380,169 +2381,156 +2382,435 +2383,293 +2384,93 +2385,376 +2386,275 +2387,103 +2388,106 +2389,297 +2390,381 +2391,1 +2392,413 +2393,161 +2394,279 +2395,496 +2396,419 +2397,457 +2398,380 +2399,447 +2400,476 +2401,466 +2402,402 +2403,498 +2404,376 +2405,19 +2406,203 +2407,250 +2408,35 +2409,13 +2410,207 +2411,117 +2412,50 +2413,170 +2414,194 +2415,78 +2416,162 +2417,9 +2418,263 +2419,150 +2420,345 +2421,270 +2422,213 +2423,253 +2424,403 +2425,316 +2426,98 +2427,353 +2428,125 +2429,11 +2430,241 +2431,1 +2432,155 +2433,279 +2434,432 +2435,305 +2436,426 +2437,200 +2438,197 +2439,322 +2440,326 +2441,407 +2442,367 +2443,275 +2444,7 +2445,481 +2446,343 +2447,415 +2448,203 +2449,386 +2450,444 +2451,278 +2452,138 +2453,86 +2454,133 +2455,333 +2456,36 +2457,430 +2458,222 +2459,247 +2460,275 +2461,430 +2462,406 +2463,29 +2464,99 +2465,484 +2466,394 +2467,353 +2468,39 +2469,394 +2470,262 +2471,133 +2472,314 +2473,241 +2474,336 +2475,179 +2476,286 +2477,219 +2478,13 +2479,420 +2480,288 +2481,247 +2482,244 +2483,408 +2484,439 +2485,81 +2486,154 +2487,40 +2488,215 +2489,25 +2490,353 +2491,294 +2492,131 +2493,474 +2494,56 +2495,487 +2496,50 +2497,429 +2498,476 +2499,355 +2500,343 +2501,498 +2502,101 +2503,473 +2504,95 +2505,254 +2506,20 +2507,98 +2508,156 +2509,59 +2510,262 +2511,4 +2512,279 +2513,263 +2514,108 +2515,180 +2516,143 +2517,398 +2518,379 +2519,351 +2520,200 +2521,217 +2522,94 +2523,65 +2524,88 +2525,121 +2526,448 +2527,414 +2528,164 +2529,403 +2530,466 +2531,418 +2532,101 +2533,484 +2534,34 +2535,453 +2536,194 +2537,391 +2538,192 +2539,374 +2540,316 +2541,145 +2542,161 +2543,414 +2544,155 +2545,458 +2546,102 +2547,118 +2548,101 +2549,229 +2550,414 +2551,198 +2552,123 +2553,407 +2554,3 +2555,291 +2556,14 +2557,45 +2558,63 +2559,346 +2560,275 +2561,251 +2562,302 +2563,4 +2564,150 +2565,270 +2566,420 +2567,419 +2568,181 +2569,422 +2570,332 +2571,196 +2572,348 +2573,190 +2574,156 +2575,10 +2576,108 +2577,135 +2578,108 +2579,29 +2580,405 +2581,210 +2582,430 +2583,45 +2584,465 +2585,372 +2586,21 +2587,64 +2588,253 +2589,424 +2590,168 +2591,91 +2592,39 +2593,433 +2594,298 +2595,326 +2596,171 +2597,138 +2598,57 +2599,234 +2600,442 +2601,406 +2602,267 +2603,238 +2604,167 +2605,122 +2606,180 +2607,290 +2608,318 +2609,52 +2610,385 +2611,3 +2612,431 +2613,191 +2614,389 +2615,249 +2616,461 +2617,127 +2618,185 +2619,87 +2620,471 +2621,444 +2622,286 +2623,278 +2624,243 +2625,438 +2626,425 +2627,275 +2628,296 +2629,368 +2630,472 +2631,20 +2632,43 +2633,282 +2634,399 +2635,391 +2636,266 +2637,324 +2638,349 +2639,58 +2640,219 +2641,314 +2642,457 +2643,293 +2644,236 +2645,71 +2646,407 +2647,116 +2648,191 +2649,267 +2650,418 +2651,216 +2652,463 +2653,52 +2654,51 +2655,163 +2656,210 +2657,293 +2658,354 +2659,358 +2660,308 +2661,215 +2662,26 +2663,341 +2664,398 +2665,236 +2666,83 +2667,25 +2668,265 +2669,89 +2670,203 +2671,156 +2672,258 +2673,381 +2674,201 +2675,356 +2676,108 +2677,64 +2678,187 +2679,222 +2680,55 +2681,228 +2682,45 +2683,160 +2684,260 +2685,414 +2686,29 +2687,444 +2688,282 +2689,425 +2690,124 +2691,54 +2692,299 +2693,225 +2694,205 +2695,375 +2696,220 +2697,192 +2698,277 +2699,140 +2700,97 +2701,426 +2702,458 +2703,417 +2704,281 +2705,156 +2706,3 +2707,3 +2708,94 +2709,213 +2710,440 +2711,136 +2712,294 +2713,377 +2714,394 +2715,328 +2716,434 +2717,347 +2718,313 +2719,293 +2720,237 +2721,465 +2722,263 +2723,200 +2724,265 +2725,321 +2726,155 +2727,134 +2728,158 +2729,34 +2730,201 +2731,138 +2732,212 +2733,136 +2734,387 +2735,331 +2736,367 +2737,266 +2738,173 +2739,372 +2740,276 +2741,315 +2742,18 +2743,349 +2744,372 +2745,426 +2746,126 +2747,195 +2748,152 +2749,280 +2750,25 +2751,53 +2752,79 +2753,450 +2754,8 +2755,494 +2756,154 +2757,275 +2758,412 +2759,499 +2760,360 +2761,396 +2762,385 +2763,296 +2764,127 +2765,222 +2766,281 +2767,162 +2768,104 +2769,227 +2770,159 +2771,164 +2772,341 +2773,495 +2774,210 +2775,206 +2776,476 +2777,91 +2778,464 +2779,190 +2780,366 +2781,229 +2782,379 +2783,454 +2784,289 +2785,484 +2786,321 +2787,383 +2788,131 +2789,418 +2790,328 +2791,338 +2792,441 +2793,88 +2794,113 +2795,227 +2796,289 +2797,433 +2798,352 +2799,357 +2800,459 +2801,151 +2802,117 +2803,130 +2804,189 +2805,54 +2806,357 +2807,343 +2808,303 +2809,401 +2810,122 +2811,160 +2812,256 +2813,269 +2814,187 +2815,309 +2816,459 +2817,386 +2818,452 +2819,62 +2820,304 +2821,395 +2822,364 +2823,175 +2824,441 +2825,250 +2826,193 +2827,471 +2828,131 +2829,431 +2830,374 +2831,360 +2832,423 +2833,104 +2834,292 +2835,116 +2836,317 +2837,311 +2838,375 +2839,265 +2840,16 +2841,200 +2842,90 +2843,192 +2844,58 +2845,408 +2846,243 +2847,109 +2848,369 +2849,335 +2850,468 +2851,472 +2852,434 +2853,487 +2854,224 +2855,266 +2856,205 +2857,313 +2858,285 +2859,178 +2860,124 +2861,105 +2862,146 +2863,329 +2864,471 +2865,212 +2866,210 +2867,168 +2868,287 +2869,375 +2870,234 +2871,195 +2872,392 +2873,246 +2874,371 +2875,496 +2876,214 +2877,428 +2878,318 +2879,169 +2880,194 +2881,432 +2882,175 +2883,143 +2884,135 +2885,259 +2886,140 +2887,321 +2888,165 +2889,293 +2890,473 +2891,67 +2892,475 +2893,317 +2894,481 +2895,17 +2896,446 +2897,24 +2898,207 +2899,479 +2900,47 +2901,344 +2902,310 +2903,388 +2904,174 +2905,136 +2906,284 +2907,457 +2908,168 +2909,454 +2910,304 +2911,389 +2912,461 +2913,333 +2914,415 +2915,303 +2916,96 +2917,463 +2918,314 +2919,401 +2920,106 +2921,436 +2922,416 +2923,121 +2924,313 +2925,245 +2926,157 +2927,190 +2928,170 +2929,230 +2930,257 +2931,240 +2932,499 +2933,150 +2934,490 +2935,89 +2936,346 +2937,223 +2938,154 +2939,261 +2940,191 +2941,344 +2942,139 +2943,392 +2944,287 +2945,489 +2946,313 +2947,440 +2948,468 +2949,414 +2950,180 +2951,301 +2952,119 +2953,104 +2954,84 +2955,195 +2956,380 +2957,252 +2958,414 +2959,488 +2960,72 +2961,67 +2962,441 +2963,199 +2964,334 +2965,320 +2966,357 +2967,301 +2968,451 +2969,193 +2970,186 +2971,228 +2972,183 +2973,315 +2974,110 +2975,125 +2976,152 +2977,283 +2978,350 +2979,202 +2980,412 +2981,80 +2982,473 +2983,432 +2984,243 +2985,333 +2986,246 +2987,312 +2988,470 +2989,94 +2990,73 +2991,491 +2992,409 +2993,169 +2994,172 +2995,90 +2996,4 +2997,402 +2998,338 +2999,38 +3000,265 +3001,319 +3002,185 +3003,397 +3004,203 +3005,245 +3006,492 +3007,172 +3008,307 +3009,401 +3010,13 +3011,80 +3012,2 +3013,172 +3014,77 +3015,465 +3016,196 +3017,132 +3018,377 +3019,436 +3020,42 +3021,399 +3022,464 +3023,239 +3024,185 +3025,310 +3026,488 +3027,123 +3028,91 +3029,215 +3030,150 +3031,327 +3032,171 +3033,183 +3034,421 +3035,43 +3036,235 +3037,28 +3038,221 +3039,212 +3040,122 +3041,84 +3042,55 +3043,97 +3044,165 +3045,244 +3046,246 +3047,495 +3048,112 +3049,217 +3050,362 +3051,424 +3052,154 +3053,474 +3054,498 +3055,339 +3056,190 +3057,202 +3058,289 +3059,208 +3060,195 +3061,262 +3062,188 +3063,50 +3064,393 +3065,82 +3066,484 +3067,361 +3068,56 +3069,309 +3070,349 +3071,478 +3072,219 +3073,367 +3074,294 +3075,329 +3076,172 +3077,66 +3078,389 +3079,33 +3080,235 +3081,313 +3082,195 +3083,134 +3084,483 +3085,165 +3086,267 +3087,345 +3088,180 +3089,202 +3090,145 +3091,8 +3092,228 +3093,304 +3094,477 +3095,400 +3096,49 +3097,150 +3098,304 +3099,109 +3100,106 +3101,49 +3102,294 +3103,107 +3104,474 +3105,359 +3106,276 +3107,63 +3108,9 +3109,189 +3110,95 +3111,144 +3112,163 +3113,248 +3114,309 +3115,94 +3116,35 +3117,349 +3118,49 +3119,417 +3120,429 +3121,417 +3122,64 +3123,137 +3124,425 +3125,156 +3126,350 +3127,428 +3128,394 +3129,41 +3130,325 +3131,431 +3132,258 +3133,380 +3134,396 +3135,452 +3136,287 +3137,454 +3138,324 +3139,423 +3140,35 +3141,107 +3142,341 +3143,63 +3144,283 +3145,371 +3146,212 +3147,449 +3148,43 +3149,40 +3150,148 +3151,174 +3152,79 +3153,357 +3154,210 +3155,413 +3156,62 +3157,436 +3158,109 +3159,273 +3160,183 +3161,352 +3162,396 +3163,44 +3164,481 +3165,201 +3166,324 +3167,186 +3168,27 +3169,387 +3170,35 +3171,35 +3172,425 +3173,153 +3174,437 +3175,376 +3176,220 +3177,282 +3178,401 +3179,89 +3180,201 +3181,358 +3182,149 +3183,23 +3184,96 +3185,221 +3186,331 +3187,300 +3188,325 +3189,270 +3190,495 +3191,132 +3192,197 +3193,331 +3194,342 +3195,391 +3196,446 +3197,321 +3198,57 +3199,350 +3200,400 +3201,500 +3202,108 +3203,211 +3204,339 +3205,460 +3206,72 +3207,179 +3208,351 +3209,71 +3210,150 +3211,17 +3212,466 +3213,436 +3214,458 +3215,332 +3216,61 +3217,440 +3218,324 +3219,321 +3220,337 +3221,485 +3222,187 +3223,216 +3224,291 +3225,425 +3226,312 +3227,44 +3228,172 +3229,359 +3230,398 +3231,44 +3232,152 +3233,287 +3234,103 +3235,347 +3236,96 +3237,408 +3238,107 +3239,246 +3240,175 +3241,497 +3242,449 +3243,134 +3244,204 +3245,453 +3246,225 +3247,266 +3248,23 +3249,84 +3250,83 +3251,186 +3252,437 +3253,144 +3254,296 +3255,248 +3256,240 +3257,1 +3258,228 +3259,24 +3260,9 +3261,280 +3262,155 +3263,127 +3264,359 +3265,493 +3266,177 +3267,253 +3268,163 +3269,258 +3270,182 +3271,54 +3272,146 +3273,80 +3274,147 +3275,44 +3276,197 +3277,250 +3278,254 +3279,213 +3280,65 +3281,301 +3282,348 +3283,268 +3284,495 +3285,259 +3286,174 +3287,212 +3288,112 +3289,423 +3290,409 +3291,208 +3292,303 +3293,472 +3294,248 +3295,398 +3296,199 +3297,165 +3298,288 +3299,172 +3300,61 +3301,483 +3302,455 +3303,41 +3304,13 +3305,6 +3306,318 +3307,370 +3308,470 +3309,440 +3310,343 +3311,428 +3312,167 +3313,391 +3314,456 +3315,495 +3316,151 +3317,261 +3318,188 +3319,132 +3320,267 +3321,244 +3322,255 +3323,91 +3324,227 +3325,147 +3326,450 +3327,7 +3328,416 +3329,490 +3330,448 +3331,322 +3332,338 +3333,137 +3334,199 +3335,481 +3336,472 +3337,386 +3338,198 +3339,390 +3340,371 +3341,302 +3342,14 +3343,99 +3344,252 +3345,296 +3346,337 +3347,129 +3348,131 +3349,248 +3350,194 +3351,342 +3352,245 +3353,392 +3354,175 +3355,440 +3356,241 +3357,132 +3358,314 +3359,239 +3360,159 +3361,119 +3362,296 +3363,54 +3364,289 +3365,36 +3366,410 +3367,62 +3368,491 +3369,1 +3370,106 +3371,352 +3372,167 +3373,80 +3374,343 +3375,205 +3376,316 +3377,137 +3378,276 +3379,349 +3380,45 +3381,402 +3382,372 +3383,396 +3384,375 +3385,496 +3386,132 +3387,126 +3388,182 +3389,13 +3390,200 +3391,174 +3392,269 +3393,491 +3394,210 +3395,249 +3396,354 +3397,496 +3398,232 +3399,160 +3400,499 +3401,136 +3402,252 +3403,42 +3404,183 +3405,403 +3406,206 +3407,35 +3408,458 +3409,173 +3410,120 +3411,327 +3412,49 +3413,10 +3414,313 +3415,332 +3416,164 +3417,370 +3418,156 +3419,120 +3420,209 +3421,474 +3422,380 +3423,376 +3424,156 +3425,413 +3426,405 +3427,324 +3428,388 +3429,393 +3430,287 +3431,106 +3432,49 +3433,287 +3434,31 +3435,432 +3436,304 +3437,133 +3438,72 +3439,408 +3440,287 +3441,67 +3442,373 +3443,400 +3444,442 +3445,217 +3446,456 +3447,101 +3448,262 +3449,154 +3450,61 +3451,485 +3452,274 +3453,258 +3454,261 +3455,490 +3456,96 +3457,74 +3458,405 +3459,331 +3460,9 +3461,451 +3462,217 +3463,62 +3464,426 +3465,444 +3466,282 +3467,488 +3468,215 +3469,265 +3470,408 +3471,5 +3472,404 +3473,64 +3474,487 +3475,114 +3476,311 +3477,162 +3478,311 +3479,63 +3480,347 +3481,296 +3482,414 +3483,311 +3484,38 +3485,413 +3486,409 +3487,257 +3488,374 +3489,308 +3490,290 +3491,414 +3492,31 +3493,310 +3494,37 +3495,449 +3496,89 +3497,109 +3498,167 +3499,494 +3500,227 +3501,288 +3502,318 +3503,456 +3504,26 +3505,440 +3506,276 +3507,28 +3508,377 +3509,174 +3510,188 +3511,475 +3512,410 +3513,340 +3514,299 +3515,401 +3516,18 +3517,93 +3518,30 +3519,464 +3520,389 +3521,186 +3522,441 +3523,493 +3524,110 +3525,121 +3526,367 +3527,166 +3528,303 +3529,17 +3530,303 +3531,341 +3532,252 +3533,25 +3534,121 +3535,391 +3536,363 +3537,377 +3538,256 +3539,19 +3540,212 +3541,115 +3542,265 +3543,424 +3544,376 +3545,6 +3546,183 +3547,371 +3548,244 +3549,63 +3550,298 +3551,322 +3552,19 +3553,64 +3554,70 +3555,450 +3556,254 +3557,283 +3558,423 +3559,301 +3560,16 +3561,177 +3562,241 +3563,8 +3564,150 +3565,489 +3566,246 +3567,156 +3568,261 +3569,14 +3570,156 +3571,409 +3572,374 +3573,116 +3574,465 +3575,472 +3576,268 +3577,160 +3578,434 +3579,349 +3580,8 +3581,460 +3582,114 +3583,296 +3584,214 +3585,246 +3586,84 +3587,410 +3588,452 +3589,103 +3590,34 +3591,356 +3592,360 +3593,448 +3594,98 +3595,129 +3596,473 +3597,79 +3598,85 +3599,116 +3600,73 +3601,371 +3602,50 +3603,303 +3604,107 +3605,4 +3606,324 +3607,437 +3608,128 +3609,142 +3610,412 +3611,358 +3612,69 +3613,233 +3614,66 +3615,404 +3616,483 +3617,434 +3618,236 +3619,211 +3620,54 +3621,464 +3622,352 +3623,29 +3624,295 +3625,119 +3626,129 +3627,142 +3628,291 +3629,478 +3630,95 +3631,389 +3632,418 +3633,111 +3634,43 +3635,53 +3636,62 +3637,172 +3638,343 +3639,321 +3640,231 +3641,193 +3642,72 +3643,122 +3644,311 +3645,374 +3646,129 +3647,105 +3648,229 +3649,485 +3650,107 +3651,178 +3652,193 +3653,235 +3654,347 +3655,187 +3656,22 +3657,485 +3658,417 +3659,427 +3660,310 +3661,113 +3662,268 +3663,34 +3664,304 +3665,48 +3666,179 +3667,56 +3668,235 +3669,145 +3670,423 +3671,260 +3672,283 +3673,281 +3674,414 +3675,229 +3676,73 +3677,491 +3678,182 +3679,224 +3680,270 +3681,227 +3682,265 +3683,318 +3684,497 +3685,376 +3686,160 +3687,426 +3688,142 +3689,324 +3690,47 +3691,272 +3692,417 +3693,62 +3694,271 +3695,449 +3696,96 +3697,220 +3698,48 +3699,164 +3700,374 +3701,383 +3702,254 +3703,269 +3704,433 +3705,25 +3706,259 +3707,187 +3708,152 +3709,465 +3710,362 +3711,211 +3712,296 +3713,86 +3714,468 +3715,404 +3716,177 +3717,174 +3718,129 +3719,439 +3720,250 +3721,381 +3722,79 +3723,362 +3724,4 +3725,168 +3726,80 +3727,28 +3728,481 +3729,41 +3730,167 +3731,203 +3732,174 +3733,149 +3734,50 +3735,357 +3736,24 +3737,140 +3738,470 +3739,98 +3740,487 +3741,242 +3742,20 +3743,133 +3744,293 +3745,421 +3746,104 +3747,207 +3748,488 +3749,84 +3750,40 +3751,228 +3752,249 +3753,139 +3754,339 +3755,210 +3756,459 +3757,212 +3758,452 +3759,74 +3760,472 +3761,474 +3762,466 +3763,149 +3764,195 +3765,60 +3766,11 +3767,130 +3768,215 +3769,441 +3770,140 +3771,182 +3772,455 +3773,427 +3774,21 +3775,337 +3776,320 +3777,291 +3778,497 +3779,498 +3780,289 +3781,474 +3782,323 +3783,268 +3784,286 +3785,98 +3786,175 +3787,426 +3788,137 +3789,473 +3790,370 +3791,138 +3792,295 +3793,426 +3794,452 +3795,257 +3796,96 +3797,355 +3798,98 +3799,61 +3800,177 +3801,232 +3802,210 +3803,398 +3804,405 +3805,432 +3806,51 +3807,25 +3808,28 +3809,109 +3810,325 +3811,475 +3812,157 +3813,468 +3814,411 +3815,46 +3816,223 +3817,244 +3818,95 +3819,142 +3820,125 +3821,279 +3822,401 +3823,47 +3824,1 +3825,284 +3826,479 +3827,91 +3828,360 +3829,183 +3830,266 +3831,489 +3832,177 +3833,307 +3834,194 +3835,225 +3836,195 +3837,326 +3838,50 +3839,341 +3840,450 +3841,216 +3842,86 +3843,263 +3844,80 +3845,364 +3846,85 +3847,190 +3848,356 +3849,132 +3850,460 +3851,266 +3852,108 +3853,61 +3854,383 +3855,18 +3856,118 +3857,113 +3858,107 +3859,114 +3860,497 +3861,383 +3862,287 +3863,85 +3864,138 +3865,234 +3866,203 +3867,151 +3868,232 +3869,394 +3870,221 +3871,328 +3872,77 +3873,183 +3874,27 +3875,481 +3876,152 +3877,335 +3878,468 +3879,349 +3880,367 +3881,350 +3882,176 +3883,338 +3884,141 +3885,221 +3886,434 +3887,480 +3888,360 +3889,165 +3890,495 +3891,409 +3892,71 +3893,6 +3894,367 +3895,110 +3896,161 +3897,497 +3898,117 +3899,406 +3900,97 +3901,90 +3902,298 +3903,211 +3904,342 +3905,383 +3906,259 +3907,408 +3908,303 +3909,418 +3910,394 +3911,307 +3912,168 +3913,17 +3914,318 +3915,344 +3916,387 +3917,231 +3918,110 +3919,126 +3920,10 +3921,162 +3922,389 +3923,75 +3924,333 +3925,469 +3926,292 +3927,10 +3928,13 +3929,198 +3930,122 +3931,298 +3932,133 +3933,146 +3934,24 +3935,4 +3936,471 +3937,67 +3938,108 +3939,143 +3940,373 +3941,23 +3942,377 +3943,333 +3944,480 +3945,269 +3946,275 +3947,328 +3948,413 +3949,328 +3950,494 +3951,254 +3952,91 +3953,444 +3954,258 +3955,489 +3956,258 +3957,226 +3958,401 +3959,191 +3960,432 +3961,341 +3962,296 +3963,146 +3964,219 +3965,431 +3966,401 +3967,439 +3968,327 +3969,240 +3970,474 +3971,119 +3972,229 +3973,123 +3974,142 +3975,171 +3976,203 +3977,71 +3978,332 +3979,28 +3980,246 +3981,2 +3982,274 +3983,473 +3984,443 +3985,444 +3986,96 +3987,95 +3988,362 +3989,479 +3990,402 +3991,68 +3992,100 +3993,156 +3994,251 +3995,110 +3996,247 +3997,171 +3998,25 +3999,366 +4000,149 +4001,28 +4002,205 +4003,147 +4004,395 +4005,346 +4006,239 +4007,30 +4008,480 +4009,235 +4010,80 +4011,385 +4012,118 +4013,173 +4014,467 +4015,8 +4016,95 +4017,266 +4018,165 +4019,11 +4020,267 +4021,114 +4022,17 +4023,409 +4024,278 +4025,132 +4026,76 +4027,486 +4028,257 +4029,32 +4030,238 +4031,302 +4032,329 +4033,242 +4034,281 +4035,229 +4036,267 +4037,11 +4038,312 +4039,399 +4040,201 +4041,332 +4042,415 +4043,348 +4044,339 +4045,430 +4046,307 +4047,78 +4048,217 +4049,345 +4050,322 +4051,185 +4052,294 +4053,387 +4054,326 +4055,173 +4056,308 +4057,123 +4058,262 +4059,68 +4060,383 +4061,87 +4062,404 +4063,252 +4064,220 +4065,289 +4066,18 +4067,179 +4068,486 +4069,146 +4070,491 +4071,124 +4072,300 +4073,495 +4074,169 +4075,489 +4076,184 +4077,334 +4078,168 +4079,174 +4080,433 +4081,363 +4082,375 +4083,33 +4084,77 +4085,355 +4086,16 +4087,412 +4088,190 +4089,400 +4090,222 +4091,467 +4092,325 +4093,496 +4094,27 +4095,266 +4096,73 +4097,126 +4098,322 +4099,386 +4100,283 +4101,367 +4102,329 +4103,279 +4104,161 +4105,57 +4106,423 +4107,452 +4108,410 +4109,200 +4110,208 +4111,235 +4112,449 +4113,320 +4114,355 +4115,361 +4116,155 +4117,60 +4118,483 +4119,19 +4120,324 +4121,210 +4122,256 +4123,210 +4124,400 +4125,474 +4126,172 +4127,488 +4128,329 +4129,203 +4130,46 +4131,266 +4132,363 +4133,140 +4134,201 +4135,140 +4136,176 +4137,22 +4138,49 +4139,304 +4140,407 +4141,212 +4142,240 +4143,108 +4144,495 +4145,86 +4146,161 +4147,262 +4148,435 +4149,42 +4150,143 +4151,2 +4152,187 +4153,72 +4154,402 +4155,271 +4156,220 +4157,288 +4158,400 +4159,206 +4160,366 +4161,46 +4162,444 +4163,358 +4164,195 +4165,37 +4166,235 +4167,248 +4168,142 +4169,203 +4170,143 +4171,177 +4172,160 +4173,396 +4174,203 +4175,485 +4176,272 +4177,317 +4178,153 +4179,375 +4180,256 +4181,50 +4182,342 +4183,385 +4184,416 +4185,39 +4186,360 +4187,211 +4188,403 +4189,9 +4190,247 +4191,443 +4192,237 +4193,485 +4194,371 +4195,324 +4196,267 +4197,178 +4198,424 +4199,172 +4200,457 +4201,236 +4202,116 +4203,90 +4204,228 +4205,482 +4206,389 +4207,82 +4208,11 +4209,325 +4210,415 +4211,66 +4212,23 +4213,498 +4214,308 +4215,275 +4216,221 +4217,178 +4218,332 +4219,261 +4220,387 +4221,380 +4222,44 +4223,367 +4224,164 +4225,258 +4226,483 +4227,122 +4228,366 +4229,386 +4230,456 +4231,305 +4232,477 +4233,219 +4234,410 +4235,119 +4236,25 +4237,296 +4238,313 +4239,135 +4240,432 +4241,179 +4242,185 +4243,173 +4244,81 +4245,226 +4246,294 +4247,444 +4248,8 +4249,14 +4250,121 +4251,404 +4252,457 +4253,166 +4254,150 +4255,254 +4256,134 +4257,490 +4258,217 +4259,269 +4260,317 +4261,465 +4262,19 +4263,293 +4264,100 +4265,423 +4266,33 +4267,377 +4268,33 +4269,130 +4270,478 +4271,44 +4272,374 +4273,120 +4274,325 +4275,298 +4276,289 +4277,217 +4278,396 +4279,37 +4280,271 +4281,472 +4282,110 +4283,158 +4284,161 +4285,96 +4286,442 +4287,402 +4288,24 +4289,195 +4290,27 +4291,496 +4292,404 +4293,245 +4294,4 +4295,313 +4296,13 +4297,79 +4298,336 +4299,438 +4300,114 +4301,314 +4302,192 +4303,16 +4304,257 +4305,261 +4306,397 +4307,370 +4308,36 +4309,473 +4310,255 +4311,20 +4312,279 +4313,294 +4314,397 +4315,57 +4316,349 +4317,95 +4318,470 +4319,111 +4320,240 +4321,21 +4322,330 +4323,336 +4324,3 +4325,456 +4326,254 +4327,155 +4328,380 +4329,9 +4330,319 +4331,153 +4332,78 +4333,267 +4334,244 +4335,251 +4336,332 +4337,329 +4338,476 +4339,383 +4340,55 +4341,240 +4342,153 +4343,355 +4344,279 +4345,228 +4346,303 +4347,123 +4348,165 +4349,312 +4350,483 +4351,353 +4352,491 +4353,9 +4354,463 +4355,217 +4356,262 +4357,24 +4358,295 +4359,56 +4360,267 +4361,29 +4362,362 +4363,196 +4364,306 +4365,488 +4366,363 +4367,287 +4368,317 +4369,41 +4370,200 +4371,486 +4372,428 +4373,43 +4374,9 +4375,285 +4376,18 +4377,450 +4378,413 +4379,121 +4380,162 +4381,338 +4382,6 +4383,269 +4384,6 +4385,16 +4386,168 +4387,499 +4388,213 +4389,157 +4390,374 +4391,147 +4392,217 +4393,65 +4394,489 +4395,198 +4396,204 +4397,135 +4398,314 +4399,181 +4400,47 +4401,95 +4402,203 +4403,473 +4404,42 +4405,39 +4406,372 +4407,72 +4408,185 +4409,154 +4410,94 +4411,341 +4412,434 +4413,341 +4414,268 +4415,241 +4416,379 +4417,135 +4418,293 +4419,373 +4420,279 +4421,321 +4422,274 +4423,164 +4424,288 +4425,108 +4426,475 +4427,461 +4428,362 +4429,461 +4430,422 +4431,50 +4432,235 +4433,332 +4434,81 +4435,194 +4436,80 +4437,111 +4438,283 +4439,140 +4440,157 +4441,91 +4442,488 +4443,194 +4444,466 +4445,8 +4446,288 +4447,238 +4448,285 +4449,293 +4450,403 +4451,164 +4452,498 +4453,45 +4454,361 +4455,270 +4456,90 +4457,108 +4458,132 +4459,16 +4460,114 +4461,378 +4462,123 +4463,358 +4464,285 +4465,436 +4466,314 +4467,47 +4468,456 +4469,80 +4470,294 +4471,50 +4472,339 +4473,14 +4474,214 +4475,486 +4476,39 +4477,217 +4478,87 +4479,200 +4480,236 +4481,183 +4482,231 +4483,416 +4484,459 +4485,39 +4486,351 +4487,426 +4488,125 +4489,444 +4490,348 +4491,324 +4492,169 +4493,218 +4494,375 +4495,16 +4496,357 +4497,419 +4498,475 +4499,36 +4500,320 +4501,204 +4502,48 +4503,120 +4504,118 +4505,176 +4506,327 +4507,353 +4508,3 +4509,164 +4510,495 +4511,454 +4512,377 +4513,470 +4514,162 +4515,150 +4516,19 +4517,237 +4518,276 +4519,454 +4520,150 +4521,324 +4522,385 +4523,333 +4524,355 +4525,18 +4526,359 +4527,19 +4528,185 +4529,89 +4530,415 +4531,178 +4532,35 +4533,430 +4534,212 +4535,372 +4536,339 +4537,14 +4538,303 +4539,246 +4540,68 +4541,324 +4542,61 +4543,393 +4544,481 +4545,108 +4546,68 +4547,475 +4548,483 +4549,19 +4550,14 +4551,46 +4552,406 +4553,130 +4554,403 +4555,296 +4556,340 +4557,405 +4558,236 +4559,187 +4560,407 +4561,401 +4562,448 +4563,129 +4564,202 +4565,248 +4566,405 +4567,76 +4568,426 +4569,293 +4570,165 +4571,200 +4572,225 +4573,283 +4574,264 +4575,125 +4576,30 +4577,149 +4578,290 +4579,180 +4580,386 +4581,183 +4582,381 +4583,466 +4584,251 +4585,51 +4586,412 +4587,364 +4588,133 +4589,463 +4590,144 +4591,426 +4592,424 +4593,294 +4594,351 +4595,52 +4596,271 +4597,19 +4598,120 +4599,244 +4600,173 +4601,140 +4602,20 +4603,441 +4604,487 +4605,113 +4606,465 +4607,228 +4608,85 +4609,90 +4610,228 +4611,222 +4612,48 +4613,421 +4614,375 +4615,127 +4616,498 +4617,215 +4618,34 +4619,335 +4620,91 +4621,282 +4622,7 +4623,105 +4624,3 +4625,188 +4626,373 +4627,486 +4628,240 +4629,244 +4630,370 +4631,241 +4632,130 +4633,94 +4634,393 +4635,54 +4636,453 +4637,173 +4638,222 +4639,324 +4640,140 +4641,8 +4642,3 +4643,423 +4644,129 +4645,148 +4646,128 +4647,93 +4648,58 +4649,414 +4650,198 +4651,475 +4652,445 +4653,157 +4654,438 +4655,145 +4656,362 +4657,353 +4658,248 +4659,282 +4660,5 +4661,358 +4662,416 +4663,183 +4664,122 +4665,33 +4666,327 +4667,10 +4668,10 +4669,382 +4670,321 +4671,262 +4672,21 +4673,229 +4674,33 +4675,171 +4676,333 +4677,184 +4678,123 +4679,188 +4680,487 +4681,299 +4682,204 +4683,130 +4684,16 +4685,3 +4686,296 +4687,99 +4688,252 +4689,219 +4690,399 +4691,3 +4692,167 +4693,164 +4694,372 +4695,388 +4696,374 +4697,220 +4698,90 +4699,125 +4700,337 +4701,261 +4702,32 +4703,378 +4704,399 +4705,328 +4706,189 +4707,61 +4708,77 +4709,321 +4710,463 +4711,471 +4712,104 +4713,135 +4714,118 +4715,123 +4716,236 +4717,494 +4718,270 +4719,469 +4720,241 +4721,211 +4722,197 +4723,419 +4724,6 +4725,321 +4726,334 +4727,44 +4728,394 +4729,116 +4730,416 +4731,366 +4732,184 +4733,494 +4734,211 +4735,4 +4736,291 +4737,219 +4738,7 +4739,358 +4740,123 +4741,346 +4742,63 +4743,10 +4744,215 +4745,466 +4746,469 +4747,396 +4748,194 +4749,141 +4750,279 +4751,195 +4752,326 +4753,417 +4754,462 +4755,140 +4756,264 +4757,393 +4758,183 +4759,291 +4760,347 +4761,309 +4762,481 +4763,12 +4764,91 +4765,455 +4766,282 +4767,182 +4768,19 +4769,408 +4770,244 +4771,183 +4772,386 +4773,235 +4774,191 +4775,440 +4776,354 +4777,266 +4778,478 +4779,112 +4780,147 +4781,336 +4782,328 +4783,409 +4784,351 +4785,132 +4786,238 +4787,452 +4788,299 +4789,190 +4790,297 +4791,7 +4792,261 +4793,189 +4794,92 +4795,241 +4796,154 +4797,15 +4798,344 +4799,423 +4800,263 +4801,185 +4802,55 +4803,254 +4804,31 +4805,203 +4806,130 +4807,101 +4808,343 +4809,359 +4810,65 +4811,405 +4812,134 +4813,413 +4814,134 +4815,357 +4816,327 +4817,175 +4818,60 +4819,80 +4820,343 +4821,351 +4822,397 +4823,368 +4824,400 +4825,17 +4826,102 +4827,149 +4828,364 +4829,405 +4830,110 +4831,302 +4832,124 +4833,400 +4834,225 +4835,154 +4836,175 +4837,300 +4838,125 +4839,272 +4840,83 +4841,436 +4842,439 +4843,226 +4844,363 +4845,33 +4846,352 +4847,251 +4848,160 +4849,158 +4850,85 +4851,494 +4852,247 +4853,281 +4854,411 +4855,218 +4856,148 +4857,412 +4858,25 +4859,373 +4860,97 +4861,283 +4862,210 +4863,209 +4864,192 +4865,403 +4866,244 +4867,328 +4868,161 +4869,189 +4870,12 +4871,477 +4872,198 +4873,191 +4874,334 +4875,314 +4876,155 +4877,492 +4878,56 +4879,165 +4880,137 +4881,415 +4882,12 +4883,321 +4884,312 +4885,87 +4886,396 +4887,342 +4888,242 +4889,273 +4890,326 +4891,207 +4892,352 +4893,269 +4894,475 +4895,66 +4896,143 +4897,163 +4898,285 +4899,127 +4900,56 +4901,277 +4902,403 +4903,478 +4904,125 +4905,346 +4906,56 +4907,13 +4908,40 +4909,210 +4910,372 +4911,238 +4912,158 +4913,185 +4914,396 +4915,29 +4916,42 +4917,35 +4918,185 +4919,498 +4920,410 +4921,79 +4922,75 +4923,70 +4924,144 +4925,451 +4926,220 +4927,153 +4928,172 +4929,234 +4930,322 +4931,88 +4932,142 +4933,433 +4934,437 +4935,80 +4936,487 +4937,320 +4938,140 +4939,104 +4940,344 +4941,168 +4942,81 +4943,443 +4944,369 +4945,27 +4946,412 +4947,336 +4948,154 +4949,354 +4950,412 +4951,318 +4952,206 +4953,20 +4954,392 +4955,237 +4956,491 +4957,226 +4958,245 +4959,449 +4960,388 +4961,426 +4962,341 +4963,398 +4964,207 +4965,461 +4966,320 +4967,223 +4968,305 +4969,204 +4970,218 +4971,419 +4972,299 +4973,337 +4974,438 +4975,19 +4976,219 +4977,377 +4978,148 +4979,188 +4980,463 +4981,280 +4982,493 +4983,67 +4984,156 +4985,69 +4986,324 +4987,377 +4988,289 +4989,341 +4990,25 +4991,386 +4992,250 +4993,395 +4994,262 +4995,70 +4996,14 +4997,366 +4998,420 +4999,153 +5000,464 diff --git a/test/static/executor/test_data/regions.csv b/test/static/executor/test_data/regions.csv new file mode 100644 index 0000000..b9bdb04 --- /dev/null +++ b/test/static/executor/test_data/regions.csv @@ -0,0 +1,11 @@ +id:int +1 +2 +3 +4 +5 +6 +7 +8 +9 +10