Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__/
*.py[cod]
.venv/
.env
Binary file removed __pycache__/codegen.cpython-313.pyc
Binary file not shown.
Binary file removed __pycache__/phi_parser.cpython-312.pyc
Binary file not shown.
Binary file removed __pycache__/phi_parser.cpython-313.pyc
Binary file not shown.
4 changes: 1 addition & 3 deletions codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,6 @@ def _emit_filter(spec: PhiSpec) -> str:
or_clauses.append("" + " and ".join(and_group) + "")

condition = " or ".join(or_clauses)
if len(condition.strip()) == 0:
condition = True

lines.append(f" if ({condition}):")
lines.append(" filtered_mf_struct[_key] = entry")
Expand Down Expand Up @@ -234,4 +232,4 @@ def main() -> None:


if __name__ == "__main__":
main()
main()
28 changes: 14 additions & 14 deletions phi_input_case2.txt
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Case 2: Count and Max quant per customer
"""
SELECT
cust,
COUNT(*),
MAX(quant)
FROM sales
WHERE quant IS NOT NULL
GROUP BY cust;
select state, month, a_sum_quant, b_avg_quant
from sales
where year = 2018
group by state, month; a, b
such that a_state = state and a_month = month
and b_state = state and b_month = month
having a_sum_quant >= 3 * b_avg_quant
"""
S: cust, 1_count_*, 1_max_quant
n: 1
V: cust
F: 1_count_*, 1_max_quant

S: state, month, 1_sum_quant, 2_avg_quant
n: 2
V: state, month
F: 1_sum_quant, 2_avg_quant
sigma:
1: (cust == g_cust) and (quant is not None)
G:
1: state == g_state and month == g_month and year == 2018
2: state == g_state and month == g_month and year == 2018
G: 1_sum_quant >= 3 * 2_avg_quant
33 changes: 7 additions & 26 deletions phi_input_case3.txt
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
"""
WITH a AS (
SELECT
state,
month,
SUM(quant) AS a_sum_quant
FROM sales
WHERE state = 'CA'
GROUP BY state, month
),
b AS (
SELECT
state,
MAX(quant) AS b_max_quant
FROM sales
GROUP BY state
)
SELECT
a.state,
a.month,
a.a_sum_quant,
b.b_max_quant
FROM a
JOIN b
ON a.state = b.state
WHERE a.a_sum_quant >= b.b_max_quant * 2;

select state, month, a_sum_quant, b_max_quant
from sales
where state = 'CA'
group by state,month; a,b
such that a_state = state and a_month = month
and b_state = state
having a_sum_quant >= b_max_quant * 2
"""
S: state, month, 1_sum_quant, 2_max_quant
n: 2
Expand Down
6 changes: 3 additions & 3 deletions phi_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ def parse_phi_file(path: str) -> PhiSpec:
grouping_attrs = _split_list(V)

aggs: List[AggSpec] = []
having: List[List[str]] = []
if F:
for item in _split_list(F):
m = _AGG_RE.match(item)
Expand Down Expand Up @@ -152,6 +151,8 @@ def parse_phi_file(path: str) -> PhiSpec:
# Split OR (case-insensitive)
or_blocks = re.split(r'\s+OR\s+', G, flags=re.IGNORECASE)

having: List[List[str]] = []

for block in or_blocks:
and_parts = re.split(r'\s+AND\s+', block, flags=re.IGNORECASE)
rewrite = []
Expand Down Expand Up @@ -197,5 +198,4 @@ def _split_list(s: str) -> List[str]:


def _first_token(s: str) -> str:
return s.strip().split()[0]

return s.strip().split()[0]
97 changes: 97 additions & 0 deletions qpe_case1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from __future__ import annotations
import os
import psycopg2
import psycopg2.extras
from dotenv import load_dotenv

# Auto-generated by codegen.py. Re-generate anytime.

def _safe_eval_predicate(expr: str, env: dict) -> bool:
"""
English comments:
Evaluate a predicate expression in a restricted environment.
- Variables are taken from the given env dict.
- Supports Python boolean logic (and/or/not).
"""
if not expr:
return True
return bool(eval(expr, {"__builtins__": {}}, env))


def run_query():
try:
load_dotenv()
user = os.getenv("USER")
password = os.getenv("PASSWORD")
dbname = os.getenv("DBNAME")
host = os.getenv("HOST", "localhost")
port = os.getenv("PORT", "5432")

conn = psycopg2.connect(
dbname=dbname,
user=user,
password=password,
host=host,
port=port,
cursor_factory=psycopg2.extras.DictCursor
)
cur = conn.cursor()

except:
raise RuntimeError("Incorrect USER/PASSWORD/DBNAME in .env")

# mf_struct maps grouping key tuple -> entry dict
mf_struct = {}

