sql-data-guard 0.1.3__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sql_data_guard-0.1.3/src/sql_data_guard.egg-info → sql_data_guard-0.1.4}/PKG-INFO +1 -1
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/pyproject.toml +1 -1
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/restriction_verification.py +39 -16
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/sql_data_guard.py +26 -13
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4/src/sql_data_guard.egg-info}/PKG-INFO +1 -1
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_sql_guard_curr_unit.py +113 -68
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_sql_guard_updates_unit.py +9 -40
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/LICENSE.md +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/README.md +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/setup.cfg +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/__init__.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/rest/__init__.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/rest/sql_data_guard_rest.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/restriction_validation.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/verification_context.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/verification_utils.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard.egg-info/SOURCES.txt +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard.egg-info/dependency_links.txt +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard.egg-info/requires.txt +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard.egg-info/top_level.txt +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_duckdb_unit.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_rest_api_unit.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_sql_guard_joins_unit.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_sql_guard_llm.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_sql_guard_unit.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_sql_guard_validation_unit.py +0 -0
- {sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/test/test_utils.py +0 -0
{sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/restriction_verification.py
RENAMED
|
@@ -103,38 +103,61 @@ def _verify_restriction(
|
|
|
103
103
|
Verifies if a given restriction is satisfied within an SQL expression.
|
|
104
104
|
|
|
105
105
|
Args:
|
|
106
|
-
restriction (dict): The restriction to verify, containing 'column' and 'value'
|
|
106
|
+
restriction (dict): The restriction to verify, containing 'column' and 'value' or 'values'.
|
|
107
107
|
from_table (Table): The table reference to check the restriction against.
|
|
108
|
-
exp (
|
|
108
|
+
exp (Expression): The SQL expression to check against the restriction.
|
|
109
109
|
|
|
110
110
|
Returns:
|
|
111
111
|
bool: True if the restriction is satisfied, False otherwise.
|
|
112
112
|
"""
|
|
113
|
+
|
|
113
114
|
if isinstance(exp, expr.Not):
|
|
114
115
|
return False
|
|
116
|
+
|
|
115
117
|
if isinstance(exp, expr.Paren):
|
|
116
118
|
return _verify_restriction(restriction, from_table, exp.this)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if not exp.this.name == restriction["column"]:
|
|
119
|
+
|
|
120
|
+
if not isinstance(exp.this, expr.Column) or exp.this.name != restriction["column"]:
|
|
120
121
|
return False
|
|
122
|
+
|
|
121
123
|
if exp.this.table and from_table.alias and exp.this.table != from_table.alias:
|
|
122
124
|
return False
|
|
123
125
|
if exp.this.table and not from_table.alias and exp.this.table != from_table.name:
|
|
124
126
|
return False
|
|
127
|
+
|
|
128
|
+
values = _get_restriction_values(restriction) # Get correct restriction values
|
|
129
|
+
|
|
130
|
+
# Handle IN condition correctly
|
|
131
|
+
if isinstance(exp, expr.In):
|
|
132
|
+
expr_values = [str(val.this) for val in exp.expressions]
|
|
133
|
+
return any(v in values for v in expr_values)
|
|
134
|
+
|
|
135
|
+
# Handle EQ (=) condition
|
|
125
136
|
if isinstance(exp, expr.EQ) and isinstance(exp.right, expr.Condition):
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
else:
|
|
129
|
-
values = _get_restriction_values(restriction)
|
|
130
|
-
return exp.right.this in values
|
|
137
|
+
return str(exp.right.this) in values
|
|
138
|
+
|
|
131
139
|
if isinstance(exp, expr.Between):
|
|
132
|
-
|
|
133
|
-
values
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
low, high = int(exp.args["low"].this), int(exp.args["high"].this)
|
|
141
|
+
if len(values) == 2: # Ensure we have exactly two values
|
|
142
|
+
restriction_low, restriction_high = map(int, values)
|
|
143
|
+
return restriction_low <= low and high <= restriction_high
|
|
144
|
+
|
|
145
|
+
if isinstance(exp, (expr.LT, expr.LTE, expr.GT, expr.GTE)) and isinstance(
|
|
146
|
+
exp.right, expr.Condition
|
|
147
|
+
):
|
|
148
|
+
if restriction.get("operation") not in [">=", ">", "<=", "<"]:
|
|
149
|
+
return False
|
|
150
|
+
assert len(values) == 1
|
|
151
|
+
if isinstance(exp, expr.LT) and restriction["operation"] == "<":
|
|
152
|
+
return str(exp.right.this) < values[0]
|
|
153
|
+
elif isinstance(exp, expr.LTE) and restriction["operation"] == "<=":
|
|
154
|
+
return str(exp.right.this) <= values[0]
|
|
155
|
+
elif isinstance(exp, expr.GT) and restriction["operation"] == ">":
|
|
156
|
+
return str(exp.right.this) > values[0]
|
|
157
|
+
elif isinstance(exp, expr.GTE) and restriction["operation"] == ">=":
|
|
158
|
+
return str(exp.right.this) >= values[0]
|
|
159
|
+
else:
|
|
160
|
+
return False
|
|
138
161
|
return False
|
|
139
162
|
|
|
140
163
|
|
|
@@ -77,6 +77,10 @@ def _verify_where_clause(
|
|
|
77
77
|
select_statement: expr.Query,
|
|
78
78
|
from_tables: List[expr.Table],
|
|
79
79
|
):
|
|
80
|
+
where_clause = select_statement.find(expr.Where)
|
|
81
|
+
if where_clause:
|
|
82
|
+
for sub in where_clause.find_all(expr.Subquery):
|
|
83
|
+
_verify_query_statement(sub.this, context)
|
|
80
84
|
_verify_static_expression(select_statement, context)
|
|
81
85
|
verify_restrictions(select_statement, context, from_tables)
|
|
82
86
|
|
|
@@ -124,10 +128,6 @@ def _has_static_expression(context: VerificationContext, exp: expr.Expression) -
|
|
|
124
128
|
|
|
125
129
|
|
|
126
130
|
def _verify_query_statement(query_statement: expr.Query, context: VerificationContext):
|
|
127
|
-
for select in query_statement.selects:
|
|
128
|
-
for sub in select.find_all(expr.Subquery):
|
|
129
|
-
_add_table_alias(sub, context)
|
|
130
|
-
_verify_query_statement(sub.this, context)
|
|
131
131
|
if isinstance(query_statement, expr.Union):
|
|
132
132
|
_verify_query_statement(query_statement.left, context)
|
|
133
133
|
_verify_query_statement(query_statement.right, context)
|
|
@@ -135,10 +135,16 @@ def _verify_query_statement(query_statement: expr.Query, context: VerificationCo
|
|
|
135
135
|
for cte in query_statement.ctes:
|
|
136
136
|
_add_table_alias(cte, context)
|
|
137
137
|
_verify_query_statement(cte.this, context)
|
|
138
|
-
|
|
139
|
-
if
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
from_tables = _verify_from_tables(context, query_statement)
|
|
139
|
+
if not context.can_fix:
|
|
140
|
+
return query_statement
|
|
141
|
+
_verify_select_clause(context, query_statement, from_tables)
|
|
142
|
+
_verify_where_clause(context, query_statement, from_tables)
|
|
143
|
+
_verify_order_by_clause(context, query_statement)
|
|
144
|
+
return query_statement
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _verify_from_tables(context, query_statement):
|
|
142
148
|
from_tables = _get_from_clause_tables(query_statement, context)
|
|
143
149
|
for t in from_tables:
|
|
144
150
|
found = False
|
|
@@ -147,11 +153,14 @@ def _verify_query_statement(query_statement: expr.Query, context: VerificationCo
|
|
|
147
153
|
found = True
|
|
148
154
|
if not found:
|
|
149
155
|
context.add_error(f"Table {t.name} is not allowed", False, 1)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
return from_tables
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _verify_order_by_clause(context, query_statement):
|
|
160
|
+
for exp_type in [expr.Order, expr.Offset]:
|
|
161
|
+
for exp in find_direct(query_statement, exp_type):
|
|
162
|
+
for sub in exp.find_all(expr.Subquery):
|
|
163
|
+
_verify_query_statement(sub.this, context)
|
|
155
164
|
|
|
156
165
|
|
|
157
166
|
def _verify_select_clause(
|
|
@@ -159,6 +168,10 @@ def _verify_select_clause(
|
|
|
159
168
|
select_clause: expr.Query,
|
|
160
169
|
from_tables: List[expr.Table],
|
|
161
170
|
):
|
|
171
|
+
for select in select_clause.selects:
|
|
172
|
+
for sub in select.find_all(expr.Subquery):
|
|
173
|
+
_add_table_alias(sub, context)
|
|
174
|
+
_verify_query_statement(sub.this, context)
|
|
162
175
|
to_remove = []
|
|
163
176
|
for e in select_clause.expressions:
|
|
164
177
|
if not _verify_select_clause_element(from_tables, context, e):
|
|
@@ -3,7 +3,6 @@ import os
|
|
|
3
3
|
import sqlite3
|
|
4
4
|
from sqlite3 import Connection
|
|
5
5
|
from typing import Set, Generator
|
|
6
|
-
|
|
7
6
|
import pytest
|
|
8
7
|
from sql_data_guard import verify_sql
|
|
9
8
|
from conftest import verify_sql_test
|
|
@@ -25,7 +24,7 @@ class TestSQLJoins:
|
|
|
25
24
|
"column": "price",
|
|
26
25
|
"value": 100,
|
|
27
26
|
"operation": ">=",
|
|
28
|
-
}
|
|
27
|
+
}
|
|
29
28
|
],
|
|
30
29
|
},
|
|
31
30
|
{
|
|
@@ -176,7 +175,7 @@ class TestSQLJoins:
|
|
|
176
175
|
cnn=cnn,
|
|
177
176
|
data=[
|
|
178
177
|
{
|
|
179
|
-
"Product2",
|
|
178
|
+
"Product2",
|
|
180
179
|
},
|
|
181
180
|
{
|
|
182
181
|
"Product4", # Product4 has price = 100
|
|
@@ -537,45 +536,19 @@ class TestSQLOrderDateBetweenRestrictions:
|
|
|
537
536
|
"SELECT prod_id, prod_name, price FROM products WHERE price BETWEEN 80 AND 150",
|
|
538
537
|
config,
|
|
539
538
|
cnn=cnn,
|
|
540
|
-
errors={
|
|
541
|
-
"Missing restriction for table: products column: price value: [80, 150]"
|
|
542
|
-
},
|
|
543
|
-
fix="SELECT prod_id, prod_name, price FROM products WHERE (price BETWEEN 80 AND "
|
|
544
|
-
"150) AND price BETWEEN 80 AND 150",
|
|
545
539
|
data=[
|
|
546
|
-
(1, "Product A", 120
|
|
547
|
-
(2, "Product B", 80
|
|
548
|
-
(3, "Product C", 150
|
|
540
|
+
(1, "Product A", 120),
|
|
541
|
+
(2, "Product B", 80),
|
|
542
|
+
(3, "Product C", 150),
|
|
549
543
|
],
|
|
550
544
|
)
|
|
551
545
|
|
|
552
|
-
def test_price_not_between_restriction(self, cnn, config):
|
|
553
|
-
sql_query = """
|
|
554
|
-
SELECT prod_id, prod_name, price
|
|
555
|
-
FROM products
|
|
556
|
-
WHERE price NOT BETWEEN 80 AND 150
|
|
557
|
-
"""
|
|
558
|
-
verify_sql_test(
|
|
559
|
-
sql_query,
|
|
560
|
-
config,
|
|
561
|
-
errors={
|
|
562
|
-
"Missing restriction for table: products column: price value: [80, 150]"
|
|
563
|
-
},
|
|
564
|
-
fix="SELECT prod_id, prod_name, price FROM products WHERE (NOT price BETWEEN 80 AND 150) AND price BETWEEN 80 AND 150",
|
|
565
|
-
cnn=cnn,
|
|
566
|
-
data=[],
|
|
567
|
-
)
|
|
568
|
-
|
|
569
546
|
def test_count_products_within_price_range(self, cnn, config):
|
|
570
547
|
verify_sql_test(
|
|
571
548
|
"SELECT COUNT(*) FROM products WHERE price BETWEEN 80 AND 150",
|
|
572
549
|
config,
|
|
573
|
-
errors={
|
|
574
|
-
"Missing restriction for table: products column: price value: [80, 150]"
|
|
575
|
-
},
|
|
576
|
-
fix="SELECT COUNT(*) FROM products WHERE (price BETWEEN 80 AND 150) AND price BETWEEN 80 AND 150",
|
|
577
550
|
cnn=cnn,
|
|
578
|
-
data=[(3,)], # Expecting 3 products
|
|
551
|
+
data=[(3,)], # Expecting 3 products
|
|
579
552
|
)
|
|
580
553
|
|
|
581
554
|
def test_left_join_products_with_orders(self, cnn, config):
|
|
@@ -585,12 +558,6 @@ class TestSQLOrderDateBetweenRestrictions:
|
|
|
585
558
|
LEFT JOIN orders o ON p.prod_id = o.prod_id
|
|
586
559
|
WHERE p.price BETWEEN 80 AND 150""",
|
|
587
560
|
config,
|
|
588
|
-
errors={
|
|
589
|
-
"Missing restriction for table: products column: p.price value: [80, 150]"
|
|
590
|
-
},
|
|
591
|
-
fix="SELECT p.prod_name, o.order_id, COALESCE(o.quantity, 0) AS quantity FROM "
|
|
592
|
-
"products AS p LEFT JOIN orders AS o ON p.prod_id = o.prod_id WHERE (p.price "
|
|
593
|
-
"BETWEEN 80 AND 150) AND p.price BETWEEN 80 AND 150",
|
|
594
561
|
cnn=cnn,
|
|
595
562
|
data=[("Product A", 1, 10), ("Product B", 2, 5), ("Product C", 3, 7)],
|
|
596
563
|
)
|
|
@@ -599,11 +566,11 @@ class TestSQLOrderDateBetweenRestrictions:
|
|
|
599
566
|
verify_sql_test(
|
|
600
567
|
"SELECT prod_name, price FROM products WHERE price < 90",
|
|
601
568
|
config,
|
|
569
|
+
cnn=cnn,
|
|
602
570
|
errors={
|
|
603
571
|
"Missing restriction for table: products column: price value: [80, 150]"
|
|
604
572
|
},
|
|
605
573
|
fix="SELECT prod_name, price FROM products WHERE (price < 90) AND price BETWEEN 80 AND 150",
|
|
606
|
-
cnn=cnn,
|
|
607
574
|
data=[("Product B", 80)],
|
|
608
575
|
)
|
|
609
576
|
|
|
@@ -613,10 +580,6 @@ class TestSQLOrderDateBetweenRestrictions:
|
|
|
613
580
|
"FROM products "
|
|
614
581
|
"WHERE price BETWEEN 80 AND 150 AND prod_category = 'CategoryA'",
|
|
615
582
|
config,
|
|
616
|
-
errors={
|
|
617
|
-
"Missing restriction for table: products column: price value: [80, 150]"
|
|
618
|
-
},
|
|
619
|
-
fix="SELECT prod_id, prod_name, price, prod_category FROM products WHERE (price BETWEEN 80 AND 150 AND prod_category = 'CategoryA') AND price BETWEEN 80 AND 150",
|
|
620
583
|
cnn=cnn,
|
|
621
584
|
data=[
|
|
622
585
|
(1, "Product A", 120, "CategoryA"),
|
|
@@ -631,10 +594,6 @@ class TestSQLOrderDateBetweenRestrictions:
|
|
|
631
594
|
"WHERE price BETWEEN 90 AND 125 "
|
|
632
595
|
"GROUP BY prod_category",
|
|
633
596
|
config,
|
|
634
|
-
errors={
|
|
635
|
-
"Missing restriction for table: products column: price value: [80, 150]"
|
|
636
|
-
},
|
|
637
|
-
fix="SELECT COUNT(prod_id) AS product_count, prod_category FROM products WHERE (price BETWEEN 90 AND 125) AND price BETWEEN 80 AND 150 GROUP BY prod_category",
|
|
638
597
|
cnn=cnn,
|
|
639
598
|
data=[(1, "CategoryA")], # Only Product A fits in this range
|
|
640
599
|
)
|
|
@@ -646,11 +605,6 @@ class TestSQLOrderDateBetweenRestrictions:
|
|
|
646
605
|
"INNER JOIN products p ON o.prod_id = p.prod_id "
|
|
647
606
|
"WHERE p.price BETWEEN 90 AND 150",
|
|
648
607
|
config,
|
|
649
|
-
errors={
|
|
650
|
-
"Missing restriction for table: products column: p.price value: [80, 150]"
|
|
651
|
-
},
|
|
652
|
-
fix="SELECT o.order_id, p.prod_name, p.price FROM orders AS o INNER JOIN products AS p "
|
|
653
|
-
"ON o.prod_id = p.prod_id WHERE (p.price BETWEEN 90 AND 150) AND p.price BETWEEN 80 AND 150",
|
|
654
608
|
cnn=cnn,
|
|
655
609
|
data=[
|
|
656
610
|
(1, "Product A", 120),
|
|
@@ -658,31 +612,122 @@ class TestSQLOrderDateBetweenRestrictions:
|
|
|
658
612
|
],
|
|
659
613
|
)
|
|
660
614
|
|
|
661
|
-
def
|
|
615
|
+
def test_existent_product_between(self, cnn, config):
|
|
662
616
|
verify_sql_test(
|
|
663
617
|
"SELECT prod_id, prod_name, price "
|
|
664
618
|
"FROM products "
|
|
665
|
-
"WHERE price BETWEEN
|
|
619
|
+
"WHERE price BETWEEN 100 AND 140",
|
|
666
620
|
config,
|
|
667
|
-
errors={
|
|
668
|
-
"Missing restriction for table: products column: price value: [80, 150]"
|
|
669
|
-
},
|
|
670
|
-
fix="SELECT prod_id, prod_name, price FROM products WHERE (price BETWEEN 200 AND 300) AND price BETWEEN 80 AND 150",
|
|
671
621
|
cnn=cnn,
|
|
672
|
-
data=[], # No products in this range
|
|
622
|
+
data=[(1, "Product A", 120.0)], # No products in this range
|
|
673
623
|
)
|
|
674
624
|
|
|
675
|
-
def
|
|
625
|
+
def test_group_by_having_price(self, cnn, config):
|
|
676
626
|
verify_sql_test(
|
|
677
|
-
"SELECT prod_category,
|
|
627
|
+
"SELECT prod_category, price "
|
|
678
628
|
"FROM products "
|
|
679
|
-
"
|
|
680
|
-
"
|
|
629
|
+
"WHERE price > 100 "
|
|
630
|
+
"GROUP BY prod_category",
|
|
631
|
+
config,
|
|
632
|
+
{"Missing restriction for table: products column: price value: [80, 150]"},
|
|
633
|
+
"SELECT prod_category, price FROM products WHERE (price > 100) AND price BETWEEN 80 AND 150 GROUP BY prod_category",
|
|
634
|
+
cnn=cnn,
|
|
635
|
+
data=[("CategoryA", 120)], # Products in CategoryA with price > 100
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
class TestSQLOrderRestrictions:
|
|
640
|
+
|
|
641
|
+
@pytest.fixture(scope="class")
|
|
642
|
+
def cnn(self):
|
|
643
|
+
with sqlite3.connect(":memory:") as conn:
|
|
644
|
+
# Create orders table
|
|
645
|
+
conn.execute(
|
|
646
|
+
"""
|
|
647
|
+
CREATE TABLE orders (
|
|
648
|
+
id INTEGER,
|
|
649
|
+
product_name TEXT,
|
|
650
|
+
account_id INTEGER
|
|
651
|
+
)"""
|
|
652
|
+
)
|
|
653
|
+
# Insert sample data into orders table
|
|
654
|
+
|
|
655
|
+
conn.execute(
|
|
656
|
+
"""INSERT INTO orders (id, product_name, account_id)
|
|
657
|
+
VALUES
|
|
658
|
+
(1, 'Product A', 123),
|
|
659
|
+
(2, 'Product B', 124),
|
|
660
|
+
(3, "Product C", 125)
|
|
661
|
+
"""
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
yield conn
|
|
665
|
+
|
|
666
|
+
@pytest.fixture(scope="class")
|
|
667
|
+
def config(self):
|
|
668
|
+
# Assuming self._ALLOWED_ACCOUNT_ID is defined
|
|
669
|
+
self._ALLOWED_ACCOUNT_ID = 124 # Example value for the allowed account ID
|
|
670
|
+
self._TABLE_NAME = "orders" # Define table name
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
"tables": [
|
|
674
|
+
{
|
|
675
|
+
"table_name": self._TABLE_NAME,
|
|
676
|
+
"columns": ["id", "product_name", "account_id"],
|
|
677
|
+
"restrictions": [
|
|
678
|
+
{
|
|
679
|
+
"column": "account_id",
|
|
680
|
+
"value": [
|
|
681
|
+
self._ALLOWED_ACCOUNT_ID,
|
|
682
|
+
],
|
|
683
|
+
}, # Restriction without IN
|
|
684
|
+
],
|
|
685
|
+
}
|
|
686
|
+
]
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
def test_in_operator_with_restriction_(self, config, cnn):
|
|
690
|
+
sql = """SELECT product_name FROM orders WHERE account_id IN (123, 124, 125)"""
|
|
691
|
+
|
|
692
|
+
# Modify the config to handle "value" as "values" just for this specific test case
|
|
693
|
+
for table in config["tables"]:
|
|
694
|
+
for restriction in table["restrictions"]:
|
|
695
|
+
if "value" in restriction:
|
|
696
|
+
# If 'value' is present, convert it to 'values'
|
|
697
|
+
restriction["values"] = restriction["value"]
|
|
698
|
+
del restriction["value"] # Remove 'value' key
|
|
699
|
+
|
|
700
|
+
# Run the verify_sql_test function with the defined SQL query and configuration
|
|
701
|
+
verify_sql_test(
|
|
702
|
+
sql,
|
|
703
|
+
config,
|
|
704
|
+
errors=set(), # No errors expected as the restriction matches the IN clause
|
|
705
|
+
fix=None, # No fix should be needed
|
|
706
|
+
cnn=cnn,
|
|
707
|
+
data=[("Product A",), ("Product B",), ("Product C",)],
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
def test_id_greater_than_122_should_return_error(self, config):
|
|
711
|
+
"""Test case for ensuring that queries with id >= 123 are invalid"""
|
|
712
|
+
|
|
713
|
+
# SQL query to test
|
|
714
|
+
sql_query = "SELECT id, product_name FROM orders WHERE id >= 123"
|
|
715
|
+
|
|
716
|
+
# Run the verify_sql_test function to validate the query against the restrictions
|
|
717
|
+
res = verify_sql(sql_query, config)
|
|
718
|
+
|
|
719
|
+
# Assert that the query is not allowed (should return an error)
|
|
720
|
+
assert res["allowed"] is False, res
|
|
721
|
+
|
|
722
|
+
def test_id_greater_return_error(self, config, cnn):
|
|
723
|
+
|
|
724
|
+
verify_sql_test(
|
|
725
|
+
"SELECT id, product_name FROM orders WHERE id >= 123",
|
|
681
726
|
config,
|
|
727
|
+
cnn=cnn,
|
|
682
728
|
errors={
|
|
683
|
-
"Missing restriction for table:
|
|
729
|
+
"Missing restriction for table: orders column: account_id value: [124]"
|
|
684
730
|
},
|
|
685
|
-
fix="SELECT
|
|
686
|
-
|
|
687
|
-
data=[("CategoryA", 135)], # Avg of 120 and 150 in CategoryA
|
|
731
|
+
fix="SELECT id, product_name FROM orders WHERE (id >= 123) AND account_id = 124",
|
|
732
|
+
data=[],
|
|
688
733
|
)
|
|
@@ -28,7 +28,7 @@ class TestInvalidQueries:
|
|
|
28
28
|
)"""
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
# Insert values into products1
|
|
31
|
+
# Insert values into products1
|
|
32
32
|
conn.execute(
|
|
33
33
|
"INSERT INTO products1 VALUES (324, 'prod1', 'delivered', 'granted', '27-02-2025', 'c1')"
|
|
34
34
|
)
|
|
@@ -678,10 +678,6 @@ class TestMultipleRestriction:
|
|
|
678
678
|
"SELECT id FROM products1 WHERE id IN (324)",
|
|
679
679
|
config,
|
|
680
680
|
cnn=cnn,
|
|
681
|
-
errors={
|
|
682
|
-
"Missing restriction for table: products1 column: id value: [324, 224]"
|
|
683
|
-
},
|
|
684
|
-
fix="SELECT id FROM products1 WHERE (id IN (324)) AND id IN (324, 224)",
|
|
685
681
|
data=[["324"]],
|
|
686
682
|
)
|
|
687
683
|
|
|
@@ -701,10 +697,6 @@ class TestMultipleRestriction:
|
|
|
701
697
|
verify_sql_test(
|
|
702
698
|
"""SELECT id FROM products1 WHERE id IN (324, 224)""",
|
|
703
699
|
config,
|
|
704
|
-
errors={
|
|
705
|
-
"Missing restriction for table: products1 column: id value: [324, 224]"
|
|
706
|
-
},
|
|
707
|
-
fix="SELECT id FROM products1 WHERE (id IN (324, 224)) AND id IN (324, 224)",
|
|
708
700
|
cnn=cnn,
|
|
709
701
|
data=[["324"]],
|
|
710
702
|
)
|
|
@@ -713,60 +705,37 @@ class TestMultipleRestriction:
|
|
|
713
705
|
verify_sql_test(
|
|
714
706
|
"""SELECT id FROM products1 WHERE id IN (324, 233)""",
|
|
715
707
|
config,
|
|
716
|
-
errors={
|
|
717
|
-
"Missing restriction for table: products1 column: id value: [324, 224]"
|
|
718
|
-
},
|
|
719
|
-
fix="SELECT id FROM products1 WHERE (id IN (324, 233)) AND id IN (324, 224)",
|
|
720
708
|
cnn=cnn,
|
|
721
709
|
data=[["324"]],
|
|
722
710
|
)
|
|
723
711
|
|
|
724
712
|
def test_in_operator_with_or(self, config, cnn):
|
|
725
713
|
verify_sql_test(
|
|
726
|
-
"""SELECT id FROM products1 WHERE
|
|
714
|
+
"""SELECT id FROM products1 WHERE id IN (324, 224) OR prod_name = 'prod3'""",
|
|
727
715
|
config,
|
|
728
|
-
cnn=cnn,
|
|
729
716
|
errors={
|
|
730
717
|
"Missing restriction for table: products1 column: id value: [324, 224]"
|
|
731
718
|
},
|
|
732
|
-
fix="
|
|
733
|
-
|
|
734
|
-
)
|
|
735
|
-
|
|
736
|
-
def test_not_in_operator(self, config, cnn):
|
|
737
|
-
verify_sql_test(
|
|
738
|
-
"""SELECT id FROM products1 WHERE (NOT id IN (324, 224))""",
|
|
739
|
-
config,
|
|
719
|
+
fix="SELECT id FROM products1 WHERE (id IN (324, 224) OR prod_name = 'prod3') AND "
|
|
720
|
+
"id IN (324, 224)",
|
|
740
721
|
cnn=cnn,
|
|
741
|
-
|
|
742
|
-
"
|
|
743
|
-
|
|
744
|
-
fix="SELECT id FROM products1 WHERE ((NOT id IN (324, 224))) AND id IN (324, 224)",
|
|
745
|
-
data=[],
|
|
722
|
+
data=[
|
|
723
|
+
("324",),
|
|
724
|
+
],
|
|
746
725
|
)
|
|
747
726
|
|
|
748
727
|
def test_in_operator_with_numeric_values(self, config, cnn):
|
|
749
728
|
verify_sql_test(
|
|
750
|
-
"""SELECT id FROM products2 WHERE
|
|
729
|
+
"""SELECT id FROM products2 WHERE id IN (324, 224) AND category IN ('electronics', 'furniture')""",
|
|
751
730
|
config,
|
|
752
731
|
cnn=cnn,
|
|
753
|
-
errors={
|
|
754
|
-
"Missing restriction for table: products2 column: id value: [324, 224]"
|
|
755
|
-
},
|
|
756
|
-
fix="SELECT id FROM products2 WHERE ((id IN (324, 224) AND category IN "
|
|
757
|
-
"('electronics', 'furniture'))) AND id IN (324, 224)",
|
|
758
732
|
data=[],
|
|
759
733
|
)
|
|
760
734
|
|
|
761
735
|
def test_in_operator_with_between(self, config, cnn):
|
|
762
736
|
verify_sql_test(
|
|
763
|
-
"""SELECT id FROM products1 WHERE
|
|
737
|
+
"""SELECT id FROM products1 WHERE id in (324, 224) AND date BETWEEN '2024-01-01' AND '2025-01-01' """,
|
|
764
738
|
config,
|
|
765
739
|
cnn=cnn,
|
|
766
|
-
errors={
|
|
767
|
-
"Missing restriction for table: products1 column: id value: [324, 224]"
|
|
768
|
-
},
|
|
769
|
-
fix="SELECT id FROM products1 WHERE ((id IN ('324', '224') AND date BETWEEN "
|
|
770
|
-
"'2024-01-01' AND '2025-01-01')) AND id IN (324, 224)",
|
|
771
740
|
data=[],
|
|
772
741
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard/rest/sql_data_guard_rest.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sql_data_guard-0.1.3 → sql_data_guard-0.1.4}/src/sql_data_guard.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|