PostgreSQL create or replace view和重建视图 有什么区别?

一、 replace vs 重建

       遇到开发提了个问题,create or replace view和重建视图(drop+create)有什么区别,查询资料整理了一下。

1. create or replace

  • 当存在同名视图时,尝试将其替换
  • 新视图语句必须与现有视图查询具有相同的列(即相同的列名、列顺序和数据类型)
  • pg 8.1开始,之前向视图末尾添加新列
  • 总体而言,改动限制较大,但replace后不影响权限和依赖于该视图的对象

2. drop+create

  • 新视图定义不依赖原视图,灵活度高
  • 重建后权限丢失,必须重新授权
-- 对象授权查询
SELECT * FROM information_schema.table_privileges WHERE table_name='视图名' and grantor<>grantee;
  • 依赖于该视图的对象需要一起重建,复杂度可能较高

二、 源码学习

1. CREATE OR REPLACE VIEW

       按照 "CREATE OR REPLACE VIEW" 关键字搜索,这部分代码在ATExecCmd函数(tablecmds.c文件)。可以看到它对应的命令类型叫AT_AddColumnToView,对应操作为调用ATExecAddColumn函数为视图新加列。

/** ATExecCmd: dispatch a subcommand to appropriate execution routine*/
static void
ATExecCmd(List **wqueue, AlteredTableInfo *tab,AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,AlterTableUtilityContext *context)
{ObjectAddress address = InvalidObjectAddress;Relation	rel = tab->rel;switch (cmd->subtype){case AT_AddColumn:		/* ADD COLUMN */case AT_AddColumnToView:	/* add column via CREATE OR REPLACE VIEW */address = ATExecAddColumn(wqueue, tab, rel, &cmd,false, false,lockmode, cur_pass, context);break;
...

2. ATExecAddColumn函数

比较长的一个函数,主要操作如下:

  • 权限检查

/** Add a column to a table.  The return value is the address of the* new column in the parent relation.** cmd is pass-by-ref so that we can replace it with the parse-transformed* copy (but that happens only after we check for IF NOT EXISTS).*/
static ObjectAddress
ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,AlterTableCmd **cmd,bool recurse, bool recursing,LOCKMODE lockmode, int cur_pass,AlterTableUtilityContext *context)
{Oid			myrelid = RelationGetRelid(rel);ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);bool		if_not_exists = (*cmd)->missing_ok;Relation	pgclass,attrdesc;
.../* At top level, permission check was done in ATPrepCmd, else do it */if (recursing)ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);if (rel->rd_rel->relispartition && !recursing)ereport(ERROR,(errcode(ERRCODE_WRONG_OBJECT_TYPE),errmsg("cannot add column to a partition")));
  • 获取表锁,等级为RowExclusiveLock

attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
  • 判断是否为递归子表加列,能否进行merge列

    若是merge,要求更新前后列类型及排序规则一致(Child column must match on type, typmod, and collation)

	/** Are we adding the column to a recursion child?  If so, check whether to* merge with an existing definition for the column.  If we do merge, we* must not recurse.  Children will already have the column, and recursing* into them would mess up attinhcount.*/if (colDef->inhcount > 0){HeapTuple	tuple;/* Does child already have a column by this name? */tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);if (HeapTupleIsValid(tuple)){Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);Oid			ctypeId;int32		ctypmod;Oid			ccollid;/* Child column must match on type, typmod, and collation */typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);if (ctypeId != childatt->atttypid ||ctypmod != childatt->atttypmod)ereport(ERROR,(errcode(ERRCODE_DATATYPE_MISMATCH),errmsg("child table \"%s\" has different type for column \"%s\"",RelationGetRelationName(rel), colDef->colname)));ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);if (ccollid != childatt->attcollation)ereport(ERROR,(errcode(ERRCODE_COLLATION_MISMATCH),errmsg("child table \"%s\" has different collation for column \"%s\"",RelationGetRelationName(rel), colDef->colname),errdetail("\"%s\" versus \"%s\"",get_collation_name(ccollid),get_collation_name(childatt->attcollation))));/* Bump the existing child att's inhcount */childatt->attinhcount++;CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);heap_freetuple(tuple);/* Inform the user about the merge */ereport(NOTICE,(errmsg("merging definition of column \"%s\" for child \"%s\"",colDef->colname, RelationGetRelationName(rel))));table_close(attrdesc, RowExclusiveLock);return InvalidObjectAddress;}}
  • 一些检查,例如列名是否已存在,列数是否超出限制等

	/* skip if the name already exists and if_not_exists is true */if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists)){table_close(attrdesc, RowExclusiveLock);return InvalidObjectAddress;}