# SCAN 0: initialize mf_struct entries for distinct grouping keys
cur.execute('SELECT * FROM sales')
for row in cur:
key = (row['prod'], row['month'])
if key not in mf_struct:
entry = {}
entry['prod'] = row['prod']
entry['month'] = row['month']
entry['1_sum_quant'] = 0
entry['2_sum_quant'] = 0
mf_struct[key] = entry

# SCAN 1: compute aggregates for grouping variable 1
cur.execute('SELECT * FROM sales')
for row in cur:
for _key, entry in mf_struct.items():
env = dict(row)
env['g_prod'] = entry.get('prod')
env['g_month'] = entry.get('month')
if not _safe_eval_predicate('prod == g_prod and month == g_month', env):
continue
entry['1_sum_quant'] += (row['quant'] if row['quant'] is not None else 0)

# SCAN 2: compute aggregates for grouping variable 2
cur.execute('SELECT * FROM sales')
for row in cur:
for _key, entry in mf_struct.items():
env = dict(row)
env['g_prod'] = entry.get('prod')
env['g_month'] = entry.get('month')
if not _safe_eval_predicate('prod == g_prod', env):
continue
entry['2_sum_quant'] += (row['quant'] if row['quant'] is not None else 0)

filtered_mf_struct = {}
for _key, entry in mf_struct.items():
if (entry['1_sum_quant'] > 0.9*entry['2_sum_quant']/10 and (entry['1_sum_quant'] > 40000 or entry['2_sum_quant']<500000) and entry['month'] == 11):
filtered_mf_struct[_key] = entry

# Output
out_cols = ['prod', 'month', '1_sum_quant', '2_sum_quant']
print("\t".join(out_cols))
for _key, entry in filtered_mf_struct.items():
row_out = [str(entry.get(c, "")) for c in out_cols]
print("\t".join(row_out))

cur.close()
conn.close()


if __name__ == "__main__":
run_query()
103 changes: 103 additions & 0 deletions qpe_case2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from __future__ import annotations
import os
import psycopg2
import psycopg2.extras
from dotenv import load_dotenv

# Auto-generated by codegen.py. Re-generate anytime.

def _safe_eval_predicate(expr: str, env: dict) -> bool:
"""
English comments:
Evaluate a predicate expression in a restricted environment.
- Variables are taken from the given env dict.
- Supports Python boolean logic (and/or/not).
"""
if not expr:
return True
return bool(eval(expr, {"__builtins__": {}}, env))


def run_query():
try:
load_dotenv()
user = os.getenv("USER")
password = os.getenv("PASSWORD")
dbname = os.getenv("DBNAME")
host = os.getenv("HOST", "localhost")
port = os.getenv("PORT", "5432")

conn = psycopg2.connect(
dbname=dbname,
user=user,
password=password,
host=host,
port=port,
cursor_factory=psycopg2.extras.DictCursor
)
cur = conn.cursor()

except:
raise RuntimeError("Incorrect USER/PASSWORD/DBNAME in .env")

# mf_struct maps grouping key tuple -> entry dict
mf_struct = {}

# SCAN 0: initialize mf_struct entries for distinct grouping keys
cur.execute('SELECT * FROM sales')
for row in cur:
key = (row['state'], row['month'])
if key not in mf_struct:
entry = {}
entry['state'] = row['state']
entry['month'] = row['month']
entry['1_sum_quant'] = 0
entry['2_avg_quant__sum'] = 0
entry['2_avg_quant__count'] = 0
entry['2_avg_quant'] = 0
mf_struct[key] = entry

# SCAN 1: compute aggregates for grouping variable 1
cur.execute('SELECT * FROM sales')
for row in cur:
for _key, entry in mf_struct.items():
env = dict(row)
env['g_state'] = entry.get('state')
env['g_month'] = entry.get('month')
if not _safe_eval_predicate('state == g_state and month == g_month and year == 2018', env):
continue
entry['1_sum_quant'] += (row['quant'] if row['quant'] is not None else 0)

# SCAN 2: compute aggregates for grouping variable 2
cur.execute('SELECT * FROM sales')
for row in cur:
for _key, entry in mf_struct.items():
env = dict(row)
env['g_state'] = entry.get('state')
env['g_month'] = entry.get('month')
if not _safe_eval_predicate('state == g_state and month == g_month and year == 2018', env):
continue
val = row['quant']
if val is not None:
entry['2_avg_quant__sum'] += val
entry['2_avg_quant__count'] += 1
entry['2_avg_quant'] = entry['2_avg_quant__sum'] / entry['2_avg_quant__count']

filtered_mf_struct = {}
for _key, entry in mf_struct.items():
if (entry['1_sum_quant'] >= 3 * entry['2_avg_quant']):
filtered_mf_struct[_key] = entry

# Output
out_cols = ['state', 'month', '1_sum_quant', '2_avg_quant']
print("\t".join(out_cols))
for _key, entry in filtered_mf_struct.items():
row_out = [str(entry.get(c, "")) for c in out_cols]
print("\t".join(row_out))

cur.close()
conn.close()


if __name__ == "__main__":
run_query()
Loading