Przeglądaj źródła

update readme, remove clients

Danilo Fragoso 4 miesięcy temu
rodzic
commit
7a93688d85

Plik diff jest za duży
+ 540 - 882
README.md


+ 334 - 0
benchmarks/quick_bench.sh

@@ -0,0 +1,334 @@
+#!/bin/bash
+set -e
+
+echo "========================================"
+echo "PizzaSQL Quick Benchmarks"
+echo "========================================"
+echo ""
+
+# Configuration
+PIZZASQL_HTTP_URL="http://localhost:8080"
+PIZZASQL_PG_HOST="localhost"
+PIZZASQL_PG_PORT="5433"
+POSTGRES_HOST="localhost"
+POSTGRES_PORT="5432"
+POSTGRES_DB="postgres"
+POSTGRES_USER="$USER"
+SQLITE_DB="/tmp/pizzasql_benchmark.db"
+NUM_ROWS=1000
+NUM_QUERIES=100
+
+# Colors
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+echo -e "${BLUE}Configuration:${NC}"
+echo "  Rows to insert: $NUM_ROWS"
+echo "  Query iterations: $NUM_QUERIES"
+echo "  PizzaSQL HTTP: $PIZZASQL_HTTP_URL"
+echo "  PizzaSQL PostgreSQL: $PIZZASQL_PG_HOST:$PIZZASQL_PG_PORT"
+echo "  PostgreSQL: $POSTGRES_HOST:$POSTGRES_PORT"
+echo "  SQLite DB: $SQLITE_DB"
+echo ""
+
+# Cleanup
+rm -f $SQLITE_DB
+curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+    -H "Content-Type: application/json" \
+    -d '{"sql":"DROP TABLE IF EXISTS users"}' > /dev/null 2>&1 || true
+PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -c "DROP TABLE IF EXISTS users_pg" > /dev/null 2>&1 || true
+PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -c "DROP TABLE IF EXISTS users_bench" > /dev/null 2>&1 || true
+
+echo -e "${GREEN}=== Setup: Creating Tables ===${NC}"
+echo ""
+
+# SQLite
+sqlite3 $SQLITE_DB "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, age INTEGER, active INTEGER)"
+echo "✓ SQLite table created"
+
+# PizzaSQL HTTP
+curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+    -H "Content-Type: application/json" \
+    -d '{"sql":"DROP TABLE IF EXISTS users"}' > /dev/null 2>&1 || true
+curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+    -H "Content-Type: application/json" \
+    -d '{"sql":"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, age INTEGER, active INTEGER)"}' > /dev/null
+echo "✓ PizzaSQL HTTP table created"
+
+# PizzaSQL PostgreSQL
+PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -c "DROP TABLE IF EXISTS users_pg" > /dev/null 2>&1 || true
+PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -c "CREATE TABLE users_pg (id INTEGER PRIMARY KEY, name TEXT, email TEXT, age INTEGER, active INTEGER)" > /dev/null 2>&1
+echo "✓ PizzaSQL PostgreSQL table created"
+
+# PostgreSQL
+PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -c "DROP TABLE IF EXISTS users_bench" > /dev/null 2>&1 || true
+PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE TABLE users_bench (id SERIAL PRIMARY KEY, name TEXT, email TEXT, age INTEGER, active INTEGER)" > /dev/null 2>&1
+echo "✓ PostgreSQL table created"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 1: INSERT $NUM_ROWS Rows ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_ROWS); do
+    sqlite3 $SQLITE_DB "INSERT INTO users (name, email, age, active) VALUES ('User$i', 'user$i@test.com', $((20 + $i % 50)), 1)" 2>/dev/null
+done
+end=$(date +%s%N)
+sqlite_insert_ms=$(( (end - start) / 1000000 ))
+sqlite_insert_ops=$(echo "scale=0; $NUM_ROWS * 1000 / $sqlite_insert_ms" | bc)
+echo "  SQLite:              ${sqlite_insert_ms}ms total (${sqlite_insert_ops} inserts/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_ROWS); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d "{\"sql\":\"INSERT INTO users (name, email, age, active) VALUES ('User$i', 'user$i@test.com', $((20 + $i % 50)), 1)\"}" > /dev/null
+done
+end=$(date +%s%N)
+http_insert_ms=$(( (end - start) / 1000000 ))
+http_insert_ops=$(echo "scale=0; $NUM_ROWS * 1000 / $http_insert_ms" | bc)
+echo "  PizzaSQL (HTTP):     ${http_insert_ms}ms total (${http_insert_ops} inserts/sec)"
+
+# PizzaSQL PostgreSQL (using single connection with transaction)
+start=$(date +%s%N)
+{
+    echo "BEGIN;"
+    for i in $(seq 1 $NUM_ROWS); do
+        echo "INSERT INTO users_pg (name, email, age, active) VALUES ('User$i', 'user$i@test.com', $((20 + $i % 50)), 1);"
+    done
+    echo "COMMIT;"
+} | PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -q > /dev/null 2>&1
+end=$(date +%s%N)
+pg_insert_ms=$(( (end - start) / 1000000 ))
+pg_insert_ops=$(echo "scale=0; $NUM_ROWS * 1000 / $pg_insert_ms" | bc)
+echo "  PizzaSQL (Postgres): ${pg_insert_ms}ms total (${pg_insert_ops} inserts/sec)"
+
+# PostgreSQL
+start=$(date +%s%N)
+{
+    echo "BEGIN;"
+    for i in $(seq 1 $NUM_ROWS); do
+        echo "INSERT INTO users_bench (name, email, age, active) VALUES ('User$i', 'user$i@test.com', $((20 + $i % 50)), 1);"
+    done
+    echo "COMMIT;"
+} | PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -q > /dev/null 2>&1
+end=$(date +%s%N)
+postgres_insert_ms=$(( (end - start) / 1000000 ))
+postgres_insert_ops=$(echo "scale=0; $NUM_ROWS * 1000 / $postgres_insert_ms" | bc)
+echo "  PostgreSQL:          ${postgres_insert_ms}ms total (${postgres_insert_ops} inserts/sec)"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 2: SELECT (Full Scan) ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_QUERIES); do
+    sqlite3 $SQLITE_DB "SELECT * FROM users WHERE age > 30" > /dev/null
+done
+end=$(date +%s%N)
+sqlite_select_ms=$(( (end - start) / 1000000 ))
+sqlite_select_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $sqlite_select_ms" | bc)
+echo "  SQLite:              ${sqlite_select_ms}ms total (${sqlite_select_ops} queries/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_QUERIES); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d '{"sql":"SELECT * FROM users WHERE age > 30"}' > /dev/null
+done
+end=$(date +%s%N)
+http_select_ms=$(( (end - start) / 1000000 ))
+http_select_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $http_select_ms" | bc)
+echo "  PizzaSQL (HTTP):     ${http_select_ms}ms total (${http_select_ops} queries/sec)"
+
+# PizzaSQL PostgreSQL (using single connection)
+start=$(date +%s%N)
+{
+    for i in $(seq 1 $NUM_QUERIES); do
+        echo "SELECT * FROM users_pg WHERE age > 30;"
+    done
+} | PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -q > /dev/null 2>&1
+end=$(date +%s%N)
+pg_select_ms=$(( (end - start) / 1000000 ))
+pg_select_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $pg_select_ms" | bc)
+echo "  PizzaSQL (Postgres): ${pg_select_ms}ms total (${pg_select_ops} queries/sec)"
+
+# PostgreSQL
+start=$(date +%s%N)
+{
+    for i in $(seq 1 $NUM_QUERIES); do
+        echo "SELECT * FROM users_bench WHERE age > 30;"
+    done
+} | PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -q > /dev/null 2>&1
+end=$(date +%s%N)
+postgres_select_ms=$(( (end - start) / 1000000 ))
+postgres_select_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $postgres_select_ms" | bc)
+echo "  PostgreSQL:          ${postgres_select_ms}ms total (${postgres_select_ops} queries/sec)"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 3: CREATE INDEX ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+sqlite3 $SQLITE_DB "CREATE INDEX idx_age ON users(age)" > /dev/null
+end=$(date +%s%N)
+sqlite_index_ms=$(( (end - start) / 1000000 ))
+echo "  SQLite:              ${sqlite_index_ms}ms"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+    -H "Content-Type: application/json" \
+    -d '{"sql":"CREATE INDEX idx_age ON users(age)"}' > /dev/null
+end=$(date +%s%N)
+http_index_ms=$(( (end - start) / 1000000 ))
+echo "  PizzaSQL (HTTP):     ${http_index_ms}ms"
+
+# PizzaSQL PostgreSQL
+start=$(date +%s%N)
+PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -c "CREATE INDEX idx_age_pg ON users_pg(age)" > /dev/null 2>&1
+end=$(date +%s%N)
+pg_index_ms=$(( (end - start) / 1000000 ))
+echo "  PizzaSQL (Postgres): ${pg_index_ms}ms"
+
+# PostgreSQL
+start=$(date +%s%N)
+PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX idx_age_bench ON users_bench(age)" > /dev/null 2>&1
+end=$(date +%s%N)
+postgres_index_ms=$(( (end - start) / 1000000 ))
+echo "  PostgreSQL:          ${postgres_index_ms}ms"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 4: SELECT with INDEX ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_QUERIES); do
+    sqlite3 $SQLITE_DB "SELECT * FROM users WHERE age = 35" > /dev/null
+done
+end=$(date +%s%N)
+sqlite_indexed_ms=$(( (end - start) / 1000000 ))
+sqlite_indexed_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $sqlite_indexed_ms" | bc)
+echo "  SQLite:              ${sqlite_indexed_ms}ms total (${sqlite_indexed_ops} queries/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_QUERIES); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d '{"sql":"SELECT * FROM users WHERE age = 35"}' > /dev/null
+done
+end=$(date +%s%N)
+http_indexed_ms=$(( (end - start) / 1000000 ))
+http_indexed_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $http_indexed_ms" | bc)
+echo "  PizzaSQL (HTTP):     ${http_indexed_ms}ms total (${http_indexed_ops} queries/sec)"
+
+# PizzaSQL PostgreSQL (using single connection)
+start=$(date +%s%N)
+{
+    for i in $(seq 1 $NUM_QUERIES); do
+        echo "SELECT * FROM users_pg WHERE age = 35;"
+    done
+} | PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -q > /dev/null 2>&1
+end=$(date +%s%N)
+pg_indexed_ms=$(( (end - start) / 1000000 ))
+pg_indexed_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $pg_indexed_ms" | bc)
+echo "  PizzaSQL (Postgres): ${pg_indexed_ms}ms total (${pg_indexed_ops} queries/sec)"
+
+# PostgreSQL
+start=$(date +%s%N)
+{
+    for i in $(seq 1 $NUM_QUERIES); do
+        echo "SELECT * FROM users_bench WHERE age = 35;"
+    done
+} | PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -q > /dev/null 2>&1
+end=$(date +%s%N)
+postgres_indexed_ms=$(( (end - start) / 1000000 ))
+postgres_indexed_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $postgres_indexed_ms" | bc)
+echo "  PostgreSQL:          ${postgres_indexed_ms}ms total (${postgres_indexed_ops} queries/sec)"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 5: Aggregate Query ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_QUERIES); do
+    sqlite3 $SQLITE_DB "SELECT age, COUNT(*) as count, AVG(id) as avg_id FROM users GROUP BY age" > /dev/null
+done
+end=$(date +%s%N)
+sqlite_agg_ms=$(( (end - start) / 1000000 ))
+sqlite_agg_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $sqlite_agg_ms" | bc)
+echo "  SQLite:              ${sqlite_agg_ms}ms total (${sqlite_agg_ops} queries/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_QUERIES); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d '{"sql":"SELECT age, COUNT(*) as count, AVG(id) as avg_id FROM users GROUP BY age"}' > /dev/null
+done
+end=$(date +%s%N)
+http_agg_ms=$(( (end - start) / 1000000 ))
+http_agg_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $http_agg_ms" | bc)
+echo "  PizzaSQL (HTTP):     ${http_agg_ms}ms total (${http_agg_ops} queries/sec)"
+
+# PizzaSQL PostgreSQL (using single connection)
+start=$(date +%s%N)
+{
+    for i in $(seq 1 $NUM_QUERIES); do
+        echo "SELECT age, COUNT(*) as count, AVG(id) as avg_id FROM users_pg GROUP BY age;"
+    done
+} | PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -q > /dev/null 2>&1
+end=$(date +%s%N)
+pg_agg_ms=$(( (end - start) / 1000000 ))
+pg_agg_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $pg_agg_ms" | bc)
+echo "  PizzaSQL (Postgres): ${pg_agg_ms}ms total (${pg_agg_ops} queries/sec)"
+
+# PostgreSQL
+start=$(date +%s%N)
+{
+    for i in $(seq 1 $NUM_QUERIES); do
+        echo "SELECT age, COUNT(*) as count, AVG(id) as avg_id FROM users_bench GROUP BY age;"
+    done
+} | PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -q > /dev/null 2>&1
+end=$(date +%s%N)
+postgres_agg_ms=$(( (end - start) / 1000000 ))
+postgres_agg_ops=$(echo "scale=0; $NUM_QUERIES * 1000 / $postgres_agg_ms" | bc)
+echo "  PostgreSQL:          ${postgres_agg_ms}ms total (${postgres_agg_ops} queries/sec)"
+
+echo ""
+echo -e "${BLUE}========================================${NC}"
+echo -e "${BLUE}Summary${NC}"
+echo -e "${BLUE}========================================${NC}"
+echo ""
+
+printf "%-25s %-20s %-20s %-20s %-20s\n" "Operation" "SQLite" "PizzaSQL (HTTP)" "PizzaSQL (PG Wire)" "PostgreSQL"
+printf "%-25s %-20s %-20s %-20s %-20s\n" "-------------------------" "--------------------" "--------------------" "--------------------" "--------------------"
+printf "%-25s %-20s %-20s %-20s %-20s\n" "INSERT ($NUM_ROWS rows)" "${sqlite_insert_ops} ops/s" "${http_insert_ops} ops/s" "${pg_insert_ops} ops/s" "${postgres_insert_ops} ops/s"
+printf "%-25s %-20s %-20s %-20s %-20s\n" "SELECT (no index)" "${sqlite_select_ops} q/s" "${http_select_ops} q/s" "${pg_select_ops} q/s" "${postgres_select_ops} q/s"
+printf "%-25s %-20s %-20s %-20s %-20s\n" "CREATE INDEX" "${sqlite_index_ms} ms" "${http_index_ms} ms" "${pg_index_ms} ms" "${postgres_index_ms} ms"
+printf "%-25s %-20s %-20s %-20s %-20s\n" "SELECT (indexed)" "${sqlite_indexed_ops} q/s" "${http_indexed_ops} q/s" "${pg_indexed_ops} q/s" "${postgres_indexed_ops} q/s"
+printf "%-25s %-20s %-20s %-20s %-20s\n" "AGGREGATE" "${sqlite_agg_ops} q/s" "${http_agg_ops} q/s" "${pg_agg_ops} q/s" "${postgres_agg_ops} q/s"
+
+echo ""
+echo -e "${GREEN}Benchmarks completed!${NC}"
+
+# Cleanup
+echo ""
+echo -e "${YELLOW}Cleaning up...${NC}"
+rm -f $SQLITE_DB
+curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+    -H "Content-Type: application/json" \
+    -d '{"sql":"DROP TABLE IF EXISTS users"}' > /dev/null 2>&1 || true
+PGPASSWORD="" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -U postgres -d pizzasql -c "DROP TABLE IF EXISTS users_pg" > /dev/null 2>&1 || true
+PGPASSWORD="" psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -c "DROP TABLE IF EXISTS users_bench" > /dev/null 2>&1 || true
+echo "✓ Cleanup complete"