.../* Determine the new attribute's number */newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;if (newattnum > MaxHeapAttributeNumber)ereport(ERROR,(errcode(ERRCODE_TOO_MANY_COLUMNS),errmsg("tables can have at most %d columns",MaxHeapAttributeNumber)));
  • 填充pg_attribute及pg_class信息

	/* construct new attribute's pg_attribute entry */attribute.attrelid = myrelid;namestrcpy(&(attribute.attname), colDef->colname);attribute.atttypid = typeOid;attribute.attstattarget = (newattnum > 0) ? -1 : 0;attribute.attlen = tform->typlen;attribute.attnum = newattnum;attribute.attndims = list_length(colDef->typeName->arrayBounds);attribute.atttypmod = typmod;attribute.attbyval = tform->typbyval;attribute.attalign = tform->typalign;attribute.attstorage = tform->typstorage;attribute.attcompression = GetAttributeCompression(typeOid,colDef->compression);attribute.attnotnull = colDef->is_not_null;attribute.atthasdef = false;attribute.atthasmissing = false;attribute.attidentity = colDef->identity;attribute.attgenerated = colDef->generated;attribute.attisdropped = false;attribute.attislocal = colDef->is_local;attribute.attinhcount = colDef->inhcount;attribute.attcollation = collOid;/* attribute.attacl is handled by InsertPgAttributeTuples() */ReleaseSysCache(typeTuple);tupdesc = CreateTupleDesc(lengthof(aattr), (FormData_pg_attribute **) &aattr);InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);table_close(attrdesc, RowExclusiveLock);/** Update pg_class tuple as appropriate*/((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);heap_freetuple(reltup);/* Post creation hook for new attribute */InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);table_close(pgclass, RowExclusiveLock);/* Make the attribute's catalog entry visible */CommandCounterIncrement();
  • 填充默认值

/** Store the DEFAULT, if any, in the catalogs*/if (colDef->raw_default){RawColumnDefault *rawEnt;rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));rawEnt->attnum = attribute.attnum;rawEnt->raw_default = copyObject(colDef->raw_default);/** Attempt to skip a complete table rewrite by storing the specified* DEFAULT value outside of the heap.  This may be disabled inside* AddRelationNewConstraints if the optimization cannot be applied.*/rawEnt->missingMode = (!colDef->generated);rawEnt->generated = colDef->generated;/** This function is intended for CREATE TABLE, so it processes a* _list_ of defaults, but we just do one.*/AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,false, true, false, NULL);/* Make the additional catalog changes visible */CommandCounterIncrement();/** Did the request for a missing value work? If not we'll have to do a* rewrite*/if (!rawEnt->missingMode)tab->rewrite |= AT_REWRITE_DEFAULT_VAL;}/** Tell Phase 3 to fill in the default expression, if there is one.** If there is no default, Phase 3 doesn't have to do anything, because* that effectively means that the default is NULL.  The heap tuple access* routines always check for attnum > # of attributes in tuple, and return* NULL if so, so without any modification of the tuple data we will get* the effect of NULL values in the new column.** An exception occurs when the new column is of a domain type: the domain* might have a NOT NULL constraint, or a check constraint that indirectly* rejects nulls.  If there are any domain constraints then we construct* an explicit NULL default value that will be passed through* CoerceToDomain processing.  (This is a tad inefficient, since it causes* rewriting the table which we really don't have to do, but the present* design of domain processing doesn't offer any simple way of checking* the constraints more directly.)** Note: we use build_column_default, and not just the cooked default* returned by AddRelationNewConstraints, so that the right thing happens* when a datatype's default applies.** Note: it might seem that this should happen at the end of Phase 2, so* that the effects of subsequent subcommands can be taken into account.* It's intentional that we do it now, though.  The new column should be* filled according to what is said in the ADD COLUMN subcommand, so that* the effects are the same as if this subcommand had been run by itself* and the later subcommands had been issued in new ALTER TABLE commands.** We can skip this entirely for relations without storage, since Phase 3* is certainly not going to touch them.  System attributes don't have* interesting defaults, either.*/if (RELKIND_HAS_STORAGE(relkind) && attribute.attnum > 0){/** For an identity column, we can't use build_column_default(),* because the sequence ownership isn't set yet.  So do it manually.*/if (colDef->identity){NextValueExpr *nve = makeNode(NextValueExpr);nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);nve->typeId = typeOid;defval = (Expr *) nve;/* must do a rewrite for identity columns */tab->rewrite |= AT_REWRITE_DEFAULT_VAL;}elsedefval = (Expr *) build_column_default(rel, attribute.attnum);if (!defval && DomainHasConstraints(typeOid)){Oid			baseTypeId;int32		baseTypeMod;Oid			baseTypeColl;baseTypeMod = typmod;baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);baseTypeColl = get_typcollation(baseTypeId);defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);defval = (Expr *) coerce_to_target_type(NULL,(Node *) defval,baseTypeId,typeOid,typmod,COERCION_ASSIGNMENT,COERCE_IMPLICIT_CAST,-1);if (defval == NULL) /* should not happen */elog(ERROR, "failed to coerce base type to domain");}if (defval){NewColumnValue *newval;newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));newval->attnum = attribute.attnum;newval->expr = expression_planner(defval);newval->is_generated = (colDef->generated != '\0');tab->newvals = lappend(tab->newvals, newval);}if (DomainHasConstraints(typeOid))tab->rewrite |= AT_REWRITE_DEFAULT_VAL;if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing){/** If the new column is NOT NULL, and there is no missing value,* tell Phase 3 it needs to check for NULLs.*/tab->verify_new_notnull |= colDef->is_not_null;}}
  • 更新pg_attribute的atttypid及attcollation字段

	/** Add needed dependency entries for the new column.*/add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
  • 若有子表,为每个子表递归调用该函数新增列

