微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

PostgreSQL动态分区和休眠插入无法通过插入触发器返回NULL之前

如何解决PostgreSQL动态分区和休眠插入无法通过插入触发器返回NULL之前

Postgresql动态分区和休眠插入不能通过触发器工作。

我有一个Graph和Node表。该图通过node_id与Node表具有一对一关系,而node表通过parent_id与自身具有递归关系。每个节点表和图形表都有 tenant_name ,在多租户表中,我想按照https://read.acloud.guru/how-to-partition-dynamically-in-postgresql-ce3acbaef66c

所述使用动态分区进行分区。

所以我有

CREATE TABLE IF NOT EXISTS graph
(
  id SERIAL NOT NULL,name character varying(255) NOT NULL,opt_lock_version int NOT NUll default 1,type character varying(255) NOT NULL,tenant_name character varying(255) NOT NULL,node_id int,CONSTRAINT graph_pkey PRIMARY KEY (id),CONSTRAINT graph_node_fk FOREIGN KEY (node_id)
      REFERENCES node (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,CONSTRAINT graph_node_id_uk UNIQUE (node_id)
);

CREATE TABLE IF NOT EXISTS node
(
  id SERIAL NOT NULL,pending_removal boolean,parent_id int,CONSTRAINT node_pkey PRIMARY KEY (id),CONSTRAINT node_parent_id_fk FOREIGN KEY (parent_id)
      REFERENCES node (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

我已经存储了创建动态表的功能,并在动态创建的动态表中插入了一行(并且有效):

CREATE OR REPLACE FUNCTION graph_insert_function()
RETURNS TRIGGER AS $$
DECLARE
    tenant_name    TEXT;
    node_partition_name TEXT;
    graph_partition_name TEXT;
BEGIN
    tenant_name := NEW.tenant_name;
    graph_partition_name := 'graph_' || tenant_name;
    node_partition_name := 'node_' || tenant_name;


IF NOT EXISTS  
    (SELECT 1
     FROM   information_schema.tables 
     WHERE  table_name = '|| node_partition_name ||') 
THEN
    RAISE NOTICE 'A node table partition has been created %',node_partition_name;
    EXECUTE format(E'CREATE TABLE IF NOT EXISTS  %I ( CHECK ( tenant_name = ''%s'')) INHERITS (node)',node_partition_name,tenant_name) ;
END IF;

IF NOT EXISTS
    (SELECT 1
     FROM   information_schema.tables 
     WHERE  table_name = '|| graph_partition_name ||') 
THEN
    RAISE NOTICE 'A node table partition has been created %',graph_partition_name;
    EXECUTE format(E'CREATE TABLE IF NOT EXISTS %I ( CHECK ( tenant_name = ''%s'')) INHERITS (graph)',graph_partition_name,tenant_name) ;
END IF;


EXECUTE format(E'INSERT INTO %I (id,name,opt_lock_version,type,tenant_name,node_id) VALUES ($1,$2,$3,$4,$5,$6) returning id',graph_partition_name) using NEW.id,NEW.name,NEW.opt_lock_version,NEW.type,NEW.tenant_name,NEW.node_id;

RETURN NULL;  // IF I RETURN NEW then it will also insert into main graph table which I do not want.

END
$$
LANGUAGE plpgsql;

问题是现在Graph表中出现了Hibernate Insert,并且由于存储函数返回NULL,所以它返回“ INSERT 0 0”。 Hibernate失败,批处理更新从 update [0]返回意外行计数为1;实际行数:0;预期:1;语句执行。记录已插入到动态表中,但是0 行在主表中受到影响。我不想在主图形表中重复数据(应该为空),只有动态表才会有记录。

如何伪造“ INSERT 0 1”或如何指示已插入数据以使其休眠?

更新:

我能够通过更改休眠内部 org.hibernate.jdbc.Expectations 类来伪造插入内容,该类检查受影响的记录数,并通过threadlocal变量count_zero将其转换为TRUE:现在唯一的问题更新和删除语句没有 tenant_name ,否则它不会对租户进行全表扩展,而不仅仅是分区,因此更新/删除将很慢。

/**
 * Holds varIoUs often used {@link Expectation} deFinitions.
 *
 * @author Steve Ebersole
 */
public class Expectations {

    **public static final ThreadLocal<Boolean> count_zero = new ThreadLocal<Boolean>();**

    private static final CoreMessageLogger LOG = CoreLogging.messageLogger( Expectations.class );

    private static sqlExceptionHelper sqlExceptionHelper = new sqlExceptionHelper( false );

    public static final int USUAL_EXPECTED_COUNT = 1;
    public static final int USUAL_ParaM_POSITION = 1;


    // Base Expectation impls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    public static class BasicExpectation implements Expectation {
        private final int expectedRowCount;

        protected BasicExpectation(int expectedRowCount) {
            this.expectedRowCount = expectedRowCount;
            if ( expectedRowCount < 0 ) {
                throw new IllegalArgumentException( "Expected row count must be greater than zero" );
            }
        }

        public final void verifyOutcome(int rowCount,PreparedStatement statement,int batchPosition) {
            rowCount = determineRowCount( rowCount,statement );
            if ( batchPosition < 0 ) {
                checkNonBatched( rowCount,statement );
            }
            else {
                checkBatched( rowCount,batchPosition,statement );
            }
        }

        private void checkBatched(int rowCount,int batchPosition,PreparedStatement statement) {
            if ( rowCount == -2 ) {
                LOG.debugf( "Success of batch update unkNown: %s",batchPosition );
            }
            else if ( rowCount == -3 ) {
                throw new BatchFailedException( "Batch update Failed: " + batchPosition );
            }
            else {
                if ( expectedRowCount > rowCount ) {
                    **if(count_zero.get() == null || count_zero.get()) {**
                        throw new StaleStateException(
                                "Batch update returned unexpected row count from update ["
                                        + batchPosition + "]; actual row count: " + rowCount
                                        + "; expected: " + expectedRowCount + "; statement executed: "
                                        + statement
                        );
                    }
                }
                if ( expectedRowCount < rowCount ) {
                    **if(count_zero.get() == null || count_zero.get()) {**
                        String msg = "Batch update returned unexpected row count from update [" +
                                batchPosition + "]; actual row count: " + rowCount +
                                "; expected: " + expectedRowCount;
                        throw new BatchedTooManyRowsAffectedException(msg,expectedRowCount,rowCount,batchPosition);
                    }
                }
            }
        }

        private void checkNonBatched(int rowCount,PreparedStatement statement) {
            if ( expectedRowCount > rowCount ) {
                **if(count_zero.get() == null || count_zero.get()) {**
                    throw new StaleStateException(
                            "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount
                                    + "; statement executed: " + statement
                    );
                }
            }
            if ( expectedRowCount < rowCount ) {
                **if(count_zero.get() == null || count_zero.get()) {**
                    String msg = "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount;
                    throw new TooManyRowsAffectedException(msg,rowCount);
                }
            }
        }

        public int prepare(PreparedStatement statement) throws sqlException,HibernateException {
            return 0;
        }

        public boolean canBeBatched() {
            return true;
        }

        protected int determineRowCount(int reportedRowCount,PreparedStatement statement) {
            return reportedRowCount;
        }
    }

    public static class BasicParamExpectation extends BasicExpectation {
        private final int parameterPosition;

        protected BasicParamExpectation(int expectedRowCount,int parameterPosition) {
            super( expectedRowCount );
            this.parameterPosition = parameterPosition;
        }

        @Override
        public int prepare(PreparedStatement statement) throws sqlException,HibernateException {
            toCallableStatement( statement ).registerOutParameter( parameterPosition,Types.NUMERIC );
            return 1;
        }

        @Override
        public boolean canBeBatched() {
            return false;
        }

        @Override
        protected int determineRowCount(int reportedRowCount,PreparedStatement statement) {
            try {
                return toCallableStatement( statement ).getInt( parameterPosition );
            }
            catch (sqlException sqle) {
                sqlExceptionHelper.logExceptions( sqle,"Could not extract row counts from CallableStatement" );
                throw new GenericJDBCException( "Could not extract row counts from CallableStatement",sqle );
            }
        }

        private CallableStatement toCallableStatement(PreparedStatement statement) {
            if ( !CallableStatement.class.isinstance( statement ) ) {
                throw new HibernateException(
                        "BasicParamExpectation operates exclusively on CallableStatements : " + statement.getClass()
                );
            }
            return (CallableStatement) statement;
        }
    }


    // VarIoUs Expectation instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    public static final Expectation NONE = new Expectation() {
        public void verifyOutcome(int rowCount,int batchPosition) {
            // explicitly doAfterTransactionCompletion no checking...
        }

        public int prepare(PreparedStatement statement) {
            return 0;
        }

        public boolean canBeBatched() {
            return true;
        }
    };

    public static final Expectation BASIC = new BasicExpectation( USUAL_EXPECTED_COUNT );

    public static final Expectation ParaM = new BasicParamExpectation( USUAL_EXPECTED_COUNT,USUAL_ParaM_POSITION );


    public static Expectation appropriateExpectation(ExecuteUpdateResultCheckStyle style) {
        if ( style == ExecuteUpdateResultCheckStyle.NONE ) {
            return NONE;
        }
        else if ( style == ExecuteUpdateResultCheckStyle.COUNT ) {
            return BASIC;
        }
        else if ( style == ExecuteUpdateResultCheckStyle.ParaM ) {
            return ParaM;
        }
        else {
            throw new HibernateException( "unkNown check style : " + style );
        }
    }

    private Expectations() {
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。