+ 293 - 0
benchmarks/run_benchmarks.sh

@@ -0,0 +1,293 @@
+#!/bin/bash
+set -e
+
+echo "========================================"
+echo "PizzaSQL Benchmarks"
+echo "========================================"
+echo ""
+
+# Configuration
+PIZZASQL_HTTP_URL="http://localhost:8080"
+PIZZASQL_PG_HOST="localhost"
+PIZZASQL_PG_PORT="5432"
+SQLITE_DB="benchmark.db"
+NUM_ROWS=10000
+NUM_ITERATIONS=1000
+
+# Colors
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+# Check dependencies
+command -v sqlite3 >/dev/null 2>&1 || { echo "sqlite3 is required but not installed. Aborting." >&2; exit 1; }
+command -v psql >/dev/null 2>&1 || { echo "psql is required but not installed. Aborting." >&2; exit 1; }
+command -v curl >/dev/null 2>&1 || { echo "curl is required but not installed. Aborting." >&2; exit 1; }
+
+echo -e "${BLUE}Configuration:${NC}"
+echo "  Rows to insert: $NUM_ROWS"
+echo "  Query iterations: $NUM_ITERATIONS"
+echo "  PizzaSQL HTTP: $PIZZASQL_HTTP_URL"
+echo "  PizzaSQL PostgreSQL: $PIZZASQL_PG_HOST:$PIZZASQL_PG_PORT"
+echo "  SQLite DB: $SQLITE_DB"
+echo ""
+
+# Function to time a command
+time_command() {
+    local name=$1
+    shift
+    local start=$(date +%s%N)
+    "$@" > /dev/null 2>&1
+    local end=$(date +%s%N)
+    local duration=$(( (end - start) / 1000000 ))
+    echo "$name: ${duration}ms"
+    echo $duration
+}
+
+# Function to calculate throughput
+calc_throughput() {
+    local operations=$1
+    local time_ms=$2
+    local ops_per_sec=$(echo "scale=2; $operations * 1000 / $time_ms" | bc)
+    echo "$ops_per_sec"
+}
+
+# Cleanup
+cleanup() {
+    echo -e "${YELLOW}Cleaning up...${NC}"
+    rm -f $SQLITE_DB
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d '{"sql":"DROP TABLE IF EXISTS benchmark_users"}' > /dev/null 2>&1 || true
+    psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "DROP TABLE IF EXISTS benchmark_users" > /dev/null 2>&1 || true
+}
+
+trap cleanup EXIT
+
+echo -e "${GREEN}=== Benchmark 1: CREATE TABLE ===${NC}"
+echo ""
+
+# SQLite
+sqlite3 $SQLITE_DB "CREATE TABLE benchmark_users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, age INTEGER, created_at TEXT)"
+sqlite_create_time=$(time_command "SQLite CREATE" sqlite3 $SQLITE_DB "CREATE TABLE IF NOT EXISTS benchmark_test (id INTEGER PRIMARY KEY, value TEXT)")
+
+# PizzaSQL HTTP
+pizzasql_http_create_time=$(time_command "PizzaSQL HTTP CREATE" curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+    -H "Content-Type: application/json" \
+    -d '{"sql":"CREATE TABLE benchmark_users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, age INTEGER, created_at TEXT)"}')
+
+# PizzaSQL PostgreSQL
+pizzasql_pg_create_time=$(time_command "PizzaSQL PostgreSQL CREATE" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "CREATE TABLE IF NOT EXISTS benchmark_test (id INTEGER PRIMARY KEY, value TEXT)")
+
+echo ""
+echo -e "${GREEN}=== Benchmark 2: INSERT $NUM_ROWS Rows ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_ROWS); do
+    sqlite3 $SQLITE_DB "INSERT INTO benchmark_users (name, email, age, created_at) VALUES ('User$i', 'user$i@example.com', $((20 + $i % 50)), '2024-01-01')" 2>/dev/null
+done
+end=$(date +%s%N)
+sqlite_insert_time=$(( (end - start) / 1000000 ))
+sqlite_insert_ops=$(calc_throughput $NUM_ROWS $sqlite_insert_time)
+echo "SQLite INSERT: ${sqlite_insert_time}ms (${sqlite_insert_ops} ops/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_ROWS); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d "{\"sql\":\"INSERT INTO benchmark_users (name, email, age, created_at) VALUES ('User$i', 'user$i@example.com', $((20 + $i % 50)), '2024-01-01')\"}" > /dev/null
+done
+end=$(date +%s%N)
+pizzasql_http_insert_time=$(( (end - start) / 1000000 ))
+pizzasql_http_insert_ops=$(calc_throughput $NUM_ROWS $pizzasql_http_insert_time)
+echo "PizzaSQL HTTP INSERT: ${pizzasql_http_insert_time}ms (${pizzasql_http_insert_ops} ops/sec)"
+
+# PizzaSQL PostgreSQL
+start=$(date +%s%N)
+for i in $(seq 1 $NUM_ROWS); do
+    psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "INSERT INTO benchmark_users (name, email, age, created_at) VALUES ('User$i', 'user$i@example.com', $((20 + $i % 50)), '2024-01-01')" > /dev/null 2>&1
+done
+end=$(date +%s%N)
+pizzasql_pg_insert_time=$(( (end - start) / 1000000 ))
+pizzasql_pg_insert_ops=$(calc_throughput $NUM_ROWS $pizzasql_pg_insert_time)
+echo "PizzaSQL PostgreSQL INSERT: ${pizzasql_pg_insert_time}ms (${pizzasql_pg_insert_ops} ops/sec)"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 3: SELECT (Full Table Scan) ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    sqlite3 $SQLITE_DB "SELECT * FROM benchmark_users WHERE age > 30" > /dev/null
+done
+end=$(date +%s%N)
+sqlite_select_time=$(( (end - start) / 1000000 ))
+sqlite_select_ops=$(calc_throughput 100 $sqlite_select_time)
+echo "SQLite SELECT: ${sqlite_select_time}ms (${sqlite_select_ops} queries/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d '{"sql":"SELECT * FROM benchmark_users WHERE age > 30"}' > /dev/null
+done
+end=$(date +%s%N)
+pizzasql_http_select_time=$(( (end - start) / 1000000 ))
+pizzasql_http_select_ops=$(calc_throughput 100 $pizzasql_http_select_time)
+echo "PizzaSQL HTTP SELECT: ${pizzasql_http_select_time}ms (${pizzasql_http_select_ops} queries/sec)"
+
+# PizzaSQL PostgreSQL
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "SELECT * FROM benchmark_users WHERE age > 30" > /dev/null 2>&1
+done
+end=$(date +%s%N)
+pizzasql_pg_select_time=$(( (end - start) / 1000000 ))
+pizzasql_pg_select_ops=$(calc_throughput 100 $pizzasql_pg_select_time)
+echo "PizzaSQL PostgreSQL SELECT: ${pizzasql_pg_select_time}ms (${pizzasql_pg_select_ops} queries/sec)"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 4: CREATE INDEX ===${NC}"
+echo ""
+
+# SQLite
+sqlite_index_time=$(time_command "SQLite CREATE INDEX" sqlite3 $SQLITE_DB "CREATE INDEX idx_age ON benchmark_users(age)")
+
+# PizzaSQL HTTP
+pizzasql_http_index_time=$(time_command "PizzaSQL HTTP CREATE INDEX" curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+    -H "Content-Type: application/json" \
+    -d '{"sql":"CREATE INDEX idx_age ON benchmark_users(age)"}')
+
+# PizzaSQL PostgreSQL
+pizzasql_pg_index_time=$(time_command "PizzaSQL PostgreSQL CREATE INDEX" psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "CREATE INDEX idx_age_pg ON benchmark_users(age)")
+
+echo ""
+echo -e "${GREEN}=== Benchmark 5: SELECT with INDEX ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    sqlite3 $SQLITE_DB "SELECT * FROM benchmark_users WHERE age = 35" > /dev/null
+done
+end=$(date +%s%N)
+sqlite_indexed_time=$(( (end - start) / 1000000 ))
+sqlite_indexed_ops=$(calc_throughput 100 $sqlite_indexed_time)
+echo "SQLite SELECT (indexed): ${sqlite_indexed_time}ms (${sqlite_indexed_ops} queries/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d '{"sql":"SELECT * FROM benchmark_users WHERE age = 35"}' > /dev/null
+done
+end=$(date +%s%N)
+pizzasql_http_indexed_time=$(( (end - start) / 1000000 ))
+pizzasql_http_indexed_ops=$(calc_throughput 100 $pizzasql_http_indexed_time)
+echo "PizzaSQL HTTP SELECT (indexed): ${pizzasql_http_indexed_time}ms (${pizzasql_http_indexed_ops} queries/sec)"
+
+# PizzaSQL PostgreSQL
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "SELECT * FROM benchmark_users WHERE age = 35" > /dev/null 2>&1
+done
+end=$(date +%s%N)
+pizzasql_pg_indexed_time=$(( (end - start) / 1000000 ))
+pizzasql_pg_indexed_ops=$(calc_throughput 100 $pizzasql_pg_indexed_time)
+echo "PizzaSQL PostgreSQL SELECT (indexed): ${pizzasql_pg_indexed_time}ms (${pizzasql_pg_indexed_ops} queries/sec)"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 6: UPDATE ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    sqlite3 $SQLITE_DB "UPDATE benchmark_users SET age = age + 1 WHERE id = $i" > /dev/null
+done
+end=$(date +%s%N)
+sqlite_update_time=$(( (end - start) / 1000000 ))
+sqlite_update_ops=$(calc_throughput 100 $sqlite_update_time)
+echo "SQLite UPDATE: ${sqlite_update_time}ms (${sqlite_update_ops} ops/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d "{\"sql\":\"UPDATE benchmark_users SET age = age + 1 WHERE id = $i\"}" > /dev/null
+done
+end=$(date +%s%N)
+pizzasql_http_update_time=$(( (end - start) / 1000000 ))
+pizzasql_http_update_ops=$(calc_throughput 100 $pizzasql_http_update_time)
+echo "PizzaSQL HTTP UPDATE: ${pizzasql_http_update_time}ms (${pizzasql_http_update_ops} ops/sec)"
+
+# PizzaSQL PostgreSQL
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "UPDATE benchmark_users SET age = age + 1 WHERE id = $i" > /dev/null 2>&1
+done
+end=$(date +%s%N)
+pizzasql_pg_update_time=$(( (end - start) / 1000000 ))
+pizzasql_pg_update_ops=$(calc_throughput 100 $pizzasql_pg_update_time)
+echo "PizzaSQL PostgreSQL UPDATE: ${pizzasql_pg_update_time}ms (${pizzasql_pg_update_ops} ops/sec)"
+
+echo ""
+echo -e "${GREEN}=== Benchmark 7: Aggregate Query ===${NC}"
+echo ""
+
+# SQLite
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    sqlite3 $SQLITE_DB "SELECT age, COUNT(*) as count, AVG(id) as avg_id FROM benchmark_users GROUP BY age" > /dev/null
+done
+end=$(date +%s%N)
+sqlite_agg_time=$(( (end - start) / 1000000 ))
+sqlite_agg_ops=$(calc_throughput 100 $sqlite_agg_time)
+echo "SQLite AGGREGATE: ${sqlite_agg_time}ms (${sqlite_agg_ops} queries/sec)"
+
+# PizzaSQL HTTP
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    curl -s -X POST "$PIZZASQL_HTTP_URL/query" \
+        -H "Content-Type: application/json" \
+        -d '{"sql":"SELECT age, COUNT(*) as count, AVG(id) as avg_id FROM benchmark_users GROUP BY age"}' > /dev/null
+done
+end=$(date +%s%N)
+pizzasql_http_agg_time=$(( (end - start) / 1000000 ))
+pizzasql_http_agg_ops=$(calc_throughput 100 $pizzasql_http_agg_time)
+echo "PizzaSQL HTTP AGGREGATE: ${pizzasql_http_agg_time}ms (${pizzasql_http_agg_ops} queries/sec)"
+
+# PizzaSQL PostgreSQL
+start=$(date +%s%N)
+for i in $(seq 1 100); do
+    psql -h $PIZZASQL_PG_HOST -p $PIZZASQL_PG_PORT -d pizzasql -c "SELECT age, COUNT(*) as count, AVG(id) as avg_id FROM benchmark_users GROUP BY age" > /dev/null 2>&1
+done
+end=$(date +%s%N)
+pizzasql_pg_agg_time=$(( (end - start) / 1000000 ))
+pizzasql_pg_agg_ops=$(calc_throughput 100 $pizzasql_pg_agg_time)
+echo "PizzaSQL PostgreSQL AGGREGATE: ${pizzasql_pg_agg_time}ms (${pizzasql_pg_agg_ops} queries/sec)"
+
+echo ""
+echo -e "${BLUE}========================================${NC}"
+echo -e "${BLUE}Summary${NC}"
+echo -e "${BLUE}========================================${NC}"
+echo ""
+
+printf "%-30s %-15s %-15s %-15s\n" "Operation" "SQLite" "PizzaSQL HTTP" "PizzaSQL PG"
+printf "%-30s %-15s %-15s %-15s\n" "------------------------------" "---------------" "---------------" "---------------"
+printf "%-30s %-15s %-15s %-15s\n" "INSERT ($NUM_ROWS rows)" "${sqlite_insert_ops} ops/s" "${pizzasql_http_insert_ops} ops/s" "${pizzasql_pg_insert_ops} ops/s"
+printf "%-30s %-15s %-15s %-15s\n" "SELECT (no index)" "${sqlite_select_ops} q/s" "${pizzasql_http_select_ops} q/s" "${pizzasql_pg_select_ops} q/s"
+printf "%-30s %-15s %-15s %-15s\n" "SELECT (indexed)" "${sqlite_indexed_ops} q/s" "${pizzasql_http_indexed_ops} q/s" "${pizzasql_pg_indexed_ops} q/s"
+printf "%-30s %-15s %-15s %-15s\n" "UPDATE" "${sqlite_update_ops} ops/s" "${pizzasql_http_update_ops} ops/s" "${pizzasql_pg_update_ops} ops/s"
+printf "%-30s %-15s %-15s %-15s\n" "AGGREGATE" "${sqlite_agg_ops} q/s" "${pizzasql_http_agg_ops} q/s" "${pizzasql_pg_agg_ops} q/s"
+
+echo ""
+echo -e "${GREEN}Benchmarks completed!${NC}"

+ 0 - 191
clients/README.md

@@ -1,191 +0,0 @@
-# PizzaSQL Client Libraries
-
-Official client libraries for PizzaSQL in multiple programming languages.
-
-## Available Clients
-
-| Language | Directory | Package Manager | Status |
-|----------|-----------|----------------|---------|
-| JavaScript/TypeScript | [js/](js/) | npm | ✅ Ready |
-| Python | [python/](python/) | pip | ✅ Ready |
-| Go | [go/](go/) | go get | ✅ Ready |
-| Ruby | [ruby/](ruby/) | gem | ✅ Ready |
-
-## Quick Start
-
-All client libraries follow a similar API design pattern for consistency across languages.
-
-### JavaScript/TypeScript (Node.js/Bun)
-
-```javascript
-import { connect } from 'pizzasql';
-
-const db = connect('http://localhost:8080/mydb', 'api-key');
-const users = await db.sql('SELECT * FROM users');
-const userIds = users.map(u => u.id);
-```
-
-[Full documentation](js/README.md)
-
-### Python
-
-```python
-from pizzasql import connect
-
-db = connect('http://localhost:8080/mydb', api_key='api-key')
-users = db.sql('SELECT * FROM users')
-user_ids = [u['id'] for u in users]
-```
-
-[Full documentation](python/README.md)
-
-### Go
-
-```go
-import "github.com/pizzasql/pizzasql-go"
-
-db, err := pizzasql.Connect("http://localhost:8080/mydb", "api-key")
-if err != nil {
-    log.Fatal(err)
-}
-
-rows, err := db.SQL("SELECT * FROM users")
-if err != nil {
-    log.Fatal(err)
-}
-
-for _, row := range rows {
-    fmt.Println(row["id"])
-}
-```
-
-[Full documentation](go/README.md)
-
-### Ruby
-
-```ruby
-require 'pizzasql'
-
-db = PizzaSQL.connect('http://localhost:8080/mydb', 'api-key')
-users = db.sql('SELECT * FROM users')
-user_ids = users.map { |u| u['id'] }
-```
-
-[Full documentation](ruby/README.md)
-
-## Common Features
-
-All client libraries support:
-
-- **SQL Queries** - Execute any SQL query and get JSON results
-- **Export** - Export database or specific tables to SQL or CSV
-- **Import** - Import data from SQL or CSV files
-- **Authentication** - Optional API key authentication
-- **Error Handling** - Proper error handling with meaningful messages
-
-## Installation
-
-### JavaScript/TypeScript
-
-```bash
-npm install pizzasql
-# or
-bun add pizzasql
-```
-
-### Python
-
-```bash
-pip install pizzasql
-```
-
-### Go
-
-```bash
-go get github.com/pizzasql/pizzasql-go
-```
-
-### Ruby
-
-```bash
-gem install pizzasql
-```
-
-## API Consistency
-
-All clients implement the same core methods:
-
-| Method | JavaScript | Python | Go | Ruby |
-|--------|------------|--------|-----|------|
-| Connect | `connect(uri, apiKey)` | `connect(uri, api_key)` | `Connect(uri, apiKey)` | `connect(uri, api_key)` |
-| Query | `sql(query)` | `sql(query)` | `SQL(query)` | `sql(query)` |
-| Export | `export(table, format)` | `export(table, format)` | `Export(table, format)` | `export(table:, format:)` |
-| Import | `import(data, format, createTable)` | `import_data(data, format, create_table)` | `Import(data, format, createTable)` | `import(data, format:, create_table:)` |
-
-## URI Format
-
-All clients accept URIs in the following formats:
-
-```
-http://localhost:8080/mydb
-https://pizzabase.cloud/my_org/sql/my_db:32131
-```
-
-The database name is extracted from the last segment of the URI path.
-
-## Authentication
-
-All clients support optional API key authentication via Bearer token:
-
-```javascript
-// With API key
-const db = connect('http://localhost:8080/mydb', 'your-api-key-here');
-
-// Without API key
-const db = connect('http://localhost:8080/mydb');
-```
-
-## Response Format
-
-All clients return query results as an array of objects/dictionaries/maps:
-
-```json
-[
-  {"id": 1, "name": "Alice", "email": "alice@example.com"},
-  {"id": 2, "name": "Bob", "email": "bob@example.com"}
-]
-```
-
-## Export/Import Formats
-
-Supported formats:
-- `sql` - SQL statements (default)
-- `csv` - Comma-separated values
-
-### Export Examples
-
-```javascript
-// Export entire database as SQL
-const sqlData = await db.export('', 'sql');
-
-// Export specific table as CSV
-const csvData = await db.export('users', 'csv');
-```
-
-### Import Examples
-
-```javascript
-// Import SQL file
-await db.import(sqlData, 'sql', false);
-
-// Import CSV with auto table creation
-await db.import(csvData, 'csv', true);
-```
-
-## Contributing
-
-Each client library is maintained independently. See individual client directories for development setup and contribution guidelines.
-
-## License
-
-MIT

+ 0 - 205
clients/go/README.md

@@ -1,205 +0,0 @@
-# PizzaSQL Go Client
-
-Go client library for PizzaSQL - a simple, lightweight SQL database with HTTP API.
-
-## Installation
-
-```bash
-go get github.com/pizzasql/pizzasql-go
-```
-
-## Usage
-
-### Basic Example
-
-```go
-package main
-
-import (
-    "fmt"
-    "log"
-
-    "github.com/pizzasql/pizzasql-go"
-)
-
-func main() {
-    // Connect to database
-    db, err := pizzasql.Connect("http://localhost:8080/mydb", "")
-    if err != nil {
-        log.Fatal(err)
-    }
-
-    // Execute a query
-    rows, err := db.SQL("SELECT * FROM users")
-    if err != nil {
-        log.Fatal(err)
-    }
-
-    // Process results
-    for _, row := range rows {
-        fmt.Printf("User ID: %v, Name: %v\n", row["id"], row["name"])
-    }
-}
-```
-
-### With API Key
-
-```go
-db, err := pizzasql.Connect(
-    "https://pizzabase.cloud/my_org/sql/my_db:32131",
-    "a78tsda68bdt6ad5afsd65saf5sd5a7d6sd87asy8d9aysnd7ay==",
-)
-if err != nil {
-    log.Fatal(err)
-}
-
-rows, err := db.SQL("SELECT 42 as answer")
-if err != nil {
-    log.Fatal(err)
-}
-```
-
-### Creating Tables and Inserting Data
-
-```go
-// Create table
-_, err = db.SQL(`
-    CREATE TABLE users (
-        id INTEGER PRIMARY KEY,
-        name TEXT NOT NULL,
-        email TEXT UNIQUE
-    )
-`)
-if err != nil {
-    log.Fatal(err)
-}
-
-// Insert data
-_, err = db.SQL(`
-    INSERT INTO users (id, name, email)
-    VALUES (1, 'Alice', 'alice@example.com')
-`)
-if err != nil {
-    log.Fatal(err)
-}
-```
-
-### Filtering and Mapping Results
-
-```go
-rows, err := db.SQL("SELECT * FROM users")
-if err != nil {
-    log.Fatal(err)
-}
-
-// Extract specific field
-var userIDs []interface{}
-for _, row := range rows {
-    userIDs = append(userIDs, row["id"])
-}
-
-fmt.Println(userIDs) // [1, 2, 3, ...]
-```
-
-### Export Database
-
-```go
-// Export entire database as SQL
-data, err := db.Export("", "sql")
-if err != nil {
-    log.Fatal(err)
-}
-
-// Export specific table as CSV
-csvData, err := db.Export("users", "csv")
-if err != nil {
-    log.Fatal(err)
-}
-
-// Save to file
-err = os.WriteFile("users.csv", csvData, 0644)
-if err != nil {
-    log.Fatal(err)
-}
-```
-
-### Import Data
-
-```go
-// Read CSV file
-data, err := os.ReadFile("users.csv")
-if err != nil {
-    log.Fatal(err)
-}
-
-// Import with table creation
-err = db.Import(data, "csv", true)
-if err != nil {
-    log.Fatal(err)
-}
-```
-
-## API Reference
-
-### Connect(uri string, apiKey string) (*Client, error)
-
-Creates a new PizzaSQL client connection.
-
-**Parameters:**
-- `uri` - Database URI (e.g., `http://localhost:8080/mydb`)
-- `apiKey` - Optional API key for authentication
-
-**Returns:**
-- `*Client` - Connected client instance
-- `error` - Error if connection fails
-
-### (*Client) SQL(query string) ([]Row, error)
-
-Executes a SQL query and returns the results.
-
-**Parameters:**
-- `query` - SQL query string
-
-**Returns:**
-- `[]Row` - Slice of rows (each row is `map[string]interface{}`)
-- `error` - Error if query fails
-
-### (*Client) Export(table string, format string) ([]byte, error)
-
-Exports database or table data.
-
-**Parameters:**
-- `table` - Table name (empty string for entire database)
-- `format` - Export format: `"sql"` or `"csv"`
-
-**Returns:**
-- `[]byte` - Exported data
-- `error` - Error if export fails
-
-### (*Client) Import(data []byte, format string, createTable bool) error
-
-Imports data into the database.
-
-**Parameters:**
-- `data` - Data to import
-- `format` - Import format: `"sql"` or `"csv"`
-- `createTable` - Create table if it doesn't exist (CSV only)
-
-**Returns:**
-- `error` - Error if import fails
-
-## Error Handling
-
-All methods return errors following Go conventions. Always check for errors:
-
-```go
-rows, err := db.SQL("SELECT * FROM users")
-if err != nil {
-    log.Printf("Query failed: %v", err)
-    return
-}
-```
-
-## License
-
-MIT

+ 0 - 5
clients/go/go.mod

@@ -1,5 +0,0 @@
-module github.com/pizzasql/pizzasql-go
-
-go 1.21
-
-// No external dependencies - uses only Go standard library

+ 0 - 182
clients/go/pizzasql.go

@@ -1,182 +0,0 @@
-package pizzasql
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"io"
-	"net/http"
-	"net/url"
-	"strings"
-)
-
-// Client represents a connection to a PizzaSQL database
-type Client struct {
-	baseURL string
-	dbName  string
-	apiKey  string
-	client  *http.Client
-}
-
-// Row represents a single row in the result set
-type Row map[string]interface{}
-
-// QueryResult represents the result of a SQL query
-type QueryResult struct {
-	Rows []Row `json:"rows"`
-}
-
-// Connect creates a new PizzaSQL client connection
-// URI format: http://host:port/dbname or https://pizzabase.cloud/my_org/sql/my_db:32131
-func Connect(uri string, apiKey string) (*Client, error) {
-	parsedURL, err := url.Parse(uri)
-	if err != nil {
-		return nil, fmt.Errorf("invalid URI: %w", err)
-	}
-
-	// Extract database name from path
-	path := strings.Trim(parsedURL.Path, "/")
-	if path == "" {
-		return nil, fmt.Errorf("database name not found in URI path")
-	}
-
-	// Split path to get database name (last segment)
-	pathParts := strings.Split(path, "/")
-	dbName := pathParts[len(pathParts)-1]
-
-	// Reconstruct base URL without the database path
-	baseURL := fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host)
-
-	return &Client{
-		baseURL: baseURL,
-		dbName:  dbName,
-		apiKey:  apiKey,
-		client:  &http.Client{},
-	}, nil
-}
-
-// SQL executes a SQL query and returns the results as a slice of rows
-func (c *Client) SQL(query string) ([]Row, error) {
-	// Prepare request body
-	body := map[string]string{"query": query}
-	jsonBody, err := json.Marshal(body)
-	if err != nil {
-		return nil, fmt.Errorf("failed to marshal request: %w", err)
-	}
-
-	// Create request
-	url := fmt.Sprintf("%s/%s/query", c.baseURL, c.dbName)
-	req, err := http.NewRequest("POST", url, bytes.NewReader(jsonBody))
-	if err != nil {
-		return nil, fmt.Errorf("failed to create request: %w", err)
-	}
-
-	// Set headers
-	req.Header.Set("Content-Type", "application/json")
-	if c.apiKey != "" {
-		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
-	}
-
-	// Execute request
-	resp, err := c.client.Do(req)
-	if err != nil {
-		return nil, fmt.Errorf("request failed: %w", err)
-	}
-	defer resp.Body.Close()
-
-	// Read response body
-	respBody, err := io.ReadAll(resp.Body)
-	if err != nil {
-		return nil, fmt.Errorf("failed to read response: %w", err)
-	}
-
-	// Check status code
-	if resp.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody))
-	}
-
-	// Parse response
-	var result QueryResult
-	if err := json.Unmarshal(respBody, &result); err != nil {
-		return nil, fmt.Errorf("failed to parse response: %w", err)
-	}
-
-	return result.Rows, nil
-}
-
-// Export exports a database or table to SQL or CSV format
-func (c *Client) Export(table string, format string) ([]byte, error) {
-	params := url.Values{}
-	if table != "" {
-		params.Set("table", table)
-	}
-	if format != "" {
-		params.Set("format", format)
-	}
-
-	url := fmt.Sprintf("%s/%s/export?%s", c.baseURL, c.dbName, params.Encode())
-	req, err := http.NewRequest("GET", url, nil)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create request: %w", err)
-	}
-
-	if c.apiKey != "" {
-		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
-	}
-
-	resp, err := c.client.Do(req)
-	if err != nil {
-		return nil, fmt.Errorf("request failed: %w", err)
-	}
-	defer resp.Body.Close()
-
-	data, err := io.ReadAll(resp.Body)
-	if err != nil {
-		return nil, fmt.Errorf("failed to read response: %w", err)
-	}
-
-	if resp.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("export failed with status %d: %s", resp.StatusCode, string(data))
-	}
-
-	return data, nil
-}
-
-// Import imports data from SQL or CSV format
-func (c *Client) Import(data []byte, format string, createTable bool) error {
-	params := url.Values{}
-	if format != "" {
-		params.Set("format", format)
-	}
-	if createTable {
-		params.Set("create_table", "true")
-	}
-
-	url := fmt.Sprintf("%s/%s/import?%s", c.baseURL, c.dbName, params.Encode())
-	req, err := http.NewRequest("POST", url, bytes.NewReader(data))
-	if err != nil {
-		return fmt.Errorf("failed to create request: %w", err)
-	}
-
-	req.Header.Set("Content-Type", "application/octet-stream")
-	if c.apiKey != "" {
-		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
-	}
-
-	resp, err := c.client.Do(req)
-	if err != nil {
-		return fmt.Errorf("request failed: %w", err)
-	}
-	defer resp.Body.Close()
-
-	respBody, err := io.ReadAll(resp.Body)
-	if err != nil {
-		return fmt.Errorf("failed to read response: %w", err)
-	}
-
-	if resp.StatusCode != http.StatusOK {
-		return fmt.Errorf("import failed with status %d: %s", resp.StatusCode, string(respBody))
-	}
-
-	return nil
-}

