trigger support, add using casts, readme enhance

This commit is contained in:
Robert Lechte 2018-12-09 15:26:15 +11:00
parent 78c779620c
commit 6d507eae09
13 changed files with 121 additions and 10 deletions

View File

@ -18,6 +18,16 @@ You can also detect changes for a single specific schema only with `--schema mys
**Migra supports PostgreSQL >= 10 only.** Known issues exist with earlier versions. Development resources are limited, and feature support rather than backwards compatibility is prioritised.
## Support `migra`'s maintenance and future development
This project isn't sponsored by my employer or any other organisation: It's been built with many hours of voluntary unpaid work.
I've recently set up [a Patreon](https://patreon.com/djrobstep) to help support future development of `migra` and related projects.
If you or your employer uses `migra`, please consider [becoming a subscriber](https://patreon.com/djrobstep).
If you require specific features or support, more formal commercial arrangements can be discussed (email me at the author email specified in this repo's `pyproject.toml`)
## Folks, schemas are good
Schema migrations are without doubt the most cumbersome and annoying part of working with SQL databases. So much so that some people think that schemas themselves are bad!
@ -66,9 +76,25 @@ If you want to generate `drop ...` statements, you need to use the `--unsafe` fl
## Features and Limitations
`migra` plays nicely with extensions. Schema contents belonging to extensions will be ignored and left to the extension to manage.
Table of supported features:
`migra` supports most PostgreSQL features: tables (including partitioning), views, functions, constraints, indexes, enums, collations, extensions, row level security policies, schemas and sequences.
Feature | Supported | Notes/limitations
--- | --- | ---
tables | ✔ |
partitioned tables | ✔ | NEW!
constraints | ✔ |
views | ✔ |
functions | ✔ | Dependency-aware. All languages except C/INTERNAL
sequences | ✔ | Does not track sequence numbers
schemas | ✔ |
extensions | ✔ |
enums | ✔ |
privileges | ✔ | Not exhaustive. Requires --with-privileges flag
row-level security | ✔ | NEW! Doesn't include role management
triggers | ✔ | NEW!
custom types/domains | In progress |
`migra` plays nicely with extensions. Schema contents belonging to extensions will be ignored and left to the extension to manage.
`migra` plays nicely with view/function dependencies, and will drop/create them in the correct order.

View File

@ -18,6 +18,7 @@ THINGS = [
"privileges",
"collations",
"rlspolicies",
"triggers"
]
PK = "PRIMARY KEY"

View File

@ -64,10 +64,12 @@ class Migration(object):
def add_all_changes(self, privileges=False):
self.add(self.changes.schemas(creations_only=True))
self.add(self.changes.extensions(creations_only=True))
self.add(self.changes.collations(creations_only=True))
self.add(self.changes.enums(creations_only=True, modifications=False))
self.add(self.changes.sequences(creations_only=True))
self.add(self.changes.triggers(drops_only=True))
self.add(self.changes.rlspolicies(drops_only=True))
if privileges:
self.add(self.changes.privileges(drops_only=True))
@ -75,6 +77,7 @@ class Migration(object):
self.add(self.changes.pk_constraints(drops_only=True))
self.add(self.changes.indexes(drops_only=True))
self.add(self.changes.selectables())
self.add(self.changes.sequences(drops_only=True))
@ -86,6 +89,7 @@ class Migration(object):
if privileges:
self.add(self.changes.privileges(creations_only=True))
self.add(self.changes.rlspolicies(creations_only=True))
self.add(self.changes.triggers(creations_only=True))
self.add(self.changes.collations(drops_only=True))
self.add(self.changes.schemas(drops_only=True))

View File

@ -13,7 +13,7 @@ homepage = "https://migra.djrobstep.com/"
python = "*"
sqlbag = "*"
six = "*"
schemainspect = ">=0.1.1543655873"
schemainspect = ">= 0.1.1543708870"
psycopg2-binary = { version="*", optional = true }
[tool.poetry.dev-dependencies]

View File

@ -1,5 +1,5 @@
alter table "public"."t" add column "c" text collate "numeric";
alter table "public"."t" alter column "b" set data type text collate "numeric";
alter table "public"."t" alter column "b" set data type text collate "numeric" using "b"::text;
drop collation if exists "public"."posix";

View File

@ -64,7 +64,7 @@ create table "public"."order_items" (
);
alter table "public"."orders" alter column "status" set data type varchar;
alter table "public"."orders" alter column "status" set data type varchar using "status"::varchar;
drop type "public"."shipping_status";
@ -80,7 +80,7 @@ alter table "public"."orders" add column "h" hstore;
alter table "public"."orders" alter column "order_id" drop default;
alter table "public"."orders" alter column "status2" set data type text;
alter table "public"."orders" alter column "status2" set data type text using "status2"::text;
alter table "public"."products" drop column "oldcolumn";
@ -94,7 +94,7 @@ alter table "public"."products" add column "newcolumn2" interval;
alter table "public"."products" alter column "name" drop not null;
alter table "public"."products" alter column "name" set data type text;
alter table "public"."products" alter column "name" set data type text using "name"::text;
alter table "public"."products" alter column "price" set default 100;

View File

@ -64,7 +64,7 @@ create table "public"."order_items" (
);
alter table "public"."orders" alter column "status" set data type varchar;
alter table "public"."orders" alter column "status" set data type varchar using "status"::varchar;
drop type "public"."shipping_status";
@ -80,7 +80,7 @@ alter table "public"."orders" add column "h" hstore;
alter table "public"."orders" alter column "order_id" drop default;
alter table "public"."orders" alter column "status2" set data type text;
alter table "public"."orders" alter column "status2" set data type text using "status2"::text;
alter table "public"."products" drop column "z";
@ -90,7 +90,7 @@ alter table "public"."products" add column "newcolumn2" interval;
alter table "public"."products" alter column "name" drop not null;
alter table "public"."products" alter column "name" set data type text;
alter table "public"."products" alter column "name" set data type text using "name"::text;
alter table "public"."products" alter column "price" set default 100;

View File

@ -0,0 +1,34 @@
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when they must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
CREATE TRIGGER emp_stamp_drop BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();

View File

View File

@ -0,0 +1,34 @@
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when they must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
CREATE TRIGGER emp_stamp_create BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();

View File

@ -0,0 +1,7 @@
drop trigger if exists "emp_stamp_drop" on "public"."emp";
drop trigger if exists "emp_stamp" on "public"."emp";
CREATE TRIGGER emp_stamp_create BEFORE INSERT OR UPDATE ON public.emp FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
CREATE TRIGGER emp_stamp BEFORE UPDATE ON public.emp FOR EACH ROW EXECUTE PROCEDURE emp_stamp();

View File

View File

@ -54,6 +54,11 @@ def test_collations():
do_fixture_test(FIXTURE_NAME)
def test_triggers():
for FIXTURE_NAME in ["triggers"]:
do_fixture_test(FIXTURE_NAME)
def test_singleschemea():
for FIXTURE_NAME in ["singleschema"]:
do_fixture_test(FIXTURE_NAME, schema="goodschema")