samarth upadhyay commited on
Commit
a8d95dc
·
verified ·
1 Parent(s): 913a0de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -135
app.py CHANGED
@@ -2,51 +2,118 @@ import subprocess
2
  import sys
3
  import uuid
4
  import os
5
- import tempfile
6
- import io
7
- import pandas as pd
8
  from flask import Flask, request, jsonify
9
  from flask_cors import CORS
10
 
11
  app = Flask(__name__)
12
  CORS(app)
13
 
14
- # --- STANDARD SAMPLE DATA ---
15
- SAMPLE_SQL = """
16
- -- Standard Sample Data
17
- CREATE TABLE Departments (
18
- dept_id INT PRIMARY KEY,
19
- dept_name VARCHAR(50),
20
- location VARCHAR(50)
21
- );
22
-
23
- INSERT INTO Departments VALUES (1, 'Engineering', 'New York');
24
- INSERT INTO Departments VALUES (2, 'HR', 'London');
25
- INSERT INTO Departments VALUES (3, 'Marketing', 'San Francisco');
26
- INSERT INTO Departments VALUES (4, 'Sales', 'Tokyo');
27
-
28
- CREATE TABLE Employees (
29
- emp_id INT PRIMARY KEY,
30
- name VARCHAR(50),
31
- dept_id INT,
32
- role VARCHAR(50),
33
- salary DECIMAL(10, 2)
34
- );
35
-
36
- INSERT INTO Employees VALUES (101, 'Alice Smith', 1, 'Engineer', 90000);
37
- INSERT INTO Employees VALUES (102, 'Bob Jones', 1, 'Senior Engineer', 110000);
38
- INSERT INTO Employees VALUES (103, 'Charlie Brown', 2, 'Recruiter', 60000);
39
- INSERT INTO Employees VALUES (104, 'Diana Prince', 3, 'CMO', 150000);
40
- INSERT INTO Employees VALUES (105, 'Evan Wright', 1, 'Intern', 40000);
41
- INSERT INTO Employees VALUES (106, 'Fiona Green', 4, 'Sales Rep', 75000);
42
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- @app.route('/')
45
- def home():
46
- return jsonify({"status": "ok", "message": "SQL Compiler API Ready"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  def run_command(command, input_text=None, timeout=15):
49
- """Helper to run shell commands safely"""
50
  try:
51
  run_process = subprocess.run(
52
  command,
@@ -57,120 +124,55 @@ def run_command(command, input_text=None, timeout=15):
57
  )
58
  output = run_process.stdout
59
  if run_process.stderr:
60
- output += "\n--- Database Messages/Errors ---\n" + run_process.stderr
 
 
 
61
  return output
62
  except subprocess.TimeoutExpired:
63
  return "Error: Execution timed out (15s limit)."
64
  except Exception as e:
65
  return f"System Error: {str(e)}"
66
 
67
- def file_to_sql(file, filename):
68
- """Converts CSV/Excel file objects to SQL CREATE/INSERT strings"""
69
- try:
70
- # 1. Read Data
71
- if filename.lower().endswith('.csv'):
72
- df = pd.read_csv(file)
73
- else:
74
- df = pd.read_excel(file)
75
-
76
- # 2. Sanitize Table Name
77
- table_name = os.path.splitext(filename)[0]
78
- table_name = "".join([c if c.isalnum() else "_" for c in table_name])
79
- if not table_name: table_name = "uploaded_table"
80
-
81
- # 3. Sanitize Column Names
82
- df.columns = ["".join([c if c.isalnum() else "_" for c in str(col)]) for col in df.columns]
83
-
84
- # 4. Generate CREATE TABLE
85
- cols = []
86
- for col, dtype in zip(df.columns, df.dtypes):
87
- sql_type = "TEXT"
88
- if "int" in str(dtype): sql_type = "INTEGER"
89
- elif "float" in str(dtype): sql_type = "DECIMAL(10,2)"
90
- cols.append(f"{col} {sql_type}")
91
-
92
- create_stmt = f"CREATE TABLE {table_name} ({', '.join(cols)});\n"
93
-
94
- # 5. Generate INSERT Statements
95
- inserts = []
96
- for _, row in df.iterrows():
97
- vals = []
98
- for v in row:
99
- if pd.isna(v):
100
- vals.append("NULL")
101
- elif isinstance(v, str):
102
- clean_v = str(v).replace("'", "''")
103
- vals.append(f"'{clean_v}'")
104
- else:
105
- vals.append(str(v))
106
- inserts.append(f"INSERT INTO {table_name} VALUES ({', '.join(vals)});")
107
-
108
- return create_stmt + "\n".join(inserts) + "\n"
109
-
110
- except Exception as e:
111
- return f"-- Error converting file {filename}: {str(e)}\n"
112
 
113
  @app.route('/api/run-sql', methods=['POST'])
114
  def run_sql():
115
  sql_code = request.form.get('code', '')
116
- db_type = request.form.get('type', 'sqlite').lower()
117
- use_sample = request.form.get('use_sample') == 'true'
118
-
119
- # SUPPORT MULTIPLE FILES
120
- uploaded_files = request.files.getlist('database')
121
 
122
  if not sql_code:
123
  return jsonify({'output': 'No SQL code provided'}), 400
124
 
125
- output = ""
126
- session_id = uuid.uuid4().hex[:8]
127
- temp_file_path = None
128
-
129
- # Prepare Initialization SQL
130
  init_sql = ""
 
 
 
 
 
 
 
 
 
131
 
132
- if use_sample:
133
- init_sql += SAMPLE_SQL + "\n"
134
-
135
- # Process all uploaded files (Convert CSV/Excel to SQL, Append SQL Dumps)
136
- binary_db_file = None
137
-
138
- for file in uploaded_files:
139
- fname = file.filename.lower()
140
-
141
- # Data Files (CSV/Excel) -> Convert to SQL
142
- if fname.endswith('.csv') or fname.endswith('.xlsx') or fname.endswith('.xls'):
143
- converted_sql = file_to_sql(file, file.filename)
144
- init_sql += f"-- Imported from {file.filename}\n" + converted_sql + "\n"
145
-
146
- # SQL Dumps -> Append to Script
147
- elif fname.endswith('.sql'):
148
- content = file.read().decode('utf-8', errors='ignore')
149
- init_sql += f"\n-- Dump from {file.filename}\n{content}\n"
150
-
151
- # SQLite Binary -> Save for later (Only 1 supported per run for SQLite)
152
- elif (fname.endswith('.db') or fname.endswith('.sqlite')) and db_type == 'sqlite':
153
- binary_db_file = file
154
 
155
  try:
156
- # --- 1. SQLITE LOGIC ---
157
  if db_type == 'sqlite':
158
- db_target = ":memory:"
159
-
160
- # Handle Binary DB File if present
161
- if binary_db_file:
162
- fd, temp_file_path = tempfile.mkstemp(suffix='.db')
163
- os.close(fd)
164
- binary_db_file.save(temp_file_path)
165
- db_target = temp_file_path
166
-
167
- cmd = ["sqlite3", db_target]
168
  header = ".mode column\n.headers on\n"
169
  full_script = header + init_sql + sql_code
170
  output = run_command(cmd, input_text=full_script)
171
 
172
- # --- 2. POSTGRESQL LOGIC ---
173
- elif db_type in ['postgres', 'postgresql']:
174
  schema_name = f"sess_{session_id}"
175
  run_command(["psql", "-h", "localhost", "-U", "playground", "-d", "playground", "-c", f"CREATE SCHEMA {schema_name};"])
176
  try:
@@ -179,26 +181,23 @@ def run_sql():
179
  finally:
180
  run_command(["psql", "-h", "localhost", "-U", "playground", "-d", "playground", "-c", f"DROP SCHEMA {schema_name} CASCADE;"])
181
 
182
- # --- 3. MYSQL LOGIC ---
183
  elif db_type == 'mysql':
184
  db_name = f"sess_{session_id}"
185
  run_command(["mysql", "-h", "localhost", "-u", "playground", "-e", f"CREATE DATABASE {db_name};"])
186
  try:
 
 
 
187
  full_script = f"USE {db_name};\n{init_sql}\n{sql_code}"
188
  output = run_command(["mysql", "-h", "localhost", "-u", "playground", "-t"], input_text=full_script)
189
  finally:
190
  run_command(["mysql", "-h", "localhost", "-u", "playground", "-e", f"DROP DATABASE {db_name};"])
191
 
192
- else:
193
- return jsonify({'output': 'Unsupported database type.'}), 400
194
-
195
  except Exception as e:
196
  output = f"Server Error: {str(e)}"
197
- finally:
198
- if temp_file_path and os.path.exists(temp_file_path):
199
- os.remove(temp_file_path)
200
 
201
- return jsonify({'output': output})
202
 
203
  if __name__ == '__main__':
204
  app.run(host="0.0.0.0", port=7860)
 
2
  import sys
3
  import uuid
4
  import os
5
+ import random
 
 
6
  from flask import Flask, request, jsonify
7
  from flask_cors import CORS
8
 
9
  app = Flask(__name__)
10
  CORS(app)
11
 
12
+ # --- DATASET GENERATORS ---
13
+
14
+ def get_small_dataset():
15
+ return """
16
+ -- SMALL DATASET: School Management
17
+ CREATE TABLE Teachers (
18
+ id INT PRIMARY KEY,
19
+ name VARCHAR(50),
20
+ subject VARCHAR(30)
21
+ );
22
+ INSERT INTO Teachers VALUES (1, 'Mr. Smith', 'Math'), (2, 'Ms. Johnson', 'Science'), (3, 'Mrs. Davis', 'History');
23
+
24
+ CREATE TABLE Students (
25
+ id INT PRIMARY KEY,
26
+ name VARCHAR(50),
27
+ grade INT,
28
+ teacher_id INT
29
+ );
30
+ INSERT INTO Students VALUES (101, 'Alice', 5, 1), (102, 'Bob', 5, 1), (103, 'Charlie', 6, 2), (104, 'Diana', 6, 2), (105, 'Evan', 5, 3);
31
+ """
32
+
33
+ def get_medium_dataset():
34
+ return """
35
+ -- MEDIUM DATASET: E-Commerce Store
36
+ CREATE TABLE Products (
37
+ id INT PRIMARY KEY,
38
+ name VARCHAR(50),
39
+ price DECIMAL(10,2),
40
+ stock INT
41
+ );
42
+ INSERT INTO Products VALUES (1, 'Laptop', 999.99, 10), (2, 'Mouse', 25.50, 50), (3, 'Keyboard', 45.00, 30), (4, 'Monitor', 150.00, 15), (5, 'Headphones', 80.00, 20);
43
+
44
+ CREATE TABLE Customers (
45
+ id INT PRIMARY KEY,
46
+ name VARCHAR(50),
47
+ city VARCHAR(50)
48
+ );
49
+ INSERT INTO Customers VALUES (1, 'John Doe', 'New York'), (2, 'Jane Smith', 'London'), (3, 'Mike Ross', 'New York'), (4, 'Rachel Zane', 'Toronto');
50
+
51
+ CREATE TABLE Orders (
52
+ id INT PRIMARY KEY,
53
+ customer_id INT,
54
+ product_id INT,
55
+ quantity INT,
56
+ date VARCHAR(20)
57
+ );
58
+ INSERT INTO Orders VALUES (1001, 1, 1, 1, '2023-01-01'), (1002, 2, 3, 2, '2023-01-02'), (1003, 1, 2, 1, '2023-01-03'), (1004, 3, 1, 1, '2023-01-04'), (1005, 4, 5, 1, '2023-01-05'), (1006, 2, 2, 5, '2023-01-05');
59
+ """
60
+
61
+ def get_large_dataset():
62
+ # Generates a synthetic log dataset
63
+ sql = """
64
+ -- LARGE DATASET: Web Server Traffic
65
+ CREATE TABLE Users (
66
+ user_id INT PRIMARY KEY,
67
+ username VARCHAR(50),
68
+ country VARCHAR(50)
69
+ );
70
+ INSERT INTO Users VALUES (1, 'admin', 'USA'), (2, 'guest', 'UK'), (3, 'power_user', 'Canada'), (4, 'bot_01', 'Russia'), (5, 'test_acc', 'India');
71
+
72
+ CREATE TABLE ServerLogs (
73
+ log_id INT PRIMARY KEY,
74
+ user_id INT,
75
+ endpoint VARCHAR(50),
76
+ status_code INT,
77
+ response_time_ms INT
78
+ );
79
+ """
80
+
81
+ # Generate 100 rows of log data
82
+ endpoints = ['/home', '/login', '/api/data', '/dashboard', '/logout', '/404']
83
+ statuses = [200, 200, 200, 404, 500, 301]
84
+
85
+ values = []
86
+ for i in range(1, 101):
87
+ uid = random.randint(1, 5)
88
+ ep = random.choice(endpoints)
89
+ sc = random.choice(statuses)
90
+ rt = random.randint(20, 500)
91
+ values.append(f"({i}, {uid}, '{ep}', {sc}, {rt})")
92
+
93
+ sql += f"INSERT INTO ServerLogs VALUES {', '.join(values)};"
94
+ return sql
95
 
96
+ # --- HELPERS ---
97
+
98
+ def detect_db_type(sql_code):
99
+ """
100
+ Simple heuristic to auto-detect dialect.
101
+ Defaults to SQLite (most permissive for playgrounds).
102
+ """
103
+ code_upper = sql_code.upper()
104
+
105
+ # MySQL Specifics
106
+ if "AUTO_INCREMENT" in code_upper or "SHOW TABLES" in code_upper or "UNSIGNED" in code_upper:
107
+ return "mysql"
108
+
109
+ # PostgreSQL Specifics
110
+ if "RETURNING" in code_upper or "SERIAL" in code_upper or "::" in code_upper or "ILIKE" in code_upper:
111
+ return "postgres"
112
+
113
+ # Default
114
+ return "sqlite"
115
 
116
  def run_command(command, input_text=None, timeout=15):
 
117
  try:
118
  run_process = subprocess.run(
119
  command,
 
124
  )
125
  output = run_process.stdout
126
  if run_process.stderr:
127
+ # Filter out common CLI noise
128
+ errs = [line for line in run_process.stderr.split('\n') if not line.startswith('Note:') and line.strip()]
129
+ if errs:
130
+ output += "\n--- System Messages ---\n" + "\n".join(errs)
131
  return output
132
  except subprocess.TimeoutExpired:
133
  return "Error: Execution timed out (15s limit)."
134
  except Exception as e:
135
  return f"System Error: {str(e)}"
136
 
137
+ # --- ROUTES ---
138
+
139
+ @app.route('/')
140
+ def home():
141
+ return jsonify({"status": "ok", "message": "SQL Compiler API Ready"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  @app.route('/api/run-sql', methods=['POST'])
144
  def run_sql():
145
  sql_code = request.form.get('code', '')
146
+ dataset_size = request.form.get('dataset', 'small') # small, medium, large
 
 
 
 
147
 
148
  if not sql_code:
149
  return jsonify({'output': 'No SQL code provided'}), 400
150
 
151
+ # 1. Select Dataset
 
 
 
 
152
  init_sql = ""
153
+ if dataset_size == 'small':
154
+ init_sql = get_small_dataset()
155
+ elif dataset_size == 'medium':
156
+ init_sql = get_medium_dataset()
157
+ elif dataset_size == 'large':
158
+ init_sql = get_large_dataset()
159
+
160
+ # 2. Auto-detect DB Type
161
+ db_type = detect_db_type(sql_code)
162
 
163
+ session_id = uuid.uuid4().hex[:8]
164
+ output = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  try:
167
+ # --- SQLITE ---
168
  if db_type == 'sqlite':
169
+ cmd = ["sqlite3", ":memory:"]
 
 
 
 
 
 
 
 
 
170
  header = ".mode column\n.headers on\n"
171
  full_script = header + init_sql + sql_code
172
  output = run_command(cmd, input_text=full_script)
173
 
174
+ # --- POSTGRESQL ---
175
+ elif db_type == 'postgres':
176
  schema_name = f"sess_{session_id}"
177
  run_command(["psql", "-h", "localhost", "-U", "playground", "-d", "playground", "-c", f"CREATE SCHEMA {schema_name};"])
178
  try:
 
181
  finally:
182
  run_command(["psql", "-h", "localhost", "-U", "playground", "-d", "playground", "-c", f"DROP SCHEMA {schema_name} CASCADE;"])
183
 
184
+ # --- MYSQL ---
185
  elif db_type == 'mysql':
186
  db_name = f"sess_{session_id}"
187
  run_command(["mysql", "-h", "localhost", "-u", "playground", "-e", f"CREATE DATABASE {db_name};"])
188
  try:
189
+ # MySQL doesn't support "INSERT INTO ... VALUES (...), (...)" syntax exactly same way if mixed with other logic sometimes,
190
+ # but standard SQL is fine.
191
+ # Note: We pipe specific settings to make output pretty
192
  full_script = f"USE {db_name};\n{init_sql}\n{sql_code}"
193
  output = run_command(["mysql", "-h", "localhost", "-u", "playground", "-t"], input_text=full_script)
194
  finally:
195
  run_command(["mysql", "-h", "localhost", "-u", "playground", "-e", f"DROP DATABASE {db_name};"])
196
 
 
 
 
197
  except Exception as e:
198
  output = f"Server Error: {str(e)}"
 
 
 
199
 
200
+ return jsonify({'output': output, 'detected_engine': db_type})
201
 
202
  if __name__ == '__main__':
203
  app.run(host="0.0.0.0", port=7860)