+ 0 - 169
clients/js/README.md

@@ -1,169 +0,0 @@
-# PizzaSQL JavaScript/TypeScript Client
-
-A lightweight client for PizzaSQL that works with Node.js, Bun, and browsers.
-
-## Installation
-
-```bash
-# npm
-npm install pizzasql
-
-# bun
-bun add pizzasql
-
-# pnpm
-pnpm add pizzasql
-```
-
-## Quick Start
-
-```typescript
-import { connect } from 'pizzasql';
-
-const db = connect('http://localhost:8080/mydb', 'your-api-key');
-
-// Simple query
-const users = await db.sql('SELECT * FROM users');
-console.log(users);
-// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
-
-// Query with parameters
-const user = await db.sql('SELECT * FROM users WHERE id = ?', [1]);
-
-// Chain operations
-const names = (await db.sql('SELECT name FROM users')).map(u => u.name);
-```
-
-## API Reference
-
-### `connect(uri, apiKey?)`
-
-Create a new database connection.
-
-```typescript
-const db = connect('http://localhost:8080/mydb', 'optional-api-key');
-```
-
-### `db.sql(query, params?)`
-
-Execute a query and return rows as objects.
-
-```typescript
-// Simple query
-const rows = await db.sql('SELECT * FROM users');
-
-// With parameters (prevents SQL injection)
-const rows = await db.sql('SELECT * FROM users WHERE age > ?', [18]);
-
-// With TypeScript types
-interface User {
-  id: number;
-  name: string;
-  email: string;
-}
-const users = await db.sql<User>('SELECT * FROM users');
-```
-
-### `db.query(query, params?)`
-
-Execute a query and return full result with metadata.
-
-```typescript
-const result = await db.query('SELECT * FROM users');
-console.log(result.columns);      // [{ name: 'id', type: 'INTEGER' }, ...]
-console.log(result.rows);         // [{ id: 1, name: 'Alice' }, ...]
-console.log(result.executionTime); // '1.234ms'
-```
-
-### `db.execute(statements, transaction?)`
-
-Execute multiple statements in a batch.
-
-```typescript
-const result = await db.execute([
-  { sql: 'INSERT INTO users (name) VALUES (?)', params: ['Alice'] },
-  { sql: 'INSERT INTO users (name) VALUES (?)', params: ['Bob'] },
-], true); // true = wrap in transaction
-
-console.log(result.totalRowsAffected); // 2
-```
-
-### `db.tables()`
-
-List all tables.
-
-```typescript
-const tables = await db.tables();
-// ['users', 'posts', 'comments']
-```
-
-### `db.schema(tableName)`
-
-Get table schema.
-
-```typescript
-const columns = await db.schema('users');
-// [{ name: 'id', type: 'INTEGER' }, { name: 'name', type: 'TEXT' }]
-```
-
-### `db.use(database)`
-
-Switch to a different database.
-
-```typescript
-const otherDb = db.use('other_database');
-const rows = await otherDb.sql('SELECT * FROM other_table');
-```
-
-## Examples
-
-### CRUD Operations
-
-```typescript
-// Create
-await db.sql('INSERT INTO users (name, email) VALUES (?, ?)', ['Alice', 'alice@example.com']);
-
-// Read
-const users = await db.sql('SELECT * FROM users WHERE active = ?', [true]);
-
-// Update
-await db.sql('UPDATE users SET name = ? WHERE id = ?', ['Alicia', 1]);
-
-// Delete
-await db.sql('DELETE FROM users WHERE id = ?', [1]);
-```
-
-### Transactions
-
-```typescript
-await db.execute([
-  { sql: 'UPDATE accounts SET balance = balance - ? WHERE id = ?', params: [100, 1] },
-  { sql: 'UPDATE accounts SET balance = balance + ? WHERE id = ?', params: [100, 2] },
-], true);
-```
-
-### Error Handling
-
-```typescript
-try {
-  await db.sql('SELECT * FROM nonexistent_table');
-} catch (error) {
-  console.error(error.message);
-  // [TABLE_NOT_FOUND] Table 'nonexistent_table' does not exist
-}
-```
-
-## Configuration
-
-```typescript
-import { createClient } from 'pizzasql';
-
-const db = createClient('http://localhost:8080/mydb', {
-  apiKey: 'your-api-key',
-  timeout: 60000, // 60 seconds
-});
-```
-
-## License
-
-MIT

