Skip to content

SQL Playground — C source

Run real SQL on sample datasets - or your own schema - right in the browser. Write queries, see formatted results instantly, and export or share them. Powered by sql.js (SQLite WASM); 100% client-side.

This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/* sql-playground — SQL statement splitting, read-only classification, CSV / Markdown serialization (C). */
#include <ctype.h>
#include <stdio.h>
#include <string.h>

static int is_read_only(const char *stmt) { /* leading keyword + word boundary ("SELECTOR" fails) */
    static const char *const KW[] = {"SELECT", "WITH", "VALUES", "EXPLAIN", "PRAGMA"};
    while (isspace((unsigned char)*stmt)) stmt++;
    for (size_t k = 0; k < sizeof KW / sizeof KW[0]; k++) {
        size_t i = 0;
        while (KW[k][i] != '\0' && tolower((unsigned char)stmt[i]) == tolower((unsigned char)KW[k][i])) i++;
        if (KW[k][i] == '\0' && !isalnum((unsigned char)stmt[i]) && stmt[i] != '_') return 1;
    }
    return 0;
}

static int split_statements(const char *sql, char out[][512]) { /* ';' outside '...' ('' escapes) */
    int n = 0, in_string = 0;
    char buf[512];
    size_t len = 0;
    for (const char *p = sql;; p++) {
        char ch = *p;
        if (ch != '\0' && (in_string || ch != ';')) {
            buf[len++] = ch;
            if (ch == '\'' && in_string && p[1] == '\'') buf[len++] = *++p; /* '' escape */
            else if (ch == '\'') in_string = !in_string;
            continue;
        }
        buf[len] = '\0';
        char *s = buf, *e = s + strlen(s);
        while (isspace((unsigned char)*s)) s++;
        while (e > s && isspace((unsigned char)e[-1])) e--;
        *e = '\0';
        if (*s != '\0' && n < 8) strcpy(out[n++], s);
        len = 0;
        if (ch == '\0') return n;
    }
}

static void csv_field(const char *cell, char *out) { /* RFC-4180-ish; NULL renders as text */
    const char *v = cell != NULL ? cell : "NULL";
    if (strpbrk(v, ",\"\r\n") == NULL) { strcpy(out, v); return; }
    char *o = out;
    *o++ = '"';
    for (const char *p = v; *p != '\0'; p++) {
        if (*p == '"') *o++ = '"';
        *o++ = *p;
    }
    *o++ = '"';
    *o = '\0';
}

static void md_row(const char *const *cells, int ncols) { /* pipes escaped, NULL as text */
    for (int c = 0; c < ncols; c++) {
        printf(c > 0 ? " | " : "| ");
        for (const char *p = cells[c] != NULL ? cells[c] : "NULL"; *p != '\0'; p++) {
            if (*p == '|') putchar('\\');
            putchar(*p);
        }
    }
    puts(" |");
}

int main(void) {
    char stmts[8][512], field[512];
    int n = split_statements("CREATE TABLE t (id INT);  SELECT * FROM t WHERE note = 'a''b;c' ; ;DROP TABLE t", stmts);
    for (int i = 0; i < n; i++)
        printf("[%s] %s\n", is_read_only(stmts[i]) ? "read-only" : "write", stmts[i]);
    const char *cols[] = {"name", "note"};
    const char *rows[2][2] = {{"O'Brien", "has, \"quotes\""}, {NULL, "pipe|inside"}};
    puts("\nCSV:");
    for (int r = -1; r < 2; r++)
        for (int c = 0; c < 2; c++)
            printf("%s%c", (csv_field(r < 0 ? cols[c] : rows[r][c], field), field), c > 0 ? '\n' : ',');
    puts("\nMarkdown:");
    md_row(cols, 2);
    puts("| --- | --- |");
    for (int r = 0; r < 2; r++) md_row(rows[r], 2);
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →