pg truncate

命令选项

TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ][ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

1.ONLY:只truncate指定的表。当表有继承子表或有子分区时,默认会一起truncate;only可只truncate继承父表。分区父表不能指定only

--不能truncate only分区父表
=> truncate only parttable;
ERROR:  42809: cannot truncate only a partitioned table
HINT:  Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.
LOCATION:  ExecuteTruncate, tablecmds.c:1655
--truncate only继承父表,只清理父表
=> truncate table only parenttable;
TRUNCATE TABLE
=> select tableoid::regclass,count(*) from parenttable group by tableoid::regclass ;tableoid  | count 
------------+-------childtable |     1--直接truncate继承父表,子表也会被清理
=>  truncate table parenttable;
TRUNCATE TABLE
=> select tableoid::regclass,count(*) from parenttable group by tableoid::regclass ;tableoid | count 
----------+-------
(0 rows)

2.RESTART IDENTITY CONTINUE IDENTITY:列上的序列是否要重置,默认CONTINUE。

--bigserial 默认会创建列上的序列
=> create table tableserial (a  bigserial not null,b name);
CREATE TABLE
=> \d+ tableserial;Table "public.tableserial"Column |  Type  | Collation | Nullable |                Default                 | Storage | Stats target | Description 
--------+--------+-----------+----------+----------------------------------------+---------+--------------+-------------a      | bigint |           | not null | nextval('tableserial_a_seq'::regclass) | plain   |              | b      | name   |           |          |                                        | plain   |              | => insert into tableserial(b) select md5(random()::text) from generate_series(1,1000);
INSERT 0 1000 
--seq当前值为1000
=> select currval('tableserial_a_seq'::regclass);currval 
---------1000--直接truncate默认不会重置序列
=>  truncate table tableserial;
TRUNCATE TABLE
=> select currval('tableserial_a_seq'::regclass) cur,nextval('tableserial_a_seq'::regclass);cur  | nextval 
------+---------1000 |    1001--显示指定RESTART IDENTITY,重置序列
=>  truncate table tableserial RESTART IDENTITY;
TRUNCATE TABLE
--注意seq在nextval时重置了
=>  select currval('tableserial_a_seq'::regclass) cur,nextval('tableserial_a_seq'::regclass);cur  | nextval 
------+---------1001 |       1

3.CASCADE:清理表及其所有外键表的数据

--创建主表和外键表和数据
=>  create table pri_tab(id bigint primary key,name varchar(10));
CREATE TABLE=> insert into pri_tab values (1,'abc'),(2,'abc'),(3,'abc');
INSERT 0 3=>  create table frn_tab(id bigint,FOREIGN KEY (id) REFERENCES pri_tab(id));
CREATE TABLE=> insert into frn_tab values (1),(2);
INSERT 0 2=> select * from pri_tab;id | name 
----+------1 | abc2 | abc3 | abc
(3 rows)--外键表frn_tab依赖主表pri_tab的数据
=> select * from frn_tab;id 
----12
(2 rows)--有主表外键reference时,外键表必须跟cascade,否则无法清理
=> truncate table pri_tab ;
ERROR:  0A000: cannot truncate a table referenced in a foreign key constraint
DETAIL:  Table "frn_tab" references "pri_tab".
HINT:  Truncate table "frn_tab" at the same time, or use TRUNCATE ... CASCADE.
LOCATION:  heap_truncate_check_FKs, heap.c:3427--外键约束的表一起清空
=> truncate table pri_tab cascade;
NOTICE:  00000: truncate cascades to table "frn_tab"
LOCATION:  ExecuteTruncateGuts, tablecmds.c:1725
TRUNCATE TABLE
=> select * from pri_tab;id | name 
----+------
(0 rows)=>  select * from frn_tab;id 
----
(0 rows)

由于外键表依赖主表的数据,不能直接truncate主表,必须加cascade,此时外键表也跟随主表一起清空

4.RESTRICT
是否清理foreign key表。没什么用,default选项,加不加都是这样。清理附带的外键表应加CASCADE。

MVCC/transaction

pg官方文档有这么一段化

TRUNCATE is not MVCC-safe. After truncation, the table will appear empty to concurrent transactions, if they are using a snapshot taken before the truncation occurred.
TRUNCATE is transaction-safe with respect to the data in the tables: the truncation will be safely rolled back if the surrounding transaction does not commit.

transaction-safe意思是可以放在事务块里,可以回退
回滚truncate:

=> begin;
BEGIN
=> truncate t1;
TRUNCATE TABLE
=> rollback;
ROLLBACK
=> select count(*) from t1;count 
-------100

not MVCC-safe意思是一个会话在truncate前打了一个快照,快照期间如果发生truncate,这个快照是可以读到truncate清理后的结果的。这不符合MVCC。
不过这个问题不算太大,在会话场景下,因为truncate是8级锁,快照没有结束的话最低在表上有一个读共享锁,所以truncate不会执行。

This will only be an issue for a transaction that did not access the table in question before the DDL command started —any transaction that has done so would hold at least an ACCESS SHARE table lock, which would block the DDL command until that transaction completes

功能更新

在这里插入图片描述
truncate更新功能不多,只需要注意14的时候支持truncate foreign tables即可。truncate foreign tables前提是fdw得支持TRUNCATE API

Also it extends postgres_fdw so that it can issue TRUNCATE command to foreign servers, by adding new routine for that TRUNCATE API.

pg truncate和其他库的功能差异

在这里插入图片描述
在这里插入图片描述
truncate很快、8级锁等特性已经是人尽皆知的事情了,相对于其他数据库,pg还可以:选择是否重置序列RESTART IDENTITY CONTINUE IDENTITY)、回滚简单的授权

truncate做了什么

create table lzl(a int);
create index lzl_idx on lzl(a);
create sequence lzl_seq start with 1;
alter table lzl alter column a set default nextval('lzl_seq');
--select pg_relation_filepath('lzl');
--db路径
=> select oid from  pg_database where datname='lzldb';oid   
--------418679--刚创建时候各个rel的oid=relfilenode
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428363 | rlzl_idx | 428366 |      428366 | ilzl_seq | 428367 |      428367 | S
(3 rows)=> truncate table lzl;
TRUNCATE TABLE
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428370 | rlzl_idx | 428366 |      428371 | ilzl_seq | 428367 |      428367 | S
--truncate后,表和索引重建了,sequence却没有M=> truncate table lzl RESTART IDENTITY;
TRUNCATE TABLE
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428372 | rlzl_idx | 428366 |      428373 | ilzl_seq | 428367 |      428367 | S
--显示restart,sequence还是没有重建M=> alter sequence lzl_seq restart;
ALTER SEQUENCE
M=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428372 | rlzl_idx | 428366 |      428373 | ilzl_seq | 428367 |      428374 | S
--显示restart sequence是会重建sequence的

truncate ···RESTART IDENTITY没有重建我们sequence,alter sequence lzl_seq restart重建了sequence。应该是RESTART IDENTITY没有理解对。在看下官方文档对RESTART IDENTITY的解释

Automatically restart sequences owned by columns of the truncated table(s).

sequence必须owned by表上的列,注意不是owner to。虽然\d可以看到表上的sequence,但是它可能不属于表

 \d+ lzl;Table "public.lzl"Column |  Type   | Collation | Nullable |           Default            | Storage | Stats target | Description 
--------+---------+-----------+----------+------------------------------+---------+--------------+-------------a      | integer |           |          | nextval('lzl_seq'::regclass) | plain   |              | 

owned by修改sequence的所属表

=> ALTER SEQUENCE lzl_seq OWNED BY lzl.a;
ALTER SEQUENCE--查看序列的所有者信息
SELECT s.relname AS seq, n.nspname AS sch, t.relname AS tab, a.attname AS col  
FROM pg_class s  
JOIN pg_depend d ON d.objid=s.oid AND d.classid='pg_class'::regclass AND d.refclassid='pg_class'::regclass  
JOIN pg_class t ON t.oid=d.refobjid  
JOIN pg_namespace n ON n.oid=t.relnamespace  
JOIN pg_attribute a ON a.attrelid=t.oid AND a.attnum=d.refobjsubid  
WHERE s.relkind='S' AND d.deptype='a';seq        |  sch   |     tab     | col 
-------------------+--------+-------------+-----tableserial_a_seq | public | tableserial | alzl_seq           | public | lzl         | a=> truncate table lzl RESTART IDENTITY;
TRUNCATE TABLE
M=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------lzl     | 428363 |      428375 | rlzl_idx | 428366 |      428376 | ilzl_seq | 428367 |      428377 | S

sequence owned by表上的列时,truncate显示带RESTART IDENTITY就会restart这个sequence,也就重建了sequence。默认以serial/bigserial方式创建的序列是被表拥有的,随表drop而删除;那些不被表拥有的序列,drop不会删除
truncate重建特性汇总:

  • 直接truncate table会重建表和索引
  • truncate table+RESTART IDENTITY,会重建(也就是retart)属于这个表的sequence。只要不属于这个表的sequence,哪怕列上关联了seq的默认值,也不会重建这个seq