+ 0 - 215
clients/js/index.ts

@@ -1,215 +0,0 @@
-/**
- * PizzaSQL Client for JavaScript/TypeScript
- * Works with Node.js, Bun, and browsers
- */
-
-export interface PizzaSQLConfig {
-  apiKey?: string;
-  timeout?: number;
-}
-
-export interface Column {
-  name: string;
-  type: string;
-}
-
-export interface QueryResult<T = Record<string, unknown>> {
-  columns: Column[];
-  rows: T[];
-  rowsAffected: number;
-  lastInsertId: number;
-  executionTime: string;
-}
-
-export interface ExecuteResult {
-  results: { rowsAffected: number; lastInsertId: number }[];
-  totalRowsAffected: number;
-  executionTime: string;
-}
-
-export interface TableInfo {
-  tables: string[];
-  count: number;
-}
-
-export interface PizzaSQLError {
-  code: string;
-  message: string;
-  details?: Record<string, unknown>;
-}
-
-class PizzaSQLClient {
-  private baseUrl: string;
-  private apiKey?: string;
-  private timeout: number;
-  private database?: string;
-
-  constructor(uri: string, config: PizzaSQLConfig = {}) {
-    // Parse URI: https://host:port/database or https://host:port
-    const url = new URL(uri);
-    this.baseUrl = `${url.protocol}//${url.host}`;
-    this.database = url.pathname.slice(1) || undefined;
-    this.apiKey = config.apiKey;
-    this.timeout = config.timeout || 30000;
-  }
-
-  private async request<T>(
-    path: string,
-    options: RequestInit = {}
-  ): Promise<T> {
-    const headers: Record<string, string> = {
-      'Content-Type': 'application/json',
-      ...(options.headers as Record<string, string>),
-    };
-
-    if (this.apiKey) {
-      headers['Authorization'] = `Bearer ${this.apiKey}`;
-    }
-
-    if (this.database) {
-      headers['X-Database'] = this.database;
-    }
-
-    const controller = new AbortController();
-    const timeoutId = setTimeout(() => controller.abort(), this.timeout);
-
-    try {
-      const response = await fetch(`${this.baseUrl}${path}`, {
-        ...options,
-        headers,
-        signal: controller.signal,
-      });
-
-      const data = await response.json();
-
-      if (!response.ok) {
-        const error = data.error as PizzaSQLError;
-        throw new Error(`[${error.code}] ${error.message}`);
-      }
-
-      return data as T;
-    } finally {
-      clearTimeout(timeoutId);
-    }
-  }
-
-  /**
-   * Execute a SQL query with optional parameters
-   */
-  async query<T = Record<string, unknown>>(
-    sql: string,
-    params: unknown[] = []
-  ): Promise<QueryResult<T>> {
-    const result = await this.request<{
-      columns: Column[];
-      rows: unknown[][];
-      rowsAffected: number;
-      lastInsertId: number;
-      executionTime: string;
-    }>('/query', {
-      method: 'POST',
-      body: JSON.stringify({ sql, params }),
-    });
-
-    // Transform rows from arrays to objects
-    const rows = result.rows.map((row) => {
-      const obj: Record<string, unknown> = {};
-      result.columns.forEach((col, i) => {
-        obj[col.name] = row[i];
-      });
-      return obj as T;
-    });
-
-    return {
-      ...result,
-      rows,
-    };
-  }
-
-  /**
-   * Shorthand for query - returns rows directly
-   */
-  async sql<T = Record<string, unknown>>(
-    sql: string,
-    params: unknown[] = []
-  ): Promise<T[]> {
-    const result = await this.query<T>(sql, params);
-    return result.rows;
-  }
-
-  /**
-   * Execute multiple statements in a batch
-   */
-  async execute(
-    statements: { sql: string; params?: unknown[] }[],
-    transaction = true
-  ): Promise<ExecuteResult> {
-    return this.request<ExecuteResult>('/execute', {
-      method: 'POST',
-      body: JSON.stringify({
-        statements: statements.map((s) => ({
-          sql: s.sql,
-          params: s.params || [],
-        })),
-        transaction,
-      }),
-    });
-  }
-
-  /**
-   * List all tables in the database
-   */
-  async tables(): Promise<string[]> {
-    const result = await this.request<TableInfo>('/schema/tables');
-    return result.tables;
-  }
-
-  /**
-   * Get schema for a specific table
-   */
-  async schema(tableName: string): Promise<Column[]> {
-    const result = await this.request<{ columns: Column[] }>(
-      `/schema/tables/${encodeURIComponent(tableName)}`
-    );
-    return result.columns;
-  }
-
-  /**
-   * Health check
-   */
-  async health(): Promise<{ status: string; database: string }> {
-    return this.request('/health');
-  }
-
-  /**
-   * Use a different database
-   */
-  use(database: string): PizzaSQLClient {
-    const client = new PizzaSQLClient(this.baseUrl, {
-      apiKey: this.apiKey,
-      timeout: this.timeout,
-    });
-    client.database = database;
-    return client;
-  }
-}
-
-/**
- * Create a new PizzaSQL connection
- */
-export function connect(uri: string, apiKey?: string): PizzaSQLClient {
-  return new PizzaSQLClient(uri, { apiKey });
-}
-
-/**
- * Create a new PizzaSQL connection with full config
- */
-export function createClient(
-  uri: string,
-  config: PizzaSQLConfig = {}
-): PizzaSQLClient {
-  return new PizzaSQLClient(uri, config);
-}
-
-// Default export
-export default { connect, createClient };

