File size: 6,961 Bytes
b597938
2a69151
b597938
 
a8d95dc
2a69151
 
 
 
 
 
a8d95dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3b9735e
a8d95dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a69151
b597938
 
 
 
 
 
 
 
 
 
 
a8d95dc
 
 
 
b597938
 
 
 
 
2a69151
a8d95dc
 
 
 
 
2a69151
 
 
b597938
a8d95dc
2a69151
b597938
 
2a69151
a8d95dc
b597938
a8d95dc
 
 
 
 
 
 
 
 
3b9735e
a8d95dc
 
2a69151
 
a8d95dc
b597938
a8d95dc
b597938
 
 
 
a8d95dc
 
b597938
 
 
 
 
 
 
 
a8d95dc
b597938
 
 
 
a8d95dc
 
 
b597938
 
 
 
 
2a69151
b597938
2a69151
a8d95dc
2a69151
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import subprocess
import sys
import uuid
import os
import random
from flask import Flask, request, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# --- DATASET GENERATORS ---

def get_small_dataset():
    return """
    -- SMALL DATASET: School Management
    CREATE TABLE Teachers (
        id INT PRIMARY KEY,
        name VARCHAR(50),
        subject VARCHAR(30)
    );
    INSERT INTO Teachers VALUES (1, 'Mr. Smith', 'Math'), (2, 'Ms. Johnson', 'Science'), (3, 'Mrs. Davis', 'History');

    CREATE TABLE Students (
        id INT PRIMARY KEY,
        name VARCHAR(50),
        grade INT,
        teacher_id INT
    );
    INSERT INTO Students VALUES (101, 'Alice', 5, 1), (102, 'Bob', 5, 1), (103, 'Charlie', 6, 2), (104, 'Diana', 6, 2), (105, 'Evan', 5, 3);
    """

def get_medium_dataset():
    return """
    -- MEDIUM DATASET: E-Commerce Store
    CREATE TABLE Products (
        id INT PRIMARY KEY,
        name VARCHAR(50),
        price DECIMAL(10,2),
        stock INT
    );
    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);

    CREATE TABLE Customers (
        id INT PRIMARY KEY,
        name VARCHAR(50),
        city VARCHAR(50)
    );
    INSERT INTO Customers VALUES (1, 'John Doe', 'New York'), (2, 'Jane Smith', 'London'), (3, 'Mike Ross', 'New York'), (4, 'Rachel Zane', 'Toronto');

    CREATE TABLE Orders (
        id INT PRIMARY KEY,
        customer_id INT,
        product_id INT,
        quantity INT,
        date VARCHAR(20)
    );
    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');
    """

def get_large_dataset():
    # Generates a synthetic log dataset
    sql = """
    -- LARGE DATASET: Web Server Traffic
    CREATE TABLE Users (
        user_id INT PRIMARY KEY,
        username VARCHAR(50),
        country VARCHAR(50)
    );
    INSERT INTO Users VALUES (1, 'admin', 'USA'), (2, 'guest', 'UK'), (3, 'power_user', 'Canada'), (4, 'bot_01', 'Russia'), (5, 'test_acc', 'India');
    
    CREATE TABLE ServerLogs (
        log_id INT PRIMARY KEY,
        user_id INT,
        endpoint VARCHAR(50),
        status_code INT,
        response_time_ms INT
    );
    """
    
    # Generate 100 rows of log data
    endpoints = ['/home', '/login', '/api/data', '/dashboard', '/logout', '/404']
    statuses = [200, 200, 200, 404, 500, 301]
    
    values = []
    for i in range(1, 101):
        uid = random.randint(1, 5)
        ep = random.choice(endpoints)
        sc = random.choice(statuses)
        rt = random.randint(20, 500)
        values.append(f"({i}, {uid}, '{ep}', {sc}, {rt})")
    
    sql += f"INSERT INTO ServerLogs VALUES {', '.join(values)};"
    return sql

# --- HELPERS ---

