PostgreSQL中替代SQL Server Instead of Trigger的Inserted表实现咨询
嘿,我刚好折腾过类似的需求,给你捋清楚PostgreSQL里怎么实现这个链式哈希的批量插入逻辑——毕竟要搞简易区块链,核心就是每条记录的哈希得跟前面的记录串起来对吧?
首先得明确:PostgreSQL里没有像SQL Server那样的INSTEAD OF INSERT触发器(这个只能用在视图上),但我们可以用BEFORE INSERT触发器来达到同样的效果:拦截插入请求,计算链式哈希后再把修改后的记录插入表中。而且批量插入的场景也完全能覆盖,下面是具体步骤:
1. 先准备哈希计算的依赖
PostgreSQL本身没有内置的SHA256哈希函数,得先启用pgcrypto扩展:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
2. 创建区块链数据表格
我们需要一个表来存储数据、前一条记录的哈希,以及当前记录的哈希:
CREATE TABLE blockchain_data ( id SERIAL PRIMARY KEY, payload TEXT NOT NULL, -- 这里放你要存储的实际业务数据 prev_hash VARCHAR(64) NOT NULL, -- 前一条记录的哈希值 current_hash VARCHAR(64) NOT NULL, -- 当前记录的哈希(包含prev_hash) created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
3. 编写触发器函数处理哈希计算
这个函数是核心,它会在每条记录插入前自动计算链式哈希:
CREATE OR REPLACE FUNCTION generate_chain_hash() RETURNS TRIGGER AS $$ DECLARE last_record_hash VARCHAR(64); BEGIN -- 获取表中最后一条记录的哈希,如果表是空的就用初始种子(空字符串的SHA256) SELECT current_hash INTO last_record_hash FROM blockchain_data ORDER BY id DESC LIMIT 1 FOR UPDATE; -- 加锁防止并发插入时出现哈希分叉 -- 处理第一条记录的情况 IF last_record_hash IS NULL THEN last_record_hash = encode(digest('', 'sha256'), 'hex'); END IF; -- 给当前要插入的记录设置前哈希 NEW.prev_hash = last_record_hash; -- 计算当前记录的哈希:把业务数据和前哈希拼接后做SHA256哈希 NEW.current_hash = encode(digest(NEW.payload || last_record_hash, 'sha256'), 'hex'); RETURN NEW; -- 返回修改后的记录,触发器会把这条记录插入表中 END; $$ LANGUAGE plpgsql;
这里要注意FOR UPDATE的作用:如果有多个并发插入请求,加锁能保证同一时间只有一个事务能获取最后一条记录的哈希,避免出现两条记录共用同一个prev_hash导致区块链分叉的问题。
4. 创建触发器绑定函数
把上面的函数绑定到blockchain_data表的插入事件上:
CREATE TRIGGER trigger_before_insert_blockchain BEFORE INSERT ON blockchain_data FOR EACH ROW EXECUTE FUNCTION generate_chain_hash();
FOR EACH ROW意味着批量插入时每一条记录都会触发这个函数,而且同一事务里前面插入的记录的哈希会立即被后面的记录获取到,完美支持批量插入的链式计算。
5. 测试验证
现在可以试试批量插入,看看哈希链是否正常:
-- 先插入第一条记录 INSERT INTO blockchain_data (payload) VALUES ('初始测试数据'); -- 批量插入多条记录 INSERT INTO blockchain_data (payload) VALUES ('第二条业务数据'), ('第三条业务数据'), ('第四条业务数据');
查询验证哈希链:
SELECT id, payload, prev_hash, current_hash FROM blockchain_data;
你会看到每条记录的prev_hash都严格等于上一条记录的current_hash,完全符合区块链的链式哈希要求。
额外说明
如果你的业务数据不是文本类型,比如是JSON或者其他格式,只需要修改digest()函数里的拼接逻辑就行——把业务数据转换成字符串再和prev_hash拼接即可。
内容的提问来源于stack exchange,提问作者Ryan Fisch