源码分析

truncate也是utility命令,很快就可以找到入口函数
src/backend/commands/tablecmds.c中的ExecuteTruncate为入口函数,注释其实已经说明truncate要获得exclusive lock,并检查权限和relation是否ok,递归检查所有需要truncate的表

voidExecuteTruncate(TruncateStmt *stmt){.../** Open, exclusive-lock, and check all the explicitly-specified relations*/foreach(cell, stmt->relations){...
LOCKMODE lockmode = AccessExclusiveLock;  //8级锁
...rel = table_open(myrelid, NoLock); //打开表void
ExecuteTruncate(TruncateStmt *stmt)
{
...foreach(cell, stmt->relations){
...LOCKMODE	lockmode = AccessExclusiveLock; //8级锁
.../* open the relation, we already hold a lock on it */rel = table_open(myrelid, NoLock); //打开表
...truncate_check_activity(rel);  //虽然已经有锁了,但是还是要验证是否在使用
...if (recurse) //递归执行{
...children = find_all_inheritors(myrelid, lockmode, NULL); //找到所有继承子表foreach(child, children){
...//上面只检查了父表,递归要检查子表truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);truncate_check_activity(rel);rels = lappend(rels, rel);  //加入到待truncate的rel队列中relids = lappend_oid(relids, childrelid);
...}}//递归结束//发现truncate only分区父表,直接报错else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)ereport(ERROR,(errcode(ERRCODE_WRONG_OBJECT_TYPE),errmsg("cannot truncate only a partitioned table"),errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));}//主体函数	ExecuteTruncateGuts(rels, relids, relids_logged,stmt->behavior, stmt->restart_seqs);/* And close the rels */foreach(cell, rels){Relation	rel = (Relation) lfirst(cell);table_close(rel, NoLock);}
}

ExecuteTruncateGuts函数不仅被truncate命令调用,还被订阅端调用(发布订阅可以同步truncate)。

void
ExecuteTruncateGuts(List *explicit_rels,List *relids,List *relids_logged,DropBehavior behavior, bool restart_seqs)
{
...rels = list_copy(explicit_rels);if (behavior == DROP_CASCADE)  //如果指定了cascade选项,提取所有reference的relation{for (;;){
...newrelids = heap_truncate_find_FKs(relids); //找到fkif (newrelids == NIL)break;			/* nothing else to add */ //没有rel直接退出foreach(cell, newrelids){
...rel = table_open(relid, AccessExclusiveLock); //所有rel获得AccessExclusiveLockereport(NOTICE,(errmsg("truncate cascades to table \"%s\"",RelationGetRelationName(rel))));truncate_check_rel(relid, rel->rd_rel);  //检查是否是可以truncate的对象,得是存储数据的表truncate_check_perms(relid, rel->rd_rel);  //检查是否有权限truncate_check_activity(rel);  //检查是否在使用
...}}}...if (restart_seqs) //restart seq的处理{foreach(cell, rels){Relation	rel = (Relation) lfirst(cell);List	   *seqlist = getOwnedSequences(RelationGetRelid(rel));
...//只是做sequence的权限检查if (!pg_class_ownercheck(seq_relid, GetUserId()))aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,RelationGetRelationName(seq_rel));
...}}...//执行所有before truncate触发器foreach(cell, rels){ExecBSTruncateTriggers(estate, resultRelInfo);resultRelInfo++;}//正式开始truncateforeach(cell, rels){
...//如果是分区父表,啥都不做if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)continue;//如果是foreign table的处理if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE){...}...//如果是同一事务,因为可能会回退,直接执行heap_truncate_one_rel函数,不创建新的relfilenodeif (rel->rd_createSubid == mySubid ||rel->rd_newRelfilenodeSubid == mySubid){/* Immediate, non-rollbackable truncation is OK */heap_truncate_one_rel(rel);}else{
...//设置NewRelfilenodeRelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence);heap_relid = RelationGetRelid(rel);//toast同理toast_relid = rel->rd_rel->reltoastrelid;if (OidIsValid(toast_relid)){Relation	toastrel = relation_open(toast_relid,AccessExclusiveLock);RelationSetNewRelfilenode(toastrel,toastrel->rd_rel->relpersistence);table_close(toastrel, NoLock);}...//重建索引reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,&reindex_params);}pgstat_count_truncate(rel); //更新pgstat的truncate计算}...//重置sequence foreach(cell, seq_relids){Oid			seq_relid = lfirst_oid(cell);ResetSequence(seq_relid);}//写walif (list_length(relids_logged) > 0){...}//触发AFTER TRUNCATE triggersresultRelInfo = resultRelInfos;foreach(cell, rels){ExecASTruncateTriggers(estate, resultRelInfo);resultRelInfo++;}
...
}	

