sql-testing-tools 0.2.10__py3-none-any.whl → 0.2.12__py3-none-any.whl

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.
@@ -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("dbiu."):
34
- dbVersion = dbMap.get(database)
34
+ if isinstance(database, str) and database.startswith(dbiu):
35
+ db_version = dbMap.get(database)
35
36
  elif isinstance(database, int):
36
- dbVersion = database
37
- database = "dbiu."+str(database)
37
+ db_version = database
38
+ database = dbiu+str(database)
38
39
  elif isinstance(database, str):
39
- dbVersion = None
40
+ db_version = None
40
41
  else:
41
42
  raise Exception("Invalid database selected")
42
43
 
43
- if(dbVersion is not None):
44
- os.system('pip install dbiu_databases=='+str(dbVersion))
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,9 +52,7 @@ def setDBName(database):
51
52
 
52
53
 
53
54
  def run(sql: str):
54
- global __db
55
-
56
- if __db.startswith("dbiu.") and not __db.endswith(".db"):
55
+ if __db.startswith(dbiu) and not __db.endswith(".db"):
57
56
  with importlib.resources.path('dbiu_databases', "base.db") as db_path:
58
57
  return __run(sql, db_path)
59
58
  else:
@@ -66,7 +65,7 @@ def __run(sql: str, db: str):
66
65
  con.commit()
67
66
  return cur
68
67
 
69
- def runFromFile(path: str):
68
+ def runFromFile(path: str, limit: int = None):
70
69
  sql = getSQLFromFile(path)
71
70
  if not sql:
72
71
  raise Exception("\nSQL-Datei ist leer. Aufgabe wurde noch nicht bearbeitet.")
@@ -81,15 +80,19 @@ def runFromFile(path: str):
81
80
  raise Exception(f"\n\nSyntax-Fehler in der SQL-Abfrage:\n{str(e)}")
82
81
 
83
82
  headers = [h[0] for h in res.description] if res.description else []
84
- rows = res.fetchall() if res else []
83
+ if limit:
84
+ rows = res.fetchmany(limit)
85
+ else:
86
+ rows = res.fetchall() if res else []
85
87
 
86
88
  return headers, rows
87
89
 
88
90
 
89
- def runAndGetStringTable_fromFile(path: str, count: int = 5, maxLineLength: int = 85):
91
+ def runAndGetStringTable_fromFile(path: str, count: int = 5, max_line_length: int = 85):
90
92
  try:
91
- headers, rows = runFromFile(path)
92
- resultCount = len(rows)
93
+ limit = 1000
94
+ headers, rows = runFromFile(path, limit)
95
+ result_count = len(rows)
93
96
  rows = rows[:count]
94
97
  s = ""
95
98
 
@@ -106,52 +109,51 @@ def runAndGetStringTable_fromFile(path: str, count: int = 5, maxLineLength: int
106
109
  matrix[col].append(rows[row][col])
107
110
 
108
111
  for col in range(len(matrix)):
109
- maxLength = max([len(str(x)) for x in matrix[col]])
112
+ max_length = max([len(str(x)) for x in matrix[col]])
110
113
 
111
114
  for row in range(0, len(matrix[col])):
112
- matrix[col][row] = str(matrix[col][row]).ljust(maxLength)
115
+ matrix[col][row] = str(matrix[col][row]).ljust(max_length)
113
116
 
114
- normalizedRows = []
117
+ normalized_rows = []
115
118
  spacing = " "
116
- spacingLength = len(spacing) * (len(matrix[0]) - 1)
119
+ spacing_length = len(spacing) * (len(matrix[0]) - 1)
117
120
 
118
- lineLength = sum([len(x[0]) for x in matrix]) + spacingLength
121
+ line_length = sum([len(x[0]) for x in matrix]) + spacing_length
119
122
 
120
123
 
121
124
 
122
- if lineLength > maxLineLength:
123
- valLength = lineLength - spacingLength
124
- maxColLength = int(maxLineLength/len(matrix))
125
+ if line_length > max_line_length:
126
+ max_col_length = int(max_line_length/len(matrix))
125
127
 
126
128
  for col in range(len(matrix)):
127
129
  for row in range(0, len(matrix[col])):
128
- cutContent = len(matrix[col][row].strip()) > maxColLength
129
- if cutContent:
130
- matrix[col][row] = matrix[col][row][:maxColLength-2] + ".."
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] + ".."
131
133
  else:
132
- matrix[col][row] = matrix[col][row][:maxColLength]
134
+ matrix[col][row] = matrix[col][row][:max_col_length]
133
135
 
134
136
 
135
137
  for row in range(min(count+1, len(matrix[0]))):
136
- normalizedRows.append(spacing.join([str(matrix[col][row]) for col in range(len(matrix))]))
137
- normalizedRows.insert(1, len(normalizedRows[0])*"-")
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])*"-")
138
140
 
139
- s = "\n".join(normalizedRows[:count+2])
140
- s += "\n" + normalizedRows[1]
141
+ s = "\n".join(normalized_rows[:count+2])
142
+ s += "\n" + normalized_rows[1]
141
143
 
142
- if (resultCount > count):
143
- s += "\n... " + str(resultCount - count) + " weitere Zeilen"
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"
144
149
  else:
145
150
  s += "\n... keine weiteren Zeilen"
