diff --git a/.github/workflows/declarative-sql-review-action.yml b/.github/workflows/declarative-sql-review-action.yml new file mode 100644 index 0000000..b32edad --- /dev/null +++ b/.github/workflows/declarative-sql-review-action.yml @@ -0,0 +1,30 @@ +name: SQL review on pull request with declarative release using bytebase-action image + +on: + pull_request: + branches: + - main + paths: + - "schema/*.sql" + +jobs: + check-release-on-prod: + permissions: + pull-requests: write # write permission required to allow the action writes the check results to the comment. + runs-on: ubuntu-latest # use self-hosted machines if your Bytebase runs in internal networks. + container: + image: bytebase/bytebase-action:latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Check release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # set GITHUB_TOKEN because the 'Check release' step needs it to comment the pull request with check results. + BYTEBASE_URL: https://demo.bytebase.com + BYTEBASE_SERVICE_ACCOUNT: api@service.bytebase.com # set service account via environment variable + BYTEBASE_SERVICE_ACCOUNT_SECRET: ${{ secrets.BYTEBASE_SERVICE_ACCOUNT_SECRET }} # set service account secret via environment variable + BYTEBASE_PROJECT: "projects/hr" + BYTEBASE_TARGETS: "instances/prod-sample-instance/databases/hr_prod" + FILE_PATTERN: "schema/*.sql" + run: | + bytebase-action check --url=${{ env.BYTEBASE_URL }} --project=${{ env.BYTEBASE_PROJECT }} --targets=${{ env.BYTEBASE_TARGETS }} --file-pattern=${{ env.FILE_PATTERN }} --declarative diff --git a/schema/schema.sql b/schema/schema.sql index 98d0a02..d06b817 100644 --- a/schema/schema.sql +++ b/schema/schema.sql @@ -1,120 +1,112 @@ -CREATE TABLE public.employee ( - emp_no SERIAL NOT NULL, - birth_date DATE NOT NULL, - first_name TEXT NOT NULL, - last_name TEXT NOT NULL, - gender TEXT NOT NULL CHECK (gender IN('M', 'F')) NOT NULL, - hire_date DATE NOT NULL, - PRIMARY KEY (emp_no) +COMMENT ON SCHEMA "public" IS 'standard public schema'; + +CREATE TABLE "public"."audit" ( + "id" serial, + "operation" text NOT NULL, + "query" text, + "user_name" text NOT NULL, + "changed_at" timestamp(6) with time zone DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "audit_pkey" PRIMARY KEY (id) ); -CREATE INDEX idx_employee_hire_date ON public.employee (hire_date); +CREATE INDEX "idx_audit_changed_at" ON ONLY "public"."audit" (changed_at); + +CREATE INDEX "idx_audit_operation" ON ONLY "public"."audit" (operation); + +CREATE INDEX "idx_audit_username" ON ONLY "public"."audit" (user_name); + +CREATE TABLE "public"."department" ( + "dept_no" text NOT NULL, + "dept_name" text NOT NULL, + CONSTRAINT "department_pkey" PRIMARY KEY (dept_no), + CONSTRAINT "department_dept_name_key" UNIQUE (dept_name) +); -CREATE TABLE public.department ( - dept_no TEXT NOT NULL, - dept_name TEXT NOT NULL, - PRIMARY KEY (dept_no), - UNIQUE (dept_name) +CREATE TABLE "public"."dept_emp" ( + "emp_no" integer NOT NULL, + "dept_no" text NOT NULL, + "from_date" date NOT NULL, + "to_date" date NOT NULL, + CONSTRAINT "dept_emp_pkey" PRIMARY KEY (emp_no, dept_no), + CONSTRAINT "dept_emp_dept_no_fkey" FOREIGN KEY ("dept_no") REFERENCES "public"."department" ("dept_no") ON DELETE CASCADE, + CONSTRAINT "dept_emp_emp_no_fkey" FOREIGN KEY ("emp_no") REFERENCES "public"."employee" ("emp_no") ON DELETE CASCADE ); -CREATE TABLE public.dept_manager ( - emp_no INT NOT NULL, - dept_no TEXT NOT NULL, - from_date DATE NOT NULL, - to_date DATE NOT NULL, - FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE, - FOREIGN KEY (dept_no) REFERENCES department (dept_no) ON DELETE CASCADE, - PRIMARY KEY (emp_no, dept_no) +CREATE TABLE "public"."dept_manager" ( + "emp_no" integer NOT NULL, + "dept_no" text NOT NULL, + "from_date" date NOT NULL, + "to_date" date NOT NULL, + CONSTRAINT "dept_manager_pkey" PRIMARY KEY (emp_no, dept_no), + CONSTRAINT "dept_manager_dept_no_fkey" FOREIGN KEY ("dept_no") REFERENCES "public"."department" ("dept_no") ON DELETE CASCADE, + CONSTRAINT "dept_manager_emp_no_fkey" FOREIGN KEY ("emp_no") REFERENCES "public"."employee" ("emp_no") ON DELETE CASCADE ); -CREATE TABLE public.dept_emp ( - emp_no INT NOT NULL, - dept_no TEXT NOT NULL, - from_date DATE NOT NULL, - to_date DATE NOT NULL, - FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE, - FOREIGN KEY (dept_no) REFERENCES department (dept_no) ON DELETE CASCADE, - PRIMARY KEY (emp_no, dept_no) +CREATE TABLE "public"."employee" ( + "emp_no" serial, + "birth_date" date NOT NULL, + "first_name" text NOT NULL, + "last_name" text NOT NULL, + "gender" text NOT NULL, + "hire_date" date NOT NULL, + CONSTRAINT "employee_pkey" PRIMARY KEY (emp_no), + CONSTRAINT "employee_gender_check" CHECK (gender = ANY (ARRAY['M'::text, 'F'::text])) ); -CREATE TABLE public.title ( - emp_no INT NOT NULL, - title TEXT NOT NULL, - from_date DATE NOT NULL, - to_date DATE, - FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE, - PRIMARY KEY (emp_no, title, from_date) -); - -CREATE TABLE public.salary ( - emp_no INT NOT NULL, - amount INT NOT NULL, - from_date DATE NOT NULL, - to_date DATE NOT NULL, - FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE, - PRIMARY KEY (emp_no, from_date) +CREATE INDEX "idx_employee_hire_date" ON ONLY "public"."employee" (hire_date); + +CREATE TABLE "public"."salary" ( + "emp_no" integer NOT NULL, + "amount" integer NOT NULL, + "from_date" date NOT NULL, + "to_date" date NOT NULL, + CONSTRAINT "salary_pkey" PRIMARY KEY (emp_no, from_date), + CONSTRAINT "salary_emp_no_fkey" FOREIGN KEY ("emp_no") REFERENCES "public"."employee" ("emp_no") ON DELETE CASCADE ); -CREATE INDEX idx_salary_amount ON public.salary (amount); +CREATE INDEX "idx_salary_amount" ON ONLY "public"."salary" (amount); -CREATE TABLE public.audit ( - id SERIAL PRIMARY KEY, - operation TEXT NOT NULL, - query TEXT, - user_name TEXT NOT NULL, - changed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +CREATE TABLE "public"."title" ( + "emp_no" integer NOT NULL, + "title" text NOT NULL, + "from_date" date NOT NULL, + "to_date" date, + CONSTRAINT "title_pkey" PRIMARY KEY (emp_no, title, from_date), + CONSTRAINT "title_emp_no_fkey" FOREIGN KEY ("emp_no") REFERENCES "public"."employee" ("emp_no") ON DELETE CASCADE ); -CREATE INDEX idx_audit_operation ON public.audit (operation); -CREATE INDEX idx_audit_username ON public.audit (user_name); -CREATE INDEX idx_audit_changed_at ON public.audit (changed_at); +CREATE VIEW "public"."current_dept_emp" AS SELECT l.emp_no, + d.dept_no, + l.from_date, + l.to_date + FROM (public.dept_emp d + JOIN public.dept_emp_latest_date l ON (((d.emp_no = l.emp_no) AND (d.from_date = l.from_date) AND (l.to_date = d.to_date)))); -CREATE OR REPLACE FUNCTION public.log_dml_operations() RETURNS TRIGGER AS $$ +CREATE VIEW "public"."dept_emp_latest_date" AS SELECT emp_no, + max(from_date) AS from_date, + max(to_date) AS to_date + FROM public.dept_emp + GROUP BY emp_no; + +CREATE OR REPLACE FUNCTION public.log_dml_operations() + RETURNS trigger + LANGUAGE plpgsql +AS $function$ BEGIN IF (TG_OP = 'INSERT') THEN - INSERT INTO public.audit (operation, query, user_name) + INSERT INTO audit (operation, query, user_name) VALUES ('INSERT', current_query(), current_user); RETURN NEW; ELSIF (TG_OP = 'UPDATE') THEN - INSERT INTO public.audit (operation, query, user_name) + INSERT INTO audit (operation, query, user_name) VALUES ('UPDATE', current_query(), current_user); RETURN NEW; ELSIF (TG_OP = 'DELETE') THEN - INSERT INTO public.audit (operation, query, user_name) + INSERT INTO audit (operation, query, user_name) VALUES ('DELETE', current_query(), current_user); RETURN OLD; END IF; RETURN NULL; END; -$$ LANGUAGE plpgsql; - --- only log update and delete, otherwise, it will cause too much change. -CREATE TRIGGER salary_log_trigger -AFTER UPDATE OR DELETE ON public.salary -FOR EACH ROW -EXECUTE FUNCTION public.log_dml_operations(); - -CREATE OR REPLACE VIEW public.dept_emp_latest_date AS -SELECT - emp_no, - MAX( - from_date) AS from_date, - MAX( - to_date) AS to_date -FROM - public.dept_emp -GROUP BY - emp_no; - --- shows only the current department for each employee -CREATE OR REPLACE VIEW public.current_dept_emp AS -SELECT - l.emp_no, - dept_no, - l.from_date, - l.to_date -FROM - public.dept_emp d - INNER JOIN public.dept_emp_latest_date l ON d.emp_no = l.emp_no - AND d.from_date = l.from_date - AND l.to_date = d.to_date; +$function$; + diff --git a/schema/users.sql b/schema/users.sql deleted file mode 100644 index b66be12..0000000 --- a/schema/users.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE public.users ( - id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - email VARCHAR(255) NOT NULL UNIQUE -);