ExecuteTruncateGuts函数根据truncate选项进行处理,处理过程如下:

  1. 根据cascade选项找到所有reference的外键表
  2. 触发before truncate触发器
  3. 执行truncate
  • 如果是同一事务,不立即生成NewRelfilenode,直接调用函数heap_truncate_one_rel进行truncate
  • 如果不是同一事务,调用RelationSetNewRelfilenode新建NewRelfilenode
  1. reindex_relation函数重建索引
  2. 根据restart identity重置sequence
  3. 写wal日志
  4. 触发after truncate触发器

后面大概追了下函数,套娃比较多
RelationSetNewRelfilenode
table_relation_set_new_filenode
relation_set_new_filenode在这里插入代码片
heapam_relation_set_new_filenode
RelationCreateStorage
然后到src/backend/storage/smgr/smgr.c中的smgrcreatesmgr_create。后面就没看太懂了(一到函数指针就有点追不到的感觉,先这样吧~)···
对于smgr.c有这样的注释:

public interface routines to storage manager switch
All file system operations in POSTGRES dispatch through these routines.

任何文件系统操作都会经过smgr(storage manager);到这里就是文件系统操作了。

reference

https://www.postgresql.org/docs/15/sql-truncate.html
https://www.postgresql.org/docs/current/mvcc-caveats.html
https://pgpedia.info/t/truncate.html
https://www.orafaq.com/wiki/SQL_FAQ
https://learnsql.com/blog/difference-between-truncate-delete-and-drop-table-in-sql/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/175785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue2使用ts vue-class-component

目前,对于Vue3来说,TypeScript的支持已经相当成熟,但公司的老项目一直处于迭代和维护无法从v2重构成v3,并且重构的成本也是很大的一个问题,所以记录一下vue2如何去搭配TypeScript。 目录 一、脚手架创建项目 二、vu…

计算机网络:快速了解网络框架

文章目录 前言一、什么是Internet?1.从具体构成角度什么是协议? 2.从服务角度3小结 二、网络边缘1.采用网络设施面向连接服务(TCP)2.采用基础设施的无连接服务(UDP) 三、网络的核心1.电路交换2.分组交换3.分…

Android : 获取、添加、手机联系人-ContentResolver简单应用

示例图: MainActivity.java package com.example.mygetdata;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat;import android.Mani…

2024 年应该使用 Bun、Node.js 还是 Deno

2024 年应该使用 Bun、Node.js 还是 Deno 到 2024 年,构建基于 JavaScript 的现代 API 相对简单。我们可以使用Express.js等库并在几分钟内启动可用的 API。但是,现在最具挑战性的部分是选择正确的 JavaScript 引擎。 目前主流的三个运行时是&#xff…

Android 单元测试初体验(二)-断言

[TOC](Android 单元测试初体验(二)-断言) 前言 当初在学校学安卓的时候,老师敢教学进度,翻到单元测试这一章节的时候提了两句,没有把单元测试当重点讲,只是说我们工作中几乎不会用到,果真在之前的几年工作当中我真的没…

95.STL-遍历算法 for_each

算法概述: 算法主要是由头文件 <algorithm> <functional> <numeric> 组成。 <algorithm> 是所有STL头文件中最大的一个&#xff0c;范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 <numeric> 体积很小&#xff0c;只包括几个在序列上面…

第1章 爬虫基础

目录 1. HTTP 基本原理1.1 URI 和 URL1.2 HTTP 和 HTTPS1.3 请求1.3.1 请求方法1.3.2 请求的网址1.3.3 请求头1.3.4 请求体 1.4 响应1.4.1 响应状态码1.4.2 响应头1.4.3 响应体 2. Web 网页基础2.1 网页的组成2.1.1 HTML2.1.2 CSS2.1.3 JavaScript 2.2 网页的结构2.3 节点树及节…

人工智能 -- 技术概览

1、我们身处人工智能的时代 人们从早期做web开发&#xff0c;到移动端的开发&#xff1b;之后随着数据量的增大&#xff0c;人们开始研究高并发的问题&#xff1b;当数据量不断的增大&#xff0c;而人们希望数据不被浪费时&#xff0c;产生了大数据的技术&#xff0c;包括&…

