From 3b65b90b6f6706f37acb80c4c7aa0ca3d7e798f1 Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Tue, 10 Mar 2026 13:42:05 -0700 Subject: [PATCH] Remove IF NOT EXISTS from migrations and drop mysql CLI fallback - Drop IF NOT EXISTS from all CREATE TABLE statements in the initial migration. sqlx tracks applied migrations in _sqlx_migrations and guarantees each runs exactly once; the guard was masking partial failures and giving a false sense of idempotency. - Remove the mysql CLI fallback in dev-setup.sh. Running raw .sql files via docker exec bypasses sqlx migration tracking, which causes re-apply errors on subsequent runs. Fail fast with a clear install message instead. --- migrations/20260306000001_initial_schema.sql | 18 +++++------ scripts/dev-setup.sh | 32 ++------------------ 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/migrations/20260306000001_initial_schema.sql b/migrations/20260306000001_initial_schema.sql index 18b50ff..8023a01 100644 --- a/migrations/20260306000001_initial_schema.sql +++ b/migrations/20260306000001_initial_schema.sql @@ -4,7 +4,7 @@ -- ─── Channels ──────────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS channels ( +CREATE TABLE channels ( id BINARY(16) NOT NULL, name TEXT NOT NULL, channel_type ENUM('stream','forum','dm','workflow') NOT NULL DEFAULT 'stream', @@ -30,7 +30,7 @@ CREATE INDEX idx_channels_created_by ON channels (created_by); -- ─── Channel Members ───────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS channel_members ( +CREATE TABLE channel_members ( channel_id BINARY(16) NOT NULL, pubkey VARBINARY(32) NOT NULL, role ENUM('owner','admin','member','guest','bot') NOT NULL DEFAULT 'member', @@ -50,7 +50,7 @@ CREATE INDEX idx_channel_members_channel ON channel_members (channel_id); -- ─── Users ─────────────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS users ( +CREATE TABLE users ( pubkey VARBINARY(32) NOT NULL, nip05_handle VARCHAR(255) UNIQUE, display_name TEXT, @@ -83,7 +83,7 @@ CREATE INDEX idx_users_okta ON users (okta_user_id); -- ⚠️ Deduplication by id alone is not enforceable via unique index across -- partitions. SHA-256 collision resistance + app-layer INSERT IGNORE used. -CREATE TABLE IF NOT EXISTS events ( +CREATE TABLE events ( id VARBINARY(32) NOT NULL, pubkey VARBINARY(32) NOT NULL, created_at DATETIME(6) NOT NULL, @@ -121,7 +121,7 @@ CREATE INDEX idx_events_kind_created ON events (kind, created_at); -- ─── Persistent Subscriptions ──────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS subscriptions ( +CREATE TABLE subscriptions ( id VARCHAR(255) NOT NULL, name TEXT NOT NULL, owner_pubkey VARBINARY(32) NOT NULL, @@ -165,7 +165,7 @@ CREATE INDEX idx_subscriptions_status ON subscriptions (status); -- ⚠️ MySQL does not support FK constraints on partitioned tables. -- subscription_id → subscriptions(id) enforced at application layer. -CREATE TABLE IF NOT EXISTS delivery_log ( +CREATE TABLE delivery_log ( id BIGINT NOT NULL AUTO_INCREMENT, subscription_id VARCHAR(255) NOT NULL, event_id VARBINARY(32) NOT NULL, @@ -192,7 +192,7 @@ CREATE INDEX idx_delivery_log_failures ON delivery_log (subscription_id, de -- ─── Workflows ──────────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS workflows ( +CREATE TABLE workflows ( id BINARY(16) NOT NULL, name TEXT NOT NULL, owner_pubkey VARBINARY(32) NOT NULL, @@ -224,7 +224,7 @@ CREATE INDEX idx_workflows_channel ON workflows (channel_id); -- ─── API Tokens ─────────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS api_tokens ( +CREATE TABLE api_tokens ( id BINARY(16) NOT NULL, token_hash VARBINARY(32) NOT NULL UNIQUE, owner_pubkey VARBINARY(32) NOT NULL, @@ -248,7 +248,7 @@ CREATE INDEX idx_api_tokens_hash ON api_tokens (token_hash); -- ─── Rate Limit Violations ──────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS rate_limit_violations ( +CREATE TABLE rate_limit_violations ( id BIGINT NOT NULL AUTO_INCREMENT, pubkey VARBINARY(32) NOT NULL, violation_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh index 5257a5e..923358f 100755 --- a/scripts/dev-setup.sh +++ b/scripts/dev-setup.sh @@ -12,12 +12,6 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" TIMEOUT=120 # seconds to wait for services to become healthy -DB_USER="${SPROUT_DB_USER:-sprout}" -DB_PASS="${SPROUT_DB_PASS:-sprout_dev}" -DB_NAME="${SPROUT_DB_NAME:-sprout}" -DOCKER_DB_HOST="${SPROUT_DOCKER_DB_HOST:-mysql}" -DOCKER_NETWORK="${SPROUT_DOCKER_NETWORK:-sprout-net}" -MYSQL_CLIENT_IMAGE="${SPROUT_DB_CLIENT_IMAGE:-mysql:8.0}" # Colors RED='\033[0;31m' @@ -45,13 +39,6 @@ fi cd "${REPO_ROOT}" -run_mysql_in_container() { - docker run --rm -i --network "${DOCKER_NETWORK}" \ - -e MYSQL_PWD="${DB_PASS}" \ - "${MYSQL_CLIENT_IMAGE}" \ - mysql -h"${DOCKER_DB_HOST}" -u"${DB_USER}" "${DB_NAME}" "$@" -} - # ---- Start services --------------------------------------------------------- log "Starting services..." @@ -118,22 +105,9 @@ else sqlx migrate run --source "${MIGRATION_DIR}" success "Migrations applied via sqlx" else - # Fallback: run SQL files directly via mysql in the container - log "sqlx CLI not found — applying migrations via mysql CLI..." - shopt -s nullglob - SQL_FILES=("${MIGRATION_DIR}"/*.sql) - shopt -u nullglob - - if [[ ${#SQL_FILES[@]} -eq 0 ]]; then - warn "No .sql files found in ${MIGRATION_DIR}. Skipping." - else - for sql_file in "${SQL_FILES[@]}"; do - filename="$(basename "${sql_file}")" - log " Applying ${filename}..." - run_mysql_in_container < "${sql_file}" - done - success "Migrations applied via mysql" - fi + error "sqlx CLI not found. Install it with: cargo install sqlx-cli --no-default-features --features mysql" + error "Running migrations directly via mysql bypasses migration tracking and causes errors." + exit 1 fi fi