Search code examples
triggerssequenceinformix

Is it possible to use nextval of a sequence as default value of a field in a table in infomrix database?


I have a table named T1 with a field id(int8) and a sequence named seq_id.

Can I use seq_id.nextval as a default value for id in informix database?

Or another way, can I use a trigger to update id before insert with seq_id.nextval?


Solution

  • In Informix you cannot use a sequence NEXTVAL as a default value for a column.

    One option is to turn the column into a BIGSERIAL.

    Other option is with an insert trigger. I did not find a way to directly assign the sequence NEXTVAL in the trigger definition, but it can be done by using a stored procedure.

    CREATE SEQUENCE seq_id 
        INCREMENT BY 1 START WITH 1 
        MINVALUE 0 
        NOCYCLE CACHE 10 
        ORDER; 
    
    CREATE TABLE t1
    (
          id BIGINT NOT NULL
        , val1 CHAR(4)
    ); 
    

    I found 2 ways to use the procedure. A generic procedure that returns the sequence NEXTVAL and a trigger procedure, that assigns the sequence NEXTVAL to the id.

    Using a generic procedure:

    CREATE FUNCTION spl_get_seq_id()
    RETURNING BIGINT AS seq_id_next;
        DEFINE seq_id_next BIGINT;
        LET seq_id_next = seq_id.NEXTVAL;
        RETURN seq_id_next;
    END FUNCTION;
    
    CREATE TRIGGER t1_ti
        INSERT ON t1 REFERENCING NEW AS new_ins
        FOR EACH ROW
        (
            EXECUTE FUNCTION spl_get_seq_id() INTO id
        );
    

    Inserting a few values into the table and checking the result:

    INSERT INTO t1( id, val1 ) VALUES ( 1000, 'AAAA' );
    INSERT INTO t1( val1 ) VALUES ( 'AAAB' );
    INSERT INTO t1( id ) VALUES ( 1 );
    INSERT INTO t1( id, val1 ) VALUES ( NULL::BIGINT, 'AAAD' );
    
    SELECT * FROM t1;
    
    id val1
    
     1 AAAA
     2 AAAB
     3
     4 AAAD
    

    Using a trigger procedure:

    DROP TRIGGER t1_ti;
    
    CREATE PROCEDURE t1_ti_spl_get_seq_id()
    REFERENCING NEW AS new_values FOR t1;
        LET new_values.id = seq_id.NEXTVAL;
    END PROCEDURE;
    
    CREATE TRIGGER t1_ti
        INSERT ON t1
        FOR EACH ROW
        (
            EXECUTE PROCEDURE t1_ti_spl_get_seq_id() WITH TRIGGER REFERENCES
        );
    

    Inserting a few values into the table and checking the result:

    INSERT INTO t1( id, val1 ) VALUES ( 1000, 'AAAE' );
    INSERT INTO t1( val1 ) VALUES ( 'AAAF' );
    INSERT INTO t1( id ) VALUES ( 1 );
    INSERT INTO t1( id, val1 ) VALUES ( NULL::BIGINT, 'AAAH' );
    
    SELECT * FROM t1;
    
    id val1
    
     1 AAAA
     2 AAAB
     3
     4 AAAD
     5 AAAE
     6 AAAF
     7
     8 AAAH
    

    I used BIGINT for the id column, but it should work for INT8 (as to why did I use it, seems there is some benefits: Counters and codes: BIGINT, INT8, INTEGER, and SMALLINT).

    EDIT 1:

    In response to your comment, you can try a condition on the trigger, based on the session user. This will only work if the cdc software is using a dedicated user. In this example, cdc_agent is the user that the cdc software uses in Informix.

    DATABASE db1;
    
    GRANT CONNECT TO cdc_agent;
    GRANT CONNECT TO myuser;
    
    CREATE SEQUENCE seq_id 
        INCREMENT BY 2 START WITH 2 
        MINVALUE 0 
        NOCYCLE CACHE 10 
        ORDER; 
    
    GRANT SELECT ON seq_id TO cdc_agent;
    GRANT SELECT ON seq_id TO myuser;
    
    CREATE TABLE t1
    (
          id BIGINT NOT NULL
        , val1 CHAR(4)
    );
    
    GRANT ALL ON t1 TO cdc_agent;
    GRANT ALL ON t1 TO myuser;
    
    CREATE PROCEDURE t1_ti_spl_get_seq_id()
    REFERENCING NEW AS new_values FOR t1;
        LET new_values.id = seq_id.NEXTVAL;
    END PROCEDURE;
    
    GRANT EXECUTE ON t1_ti_spl_get_seq_id TO cdc_agent;
    GRANT EXECUTE ON t1_ti_spl_get_seq_id TO myuser;  
    
    CREATE TRIGGER t1_ti
        INSERT ON t1 REFERENCING NEW AS new_ins
        FOR EACH ROW WHEN ( USER <> "cdc_agent" )
        (
            EXECUTE PROCEDURE t1_ti_spl_get_seq_id() WITH TRIGGER REFERENCES
        );
    

    Inserting a few values into the table and checking the result:

    -- with user "cdc_agent"
    INSERT INTO t1( id, val1 ) VALUES ( 11, 'AAAA' );
    INSERT INTO t1( id, val1 ) VALUES ( 13, 'AAAC' );
    -- with user "myuser"
    INSERT INTO t1( id, val1 ) VALUES ( 1, 'AAAB' );
    INSERT INTO t1( id, val1 ) VALUES ( 3, 'AAAD' );
    
    SELECT * FROM t1;   
    
      id val1
    
      11 AAAA
      13 AAAC
       2 AAAB
       4 AAAD