|
|
@@ -20,21 +20,20 @@ SQLite-compatible SQL · PostgreSQL wire protocol · HTTP/JSON API · Built-in s
|
|
|
|
|
|
PizzaSQL is a SQL database engine built from the ground up in Go. It features a hand-written recursive descent parser, SQLite-compatible SQL syntax, and multiple access methods.
|
|
|
|
|
|
-PizzaSQL passes **100% of the SQLite SQLLogicTest suite** - over 5 million individual SQL tests covering edge cases, type coercion, complex queries, and SQLite compatibility.
|
|
|
+PizzaSQL passes **100% of the SQLite SQLLogicTest suite** — over 5 million individual SQL tests covering edge cases, type coercion, complex queries, and SQLite compatibility.
|
|
|
|
|
|
### Access Methods
|
|
|
|
|
|
-- **PostgreSQL Wire Protocol** - Connect with `psql`, any PostgreSQL client library (psycopg2, node-postgres, etc.)
|
|
|
-- **HTTP/JSON API** - Query via REST endpoints from any language and the web
|
|
|
-- **CLI & REPL** - Interactive shell and command-line execution
|
|
|
+- **PostgreSQL Wire Protocol** — Connect with `psql`, any PostgreSQL client library (psycopg2, node-postgres, etc.)
|
|
|
+- **HTTP/JSON API** — Query via REST endpoints from any language and the web
|
|
|
+- **CLI & REPL** — Interactive shell and command-line execution
|
|
|
|
|
|
### Architecture
|
|
|
|
|
|
-- **Hand-Written Lexer & Parser** - Pure Go implementation
|
|
|
-- **PizzaKV Storage** - Custom high-performance Zig backend with radix trie indexes
|
|
|
-- **Thread-Safe** - Concurrent query execution with mutex-based locking
|
|
|
-
|
|
|
-PizzaSQL provides SQLite SQL compatibility with PostgreSQL wire protocol support, making it easy to integrate with existing tools and libraries while maintaining full control over the SQL dialect.
|
|
|
+- **Hand-Written Lexer & Parser** — Pure Go implementation
|
|
|
+- **PizzaKV Storage** — Custom high-performance Zig backend with radix trie indexes
|
|
|
+- **Unix Socket Transport** — Low-latency communication between PizzaSQL and PizzaKV via Unix domain sockets
|
|
|
+- **Thread-Safe** — Concurrent query execution with mutex-based locking
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -55,7 +54,7 @@ PizzaSQL provides SQLite SQL compatibility with PostgreSQL wire protocol support
|
|
|
**PostgreSQL Wire Protocol**
|
|
|
```bash
|
|
|
# Start server
|
|
|
-./pizzasql -pg -pg-port 5432
|
|
|
+./pizzasql -kv -pg
|
|
|
|
|
|
# Connect with psql
|
|
|
psql -h localhost -p 5432 -d pizzasql
|
|
|
@@ -67,7 +66,7 @@ postgresql://localhost:5432/pizzasql
|
|
|
**HTTP/JSON API**
|
|
|
```bash
|
|
|
# Start HTTP server
|
|
|
-./pizzasql -http
|
|
|
+./pizzasql -kv -http
|
|
|
|
|
|
# Query via REST
|
|
|
curl -X POST http://localhost:8080/query \
|
|
|
@@ -77,29 +76,30 @@ curl -X POST http://localhost:8080/query \
|
|
|
|
|
|
**CLI/REPL**
|
|
|
```bash
|
|
|
-# Interactive mode
|
|
|
-./pizzasql
|
|
|
+# Interactive mode with storage
|
|
|
+./pizzasql -kv
|
|
|
|
|
|
# Single statement
|
|
|
-./pizzasql -e "SELECT * FROM users LIMIT 10"
|
|
|
+./pizzasql "SELECT * FROM users LIMIT 10"
|
|
|
```
|
|
|
|
|
|
### SQLite Compatibility
|
|
|
|
|
|
-- ROWID Support - Implicit rowid column for all tables
|
|
|
-- AUTOINCREMENT - Sequential ID generation
|
|
|
-- Type Affinity - SQLite-compatible type system
|
|
|
-- PRAGMA Statements - table_info, database_list, table_list, version
|
|
|
-- SQLite Functions - printf, hex, random, glob, instr, zeroblob
|
|
|
-- Conflict Resolution - INSERT OR REPLACE/IGNORE/FAIL/ABORT
|
|
|
+- ROWID Support — Implicit rowid column for all tables
|
|
|
+- AUTOINCREMENT — Sequential ID generation
|
|
|
+- Type Affinity — SQLite-compatible type system
|
|
|
+- PRAGMA Statements — table_info, database_list, table_list, version
|
|
|
+- SQLite Functions — printf, hex, random, glob, instr, zeroblob
|
|
|
+- Conflict Resolution — INSERT OR REPLACE/IGNORE/FAIL/ABORT
|
|
|
|
|
|
### Performance & Architecture
|
|
|
|
|
|
-- Hand-Written Parser - 176,000 statements/sec
|
|
|
-- Fast Lexer - 227,000 ops/sec tokenization
|
|
|
-- Automatic Indexing - 10-100x faster than full table scans
|
|
|
-- Thread-Safe - Concurrent query execution with mutex-based locking
|
|
|
-- Connection Pooling - Efficient resource management
|
|
|
+- Hand-Written Parser — 176,000 statements/sec
|
|
|
+- Fast Lexer — 227,000 ops/sec tokenization
|
|
|
+- Automatic Indexing — 10-100x faster than full table scans
|
|
|
+- Unix Socket Transport — Sub-millisecond KV latency
|
|
|
+- Thread-Safe — Concurrent query execution with mutex-based locking
|
|
|
+- Connection Pooling — Efficient resource management
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -109,141 +109,157 @@ curl -X POST http://localhost:8080/query \
|
|
|
|
|
|
**Prerequisites:**
|
|
|
- Go 1.21+
|
|
|
-- PizzaKV - Storage backend (auto-launched with `-kv` flag)
|
|
|
+- PizzaKV — Storage backend (auto-launched with `-kv` flag)
|
|
|
|
|
|
-**Build from source:**
|
|
|
+**Build and install:**
|
|
|
```bash
|
|
|
git clone https://github.com/danfragoso/pizzasql.git
|
|
|
cd pizzasql
|
|
|
-make build
|
|
|
+make install # installs to /usr/local/bin
|
|
|
+# or
|
|
|
+make install PREFIX=~/.local # user install, no sudo
|
|
|
+```
|
|
|
+
|
|
|
+**Build only:**
|
|
|
+```bash
|
|
|
+make build # output: ./bin/pizzasql
|
|
|
```
|
|
|
|
|
|
### Start the Server
|
|
|
|
|
|
-**Option 1: HTTP API (recommended for web apps)**
|
|
|
+**Option 1: HTTP API**
|
|
|
```bash
|
|
|
-# Auto-launch PizzaKV and start HTTP server
|
|
|
-./pizzasql -http -kv
|
|
|
-
|
|
|
-# Server available at http://localhost:8080
|
|
|
+pizzasql -kv -http
|
|
|
+# Listening on http://localhost:8080
|
|
|
```
|
|
|
|
|
|
-**Option 2: PostgreSQL Wire Protocol (recommended for existing PostgreSQL tools)**
|
|
|
+**Option 2: PostgreSQL wire protocol**
|
|
|
```bash
|
|
|
-# Auto-launch PizzaKV and start PostgreSQL-compatible server
|
|
|
-./pizzasql -pg -kv
|
|
|
-
|
|
|
-# Connect with psql
|
|
|
-psql -h localhost -p 5432 -d pizzasql
|
|
|
+pizzasql -kv -pg
|
|
|
+# psql -h localhost -p 5432 -d pizzasql
|
|
|
+```
|
|
|
|
|
|
-# Or use any PostgreSQL client library
|
|
|
+**Option 3: Both at once**
|
|
|
+```bash
|
|
|
+pizzasql -kv -http -pg
|
|
|
```
|
|
|
|
|
|
-**Option 3: Interactive CLI**
|
|
|
+**Option 4: Interactive REPL**
|
|
|
```bash
|
|
|
-# Auto-launch PizzaKV and start REPL
|
|
|
-./pizzasql -kv
|
|
|
+pizzasql -kv
|
|
|
```
|
|
|
|
|
|
+The `-kv` flag auto-launches a PizzaKV storage process connected via Unix socket (`.pizzakv.sock` in the working directory). PizzaSQL writes its runtime state to `/tmp/pizzasql/<pid>/runtime.json` and cleans up on exit.
|
|
|
+
|
|
|
### Your First Query
|
|
|
|
|
|
-**Using psql (PostgreSQL wire protocol):**
|
|
|
+**Using psql:**
|
|
|
```sql
|
|
|
--- Connect
|
|
|
psql -h localhost -p 5432 -d pizzasql
|
|
|
|
|
|
--- Create table (SQLite syntax!)
|
|
|
CREATE TABLE users (
|
|
|
- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
- name TEXT NOT NULL,
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ name TEXT NOT NULL,
|
|
|
email TEXT UNIQUE
|
|
|
);
|
|
|
|
|
|
--- Insert data
|
|
|
-INSERT INTO users (name, email) VALUES
|
|
|
+INSERT INTO users (name, email) VALUES
|
|
|
('Alice', 'alice@example.com'),
|
|
|
- ('Bob', 'bob@example.com');
|
|
|
+ ('Bob', 'bob@example.com');
|
|
|
|
|
|
--- Query
|
|
|
SELECT * FROM users;
|
|
|
```
|
|
|
|
|
|
**Using HTTP API:**
|
|
|
```bash
|
|
|
-# Create table
|
|
|
-curl -X POST http://localhost:8080/query \
|
|
|
- -H "Content-Type: application/json" \
|
|
|
- -d '{
|
|
|
- "sql": "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"
|
|
|
- }'
|
|
|
-
|
|
|
-# Insert with parameters
|
|
|
-curl -X POST http://localhost:8080/query \
|
|
|
- -H "Content-Type: application/json" \
|
|
|
- -d '{
|
|
|
- "sql": "INSERT INTO users (name, email) VALUES (?, ?)",
|
|
|
- "params": ["Alice", "alice@example.com"]
|
|
|
- }'
|
|
|
-
|
|
|
-# Query
|
|
|
curl -X POST http://localhost:8080/query \
|
|
|
-H "Content-Type: application/json" \
|
|
|
- -d '{
|
|
|
- "sql": "SELECT * FROM users WHERE name = ?",
|
|
|
- "params": ["Alice"]
|
|
|
- }'
|
|
|
+ -d '{"sql": "SELECT * FROM users"}'
|
|
|
```
|
|
|
|
|
|
**Response:**
|
|
|
```json
|
|
|
{
|
|
|
"columns": [
|
|
|
- {"name": "id", "type": "INTEGER"},
|
|
|
- {"name": "name", "type": "TEXT"},
|
|
|
+ {"name": "id", "type": "INTEGER"},
|
|
|
+ {"name": "name", "type": "TEXT"},
|
|
|
{"name": "email", "type": "TEXT"}
|
|
|
],
|
|
|
"rows": [
|
|
|
- [1, "Alice", "alice@example.com"]
|
|
|
+ [1, "Alice", "alice@example.com"],
|
|
|
+ [2, "Bob", "bob@example.com"]
|
|
|
],
|
|
|
"rowsAffected": 0,
|
|
|
- "lastInsertId": 0,
|
|
|
- "executionTime": "1.234ms"
|
|
|
+ "executionTimeMicro": 108
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
-## Benchmarks
|
|
|
+## Process Management
|
|
|
|
|
|
-Performance comparison between PizzaSQL, SQLite, and PostgreSQL.
|
|
|
+PizzaSQL uses a per-instance runtime directory at `/tmp/pizzasql/<pid>/` to track process state. Each directory contains a `runtime.json` with the PizzaSQL and PizzaKV PIDs and connection info.
|
|
|
|
|
|
-| Operation | SQLite | PizzaSQL (HTTP) | PizzaSQL (PG Wire) | PostgreSQL |
|
|
|
-|-----------|--------|-----------------|--------------------|------------|
|
|
|
-| **INSERT (1000 rows)** | 308 ops/s | 138 ops/s | 5780 ops/s | 12820 ops/s |
|
|
|
-| **SELECT (no index)** | 288 q/s | 128 q/s | 165 q/s | 414 q/s |
|
|
|
-| **CREATE INDEX** | 6 ms | 69 ms | 63 ms | 31 ms |
|
|
|
-| **SELECT (indexed)** | 318 q/s | 136 q/s | 1562 q/s | 2222 q/s |
|
|
|
-| **AGGREGATE** | 313 q/s | 137 q/s | 1052 q/s | 980 q/s |
|
|
|
+```
|
|
|
+/tmp/pizzasql/
|
|
|
+ 12345/
|
|
|
+ runtime.json ← { "pizzasql": { "pid": 12345, ... }, "pizzakv": { "pid": 12346, ... } }
|
|
|
+ 67890/
|
|
|
+ runtime.json
|
|
|
+```
|
|
|
|
|
|
-**Note:** Wire protocol benchmarks (PizzaSQL PG Wire and PostgreSQL) use connection reuse with transactions, which is what client libraries do automatically. PizzaSQL's aggregate queries outperform PostgreSQL in this benchmark.
|
|
|
+**Multiple instances** are supported as long as each runs from a different working directory (each needs its own `.db` and `.pizzakv.sock` file). If you try to launch `-kv` in a directory that already has a `.db` file and another instance is running, PizzaSQL will refuse and tell you the conflicting PID.
|
|
|
|
|
|
-### Run Your Own Benchmarks
|
|
|
+**Stale entries** (from crashed processes) are cleaned up automatically on the next startup.
|
|
|
+
|
|
|
+**Startup prompt** — if another live pizzasql instance is detected you'll see:
|
|
|
+```
|
|
|
+Warning: 1 pizzasql instance(s) already running:
|
|
|
+ PID 12345 http=:8080 kv=unix:.pizzakv.sock
|
|
|
+Continue anyway? [y/N]
|
|
|
+```
|
|
|
|
|
|
+**Connecting to an external PizzaKV** (without `-kv`):
|
|
|
```bash
|
|
|
-./benchmarks/quick_bench.sh
|
|
|
+pizzasql -kvaddr localhost:8085 -http
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
-## PostgreSQL Wire Protocol Support
|
|
|
+## Benchmarks
|
|
|
+
|
|
|
+Performance on an M2 MacBook Air, 10,000-row table, 200 repetitions.
|
|
|
+
|
|
|
+| Workload | SQLite | PostgreSQL | PizzaSQL |
|
|
|
+|---|---|---|---|
|
|
|
+| Point lookup by PK | 0.003 ms | 0.092 ms | 15.628 ms |
|
|
|
+| Category scan (no index) | 0.666 ms | 0.604 ms | 16.080 ms |
|
|
|
+| Value range (no index) | 1.531 ms | 1.116 ms | 17.909 ms |
|
|
|
+| COUNT(*) | 0.004 ms | 0.305 ms | 15.651 ms |
|
|
|
+| Aggregate by category | 2.419 ms | 1.070 ms | 16.667 ms |
|
|
|
+| Top-10 ORDER BY DESC | 0.861 ms | 1.020 ms | 21.735 ms |
|
|
|
+| **Category scan (indexed)** | 0.537 ms | 0.279 ms | **0.108 ms** |
|
|
|
+| Value range (indexed) | 2.456 ms | 0.828 ms | 17.854 ms |
|
|
|
|
|
|
-PizzaSQL implements the PostgreSQL wire protocol, allowing you to connect with any PostgreSQL client while using SQLite-compatible SQL syntax.
|
|
|
+Raw PizzaKV single-key read: **0.024 ms**. Full-table prefix scan (10k rows): **0.791 ms**. The dominant cost for full-scan queries is JSON deserialization (~15 ms for 10k rows).
|
|
|
|
|
|
-### Protocol Features
|
|
|
+Indexed equality lookups are faster than both SQLite and PostgreSQL because PizzaKV's radix trie resolves the index directly to rowids with no B-tree traversal overhead.
|
|
|
+
|
|
|
+### Run Benchmarks
|
|
|
+
|
|
|
+```bash
|
|
|
+# Requires PizzaSQL running at :8080 and PostgreSQL at :5432
|
|
|
+go run ./cmd/bench/
|
|
|
+
|
|
|
+# Include raw KV benchmark (find the KV port in /tmp/pizzasql/<pid>/runtime.json)
|
|
|
+go run ./cmd/bench/ -kvaddr localhost:<port>
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## PostgreSQL Wire Protocol Support
|
|
|
|
|
|
-- **Universal compatibility** - Works with thousands of PostgreSQL tools and libraries
|
|
|
-- **SQLite simplicity** - No complex types, permissions, or schemas to manage
|
|
|
-- **Binary protocol** - Efficient data transfer
|
|
|
+PizzaSQL implements the PostgreSQL wire protocol, allowing you to use any PostgreSQL client with SQLite-compatible SQL syntax.
|
|
|
|
|
|
### Connecting with Client Libraries
|
|
|
|
|
|
@@ -251,33 +267,12 @@ PizzaSQL implements the PostgreSQL wire protocol, allowing you to connect with a
|
|
|
```python
|
|
|
import psycopg2
|
|
|
|
|
|
-# Connect to PizzaSQL
|
|
|
-conn = psycopg2.connect(
|
|
|
- host="localhost",
|
|
|
- port=5432,
|
|
|
- database="pizzasql"
|
|
|
-)
|
|
|
-
|
|
|
-# Use SQLite-compatible SQL
|
|
|
+conn = psycopg2.connect(host="localhost", port=5432, database="pizzasql")
|
|
|
cur = conn.cursor()
|
|
|
-cur.execute("""
|
|
|
- CREATE TABLE users (
|
|
|
- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
- name TEXT NOT NULL,
|
|
|
- email TEXT
|
|
|
- )
|
|
|
-""")
|
|
|
-
|
|
|
-# Parameterized queries work as expected
|
|
|
-cur.execute("INSERT INTO users (name, email) VALUES (%s, %s)",
|
|
|
- ("Alice", "alice@example.com"))
|
|
|
-
|
|
|
-# Query results
|
|
|
-cur.execute("SELECT * FROM users WHERE name = %s", ("Alice",))
|
|
|
-rows = cur.fetchall()
|
|
|
-for row in rows:
|
|
|
- print(row)
|
|
|
-
|
|
|
+cur.execute("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")
|
|
|
+cur.execute("INSERT INTO users (name) VALUES (%s)", ("Alice",))
|
|
|
+cur.execute("SELECT * FROM users")
|
|
|
+print(cur.fetchall())
|
|
|
conn.commit()
|
|
|
conn.close()
|
|
|
```
|
|
|
@@ -285,600 +280,212 @@ conn.close()
|
|
|
**Node.js (node-postgres):**
|
|
|
```javascript
|
|
|
const { Client } = require('pg');
|
|
|
-
|
|
|
-// Connect to PizzaSQL
|
|
|
-const client = new Client({
|
|
|
- host: 'localhost',
|
|
|
- port: 5432,
|
|
|
- database: 'pizzasql'
|
|
|
-});
|
|
|
-
|
|
|
+const client = new Client({ host: 'localhost', port: 5432, database: 'pizzasql' });
|
|
|
await client.connect();
|
|
|
-
|
|
|
-// Use SQLite-compatible SQL
|
|
|
-await client.query(`
|
|
|
- CREATE TABLE users (
|
|
|
- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
- name TEXT NOT NULL,
|
|
|
- email TEXT
|
|
|
- )
|
|
|
-`);
|
|
|
-
|
|
|
-// Parameterized queries
|
|
|
-await client.query(
|
|
|
- 'INSERT INTO users (name, email) VALUES ($1, $2)',
|
|
|
- ['Alice', 'alice@example.com']
|
|
|
-);
|
|
|
-
|
|
|
-// Query results
|
|
|
-const res = await client.query(
|
|
|
- 'SELECT * FROM users WHERE name = $1',
|
|
|
- ['Alice']
|
|
|
-);
|
|
|
+await client.query("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
|
|
|
+await client.query("INSERT INTO users (name) VALUES ($1)", ['Alice']);
|
|
|
+const res = await client.query("SELECT * FROM users");
|
|
|
console.log(res.rows);
|
|
|
-
|
|
|
await client.end();
|
|
|
```
|
|
|
|
|
|
**Go (lib/pq):**
|
|
|
```go
|
|
|
-package main
|
|
|
-
|
|
|
-import (
|
|
|
- "database/sql"
|
|
|
- _ "github.com/lib/pq"
|
|
|
-)
|
|
|
-
|
|
|
-func main() {
|
|
|
- // Connect to PizzaSQL
|
|
|
- db, err := sql.Open("postgres",
|
|
|
- "host=localhost port=5432 dbname=pizzasql sslmode=disable")
|
|
|
- if err != nil {
|
|
|
- panic(err)
|
|
|
- }
|
|
|
- defer db.Close()
|
|
|
-
|
|
|
- // Use SQLite-compatible SQL
|
|
|
- _, err = db.Exec(`
|
|
|
- CREATE TABLE users (
|
|
|
- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
- name TEXT NOT NULL,
|
|
|
- email TEXT
|
|
|
- )
|
|
|
- `)
|
|
|
-
|
|
|
- // Parameterized queries
|
|
|
- _, err = db.Exec(
|
|
|
- "INSERT INTO users (name, email) VALUES ($1, $2)",
|
|
|
- "Alice", "alice@example.com")
|
|
|
-
|
|
|
- // Query results
|
|
|
- rows, err := db.Query(
|
|
|
- "SELECT * FROM users WHERE name = $1",
|
|
|
- "Alice")
|
|
|
- defer rows.Close()
|
|
|
-
|
|
|
- for rows.Next() {
|
|
|
- var id int
|
|
|
- var name, email string
|
|
|
- rows.Scan(&id, &name, &email)
|
|
|
- fmt.Printf("%d: %s (%s)\n", id, name, email)
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-**Ruby (pg gem):**
|
|
|
-```ruby
|
|
|
-require 'pg'
|
|
|
-
|
|
|
-# Connect to PizzaSQL
|
|
|
-conn = PG.connect(
|
|
|
- host: 'localhost',
|
|
|
- port: 5432,
|
|
|
- dbname: 'pizzasql'
|
|
|
-)
|
|
|
-
|
|
|
-# Use SQLite-compatible SQL
|
|
|
-conn.exec(<<-SQL)
|
|
|
- CREATE TABLE users (
|
|
|
- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
- name TEXT NOT NULL,
|
|
|
- email TEXT
|
|
|
- )
|
|
|
-SQL
|
|
|
-
|
|
|
-# Parameterized queries
|
|
|
-conn.exec_params(
|
|
|
- 'INSERT INTO users (name, email) VALUES ($1, $2)',
|
|
|
- ['Alice', 'alice@example.com']
|
|
|
-)
|
|
|
-
|
|
|
-# Query results
|
|
|
-result = conn.exec_params(
|
|
|
- 'SELECT * FROM users WHERE name = $1',
|
|
|
- ['Alice']
|
|
|
-)
|
|
|
-
|
|
|
-result.each do |row|
|
|
|
- puts "#{row['id']}: #{row['name']} (#{row['email']})"
|
|
|
-end
|
|
|
-
|
|
|
-conn.close
|
|
|
-```
|
|
|
-
|
|
|
-**PHP (PDO):**
|
|
|
-```php
|
|
|
-<?php
|
|
|
-// Connect to PizzaSQL
|
|
|
-$dsn = "pgsql:host=localhost;port=5432;dbname=pizzasql";
|
|
|
-$pdo = new PDO($dsn);
|
|
|
-
|
|
|
-// Use SQLite-compatible SQL
|
|
|
-$pdo->exec("
|
|
|
- CREATE TABLE users (
|
|
|
- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
- name TEXT NOT NULL,
|
|
|
- email TEXT
|
|
|
- )
|
|
|
-");
|
|
|
-
|
|
|
-// Parameterized queries
|
|
|
-$stmt = $pdo->prepare(
|
|
|
- "INSERT INTO users (name, email) VALUES (?, ?)"
|
|
|
-);
|
|
|
-$stmt->execute(['Alice', 'alice@example.com']);
|
|
|
-
|
|
|
-// Query results
|
|
|
-$stmt = $pdo->prepare(
|
|
|
- "SELECT * FROM users WHERE name = ?"
|
|
|
-);
|
|
|
-$stmt->execute(['Alice']);
|
|
|
-$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
-
|
|
|
-foreach ($rows as $row) {
|
|
|
- echo "{$row['id']}: {$row['name']} ({$row['email']})\n";
|
|
|
-}
|
|
|
-?>
|
|
|
-```
|
|
|
-
|
|
|
-**Rust (tokio-postgres):**
|
|
|
-```rust
|
|
|
-use tokio_postgres::{NoTls, Error};
|
|
|
-
|
|
|
-#[tokio::main]
|
|
|
-async fn main() -> Result<(), Error> {
|
|
|
- // Connect to PizzaSQL
|
|
|
- let (client, connection) = tokio_postgres::connect(
|
|
|
- "host=localhost port=5432 dbname=pizzasql",
|
|
|
- NoTls
|
|
|
- ).await?;
|
|
|
-
|
|
|
- tokio::spawn(async move {
|
|
|
- if let Err(e) = connection.await {
|
|
|
- eprintln!("connection error: {}", e);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // Use SQLite-compatible SQL
|
|
|
- client.execute(
|
|
|
- "CREATE TABLE users (
|
|
|
- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
- name TEXT NOT NULL,
|
|
|
- email TEXT
|
|
|
- )", &[]
|
|
|
- ).await?;
|
|
|
-
|
|
|
- // Parameterized queries
|
|
|
- client.execute(
|
|
|
- "INSERT INTO users (name, email) VALUES ($1, $2)",
|
|
|
- &[&"Alice", &"alice@example.com"]
|
|
|
- ).await?;
|
|
|
-
|
|
|
- // Query results
|
|
|
- let rows = client.query(
|
|
|
- "SELECT * FROM users WHERE name = $1",
|
|
|
- &[&"Alice"]
|
|
|
- ).await?;
|
|
|
-
|
|
|
- for row in rows {
|
|
|
- let id: i32 = row.get(0);
|
|
|
- let name: &str = row.get(1);
|
|
|
- let email: &str = row.get(2);
|
|
|
- println!("{}: {} ({})", id, name, email);
|
|
|
- }
|
|
|
-
|
|
|
- Ok(())
|
|
|
-}
|
|
|
+db, _ := sql.Open("postgres", "host=localhost port=5432 dbname=pizzasql sslmode=disable")
|
|
|
+db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")
|
|
|
+db.Exec("INSERT INTO users (name) VALUES ($1)", "Alice")
|
|
|
+rows, _ := db.Query("SELECT * FROM users")
|
|
|
```
|
|
|
|
|
|
### Command-Line Tools
|
|
|
|
|
|
-**psql (PostgreSQL CLI):**
|
|
|
```bash
|
|
|
-# Connect interactively
|
|
|
+# Interactive
|
|
|
psql -h localhost -p 5432 -d pizzasql
|
|
|
|
|
|
-# Execute single command
|
|
|
-psql -h localhost -p 5432 -d pizzasql \
|
|
|
- -c "SELECT * FROM users WHERE age > 25"
|
|
|
+# Single command
|
|
|
+psql -h localhost -p 5432 -d pizzasql -c "SELECT * FROM users"
|
|
|
|
|
|
-# Execute SQL file
|
|
|
+# SQL file
|
|
|
psql -h localhost -p 5432 -d pizzasql -f schema.sql
|
|
|
|
|
|
-# CSV output
|
|
|
-psql -h localhost -p 5432 -d pizzasql \
|
|
|
- -c "SELECT * FROM users" --csv > users.csv
|
|
|
-```
|
|
|
-
|
|
|
-**pgcli (Enhanced PostgreSQL CLI):**
|
|
|
-```bash
|
|
|
+# pgcli
|
|
|
pgcli postgresql://localhost:5432/pizzasql
|
|
|
```
|
|
|
|
|
|
-**DBeaver, pgAdmin, DataGrip:**
|
|
|
-- Connection type: PostgreSQL
|
|
|
-- Host: localhost
|
|
|
-- Port: 5432
|
|
|
-- Database: pizzasql
|
|
|
-- No username/password required
|
|
|
+**DBeaver / DataGrip / pgAdmin:** connection type PostgreSQL, host `localhost`, port `5432`, database `pizzasql`, no credentials.
|
|
|
|
|
|
### Important Notes
|
|
|
|
|
|
-1. **SQL Dialect**: PizzaSQL uses **SQLite SQL syntax**, not PostgreSQL syntax
|
|
|
+1. **SQL Dialect**: PizzaSQL uses **SQLite syntax**, not PostgreSQL syntax
|
|
|
- Use `INTEGER PRIMARY KEY AUTOINCREMENT`, not `SERIAL`
|
|
|
- - Use `TEXT` type, not `VARCHAR` with enforced length
|
|
|
- - SQLite type affinity rules apply
|
|
|
-
|
|
|
-2. **Parameter Placeholders**: Client libraries use their standard placeholders
|
|
|
- - Python/Node.js: `$1, $2, $3...`
|
|
|
- - Go: `$1, $2, $3...`
|
|
|
- - PHP: `?` or named parameters
|
|
|
- - These are automatically converted to PizzaSQL's `?` placeholder
|
|
|
-
|
|
|
-3. **Compatibility**: Works with PostgreSQL clients, not PostgreSQL-specific features
|
|
|
- - No schemas, roles, or permissions
|
|
|
- - No PostgreSQL-specific types (ARRAY, JSON, etc.)
|
|
|
- - Use SQLite functions, not PostgreSQL functions
|
|
|
+ - Use `TEXT`, not `VARCHAR` with enforced length
|
|
|
+2. **Parameter placeholders**: use `$1, $2...` (PostgreSQL style) or `?` (SQLite style)
|
|
|
+3. **No PostgreSQL-specific features**: no schemas, roles, `ARRAY`, `JSONB`, etc.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Architecture
|
|
|
|
|
|
-PizzaSQL is built with a clean, modular architecture that processes SQL queries through distinct stages:
|
|
|
-
|
|
|
```mermaid
|
|
|
graph TD
|
|
|
- Client[Client Applications<br/>CLI, HTTP API, PostgreSQL Protocol]
|
|
|
-
|
|
|
+ Client[Client Applications<br/>CLI · HTTP API · PostgreSQL Protocol]
|
|
|
Client --> Lexer
|
|
|
|
|
|
subgraph PizzaSQL Core
|
|
|
- Lexer[Lexer - SQL Tokenizer<br/>• 100+ token types<br/>• 227,000 ops/sec]
|
|
|
- Parser[Parser - AST Builder<br/>• Hand-written recursive descent<br/>• 176,000 statements/sec<br/>• Operator precedence]
|
|
|
- Analyzer[Analyzer - Semantic Analysis<br/>• Type checking<br/>• Scope resolution<br/>• Function validation<br/>• Thread-safe sync.RWMutex]
|
|
|
- Executor[Executor - Query Engine<br/>• Query execution<br/>• Index optimization<br/>• Transaction management<br/>• Expression evaluation]
|
|
|
-
|
|
|
- Lexer --> Parser
|
|
|
- Parser --> Analyzer
|
|
|
- Analyzer --> Executor
|
|
|
+ Lexer[Lexer - SQL Tokenizer<br/>227,000 ops/sec]
|
|
|
+ Parser[Parser - AST Builder<br/>176,000 statements/sec]
|
|
|
+ Analyzer[Analyzer - Semantic Analysis<br/>Type checking · Scope resolution]
|
|
|
+ Executor[Executor - Query Engine<br/>Index optimization · Transactions]
|
|
|
+ Lexer --> Parser --> Analyzer --> Executor
|
|
|
end
|
|
|
|
|
|
- Executor --> Storage[Storage Layer - PizzaKV<br/>• Custom high-performance Zig backend<br/>• Radix trie indexes<br/>• Persistent storage<br/>• Connection pooling]
|
|
|
+ Executor --> Storage[PizzaKV Storage<br/>Unix socket · Radix trie · Persistent]
|
|
|
|
|
|
style Client fill:#e1f5ff,stroke:#0288d1,stroke-width:2px
|
|
|
style PizzaSQL Core fill:#fff3e0,stroke:#f57c00,stroke-width:2px
|
|
|
style Storage fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
|
|
|
- style Lexer fill:#fff9c4,stroke:#fbc02d
|
|
|
- style Parser fill:#fff9c4,stroke:#fbc02d
|
|
|
- style Analyzer fill:#fff9c4,stroke:#fbc02d
|
|
|
- style Executor fill:#fff9c4,stroke:#fbc02d
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## Documentation
|
|
|
|
|
|
-### CLI Usage
|
|
|
+### CLI Reference
|
|
|
|
|
|
-**Interactive REPL:**
|
|
|
```bash
|
|
|
-./pizzasql -kv
|
|
|
-```
|
|
|
-
|
|
|
-Built-in commands:
|
|
|
-- `help` - Show available commands
|
|
|
-- `quit` - Exit the REPL
|
|
|
-- `tables` - List all tables
|
|
|
-- `clear` - Clear screen
|
|
|
-
|
|
|
-**Single Statement:**
|
|
|
-```bash
|
|
|
-./pizzasql -e "SELECT * FROM users LIMIT 10"
|
|
|
-```
|
|
|
-
|
|
|
-**Piped Input:**
|
|
|
-```bash
|
|
|
-cat schema.sql | ./pizzasql
|
|
|
-```
|
|
|
-
|
|
|
-**Expression-Only Mode:**
|
|
|
-```bash
|
|
|
-./pizzasql -e "SELECT 2 + 2 * 10"
|
|
|
-# Result: 22
|
|
|
+# Storage
|
|
|
+-kv Launch PizzaKV automatically (Unix socket)
|
|
|
+-kvaddr string Connect to existing PizzaKV (e.g. localhost:8085)
|
|
|
+-kvflags string Extra flags forwarded to pizzakv (e.g. "-iwal")
|
|
|
+-db string Database name (default "pizzasql")
|
|
|
+-pool int KV connection pool size (default 5)
|
|
|
+
|
|
|
+# HTTP server
|
|
|
+-http Enable HTTP server
|
|
|
+-http-host string Host (default "localhost")
|
|
|
+-http-port int Port (default 8080)
|
|
|
+-http-cors Enable CORS headers (default true)
|
|
|
+-http-compression Enable gzip compression (default true)
|
|
|
+-http-auth Enable API key authentication
|
|
|
+-api-keys string Comma-separated API keys
|
|
|
+
|
|
|
+# PostgreSQL wire protocol
|
|
|
+-pg Enable PostgreSQL server
|
|
|
+-pg-host string Host (default "localhost")
|
|
|
+-pg-port int Port (default 5432)
|
|
|
+
|
|
|
+# Export / Import
|
|
|
+-o string Output file (export)
|
|
|
+-i string Input file (import)
|
|
|
+-table string Table name (required for CSV)
|
|
|
+-format string Format: sql, csv (auto-detected from extension)
|
|
|
+-drop Include DROP TABLE in SQL export
|
|
|
+-create-table Create table from CSV schema on import
|
|
|
+-ignore-errors Continue import on row errors
|
|
|
+
|
|
|
+# Misc
|
|
|
+-quiet Suppress request/query logging
|
|
|
```
|
|
|
|
|
|
### HTTP API
|
|
|
|
|
|
-#### POST /query - Execute SQL Query
|
|
|
-
|
|
|
-```bash
|
|
|
-curl -X POST http://localhost:8080/query \
|
|
|
- -H "Content-Type: application/json" \
|
|
|
- -d '{
|
|
|
- "sql": "SELECT * FROM users WHERE age > ?",
|
|
|
- "params": [25]
|
|
|
- }'
|
|
|
-```
|
|
|
+| Method | Path | Description |
|
|
|
+|--------|------|-------------|
|
|
|
+| POST | `/query` | Execute SQL, return rows |
|
|
|
+| POST | `/execute` | Batch statements |
|
|
|
+| GET | `/schema/tables` | List all tables |
|
|
|
+| GET | `/schema/tables/{name}` | Table schema |
|
|
|
+| GET | `/health` | Health check |
|
|
|
+| GET | `/stats` | Runtime statistics |
|
|
|
+| GET | `/metrics` | Prometheus metrics |
|
|
|
+| POST | `/transaction/begin` | Begin transaction |
|
|
|
+| POST | `/transaction/commit` | Commit |
|
|
|
+| POST | `/transaction/rollback` | Rollback |
|
|
|
|
|
|
-Response:
|
|
|
-```json
|
|
|
-{
|
|
|
- "columns": [
|
|
|
- {"name": "id", "type": "INTEGER"},
|
|
|
- {"name": "name", "type": "TEXT"}
|
|
|
- ],
|
|
|
- "rows": [[1, "Alice"], [2, "Bob"]],
|
|
|
- "rowsAffected": 0,
|
|
|
- "lastInsertId": 0,
|
|
|
- "executionTime": "1.2ms"
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-#### POST /execute - Batch Execution
|
|
|
-
|
|
|
-```bash
|
|
|
-curl -X POST http://localhost:8080/execute \
|
|
|
- -H "Content-Type: application/json" \
|
|
|
- -d '{
|
|
|
- "statements": [
|
|
|
- {"sql": "INSERT INTO users (name) VALUES (?)", "params": ["Alice"]},
|
|
|
- {"sql": "INSERT INTO users (name) VALUES (?)", "params": ["Bob"]}
|
|
|
- ],
|
|
|
- "transaction": true
|
|
|
- }'
|
|
|
-```
|
|
|
+**Multi-database:** pass `X-Database: <name>` header to route queries to a specific database. Databases are created on first access.
|
|
|
|
|
|
-#### GET /schema/tables - List Tables
|
|
|
+### Database Export / Import
|
|
|
|
|
|
```bash
|
|
|
-curl http://localhost:8080/schema/tables
|
|
|
-```
|
|
|
+# Export full database
|
|
|
+pizzasql -db mydb -o backup.sql
|
|
|
|
|
|
-#### GET /schema/tables/{name} - Table Schema
|
|
|
+# Export with DROP TABLE
|
|
|
+pizzasql -db mydb -o backup.sql -drop
|
|
|
|
|
|
-```bash
|
|
|
-curl http://localhost:8080/schema/tables/users
|
|
|
-```
|
|
|
+# Export single table
|
|
|
+pizzasql -db mydb -table users -o users.sql
|
|
|
|
|
|
-#### GET /health - Health Check
|
|
|
-
|
|
|
-```bash
|
|
|
-curl http://localhost:8080/health
|
|
|
-```
|
|
|
+# Export to CSV
|
|
|
+pizzasql -db mydb -table users -o users.csv
|
|
|
|
|
|
-#### GET /metrics - Prometheus Metrics
|
|
|
+# Import SQL
|
|
|
+pizzasql -db mydb -i backup.sql
|
|
|
|
|
|
-```bash
|
|
|
-curl http://localhost:8080/metrics
|
|
|
+# Import CSV (create table from header)
|
|
|
+pizzasql -db mydb -table users -i users.csv -create-table
|
|
|
```
|
|
|
|
|
|
-### Database Export/Import
|
|
|
-
|
|
|
-**Export entire database:**
|
|
|
-```bash
|
|
|
-./pizzasql -db mydb -o backup.sql
|
|
|
-```
|
|
|
+### SQL Support
|
|
|
|
|
|
-**Export with DROP TABLE statements:**
|
|
|
-```bash
|
|
|
-./pizzasql -db mydb -o backup.sql -drop
|
|
|
-```
|
|
|
+**Data Types:** `INTEGER` (INT, BIGINT, BOOLEAN) · `REAL` (FLOAT, DOUBLE, DECIMAL) · `TEXT` (VARCHAR, CHAR) · `BLOB` · `NUMERIC`
|
|
|
|
|
|
-**Export specific table:**
|
|
|
-```bash
|
|
|
-./pizzasql -db mydb -table users -o users.sql
|
|
|
-```
|
|
|
+**Joins:** INNER · LEFT · RIGHT · FULL OUTER · CROSS
|
|
|
|
|
|
-**Export to CSV:**
|
|
|
-```bash
|
|
|
-./pizzasql -db mydb -table users -o users.csv
|
|
|
-```
|
|
|
+**Aggregates:** COUNT · COUNT(DISTINCT) · SUM · AVG · MIN · MAX
|
|
|
|
|
|
-**Import SQL file:**
|
|
|
-```bash
|
|
|
-./pizzasql -db mydb -i backup.sql
|
|
|
-```
|
|
|
+**Functions:** UPPER, LOWER, LENGTH, SUBSTR, TRIM, REPLACE, CONCAT, ABS, ROUND, CEIL, FLOOR, MOD, COALESCE, NULLIF, IFNULL, printf, hex, random, glob, instr, zeroblob
|
|
|
|
|
|
-**Import CSV:**
|
|
|
-```bash
|
|
|
-./pizzasql -db mydb -table users -i users.csv -create-table
|
|
|
-```
|
|
|
-
|
|
|
-### SQL Support
|
|
|
-
|
|
|
-**Data Types:**
|
|
|
-- `INTEGER` (INT, SMALLINT, BIGINT, BOOLEAN)
|
|
|
-- `REAL` (FLOAT, DOUBLE, DECIMAL)
|
|
|
-- `TEXT` (VARCHAR, CHAR, CHARACTER)
|
|
|
-- `BLOB` (binary data)
|
|
|
-- `NUMERIC` (flexible numeric)
|
|
|
-
|
|
|
-**Joins:**
|
|
|
-- INNER JOIN
|
|
|
-- LEFT JOIN / LEFT OUTER JOIN
|
|
|
-- RIGHT JOIN / RIGHT OUTER JOIN
|
|
|
-- FULL OUTER JOIN
|
|
|
-- CROSS JOIN
|
|
|
-
|
|
|
-**Aggregates:**
|
|
|
-- COUNT, COUNT(DISTINCT)
|
|
|
-- SUM, AVG, MIN, MAX
|
|
|
-
|
|
|
-**Functions:**
|
|
|
-- String: UPPER, LOWER, LENGTH, SUBSTR, TRIM, REPLACE, CONCAT
|
|
|
-- Numeric: ABS, ROUND, CEIL, FLOOR, MOD
|
|
|
-- Null handling: COALESCE, NULLIF, IFNULL
|
|
|
-- SQLite: printf, hex, random, glob, instr, zeroblob
|
|
|
-
|
|
|
-**Transaction Support:**
|
|
|
+**Transactions:**
|
|
|
```sql
|
|
|
BEGIN;
|
|
|
-INSERT INTO accounts (name, balance) VALUES ('Alice', 1000);
|
|
|
UPDATE accounts SET balance = balance - 100 WHERE name = 'Alice';
|
|
|
-COMMIT;
|
|
|
-
|
|
|
--- Rollback on error
|
|
|
-BEGIN;
|
|
|
-UPDATE accounts SET balance = balance - 100 WHERE name = 'Alice';
|
|
|
-ROLLBACK;
|
|
|
-
|
|
|
--- Savepoints
|
|
|
-BEGIN;
|
|
|
-INSERT INTO users (name) VALUES ('Alice');
|
|
|
-SAVEPOINT sp1;
|
|
|
-INSERT INTO users (name) VALUES ('Bob');
|
|
|
-ROLLBACK TO SAVEPOINT sp1;
|
|
|
+UPDATE accounts SET balance = balance + 100 WHERE name = 'Bob';
|
|
|
COMMIT;
|
|
|
```
|
|
|
|
|
|
**Indexes:**
|
|
|
```sql
|
|
|
--- Create index
|
|
|
CREATE INDEX idx_users_email ON users(email);
|
|
|
-
|
|
|
--- Multi-column index
|
|
|
-CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
|
|
|
-
|
|
|
--- Unique index
|
|
|
CREATE UNIQUE INDEX idx_users_email ON users(email);
|
|
|
-
|
|
|
--- Drop index
|
|
|
DROP INDEX idx_users_email;
|
|
|
```
|
|
|
|
|
|
-**PRAGMA Statements:**
|
|
|
+**PRAGMA:**
|
|
|
```sql
|
|
|
--- Table schema
|
|
|
PRAGMA table_info(users);
|
|
|
-
|
|
|
--- List tables
|
|
|
PRAGMA table_list;
|
|
|
-
|
|
|
--- Database list
|
|
|
PRAGMA database_list;
|
|
|
-
|
|
|
--- Version
|
|
|
-PRAGMA version;
|
|
|
-```
|
|
|
-
|
|
|
-**Query Plans:**
|
|
|
-```sql
|
|
|
--- Show execution plan
|
|
|
-EXPLAIN SELECT * FROM users WHERE id = 1;
|
|
|
-
|
|
|
--- Detailed query plan
|
|
|
-EXPLAIN QUERY PLAN SELECT * FROM users WHERE id = 1;
|
|
|
-```
|
|
|
-
|
|
|
-### Configuration
|
|
|
-
|
|
|
-**Command-Line Options:**
|
|
|
-```bash
|
|
|
-# Database options
|
|
|
--kvaddr string PizzaKV server address (default "localhost:8085")
|
|
|
--kv Launch PizzaKV automatically
|
|
|
--kvflags string Flags to pass to PizzaKV (e.g., "-iwal -port=9090")
|
|
|
--db string Database name (default "pizzasql")
|
|
|
--e string Execute single statement and exit
|
|
|
-
|
|
|
-# PostgreSQL server options
|
|
|
--pg Enable PostgreSQL wire protocol server
|
|
|
--pg-host string PostgreSQL server host (default "localhost")
|
|
|
--pg-port int PostgreSQL server port (default 5432)
|
|
|
-
|
|
|
-# HTTP server options
|
|
|
--http Start HTTP server
|
|
|
--http-host string HTTP server host (default "localhost")
|
|
|
--http-port int HTTP server port (default 8080)
|
|
|
--http-cors Enable CORS headers
|
|
|
--http-auth Enable authentication
|
|
|
--api-keys string Comma-separated API keys
|
|
|
-
|
|
|
-# Export/Import options
|
|
|
--o string Output file for export
|
|
|
--i string Input file for import
|
|
|
--table string Specific table to export/import
|
|
|
--format string Export/import format: sql, csv
|
|
|
--drop Include DROP TABLE statements in export
|
|
|
--create-table Create table if not exists (CSV import)
|
|
|
--ignore-errors Continue import on errors
|
|
|
-
|
|
|
-# Other options
|
|
|
--version Print version and exit
|
|
|
--help Show help message
|
|
|
--quiet Disable query logging
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
-PizzaSQL has comprehensive test coverage across all components.
|
|
|
-
|
|
|
-### Running Tests
|
|
|
-
|
|
|
```bash
|
|
|
-# All tests
|
|
|
-make test
|
|
|
-
|
|
|
-# Specific component
|
|
|
-make test-lexer
|
|
|
-make test-parser
|
|
|
-go test ./pkg/analyzer/...
|
|
|
+make test # all tests
|
|
|
+make test-v # verbose
|
|
|
+make test-cover # coverage report → coverage.html
|
|
|
+make test-race # race detector
|
|
|
+make bench # benchmarks
|
|
|
+
|
|
|
+# Component tests
|
|
|
+go test ./pkg/lexer/...
|
|
|
+go test ./pkg/parser/...
|
|
|
go test ./pkg/executor/...
|
|
|
-
|
|
|
-# With verbose output
|
|
|
-make test-v
|
|
|
-
|
|
|
-# With coverage report
|
|
|
-make test-cover
|
|
|
-open coverage.html
|
|
|
-
|
|
|
-# With race detection
|
|
|
-make test-race
|
|
|
-
|
|
|
-# Benchmarks
|
|
|
-make bench
|
|
|
```
|
|
|
|
|
|
-### Test Coverage
|
|
|
-
|
|
|
-| Component | Coverage | Test Count |
|
|
|
-|-----------|----------|------------|
|
|
|
-| Lexer | ~95% | 15 test functions |
|
|
|
-| Parser | ~90% | 35+ test functions |
|
|
|
-| Analyzer | ~85% | 20+ test functions |
|
|
|
-| Executor | ~80% | 25+ test functions |
|
|
|
-| HTTP Server | ~75% | 15+ test functions |
|
|
|
-| PostgreSQL Server | ~70% | 10+ test functions |
|
|
|
-
|
|
|
### SQLLogicTest
|
|
|
|
|
|
-PizzaSQL passes **100% of the SQLite SQLLogicTest suite** - over 5 million individual SQL tests. This comprehensive test suite validates:
|
|
|
+```bash
|
|
|
+# Run against a live server
|
|
|
+make sqllogictest URL=http://localhost:8080
|
|
|
+
|
|
|
+# Quick smoke test
|
|
|
+make sqllogictest-basic
|
|
|
|
|
|
-- Complex SQL query correctness
|
|
|
-- SQLite compatibility and edge cases
|
|
|
-- Type coercion and affinity
|
|
|
-- Aggregates and joins
|
|
|
-- Transaction semantics
|
|
|
-- Index optimization
|
|
|
+# Download full SQLite corpus (~5M tests)
|
|
|
+make sqllogictest-download
|
|
|
+make sqllogictest
|
|
|
+```
|