+ 0 - 26
clients/js/package.json

@@ -1,26 +0,0 @@
-{
-  "name": "pizzasql",
-  "version": "0.1.0",
-  "description": "PizzaSQL client for JavaScript/TypeScript",
-  "main": "dist/index.js",
-  "module": "dist/index.mjs",
-  "types": "dist/index.d.ts",
-  "exports": {
-    ".": {
-      "import": "./dist/index.mjs",
-      "require": "./dist/index.js",
-      "types": "./dist/index.d.ts"
-    }
-  },
-  "scripts": {
-    "build": "tsup index.ts --format cjs,esm --dts",
-    "test": "bun test"
-  },
-  "keywords": ["pizzasql", "database", "sql", "client"],
-  "author": "PizzaSQL Team",
-  "license": "MIT",
-  "devDependencies": {
-    "tsup": "^8.0.0",
-    "typescript": "^5.0.0"
-  }
-}

+ 0 - 190
clients/python/README.md

@@ -1,190 +0,0 @@
-# PizzaSQL Python Client
-
-A simple, Pythonic client for PizzaSQL.
-
-## Installation
-
-```bash
-pip install pizzasql
-
-# With httpx for better performance (optional)
-pip install pizzasql[httpx]
-```
-
-## Quick Start
-
-```python
-from pizzasql import connect
-
-db = connect('http://localhost:8080/mydb', api_key='your-api-key')
-
-# Simple query
-users = db.sql('SELECT * FROM users')
-print(users)
-# [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]
-
-# Query with parameters
-user = db.sql('SELECT * FROM users WHERE id = ?', [1])
-
-# Iterate over results
-for user in db.sql('SELECT * FROM users'):
-    print(user['name'])
-```
-
-## API Reference
-
-### `connect(uri, api_key=None)`
-
-Create a new database connection.
-
-```python
-db = connect('http://localhost:8080/mydb', api_key='optional-api-key')
-```
-
-### `db.sql(query, params=None)`
-
-Execute a query and return rows as dictionaries.
-
-```python
-# Simple query
-rows = db.sql('SELECT * FROM users')
-
-# With parameters (prevents SQL injection)
-rows = db.sql('SELECT * FROM users WHERE age > ?', [18])
-
-# Use with list comprehensions
-names = [u['name'] for u in db.sql('SELECT name FROM users')]
-```
-
-### `db.query(query, params=None)`
-
-Execute a query and return full result with metadata.
-
-```python
-result = db.query('SELECT * FROM users')
-print(result.columns)        # [Column(name='id', type='INTEGER'), ...]
-print(result.rows)           # [{'id': 1, 'name': 'Alice'}, ...]
-print(result.execution_time) # '1.234ms'
-print(len(result))           # Number of rows
-
-# QueryResult is iterable
-for row in result:
-    print(row)
-```
-
-### `db.execute(statements, transaction=True)`
-
-Execute multiple statements in a batch.
-
-```python
-result = db.execute([
-    {'sql': 'INSERT INTO users (name) VALUES (?)', 'params': ['Alice']},
-    {'sql': 'INSERT INTO users (name) VALUES (?)', 'params': ['Bob']},
-], transaction=True)
-
-print(result['totalRowsAffected'])  # 2
-```
-
-### `db.tables()`
-
-List all tables.
-
-```python
-tables = db.tables()
-# ['users', 'posts', 'comments']
-```
-
-### `db.schema(table_name)`
-
-Get table schema.
-
-```python
-columns = db.schema('users')
-# [Column(name='id', type='INTEGER'), Column(name='name', type='TEXT')]
-```
-
-### `db.use(database)`
-
-Switch to a different database.
-
-```python
-other_db = db.use('other_database')
-rows = other_db.sql('SELECT * FROM other_table')
-```
-
-## Examples
-
-### CRUD Operations
-
-```python
-# Create
-db.sql('INSERT INTO users (name, email) VALUES (?, ?)', ['Alice', 'alice@example.com'])
-
-# Read
-users = db.sql('SELECT * FROM users WHERE active = ?', [True])
-
-# Update
-db.sql('UPDATE users SET name = ? WHERE id = ?', ['Alicia', 1])
-
-# Delete
-db.sql('DELETE FROM users WHERE id = ?', [1])
-```
-
-### Transactions
-
-```python
-db.execute([
-    {'sql': 'UPDATE accounts SET balance = balance - ? WHERE id = ?', 'params': [100, 1]},
-    {'sql': 'UPDATE accounts SET balance = balance + ? WHERE id = ?', 'params': [100, 2]},
-], transaction=True)
-```
-
-### Context Manager
-
-```python
-with connect('http://localhost:8080/mydb') as db:
-    users = db.sql('SELECT * FROM users')
-    # Connection automatically closed
-```
-
-### Error Handling
-
-```python
-from pizzasql import connect, PizzaSQLError
-
-try:
-    db.sql('SELECT * FROM nonexistent_table')
-except PizzaSQLError as e:
-    print(e.code)     # 'TABLE_NOT_FOUND'
-    print(e.message)  # "Table 'nonexistent_table' does not exist"
-```
-
-### Data Processing
-
-```python
-# List comprehension
-emails = [u['email'] for u in db.sql('SELECT email FROM users')]
-
-# Filter
-active_users = [u for u in db.sql('SELECT * FROM users') if u['active']]
-
-# Aggregation
-from collections import Counter
-status_counts = Counter(u['status'] for u in db.sql('SELECT status FROM users'))
-```
-
-## Configuration
-
-```python
-from pizzasql import PizzaSQL
-
-db = PizzaSQL(
-    'http://localhost:8080/mydb',
-    api_key='your-api-key',
-    timeout=60.0  # 60 seconds
-)
-```
-
-## License
-
-MIT

