diff --git a/contrib/b_sql_plugin/doc/INSERT.MD b/contrib/b_sql_plugin/doc/INSERT.MD new file mode 100644 index 0000000000000000000000000000000000000000..ecb7b6595449d00dad90c527cc01d77e17131928 --- /dev/null +++ b/contrib/b_sql_plugin/doc/INSERT.MD @@ -0,0 +1,191 @@ +# INSERT + +## 功能描述 + +向表中添加一行或多行数据。 + +## 注意事项 + +- 只有拥有表INSERT权限的用户,才可以向表中插入数据。用户被授予insert any table权限,相当于用户对除系统模式之外的任何模式具有USAGE权限,并且拥有这些模式下表的INSERT权限 +- 如果使用RETURNING子句,用户必须要有该表的SELECT权限。 +- 如果使用ON DUPLICATE KEY UPDATE,用户必须要有该表的SELECT、UPDATE权限,唯一约束(主键或唯一索引)的SELECT权限。 +- 如果使用query子句插入来自查询里的数据行,用户还需要拥有在查询里使用的表的SELECT权限。 +- 当连接到TD兼容的数据库时,td\_compatible\_truncation参数设置为on时,将启用超长字符串自动截断功能,在后续的insert语句中(不包含外表的场景下),对目标表中char和varchar类型的列上插入超长字符串时,系统会自动按照目标表中相应列定义的最大长度对超长字符串进行截断。 + + >![](public_sys-resources/icon-note.gif) **说明:** + >如果向字符集为字节类型编码(SQL\_ASCII,LATIN1等)的数据库中插入多字节字符数据(如汉字等),且字符数据跨越截断位置,这种情况下,按照字节长度自动截断,自动截断后会在尾部产生非预期结果。如果用户有对于截断结果正确性的要求,建议用户采用UTF8等能够按照字符截断的输入字符集作为数据库的编码集。 + + +## 语法格式 + +``` +[ WITH [ RECURSIVE ] with_query [, ...] ] +INSERT [/*+ plan_hint */] INTO table_name [partition_clause] [ AS alias ] [ ( column_name [, ...] ) ] + { DEFAULT VALUES + | VALUES [{( { expression | DEFAULT } [, ...] ) }][, ...] + | query } + [ ON DUPLICATE KEY UPDATE { NOTHING | { column_name = { expression | DEFAULT } } [, ...] [ WHERE condition ] }] + [ RETURNING {* | {output_expression [ [ AS ] output_name ] }[, ...]} ]; +``` + +## 参数说明 + +- **WITH \[ RECURSIVE \] with\_query \[, ...\]** + + 用于声明一个或多个可以在主查询中通过名称引用的子查询,相当于临时表。 + + 如果声明了RECURSIVE,那么允许SELECT子查询通过名称引用它自己。 + + - 其中with\_query的详细格式为: + + ``` + with_query_name [ ( column_name [, ...] ) ] AS [ [ NOT ] MATERIALIZED ] + ( {select | values | insert | update | delete} ) + ``` + + – with\_query\_name指定子查询生成的结果集名称,在查询中可使用该名称访问子查询的结果集。 + + – column\_name指定子查询结果集中显示的列名。 + + – 每个子查询可以是SELECT,VALUES,INSERT,UPDATE或DELETE语句。 + + – 用户可以使用MATERIALIZED / NOT MATERIALIZED对CTE进行修饰。 + + - 如果声明为MATERIALIZED,WITH查询将被物化,生成一个子查询结果集的拷贝,在引用处直接查询该拷贝,因此WITH子查询无法和主干SELECT语句进行联合优化(如谓词下推、等价类传递等),对于此类场景可以使用NOT MATERIALIZED进行修饰,如果WITH查询语义上可以作为子查询内联执行,则可以进行上述优化。 + - 如果用户没有显示声明物化属性则遵守以下规则:如果CTE只在所属主干语句中被引用一次,且语义上支持内联执行,则会被改写为子查询内联执行,否则以CTE Scan的方式物化执行。 + + >![](public_sys-resources/icon-note.gif) **说明:** + >INSERT ON DUPLICATE KEY UPDATE不支持WITH及WITH RECURSIVE子句。 + + - **plan\_hint子句** + + 以/\*+ \*/的形式在INSERT关键字后,用于对INSERT对应的语句块生成的计划进行hint调优,详细用法请参见章节[使用Plan Hint进行调优](zh-cn_topic_0289900289.md)。每条语句中只有第一个/\*+ plan\_hint \*/注释块会作为hint生效,里面可以写多条hint。 + +- **table\_name** + + 要插入数据的目标表名。 + + 取值范围:已存在的表名。 + +- **partition\_clause** + + 指定分区插入操作 + + ``` + PARTITION { ( partition_name ) | FOR ( partition_value [, ...] ) } | + SUBPARTITION { ( subpartition_name ) | FOR ( subpartition_value [, ...] ) } + ``` + + 关键字详见[SELECT](SELECT.md)一节介绍 + + 如果value子句的值和指定分区不一致,会抛出异常。 + + 示例详见[CREATE TABLE SUBPARTITION](zh-cn_topic_0000001198046401.md) + +- **column\_name** + + 目标表中的字段名: + + - 字段名可以有子字段名或者数组下标修饰。 + - 没有在字段列表中出现的每个字段,将由系统默认值,或者声明时的默认值填充,若都没有则用NULL填充。例如,向一个复合类型中的某些字段插入数据的话,其他字段将是NULL。 + - 目标字段(column\_name)可以按顺序排列。如果没有列出任何字段,则默认全部字段,且顺序为表声明时的顺序。 + - 如果value子句和query中只提供了N个字段,则目标字段为前N个字段。 + - value子句和query提供的值在表中从左到右关联到对应列。 + + 取值范围:已存在的字段名。 + +- **expression** + + 赋予对应column的一个有效表达式或值: + + - 如果是INSERT ON DUPLICATE KEY UPDATE语句下,expression可以为VALUES\(column\_name\)或EXCLUDED.column\_name用来表示引用冲突行对应的column\_name字段的值。需注意,其中VALUES\(column\_name\)不支持嵌套在表达式中(例如VALUES\(column\_name\)+1),但EXCLUDED不受此限制。 + + - 向表中字段插入单引号 “ ' ”时需要使用单引号自身进行转义。 + - 如果插入行的表达式不是正确的数据类型,系统试图进行类型转换,若转换不成功,则插入数据失败,系统返回错误信息。 + +- **DEFAULT** + + 对应字段名的缺省值。如果没有缺省值,则为NULL。 + +- **VALUES()** + + 当GUC参数sql_mode为stric_all_tables时,为所有列插入NULL,否则如果对应字段名有缺省值,插入缺省值,如果没有缺省值,判断对应字段是否有not_null约束,有插入NULL,没有则插入类型基础值,具体基础值参考视图pg_type_basic_value。 + +- **query** + + 一个查询语句(SELECT语句),将查询结果作为插入的数据。 + +- **RETURNING** + + 返回实际插入的行,RETURNING列表的语法与SELECT的输出列表一致。注意:INSERT ON DUPLICATE KEY UPDATE不支持RETURNING子句。 + +- **output\_expression** + + INSERT命令在每一行都被插入之后用于计算输出结果的表达式。 + + 取值范围:该表达式可以使用table的任意字段。可以使用\*返回被插入行的所有字段。 + +- **output\_name** + + 字段的输出名称。 + + 取值范围:字符串,符合标识符命名规范。 + +- **ON DUPLICATE KEY UPDATE** + + 对于带有唯一约束(UNIQUE INDEX或PRIMARY KEY)的表,如果插入数据违反唯一约束,则对冲突行执行UPDATE子句完成更新,对于不带唯一约束的表,则仅执行插入。UPDATE时,若指定NOTHING则忽略此条插入,可通过“EXCLUDE.” 或者 "VALUES\(\)" 来选择源数据相应的列。 + + - 支持触发器,触发器执行顺序由实际执行流程决定: + - 执行insert:触发 before insert、after insert触发器。 + - 执行update:触发before insert、before update、after update触发器。 + - 执行update nothing:触发before insert触发器。 + + - 不支持延迟生效(DEFERRABLE)的唯一约束或主键。 + + - 如果表中存在多个唯一约束,如果所插入数据违反多个唯一约束,对于检测到冲突的第一行进行更新,其他冲突行不更新(检查顺序与索引维护具有强相关性,一般先创建的索引先进行冲突检查)。 + - 如果插入多行,这些行均与表中同一行数据存在唯一约束冲突,则按照顺序,第一条执行插入或更新,之后依次执行更新。 + + - 主键、唯一索引列不允许UPDATE。 + - 不支持列存,不支持外表、内存表。 + - expression支持使用子查询表达式,其语法与功能同UPDATE。子查询表达式中支持使用“EXCLUDED.”来选择源数据相应的列。 + + +## 示例 + +``` +--创建表tpcds.reason_t2。 +openGauss=# CREATE TABLE tpcds.reason_t2 +( + r_reason_sk integer, + r_reason_id character(16), + r_reason_desc character(100) +); + +--向表中插入一条记录。 +openGauss=# INSERT INTO tpcds.reason_t2(r_reason_sk, r_reason_id, r_reason_desc) VALUES (1, 'AAAAAAAABAAAAAAA', 'reason1'); + +--向表中插入一条记录,和上一条语法等效。 +openGauss=# INSERT INTO tpcds.reason_t2 VALUES (2, 'AAAAAAAABAAAAAAA', 'reason2'); + +--向表中插入多条记录。 +openGauss=# INSERT INTO tpcds.reason_t2 VALUES (3, 'AAAAAAAACAAAAAAA','reason3'),(4, 'AAAAAAAADAAAAAAA', 'reason4'),(5, 'AAAAAAAAEAAAAAAA','reason5'); + +--向表中插入tpcds.reason中r_reason_sk小于5的记录。 +openGauss=# INSERT INTO tpcds.reason_t2 SELECT * FROM tpcds.reason WHERE r_reason_sk <5; + +--对表创建唯一索引 +openGauss=# CREATE UNIQUE INDEX reason_t2_u_index ON tpcds.reason_t2(r_reason_sk); + +--向表中插入多条记录,如果冲突则更新冲突数据行中r_reason_id字段为'BBBBBBBBCAAAAAAA'。 +openGauss=# INSERT INTO tpcds.reason_t2 VALUES (5, 'BBBBBBBBCAAAAAAA','reason5'),(6, 'AAAAAAAADAAAAAAA', 'reason6') ON DUPLICATE KEY UPDATE r_reason_id = 'BBBBBBBBCAAAAAAA'; + +--删除表tpcds.reason_t2。 +openGauss=# DROP TABLE tpcds.reason_t2; +``` + +## 优化建议 + +- VALUES + + 通过insert语句批量插入数据时,建议将多条记录合并入一条语句中执行插入,以提高数据加载性能。例如,INSERT INTO sections VALUES \(30, 'Administration', 31, 1900\)、\(40, 'Development', 35, 2000\)、 \(50, 'Development' , 60 , 2001\)。 + diff --git a/contrib/b_sql_plugin/doc/pg_type_basic_value.md b/contrib/b_sql_plugin/doc/pg_type_basic_value.md new file mode 100644 index 0000000000000000000000000000000000000000..54decb2d169b1e1f9aa31d081f841947029d3a55 --- /dev/null +++ b/contrib/b_sql_plugin/doc/pg_type_basic_value.md @@ -0,0 +1,33 @@ +# pg\_type\_basic\_value + +pg\_type\_basic\_value视图存储类型的基础值,用于insert values()的插入。默认只有系统管理员权限才可以访问此系统表,普通用户需要授权才可以访问。 + +**表 1** pg\_type\_basic\_value字段 + + + + + + + + + + + + + + + + + +

