diff --git a/dbms_test.go b/dbms_test.go index 735de09..2ab1516 100644 --- a/dbms_test.go +++ b/dbms_test.go @@ -35,6 +35,7 @@ type testcase struct { func TestQueriesPerDBMS(t *testing.T) { dbmsTypes := []DBMSType{ DBMSPostgres, + DBMSOracle, } for _, dbms := range dbmsTypes { diff --git a/sqllexer_utils.go b/sqllexer_utils.go index 223ed48..9382cd1 100644 --- a/sqllexer_utils.go +++ b/sqllexer_utils.go @@ -137,6 +137,7 @@ var keywords = map[string]bool{ "RETURNING": true, "OFFSET": true, "OF": true, + "SKIP": true, } func isWhitespace(ch rune) bool { diff --git a/testdata/oracle/complex/bulk-operations.json b/testdata/oracle/complex/bulk-operations.json new file mode 100644 index 0000000..ea1e629 --- /dev/null +++ b/testdata/oracle/complex/bulk-operations.json @@ -0,0 +1,27 @@ +{ + "input": "DECLARE TYPE EmpTabTyp IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER; emp_tab EmpTabTyp; BEGIN SELECT * BULK COLLECT INTO emp_tab FROM employees; FORALL i IN emp_tab.FIRST .. emp_tab.LAST SAVE EXCEPTIONS UPDATE employees SET test = test * 1.05 WHERE employee_id = emp_tab(i).employee_id; END;", + "outputs": [ + { + "expected": "DECLARE TYPE EmpTabTyp IS TABLE OF employees % ROWTYPE INDEX BY PLS_INTEGER; emp_tab EmpTabTyp; BEGIN SELECT * BULK COLLECT INTO emp_tab FROM employees; FORALL i IN emp_tab.FIRST . . emp_tab.LAST SAVE EXCEPTIONS UPDATE employees SET test = test * ? WHERE employee_id = emp_tab(i) . employee_id; END;", + "statement_metadata": { + "size": 33, + "tables": ["emp_tab", "employees"], + "commands": ["BEGIN", "SELECT", "UPDATE"], + "comments": [], + "procedures": [] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/complex/complex-multi-table-delete.json b/testdata/oracle/complex/complex-multi-table-delete.json new file mode 100644 index 0000000..234ce92 --- /dev/null +++ b/testdata/oracle/complex/complex-multi-table-delete.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM orders o WHERE o.customer_id IN (SELECT c.id FROM customers c WHERE NOT EXISTS (SELECT 1 FROM customer_orders co WHERE co.customer_id = c.id AND co.order_date > SYSDATE - 365)) AND EXISTS (SELECT 1 FROM order_items oi WHERE oi.order_id = o.id AND oi.product_id IN (SELECT p.id FROM products p WHERE p.category = 'Obsolete'));", + "outputs": [ + { + "expected": "DELETE FROM orders o WHERE o.customer_id IN ( SELECT c.id FROM customers c WHERE NOT EXISTS ( SELECT ? FROM customer_orders co WHERE co.customer_id = c.id AND co.order_date > SYSDATE - ? ) ) AND EXISTS ( SELECT ? FROM order_items oi WHERE oi.order_id = o.id AND oi.product_id IN ( SELECT p.id FROM products p WHERE p.category = ? ) )", + "statement_metadata": { + "size": 61, + "tables": ["orders", "customers", "customer_orders", "order_items", "products"], + "commands": ["DELETE", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/complex/complex-nested-subqueries.json b/testdata/oracle/complex/complex-nested-subqueries.json new file mode 100644 index 0000000..8ef8b45 --- /dev/null +++ b/testdata/oracle/complex/complex-nested-subqueries.json @@ -0,0 +1,16 @@ +{ + "input": "SELECT e.employee_id, (SELECT MAX(s.yoe) FROM employees s WHERE s.department_id = e.department_id) AS max_dept_yoe FROM employees e WHERE EXISTS (SELECT 1 FROM departments d WHERE d.id = e.department_id AND d.budget > (SELECT AVG(budget) FROM departments)) ORDER BY e.department_id, e.employee_id;", + "outputs": [ + { + "expected": "SELECT e.employee_id, ( SELECT MAX ( s.yoe ) FROM employees s WHERE s.department_id = e.department_id ) FROM employees e WHERE EXISTS ( SELECT ? FROM departments d WHERE d.id = e.department_id AND d.budget > ( SELECT AVG ( budget ) FROM departments ) ) ORDER BY e.department_id, e.employee_id", + "statement_metadata": { + "size": 26, + "tables": ["employees", "departments"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/complex/complex-select-aggregates-joins.json b/testdata/oracle/complex/complex-select-aggregates-joins.json new file mode 100644 index 0000000..c3375c0 --- /dev/null +++ b/testdata/oracle/complex/complex-select-aggregates-joins.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT u.id, u.name, COUNT(o.id) AS order_count, AVG(o.total) AS average_order FROM users u JOIN orders o ON u.id = o.user_id WHERE u.status = 'active' GROUP BY u.id, u.name HAVING COUNT(o.id) > 5;", + "outputs": [ + { + "expected": "SELECT u.id, u.name, COUNT ( o.id ), AVG ( o.total ) FROM users u JOIN orders o ON u.id = o.user_id WHERE u.status = ? GROUP BY u.id, u.name HAVING COUNT ( o.id ) > ?", + "statement_metadata": { + "size": 21, + "tables": ["users", "orders"], + "commands": ["SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT u.id, u.name, COUNT(o.id), AVG(o.total) FROM users u JOIN orders o ON u.id = o.user_id WHERE u.status = ? GROUP BY u.id, u.name HAVING COUNT(o.id) > ?;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/complex/extremely-complex-oracle-query.json b/testdata/oracle/complex/extremely-complex-oracle-query.json new file mode 100644 index 0000000..3c178aa --- /dev/null +++ b/testdata/oracle/complex/extremely-complex-oracle-query.json @@ -0,0 +1,16 @@ +{ + "input": "WITH RECURSIVE sales_cte (product_id, total_sales, sales_rank) AS (SELECT product_id, SUM(amount), RANK() OVER (ORDER BY SUM(amount) DESC) FROM sales GROUP BY product_id UNION ALL SELECT s.product_id, s.total_sales, s.sales_rank FROM sales s JOIN sales_cte sc ON s.product_id = sc.product_id WHERE s.amount > 1000), complex_view AS (SELECT e.employee_id, e.department_id, e.test_amt, AVG(e.test_amt) OVER (PARTITION BY e.department_id) AS avg_dept_test_amt, d.department_name, d.manager_id, (SELECT MAX(p.price) FROM products p WHERE p.department_id = e.department_id) AS max_product_price FROM employees e JOIN departments d ON e.department_id = d.id WHERE e.hire_date > SYSDATE - INTERVAL '10' YEAR) SELECT cv.*, sc.total_sales, sc.sales_rank FROM complex_view cv LEFT JOIN sales_cte sc ON cv.department_id = sc.product_id WHERE cv.avg_dept_test_amt > (SELECT AVG(total_sal) FROM (SELECT department_id, SUM(test_amt) AS total_sal FROM employees GROUP BY department_id)) AND EXISTS (SELECT 1 FROM customer_orders co WHERE co.employee_id = cv.employee_id AND co.order_status = 'Completed') ORDER BY cv.department_id, cv.test_amt DESC;", + "outputs": [ + { + "expected": "WITH RECURSIVE sales_cte ( product_id, total_sales, sales_rank ) AS ( SELECT product_id, SUM ( amount ), RANK ( ) OVER ( ORDER BY SUM ( amount ) DESC ) FROM sales GROUP BY product_id UNION ALL SELECT s.product_id, s.total_sales, s.sales_rank FROM sales s JOIN sales_cte sc ON s.product_id = sc.product_id WHERE s.amount > ? ), complex_view AS ( SELECT e.employee_id, e.department_id, e.test_amt, AVG ( e.test_amt ) OVER ( PARTITION BY e.department_id ), d.department_name, d.manager_id, ( SELECT MAX ( p.price ) FROM products p WHERE p.department_id = e.department_id ) FROM employees e JOIN departments d ON e.department_id = d.id WHERE e.hire_date > SYSDATE - INTERVAL ? YEAR ) SELECT cv. *, sc.total_sales, sc.sales_rank FROM complex_view cv LEFT JOIN sales_cte sc ON cv.department_id = sc.product_id WHERE cv.avg_dept_test_amt > ( SELECT AVG ( total_sal ) FROM ( SELECT department_id, SUM ( test_amt ) FROM employees GROUP BY department_id ) ) AND EXISTS ( SELECT ? FROM customer_orders co WHERE co.employee_id = cv.employee_id AND co.order_status = ? ) ORDER BY cv.department_id, cv.test_amt DESC", + "statement_metadata": { + "size": 79, + "tables": ["sales", "sales_cte", "products", "employees", "departments", "complex_view", "customer_orders"], + "commands": ["SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/complex/extremely-complex-stored-procedure.json b/testdata/oracle/complex/extremely-complex-stored-procedure.json new file mode 100644 index 0000000..7a4f29f --- /dev/null +++ b/testdata/oracle/complex/extremely-complex-stored-procedure.json @@ -0,0 +1,16 @@ +{ + "input": "CREATE OR REPLACE PROCEDURE /* test comments \n\tsecond line \n*/ complex_data_audit AS CURSOR emp_cursor IS SELECT employee_id FROM employees; /* another comment */ v_employee_id employees.employee_id%TYPE; BEGIN FOR emp_record IN emp_cursor LOOP v_employee_id := emp_record.employee_id; INSERT INTO audit_log (message) VALUES ('Auditing employee with ID: ' || v_employee_id); FOR c IN (SELECT * FROM customer_orders WHERE employee_id = v_employee_id) LOOP IF c.order_status = 'Pending' THEN UPDATE customer_orders SET order_status = 'Under Review' WHERE order_id = c.order_id; ELSE INSERT INTO audit_log (message) VALUES ('Order ' || c.order_id || ' already processed'); END IF; END LOOP; END LOOP; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20002, 'Error in complex_data_audit'); END complex_data_audit;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PROCEDURE complex_data_audit emp_cursor IS SELECT employee_id FROM employees; v_employee_id employees.employee_id % TYPE; BEGIN FOR emp_record IN emp_cursor LOOP v_employee_id := emp_record.employee_id; INSERT INTO audit_log ( message ) VALUES ( ? || v_employee_id ); FOR c IN ( SELECT * FROM customer_orders WHERE employee_id = v_employee_id ) LOOP IF c.order_status = ? THEN UPDATE customer_orders SET order_status = ? WHERE order_id = c.order_id; ELSE INSERT INTO audit_log ( message ) VALUES ( ? || c.order_id || ? ); END IF; END LOOP; END LOOP; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR ( ? ); END complex_data_audit", + "statement_metadata": { + "size": 135, + "tables": ["employees", "audit_log", "customer_orders"], + "commands": ["CREATE", "SELECT", "BEGIN", "INSERT", "UPDATE"], + "comments": ["/* test comments \n\tsecond line \n*/", "/* another comment */"], + "procedures": ["complex_data_audit"] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/complex/plsql-blocks.json b/testdata/oracle/complex/plsql-blocks.json new file mode 100644 index 0000000..9e09802 --- /dev/null +++ b/testdata/oracle/complex/plsql-blocks.json @@ -0,0 +1,16 @@ +{ + "input": "DECLARE x NUMBER; BEGIN SELECT COUNT(*) INTO x FROM employees; DBMS_OUTPUT.PUT_LINE('Count: ' || x); END;", + "outputs": [ + { + "expected": "DECLARE x NUMBER; BEGIN SELECT COUNT ( * ) INTO x FROM employees; DBMS_OUTPUT.PUT_LINE ( ? || x ); END", + "statement_metadata": { + "size": 21, + "tables": ["x", "employees"], + "commands": ["BEGIN", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/complex/super-complex-oracle-query.json b/testdata/oracle/complex/super-complex-oracle-query.json new file mode 100644 index 0000000..c1564cc --- /dev/null +++ b/testdata/oracle/complex/super-complex-oracle-query.json @@ -0,0 +1,16 @@ +{ + "input": "WITH ranked_sales AS (SELECT product_id, SUM(amount) AS total_sales, RANK() OVER (ORDER BY SUM(amount) DESC) sales_rank FROM sales GROUP BY product_id), dept_costs AS (SELECT department_id, SUM(test_amt) AS total_sal FROM employees GROUP BY department_id), latest_transactions AS (SELECT t.account_id, t.amount, ROW_NUMBER() OVER (PARTITION BY t.account_id ORDER BY t.transaction_date DESC) rn FROM transactions t WHERE t.transaction_date >= ADD_MONTHS(SYSDATE, -6)) SELECT e.employee_id, e.last_name, e.test_amt, d.department_name, d.location_id, rs.total_sales, rs.sales_rank, lt.amount AS latest_transaction_amount FROM employees e INNER JOIN departments d ON e.department_id = d.id LEFT JOIN ranked_sales rs ON e.product_id = rs.product_id LEFT JOIN latest_transactions lt ON e.account_id = lt.account_id AND lt.rn = 1 WHERE e.hire_date > '2010-01-01' AND (d.budget > (SELECT AVG(total_sal) FROM dept_costs) OR e.test_amt > (SELECT AVG(test_amt) FROM employees WHERE department_id = e.department_id)) AND EXISTS (SELECT 1 FROM customer_orders co WHERE co.employee_id = e.employee_id AND co.order_status = 'Completed') ORDER BY e.department_id, e.test_amt DESC;", + "outputs": [ + { + "expected": "WITH ranked_sales AS ( SELECT product_id, SUM ( amount ), RANK ( ) OVER ( ORDER BY SUM ( amount ) DESC ) sales_rank FROM sales GROUP BY product_id ), dept_costs AS ( SELECT department_id, SUM ( test_amt ) FROM employees GROUP BY department_id ), latest_transactions AS ( SELECT t.account_id, t.amount, ROW_NUMBER ( ) OVER ( PARTITION BY t.account_id ORDER BY t.transaction_date DESC ) rn FROM transactions t WHERE t.transaction_date >= ADD_MONTHS ( SYSDATE, ? ) ) SELECT e.employee_id, e.last_name, e.test_amt, d.department_name, d.location_id, rs.total_sales, rs.sales_rank, lt.amount FROM employees e INNER JOIN departments d ON e.department_id = d.id LEFT JOIN ranked_sales rs ON e.product_id = rs.product_id LEFT JOIN latest_transactions lt ON e.account_id = lt.account_id AND lt.rn = ? WHERE e.hire_date > ? AND ( d.budget > ( SELECT AVG ( total_sal ) FROM dept_costs ) OR e.test_amt > ( SELECT AVG ( test_amt ) FROM employees WHERE department_id = e.department_id ) ) AND EXISTS ( SELECT ? FROM customer_orders co WHERE co.employee_id = e.employee_id AND co.order_status = ? ) ORDER BY e.department_id, e.test_amt DESC", + "statement_metadata": { + "size": 103, + "tables": ["sales", "employees", "transactions", "departments", "ranked_sales", "latest_transactions", "dept_costs", "customer_orders"], + "commands": ["SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/conditional-delete-with-case.json b/testdata/oracle/delete/conditional-delete-with-case.json new file mode 100644 index 0000000..a9f5404 --- /dev/null +++ b/testdata/oracle/delete/conditional-delete-with-case.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM user_notifications WHERE id IN (SELECT id FROM notifications WHERE recipient_id = 123 AND status = CASE WHEN urgency = 'High' THEN 'Unread' ELSE 'Read' END);", + "outputs": [ + { + "expected": "DELETE FROM user_notifications WHERE id IN ( SELECT id FROM notifications WHERE recipient_id = ? AND status = CASE WHEN urgency = ? THEN ? ELSE ? END )", + "statement_metadata": { + "size": 43, + "tables": ["user_notifications", "notifications"], + "commands": ["DELETE", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-basic.json b/testdata/oracle/delete/delete-basic.json new file mode 100644 index 0000000..ab3115c --- /dev/null +++ b/testdata/oracle/delete/delete-basic.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM customers WHERE last_purchase_date < ADD_MONTHS(SYSDATE, -12);", + "outputs": [ + { + "expected": "DELETE FROM customers WHERE last_purchase_date < ADD_MONTHS ( SYSDATE, ? )", + "statement_metadata": { + "size": 15, + "tables": ["customers"], + "commands": ["DELETE"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-cascade.json b/testdata/oracle/delete/delete-cascade.json new file mode 100644 index 0000000..a0b7a7b --- /dev/null +++ b/testdata/oracle/delete/delete-cascade.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM orders WHERE customer_id = 456 CASCADE;", + "outputs": [ + { + "expected": "DELETE FROM orders WHERE customer_id = ? CASCADE", + "statement_metadata": { + "size": 12, + "tables": ["orders"], + "commands": ["DELETE"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-using-rowid.json b/testdata/oracle/delete/delete-using-rowid.json new file mode 100644 index 0000000..90ab26d --- /dev/null +++ b/testdata/oracle/delete/delete-using-rowid.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM employees WHERE rowid = (SELECT max(rowid) FROM employees WHERE department_id = 20);", + "outputs": [ + { + "expected": "DELETE FROM employees WHERE rowid = ( SELECT max ( rowid ) FROM employees WHERE department_id = ? )", + "statement_metadata": { + "size": 21, + "tables": ["employees"], + "commands": ["DELETE", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-where-current-of.json b/testdata/oracle/delete/delete-where-current-of.json new file mode 100644 index 0000000..4752fe7 --- /dev/null +++ b/testdata/oracle/delete/delete-where-current-of.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM employees WHERE CURRENT OF emp_cursor;", + "outputs": [ + { + "expected": "DELETE FROM employees WHERE CURRENT OF emp_cursor", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["DELETE"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-with-complex-subqueries.json b/testdata/oracle/delete/delete-with-complex-subqueries.json new file mode 100644 index 0000000..c6fc3ee --- /dev/null +++ b/testdata/oracle/delete/delete-with-complex-subqueries.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM products WHERE id IN (SELECT p.id FROM products p JOIN inventory i ON p.id = i.product_id WHERE i.quantity = 0);", + "outputs": [ + { + "expected": "DELETE FROM products WHERE id IN ( SELECT p.id FROM products p JOIN inventory i ON p.id = i.product_id WHERE i.quantity = ? )", + "statement_metadata": { + "size": 33, + "tables": ["products", "inventory"], + "commands": ["DELETE", "SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-with-flashback-query.json b/testdata/oracle/delete/delete-with-flashback-query.json new file mode 100644 index 0000000..7083c47 --- /dev/null +++ b/testdata/oracle/delete/delete-with-flashback-query.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM orders AS OF TIMESTAMP TO_TIMESTAMP('2023-03-15 08:30:00', 'YYYY-MM-DD HH24:MI:SS') WHERE order_date < '2023-01-01';", + "outputs": [ + { + "expected": "DELETE FROM orders AS OF TIMESTAMP TO_TIMESTAMP ( ? ) WHERE order_date < ?", + "statement_metadata": { + "size": 12, + "tables": ["orders"], + "commands": ["DELETE"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-with-join-syntax.json b/testdata/oracle/delete/delete-with-join-syntax.json new file mode 100644 index 0000000..d3ec876 --- /dev/null +++ b/testdata/oracle/delete/delete-with-join-syntax.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM orders o WHERE EXISTS (SELECT 1 FROM customers c WHERE o.customer_id = c.id AND c.status = 'Inactive');", + "outputs": [ + { + "expected": "DELETE FROM orders o WHERE EXISTS ( SELECT ? FROM customers c WHERE o.customer_id = c.id AND c.status = ? )", + "statement_metadata": { + "size": 27, + "tables": ["orders", "customers"], + "commands": ["DELETE", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-with-pseudocolumns.json b/testdata/oracle/delete/delete-with-pseudocolumns.json new file mode 100644 index 0000000..b1ab1b2 --- /dev/null +++ b/testdata/oracle/delete/delete-with-pseudocolumns.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM session_logs WHERE ROWNUM <= 10;", + "outputs": [ + { + "expected": "DELETE FROM session_logs WHERE ROWNUM <= ?", + "statement_metadata": { + "size": 18, + "tables": ["session_logs"], + "commands": ["DELETE"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-with-returning-clause.json b/testdata/oracle/delete/delete-with-returning-clause.json new file mode 100644 index 0000000..95be2ac --- /dev/null +++ b/testdata/oracle/delete/delete-with-returning-clause.json @@ -0,0 +1,16 @@ +{ + "input": "DELETE FROM logs WHERE entry_date < SYSDATE RETURNING id INTO :deleted_ids;", + "outputs": [ + { + "expected": "DELETE FROM logs WHERE entry_date < SYSDATE RETURNING id INTO :deleted_ids", + "statement_metadata": { + "size": 10, + "tables": ["logs"], + "commands": ["DELETE"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/delete/delete-with-subquery.json b/testdata/oracle/delete/delete-with-subquery.json new file mode 100644 index 0000000..7018cda --- /dev/null +++ b/testdata/oracle/delete/delete-with-subquery.json @@ -0,0 +1,23 @@ +{ + "input": "DELETE FROM logs WHERE entry_date < (SELECT MIN(order_date) FROM orders);", + "outputs": [ + { + "expected": "DELETE FROM logs WHERE entry_date < ( SELECT MIN ( order_date ) FROM orders )", + "statement_metadata": { + "size": 22, + "tables": ["logs", "orders"], + "commands": ["DELETE", "SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "DELETE FROM logs WHERE entry_date < (SELECT MIN(order_date) FROM orders);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-all-into-multiple-tables.json b/testdata/oracle/insert/insert-all-into-multiple-tables.json new file mode 100644 index 0000000..b7c0044 --- /dev/null +++ b/testdata/oracle/insert/insert-all-into-multiple-tables.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT ALL INTO sales (product_id, amount) VALUES (product_id, amount) INTO audit_log (action_type, message) VALUES ('INSERT', 'Inserted into sales') SELECT product_id, amount FROM temp_sales WHERE amount > 1000;", + "outputs": [ + { + "expected": "INSERT ALL INTO sales ( product_id, amount ) VALUES ( product_id, amount ) INTO audit_log ( action_type, message ) VALUES ( ? ) SELECT product_id, amount FROM temp_sales WHERE amount > ?", + "statement_metadata": { + "size": 36, + "tables": ["sales", "audit_log", "temp_sales"], + "commands": ["INSERT", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-all-multiple-conditions.json b/testdata/oracle/insert/insert-all-multiple-conditions.json new file mode 100644 index 0000000..e16049f --- /dev/null +++ b/testdata/oracle/insert/insert-all-multiple-conditions.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT ALL WHEN amount <= 100 THEN INTO small_orders (order_id, amount) VALUES (order_id, amount) WHEN amount BETWEEN 101 AND 500 THEN INTO medium_orders (order_id, amount) VALUES (order_id, amount) ELSE INTO large_orders (order_id, amount) VALUES (order_id, amount) SELECT order_id, amount FROM orders;", + "outputs": [ + { + "expected": "INSERT ALL WHEN amount <= ? THEN INTO small_orders ( order_id, amount ) VALUES ( order_id, amount ) WHEN amount BETWEEN ? AND ? THEN INTO medium_orders ( order_id, amount ) VALUES ( order_id, amount ) ELSE INTO large_orders ( order_id, amount ) VALUES ( order_id, amount ) SELECT order_id, amount FROM orders", + "statement_metadata": { + "size": 55, + "tables": ["small_orders", "medium_orders", "large_orders", "orders"], + "commands": ["INSERT", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-basic.json b/testdata/oracle/insert/insert-basic.json new file mode 100644 index 0000000..96a9f57 --- /dev/null +++ b/testdata/oracle/insert/insert-basic.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT INTO customers (id, name, address) VALUES (101, 'John Doe', '123 Oracle Ln');", + "outputs": [ + { + "expected": "INSERT INTO customers ( id, name, address ) VALUES ( ? )", + "statement_metadata": { + "size": 15, + "tables": ["customers"], + "commands": ["INSERT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-using-decode.json b/testdata/oracle/insert/insert-using-decode.json new file mode 100644 index 0000000..21c1404 --- /dev/null +++ b/testdata/oracle/insert/insert-using-decode.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT INTO user_log (user_id, action, log_date) SELECT user_id, DECODE(activity_type, 'LOGIN', 'Logged In', 'LOGOUT', 'Logged Out', 'Unknown'), SYSDATE FROM user_activity;", + "outputs": [ + { + "expected": "INSERT INTO user_log ( user_id, action, log_date ) SELECT user_id, DECODE ( activity_type, ?, ?, ?, ?, ? ), SYSDATE FROM user_activity", + "statement_metadata": { + "size": 33, + "tables": ["user_log", "user_activity"], + "commands": ["INSERT", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-with-column-ordering.json b/testdata/oracle/insert/insert-with-column-ordering.json new file mode 100644 index 0000000..6e5baa5 --- /dev/null +++ b/testdata/oracle/insert/insert-with-column-ordering.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT INTO customer_addresses (address, city, customer_id) VALUES ('123 Main St', 'Anytown', 456);", + "outputs": [ + { + "expected": "INSERT INTO customer_addresses ( address, city, customer_id ) VALUES ( ? )", + "statement_metadata": { + "size": 24, + "tables": ["customer_addresses"], + "commands": ["INSERT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-with-returning-clause.json b/testdata/oracle/insert/insert-with-returning-clause.json new file mode 100644 index 0000000..10f8de2 --- /dev/null +++ b/testdata/oracle/insert/insert-with-returning-clause.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT INTO transactions (account_id, amount) VALUES (123, 500) RETURNING transaction_id INTO :new_id;", + "outputs": [ + { + "expected": "INSERT INTO transactions ( account_id, amount ) VALUES ( ? ) RETURNING transaction_id INTO :new_id", + "statement_metadata": { + "size": 18, + "tables": ["transactions"], + "commands": ["INSERT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-with-select-union.json b/testdata/oracle/insert/insert-with-select-union.json new file mode 100644 index 0000000..19405ec --- /dev/null +++ b/testdata/oracle/insert/insert-with-select-union.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT INTO log (message) SELECT 'User logged in' FROM dual UNION ALL SELECT 'User performed an action' FROM dual;", + "outputs": [ + { + "expected": "INSERT INTO log ( message ) SELECT ? FROM dual UNION ALL SELECT ? FROM dual", + "statement_metadata": { + "size": 19, + "tables": ["log", "dual"], + "commands": ["INSERT", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-with-sequence.json b/testdata/oracle/insert/insert-with-sequence.json new file mode 100644 index 0000000..155a7e9 --- /dev/null +++ b/testdata/oracle/insert/insert-with-sequence.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT INTO products (id, name, price) VALUES (product_seq.NEXTVAL, 'New Product', 99.99);", + "outputs": [ + { + "expected": "INSERT INTO products ( id, name, price ) VALUES ( product_seq.NEXTVAL, ?, ? )", + "statement_metadata": { + "size": 14, + "tables": ["products"], + "commands": ["INSERT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/insert-with-subquery.json b/testdata/oracle/insert/insert-with-subquery.json new file mode 100644 index 0000000..5606870 --- /dev/null +++ b/testdata/oracle/insert/insert-with-subquery.json @@ -0,0 +1,23 @@ +{ + "input": "INSERT INTO orders (id, user_id, amount) SELECT order_seq.NEXTVAL, user_id, 100 FROM users WHERE status = 'active';", + "outputs": [ + { + "expected": "INSERT INTO orders ( id, user_id, amount ) SELECT order_seq.NEXTVAL, user_id, ? FROM users WHERE status = ?", + "statement_metadata": { + "size": 23, + "tables": ["orders", "users"], + "commands": ["INSERT", "SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "INSERT INTO orders (id, user_id, amount) SELECT order_seq.NEXTVAL, user_id, ? FROM users WHERE status = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/insert/multitable-insert-conditional.json b/testdata/oracle/insert/multitable-insert-conditional.json new file mode 100644 index 0000000..e901ad8 --- /dev/null +++ b/testdata/oracle/insert/multitable-insert-conditional.json @@ -0,0 +1,16 @@ +{ + "input": "INSERT FIRST INTO sales_audit (action) VALUES ('Sale occurred') WHEN amount > 1000 THEN INTO high_value_sales (sale_id, amount) VALUES (sale_id, amount) SELECT sale_id, amount FROM sales;", + "outputs": [ + { + "expected": "INSERT FIRST INTO sales_audit ( action ) VALUES ( ? ) WHEN amount > ? THEN INTO high_value_sales ( sale_id, amount ) VALUES ( sale_id, amount ) SELECT sale_id, amount FROM sales", + "statement_metadata": { + "size": 44, + "tables": ["sales_audit", "high_value_sales", "sales"], + "commands": ["INSERT", "SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/create-procedure-in-out-params.json b/testdata/oracle/procedure/create-procedure-in-out-params.json new file mode 100644 index 0000000..989dbef --- /dev/null +++ b/testdata/oracle/procedure/create-procedure-in-out-params.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE PROCEDURE CalculateDiscount(p_order_id IN NUMBER, p_discount OUT NUMBER) AS total_amount NUMBER; BEGIN SELECT SUM(price * quantity) INTO total_amount FROM order_items WHERE order_id = p_order_id; p_discount := total_amount * 0.1; END CalculateDiscount;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PROCEDURE CalculateDiscount(p_order_id IN NUMBER, p_discount OUT NUMBER) NUMBER; BEGIN SELECT SUM(price * quantity) INTO total_amount FROM order_items WHERE order_id = p_order_id; p_discount := total_amount * ?; END CalculateDiscount;", + "statement_metadata": { + "size": 57, + "tables": ["total_amount", "order_items"], + "commands": ["CREATE", "BEGIN", "SELECT"], + "comments": [], + "procedures": ["CalculateDiscount"] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/create-procedure-with-cursors.json b/testdata/oracle/procedure/create-procedure-with-cursors.json new file mode 100644 index 0000000..7571ee8 --- /dev/null +++ b/testdata/oracle/procedure/create-procedure-with-cursors.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE PROCEDURE FetchCustomerOrders(p_customer_id IN NUMBER) IS CURSOR order_cursor IS SELECT * FROM orders WHERE customer_id = p_customer_id; order_rec order_cursor%ROWTYPE; BEGIN OPEN order_cursor; LOOP FETCH order_cursor INTO order_rec; EXIT WHEN order_cursor%NOTFOUND; END LOOP; CLOSE order_cursor; END FetchCustomerOrders;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PROCEDURE FetchCustomerOrders(p_customer_id IN NUMBER) IS CURSOR order_cursor IS SELECT * FROM orders WHERE customer_id = p_customer_id; order_rec order_cursor % ROWTYPE; BEGIN OPEN order_cursor; LOOP FETCH order_cursor INTO order_rec; EXIT WHEN order_cursor % NOTFOUND; END LOOP; CLOSE order_cursor; END FetchCustomerOrders;", + "statement_metadata": { + "size": 51, + "tables": ["orders", "order_rec"], + "commands": ["CREATE", "SELECT", "BEGIN"], + "comments": [], + "procedures": ["FetchCustomerOrders"] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/create-procedure-with-exception-handling.json b/testdata/oracle/procedure/create-procedure-with-exception-handling.json new file mode 100644 index 0000000..d751895 --- /dev/null +++ b/testdata/oracle/procedure/create-procedure-with-exception-handling.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE PROCEDURE DeleteCustomer(p_customer_id IN NUMBER) AS BEGIN DELETE FROM customers WHERE id = p_customer_id; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20001, 'Error deleting customer.'); END DeleteCustomer;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PROCEDURE DeleteCustomer(p_customer_id IN NUMBER) AS BEGIN DELETE FROM customers WHERE id = p_customer_id; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(?); END DeleteCustomer;", + "statement_metadata": { + "size": 40, + "tables": ["customers"], + "commands": ["CREATE", "BEGIN", "DELETE"], + "comments": [], + "procedures": ["DeleteCustomer"] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/create-simple-stored-procedure.json b/testdata/oracle/procedure/create-simple-stored-procedure.json new file mode 100644 index 0000000..3bc5dd5 --- /dev/null +++ b/testdata/oracle/procedure/create-simple-stored-procedure.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE PROCEDURE UpdateOrderStatus(p_order_id IN NUMBER, p_status IN VARCHAR2) AS BEGIN UPDATE orders SET status = p_status WHERE order_id = p_order_id; END UpdateOrderStatus;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PROCEDURE UpdateOrderStatus(p_order_id IN NUMBER, p_status IN VARCHAR?) AS BEGIN UPDATE orders SET status = p_status WHERE order_id = p_order_id; END UpdateOrderStatus;", + "statement_metadata": { + "size": 40, + "tables": ["orders"], + "commands": ["CREATE", "BEGIN", "UPDATE"], + "comments": [], + "procedures": ["UpdateOrderStatus"] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/error-handling-exception.json b/testdata/oracle/procedure/error-handling-exception.json new file mode 100644 index 0000000..d225325 --- /dev/null +++ b/testdata/oracle/procedure/error-handling-exception.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE PROCEDURE update_test_amt(p_employee_id NUMBER, p_change NUMBER) AS BEGIN UPDATE employees SET test_amt = test_amt + p_change WHERE employee_id = p_employee_id; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20001, 'Invalid test_amt update'); END;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PROCEDURE update_test_amt(p_employee_id NUMBER, p_change NUMBER) AS BEGIN UPDATE employees SET test_amt = test_amt + p_change WHERE employee_id = p_employee_id; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(?); END;", + "statement_metadata": { + "size": 41, + "tables": ["employees"], + "commands": ["CREATE", "BEGIN", "UPDATE"], + "comments": [], + "procedures": ["update_test_amt"] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/invoke-stored-procedure-with-exec.json b/testdata/oracle/procedure/invoke-stored-procedure-with-exec.json new file mode 100644 index 0000000..a1abd41 --- /dev/null +++ b/testdata/oracle/procedure/invoke-stored-procedure-with-exec.json @@ -0,0 +1,27 @@ +{ + "input": "EXEC UpdateOrderStatus(123, 'Shipped');", + "outputs": [ + { + "expected": "EXEC UpdateOrderStatus(?);", + "statement_metadata": { + "size": 4, + "tables": [], + "commands": ["EXEC"], + "comments": [], + "procedures": [] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/invoke-stored-procedure.json b/testdata/oracle/procedure/invoke-stored-procedure.json new file mode 100644 index 0000000..91ad0a1 --- /dev/null +++ b/testdata/oracle/procedure/invoke-stored-procedure.json @@ -0,0 +1,27 @@ +{ + "input": "BEGIN UpdateOrderStatus(123, 'Shipped'); END;", + "outputs": [ + { + "expected": "BEGIN UpdateOrderStatus(?); END;", + "statement_metadata": { + "size": 5, + "tables": [], + "commands": ["BEGIN"], + "comments": [], + "procedures": [] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/packages.json b/testdata/oracle/procedure/packages.json new file mode 100644 index 0000000..aa27903 --- /dev/null +++ b/testdata/oracle/procedure/packages.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE PACKAGE mgmt AS PROCEDURE test_proc_1(p_name VARCHAR2); PROCEDURE test_proc_2(p_id NUMBER); END mgmt;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PACKAGE mgmt AS PROCEDURE test_proc_1(p_name VARCHAR?); PROCEDURE test_proc_2(p_id NUMBER); END mgmt;", + "statement_metadata": { + "size": 28, + "tables": [], + "commands": ["CREATE"], + "comments": [], + "procedures": ["test_proc_1", "test_proc_2"] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/pipelined-functions.json b/testdata/oracle/procedure/pipelined-functions.json new file mode 100644 index 0000000..5ec8108 --- /dev/null +++ b/testdata/oracle/procedure/pipelined-functions.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE FUNCTION get_departments RETURN dept_t PIPELINED AS BEGIN FOR r IN (SELECT * FROM departments) LOOP PIPE ROW(r); END LOOP; RETURN; END;", + "outputs": [ + { + "expected": "CREATE OR REPLACE FUNCTION get_departments RETURN dept_t PIPELINED AS BEGIN FOR r IN (SELECT * FROM departments) LOOP PIPE ROW(r); END LOOP; RETURN; END;", + "statement_metadata": { + "size": 28, + "tables": ["departments"], + "commands": ["CREATE", "BEGIN", "SELECT"], + "comments": [], + "procedures": [] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/stored-procedures-functions.json b/testdata/oracle/procedure/stored-procedures-functions.json new file mode 100644 index 0000000..a6ffffb --- /dev/null +++ b/testdata/oracle/procedure/stored-procedures-functions.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE PROCEDURE get_employee_count(p_dept_id IN NUMBER, p_count OUT NUMBER) AS BEGIN SELECT COUNT(*) INTO p_count FROM employees WHERE department_id = p_dept_id; END; BEGIN get_employee_count(10, :count); END;", + "outputs": [ + { + "expected": "CREATE OR REPLACE PROCEDURE get_employee_count(p_dept_id IN NUMBER, p_count OUT NUMBER) AS BEGIN SELECT COUNT(*) INTO p_count FROM employees WHERE department_id = p_dept_id; END; BEGIN get_employee_count(?, :count); END;", + "statement_metadata": { + "size": 51, + "tables": ["p_count", "employees"], + "commands": ["CREATE", "BEGIN", "SELECT"], + "comments": [], + "procedures": ["get_employee_count"] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/procedure/triggers.json b/testdata/oracle/procedure/triggers.json new file mode 100644 index 0000000..8ff82d4 --- /dev/null +++ b/testdata/oracle/procedure/triggers.json @@ -0,0 +1,27 @@ +{ + "input": "CREATE OR REPLACE TRIGGER audit_table AFTER INSERT ON logs FOR EACH ROW BEGIN INSERT INTO audit_log (action) VALUES ('Inserted new log'); END;", + "outputs": [ + { + "expected": "CREATE OR REPLACE TRIGGER audit_table AFTER INSERT ON logs FOR EACH ROW BEGIN INSERT INTO audit_log (action) VALUES (?); END;", + "statement_metadata": { + "size": 26, + "tables": ["audit_log"], + "commands": ["CREATE", "INSERT", "BEGIN"], + "comments": [], + "procedures": [] + }, + "obfuscator_config": { + "replace_digits": true + }, + "normalizer_config": { + "collect_tables": true, + "collect_commands": true, + "collect_comments": true, + "collect_procedure": true, + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/complex-join-operations.json b/testdata/oracle/select/complex-join-operations.json new file mode 100644 index 0000000..f801075 --- /dev/null +++ b/testdata/oracle/select/complex-join-operations.json @@ -0,0 +1,16 @@ +{ + "input": "SELECT e.employee_id, e.last_name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE e.test_amt > (SELECT AVG(test_amt) FROM employees WHERE department_id = e.department_id);", + "outputs": [ + { + "expected": "SELECT e.employee_id, e.last_name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE e.test_amt > ( SELECT AVG ( test_amt ) FROM employees WHERE department_id = e.department_id )", + "statement_metadata": { + "size": 30, + "tables": ["employees", "departments"], + "commands": ["SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/hierarchical-queries.json b/testdata/oracle/select/hierarchical-queries.json new file mode 100644 index 0000000..3881a25 --- /dev/null +++ b/testdata/oracle/select/hierarchical-queries.json @@ -0,0 +1,16 @@ +{ + "input": "SELECT employee_id, last_name, manager_id FROM employees START WITH manager_id IS NULL CONNECT BY PRIOR employee_id = manager_id;", + "outputs": [ + { + "expected": "SELECT employee_id, last_name, manager_id FROM employees START WITH manager_id IS ? CONNECT BY PRIOR employee_id = manager_id", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/large-objects-lobs.json b/testdata/oracle/select/large-objects-lobs.json new file mode 100644 index 0000000..eae0403 --- /dev/null +++ b/testdata/oracle/select/large-objects-lobs.json @@ -0,0 +1,16 @@ +{ + "input": "SELECT id, DBMS_LOB.SUBSTR(blob_data, 2000, 1) as blob_content, DBMS_LOB.SUBSTR(clob_data, 2000, 1) as clob_content FROM lob_test WHERE id = 1;", + "outputs": [ + { + "expected": "SELECT id, DBMS_LOB.SUBSTR ( blob_data, ?, ? ), DBMS_LOB.SUBSTR ( clob_data, ?, ? ) FROM lob_test WHERE id = ?", + "statement_metadata": { + "size": 14, + "tables": ["lob_test"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/oracle-text.json b/testdata/oracle/select/oracle-text.json new file mode 100644 index 0000000..33beecd --- /dev/null +++ b/testdata/oracle/select/oracle-text.json @@ -0,0 +1,16 @@ +{ + "input": "SELECT id, title FROM articles WHERE CONTAINS(text, 'Oracle', 1) > 0;", + "outputs": [ + { + "expected": "SELECT id, title FROM articles WHERE CONTAINS ( text, ?, ? ) > ?", + "statement_metadata": { + "size": 14, + "tables": ["articles"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/recursive-cte.json b/testdata/oracle/select/recursive-cte.json new file mode 100644 index 0000000..8524a7b --- /dev/null +++ b/testdata/oracle/select/recursive-cte.json @@ -0,0 +1,16 @@ +{ + "input": "WITH RECURSIVE subordinates AS (SELECT employee_id, manager_id FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.employee_id, e.manager_id FROM employees e JOIN subordinates s ON e.manager_id = s.employee_id) SELECT * FROM subordinates;", + "outputs": [ + { + "expected": "WITH RECURSIVE subordinates AS ( SELECT employee_id, manager_id FROM employees WHERE manager_id IS ? UNION ALL SELECT e.employee_id, e.manager_id FROM employees e JOIN subordinates s ON e.manager_id = s.employee_id ) SELECT * FROM subordinates", + "statement_metadata": { + "size": 31, + "tables": ["employees", "subordinates"], + "commands": [ "SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-basic-conditions.json b/testdata/oracle/select/select-basic-conditions.json new file mode 100644 index 0000000..d3b8b73 --- /dev/null +++ b/testdata/oracle/select/select-basic-conditions.json @@ -0,0 +1,22 @@ +{ + "input": "SELECT id, name FROM users WHERE age > 30 AND status = 'active';", + "outputs": [ + { + "expected": "SELECT id, name FROM users WHERE age > ? AND status = ?", + "statement_metadata": { + "size": 11, + "tables": ["users"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT id, name FROM users WHERE age > ? AND status = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-hierarchical-query.json b/testdata/oracle/select/select-hierarchical-query.json new file mode 100644 index 0000000..947494a --- /dev/null +++ b/testdata/oracle/select/select-hierarchical-query.json @@ -0,0 +1,25 @@ +{ + "input": "SELECT employee_id, last_name, manager_id FROM employees START WITH manager_id IS NULL CONNECT BY PRIOR employee_id = manager_id;", + "outputs": [ + { + "expected": "SELECT employee_id, last_name, manager_id FROM employees START WITH manager_id IS ? CONNECT BY PRIOR employee_id = manager_id", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT employee_id, last_name, manager_id FROM employees START WITH manager_id IS NULL CONNECT BY PRIOR employee_id = manager_id;", + "normalizer_config": { + "keep_trailing_semicolon": true + }, + "obfuscator_config": { + "replace_boolean":false + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-using-oracle-text.json b/testdata/oracle/select/select-using-oracle-text.json new file mode 100644 index 0000000..7878c10 --- /dev/null +++ b/testdata/oracle/select/select-using-oracle-text.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT id, title FROM articles WHERE CONTAINS(text, 'Oracle', 1) > 0;", + "outputs": [ + { + "expected": "SELECT id, title FROM articles WHERE CONTAINS ( text, ?, ? ) > ?", + "statement_metadata": { + "size": 14, + "tables": ["articles"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT id, title FROM articles WHERE CONTAINS(text, ?, ?) > ?;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-using-with-clause.json b/testdata/oracle/select/select-using-with-clause.json new file mode 100644 index 0000000..6bb2fc5 --- /dev/null +++ b/testdata/oracle/select/select-using-with-clause.json @@ -0,0 +1,23 @@ +{ + "input": "WITH dept_costs AS (SELECT department_id, SUM(test_amt) AS total_sal FROM employees GROUP BY department_id) SELECT * FROM dept_costs WHERE total_sal > (SELECT AVG(total_sal) FROM dept_costs);", + "outputs": [ + { + "expected": "WITH dept_costs AS ( SELECT department_id, SUM ( test_amt ) FROM employees GROUP BY department_id ) SELECT * FROM dept_costs WHERE total_sal > ( SELECT AVG ( total_sal ) FROM dept_costs )", + "statement_metadata": { + "size": 25, + "tables": ["employees", "dept_costs"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "WITH dept_costs AS (SELECT department_id, SUM(test_amt) FROM employees GROUP BY department_id) SELECT * FROM dept_costs WHERE total_sal > (SELECT AVG(total_sal) FROM dept_costs);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-flashback-query.json b/testdata/oracle/select/select-with-flashback-query.json new file mode 100644 index 0000000..1c6c6d6 --- /dev/null +++ b/testdata/oracle/select/select-with-flashback-query.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT * FROM employees AS OF TIMESTAMP TO_TIMESTAMP('2023-03-15 08:30:00', 'YYYY-MM-DD HH24:MI:SS') WHERE department_id = 10;", + "outputs": [ + { + "expected": "SELECT * FROM employees AS OF TIMESTAMP TO_TIMESTAMP ( ? ) WHERE department_id = ?", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT * FROM employees AS OF TIMESTAMP TO_TIMESTAMP(?) WHERE department_id = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-model-clause.json b/testdata/oracle/select/select-with-model-clause.json new file mode 100644 index 0000000..17f3fd9 --- /dev/null +++ b/testdata/oracle/select/select-with-model-clause.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT * FROM (SELECT year, product, amount FROM sales) MODEL DIMENSION BY (year) MEASURES (product, amount) RULES (amount['2023'] = amount['2022'] * 1.1);", + "outputs": [ + { + "expected": "SELECT * FROM ( SELECT year, product, amount FROM sales ) MODEL DIMENSION BY ( year ) MEASURES ( product, amount ) RULES ( amount [ ? ] = amount [ ? ] * ? )", + "statement_metadata": { + "size": 11, + "tables": ["sales"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT * FROM (SELECT year, product, amount FROM sales) MODEL DIMENSION BY (year) MEASURES (product, amount) RULES (amount [?] = amount [?] * ?);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-multi-line-comments.json b/testdata/oracle/select/select-with-multi-line-comments.json new file mode 100644 index 0000000..2a9fd89 --- /dev/null +++ b/testdata/oracle/select/select-with-multi-line-comments.json @@ -0,0 +1,22 @@ +{ + "input": "SELECT /* Multi-line\n comment */ id, name FROM users WHERE status = 'active';", + "outputs": [ + { + "expected": "SELECT id, name FROM users WHERE status = ?", + "statement_metadata": { + "size": 36, + "tables": ["users"], + "commands": ["SELECT"], + "comments": ["/* Multi-line\n comment */"], + "procedures": [] + } + }, + { + "expected": "SELECT id, name FROM users WHERE status = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-oracle-specific-joins.json b/testdata/oracle/select/select-with-oracle-specific-joins.json new file mode 100644 index 0000000..06b165a --- /dev/null +++ b/testdata/oracle/select/select-with-oracle-specific-joins.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT e.employee_id, e.last_name, d.department_name FROM employees e, departments d WHERE e.department_id = d.department_id(+);", + "outputs": [ + { + "expected": "SELECT e.employee_id, e.last_name, d.department_name FROM employees e, departments d WHERE e.department_id = d.department_id ( + )", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT e.employee_id, e.last_name, d.department_name FROM employees e, departments d WHERE e.department_id = d.department_id(+);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-partition-by.json b/testdata/oracle/select/select-with-partition-by.json new file mode 100644 index 0000000..86253a7 --- /dev/null +++ b/testdata/oracle/select/select-with-partition-by.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT department_id, last_name, test_amt, AVG(test_amt) OVER (PARTITION BY department_id) AS avg_dept_test_amt FROM employees;", + "outputs": [ + { + "expected": "SELECT department_id, last_name, test_amt, AVG ( test_amt ) OVER ( PARTITION BY department_id ) FROM employees", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT department_id, last_name, test_amt, AVG(test_amt) OVER (PARTITION BY department_id) FROM employees;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-pseudocolumns.json b/testdata/oracle/select/select-with-pseudocolumns.json new file mode 100644 index 0000000..a98bd68 --- /dev/null +++ b/testdata/oracle/select/select-with-pseudocolumns.json @@ -0,0 +1,22 @@ +{ + "input": "SELECT LEVEL, ROWNUM, employee_id, last_name FROM employees WHERE ROWNUM <= 10 CONNECT BY PRIOR employee_id = manager_id;", + "outputs": [ + { + "expected": "SELECT LEVEL, ROWNUM, employee_id, last_name FROM employees WHERE ROWNUM <= ? CONNECT BY PRIOR employee_id = manager_id", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT LEVEL, ROWNUM, employee_id, last_name FROM employees WHERE ROWNUM <= ? CONNECT BY PRIOR employee_id = manager_id;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-rollup-function.json b/testdata/oracle/select/select-with-rollup-function.json new file mode 100644 index 0000000..bc12385 --- /dev/null +++ b/testdata/oracle/select/select-with-rollup-function.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT department_id, job_id, SUM(test_amt) total_test_amt FROM employees GROUP BY ROLLUP (department_id, job_id);", + "outputs": [ + { + "expected": "SELECT department_id, job_id, SUM ( test_amt ) total_test_amt FROM employees GROUP BY ROLLUP ( department_id, job_id )", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT department_id, job_id, SUM(test_amt) total_test_amt FROM employees GROUP BY ROLLUP (department_id, job_id);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-sample-clause.json b/testdata/oracle/select/select-with-sample-clause.json new file mode 100644 index 0000000..74cc06b --- /dev/null +++ b/testdata/oracle/select/select-with-sample-clause.json @@ -0,0 +1,23 @@ +{ + "input": "SELECT * FROM employees SAMPLE (10);", + "outputs": [ + { + "expected": "SELECT * FROM employees SAMPLE ( ? )", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT * FROM employees SAMPLE (?);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-single-line-comments.json b/testdata/oracle/select/select-with-single-line-comments.json new file mode 100644 index 0000000..7f32afd --- /dev/null +++ b/testdata/oracle/select/select-with-single-line-comments.json @@ -0,0 +1,22 @@ +{ + "input": "SELECT id, name FROM users WHERE status = 'active'; -- Single-line comment explaining the query", + "outputs": [ + { + "expected": "SELECT id, name FROM users WHERE status = ?", + "statement_metadata": { + "size": 54, + "tables": ["users"], + "commands": ["SELECT"], + "comments": ["-- Single-line comment explaining the query"], + "procedures": [] + } + }, + { + "expected": "SELECT id, name FROM users WHERE status = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/select-with-skip-locked.json b/testdata/oracle/select/select-with-skip-locked.json new file mode 100644 index 0000000..72326d1 --- /dev/null +++ b/testdata/oracle/select/select-with-skip-locked.json @@ -0,0 +1,22 @@ +{ + "input": "SELECT * FROM orders WHERE order_status = 'PENDING' FOR UPDATE SKIP LOCKED;", + "outputs": [ + { + "expected": "SELECT * FROM orders WHERE order_status = ? FOR UPDATE SKIP LOCKED", + "statement_metadata": { + "size": 18, + "tables": ["orders"], + "commands": ["SELECT", "UPDATE"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "SELECT * FROM orders WHERE order_status = ? FOR UPDATE SKIP LOCKED;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/select/window-functions-analytics.json b/testdata/oracle/select/window-functions-analytics.json new file mode 100644 index 0000000..a0af2d8 --- /dev/null +++ b/testdata/oracle/select/window-functions-analytics.json @@ -0,0 +1,16 @@ +{ + "input": "SELECT employee_id, test_amt, AVG(yoe) OVER (PARTITION BY department_id) AS avg_department_yoe FROM employees;", + "outputs": [ + { + "expected": "SELECT employee_id, test_amt, AVG ( yoe ) OVER ( PARTITION BY department_id ) FROM employees", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["SELECT"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/conditional-update-with-case.json b/testdata/oracle/update/conditional-update-with-case.json new file mode 100644 index 0000000..949243d --- /dev/null +++ b/testdata/oracle/update/conditional-update-with-case.json @@ -0,0 +1,22 @@ +{ + "input": "UPDATE employees SET test_amt = CASE WHEN job_id = 'XX' THEN test_amt * 1.10 WHEN job_id = 'YY' THEN test_amt * 1.20 ELSE test_amt END;", + "outputs": [ + { + "expected": "UPDATE employees SET test_amt = CASE WHEN job_id = ? THEN test_amt * ? WHEN job_id = ? THEN test_amt * ? ELSE test_amt END", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["UPDATE"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE employees SET test_amt = CASE WHEN job_id = ? THEN test_amt * ? WHEN job_id = ? THEN test_amt * ? ELSE test_amt END;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/conditional-update-with-decode.json b/testdata/oracle/update/conditional-update-with-decode.json new file mode 100644 index 0000000..5f9e239 --- /dev/null +++ b/testdata/oracle/update/conditional-update-with-decode.json @@ -0,0 +1,23 @@ +{ + "input": "UPDATE order_items SET discount = DECODE(quantity, 10, 5, 20, 10, 0) WHERE order_id = 456;", + "outputs": [ + { + "expected": "UPDATE order_items SET discount = DECODE ( quantity, ?, ?, ?, ?, ? ) WHERE order_id = ?", + "statement_metadata": { + "size": 17, + "tables": ["order_items"], + "commands": ["UPDATE"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE order_items SET discount = DECODE(quantity, ?, ?, ?, ?, ?) WHERE order_id = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/dynamic-plsql.json b/testdata/oracle/update/dynamic-plsql.json new file mode 100644 index 0000000..91cdcb8 --- /dev/null +++ b/testdata/oracle/update/dynamic-plsql.json @@ -0,0 +1,16 @@ +{ + "input": "BEGIN EXECUTE IMMEDIATE 'UPDATE logs SET retention = retention * 1.1'; END;", + "outputs": [ + { + "expected": "BEGIN EXECUTE IMMEDIATE ?; END", + "statement_metadata": { + "size": 12, + "tables": [], + "commands": ["BEGIN", "EXECUTE"], + "comments": [], + "procedures": [] + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-basic.json b/testdata/oracle/update/update-basic.json new file mode 100644 index 0000000..d5ee028 --- /dev/null +++ b/testdata/oracle/update/update-basic.json @@ -0,0 +1,22 @@ +{ + "input": "UPDATE employees SET test_amt = test_amt * 1.05 WHERE department_id = 3;", + "outputs": [ + { + "expected": "UPDATE employees SET test_amt = test_amt * ? WHERE department_id = ?", + "statement_metadata": { + "size": 15, + "tables": ["employees"], + "commands": ["UPDATE"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE employees SET test_amt = test_amt * ? WHERE department_id = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-oracle-specific-syntax.json b/testdata/oracle/update/update-oracle-specific-syntax.json new file mode 100644 index 0000000..a7f6bae --- /dev/null +++ b/testdata/oracle/update/update-oracle-specific-syntax.json @@ -0,0 +1,23 @@ +{ + "input": "UPDATE (SELECT e.test_amt, d.budget FROM employees e JOIN departments d ON e.department_id = d.id) t SET t.test_amt = t.test_amt * 1.05, t.budget = t.budget - 1000;", + "outputs": [ + { + "expected": "UPDATE ( SELECT e.test_amt, d.budget FROM employees e JOIN departments d ON e.department_id = d.id ) t SET t.test_amt = t.test_amt * ?, t.budget = t.budget - ?", + "statement_metadata": { + "size": 36, + "tables": ["employees", "departments"], + "commands": ["UPDATE", "SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE (SELECT e.test_amt, d.budget FROM employees e JOIN departments d ON e.department_id = d.id) t SET t.test_amt = t.test_amt * ?, t.budget = t.budget - ?;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-using-correlated-subquery.json b/testdata/oracle/update/update-using-correlated-subquery.json new file mode 100644 index 0000000..3ba8e9d --- /dev/null +++ b/testdata/oracle/update/update-using-correlated-subquery.json @@ -0,0 +1,23 @@ +{ + "input": "UPDATE orders o SET o.status = 'DELAYED' WHERE EXISTS (SELECT 1 FROM shipments s WHERE s.order_id = o.id AND s.estimated_arrival < SYSDATE);", + "outputs": [ + { + "expected": "UPDATE orders o SET o.status = ? WHERE EXISTS ( SELECT ? FROM shipments s WHERE s.order_id = o.id AND s.estimated_arrival < SYSDATE )", + "statement_metadata": { + "size": 27, + "tables": ["orders", "shipments"], + "commands": ["UPDATE", "SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE orders o SET o.status = ? WHERE EXISTS (SELECT ? FROM shipments s WHERE s.order_id = o.id AND s.estimated_arrival < SYSDATE);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-using-join-syntax.json b/testdata/oracle/update/update-using-join-syntax.json new file mode 100644 index 0000000..18d78d2 --- /dev/null +++ b/testdata/oracle/update/update-using-join-syntax.json @@ -0,0 +1,23 @@ +{ + "input": "UPDATE (SELECT a.account_balance, t.transaction_amount FROM accounts a JOIN transactions t ON a.account_id = t.account_id) SET account_balance = account_balance + transaction_amount;", + "outputs": [ + { + "expected": "UPDATE ( SELECT a.account_balance, t.transaction_amount FROM accounts a JOIN transactions t ON a.account_id = t.account_id ) SET account_balance = account_balance + transaction_amount", + "statement_metadata": { + "size": 36, + "tables": ["accounts", "transactions"], + "commands": ["UPDATE", "SELECT", "JOIN"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE (SELECT a.account_balance, t.transaction_amount FROM accounts a JOIN transactions t ON a.account_id = t.account_id) SET account_balance = account_balance + transaction_amount;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-with-correlated-subquery.json b/testdata/oracle/update/update-with-correlated-subquery.json new file mode 100644 index 0000000..5846dbc --- /dev/null +++ b/testdata/oracle/update/update-with-correlated-subquery.json @@ -0,0 +1,23 @@ +{ + "input": "UPDATE customer_orders co SET total_amount = (SELECT SUM(oi.price * oi.quantity) FROM order_items oi WHERE oi.order_id = co.id) WHERE co.status = 'Pending';", + "outputs": [ + { + "expected": "UPDATE customer_orders co SET total_amount = ( SELECT SUM ( oi.price * oi.quantity ) FROM order_items oi WHERE oi.order_id = co.id ) WHERE co.status = ?", + "statement_metadata": { + "size": 38, + "tables": ["customer_orders", "order_items"], + "commands": ["UPDATE", "SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE customer_orders co SET total_amount = (SELECT SUM(oi.price * oi.quantity) FROM order_items oi WHERE oi.order_id = co.id) WHERE co.status = ?;", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-with-join.json b/testdata/oracle/update/update-with-join.json new file mode 100644 index 0000000..24d901a --- /dev/null +++ b/testdata/oracle/update/update-with-join.json @@ -0,0 +1,22 @@ +{ + "input": "UPDATE products p SET p.price = p.price * 1.1 FROM suppliers s WHERE p.supplier_id = s.id AND s.rating > 4;", + "outputs": [ + { + "expected": "UPDATE products p SET p.price = p.price * ? FROM suppliers s WHERE p.supplier_id = s.id AND s.rating > ?", + "statement_metadata": { + "size": 23, + "tables": ["products", "suppliers"], + "commands": ["UPDATE"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE products p SET p.price = p.price * ? FROM suppliers s WHERE p.supplier_id = s.id AND s.rating > ?;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-with-returning-clause.json b/testdata/oracle/update/update-with-returning-clause.json new file mode 100644 index 0000000..8f9c17f --- /dev/null +++ b/testdata/oracle/update/update-with-returning-clause.json @@ -0,0 +1,22 @@ +{ + "input": "UPDATE orders SET order_status = 'Completed' WHERE order_id = 123 RETURNING customer_id, order_total INTO :cust_id, :total;", + "outputs": [ + { + "expected": "UPDATE orders SET order_status = ? WHERE order_id = ? RETURNING customer_id, order_total INTO :cust_id, :total", + "statement_metadata": { + "size": 12, + "tables": ["orders"], + "commands": ["UPDATE"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE orders SET order_status = ? WHERE order_id = ? RETURNING customer_id, order_total INTO :cust_id, :total;", + "normalizer_config": { + "keep_trailing_semicolon": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-with-subquery-in-set.json b/testdata/oracle/update/update-with-subquery-in-set.json new file mode 100644 index 0000000..d5a81c9 --- /dev/null +++ b/testdata/oracle/update/update-with-subquery-in-set.json @@ -0,0 +1,23 @@ +{ + "input": "UPDATE products p SET (p.price, p.stock) = (SELECT s.discounted_price, s.quantity FROM sale_items s WHERE s.product_id = p.id) WHERE EXISTS (SELECT 1 FROM sale_items s WHERE s.product_id = p.id);", + "outputs": [ + { + "expected": "UPDATE products p SET ( p.price, p.stock ) = ( SELECT s.discounted_price, s.quantity FROM sale_items s WHERE s.product_id = p.id ) WHERE EXISTS ( SELECT ? FROM sale_items s WHERE s.product_id = p.id )", + "statement_metadata": { + "size": 30, + "tables": ["products", "sale_items"], + "commands": ["UPDATE", "SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE products p SET (p.price, p.stock) = (SELECT s.discounted_price, s.quantity FROM sale_items s WHERE s.product_id = p.id) WHERE EXISTS (SELECT ? FROM sale_items s WHERE s.product_id = p.id);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/oracle/update/update-with-subquery.json b/testdata/oracle/update/update-with-subquery.json new file mode 100644 index 0000000..740712c --- /dev/null +++ b/testdata/oracle/update/update-with-subquery.json @@ -0,0 +1,23 @@ +{ + "input": "UPDATE products SET price = price * 0.9 WHERE id IN (SELECT product_id FROM inventory WHERE quantity > 100);", + "outputs": [ + { + "expected": "UPDATE products SET price = price * ? WHERE id IN ( SELECT product_id FROM inventory WHERE quantity > ? )", + "statement_metadata": { + "size": 29, + "tables": ["products", "inventory"], + "commands": ["UPDATE", "SELECT"], + "comments": [], + "procedures": [] + } + }, + { + "expected": "UPDATE products SET price = price * ? WHERE id IN (SELECT product_id FROM inventory WHERE quantity > ?);", + "normalizer_config": { + "keep_trailing_semicolon": true, + "remove_space_between_parentheses": true + } + } + ] + } + \ No newline at end of file diff --git a/testdata/postgresql/complex/select-complex-joins-window-functions.json b/testdata/postgresql/complex/select-complex-joins-window-functions.json index d53cff3..71029e5 100644 --- a/testdata/postgresql/complex/select-complex-joins-window-functions.json +++ b/testdata/postgresql/complex/select-complex-joins-window-functions.json @@ -1,8 +1,8 @@ { - "input": "SELECT \n e1.name AS employee_name,\n e1.salary,\n e2.name AS manager_name,\n AVG(e2.salary) OVER (PARTITION BY e1.manager_id) AS avg_manager_salary,\n RANK() OVER (ORDER BY e1.salary DESC) AS salary_rank\nFROM \n employees e1\nLEFT JOIN employees e2 ON e1.manager_id = e2.id\nWHERE \n e1.department_id IN (SELECT id FROM departments WHERE name LIKE 'IT%')\nAND \n e1.hire_date > '2020-01-01'\nORDER BY \n salary_rank, avg_manager_salary DESC;", + "input": "SELECT \n e1.name AS employee_name,\n e1.test_amt,\n e2.name AS manager_name,\n AVG(e2.test_amt) OVER (PARTITION BY e1.manager_id) AS avg_manager_test_amt,\n RANK() OVER (ORDER BY e1.test_amt DESC) AS test_amt_rank\nFROM \n employees e1\nLEFT JOIN employees e2 ON e1.manager_id = e2.id\nWHERE \n e1.department_id IN (SELECT id FROM departments WHERE name LIKE 'IT%')\nAND \n e1.hire_date > '2020-01-01'\nORDER BY \n test_amt_rank, avg_manager_test_amt DESC;", "outputs": [ { - "expected": "SELECT e?.name, e?.salary, e?.name, AVG ( e?.salary ) OVER ( PARTITION BY e?.manager_id ), RANK ( ) OVER ( ORDER BY e?.salary DESC ) FROM employees e? LEFT JOIN employees e? ON e?.manager_id = e?.id WHERE e?.department_id IN ( SELECT id FROM departments WHERE name LIKE ? ) AND e?.hire_date > ? ORDER BY salary_rank, avg_manager_salary DESC", + "expected": "SELECT e?.name, e?.test_amt, e?.name, AVG ( e?.test_amt ) OVER ( PARTITION BY e?.manager_id ), RANK ( ) OVER ( ORDER BY e?.test_amt DESC ) FROM employees e? LEFT JOIN employees e? ON e?.manager_id = e?.id WHERE e?.department_id IN ( SELECT id FROM departments WHERE name LIKE ? ) AND e?.hire_date > ? ORDER BY test_amt_rank, avg_manager_test_amt DESC", "statement_metadata": { "size": 30, "tables": [