PostgreSQL: UPDATE implies move across partitions

前端 未结 1 734

(Note: updated with adopted answer below.)

For a PostgreSQL 8.1 (or later) partitioned table, how does one define an UPDATE trigger and procedure

相关标签:
1条回答
  • 2021-01-31 19:33

    It can be made to work, the trigger that does the move just needs to be defined for each partition, not the whole table. So start as you did for table definitions and the INSERT trigger

    CREATE TABLE records (
     record varchar(64) NOT NULL,
     active boolean default TRUE
    );
    
    CREATE TABLE active_records (CHECK (active)) INHERITS (records);
    CREATE TABLE inactive_records (CHECK (NOT active)) INHERITS (records);
    
    CREATE OR REPLACE FUNCTION record_insert()
    RETURNS TRIGGER AS $$
    BEGIN
      IF (TRUE = NEW.active) THEN
        INSERT INTO active_records VALUES (NEW.*);
      ELSE
        INSERT INTO inactive_records VALUES (NEW.*);
      END IF;
      RETURN NULL;
    END;
    $$
    LANGUAGE plpgsql;
    
    CREATE TRIGGER record_insert_trigger
     BEFORE INSERT ON records
     FOR EACH ROW EXECUTE PROCEDURE record_insert();
    

    ... let's have some test data ...

    INSERT INTO records VALUES ('FirstLittlePiggy', TRUE);
    INSERT INTO records VALUES ('SecondLittlePiggy', FALSE);
    INSERT INTO records VALUES ('ThirdLittlePiggy', TRUE);
    INSERT INTO records VALUES ('FourthLittlePiggy', FALSE);
    INSERT INTO records VALUES ('FifthLittlePiggy', TRUE);
    

    Now the triggers on the partitions. The if NEW.active = OLD.active check is implicit in checking the value of active since we know what's allowed to be in the table in the first place.

    CREATE OR REPLACE FUNCTION active_partition_constraint()
      RETURNS TRIGGER AS $$
        BEGIN
          IF NOT (NEW.active) THEN
            INSERT INTO inactive_records VALUES (NEW.*);
            DELETE FROM active_records WHERE record = NEW.record;
            RETURN NULL;
          ELSE
            RETURN NEW;
          END IF;
        END;
        $$
        LANGUAGE plpgsql;
    
    CREATE TRIGGER active_constraint_trigger
      BEFORE UPDATE ON active_records
      FOR EACH ROW EXECUTE PROCEDURE active_partition_constraint();
    
    CREATE OR REPLACE FUNCTION inactive_partition_constraint()
      RETURNS TRIGGER AS $$
        BEGIN
          IF (NEW.active) THEN
            INSERT INTO active_records VALUES (NEW.*);
            DELETE FROM inactive_records WHERE record = NEW.record;
            RETURN NULL;
          ELSE
            RETURN NEW;
          END IF;
        END;
        $$
        LANGUAGE plpgsql;
    
    CREATE TRIGGER inactive_constraint_trigger
      BEFORE UPDATE ON inactive_records 
      FOR EACH ROW EXECUTE PROCEDURE inactive_partition_constraint();
    

    ... and test the results ...

    scratch=> SELECT * FROM active_records;
          record      | active 
    ------------------+--------
     FirstLittlePiggy | t
     ThirdLittlePiggy | t
     FifthLittlePiggy | t
    (3 rows)
    
    scratch=> UPDATE records SET active = FALSE WHERE record = 'ThirdLittlePiggy';
    UPDATE 0
    scratch=> SELECT * FROM active_records;
          record      | active 
    ------------------+--------
     FirstLittlePiggy | t
     FifthLittlePiggy | t
    (2 rows)
    
    scratch=> SELECT * FROM inactive_records;
          record       | active 
    -------------------+--------
     SecondLittlePiggy | f
     FourthLittlePiggy | f
     ThirdLittlePiggy  | f
    (3 rows)
    
    0 讨论(0)
提交回复
热议问题