CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE, api_key TEXT UNIQUE, storage_limit_bytes BIGINT DEFAULT 1073741824, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS buckets ( id SERIAL PRIMARY KEY, name TEXT NOT NULL UNIQUE, owner_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, storage_used_bytes BIGINT DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS objects ( id BIGSERIAL PRIMARY KEY, bucket_id INT NOT NULL REFERENCES buckets(id) ON DELETE CASCADE, key TEXT NOT NULL, size_bytes BIGINT NOT NULL, content_type TEXT, last_modified TIMESTAMPTZ DEFAULT NOW(), version_id TEXT UNIQUE, md5_checksum TEXT, custom_metadata JSONB DEFAULT '{}', UNIQUE (bucket_id, key) ); CREATE INDEX IF NOT EXISTS idx_buckets_owner_id ON buckets (owner_id); CREATE INDEX IF NOT EXISTS idx_users_api_key ON users (api_key); CREATE INDEX IF NOT EXISTS idx_objects_bucket_id_key ON objects (bucket_id, key); CREATE INDEX IF NOT EXISTS idx_objects_bucket_id_last_modified ON objects (bucket_id, last_modified DESC); CREATE INDEX IF NOT EXISTS idx_objects_custom_metadata_gin ON objects USING GIN (custom_metadata); CREATE OR REPLACE FUNCTION update_bucket_storage() RETURNS TRIGGER AS $$ BEGIN IF TG_OP = 'INSERT' THEN UPDATE buckets SET storage_used_bytes = storage_used_bytes + NEW.size_bytes WHERE id = NEW.bucket_id; RETURN NEW; ELSIF TG_OP = 'DELETE' THEN UPDATE buckets SET storage_used_bytes = storage_used_bytes - OLD.size_bytes WHERE id = OLD.bucket_id; RETURN OLD; ELSIF TG_OP = 'UPDATE' THEN UPDATE buckets SET storage_used_bytes = storage_used_bytes + NEW.size_bytes - OLD.size_bytes WHERE id = NEW.bucket_id; RETURN NEW; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER update_bucket_storage_trigger AFTER INSERT OR UPDATE OR DELETE ON objects FOR EACH ROW EXECUTE FUNCTION update_bucket_storage();