/** Propagate to children as appropriate.  Unlike most other ALTER* routines, we have to do this one level of recursion at a time; we can't* use find_all_inheritors to do it in one pass.*/children =find_inheritance_children(RelationGetRelid(rel), lockmode);/** If we are told not to recurse, there had better not be any child* tables; else the addition would put them out of step.*/if (children && !recurse)ereport(ERROR,(errcode(ERRCODE_INVALID_TABLE_DEFINITION),errmsg("column must be added to child tables too")));/* Children should see column as singly inherited */if (!recursing){childcmd = copyObject(*cmd);colDef = castNode(ColumnDef, childcmd->def);colDef->inhcount = 1;colDef->is_local = false;}elsechildcmd = *cmd;		/* no need to copy again */foreach(child, children){Oid			childrelid = lfirst_oid(child);Relation	childrel;AlteredTableInfo *childtab;/* find_inheritance_children already got lock */childrel = table_open(childrelid, NoLock);CheckTableNotInUse(childrel, "ALTER TABLE");/* Find or create work queue entry for this table */childtab = ATGetQueueEntry(wqueue, childrel);/* Recurse to child; return value is ignored */ATExecAddColumn(wqueue, childtab, childrel,&childcmd, recurse, true,lockmode, cur_pass, context);table_close(childrel, NoLock);}ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);return address;
}

参考

PostgreSQL: Documentation: 16: CREATE VIEW

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

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

相关文章

LeetCode算法题解(动态规划,背包问题)|LeetCode1049. 最后一块石头的重量 II、LeetCode494. 目标和

一、LeetCode1049. 最后一块石头的重量 II 题目链接&#xff1a;1049. 最后一块石头的重量 II 题目描述&#xff1a; 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将…

springboot2.1升级到2.7 actuator丢失部分metrics端点

项目场景&#xff1a; 项目需要升级springboot从2.1升级至2.7 问题描述 发现之前的metrics后面的jvm相关的端口丢了 原因分析&#xff1a; 找到这样一篇博文https://blog.csdn.net/CL_YD/article/details/120309094&#xff0c;这篇博文意思是对的&#xff0c;但是写的不太好…

Java基于springoot开发的企业招聘求职网站

演示视频&#xff1a; https://www.bilibili.com/video/BV1xw411n7Tu/?share_sourcecopy_web&vd_source11344bb73ef9b33550b8202d07ae139b 技术&#xff1a;springootmysqlvuejsbootstrappoi制作word模板 主要功能&#xff1a;求职者可以注册发布简历&#xff0c;选择简…

案例018:基于微信小程序的实习记录系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

【python入门篇】函数(6)

这一节将详细介绍Python中函数的用法&#xff0c;包括函数的定义、调用、参数、返回值、作用域等。 函数的概述&#xff1a; Python函数是一种封装了特定任务的可重用代码块。通过将程序分解为更小、更具体的任务&#xff0c;函数提供了一种有效的方式来组织和管理代码&#xf…

保姆级连接FusionInsight MRS kerberos Hive

数新网络&#xff0c;让每个人享受数据的价值https://xie.infoq.cn/link?targethttps%3A%2F%2Fwww.datacyber.com%2F 概述 本文将介绍在华为云 FusionInsight MRS&#xff08;Managed Relational Service&#xff09;的Kerberos环境中&#xff0c;如何使用Java和DBeaver实现远…

threejs创建一个旋转的正方体【完整代码】

效果&#xff1a; 中文网three.js docs 1.搭建环境 安装three 首先我们需要新建一个项目 vue/react都可 这里以vue为演示 npm i three 找到一个新的页面 在页面script的地方导入three import * as THREE from "three" 或者自己逐个导入 import {PerspectiveC…