+ 0 - 278
clients/python/pizzasql.py

@@ -1,278 +0,0 @@
-"""
-PizzaSQL Client for Python
-
-A simple, Pythonic client for PizzaSQL.
-"""
-
-from typing import Any, Dict, List, Optional, Union
-from dataclasses import dataclass
-from urllib.parse import urlparse
-import json
-
-try:
-    import httpx
-    _client_class = httpx.Client
-    _async_client_class = httpx.AsyncClient
-except ImportError:
-    import urllib.request
-    import urllib.error
-    _client_class = None
-    _async_client_class = None
-
-
-@dataclass
-class Column:
-    """Represents a column in a query result."""
-    name: str
-    type: str
-
-
-@dataclass
-class QueryResult:
-    """Result of a SQL query."""
-    columns: List[Column]
-    rows: List[Dict[str, Any]]
-    rows_affected: int
-    last_insert_id: int
-    execution_time: str
-
-    def __iter__(self):
-        return iter(self.rows)
-
-    def __len__(self):
-        return len(self.rows)
-
-    def __getitem__(self, index):
-        return self.rows[index]
-
-
-class PizzaSQLError(Exception):
-    """Exception raised for PizzaSQL errors."""
-    def __init__(self, code: str, message: str, details: Optional[Dict] = None):
-        self.code = code
-        self.message = message
-        self.details = details
-        super().__init__(f"[{code}] {message}")
-
-
-class PizzaSQL:
-    """
-    PizzaSQL client for Python.
-
-    Usage:
-        db = PizzaSQL('http://localhost:8080/mydb', api_key='your-key')
-        rows = db.sql('SELECT * FROM users')
-    """
-
-    def __init__(
-        self,
-        uri: str,
-        api_key: Optional[str] = None,
-        timeout: float = 30.0
-    ):
-        """
-        Create a new PizzaSQL connection.
-
-        Args:
-            uri: Database URI (e.g., 'http://localhost:8080/mydb')
-            api_key: Optional API key for authentication
-            timeout: Request timeout in seconds
-        """
-        parsed = urlparse(uri)
-        self._base_url = f"{parsed.scheme}://{parsed.netloc}"
-        self._database = parsed.path.lstrip('/') or None
-        self._api_key = api_key
-        self._timeout = timeout
-
-        if _client_class:
-            self._client = _client_class(timeout=timeout)
-        else:
-            self._client = None
-
-    def _headers(self) -> Dict[str, str]:
-        headers = {'Content-Type': 'application/json'}
-        if self._api_key:
-            headers['Authorization'] = f'Bearer {self._api_key}'
-        if self._database:
-            headers['X-Database'] = self._database
-        return headers
-
-    def _request(self, method: str, path: str, data: Optional[Dict] = None) -> Dict:
-        url = f"{self._base_url}{path}"
-        headers = self._headers()
-
-        if self._client:
-            # Use httpx
-            if method == 'GET':
-                response = self._client.get(url, headers=headers)
-            else:
-                response = self._client.post(url, headers=headers, json=data)
-
-            result = response.json()
-            if response.status_code >= 400:
-                error = result.get('error', {})
-                raise PizzaSQLError(
-                    error.get('code', 'UNKNOWN'),
-                    error.get('message', 'Unknown error'),
-                    error.get('details')
-                )
-            return result
-        else:
-            # Fallback to urllib
-            req = urllib.request.Request(url, headers=headers)
-            if data:
-                req.data = json.dumps(data).encode('utf-8')
-
-            try:
-                with urllib.request.urlopen(req, timeout=self._timeout) as response:
-                    return json.loads(response.read().decode('utf-8'))
-            except urllib.error.HTTPError as e:
-                result = json.loads(e.read().decode('utf-8'))
-                error = result.get('error', {})
-                raise PizzaSQLError(
-                    error.get('code', 'UNKNOWN'),
-                    error.get('message', str(e)),
-                    error.get('details')
-                )
-
-    def _transform_rows(self, columns: List[Dict], rows: List[List]) -> List[Dict[str, Any]]:
-        """Transform array rows to dictionaries."""
-        return [
-            {col['name']: row[i] for i, col in enumerate(columns)}
-            for row in rows
-        ]
-
-    def query(self, sql: str, params: Optional[List] = None) -> QueryResult:
-        """
-        Execute a SQL query and return full result.
-
-        Args:
-            sql: SQL query string
-            params: Optional list of parameters
-
-        Returns:
-            QueryResult with columns, rows, and metadata
-        """
-        result = self._request('POST', '/query', {
-            'sql': sql,
-            'params': params or []
-        })
-
-        columns = [Column(**col) for col in result.get('columns', [])]
-        rows = self._transform_rows(result.get('columns', []), result.get('rows', []))
-
-        return QueryResult(
-            columns=columns,
-            rows=rows,
-            rows_affected=result.get('rowsAffected', 0),
-            last_insert_id=result.get('lastInsertId', 0),
-            execution_time=result.get('executionTime', '')
-        )
-
-    def sql(self, sql: str, params: Optional[List] = None) -> List[Dict[str, Any]]:
-        """
-        Execute a SQL query and return rows.
-
-        Args:
-            sql: SQL query string
-            params: Optional list of parameters
-
-        Returns:
-            List of row dictionaries
-        """
-        return self.query(sql, params).rows
-
-    def execute(
-        self,
-        statements: List[Dict[str, Any]],
-        transaction: bool = True
-    ) -> Dict[str, Any]:
-        """
-        Execute multiple statements in a batch.
-
-        Args:
-            statements: List of {'sql': ..., 'params': [...]} dicts
-            transaction: Whether to wrap in a transaction
-
-        Returns:
-            Execution result with affected rows
-        """
-        return self._request('POST', '/execute', {
-            'statements': [
-                {'sql': s['sql'], 'params': s.get('params', [])}
-                for s in statements
-            ],
-            'transaction': transaction
-        })
-
-    def tables(self) -> List[str]:
-        """
-        List all tables in the database.
-
-        Returns:
-            List of table names
-        """
-        result = self._request('GET', '/schema/tables')
-        return result.get('tables', [])
-
-    def schema(self, table_name: str) -> List[Column]:
-        """
-        Get schema for a specific table.
-
-        Args:
-            table_name: Name of the table
-
-        Returns:
-            List of Column objects
-        """
-        result = self._request('GET', f'/schema/tables/{table_name}')
-        return [Column(**col) for col in result.get('columns', [])]
-
-    def health(self) -> Dict[str, str]:
-        """
-        Check database health.
-
-        Returns:
-            Health status dict
-        """
-        return self._request('GET', '/health')
-
-    def use(self, database: str) -> 'PizzaSQL':
-        """
-        Create a new client for a different database.
-
-        Args:
-            database: Database name
-
-        Returns:
-            New PizzaSQL client
-        """
-        client = PizzaSQL(self._base_url, self._api_key, self._timeout)
-        client._database = database
-        return client
-
-    def close(self):
-        """Close the underlying HTTP client."""
-        if self._client and hasattr(self._client, 'close'):
-            self._client.close()
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, *args):
-        self.close()
-
-
-# Convenience function
-def connect(uri: str, api_key: Optional[str] = None) -> PizzaSQL:
-    """
-    Create a new PizzaSQL connection.
-
-    Args:
-        uri: Database URI (e.g., 'http://localhost:8080/mydb')
-        api_key: Optional API key for authentication
-
-    Returns:
-        PizzaSQL client instance
-    """
-    return PizzaSQL(uri, api_key)

