afacc9a7db
dollar-quoted ($$...$$) 안의 :'app_password' 는 psql 이 치환하지 않아 서버에서 syntax error 발생. DO 블록을 제거하고 \gexec 로 동적 SQL 생성. - CREATE ROLE: 미존재 시에만 - ALTER ROLE: 비밀번호 항상 동기화
84 lines
3.4 KiB
PL/PgSQL
84 lines
3.4 KiB
PL/PgSQL
-- =====================================================================
|
|
-- expense_db 초기화 스크립트 (PostgreSQL)
|
|
-- =====================================================================
|
|
-- 실행 방법 (운영 PostgreSQL 컨테이너명: postgres-db):
|
|
--
|
|
-- 1) DB / 역할 생성 (superuser 로 1회)
|
|
-- docker exec -i postgres-db psql -U postgres -v app_password='<강한비밀번호>' \
|
|
-- < scripts/sql/expense_db_init.sql
|
|
--
|
|
-- 2) main-app .env 에 연결 정보 등록
|
|
-- EXPENSE_DB_URL=postgresql://expense_app:<강한비밀번호>@postgres-db:5432/expense_db
|
|
--
|
|
-- 주의:
|
|
-- - 기존 DB 가 있으면 DROP 하지 않는다. CREATE DATABASE 만 IF NOT EXISTS 대체.
|
|
-- - 비밀번호는 절대 Git 에 커밋하지 않는다. psql -v 또는 \set 으로만 전달.
|
|
-- - DROP/TRUNCATE 가 필요한 경우 본 스크립트를 수정하지 말고 별도 작업으로 진행.
|
|
-- =====================================================================
|
|
|
|
\set ON_ERROR_STOP on
|
|
|
|
-- DB 가 없을 때만 생성
|
|
SELECT 'CREATE DATABASE expense_db ENCODING ''UTF8'' TEMPLATE template0'
|
|
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'expense_db')
|
|
\gexec
|
|
|
|
-- 앱 전용 로그인 역할
|
|
-- psql 변수(:'app_password')는 dollar-quoted 블록 안에서 치환되지 않으므로
|
|
-- DO 블록을 쓰지 않고 \gexec 로 동적 SQL 을 생성·실행한다.
|
|
|
|
SELECT 'CREATE ROLE expense_app LOGIN PASSWORD ' || quote_literal(:'app_password')
|
|
WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'expense_app')
|
|
\gexec
|
|
|
|
-- 항상 최신 비밀번호로 동기화
|
|
SELECT 'ALTER ROLE expense_app WITH LOGIN PASSWORD ' || quote_literal(:'app_password')
|
|
\gexec
|
|
|
|
GRANT CONNECT ON DATABASE expense_db TO expense_app;
|
|
|
|
-- 이제 expense_db 컨텍스트로 전환
|
|
\connect expense_db
|
|
|
|
CREATE TABLE IF NOT EXISTS expense_items (
|
|
id TEXT PRIMARY KEY,
|
|
owner TEXT NOT NULL,
|
|
spent_at DATE NOT NULL,
|
|
category TEXT NOT NULL,
|
|
method TEXT NOT NULL,
|
|
merchant TEXT NOT NULL DEFAULT '',
|
|
amount BIGINT NOT NULL DEFAULT 0 CHECK (amount >= 0),
|
|
memo TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT '작성중',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_expense_owner_spent ON expense_items (owner, spent_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_expense_status ON expense_items (status);
|
|
CREATE INDEX IF NOT EXISTS idx_expense_created_at ON expense_items (created_at DESC);
|
|
|
|
-- updated_at 자동 갱신 트리거
|
|
CREATE OR REPLACE FUNCTION expense_set_updated_at() RETURNS trigger AS $$
|
|
BEGIN
|
|
NEW.updated_at := now();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS trg_expense_set_updated_at ON expense_items;
|
|
CREATE TRIGGER trg_expense_set_updated_at
|
|
BEFORE UPDATE ON expense_items
|
|
FOR EACH ROW EXECUTE FUNCTION expense_set_updated_at();
|
|
|
|
-- 권한
|
|
GRANT USAGE ON SCHEMA public TO expense_app;
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON expense_items TO expense_app;
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO expense_app;
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
GRANT USAGE, SELECT ON SEQUENCES TO expense_app;
|
|
|
|
-- 확인용 출력
|
|
SELECT 'expense_db ready' AS status;
|