正则表达式 通配符 awk文本处理工具

目录 什么是正则表达式 概念 正则表达式的结构 正则表达式的组成 元字符 元字符点&#xff08;.&#xff09; 代表字符. 点值表示点需要转义 \ r..t 代表r到t之间任意两个字符 过滤出小写 过滤出非小写 space空格 [[:space:]] 表示次数 位置锚定 例&#xff1a…

第三节HarmonyOS DevEco Studio了解基本工程目录

一、工程级目录 工程的目录结构如下。 目录详情如下&#xff1a; AppScope&#xff1a;存放应用全局所需要的资源文件。Entry&#xff1a;应用的主模块&#xff0c;存放HarmonyOS应用的代码、资源等。oh_modules&#xff1a;工程的依赖包&#xff0c;存放工程依赖的源文件。b…

Umi-OCR图片批量识别文字工具

OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/粘贴/批量导入图片&#xff0c;段落排版/排除水印&#xff0c;扫描/生成二维码。内置多国语言库。 项目地址&#xff1a;https://github.com/hiroi-sora/Umi-OCR

深度学习之图像分类(十四)CAT: Cross Attention in Vision Transformer详解

IPSA和CPSA的处理流程、维度变换细节 FLOPs的计算方法、以及flops和划分的patch数目以及patch的维度计算关系 IPSA如何进行local attention、CPSA如何进行globe attention CAT的代码详细注释---需要学习完Transformer TNT、swin transformer、crossViT CAT: Cross Atten…

C语言——打印出所有的“水仙花数”

所谓水仙花数,是指一个3位数,其各位数字立方和等于该数本身。水仙花数是指一个三位数&#xff0c;它的每个位上的数字的立方和等于它本身。例如&#xff0c;153是一个水仙花数&#xff0c;因为1^3 5^3 3^3 153。 #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>…

Scrapy爬虫异步框架(一篇文章齐全)

1、Scrapy框架初识 2、Scrapy框架持久化存储&#xff08;点击前往查阅&#xff09; 3、Scrapy框架内置管道&#xff08;点击前往查阅&#xff09; 4、Scrapy框架中间件&#xff08;点击前往查阅&#xff09; Scrapy 是一个开源的、基于Python的爬虫框架&#xff0c;它提供了…

第71讲:MySQL锁机制详解:表级锁、元数据锁和意向锁的全面解析与实践指南

MySQL中的表级锁 文章目录 MySQL中的表级锁1.MySQL中表级锁的概念2.表锁的概念以及基本使用2.1.表锁的分类以及概念2.2.表锁的使用语法2.3.表共享读锁的基本使用2.4.表独占写锁的基本使用 3.元数据锁的概念以及基本使用3.1.元数据锁的概念3.2.常见的SQL操作所对应的元数据锁3.3…

npm中的npx命令

1.概念 npx是一个执行npm软件包的二进制文件&#xff0c;通俗的讲&#xff0c;他可以执行npm的一些指令。 2.示例 用babel将ES6语法转为ES5语法 npx babel src/js -d dist/js会执行babel的相关功能&#xff0c;如果没有安装&#xff0c;也会自动安装。 当在执行npx <co…

sqli-labs靶场详解less-24(二次注入)

less-24 对于一个像我一样的小白来说这关就像php代码审计 一开始进行判断注入点的时候怎么都找不到一点思路都没有 只能搜教程 说是二次注入 从来没遇见的题型 于是从代码审计开始 先说一下什么叫二次注入 二次注入 二次注入是指通过SQL语句存储到数据库的用户输入被读取后再次…

linux 安装 mvn

mvn 下载地址&#xff1a;https://maven.apache.org/download.cgi 选择一个合适的版本 cd /opt && curl -o apache-maven-3.8.6-bin.tar.gz https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz tar -xzf apache-maven-3.8.6-bin.…

随手写了个博客多平台发布脚本:Python自动发布文章到Wordpress

​ 引言 作为一名技术博主&#xff0c;提高博客发布效率是我们始终追求的目标。在这篇文章中&#xff0c;我将分享一个基于Python的脚本&#xff0c;能够实现博客多平台发布&#xff0c;具体来说&#xff0c;是自动发布文章到WordPress。通过这个简单而高效的脚本&#xff0c…

uni-app x生成的安卓包,安装时,提示不兼容。解决方案

找到 manifest.json 进入&#xff1a;源码视图 代码 {"name" : "xxx康养","appid" : "__xxx6","description" : "xxx康养","versionName" : "1.0.12","versionCode" : 100012,&…