sql-testing-tools 0.2.11__tar.gz → 0.2.13__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_testing_tools-0.2.11/sql_testing_tools.egg-info → sql_testing_tools-0.2.13}/PKG-INFO +9 -1
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/README.md +8 -0
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/setup.py +1 -1
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools/BaseAccess.py +39 -35
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools/Helper.py +101 -87
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools/TokenProcessing.py +59 -95
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13/sql_testing_tools.egg-info}/PKG-INFO +9 -1
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/LICENSE +0 -0
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/setup.cfg +0 -0
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools/__init__.py +0 -0
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools.egg-info/SOURCES.txt +0 -0
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools.egg-info/dependency_links.txt +0 -0
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools.egg-info/requires.txt +0 -0
- {sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sql_testing_tools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
4
4
|
Description-Content-Type: text/markdown
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Dist: sqlparse>=0.5.1
|
|
@@ -135,6 +135,14 @@ res = checkEquality()
|
|
|
135
135
|
|
|
136
136
|
### Changelog
|
|
137
137
|
|
|
138
|
+
##### V 0.2.13
|
|
139
|
+
- Acccept `COUNT(colname)`, too (instead of just `COUNT(*)`)
|
|
140
|
+
|
|
141
|
+
##### V 0.2.12
|
|
142
|
+
- fixed bug when having only one colum selected
|
|
143
|
+
- fixed bug with nested conditions
|
|
144
|
+
- fixed memory problem with huge cross products (used to lead to build failed)
|
|
145
|
+
|
|
138
146
|
##### V 0.2.11
|
|
139
147
|
- fixed irrelevant equality order if both sides aren't literals
|
|
140
148
|
|
|
@@ -123,6 +123,14 @@ res = checkEquality()
|
|
|
123
123
|
|
|
124
124
|
### Changelog
|
|
125
125
|
|
|
126
|
+
##### V 0.2.13
|
|
127
|
+
- Acccept `COUNT(colname)`, too (instead of just `COUNT(*)`)
|
|
128
|
+
|
|
129
|
+
##### V 0.2.12
|
|
130
|
+
- fixed bug when having only one colum selected
|
|
131
|
+
- fixed bug with nested conditions
|
|
132
|
+
- fixed memory problem with huge cross products (used to lead to build failed)
|
|
133
|
+
|
|
126
134
|
##### V 0.2.11
|
|
127
135
|
- fixed irrelevant equality order if both sides aren't literals
|
|
128
136
|
|
|
@@ -26,22 +26,23 @@ dbMap = {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
__db=""
|
|
29
|
+
dbiu = 'dbiu.'
|
|
29
30
|
|
|
30
31
|
def setDBName(database):
|
|
31
32
|
global __db
|
|
32
33
|
|
|
33
|
-
if isinstance(database, str) and database.startswith(
|
|
34
|
-
|
|
34
|
+
if isinstance(database, str) and database.startswith(dbiu):
|
|
35
|
+
db_version = dbMap.get(database)
|
|
35
36
|
elif isinstance(database, int):
|
|
36
|
-
|
|
37
|
-
database =
|
|
37
|
+
db_version = database
|
|
38
|
+
database = dbiu+str(database)
|
|
38
39
|
elif isinstance(database, str):
|
|
39
|
-
|
|
40
|
+
db_version = None
|
|
40
41
|
else:
|
|
41
42
|
raise Exception("Invalid database selected")
|
|
42
43
|
|
|
43
|
-
if(
|
|
44
|
-
os.system('pip install dbiu_databases=='+str(
|
|
44
|
+
if(db_version is not None):
|
|
45
|
+
os.system('pip install dbiu_databases=='+str(db_version))
|
|
45
46
|
try:
|
|
46
47
|
import dbiu_databases
|
|
47
48
|
except ImportError:
|
|
@@ -51,7 +52,7 @@ def setDBName(database):
|
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
def run(sql: str):
|
|
54
|
-
if __db.startswith(
|
|
55
|
+
if __db.startswith(dbiu) and not __db.endswith(".db"):
|
|
55
56
|
with importlib.resources.path('dbiu_databases', "base.db") as db_path:
|
|
56
57
|
return __run(sql, db_path)
|
|
57
58
|
else:
|
|
@@ -64,7 +65,7 @@ def __run(sql: str, db: str):
|
|
|
64
65
|
con.commit()
|
|
65
66
|
return cur
|
|
66
67
|
|
|
67
|
-
def runFromFile(path: str):
|
|
68
|
+
def runFromFile(path: str, limit: int = None):
|
|
68
69
|
sql = getSQLFromFile(path)
|
|
69
70
|
if not sql:
|
|
70
71
|
raise Exception("\nSQL-Datei ist leer. Aufgabe wurde noch nicht bearbeitet.")
|
|
@@ -79,15 +80,19 @@ def runFromFile(path: str):
|
|
|
79
80
|
raise Exception(f"\n\nSyntax-Fehler in der SQL-Abfrage:\n{str(e)}")
|
|
80
81
|
|
|
81
82
|
headers = [h[0] for h in res.description] if res.description else []
|
|
82
|
-
|
|
83
|
+
if limit:
|
|
84
|
+
rows = res.fetchmany(limit)
|
|
85
|
+
else:
|
|
86
|
+
rows = res.fetchall() if res else []
|
|
83
87
|
|
|
84
88
|
return headers, rows
|
|
85
89
|
|
|
86
90
|
|
|
87
|
-
def runAndGetStringTable_fromFile(path: str, count: int = 5,
|
|
91
|
+
def runAndGetStringTable_fromFile(path: str, count: int = 5, max_line_length: int = 85):
|
|
88
92
|
try:
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
limit = 1000
|
|
94
|
+
headers, rows = runFromFile(path, limit)
|
|
95
|
+
result_count = len(rows)
|
|
91
96
|
rows = rows[:count]
|
|
92
97
|
s = ""
|
|
93
98
|
|
|
@@ -104,52 +109,51 @@ def runAndGetStringTable_fromFile(path: str, count: int = 5, maxLineLength: int
|
|
|
104
109
|
matrix[col].append(rows[row][col])
|
|
105
110
|
|
|
106
111
|
for col in range(len(matrix)):
|
|
107
|
-
|
|
112
|
+
max_length = max([len(str(x)) for x in matrix[col]])
|
|
108
113
|
|
|
109
114
|
for row in range(0, len(matrix[col])):
|
|
110
|
-
matrix[col][row] = str(matrix[col][row]).ljust(
|
|
115
|
+
matrix[col][row] = str(matrix[col][row]).ljust(max_length)
|
|
111
116
|
|
|
112
|
-
|
|
117
|
+
normalized_rows = []
|
|
113
118
|
spacing = " "
|
|
114
|
-
|
|
119
|
+
spacing_length = len(spacing) * (len(matrix[0]) - 1)
|
|
115
120
|
|
|
116
|
-
|
|
121
|
+
line_length = sum([len(x[0]) for x in matrix]) + spacing_length
|
|
117
122
|
|
|
118
123
|
|
|
119
124
|
|
|
120
|
-
if
|
|
121
|
-
|
|
122
|
-
maxColLength = int(maxLineLength/len(matrix))
|
|
125
|
+
if line_length > max_line_length:
|
|
126
|
+
max_col_length = int(max_line_length/len(matrix))
|
|
123
127
|
|
|
124
128
|
for col in range(len(matrix)):
|
|
125
129
|
for row in range(0, len(matrix[col])):
|
|
126
|
-
|
|
127
|
-
if
|
|
128
|
-
matrix[col][row] = matrix[col][row][:
|
|
130
|
+
cut_content = len(matrix[col][row].strip()) > max_col_length
|
|
131
|
+
if cut_content:
|
|
132
|
+
matrix[col][row] = matrix[col][row][:max_col_length-2] + ".."
|
|
129
133
|
else:
|
|
130
|
-
matrix[col][row] = matrix[col][row][:
|
|
134
|
+
matrix[col][row] = matrix[col][row][:max_col_length]
|
|
131
135
|
|
|
132
136
|
|
|
133
137
|
for row in range(min(count+1, len(matrix[0]))):
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
normalized_rows.append(spacing.join([str(matrix[col][row]) for col in range(len(matrix))]))
|
|
139
|
+
normalized_rows.insert(1, len(normalized_rows[0])*"-")
|
|
136
140
|
|
|
137
|
-
s = "\n".join(
|
|
138
|
-
s += "\n" +
|
|
141
|
+
s = "\n".join(normalized_rows[:count+2])
|
|
142
|
+
s += "\n" + normalized_rows[1]
|
|
139
143
|
|
|
140
|
-
if (
|
|
141
|
-
|
|
144
|
+
if (result_count > count):
|
|
145
|
+
if result_count == limit:
|
|
146
|
+
s += "\n... mehr als " + str(limit) + " Zeilen"
|
|
147
|
+
else:
|
|
148
|
+
s += "\n... " + str(result_count - count) + " weitere Zeilen"
|
|
142
149
|
else:
|
|
143
150
|
s += "\n... keine weiteren Zeilen"
|
|
144
|
-
|
|
145
|
-
#longestLineInS = 85 # max([len(x.replace("\t", " ")) for x in s.split("\n")])
|
|
146
|
-
|
|
147
|
-
#s = "-" * longestLineInS + "\n" + s + "\n" + "-" * longestLineInS
|
|
148
151
|
s = "\n\nDiese Meldung sagt nichts über die Korrektheit der Abgabe aus!\nDie ersten " + str(
|
|
149
152
|
count) + " Zeilen des Ergebnisses der SQL-Abfrage:\n\n" + s
|
|
150
153
|
|
|
151
154
|
return s.replace("None", "NULL")
|
|
152
155
|
except Exception as ex:
|
|
156
|
+
print(ex)
|
|
153
157
|
raise ex
|
|
154
158
|
|
|
155
159
|
def getSQLFromFile(path: str):
|
|
@@ -15,26 +15,37 @@ from sqlparse.tokens import Keyword, DML, Name, Wildcard
|
|
|
15
15
|
sql = ""
|
|
16
16
|
sol = ""
|
|
17
17
|
bd = None
|
|
18
|
+
kw_sel = 'SELECT'
|
|
19
|
+
kw_frm = 'FROM'
|
|
20
|
+
kw_by = ' BY'
|
|
21
|
+
kw_grp = 'GROUP' + kw_by
|
|
22
|
+
kw_ord = 'ORDER' + kw_by
|
|
23
|
+
kw_whr = 'WHERE'
|
|
24
|
+
kw_lim = 'LIMIT'
|
|
25
|
+
kw_hav = 'HAVING'
|
|
26
|
+
semcol = ';'
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def setup(sql_path,sol_path):
|
|
20
31
|
global sql, sol, bd
|
|
21
32
|
if(bd == None):
|
|
22
33
|
bd = Ba.getTableDict()
|
|
23
|
-
if(
|
|
24
|
-
sql = normalizeSQLQuery(Ba.getSQLFromFile(
|
|
25
|
-
if(
|
|
26
|
-
sol = normalizeSQLQuery(Ba.getSQLFromFile(
|
|
34
|
+
if(sql_path and sql_path != ""):
|
|
35
|
+
sql = normalizeSQLQuery(Ba.getSQLFromFile(sql_path), bd)
|
|
36
|
+
if(sol_path and sol_path != ""):
|
|
37
|
+
sol = normalizeSQLQuery(Ba.getSQLFromFile(sol_path), bd)
|
|
27
38
|
if(sql=='' or sol==''):
|
|
28
|
-
raise
|
|
39
|
+
raise AssertionError("\n\nSQL-Datei ist leer. Aufgabe wurde noch nicht bearbeitet.")
|
|
29
40
|
|
|
30
41
|
|
|
31
|
-
def normalizeSQLQuery(query,
|
|
42
|
+
def normalizeSQLQuery(query, base_dict):
|
|
32
43
|
try:
|
|
33
|
-
query = query.replace("\"", "'")
|
|
44
|
+
query = query.replace("\"", "'").replace("\n", " ").replace("\r", " ").replace("\t", " ").replace(" ", " ").strip()
|
|
34
45
|
parsed = sqlparse.parse(query)[0]
|
|
35
46
|
parsed.tokens = [token for token in parsed.tokens if not token.is_whitespace]
|
|
36
47
|
except Exception as e:
|
|
37
|
-
raise
|
|
48
|
+
raise AssertionError(f"\nFehler beim Parsen der SQL-Abfrage: {str(e)}")
|
|
38
49
|
|
|
39
50
|
formatted_query = []
|
|
40
51
|
alias_map = {}
|
|
@@ -43,16 +54,15 @@ def normalizeSQLQuery(query, baseDict):
|
|
|
43
54
|
for token in parsed.tokens:
|
|
44
55
|
if token.is_whitespace:
|
|
45
56
|
continue
|
|
46
|
-
elif token.ttype is Keyword and token.value.upper() ==
|
|
47
|
-
formatted_query.append(
|
|
48
|
-
|
|
49
|
-
elif isinstance(token, Identifier) and formatted_query and formatted_query[-1] == 'FROM':
|
|
57
|
+
elif token.ttype is Keyword and token.value.upper() == kw_frm:
|
|
58
|
+
formatted_query.append(kw_frm)
|
|
59
|
+
elif isinstance(token, Identifier) and formatted_query and formatted_query[-1] == kw_frm:
|
|
50
60
|
il = IdentifierList([token])
|
|
51
|
-
formatted_query.append(Tp._from(il, alias_map,
|
|
52
|
-
Tp._from(il, alias_map,
|
|
53
|
-
elif isinstance(token, IdentifierList) and formatted_query and formatted_query[-1] ==
|
|
54
|
-
formatted_query.append(Tp._from(token, alias_map,
|
|
55
|
-
Tp._from(token, alias_map,
|
|
61
|
+
formatted_query.append(Tp._from(il, alias_map, base_dict))
|
|
62
|
+
Tp._from(il, alias_map, base_dict)
|
|
63
|
+
elif isinstance(token, IdentifierList) and formatted_query and formatted_query[-1] == kw_frm:
|
|
64
|
+
formatted_query.append(Tp._from(token, alias_map, base_dict))
|
|
65
|
+
Tp._from(token, alias_map, base_dict)
|
|
56
66
|
|
|
57
67
|
formatted_query = []
|
|
58
68
|
|
|
@@ -60,40 +70,40 @@ def normalizeSQLQuery(query, baseDict):
|
|
|
60
70
|
for token in parsed.tokens:
|
|
61
71
|
if token.is_whitespace:
|
|
62
72
|
continue
|
|
63
|
-
elif token.ttype is DML and token.value.upper() ==
|
|
64
|
-
formatted_query.append(
|
|
65
|
-
elif token.ttype is Keyword and token.value.upper() ==
|
|
66
|
-
formatted_query.append(
|
|
67
|
-
elif token.ttype is Keyword and token.value.upper() ==
|
|
68
|
-
formatted_query.append(
|
|
69
|
-
elif token.ttype is Keyword and token.value.upper() ==
|
|
70
|
-
formatted_query.append(
|
|
71
|
-
elif token.ttype is Keyword and token.value.upper() ==
|
|
72
|
-
formatted_query.append(
|
|
73
|
-
elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] ==
|
|
74
|
-
formatted_query.append(Tp._from(token, alias_map,
|
|
75
|
-
elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] ==
|
|
76
|
-
formatted_query.append(Tp._groupby(token, alias_map,
|
|
77
|
-
elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] ==
|
|
78
|
-
formatted_query.append(Tp._orderby(token, alias_map,
|
|
73
|
+
elif token.ttype is DML and token.value.upper() == kw_sel:
|
|
74
|
+
formatted_query.append(kw_sel)
|
|
75
|
+
elif token.ttype is Keyword and token.value.upper() == kw_frm:
|
|
76
|
+
formatted_query.append(kw_frm)
|
|
77
|
+
elif token.ttype is Keyword and token.value.upper() == kw_grp:
|
|
78
|
+
formatted_query.append(kw_grp)
|
|
79
|
+
elif token.ttype is Keyword and token.value.upper() == kw_ord:
|
|
80
|
+
formatted_query.append(kw_ord)
|
|
81
|
+
elif token.ttype is Keyword and token.value.upper() == kw_lim:
|
|
82
|
+
formatted_query.append(kw_lim)
|
|
83
|
+
elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] == kw_frm:
|
|
84
|
+
formatted_query.append(Tp._from(token, alias_map, base_dict))
|
|
85
|
+
elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] == kw_grp:
|
|
86
|
+
formatted_query.append(Tp._groupby(token, alias_map, base_dict))
|
|
87
|
+
elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] == kw_ord:
|
|
88
|
+
formatted_query.append(Tp._orderby(token, alias_map, base_dict))
|
|
79
89
|
elif isinstance(token, Where):
|
|
80
|
-
formatted_query.append(
|
|
81
|
-
formatted_query.append(Tp._where(token, alias_map,
|
|
82
|
-
elif formatted_query and formatted_query[-1] ==
|
|
90
|
+
formatted_query.append(kw_whr)
|
|
91
|
+
formatted_query.append(Tp._where(token, alias_map, base_dict))
|
|
92
|
+
elif formatted_query and formatted_query[-1] == kw_sel and (isinstance(token, IdentifierList) or isinstance(token, Function) or isinstance(token, Identifier)):
|
|
83
93
|
if isinstance(token, Function):
|
|
84
94
|
token = IdentifierList([token])
|
|
85
|
-
formatted_query.append(Tp._select(token, alias_map,
|
|
86
|
-
elif formatted_query and formatted_query[-1] ==
|
|
95
|
+
formatted_query.append(Tp._select(token, alias_map, base_dict))
|
|
96
|
+
elif formatted_query and formatted_query[-1] == kw_lim:
|
|
87
97
|
formatted_query.append(Tp._limit(token))
|
|
88
98
|
else:
|
|
89
99
|
formatted_query.append(str(token))
|
|
90
100
|
|
|
91
101
|
return " ".join(formatted_query)
|
|
92
102
|
|
|
93
|
-
def
|
|
103
|
+
def find_table_for_column(data_dict, target_value, relevant_tables):
|
|
94
104
|
l = []
|
|
95
105
|
for key, value_list in data_dict.items():
|
|
96
|
-
if key.lower() in
|
|
106
|
+
if key.lower() in relevant_tables:
|
|
97
107
|
for sublist in value_list:
|
|
98
108
|
if sublist and sublist[0].lower() == target_value.lower():
|
|
99
109
|
l.append(key)
|
|
@@ -104,8 +114,8 @@ def findTableForColumn(data_dict, target_value, relevantTables):
|
|
|
104
114
|
l.append(key)
|
|
105
115
|
return l
|
|
106
116
|
|
|
107
|
-
def getTableScheme(table_name: str,
|
|
108
|
-
tab =
|
|
117
|
+
def getTableScheme(table_name: str, table_dict: dict):
|
|
118
|
+
tab = table_dict[table_name]
|
|
109
119
|
|
|
110
120
|
# Format the schema
|
|
111
121
|
schema = "(" + ",".join([f"{col[0]}:{col[1]}" for col in tab]) + ")"
|
|
@@ -118,18 +128,18 @@ def getCosetteKeyFromFile():
|
|
|
118
128
|
except FileNotFoundError:
|
|
119
129
|
return "NOKEY"
|
|
120
130
|
|
|
121
|
-
def buildAndSendCosetteRequest(
|
|
131
|
+
def buildAndSendCosetteRequest(base_dict, sql, sol):
|
|
122
132
|
|
|
123
133
|
err = ""
|
|
124
|
-
for
|
|
134
|
+
for _ in range(2):
|
|
125
135
|
try:
|
|
126
|
-
|
|
136
|
+
api_key=getCosetteKeyFromFile()
|
|
127
137
|
|
|
128
138
|
|
|
129
139
|
schema = ""
|
|
130
|
-
for tab in
|
|
131
|
-
schema += f"schema sch{tab}{getTableScheme(tab,
|
|
132
|
-
for tab in
|
|
140
|
+
for tab in base_dict.keys():
|
|
141
|
+
schema += f"schema sch{tab}{getTableScheme(tab, base_dict)};\n"
|
|
142
|
+
for tab in base_dict.keys():
|
|
133
143
|
schema += f"table {tab}(sch{tab});\n"
|
|
134
144
|
|
|
135
145
|
q1 = "query q1\n`"+sql+"`;\n"
|
|
@@ -138,29 +148,25 @@ def buildAndSendCosetteRequest(baseDict, sql, sol):
|
|
|
138
148
|
cosette = "-- random Kommentar\n" + schema + q1 + q2 + "verify q1 q2;\n"
|
|
139
149
|
print(cosette)
|
|
140
150
|
|
|
141
|
-
r = requests.post("https://demo.cosette.cs.washington.edu/solve",data={"api_key":
|
|
151
|
+
r = requests.post("https://demo.cosette.cs.washington.edu/solve",data={"api_key": api_key, "query": cosette}, verify=False)
|
|
142
152
|
|
|
143
153
|
print(r.text)
|
|
144
154
|
return (r.json()['result'],r.text)
|
|
145
|
-
#return r.json()['result']
|
|
146
|
-
|
|
147
155
|
except Exception as e:
|
|
148
156
|
err = str(e)
|
|
149
157
|
return ("ERR", err)
|
|
150
158
|
|
|
151
159
|
|
|
152
|
-
def
|
|
153
|
-
|
|
160
|
+
def check_keywords(start_word:str, end_words:list):
|
|
161
|
+
start_word = start_word.lower()
|
|
154
162
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if(startWord not in sol.lower() and startWord not in sql.lower()):
|
|
163
|
+
if(start_word not in sol.lower() and start_word not in sql.lower()):
|
|
158
164
|
return ""
|
|
159
165
|
|
|
160
|
-
if(
|
|
161
|
-
start = sql.lower().find(
|
|
166
|
+
if(start_word in sql.lower()):
|
|
167
|
+
start = sql.lower().find(start_word) + len(start_word)
|
|
162
168
|
end = -1
|
|
163
|
-
for kw in
|
|
169
|
+
for kw in end_words:
|
|
164
170
|
index = sql.lower().find(kw.lower(), start)
|
|
165
171
|
if -1 < index < end or end == -1:
|
|
166
172
|
end = index
|
|
@@ -168,12 +174,11 @@ def checkKeywords(startWord:str, endWords:list):
|
|
|
168
174
|
end = len(sql)
|
|
169
175
|
|
|
170
176
|
submission = str.strip(sql[start:end])
|
|
171
|
-
#print("'"+submission+"'")
|
|
172
177
|
|
|
173
|
-
start = sol.lower().find(
|
|
178
|
+
start = sol.lower().find(start_word) + len(start_word)
|
|
174
179
|
end = -1
|
|
175
180
|
|
|
176
|
-
for kw in
|
|
181
|
+
for kw in end_words:
|
|
177
182
|
index = sol.lower().find(kw.lower(), start)
|
|
178
183
|
if -1 < index < end or end == -1:
|
|
179
184
|
end = index
|
|
@@ -184,45 +189,54 @@ def checkKeywords(startWord:str, endWords:list):
|
|
|
184
189
|
|
|
185
190
|
if submission == solution:
|
|
186
191
|
return ""
|
|
187
|
-
return "Der '"+
|
|
192
|
+
return "Der '"+start_word+"' Teil der SQL-Abfrage ist nicht korrekt (oder nicht automatisch überprüfbar)."
|
|
188
193
|
|
|
189
194
|
|
|
190
|
-
def checkColumns(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return checkKeywords("SELECT ", ["FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", ";", "HAVING"])
|
|
195
|
+
def checkColumns(sql_path="", sol_path=""):
|
|
196
|
+
setup(sql_path, sol_path)
|
|
197
|
+
return check_keywords(kw_sel+" ", [kw_frm, kw_whr, kw_grp, kw_ord, kw_lim, semcol, kw_hav])
|
|
194
198
|
|
|
195
199
|
|
|
196
|
-
def checkTables(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return checkKeywords("FROM ", ["SELECT", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", ";", "HAVING"])
|
|
200
|
+
def checkTables(sql_path="", sol_path=""):
|
|
201
|
+
setup(sql_path, sol_path)
|
|
202
|
+
return check_keywords(kw_frm+" ", [kw_sel, kw_whr, kw_grp, kw_ord, kw_lim, semcol, kw_hav])
|
|
200
203
|
|
|
201
204
|
|
|
202
|
-
def checkCondition(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return checkKeywords("WHERE ", ["SELECT", "FROM", "GROUP BY", "ORDER BY", "LIMIT", ";", "HAVING"])
|
|
205
|
+
def checkCondition(sql_path="", sol_path=""):
|
|
206
|
+
setup(sql_path, sol_path)
|
|
207
|
+
return check_keywords(kw_whr+" ", [kw_sel, kw_frm, kw_grp, kw_ord, kw_lim, semcol, kw_hav])
|
|
206
208
|
|
|
207
209
|
|
|
208
|
-
def checkOrder(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return checkKeywords("ORDER BY ", ["SELECT", "WHERE", "GROUP BY", "FROM", "LIMIT", ";", "HAVING"])
|
|
210
|
+
def checkOrder(sql_path="", sol_path=""):
|
|
211
|
+
setup(sql_path, sol_path)
|
|
212
|
+
return check_keywords(kw_ord+" ", [kw_sel, kw_whr, kw_grp, kw_frm, kw_lim, semcol, kw_hav])
|
|
212
213
|
|
|
213
214
|
|
|
214
|
-
def checkGroup(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return checkKeywords("GROUP BY ", ["SELECT", "WHERE", "FROM BY", "ORDER BY", "LIMIT", ";", "HAVING"])
|
|
215
|
+
def checkGroup(sql_path="", sol_path=""):
|
|
216
|
+
setup(sql_path, sol_path)
|
|
217
|
+
return check_keywords(kw_grp+" ", [kw_sel, kw_whr, kw_grp, kw_ord, kw_lim, semcol, kw_hav])
|
|
218
218
|
|
|
219
219
|
|
|
220
|
-
def checkEquality(
|
|
221
|
-
|
|
222
|
-
setup(sqlPath, solPath)
|
|
220
|
+
def checkEquality(sql_path="", sol_path=""):
|
|
221
|
+
setup(sql_path, sol_path)
|
|
223
222
|
|
|
224
223
|
if sql == sol:
|
|
225
224
|
return ""
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
sql_build_fail = ""
|
|
228
|
+
sol_build_fail = ""
|
|
229
|
+
try:
|
|
230
|
+
Ba.runAndGetStringTable_fromFile(sql_path)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
sql_build_fail = str(e)
|
|
233
|
+
try:
|
|
234
|
+
Ba.runAndGetStringTable_fromFile(sol_path)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
sol_build_fail = str(e)
|
|
237
|
+
|
|
238
|
+
if(sql_build_fail != "" or sol_build_fail != ""):
|
|
239
|
+
return "\n\nFehler beim Ausführen der Abfrage. Vermutlich enthält die Abfrage Syntaxfehler:\n" + sql_build_fail + "\n" + sol_build_fail
|
|
226
240
|
|
|
227
241
|
result = buildAndSendCosetteRequest(bd, sql, sol)
|
|
228
242
|
|
|
@@ -11,12 +11,12 @@ except ImportError:
|
|
|
11
11
|
#####################################
|
|
12
12
|
#####################################
|
|
13
13
|
|
|
14
|
-
def _identifier(identifier, alias_map,
|
|
14
|
+
def _identifier(identifier, alias_map, base_dict: dict):
|
|
15
15
|
if isinstance(identifier, Identifier):
|
|
16
16
|
if identifier.get_real_name() and identifier.get_parent_name() and identifier.get_parent_name().lower() in alias_map.keys():
|
|
17
17
|
return f"{alias_map[identifier.get_parent_name().lower()].lower()}.{identifier.get_real_name().lower()}"
|
|
18
18
|
elif identifier.get_real_name():
|
|
19
|
-
tables = He.
|
|
19
|
+
tables = He.find_table_for_column(base_dict, identifier.get_real_name(), alias_map.keys())
|
|
20
20
|
if len(tables) == 1:
|
|
21
21
|
alias_map[tables[0].lower()] = tables[0]
|
|
22
22
|
return f"{tables[0].lower()}.{identifier.get_real_name().lower()}"
|
|
@@ -29,24 +29,33 @@ def _identifier(identifier, alias_map, baseDict: dict):
|
|
|
29
29
|
#####################################
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def _select(select, alias_map,
|
|
32
|
+
def _select(select, alias_map, base_dict: dict, insideFunction=False):
|
|
33
33
|
select_tokens = []
|
|
34
|
+
if isinstance(select, Identifier):
|
|
35
|
+
if insideFunction:
|
|
36
|
+
select_tokens.append(_identifier(select, alias_map, base_dict).lower())
|
|
37
|
+
else:
|
|
38
|
+
select_tokens.append(f"{_identifier(select, alias_map, base_dict).lower()} as {select.get_real_name().lower()}")
|
|
34
39
|
for token in select.tokens:
|
|
35
40
|
if isinstance(token, IdentifierList):
|
|
36
41
|
for identifier in token.get_identifiers():
|
|
37
|
-
select_tokens.append(f"{_identifier(identifier, alias_map,
|
|
42
|
+
select_tokens.append(f"{_identifier(identifier, alias_map, base_dict).lower()} as {identifier.get_real_name().lower()}")
|
|
38
43
|
elif isinstance(token, Identifier):
|
|
39
44
|
if insideFunction:
|
|
40
|
-
select_tokens.append(_identifier(token, alias_map,
|
|
45
|
+
select_tokens.append(_identifier(token, alias_map, base_dict).lower())
|
|
41
46
|
else:
|
|
42
|
-
select_tokens.append(f"{_identifier(token, alias_map,
|
|
47
|
+
select_tokens.append(f"{_identifier(token, alias_map, base_dict).lower()} as {token.get_real_name().lower()}")
|
|
43
48
|
elif isinstance(token, Function):
|
|
44
|
-
|
|
49
|
+
is_count = False
|
|
45
50
|
for par in token.tokens:
|
|
46
51
|
if isinstance(par, Identifier):
|
|
47
52
|
select_tokens.append(par.get_name().lower()+"(")
|
|
53
|
+
is_count = par.get_name().lower() == "count"
|
|
48
54
|
if isinstance(par, Parenthesis):
|
|
49
|
-
|
|
55
|
+
select_ident = _select(par, alias_map, base_dict, True)
|
|
56
|
+
if is_count and not any(tok.value.upper() == "DISTINCT" for tok in par.tokens):
|
|
57
|
+
select_ident = "*"
|
|
58
|
+
select_tokens[-1] += select_ident +") as funcResult"
|
|
50
59
|
elif token.ttype is Wildcard:
|
|
51
60
|
select_tokens.append("*")
|
|
52
61
|
else:
|
|
@@ -59,7 +68,7 @@ def _select(select, alias_map, baseDict: dict, insideFunction=False):
|
|
|
59
68
|
#####################################
|
|
60
69
|
|
|
61
70
|
|
|
62
|
-
def _from(from_, alias_map,
|
|
71
|
+
def _from(from_, alias_map, base_dict: dict):
|
|
63
72
|
from_tokens = []
|
|
64
73
|
if hasattr(from_, 'tokens'):
|
|
65
74
|
for token in from_.tokens:
|
|
@@ -83,32 +92,32 @@ def _from(from_, alias_map, baseDict: dict):
|
|
|
83
92
|
#####################################
|
|
84
93
|
|
|
85
94
|
|
|
86
|
-
def _groupby(groupby_, alias_map,
|
|
95
|
+
def _groupby(groupby_, alias_map, base_dict: dict):
|
|
87
96
|
groupby_tokens = []
|
|
88
97
|
if(isinstance(groupby_, Identifier)):
|
|
89
|
-
groupby_tokens.append(_identifier(groupby_, alias_map,
|
|
98
|
+
groupby_tokens.append(_identifier(groupby_, alias_map, base_dict).lower())
|
|
90
99
|
elif isinstance(groupby_, IdentifierList):
|
|
91
100
|
for identifier in groupby_.get_identifiers():
|
|
92
|
-
groupby_tokens.append(_identifier(identifier, alias_map,
|
|
101
|
+
groupby_tokens.append(_identifier(identifier, alias_map, base_dict).lower())
|
|
93
102
|
groupby_tokens.sort()
|
|
94
103
|
return ",".join(groupby_tokens)
|
|
95
104
|
|
|
96
|
-
def _orderby(orderby_, alias_map,
|
|
105
|
+
def _orderby(orderby_, alias_map, base_dict: dict):
|
|
97
106
|
orderby_tokens = []
|
|
98
107
|
if isinstance(orderby_, Identifier):
|
|
99
108
|
if len(orderby_.tokens) > 0 and orderby_.tokens[-1].ttype==Keyword.Order:
|
|
100
109
|
for tok in orderby_.tokens:
|
|
101
110
|
if isinstance(tok, Identifier):
|
|
102
|
-
orderby_tokens.append(_identifier(tok, alias_map,
|
|
111
|
+
orderby_tokens.append(_identifier(tok, alias_map, base_dict).lower())
|
|
103
112
|
if tok.ttype==Keyword.Order:
|
|
104
113
|
orderby_tokens[-1] += (" "+tok.value)
|
|
105
114
|
else:
|
|
106
|
-
orderby_tokens.append(_identifier(orderby_, alias_map,
|
|
115
|
+
orderby_tokens.append(_identifier(orderby_, alias_map, base_dict).lower())
|
|
107
116
|
orderby_tokens[-1] += " ASC"
|
|
108
117
|
elif isinstance(orderby_, IdentifierList):
|
|
109
118
|
for tok in orderby_.tokens:
|
|
110
119
|
if isinstance(tok, Identifier):
|
|
111
|
-
orderby_tokens.append(_identifier(tok, alias_map,
|
|
120
|
+
orderby_tokens.append(_identifier(tok, alias_map, base_dict).lower())
|
|
112
121
|
#elif isinstance(orderby_, Order):
|
|
113
122
|
# pass
|
|
114
123
|
# orderby_tokens.sort()
|
|
@@ -133,7 +142,7 @@ def replace_not_with_parenthesis(where_tokens):
|
|
|
133
142
|
i += 1
|
|
134
143
|
return new_tokens
|
|
135
144
|
|
|
136
|
-
def _condition(token, alias_map,
|
|
145
|
+
def _condition(token, alias_map, base_dict: dict):
|
|
137
146
|
left, operator, right = [t for t in token.tokens if not t.is_whitespace]
|
|
138
147
|
|
|
139
148
|
flipAllowed = True
|
|
@@ -158,8 +167,8 @@ def _condition(token, alias_map, baseDict: dict):
|
|
|
158
167
|
rightLiteral = True
|
|
159
168
|
flipAllowed = False
|
|
160
169
|
|
|
161
|
-
left = _identifier(left, alias_map,
|
|
162
|
-
right = _identifier(right, alias_map,
|
|
170
|
+
left = _identifier(left, alias_map, base_dict)
|
|
171
|
+
right = _identifier(right, alias_map, base_dict)
|
|
163
172
|
|
|
164
173
|
if operator.value.strip() in ("!=", "<>"):
|
|
165
174
|
return f"(NOT {left.lower()} LIKE {right})"
|
|
@@ -195,7 +204,7 @@ def is_value(token):
|
|
|
195
204
|
#####################################
|
|
196
205
|
|
|
197
206
|
|
|
198
|
-
def _paranthesis(parenthesis, alias_map,
|
|
207
|
+
def _paranthesis(parenthesis, alias_map, base_dict: dict):
|
|
199
208
|
toks = []
|
|
200
209
|
|
|
201
210
|
bracketsRequired = True
|
|
@@ -206,10 +215,10 @@ def _paranthesis(parenthesis, alias_map, baseDict: dict):
|
|
|
206
215
|
|
|
207
216
|
|
|
208
217
|
if(and_count==1 and or_count == 0):
|
|
209
|
-
val = _2element_par(parenthesis, alias_map,
|
|
218
|
+
val = _2element_par(parenthesis, alias_map, base_dict, "AND")
|
|
210
219
|
return val
|
|
211
220
|
elif(and_count==0 and or_count == 1):
|
|
212
|
-
val = _2element_par(parenthesis, alias_map,
|
|
221
|
+
val = _2element_par(parenthesis, alias_map, base_dict, "OR")
|
|
213
222
|
return val
|
|
214
223
|
|
|
215
224
|
|
|
@@ -219,9 +228,9 @@ def _paranthesis(parenthesis, alias_map, baseDict: dict):
|
|
|
219
228
|
|
|
220
229
|
for token in parenthesis.tokens:
|
|
221
230
|
if isinstance(token, Comparison):
|
|
222
|
-
toks.append(_condition(token, alias_map,
|
|
231
|
+
toks.append(_condition(token, alias_map, base_dict))
|
|
223
232
|
elif isinstance(token, Parenthesis):
|
|
224
|
-
toks.append(_paranthesis(token, alias_map,
|
|
233
|
+
toks.append(_paranthesis(token, alias_map, base_dict))
|
|
225
234
|
|
|
226
235
|
elif token.ttype is Keyword: #AND,OR
|
|
227
236
|
if token.value == "AND":
|
|
@@ -239,14 +248,14 @@ def _paranthesis(parenthesis, alias_map, baseDict: dict):
|
|
|
239
248
|
return " ".join(toks)
|
|
240
249
|
|
|
241
250
|
|
|
242
|
-
def _2element_par(parenthesis, alias_map,
|
|
251
|
+
def _2element_par(parenthesis, alias_map, base_dict: dict, keyword):
|
|
243
252
|
toks = []
|
|
244
253
|
|
|
245
254
|
for token in parenthesis.tokens:
|
|
246
255
|
if isinstance(token, Comparison):
|
|
247
|
-
toks.append(_condition(token, alias_map,
|
|
256
|
+
toks.append(_condition(token, alias_map, base_dict))
|
|
248
257
|
elif isinstance(token, Parenthesis):
|
|
249
|
-
toks.append(_paranthesis(token, alias_map,
|
|
258
|
+
toks.append(_paranthesis(token, alias_map, base_dict))
|
|
250
259
|
toks = toks.sort()
|
|
251
260
|
fill = (" "+keyword+" ")
|
|
252
261
|
return fill.join(toks)
|
|
@@ -263,13 +272,8 @@ def find_index_of_keyword(tokens, keyword):
|
|
|
263
272
|
return -1 # Wenn kein AND-Token gefunden wird
|
|
264
273
|
|
|
265
274
|
|
|
266
|
-
def _where(where, alias_map,
|
|
267
|
-
|
|
268
|
-
current_condition = []
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
isParanthesis = isinstance(where, Parenthesis)
|
|
275
|
+
def _where(where, alias_map, base_dict: dict):
|
|
276
|
+
is_paranthesis = isinstance(where, Parenthesis)
|
|
273
277
|
|
|
274
278
|
where_tokens = [token for token in where.tokens if (token.ttype is not Whitespace and token.ttype is not Newline) and token.value != "WHERE"]
|
|
275
279
|
where_tokens = replace_not_with_parenthesis(where_tokens)
|
|
@@ -282,11 +286,11 @@ def _where(where, alias_map, baseDict: dict):
|
|
|
282
286
|
|
|
283
287
|
# No AND / OR
|
|
284
288
|
if and_count + or_count == 0:
|
|
285
|
-
return _where_simpleCondition(where, alias_map,
|
|
289
|
+
return _where_simpleCondition(where, alias_map, base_dict)
|
|
286
290
|
|
|
287
291
|
# only ANDs / only ORs
|
|
288
292
|
elif and_count == 0 or or_count == 0:
|
|
289
|
-
return _where_sameStrengthKeywords(where_tokens, alias_map,
|
|
293
|
+
return _where_sameStrengthKeywords(where_tokens, alias_map, base_dict, " AND " if and_count > 0 else " OR ")
|
|
290
294
|
|
|
291
295
|
# ANDs and ORs
|
|
292
296
|
|
|
@@ -298,96 +302,56 @@ def _where(where, alias_map, baseDict: dict):
|
|
|
298
302
|
# two conditions on top level
|
|
299
303
|
if top_and_count + top_or_count == 1:
|
|
300
304
|
keyword = " AND " if top_and_count == 1 else " OR "
|
|
301
|
-
return _where_twoConditionsOnTopLevel(where, alias_map,
|
|
305
|
+
return _where_twoConditionsOnTopLevel(where, alias_map, base_dict, keyword, is_paranthesis)
|
|
302
306
|
else:
|
|
303
|
-
where_tokens = _where_addBracketsAroundAND(where, alias_map,
|
|
304
|
-
return _where(Where(where_tokens), alias_map,
|
|
307
|
+
where_tokens = _where_addBracketsAroundAND(where, alias_map, base_dict)
|
|
308
|
+
return _where(Where(where_tokens), alias_map, base_dict)
|
|
305
309
|
|
|
306
|
-
def _where_addBracketsAroundAND(where, alias_map,
|
|
310
|
+
def _where_addBracketsAroundAND(where, alias_map, base_dict: dict):
|
|
307
311
|
index = find_index_of_keyword(where.tokens, "AND")
|
|
308
312
|
par = Parenthesis(where.tokens[index-2:index+3])
|
|
309
313
|
where_tokens = where.tokens[:index-2] + [par] + where.tokens[index+3:]
|
|
310
314
|
return where_tokens
|
|
311
315
|
|
|
312
316
|
|
|
313
|
-
|
|
314
|
-
# for token in where.tokens:
|
|
315
|
-
# if token.is_whitespace or (token.ttype is Keyword and token.value.upper() == "WHERE"):
|
|
316
|
-
# continue
|
|
317
|
-
|
|
318
|
-
# if token.ttype is Keyword and token.value.upper() in ('AND', 'OR'):
|
|
319
|
-
# if current_condition:
|
|
320
|
-
# conditions.append(''.join(str(t) for t in current_condition).strip())
|
|
321
|
-
# current_condition = []
|
|
322
|
-
# conditions.append(token.value.upper())
|
|
323
|
-
# else:
|
|
324
|
-
# current_condition.append(token)
|
|
325
|
-
|
|
326
|
-
# if current_condition:
|
|
327
|
-
# conditions.append(''.join(str(t) for t in current_condition).strip())
|
|
328
|
-
|
|
329
|
-
# sorted_conditions = []
|
|
330
|
-
# current_group = []
|
|
331
|
-
# last_connector = ""
|
|
332
|
-
|
|
333
|
-
# if "OR" not in conditions:
|
|
334
|
-
# for condition in conditions:
|
|
335
|
-
# if condition == 'AND':
|
|
336
|
-
# pass
|
|
337
|
-
# else:
|
|
338
|
-
# current_group.append(condition)
|
|
339
|
-
|
|
340
|
-
# if current_group:
|
|
341
|
-
# sorted_conditions.extend(sorted(current_group))
|
|
342
|
-
|
|
343
|
-
# return " AND ".join(sorted_conditions)
|
|
344
|
-
# else:
|
|
345
|
-
# return " ".join(conditions)
|
|
346
|
-
|
|
347
|
-
# where_index = query.upper().find('WHERE')
|
|
348
|
-
|
|
349
|
-
# normalized_query = query[:where_index + 5] + ' ' + ' '.join(sorted_conditions)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
def _where_simpleCondition(where_tokens, alias_map, baseDict: dict):
|
|
317
|
+
def _where_simpleCondition(where_tokens, alias_map, base_dict: dict):
|
|
354
318
|
cond = []
|
|
355
319
|
for tok in where_tokens:
|
|
356
320
|
if tok.ttype == Keyword and tok.value.upper() == "NOT":
|
|
357
321
|
cond.append("NOT ")
|
|
358
322
|
if isinstance(tok, Comparison):
|
|
359
|
-
cond.append(_condition(tok, alias_map,
|
|
323
|
+
cond.append(_condition(tok, alias_map, base_dict))
|
|
360
324
|
elif isinstance(tok, Parenthesis):
|
|
361
325
|
for _tok in tok.tokens:
|
|
362
326
|
if isinstance(_tok, Comparison):
|
|
363
|
-
cond.append(_condition(_tok, alias_map,
|
|
327
|
+
cond.append(_condition(_tok, alias_map, base_dict))
|
|
364
328
|
elif isinstance(_tok, Parenthesis):
|
|
365
329
|
tok = _tok
|
|
366
330
|
return "".join(cond)
|
|
367
331
|
|
|
368
|
-
def _where_sameStrengthKeywords(where_tokens, alias_map,
|
|
332
|
+
def _where_sameStrengthKeywords(where_tokens, alias_map, base_dict: dict, keyword: str):
|
|
369
333
|
cond = []
|
|
370
334
|
for tok in where_tokens:
|
|
371
335
|
if isinstance(tok, Comparison):
|
|
372
|
-
cond.append(_where_simpleCondition([tok], alias_map,
|
|
336
|
+
cond.append(_where_simpleCondition([tok], alias_map, base_dict))
|
|
373
337
|
elif isinstance(tok, Parenthesis):
|
|
374
|
-
|
|
375
|
-
cond.append(("(" if
|
|
338
|
+
is_not = tok.tokens[0].ttype == Keyword and tok.tokens[0].value.upper() == "NOT"
|
|
339
|
+
cond.append(("(" if is_not else "") + _where(tok, alias_map, base_dict) + (")" if is_not else ""))
|
|
376
340
|
cond.sort()
|
|
377
341
|
return keyword.join(cond)
|
|
378
342
|
|
|
379
|
-
def _where_twoConditionsOnTopLevel(where, alias_map,
|
|
343
|
+
def _where_twoConditionsOnTopLevel(where, alias_map, base_dict: dict, keyword, is_paranthesis):
|
|
380
344
|
current_condition = []
|
|
381
345
|
for tok in where.tokens:
|
|
382
346
|
if isinstance(tok, Comparison):
|
|
383
|
-
current_condition.append(_condition(tok, alias_map,
|
|
347
|
+
current_condition.append(_condition(tok, alias_map, base_dict))
|
|
384
348
|
elif isinstance(tok, Parenthesis):
|
|
385
|
-
current_condition.append(_where(tok, alias_map,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
349
|
+
current_condition.append('(' + _where(tok, alias_map, base_dict) + ')')
|
|
350
|
+
current_condition.sort()
|
|
351
|
+
if keyword == " AND " or is_paranthesis:
|
|
352
|
+
return "(" + keyword.join(current_condition) + ")"
|
|
353
|
+
else:
|
|
354
|
+
return keyword.join(current_condition)
|
|
391
355
|
|
|
392
356
|
|
|
393
357
|
def _limit(limit):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sql_testing_tools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
4
4
|
Description-Content-Type: text/markdown
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Dist: sqlparse>=0.5.1
|
|
@@ -135,6 +135,14 @@ res = checkEquality()
|
|
|
135
135
|
|
|
136
136
|
### Changelog
|
|
137
137
|
|
|
138
|
+
##### V 0.2.13
|
|
139
|
+
- Acccept `COUNT(colname)`, too (instead of just `COUNT(*)`)
|
|
140
|
+
|
|
141
|
+
##### V 0.2.12
|
|
142
|
+
- fixed bug when having only one colum selected
|
|
143
|
+
- fixed bug with nested conditions
|
|
144
|
+
- fixed memory problem with huge cross products (used to lead to build failed)
|
|
145
|
+
|
|
138
146
|
##### V 0.2.11
|
|
139
147
|
- fixed irrelevant equality order if both sides aren't literals
|
|
140
148
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools.egg-info/requires.txt
RENAMED
|
File without changes
|
{sql_testing_tools-0.2.11 → sql_testing_tools-0.2.13}/sql_testing_tools.egg-info/top_level.txt
RENAMED
|
File without changes
|