名称

+

类型

+

描述

+

typename

+

text

+

类型名称

+

basic_value

+

text

+

类型基础值

+
+ diff --git a/contrib/b_sql_plugin/expected/empty_value_list.out b/contrib/b_sql_plugin/expected/empty_value_list.out new file mode 100644 index 0000000000000000000000000000000000000000..5cb1f356d308afc3c9e5e430c8a91345fd0942f1 --- /dev/null +++ b/contrib/b_sql_plugin/expected/empty_value_list.out @@ -0,0 +1,542 @@ +-- b compatibility case +drop database if exists b; +NOTICE: database "b" does not exist, skipping +create database b dbcompatibility 'b'; +\c b +create extension b_sql_plugin; +create table test1(num int); +create table test2(num int default 3); +create table test3(num int not null); +create table test4(num numeric not null); +create table test5(num bool not null); +create table test6(num bytea not null); +create table test7(num char not null); +create table test8(num name not null); +create table test9(num int8 not null); +create table test10(num text not null); +create table test11(num raw not null); +create table test12(num blob not null); +create table test13(num clob not null); +create table test14(num json not null); +create table test15(num xml not null); +create table test16(num point not null); +create table test17(num lseg not null); +create table test18(num path not null); +create table test19(num box not null); +create table test20(num polygon not null); +create table test21(num hash16 not null); +create table test22(num float4 not null); +create table test23(num abstime not null); +create table test24(num reltime not null); +create table test25(num tinterval not null); +create table test26(num circle not null); +create table test27(num money not null); +create table test28(num bpchar not null); +create table test29(num varchar not null); +create table test30(num nvarchar2 not null); +create table test31(num date not null); +create table test32(num time not null); +create table test33(num timestamp not null); +create table test34(num timestamptz not null); +create table test35(num interval not null); +create table test36(num timetz not null); +create table test37(num bit not null); +create table test38(num varbit not null); +create table test39(num numeric not null); +create table test40(num uuid not null); +create table test41(num smalldatetime not null); +--strict_all_tables mode +insert into test1 values(); +insert into test2 values(); +insert into test3 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test4 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test5 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test6 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test7 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test8 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test9 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test10 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test11 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test12 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test13 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test14 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test15 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test16 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test17 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test18 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test19 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test20 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test21 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test22 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test23 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test24 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test25 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test26 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test27 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test28 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test29 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test30 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test31 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test32 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test33 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test34 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test35 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test36 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test37 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test38 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test39 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test40 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +insert into test41 values(); +ERROR: null value in column "num" violates not-null constraint +DETAIL: Failing row contains (null). +--not strict_all_tables mode +set sql_mode = ''; +insert into test1 values(); +select *from test1; + num +----- + + +(2 rows) + +insert into test2 values(); +select *from test2; + num +----- + 3 + 3 +(2 rows) + +insert into test3 values(); +select *from test3; + num +----- + 0 +(1 row) + +insert into test4 values(); +select *from test4; + num +----- + 0 +(1 row) + +insert into test5 values(); +select *from test5; + num +----- + f +(1 row) + +insert into test6 values(); +select *from test6; + num +------------ + \x00000000 +(1 row) + +insert into test7 values(); +select *from test7; + num +----- + +(1 row) + +insert into test8 values(); +select *from test8; + num +----- + +(1 row) + +insert into test9 values(); +select *from test9; + num +----- + 0 +(1 row) + +insert into test10 values(); +select *from test10; + num +----- + +(1 row) + +insert into test11 values(); +select *from test11; + num +---------- + 00000000 +(1 row) + +insert into test12 values(); +select *from test12; + num +---------- + 00000000 +(1 row) + +insert into test13 values(); +select *from test13; + num +----- + +(1 row) + +insert into test14 values(); +select *from test14; + num +----- + +(1 row) + +insert into test15 values(); +select *from test15; + num +----- + +(1 row) + +insert into test16 values(); +select *from test16; + num +------- + (0,0) +(1 row) + +insert into test17 values(); +select *from test17; + num +--------------- + [(0,0),(0,0)] +(1 row) + +insert into test18 values(); +select *from test18; + num +--------- + ((0,0)) +(1 row) + +insert into test19 values(); +select *from test19; + num +------------- + (0,0),(0,0) +(1 row) + +insert into test20 values(); +select *from test20; + num +--------- + ((0,0)) +(1 row) + +insert into test21 values(); +select *from test21; + num +------------------ + 0000000000000000 +(1 row) + +insert into test22 values(); +select *from test22; + num +----- + 0 +(1 row) + +insert into test23 values(); +select *from test23; + num +------------------------------ + Wed Dec 31 16:00:00 1969 PST +(1 row) + +insert into test24 values(); +select *from test24; + num +----- + @ 0 +(1 row) + +insert into test25 values(); +select *from test25; + num +----------------------------------------------------------------- + ["Thu Jan 01 00:00:00 1970 PST" "Thu Jan 01 00:00:00 1970 PST"] +(1 row) + +insert into test26 values(); +select *from test26; + num +----------- + <(0,0),0> +(1 row) + +insert into test27 values(); +select *from test27; + num +-------- + ¥0.00 +(1 row) + +insert into test28 values(); +select *from test28; + num +----- + +(1 row) + +insert into test29 values(); +select *from test29; + num +----- + +(1 row) + +insert into test30 values(); +select *from test30; + num +----- + +(1 row) + +insert into test31 values(); +select *from test31; + num +------------ + 01-01-1970 +(1 row) + +insert into test32 values(); +select *from test32; + num +---------- + 00:00:00 +(1 row) + +insert into test33 values(); +select *from test33; + num +--------------------------------- +--?.* +(1 row) + +insert into test34 values(); +select *from test34; + num +------------------------------------- +--?.* +(1 row) + +insert into test35 values(); +select *from test35; + num +----- + @ 0 +(1 row) + +insert into test36 values(); +select *from test36; + num +------------- + 00:00:00-07 +(1 row) + +insert into test37 values(); +select *from test37; + num +----- + +(1 row) + +insert into test38 values(); +select *from test38; + num +----- + +(1 row) + +insert into test39 values(); +select *from test39; + num +----- + 0 +(1 row) + +insert into test40 values(); +select *from test40; + num +-------------------------------------- + 00000000-0000-0000-0000-000000000000 +(1 row) + +insert into test41 values(); +select *from test41; + num +-------------------------- + Thu Jan 01 08:00:00 1970 +(1 row) + +--multiple type test +create table m1(a int not null, b int, c int default 3, d int default 3 not null); +create table m2(a date not null, b timestamp, c timestamp default '2020-01-01', d timestamp default '2020-01-01' not null); +create table m3(a box not null, b path, c circle default '0,0,3', d box default '1,2,3,4' not null); +create table m4(a text not null, b char, c nvarchar2 default 'abc', d bpchar default 'bcd' not null); +insert into m1 values(); +select * from m1; + a | b | c | d +---+---+---+--- + 0 | | 3 | 3 +(1 row) + +insert into m2 values(); +select * from m2; + a | b | c | d +------------+---+--------------------------+-------------------------- + 01-01-1970 | | Wed Jan 01 00:00:00 2020 | Wed Jan 01 00:00:00 2020 +(1 row) + +insert into m3 values(); +select * from m3; + a | b | c | d +-------------+---+-----------+------------- + (0,0),(0,0) | | <(0,0),3> | (3,4),(1,2) +(1 row) + +insert into m4 values(); +select * from m4; + a | b | c | d +---+---+-----+----- + | | abc | bcd +(1 row) + +--pg_get_basic_value +select * from pg_get_basic_value('timestamp'); + pg_get_basic_value +-------------------- + now +(1 row) + +select * from pg_get_basic_value('uuid'); + pg_get_basic_value +-------------------------------------- + 00000000-0000-0000-0000-000000000000 +(1 row) + +--view +select * from pg_type_basic_value where typename = 'timestamp'; + typename | basic_value +-----------+------------- + timestamp | now +(1 row) + +select * from pg_type_basic_value where typename = 'uuid'; + typename | basic_value +----------+-------------------------------------- + uuid | 00000000-0000-0000-0000-000000000000 +(1 row) + +select * from pg_type_basic_value where typename = 'box'; + typename | basic_value +----------+------------- + box | (0,0),(0,0) +(1 row) + +select * from pg_type_basic_value where typename = 'int'; + typename | basic_value +----------+------------- +(0 rows) + +select * from pg_type_basic_value where typename = 'point'; + typename | basic_value +----------+------------- + point | (0,0) +(1 row) + +select * from pg_type_basic_value where typename = 'tinterval'; + typename | basic_value +-----------+----------------------------------------------------- + tinterval | ["1970-01-01 00:00:00+08" "1970-01-01 00:00:00+08"] +(1 row) + +\c postgres +drop database if exists b; diff --git a/contrib/b_sql_plugin/plugin_parser/analyze.cpp b/contrib/b_sql_plugin/plugin_parser/analyze.cpp index bd74a6d39734d53abb1619027654640f543753a7..318f7226b5aba137db3f27831a78f970608e58fc 100644 --- a/contrib/b_sql_plugin/plugin_parser/analyze.cpp +++ b/contrib/b_sql_plugin/plugin_parser/analyze.cpp @@ -94,6 +94,9 @@ #ifndef ENABLE_MULTIPLE_NODES #include "optimizer/clauses.h" #endif +#include "utils/date.h" +#include "utils/nabstime.h" +#include "utils/geo_decls.h" /* Hook for plugins to get control at end of parse analysis */ THR_LOCAL post_parse_analyze_hook_type post_parse_analyze_hook = NULL; static const int MILLISECONDS_PER_SECONDS = 1000; @@ -142,6 +145,10 @@ static const char* NOKEYUPDATE_KEYSHARE_ERRMSG = "/NO KEY UPDATE/KEY SHARE"; static const char* NOKEYUPDATE_KEYSHARE_ERRMSG = ""; #endif +static Node* makeConstByType(Form_pg_attribute att_tup); +static Node* makeTimetypeConst(Oid targetType, int32 targetTypmod, Oid targetCollation, int16 targetLen, bool targetByval); +static Node* makeNotTimetypeConst(Oid targetType, int32 targetTypmod, Oid targetCollation, int16 targetLen, bool targetByval); + /* * parse_analyze * Analyze a raw parse tree and transform it to Query form. @@ -1493,7 +1500,7 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) /* * Determine which variant of INSERT we have. */ - if (selectStmt == NULL) { + if (selectStmt == NULL && SQL_MODE_STRICT()) { /* * We have INSERT ... DEFAULT VALUES. We can handle this case by * emitting an empty targetlist --- all columns will be defaulted when @@ -1657,7 +1664,7 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) } } } - } else if (list_length(selectStmt->valuesLists) > 1) { + } else if (selectStmt && list_length(selectStmt->valuesLists) > 1) { /* * Process INSERT ... VALUES with multiple VALUES sublists. We * generate a VALUES RTE holding the transformed expression lists, and @@ -1784,6 +1791,43 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) * with no VALUES RTE. So it works just like SELECT without FROM. * ---------- */ + if (selectStmt == NULL) { + selectStmt = makeNode(SelectStmt); + Form_pg_attribute* attr = pstate->p_target_relation->rd_att->attrs; + auto numsRelationAttr = RelationGetNumberOfAttributes(pstate->p_target_relation); + for (int i = 0; i < numsRelationAttr; i++) { + SetToDefault* expr = NULL; + TupleDesc rd_att = pstate->p_target_relation->rd_att; + if (!attr[i]->attnotnull) { + expr = makeNode(SetToDefault); + selectStmt->valuesLists = lappend(selectStmt->valuesLists, expr); + continue; + } + /* + * Scan to see if relation has a default for this column. + */ + if (rd_att->constr && rd_att->constr->num_defval > 0) { + AttrDefault* defval = rd_att->constr->defval; + int ndef = rd_att->constr->num_defval; + + while (--ndef >= 0) { + if (defval[ndef].adnum == i + 1) { + /* + * Found it, make a SetToDefault node. + */ + expr = makeNode(SetToDefault); + selectStmt->valuesLists = lappend(selectStmt->valuesLists, expr); + break; + } + } + } + if (expr == NULL) { + selectStmt->valuesLists = lappend(selectStmt->valuesLists, makeConstByType(attr[i])); + } + } + selectStmt->valuesLists = list_make1(selectStmt->valuesLists); + } + List* valuesLists = selectStmt->valuesLists; AssertEreport(list_length(valuesLists) == 1, MOD_OPT, "list length is wrong"); @@ -1888,6 +1932,198 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) return qry; } +static Node* makeTimetypeConst(Oid targetType, int32 targetTypmod, Oid targetCollation, int16 targetLen, bool targetByval) +{ + Node* new_expr; + switch (targetType) { + case TIMESTAMPOID: + case TIMESTAMPTZOID: { + new_expr = (Node*)makeConst( + targetType, targetTypmod, targetCollation, targetLen, clock_timestamp(NULL), false, targetByval); + break; + } + case TIMETZOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall3( + timetz_in, CStringGetDatum("00:00:00"), ObjectIdGetDatum(0), Int32GetDatum(-1)), + false, + targetByval); + break; + } + case INTERVALOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall3( + interval_in, CStringGetDatum("00:00:00"), ObjectIdGetDatum(0), Int32GetDatum(-1)), + false, + targetByval); + break; + } + case TINTERVALOID: { + Datum epoch = (Datum)DirectFunctionCall1(timestamp_abstime, (TimestampGetDatum(SetEpochTimestamp()))); + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall2(mktinterval, epoch, epoch), + false, + targetByval); + break; + } + case SMALLDATETIMEOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall3( + smalldatetime_in, CStringGetDatum("1970-01-01 08:00:00"), ObjectIdGetDatum(0), Int32GetDatum(-1)), + false, + targetByval); + break; + } + default: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + timestamp2date(SetEpochTimestamp()), + false, + targetByval); + break; + } + } + return new_expr; +} + +static Node* makeNotTimetypeConst(Oid targetType, int32 targetTypmod, Oid targetCollation, int16 targetLen, bool targetByval) +{ + Node* new_expr; + switch (targetType) { + case NUMERICOID: { + new_expr = (Node*)makeConst(NUMERICOID, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall3( + numeric_in, CStringGetDatum("0"), ObjectIdGetDatum(0), Int32GetDatum(-1)), + false, + targetByval); + break; + } + case UUIDOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall3(uuid_in, + CStringGetDatum("00000000-0000-0000-0000-000000000000"), + ObjectIdGetDatum(0), + Int32GetDatum(-1)), + false, + targetByval); + break; + } + case NAMEOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + 0, + (Datum)DirectFunctionCall1(namein, CStringGetDatum("")), + false, + targetByval); + break; + } + case POINTOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall1(point_in, CStringGetDatum("(0,0)")), + false, + targetByval); + break; + } + case PATHOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall1(path_in, CStringGetDatum("0,0")), + false, + targetByval); + break; + } + case POLYGONOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall1(poly_in, CStringGetDatum("(0,0)")), + false, + targetByval); + break; + } + case CIRCLEOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall1(circle_in, CStringGetDatum("0,0,0")), + false, + targetByval); + break; + } + case LSEGOID: + case BOXOID: { + new_expr = (Node*)makeConst(targetType, + targetTypmod, + targetCollation, + targetLen, + (Datum)DirectFunctionCall1(box_in, CStringGetDatum("0,0,0,0")), + false, + targetByval); + break; + } + default: { + new_expr = + (Node*)makeConst(targetType, targetTypmod, targetCollation, 1, (Datum)0, false, targetByval); + break; + } + } + return new_expr; +} + +static Node* makeConstByType(Form_pg_attribute att_tup) +{ + Node* new_expr; + Oid targetType = att_tup->atttypid; + Oid targetCollation = att_tup->attcollation; + int16 targetLen = att_tup->attlen; + bool targetByval = att_tup->attbyval; + bool targetIsVarlena = (!targetByval) && (targetLen == -1) && targetType != PATHOID && targetType != POLYGONOID; + int32 targetTypmod; + bool targetIsTimetype = (targetType == DATEOID || targetType == TIMESTAMPOID || targetType == TIMESTAMPTZOID || + targetType == TIMETZOID || targetType == INTERVALOID || targetType == TINTERVALOID || + targetType == SMALLDATETIMEOID); + targetTypmod = targetIsTimetype ? -1 : att_tup->atttypmod; + if (!targetIsTimetype) { + if (targetIsVarlena) { + new_expr = (Node*)makeConst( + targetType, targetTypmod, targetCollation, 0, CStringGetDatum(" "), false, targetByval); + } else { + new_expr = makeNotTimetypeConst(targetType, targetTypmod, targetCollation, targetLen, targetByval); + } + } else { + new_expr = makeTimetypeConst(targetType, targetTypmod, targetCollation, targetLen, targetByval); + } + return new_expr; +} + static void checkUpsertTargetlist(Relation targetTable, List* updateTlist) { List* index_list = RelationGetIndexInfoList(targetTable); diff --git a/contrib/b_sql_plugin/plugin_parser/gram.y b/contrib/b_sql_plugin/plugin_parser/gram.y index 46c33134036130e977a25a6c87238cc2a7662877..14f40f36f26ae6ebb0b3c15e677d762a7dcfa52a 100644 --- a/contrib/b_sql_plugin/plugin_parser/gram.y +++ b/contrib/b_sql_plugin/plugin_parser/gram.y @@ -18605,6 +18605,14 @@ insert_rest: $$->selectStmt = NULL; $$->isRewritten = false; } + | VALUES '(' ')' + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->selectStmt = NULL; + $$->isRewritten = false; + } + ; insert_column_list: diff --git a/contrib/b_sql_plugin/sql/empty_value_list.sql b/contrib/b_sql_plugin/sql/empty_value_list.sql new file mode 100644 index 0000000000000000000000000000000000000000..25a6ca2a0f4f8452603984ba12942ac7305378ee --- /dev/null +++ b/contrib/b_sql_plugin/sql/empty_value_list.sql @@ -0,0 +1,205 @@ +-- b compatibility case +drop database if exists b; +create database b dbcompatibility 'b'; + +\c b +create extension b_sql_plugin; + +create table test1(num int); +create table test2(num int default 3); +create table test3(num int not null); +create table test4(num numeric not null); +create table test5(num bool not null); +create table test6(num bytea not null); +create table test7(num char not null); +create table test8(num name not null); +create table test9(num int8 not null); +create table test10(num text not null); +create table test11(num raw not null); +create table test12(num blob not null); +create table test13(num clob not null); +create table test14(num json not null); +create table test15(num xml not null); +create table test16(num point not null); +create table test17(num lseg not null); +create table test18(num path not null); +create table test19(num box not null); +create table test20(num polygon not null); +create table test21(num hash16 not null); +create table test22(num float4 not null); +create table test23(num abstime not null); +create table test24(num reltime not null); +create table test25(num tinterval not null); +create table test26(num circle not null); +create table test27(num money not null); +create table test28(num bpchar not null); +create table test29(num varchar not null); +create table test30(num nvarchar2 not null); +create table test31(num date not null); +create table test32(num time not null); +create table test33(num timestamp not null); +create table test34(num timestamptz not null); +create table test35(num interval not null); +create table test36(num timetz not null); +create table test37(num bit not null); +create table test38(num varbit not null); +create table test39(num numeric not null); +create table test40(num uuid not null); +create table test41(num smalldatetime not null); + +--strict_all_tables mode +insert into test1 values(); +insert into test2 values(); +insert into test3 values(); +insert into test4 values(); +insert into test5 values(); +insert into test6 values(); +insert into test7 values(); +insert into test8 values(); +insert into test9 values(); +insert into test10 values(); +insert into test11 values(); +insert into test12 values(); +insert into test13 values(); +insert into test14 values(); +insert into test15 values(); +insert into test16 values(); +insert into test17 values(); +insert into test18 values(); +insert into test19 values(); +insert into test20 values(); +insert into test21 values(); +insert into test22 values(); +insert into test23 values(); +insert into test24 values(); +insert into test25 values(); +insert into test26 values(); +insert into test27 values(); +insert into test28 values(); +insert into test29 values(); +insert into test30 values(); +insert into test31 values(); +insert into test32 values(); +insert into test33 values(); +insert into test34 values(); +insert into test35 values(); +insert into test36 values(); +insert into test37 values(); +insert into test38 values(); +insert into test39 values(); +insert into test40 values(); +insert into test41 values(); + +--not strict_all_tables mode +set sql_mode = ''; +insert into test1 values(); +select *from test1; +insert into test2 values(); +select *from test2; +insert into test3 values(); +select *from test3; +insert into test4 values(); +select *from test4; +insert into test5 values(); +select *from test5; +insert into test6 values(); +select *from test6; +insert into test7 values(); +select *from test7; +insert into test8 values(); +select *from test8; +insert into test9 values(); +select *from test9; +insert into test10 values(); +select *from test10; +insert into test11 values(); +select *from test11; +insert into test12 values(); +select *from test12; +insert into test13 values(); +select *from test13; +insert into test14 values(); +select *from test14; +insert into test15 values(); +select *from test15; +insert into test16 values(); +select *from test16; +insert into test17 values(); +select *from test17; +insert into test18 values(); +select *from test18; +insert into test19 values(); +select *from test19; +insert into test20 values(); +select *from test20; +insert into test21 values(); +select *from test21; +insert into test22 values(); +select *from test22; +insert into test23 values(); +select *from test23; +insert into test24 values(); +select *from test24; +insert into test25 values(); +select *from test25; +insert into test26 values(); +select *from test26; +insert into test27 values(); +select *from test27; +insert into test28 values(); +select *from test28; +insert into test29 values(); +select *from test29; +insert into test30 values(); +select *from test30; +insert into test31 values(); +select *from test31; +insert into test32 values(); +select *from test32; +insert into test33 values(); +select *from test33; +insert into test34 values(); +select *from test34; +insert into test35 values(); +select *from test35; +insert into test36 values(); +select *from test36; +insert into test37 values(); +select *from test37; +insert into test38 values(); +select *from test38; +insert into test39 values(); +select *from test39; +insert into test40 values(); +select *from test40; +insert into test41 values(); +select *from test41; + +--multiple type test +create table m1(a int not null, b int, c int default 3, d int default 3 not null); +create table m2(a date not null, b timestamp, c timestamp default '2020-01-01', d timestamp default '2020-01-01' not null); +create table m3(a box not null, b path, c circle default '0,0,3', d box default '1,2,3,4' not null); +create table m4(a text not null, b char, c nvarchar2 default 'abc', d bpchar default 'bcd' not null); +insert into m1 values(); +select * from m1; +insert into m2 values(); +select * from m2; +insert into m3 values(); +select * from m3; +insert into m4 values(); +select * from m4; + +--pg_get_basic_value +select * from pg_get_basic_value('timestamp'); +select * from pg_get_basic_value('uuid'); + +--view +select * from pg_type_basic_value where typename = 'timestamp'; +select * from pg_type_basic_value where typename = 'uuid'; +select * from pg_type_basic_value where typename = 'box'; +select * from pg_type_basic_value where typename = 'int'; +select * from pg_type_basic_value where typename = 'point'; +select * from pg_type_basic_value where typename = 'tinterval'; + +\c postgres +drop database if exists b; \ No newline at end of file diff --git a/contrib/b_sql_plugin/sql_script/empty_values.sql b/contrib/b_sql_plugin/sql_script/empty_values.sql new file mode 100644 index 0000000000000000000000000000000000000000..e04415741ded51443b01b566174595db19dbe4fe --- /dev/null +++ b/contrib/b_sql_plugin/sql_script/empty_values.sql @@ -0,0 +1,44 @@ +CREATE OR REPLACE FUNCTION pg_catalog.pg_get_basic_value(typename text) +RETURNS text +AS +$$ +BEGIN + IF typename = 'timestamp' then + return 'now'; + elsif typename = 'time' or typename = 'timetz' or typename = 'interval' or typename = 'reltime' then + return '00:00:00'; + elsif typename = 'date' then + return '1970-01-01'; + elsif typename = 'smalldatetime' then + return '1970-01-01 08:00:00'; + elsif typename = 'abstime' then + return '1970-01-01 08:00:00+08'; + elsif typename = 'uuid' then + return '00000000-0000-0000-0000-000000000000'; + elsif typename = 'bool' then + return 'false'; + elsif typename = 'point' or typename = 'polygon' then + return '(0,0)'; + elsif typename = 'path' then + return '((0,0))'; + elsif typename = 'circle' then + return '(0,0),0'; + elsif typename = 'lseg' or typename = 'box' then + return '(0,0),(0,0)'; + elsif typename = 'tinterval' then + return '["1970-01-01 00:00:00+08" "1970-01-01 00:00:00+08"]'; + else + return '0 or empty'; + end if; +end; +$$ +LANGUAGE plpgsql; + +CREATE VIEW public.pg_type_basic_value AS + SELECT + t.typname As typename, + pg_get_basic_value(t.typname) As basic_value + + FROM pg_type t; +REVOKE ALL ON public.pg_type_basic_value FROM PUBLIC; +GRANT SELECT, REFERENCES ON public.pg_type_basic_value TO PUBLIC; \ No newline at end of file