+ 0 - 30
clients/python/pyproject.toml

@@ -1,30 +0,0 @@
-[build-system]
-requires = ["setuptools>=61.0"]
-build-backend = "setuptools.build_meta"
-
-[project]
-name = "pizzasql"
-version = "0.1.0"
-description = "PizzaSQL client for Python"
-readme = "README.md"
-license = {text = "MIT"}
-requires-python = ">=3.8"
-classifiers = [
-    "Development Status :: 4 - Beta",
-    "Intended Audience :: Developers",
-    "License :: OSI Approved :: MIT License",
-    "Programming Language :: Python :: 3",
-    "Programming Language :: Python :: 3.8",
-    "Programming Language :: Python :: 3.9",
-    "Programming Language :: Python :: 3.10",
-    "Programming Language :: Python :: 3.11",
-    "Programming Language :: Python :: 3.12",
-]
-keywords = ["pizzasql", "database", "sql", "client"]
-
-[project.optional-dependencies]
-httpx = ["httpx>=0.24.0"]
-
-[project.urls]
-Homepage = "https://github.com/danfragoso/pizzasql"
-Documentation = "https://github.com/danfragoso/pizzasql/tree/main/clients/python"

+ 0 - 205
clients/ruby/README.md

@@ -1,205 +0,0 @@
-# PizzaSQL Ruby Client
-
-Ruby client library for PizzaSQL - a simple, lightweight SQL database with HTTP API.
-
-## Installation
-
-Add this line to your application's Gemfile:
-
-```ruby
-gem 'pizzasql'
-```
-
-And then execute:
-
-```bash
-bundle install
-```
-
-Or install it yourself:
-
-```bash
-gem install pizzasql
-```
-
-## Usage
-
-### Basic Example
-
-```ruby
-require 'pizzasql'
-
-# Connect to database
-db = PizzaSQL.connect('http://localhost:8080/mydb')
-
-# Execute a query
-rows = db.sql('SELECT * FROM users')
-
-# Process results
-rows.each do |row|
-  puts "User ID: #{row['id']}, Name: #{row['name']}"
-end
-```
-
-### With API Key
-
-```ruby
-db = PizzaSQL.connect(
-  'https://pizzabase.cloud/my_org/sql/my_db:32131',
-  'a78tsda68bdt6ad5afsd65saf5sd5a7d6sd87asy8d9aysnd7ay=='
-)
-
-rows = db.sql('SELECT 42 as answer')
-puts rows.first['answer'] # => 42
-```
-
-### Creating Tables and Inserting Data
-
-```ruby
-# Create table
-db.sql(<<~SQL)
-  CREATE TABLE users (
-    id INTEGER PRIMARY KEY,
-    name TEXT NOT NULL,
-    email TEXT UNIQUE
-  )
-SQL
-
-# Insert data
-db.sql(<<~SQL)
-  INSERT INTO users (id, name, email)
-  VALUES (1, 'Alice', 'alice@example.com')
-SQL
-```
-
-### Filtering and Mapping Results
-
-```ruby
-rows = db.sql('SELECT * FROM users')
-
-# Extract specific field
-user_ids = rows.map { |row| row['id'] }
-puts user_ids.inspect # => [1, 2, 3, ...]
-
-# Filter results
-adults = rows.select { |row| row['age'] >= 18 }
-```
-
-### Export Database
-
-```ruby
-# Export entire database as SQL
-sql_data = db.export(format: 'sql')
-
-# Export specific table as CSV
-csv_data = db.export(table: 'users', format: 'csv')
-
-# Save to file
-File.write('users.csv', csv_data)
-```
-
-### Import Data
-
-```ruby
-# Read CSV file
-csv_data = File.read('users.csv')
-
-# Import with table creation
-db.import(csv_data, format: 'csv', create_table: true)
-
-# Import SQL file
-sql_data = File.read('backup.sql')
-db.import(sql_data, format: 'sql')
-```
-
-## API Reference
-
-### PizzaSQL.connect(uri, api_key = nil)
-
-Creates a new PizzaSQL client connection.
-
-**Parameters:**
-- `uri` (String) - Database URI (e.g., `http://localhost:8080/mydb`)
-- `api_key` (String, optional) - API key for authentication
-
-**Returns:**
-- `Client` - Connected client instance
-
-**Example:**
-```ruby
-db = PizzaSQL.connect('http://localhost:8080/mydb')
-```
-
-### Client#sql(query)
-
-Executes a SQL query and returns the results.
-
-**Parameters:**
-- `query` (String) - SQL query string
-
-**Returns:**
-- `Array<Hash>` - Array of rows (each row is a hash)
-
-**Raises:**
-- `RuntimeError` - If query fails
-
-**Example:**
-```ruby
-rows = db.sql('SELECT * FROM users WHERE age > 18')
-```
-
-### Client#export(table: nil, format: 'sql')
-
-Exports database or table data.
-
-**Parameters:**
-- `table` (String, optional) - Table name (nil for entire database)
-- `format` (String) - Export format: `'sql'` or `'csv'`
-
-**Returns:**
-- `String` - Exported data
-
-**Raises:**
-- `RuntimeError` - If export fails
-
-**Example:**
-```ruby
-data = db.export(table: 'users', format: 'csv')
-```
-
-### Client#import(data, format: 'sql', create_table: false)
-
-Imports data into the database.
-
-**Parameters:**
-- `data` (String) - Data to import
-- `format` (String) - Import format: `'sql'` or `'csv'`
-- `create_table` (Boolean) - Create table if it doesn't exist (CSV only)
-
-**Returns:**
-- `nil`
-
-**Raises:**
-- `RuntimeError` - If import fails
-
-**Example:**
-```ruby
-csv_data = File.read('users.csv')
-db.import(csv_data, format: 'csv', create_table: true)
-```
-
-## Error Handling
-
-All methods raise `RuntimeError` on failure. Use standard Ruby error handling:
-
-```ruby
-begin
-  rows = db.sql('SELECT * FROM users')
-rescue => e
-  puts "Query failed: #{e.message}"
-end
-```
-
-## License
-
-MIT