京东采销面对面,洞悉行业新趋势 京东3C数码生态大会在武汉圆满举行

为促进湖北省3C数码产业发展&#xff0c;本地企业降本增效、促进行业交流、充分发挥京东集团全链路生态服务能力&#xff0c;支持地方3C特色产业提质增量。2023年11月23日&#xff0c;由京东零售、京东物流主办&#xff0c;湖北省电子商务行业协会联合协办的“聚力共赢、携手共…

【Kotlin精简】第9章 Kotlin Flow

1 前言 上一章节我们学习了Kotlin的协程【Kotlin精简】第8章 协程&#xff0c;我们知道 协程实质是对线程切换的封装&#xff0c;能更加安全实现异步代码同步化&#xff0c;本质上协程、线程都是服务于并发场景下&#xff0c;其中协程是协作式任务&#xff0c;线程是抢占式任务…

保姆级 ARM64 CPU架构下安装部署Docker + rancher + K8S 说明文档

1 K8S 简介 K8S是Kubernetes的简称&#xff0c;是一个开源的容器编排平台&#xff0c;用于自动部署、扩展和管理“容器化&#xff08;containerized&#xff09;应用程序”的系统。它可以跨多个主机聚集在一起&#xff0c;控制和自动化应用的部署与更新。 K8S 架构 Kubernete…

从Redis反序列化UserDetails对象异常后中发现FastJson序列化的一些问题

最近在使用SpringSecurityJWT实现认证授权的时候&#xff0c;出现Redis在反序列化userDetails的异常。通过实践发现&#xff0c;使用不同的序列化方法和不同的fastJson版本&#xff0c;异常信息各不相同。所以特地记录了下来。 一、项目代码 先来看看我项目中redis相关配置信息…

视频号小店常见问题分享,让你少走弯路,少花冤枉钱!

我是电商珠珠 视频号团队自22年7月&#xff0c;就开始发展起了自己的电商平台-视频号小店。 关于视频号小店有很多人可能还不太了解&#xff0c;尤其是对于新手来说&#xff0c;并不知道是干什么的。 我踏足电商这个领域也已经五六年了&#xff0c;视频号小店也做了一年多了…

SpringBoot集成MapStruct

引入mapstruct依赖 <dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${org.mapstruct.version}</version> </dependency>配置maven-compiler-plugin <build><plugins>&…

VMware Workstation 17 虚拟机自启动失效 解决脚本

VMware Workstation17新增加了虚拟机自启配置 但是很奇怪在我的一台计算机上能够自启&#xff0c;在另一台计算机上就失效 编写脚本 以命令方式完成虚拟机开机自启 #虚拟机自启.batif "%1""hide" goto CmdBegin start mshta vbscript:createobject("w…

缓存组件状态,提升用户体验:探索 keep-alive 的神奇世界

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Day31| Leetcode 455. 分发饼干 Leetcode 376. 摆动序列 Leetcode 53. 最大子数组和

进入贪心了&#xff0c;我觉得本专题是最烧脑的专题 Leetcode 455. 分发饼干 题目链接 455 分发饼干 让大的饼干去满足需求量大的孩子即是本题的思路&#xff1a; class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {…

仿ChatGPT对话前端页面(内含源码)

仿ChatGPT对话前端页面&#xff08;内含源码&#xff09; 前言布局样式和Js部分关键点全部源码 前言 本文主要讲解如何做出类似ChatGPT的前端页面。具体我们的效果图是长这样&#xff0c;其中除了时间是动态的之外&#xff0c;其他都是假数据。接下来让我们从布局和样式的角度…

Android Tombstone 与Debuggerd 原理浅谈

一、前言 Android系统类问题主要有stability、performance、power、security。Android集成一个守护进程tombstoned是android平台的一个守护进程&#xff0c;它注册成3个socket服务端&#xff0c;客户端封装在crash_dump和debuggerd_client。 crash_dump用于跟踪定位C crash&am…

前端入门(三)Vue生命周期、组件技术、事件总线、

文章目录 Vue生命周期Vue 组件化编程 - .vue文件非单文件组件组件的注意点组件嵌套Vue实例对象和VueComponent实例对象Js对象原型与原型链Vue与VueComponent的重要内置关系 应用单文件组件构建 Vue脚手架 - vue.cli项目文件结构refpropsmixin插件scoped样式 Vue生命周期 1、bef…

MBA-论证有效性分析

论证有效性分析∶分析下述论证中存在的缺陷和漏洞&#xff0c;选择若干要点&#xff0c;写一篇 600 字左石的文章.对该论证的有效性进行分析和评论。&#xff08;论证有效性分析的一般要点是∶概念特别是核心概念的界定和使用是否准确并前后一致&#xff0c;有无各种明显的逻辑…