def detect_db_type(sql_code):
    """
    Simple heuristic to auto-detect dialect.
    Defaults to SQLite (most permissive for playgrounds).
    """
    code_upper = sql_code.upper()
    
    # MySQL Specifics
    if "AUTO_INCREMENT" in code_upper or "SHOW TABLES" in code_upper or "UNSIGNED" in code_upper:
        return "mysql"
    
    # PostgreSQL Specifics
    if "RETURNING" in code_upper or "SERIAL" in code_upper or "::" in code_upper or "ILIKE" in code_upper:
        return "postgres"
    
    # Default
    return "sqlite"

def run_command(command, input_text=None, timeout=15):
    try:
        run_process = subprocess.run(
            command,
            input=input_text,
            capture_output=True,
            text=True,
            timeout=timeout
        )
        output = run_process.stdout
        if run_process.stderr:
             # Filter out common CLI noise
             errs = [line for line in run_process.stderr.split('\n') if not line.startswith('Note:') and line.strip()]
             if errs:
                output += "\n--- System Messages ---\n" + "\n".join(errs)
        return output
    except subprocess.TimeoutExpired:
        return "Error: Execution timed out (15s limit)."
    except Exception as e:
        return f"System Error: {str(e)}"

# --- ROUTES ---

@app.route('/')
def home():
    return jsonify({"status": "ok", "message": "SQL Compiler API Ready"})

@app.route('/api/run-sql', methods=['POST'])
def run_sql():
    sql_code = request.form.get('code', '')
    dataset_size = request.form.get('dataset', 'small') # small, medium, large
    
    if not sql_code: 
        return jsonify({'output': 'No SQL code provided'}), 400

    # 1. Select Dataset
    init_sql = ""
    if dataset_size == 'small':
        init_sql = get_small_dataset()
    elif dataset_size == 'medium':
        init_sql = get_medium_dataset()
    elif dataset_size == 'large':
        init_sql = get_large_dataset()

    # 2. Auto-detect DB Type
    db_type = detect_db_type(sql_code)
    
    session_id = uuid.uuid4().hex[:8]
    output = ""

    try:
        # --- SQLITE ---
        if db_type == 'sqlite':
            cmd = ["sqlite3", ":memory:"]
            header = ".mode column\n.headers on\n" 
            full_script = header + init_sql + sql_code
            output = run_command(cmd, input_text=full_script)

        # --- POSTGRESQL ---
        elif db_type == 'postgres':
            schema_name = f"sess_{session_id}"
            run_command(["psql", "-h", "localhost", "-U", "playground", "-d", "playground", "-c", f"CREATE SCHEMA {schema_name};"])
            try:
                full_script = f"SET search_path TO {schema_name};\n{init_sql}\n{sql_code}"
                output = run_command(["psql", "-h", "localhost", "-U", "playground", "-d", "playground"], input_text=full_script)
            finally:
                run_command(["psql", "-h", "localhost", "-U", "playground", "-d", "playground", "-c", f"DROP SCHEMA {schema_name} CASCADE;"])

        # --- MYSQL ---
        elif db_type == 'mysql':
            db_name = f"sess_{session_id}"
            run_command(["mysql", "-h", "localhost", "-u", "playground", "-e", f"CREATE DATABASE {db_name};"])
            try:
                # MySQL doesn't support "INSERT INTO ... VALUES (...), (...)" syntax exactly same way if mixed with other logic sometimes, 
                # but standard SQL is fine. 
                # Note: We pipe specific settings to make output pretty
                full_script = f"USE {db_name};\n{init_sql}\n{sql_code}"
                output = run_command(["mysql", "-h", "localhost", "-u", "playground", "-t"], input_text=full_script)
            finally:
                run_command(["mysql", "-h", "localhost", "-u", "playground", "-e", f"DROP DATABASE {db_name};"])

    except Exception as e:
        output = f"Server Error: {str(e)}"

    return jsonify({'output': output, 'detected_engine': db_type})

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=7860)