From 779ac57f5097166c3660374e2a10acfe2045dc8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=CE=A7=CE=B3=CF=86=CF=84=20Kompanion?=
 <chigammaphitau@chelydra.at>
Date: Wed, 15 Oct 2025 10:39:44 +1300
Subject: [PATCH] Add Schema files

---
 db/init/001_roles.sql        |  2 ++
 db/init/002_extensions.sql   |  2 ++
 db/init/010_schema.sql       | 39 ++++++++++++++++++++++++++++++++++++
 db/init/020_indexes.sql      | 15 ++++++++++++++
 db/scripts/create-prod-db.sh | 22 ++++++++++++++++++++
 db/scripts/create-test-db.sh | 17 ++++++++++++++++
 6 files changed, 97 insertions(+)
 create mode 100644 db/init/001_roles.sql
 create mode 100644 db/init/002_extensions.sql
 create mode 100644 db/init/010_schema.sql
 create mode 100644 db/init/020_indexes.sql
 create mode 100755 db/scripts/create-prod-db.sh
 create mode 100755 db/scripts/create-test-db.sh

diff --git a/db/init/001_roles.sql b/db/init/001_roles.sql
new file mode 100644
index 0000000..540b47b
--- /dev/null
+++ b/db/init/001_roles.sql
@@ -0,0 +1,2 @@
+CREATE ROLE kompanion LOGIN PASSWORD 'komp';
+CREATE DATABASE kompanion OWNER kompanion;
diff --git a/db/init/002_extensions.sql b/db/init/002_extensions.sql
new file mode 100644
index 0000000..6aa6c6f
--- /dev/null
+++ b/db/init/002_extensions.sql
@@ -0,0 +1,2 @@
+CREATE EXTENSION IF NOT EXISTS vector;
+CREATE EXTENSION IF NOT EXISTS pg_trgm;
diff --git a/db/init/010_schema.sql b/db/init/010_schema.sql
new file mode 100644
index 0000000..6ec8c21
--- /dev/null
+++ b/db/init/010_schema.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS namespaces (
+  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+  name TEXT UNIQUE NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS memory_items (
+  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+  namespace_id UUID NOT NULL REFERENCES namespaces(id) ON DELETE CASCADE,
+  key TEXT,
+  content TEXT NOT NULL,
+  metadata JSONB NOT NULL DEFAULT '{}',
+  tags TEXT[] NOT NULL DEFAULT '{}',
+  revision INT NOT NULL DEFAULT 1,
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  expires_at TIMESTAMPTZ,
+  deleted_at TIMESTAMPTZ,
+  last_accessed_at TIMESTAMPTZ
+);
+
+CREATE TABLE IF NOT EXISTS memory_chunks (
+  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+  item_id UUID NOT NULL REFERENCES memory_items(id) ON DELETE CASCADE,
+  seq INT NOT NULL,
+  content TEXT NOT NULL,
+  expires_at TIMESTAMPTZ,
+  deleted_at TIMESTAMPTZ,
+  content_tsv tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED
+);
+
+CREATE TABLE IF NOT EXISTS embeddings (
+  id BIGSERIAL PRIMARY KEY,
+  chunk_id UUID NOT NULL REFERENCES memory_chunks(id) ON DELETE CASCADE,
+  model TEXT NOT NULL,
+  dim INT NOT NULL,
+  vector VECTOR(1536),
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  UNIQUE(chunk_id, model)
+);
diff --git a/db/init/020_indexes.sql b/db/init/020_indexes.sql
new file mode 100644
index 0000000..9336d13
--- /dev/null
+++ b/db/init/020_indexes.sql
@@ -0,0 +1,15 @@
+CREATE UNIQUE INDEX IF NOT EXISTS ux_items_ns_key
+  ON memory_items(namespace_id, key)
+  WHERE key IS NOT NULL;
+
+CREATE INDEX IF NOT EXISTS ix_chunks_item ON memory_chunks(item_id);
+
+CREATE INDEX IF NOT EXISTS idx_chunks_content_tsv
+  ON memory_chunks USING GIN(content_tsv);
+
+CREATE INDEX IF NOT EXISTS ix_embed_model_dim ON embeddings(model, dim);
+
+-- per-model ANN index (duplicate with each concrete model name)
+CREATE INDEX IF NOT EXISTS ix_embed_vec_model_default
+  ON embeddings USING ivfflat (vector vector_cosine_ops)
+  WHERE model = 'default-emb';
diff --git a/db/scripts/create-prod-db.sh b/db/scripts/create-prod-db.sh
new file mode 100755
index 0000000..6652a42
--- /dev/null
+++ b/db/scripts/create-prod-db.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+set -euo pipefail
+DB_NAME=${1:-kompanion}
+ROLE=${ROLE:-kompanion}
+PASS=${PASS:-komp}
+
+psql -v ON_ERROR_STOP=1 <<SQL
+DO $$ BEGIN
+  PERFORM 1 FROM pg_roles WHERE rolname = '$ROLE';
+  IF NOT FOUND THEN EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', '$ROLE', '$PASS'); END IF;
+END $$;
+DO $$ BEGIN
+  IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = '$DB_NAME') THEN
+    EXECUTE format('CREATE DATABASE %I OWNER %I', '$DB_NAME', '$ROLE');
+  END IF;
+END $$;
+SQL
+
+for f in db/init/*.sql; do
+  echo "Applying $f"
+  psql -d "$DB_NAME" -f "$f"
+done
diff --git a/db/scripts/create-test-db.sh b/db/scripts/create-test-db.sh
new file mode 100755
index 0000000..1329022
--- /dev/null
+++ b/db/scripts/create-test-db.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -euo pipefail
+DB_NAME=${1:-kompanion_test}
+ROLE=${ROLE:-kompanion}
+PASS=${PASS:-komup}
+
+psql -v ON_ERROR_STOP=1 <<SQL
+DROP DATABASE IF EXISTS "$DB_NAME";
+CREATE DATABASE "$DB_NAME" OWNER "$ROLE";
+SQL
+
+for f in db/init/*.sql; do
+  echo "Applying $f"
+  psql -d "$DB_NAME" -f "$f"
+done
+
+echo "✓ Database $DB_NAME initialized."