146
-
147
- #longestLineInS = 85 # max([len(x.replace("\t", " ")) for x in s.split("\n")])
148
-
149
- #s = "-" * longestLineInS + "\n" + s + "\n" + "-" * longestLineInS
150
151
  s = "\n\nDiese Meldung sagt nichts über die Korrektheit der Abgabe aus!\nDie ersten " + str(
151
152
  count) + " Zeilen des Ergebnisses der SQL-Abfrage:\n\n" + s
152
153
 
153
154
  return s.replace("None", "NULL")
154
155
  except Exception as ex:
156
+ print(ex)
155
157
  raise ex
156
158
 
157
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
- def setup(sqlPath,solPath):
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(sqlPath and sqlPath != ""):
24
- sql = normalizeSQLQuery(Ba.getSQLFromFile(sqlPath), bd)
25
- if(solPath and solPath != ""):
26
- sol = normalizeSQLQuery(Ba.getSQLFromFile(solPath), bd)
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 Exception("\n\nSQL-Datei ist leer. Aufgabe wurde noch nicht bearbeitet.")
39
+ raise AssertionError("\n\nSQL-Datei ist leer. Aufgabe wurde noch nicht bearbeitet.")
29
40
 
30
41
 
31
- def normalizeSQLQuery(query, baseDict):
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 Exception(f"\nSyntax-Fehler in der SQL-Abfrage.")
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() == 'FROM':
47
- formatted_query.append('FROM')
48
- pass
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, baseDict))
52
- Tp._from(il, alias_map, baseDict)
53
- elif isinstance(token, IdentifierList) and formatted_query and formatted_query[-1] == 'FROM':
54
- formatted_query.append(Tp._from(token, alias_map, baseDict))
55
- Tp._from(token, alias_map, baseDict)
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() == 'SELECT':
64
- formatted_query.append('SELECT')
65
- elif token.ttype is Keyword and token.value.upper() == 'FROM':
66
- formatted_query.append('FROM')
67
- elif token.ttype is Keyword and token.value.upper() == 'GROUP BY':
68
- formatted_query.append('GROUP BY')
69
- elif token.ttype is Keyword and token.value.upper() == 'ORDER BY':
70
- formatted_query.append('ORDER BY')
71
- elif token.ttype is Keyword and token.value.upper() == 'LIMIT':
72
- formatted_query.append('LIMIT')
73
- elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] == 'FROM':
74
- formatted_query.append(Tp._from(token, alias_map, baseDict))
75
- elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] == 'GROUP BY':
76
- formatted_query.append(Tp._groupby(token, alias_map, baseDict))
77
- elif (isinstance(token, IdentifierList) or isinstance(token, Identifier)) and formatted_query and formatted_query[-1] == 'ORDER BY':
78
- formatted_query.append(Tp._orderby(token, alias_map, baseDict))
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('WHERE')
81
- formatted_query.append(Tp._where(token, alias_map, baseDict))
82
- elif formatted_query and formatted_query[-1] == 'SELECT' and (isinstance(token, IdentifierList) or isinstance(token, Function) or isinstance(token, Identifier)):
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, baseDict))
86
- elif formatted_query and formatted_query[-1] == 'LIMIT':
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 findTableForColumn(data_dict, target_value, relevantTables):
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 relevantTables:
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, tableDict: dict):
108
- tab = tableDict[table_name]
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(baseDict, sql, sol):
131
+ def buildAndSendCosetteRequest(base_dict, sql, sol):
122
132
 
123
133
  err = ""
124
- for i in range(2):
134
+ for _ in range(2):
125
135
  try:
126
- apiKey=getCosetteKeyFromFile()
136
+ api_key=getCosetteKeyFromFile()
127
137
 
128
138
 
129
139
  schema = ""
130
- for tab in baseDict.keys():
131
- schema += f"schema sch{tab}{getTableScheme(tab, baseDict)};\n"
132
- for tab in baseDict.keys():
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": apiKey, "query": cosette}, verify=False)
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 checkKeywords(startWord:str, endWords:list):
153
- global sql,sol
160
+ def check_keywords(start_word:str, end_words:list):
161
+ start_word = start_word.lower()
154
162
 
155
- startWord = startWord.lower()
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(startWord in sql.lower()):
161
- start = sql.lower().find(startWord) + len(startWord)
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 endWords:
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(startWord) + len(startWord)
178
+ start = sol.lower().find(start_word) + len(start_word)
174
179
  end = -1
175
180
 
176
- for kw in endWords:
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 '"+startWord+"' Teil der SQL-Abfrage ist nicht korrekt (oder nicht automatisch überprüfbar)."
192
+ return "Der '"+start_word+"' Teil der SQL-Abfrage ist nicht korrekt (oder nicht automatisch überprüfbar)."
188
193
 
189
194
 
190
- def checkColumns(sqlPath="", solPath=""):
191
- global sql,sol
192
- setup(sqlPath, solPath)
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(sqlPath="", solPath=""):
197
- global sql,sol
198
- setup(sqlPath, solPath)
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(sqlPath="", solPath=""):
203
- global sql,sol
204
- setup(sqlPath, solPath)
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(sqlPath="", solPath=""):
209
- global sql,sol
210
- setup(sqlPath, solPath)
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(sqlPath="", solPath=""):
215
- global sql, sol
216
- setup(sqlPath, solPath)
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(sqlPath="", solPath=""):
221
- global sql, sol
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