Skip to content
Open
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
6 changes: 3 additions & 3 deletions reducer/code/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def execute_query(sql_query, test_script, query_path="query.sql"):
try:
# result = subprocess.run([test_script], env=env, capture_output=True, shell=True)
result = subprocess.run(f"./{test_script}", env=env, capture_output=True, shell=True)
# print("STDOUT:", result.stdout.decode())
# print("STDERR:", result.stderr.decode())
# print("Return code:", result.returncode)
print("STDOUT:", result.stdout.decode())
print("STDERR:", result.stderr.decode())
print("Return code:", result.returncode)

#print("result returncode: ", result)
return result.returncode
Expand Down
9 changes: 7 additions & 2 deletions reducer/code/reduce_query.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from code.parser import SQLParser
from code.executor import execute_query
from code.delta_debugging import delta_debugging
from code.semantic import reduce_where_clause

def reduce_query(query_path, test_script, output_path):
with open(f"{query_path}/original_test.sql", "r") as original_query:
query_string = original_query.readlines()
query_string = original_query.read()


reduced_query = reduce_where_clause(query_string, test_script, output_path)
print("reduced_query ", reduced_query)

# Parse the query to an AST
parser = SQLParser()
token_tree = parser.parse(query_string)
token_tree = parser.parse([reduced_query])
token_tree_size = sum(len(parser.flatten_tokens(tree)) for tree in token_tree)

if not token_tree:
Expand Down
67 changes: 67 additions & 0 deletions reducer/code/semantic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# import sqlparse
# from code.executor import execute_query


# def extract_where_expressions(where_clause):
# return [expr.strip() for expr in where_clause.split("AND")]

# def rebuild_query(base_query, where_exprs):
# if not where_exprs:
# return base_query.split("WHERE")[0].strip() + ";"
# new_where = " AND ".join(where_exprs)
# return base_query.split("WHERE")[0].strip() + f" WHERE {new_where};"

# def reduce_where_clause(query_str, test_script, query_output_path="query.sql"):
# if "WHERE" not in query_str.upper():
# return query_str

# where_start = query_str.upper().find("WHERE")
# base_query = query_str[:where_start]
# where_clause = query_str[where_start + len("WHERE"):].strip().rstrip(";")

# expressions = extract_where_expressions(where_clause)
# i = 0

# while i < len(expressions):
# trial_exprs = expressions[:i] + expressions[i+1:]
# trial_query = rebuild_query(base_query, trial_exprs)

# result_code = execute_query(trial_query, test_script, query_output_path)

# if result_code == 0:
# print(f"[REDUCED] Removed WHERE clause: {expressions[i]}")
# expressions = trial_exprs
# else:
# print(f"[RETAINED] Keeping WHERE clause: {expressions[i]}")
# i += 1

# return rebuild_query(base_query, expressions)
import re
from code.executor import execute_query

def remove_where_clause(query_string: str) -> str:
"""
Removes the WHERE clause from the SQL query using regex (simplified).
Assumes only one WHERE clause exists and is not nested in subqueries.
"""
pattern = re.compile(r"\bWHERE\b.+?(?=(GROUP BY|ORDER BY|LIMIT|;|$))", re.IGNORECASE | re.DOTALL)
return pattern.sub('', query_string)


def reduce_where_clause(query_string: str, test_script: str, query_path: str) -> str:
"""
Try to semantically reduce the WHERE clause. Only keep the reduction
if the test script returns exit code 0 (bug still happens).
"""
reduced_query = remove_where_clause(query_string)

# Test the reduced query
result = execute_query(reduced_query, test_script, query_path)

if result == 0:
print("[✔] Removed WHERE clause — bug still happens.")
return reduced_query
else:
print("[✘] Removing WHERE clause changed behavior — keeping original.")
return query_string

1 change: 0 additions & 1 deletion reducer/code/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import shutil
import os
import sqlparse

def prepare_workspace(query_path):
src = os.path.abspath(query_path)
Expand Down
1 change: 0 additions & 1 deletion reducer/reducer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from code.reduce_query import reduce_query
from code.utils import prepare_workspace
import time
from code.utils import count_tokens

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Reduce bug-triggering SQL queries")
Expand Down
2 changes: 2 additions & 0 deletions reducer/test.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CREATE TABLE F (p BOOLEAN NOT NULL NULL NOT NULL, i BOOLEAN);
INSERT INTO F SELECT * FROM (VALUES ((NOT false), false), (NULL, (NOT (NOT true)))) AS L;