+ 0 - 123
clients/ruby/lib/pizzasql.rb

@@ -1,123 +0,0 @@
-require 'net/http'
-require 'uri'
-require 'json'
-
-module PizzaSQL
-  class Client
-    attr_reader :base_url, :db_name, :api_key
-
-    # Creates a new PizzaSQL client connection
-    #
-    # @param uri [String] Database URI (e.g., 'http://localhost:8080/mydb')
-    # @param api_key [String, nil] Optional API key for authentication
-    # @return [Client] A new client instance
-    def initialize(uri, api_key = nil)
-      parsed_uri = URI.parse(uri)
-
-      # Extract database name from path
-      path = parsed_uri.path.strip.delete_prefix('/')
-      raise ArgumentError, 'Database name not found in URI path' if path.empty?
-
-      # Split path to get database name (last segment)
-      path_parts = path.split('/')
-      @db_name = path_parts.last
-
-      # Reconstruct base URL without the database path
-      @base_url = "#{parsed_uri.scheme}://#{parsed_uri.host}:#{parsed_uri.port}"
-      @api_key = api_key
-    end
-
-    # Executes a SQL query and returns the results
-    #
-    # @param query [String] SQL query string
-    # @return [Array<Hash>] Array of rows (each row is a hash)
-    # @raise [RuntimeError] if the query fails
-    def sql(query)
-      uri = URI.parse("#{@base_url}/#{@db_name}/query")
-
-      request = Net::HTTP::Post.new(uri)
-      request['Content-Type'] = 'application/json'
-      request['Authorization'] = "Bearer #{@api_key}" if @api_key
-      request.body = { query: query }.to_json
-
-      response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
-        http.request(request)
-      end
-
-      unless response.is_a?(Net::HTTPSuccess)
-        raise "Request failed with status #{response.code}: #{response.body}"
-      end
-
-      result = JSON.parse(response.body)
-      result['rows'] || []
-    end
-
-    # Exports database or table data
-    #
-    # @param table [String, nil] Table name (nil for entire database)
-    # @param format [String] Export format: 'sql' or 'csv'
-    # @return [String] Exported data
-    # @raise [RuntimeError] if the export fails
-    def export(table: nil, format: 'sql')
-      params = {}
-      params['table'] = table if table
-      params['format'] = format if format
-
-      query_string = URI.encode_www_form(params)
-      uri = URI.parse("#{@base_url}/#{@db_name}/export?#{query_string}")
-
-      request = Net::HTTP::Get.new(uri)
-      request['Authorization'] = "Bearer #{@api_key}" if @api_key
-
-      response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
-        http.request(request)
-      end
-
-      unless response.is_a?(Net::HTTPSuccess)
-        raise "Export failed with status #{response.code}: #{response.body}"
-      end
-
-      response.body
-    end
-
-    # Imports data into the database
-    #
-    # @param data [String] Data to import
-    # @param format [String] Import format: 'sql' or 'csv'
-    # @param create_table [Boolean] Create table if it doesn't exist (CSV only)
-    # @return [void]
-    # @raise [RuntimeError] if the import fails
-    def import(data, format: 'sql', create_table: false)
-      params = {}
-      params['format'] = format if format
-      params['create_table'] = 'true' if create_table
-
-      query_string = URI.encode_www_form(params)
-      uri = URI.parse("#{@base_url}/#{@db_name}/import?#{query_string}")
-
-      request = Net::HTTP::Post.new(uri)
-      request['Content-Type'] = 'application/octet-stream'
-      request['Authorization'] = "Bearer #{@api_key}" if @api_key
-      request.body = data
-
-      response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
-        http.request(request)
-      end
-
-      unless response.is_a?(Net::HTTPSuccess)
-        raise "Import failed with status #{response.code}: #{response.body}"
-      end
-
-      nil
-    end
-  end
-
-  # Module-level convenience method to create a new client connection
-  #
-  # @param uri [String] Database URI
-  # @param api_key [String, nil] Optional API key
-  # @return [Client] A new client instance
-  def self.connect(uri, api_key = nil)
-    Client.new(uri, api_key)
-  end
-end

+ 0 - 17
clients/ruby/pizzasql.gemspec

@@ -1,17 +0,0 @@
-Gem::Specification.new do |spec|
-  spec.name          = "pizzasql"
-  spec.version       = "0.1.0"
-  spec.authors       = ["PizzaSQL"]
-  spec.email         = ["hello@pizzasql.com"]
-
-  spec.summary       = "Ruby client for PizzaSQL"
-  spec.description   = "A simple, lightweight client library for PizzaSQL - a SQL database with HTTP API"
-  spec.homepage      = "https://github.com/pizzasql/pizzasql"
-  spec.license       = "MIT"
-  spec.required_ruby_version = ">= 2.7.0"
-
-  spec.files = Dir["lib/**/*", "README.md", "LICENSE"]
-  spec.require_paths = ["lib"]
-
-  # No external dependencies - uses only Ruby standard library
-end

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików