Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wN1NY-000WGH-1o for pgsql-hackers@arkaria.postgresql.org; Wed, 13 May 2026 04:39:28 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wN1NX-007GBt-00 for pgsql-hackers@arkaria.postgresql.org; Wed, 13 May 2026 04:39:27 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wN1NW-007GBh-1W for pgsql-hackers@lists.postgresql.org; Wed, 13 May 2026 04:39:26 +0000 Received: from mail-pg1-x535.google.com ([2607:f8b0:4864:20::535]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wN1NS-00000000JWN-3opv for pgsql-hackers@postgresql.org; Wed, 13 May 2026 04:39:25 +0000 Received: by mail-pg1-x535.google.com with SMTP id 41be03b00d2f7-c80227b1f6cso2672687a12.1 for ; Tue, 12 May 2026 21:39:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778647162; x=1779251962; darn=postgresql.org; h=to:cc:date:message-id:subject:mime-version:from:from:to:cc:subject :date:message-id:reply-to; bh=tWNF04VDSAjclQjp5OS3Tob10BSOcpwTWZmRt6Icdvk=; b=ewoCDCGj7C9fgVSBz35VOy8/PR5ow571EwRZYyoUxQ2Ut6SgdBP0c0T+vSVObDth9f S7EQ43HAC/5aWk1ha5bOXRjzFoEyKKG/t2dEg7PhfTbohnPXeegiWnmpaUAtkLivPxbB AdPHn5BGfA6+GEsHmcSC5dL1unGMW1NIDSm2C+upHwxrgQx57WIRWi/+7Qn6SSg21bug JgFM/m4lqnrN/w+zW7I8sW33iVoslJrCki3o04DY8DH8O0tkFUj3JLtN5nUkhMk7GVlh vLSnGX7+uhlXvfHd5N/4cZApZEfc7pJJIPBofpefZsJpcOnPT/iNPysVgUNj1m9ffL7C IGKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778647162; x=1779251962; h=to:cc:date:message-id:subject:mime-version:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=tWNF04VDSAjclQjp5OS3Tob10BSOcpwTWZmRt6Icdvk=; b=Vpnd5GSOUeDX6rhyQF8UdE9rAG4tBpnt9YqS5ntpyIyQEtR9AkNAFc3gFqJabtK39v 2roQXM7Kpjhe1lD6Y8aDjIUViibzzATJMHc5Xa+0hnbFMX3Y3mRxTedi1kk7JMo67y12 zhrmsVmJdrezE9gHKWEWZ/MS6ZO9w5ymtAL8zPRW+v2fvxF5sXUHfTIBtWXHLl+iIgmx rDhCtCgNJxWSYUJQW/TEU9GhjUUVD+dYQUY3zbVOHYxnTHzcnvG8mFV6sY0ZaIRbyHrJ qXhQK1nJFbbMFhi0+U1h9xlUjQz0l5pFiifH6fBcaofRMuYZKjS0TNZA8IpRRP2m8LLW MYew== X-Gm-Message-State: AOJu0YwOGnybuVXLqZa3h92H6nw7UU8kq8+mgFoLLe7CR0ebambTa0Ga K7hWegXqsuASUARIfWVJrp2MRIddiyRb3nHjQjMdi2jHCPdNtNh0k66b3qgR4VX4 X-Gm-Gg: Acq92OGnAMcRvYsOYdXEK3wabwqFV1RXN9Ke71y2wKuIkIjAgGtsn/lzgHLWqN1L3wW 96RuM56grSe5y6PBnpUps0x/uX4+R47dk3kJvRgat5YqQpHRipb1k9Qy87NB68v9pQSDDzFTcn7 A5+3nESTBkzrL1v9G69I+ge22ZwQE8YHBsqMRuX/Md7nXIIWjr+bs/pudUEbfM0ht7Ri0N7grIj yGxFfkyokIuOwJk1S5TWGOjFk67uY33CiveYNbcSwjtvty6rcb3MZ02i7msuhAsxRCuac+qmlpN GBNp1ORIEz2ZY6YUkrOJgI5HDBHUG3e9i+lapvPT0Je3CMadcSmMwu2o37HOFtvWT7KbQ51vnjV J6HJrCY0jXveQq8J5jHDLAYDfkIJLQq+rzs7v+ZRexlB1qr8Lxw44o9EvhSsWx0ZOC/N/Jw5ON0 BOJRSG041J4voo4XRzWmi3/winEdqGoMw= X-Received: by 2002:a17:903:3d42:b0:2b2:b117:1d5d with SMTP id d9443c01a7336-2bd275a216cmr14106935ad.33.1778647161804; Tue, 12 May 2026 21:39:21 -0700 (PDT) Received: from smtpclient.apple ([45.32.121.103]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1d2700dsm155277265ad.2.2026.05.12.21.39.19 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 12 May 2026 21:39:20 -0700 (PDT) From: Chao Li Content-Type: multipart/mixed; boundary="Apple-Mail=_81619F64-E55B-466D-A37B-6FCB85FD7AFB" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3864.400.21\)) Subject: Fix SPLIT PARTITION bound-overlap bug and other improvements Message-Id: Date: Wed, 13 May 2026 12:38:44 +0800 Cc: Dmitry Koval , Alexander Korotkov To: PostgreSQL-development X-Mailer: Apple Mail (2.3864.400.21) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_81619F64-E55B-466D-A37B-6FCB85FD7AFB Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Hi, While testing ALTER TABLE ... SPLIT PARTITION, I found a bug and a few = behaviors and messages that seem worth improving. 0. A bound-overlap bug I numbered this item as 0 because I found it after finishing items 1, 2, = and 3. While doing a final verification before sending this email, I was = surprised to find that the partitioned table ended up with two = overlapping partitions. Here is a simple repro: ``` evantest=3D# drop table t; DROP TABLE evantest=3D# CREATE TABLE t (i int) PARTITION BY RANGE(i); CREATE TABLE evantest=3D# CREATE TABLE p0a PARTITION OF t FOR VALUES FROM (0) TO = (51); CREATE TABLE evantest=3D# CREATE TABLE p0b PARTITION OF t FOR VALUES FROM (51) TO = (100); CREATE TABLE evantest=3D# \d+ t; Partitioned table "public.t" Column | Type | Collation | Nullable | Default | Storage | = Compression | Stats target | Description = --------+---------+-----------+----------+---------+---------+------------= -+--------------+------------- i | integer | | | | plain | = | | Partition key: RANGE (i) Partitions: p0a FOR VALUES FROM (0) TO (51) p0b FOR VALUES FROM (51) TO (100) evantest=3D# ALTER TABLE t SPLIT PARTITION p0a INTO evantest-# (PARTITION p0a FOR VALUES FROM (0) TO (53), evantest(# PARTITION pdef DEFAULT); ALTER TABLE evantest=3D# evantest=3D# evantest=3D# \d+ t; Partitioned table "public.t" Column | Type | Collation | Nullable | Default | Storage | = Compression | Stats target | Description = --------+---------+-----------+----------+---------+---------+------------= -+--------------+------------- i | integer | | | | plain | = | | Partition key: RANGE (i) Partitions: p0a FOR VALUES FROM (0) TO (53) p0b FOR VALUES FROM (51) TO (100) pdef DEFAULT ``` As shown above, p0a and p0b now overlap. I think this is a real bug. The problem seems to be in check_partition_bounds_for_split_range(). If = the only non-default replacement partition is both first and last, the = function checks only the lower bound because it uses if (first) ... else = .... It never checks the upper bound in that case. 1. The documentation about splitting with a DEFAULT partition is a bit = unclear Since SPLIT PARTITION allows one of the new partitions to be specified = as DEFAULT, I wondered whether that new DEFAULT partition means the = remaining part of the split partition's bound, or the partitioned = table's global DEFAULT partition. The current documentation says: ``` This form splits a single partition of the target table into new partitions. Hash-partitioned target table is not supported. Only a simple, non-partitioned partition can be split. If the split partition is the DEFAULT = partition, one of the new partitions must be DEFAULT. If the partitioned table does not have a = DEFAULT partition, a DEFAULT partition can be defined = as one of the new partitions. The bounds of new partitions should not overlap with those of new = or existing partitions (except partition_name). The combined bounds of new partitions partition_name1, partition_name2[, = ...] should be equal to the bounds of the split partition partition_name. One of the new partitions can have the same name as the split = partition partition_name (this is suitable in case of splitting the = DEFAULT partition: after the split, the DEFAULT = partition remains with the same name, but its partition bound changes). ``` =46rom the first paragraph, it seems that the new DEFAULT partition is a = table-level default partition. However, the second paragraph says that = the combined bounds of the new partitions should be equal to the bounds = of the split partition, which can make it look as if the new DEFAULT = partition only covers the remaining part of the split partition's bound. My tests show that the new DEFAULT partition is the partitioned table's = global DEFAULT partition. So I think the documentation can be improved = to make that clearer. 2. I found this hint message confusing: ``` evantest=3D# ALTER TABLE t SPLIT PARTITION p0a INTO (PARTITION p0a1 FOR = VALUES FROM (0) TO (5), PARTITION p0a2 FOR VALUES FROM (6) to (51), = PARTITION pdef DEFAULT); ERROR: upper bound of partition "p0a2" is greater than upper bound of = split partition "p0a" LINE 1: ...0) TO (5), PARTITION p0a2 FOR VALUES FROM (6) to (51), = PARTI... ^ HINT: ALTER TABLE ... SPLIT PARTITION require combined bounds of new = partitions must exactly match the bound of the split partition. ``` In this command, one of the new explicit partition bounds exceeds the = original partition bound, but the command also specifies a DEFAULT = partition. In this case, the combined explicit bounds do not need to = exactly match the original partition bound, they only need to stay = within it. So the hint is not quite accurate for this case. 3. SPLIT PARTITION currently provides another way to add a DEFAULT = partition: ``` evantest=3D# \d+ t; Partitioned table "public.t" Column | Type | Collation | Nullable | Default | Storage | = Compression | Stats target | Description = --------+---------+-----------+----------+---------+---------+------------= -+--------------+------------- i | integer | | | | plain | = | | Partition key: RANGE (i) Partitions: p0a FOR VALUES FROM (0) TO (50) p0b FOR VALUES FROM (51) TO (100) p200 FOR VALUES FROM (200) TO (300) evantest=3D# ALTER TABLE t SPLIT PARTITION p0a INTO (PARTITION p0a FOR = VALUES FROM (0) TO (50), PARTITION pdef DEFAULT); ALTER TABLE ``` Here, the new p0a partition has the same bound as the original p0a = partition, so no real split happens. The command effectively only adds a = new DEFAULT partition. However, it still goes through the full = split-partition path: creating a new partition, moving data, attaching = the new partition, and dropping the old partition. Initially, I considered adding a fast path for this case so that it = would only add the new DEFAULT partition. But after thinking about it = more, I think it is better to reject this degenerate form instead. We = already has direct ways to add a DEFAULT partition: ``` CREATE TABLE p PARTITION OF t DEFAULT; or CREATE TABLE p; ALTER TABLE t ATTACH PARTITION p DEFAULT; ``` So I do not think SPLIT PARTITION needs to become another syntax for = adding a DEFAULT partition when no actual split is performed. Accepting = this form could also raise another question later: if this is allowed, = why does the user have to repeat the original bound at all? Why not = allow something like this? ``` ALTER TABLE t SPLIT PARTITION p0a INTO (PARTITION pdef DEFAULT); ``` That seems like an awkward direction. The attached patch tries to address all these issues. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ --Apple-Mail=_81619F64-E55B-466D-A37B-6FCB85FD7AFB Content-Disposition: attachment; filename=v1-0001-Fix-SPLIT-PARTITION-validation-with-DEFAULT.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v1-0001-Fix-SPLIT-PARTITION-validation-with-DEFAULT.patch" Content-Transfer-Encoding: quoted-printable =46rom=204fc92276d96195c38aec4477bd14c00a514173df=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Wed,=2013=20May=202026=2011:17:19=20+0800=0ASubject:=20[PATCH=20= v1]=20Fix=20SPLIT=20PARTITION=20validation=20with=20DEFAULT=0A=0AWhen=20= ALTER=20TABLE=20...=20SPLIT=20PARTITION=20specified=20a=20DEFAULT=20= partition,=20range=0Abound=20validation=20checked=20the=20lower=20bound=20= for=20the=20first=20explicit=20partition=0Aand=20the=20upper=20bound=20= for=20non-first=20explicit=20partitions.=20=20As=20a=20result,=20when=0A= there=20was=20only=20one=20explicit=20non-DEFAULT=20partition,=20its=20= upper=20bound=20was=20not=0Achecked.=20=20This=20could=20allow=20the=20= new=20partition=20to=20extend=20beyond=20the=20split=0Apartition's=20= bound=20and=20overlap=20another=20existing=20partition.=0A=0AFix=20this=20= by=20checking=20the=20upper=20bound=20whenever=20the=20explicit=20= partition=20is=0Athe=20last=20one,=20rather=20than=20only=20when=20it=20= is=20not=20the=20first=20one.=0A=0AWhile=20here,=20reject=20the=20= degenerate=20form=20where=20a=20non-DEFAULT=20partition=20is=0Asplit=20= into=20one=20non-DEFAULT=20partition=20with=20exactly=20the=20same=20= bound=20plus=20a=0ADEFAULT=20partition.=20=20That=20form=20performs=20no=20= real=20split=20and=20merely=20adds=20a=0ADEFAULT=20partition=20through=20= the=20split-partition=20path,=20for=20which=20existing=0Acommands=20= should=20be=20used=20instead.=0A=0AAlso=20clarify=20the=20documentation=20= for=20SPLIT=20PARTITION=20with=20DEFAULT,=20and=0Aadjust=20the=20hint=20= for=20explicit=20bounds=20that=20extend=20outside=20the=20split=0A= partition=20when=20a=20DEFAULT=20partition=20is=20specified.=0A=0A= Author:=20Chao=20Li=20=0A---=0A=20= doc/src/sgml/ref/alter_table.sgml=20=20=20=20=20=20=20=20=20=20=20=20=20= |=2031=20++++----=0A=20src/backend/partitioning/partbounds.c=20=20=20=20=20= =20=20=20=20|=2078=20+++++++++++++++++--=0A=20= src/test/regress/expected/partition_split.out=20|=2043=20++++++++++=0A=20= src/test/regress/sql/partition_split.sql=20=20=20=20=20=20|=2037=20= +++++++++=0A=204=20files=20changed,=20171=20insertions(+),=2018=20= deletions(-)=0A=0Adiff=20--git=20a/doc/src/sgml/ref/alter_table.sgml=20= b/doc/src/sgml/ref/alter_table.sgml=0Aindex=201f9a456fd33..97a6d331776=20= 100644=0A---=20a/doc/src/sgml/ref/alter_table.sgml=0A+++=20= b/doc/src/sgml/ref/alter_table.sgml=0A@@=20-1293,28=20+1293,33=20@@=20= WITH=20(=20MODULUS=20numeric_literal,=20REM=0A=20=20=20=20=20= =0A=20=20=20=20=20=20=0A=20=20=20=20=20=20=20This=20form=20= splits=20a=20single=20partition=20of=20the=20target=20table=20into=20new=0A= -=20=20=20=20=20=20partitions.=20Hash-partitioned=20target=20table=20is=20= not=20supported.=0A-=20=20=20=20=20=20Only=20a=20simple,=20= non-partitioned=20partition=20can=20be=20split.=0A-=20=20=20=20=20=20If=20= the=20split=20partition=20is=20the=20DEFAULT=20= partition,=0A-=20=20=20=20=20=20one=20of=20the=20new=20partitions=20must=20= be=20DEFAULT.=0A-=20=20=20=20=20=20If=20the=20= partitioned=20table=20does=20not=20have=20a=20DEFAULT=0A= +=20=20=20=20=20=20partitions.=20=20Hash-partitioned=20target=20table=20= is=20not=20supported.=0A+=20=20=20=20=20=20Only=20a=20simple,=20= non-partitioned=20partition=20can=20be=20split.=20=20If=20the=0A+=20=20=20= =20=20=20split=20partition=20is=20the=20DEFAULT=20= partition,=20one=0A+=20=20=20=20=20=20of=20the=20new=20partitions=20must=20= be=20DEFAULT.=20=20If=20the=0A+=20=20=20=20=20=20= partitioned=20table=20does=20not=20have=20a=20DEFAULT=0A= =20=20=20=20=20=20=20partition,=20a=20DEFAULT=20= partition=20can=20be=20defined=20as=20one=0A=20=20=20=20=20=20=20of=20= the=20new=20partitions.=0A=20=20=20=20=20=20=0A=20=0A=20=20=20=20=20= =20=0A-=20=20=20=20=20=20The=20bounds=20of=20new=20partitions=20= should=20not=20overlap=20with=20those=20of=20new=20or=0A-=20=20=20=20=20=20= existing=20partitions=20(except=20partition_name).=0A-=20=20=20=20=20=20= The=20combined=20bounds=20of=20new=20partitions=20=0A+=20=20=20=20= =20=20The=20bounds=20of=20new=20non-DEFAULT=20= partitions=20must=20not=0A+=20=20=20=20=20=20overlap=20with=20those=20of=20= new=20or=20existing=20partitions,=20except=0A+=20=20=20=20=20=20= partition_name,=20and=0A= +=20=20=20=20=20=20must=20not=20extend=20outside=20the=20bounds=20of=20= the=20split=20partition=0A+=20=20=20=20=20=20partition_name.=0A+=20=20=20=20=20=20= If=20no=20new=20DEFAULT=20partition=20is=20specified,=20= the=0A+=20=20=20=20=20=20combined=20bounds=20of=20the=20new=20partitions=0A= +=20=20=20=20=20=20=0A=20=20=20=20=20=20=20partition_name1,=0A=20=20=20=20=20=20=20= partition_name2[,=20= ...]=0A-=20=20=20=20=20=20=20should=20be=20equal=20to=20the=20= bounds=20of=20the=20split=20partition=0A+=20=20=20=20=20=20=20= must=20exactly=20match=20the=20bounds=20of=20the=20split=20partition=0A=20= =20=20=20=20=20=20partition_name.=0A=20=20=20=20=20=20=20= One=20of=20the=20new=20partitions=20can=20have=20the=20same=20name=20as=20= the=20split=20partition=0A-=20=20=20=20=20=20partition_name=0A-=20=20=20=20=20=20= (this=20is=20suitable=20in=20case=20of=20splitting=20the=20= DEFAULT=0A-=20=20=20=20=20=20partition:=20after=20the=20= split,=20the=20DEFAULT=20partition=0A-=20=20=20=20=20=20= remains=20with=20the=20same=20name,=20but=20its=20partition=20bound=20= changes).=0A+=20=20=20=20=20=20partition_name.=0A+=20=20=20=20=20=20= This=20is=20useful=20when=20splitting=20the=20DEFAULT=20= partition,=0A+=20=20=20=20=20=20so=20that=20after=20the=20split,=20the=20= DEFAULT=20partition=0A+=20=20=20=20=20=20keeps=20the=20= same=20name=20but=20its=20partition=20bound=20changes.=0A=20=20=20=20=20=20= =0A=20=0A=20=20=20=20=20=20=0Adiff=20--git=20= a/src/backend/partitioning/partbounds.c=20= b/src/backend/partitioning/partbounds.c=0Aindex=20= 9b4277a4987..6ca6d8b719f=20100644=0A---=20= a/src/backend/partitioning/partbounds.c=0A+++=20= b/src/backend/partitioning/partbounds.c=0A@@=20-36,6=20+36,7=20@@=0A=20= #include=20"utils/datum.h"=0A=20#include=20"utils/fmgroids.h"=0A=20= #include=20"utils/lsyscache.h"=0A+#include=20"utils/memutils.h"=0A=20= #include=20"utils/partcache.h"=0A=20#include=20"utils/ruleutils.h"=0A=20= #include=20"utils/snapmgr.h"=0A@@=20-5415,11=20+5416,11=20@@=20= check_partition_bounds_for_split_range(Relation=20parent,=0A=20=09=09=09=09= =09=09errmsg("lower=20bound=20of=20partition=20\"%s\"=20is=20less=20than=20= lower=20bound=20of=20split=20partition=20\"%s\"",=0A=20=09=09=09=09=09=09= =09=20=20=20relname,=0A=20=09=09=09=09=09=09=09=20=20=20= get_rel_name(splitPartOid)),=0A-=09=09=09=09=09=09errhint("%s=20require=20= combined=20bounds=20of=20new=20partitions=20must=20exactly=20match=20the=20= bound=20of=20the=20split=20partition.",=0A-=09=09=09=09=09=09=09=09= "ALTER=20TABLE=20...=20SPLIT=20PARTITION"),=0A+=09=09=09=09=09=09= errhint("Explicit=20partition=20bounds=20must=20be=20contained=20within=20= the=20bounds=20of=20the=20split=20partition=20when=20a=20DEFAULT=20= partition=20is=20specified."),=0A=20=09=09=09=09=09=09= parser_errposition(pstate,=20exprLocation((Node=20*)=20datum)));=0A=20=09= =09}=0A-=09=09else=0A+=0A+=09=09if=20(last)=0A=20=09=09{=0A=20=09=09=09= PartitionRangeBound=20*split_upper;=0A=20=0A@@=20-5457,8=20+5458,7=20@@=20= check_partition_bounds_for_split_range(Relation=20parent,=0A=20=09=09=09=09= =09=09errmsg("upper=20bound=20of=20partition=20\"%s\"=20is=20greater=20= than=20upper=20bound=20of=20split=20partition=20\"%s\"",=0A=20=09=09=09=09= =09=09=09=20=20=20relname,=0A=20=09=09=09=09=09=09=09=20=20=20= get_rel_name(splitPartOid)),=0A-=09=09=09=09=09=09errhint("%s=20require=20= combined=20bounds=20of=20new=20partitions=20must=20exactly=20match=20the=20= bound=20of=20the=20split=20partition.",=0A-=09=09=09=09=09=09=09=09= "ALTER=20TABLE=20...=20SPLIT=20PARTITION"),=0A+=09=09=09=09=09=09= errhint("Explicit=20partition=20bounds=20must=20be=20contained=20within=20= the=20bounds=20of=20the=20split=20partition=20when=20a=20DEFAULT=20= partition=20is=20specified."),=0A=20=09=09=09=09=09=09= parser_errposition(pstate,=20exprLocation((Node=20*)=20datum)));=0A=20=09= =09}=0A=20=09}=0A@@=20-5701,6=20+5701,70=20@@=20= check_parent_values_in_new_partitions(Relation=20parent,=0A=20=09}=0A=20= }=0A=20=0A+/*=0A+=20*=20check_split_partition_not_same_bound=0A+=20*=0A+=20= *=20Reject=20splitting=20a=20non-DEFAULT=20partition=20into=20one=20= non-DEFAULT=20partition=0A+=20*=20with=20the=20original=20bound=20plus=20= a=20DEFAULT=20partition.=20=20That=20form=20does=20not=0A+=20*=20perform=20= a=20real=20split;=20it=20merely=20adds=20a=20DEFAULT=20partition=20to=20= the=20parent=0A+=20*=20table=20through=20the=20split-partition=20path.=20= =20Users=20should=20use=0A+=20*=20CREATE=20TABLE=20...=20PARTITION=20OF=20= ...=20DEFAULT=20or=20ALTER=20TABLE=20...=20ATTACH=0A+=20*=20PARTITION=20= ...=20DEFAULT=20for=20that.=0A+=20*/=0A+static=20void=0A= +check_split_partition_not_same_bound(Relation=20parent,=0A+=09=09=09=09=09= =09=09=09=09=20Oid=20splitPartOid,=0A+=09=09=09=09=09=09=09=09=09=20= SinglePartitionSpec=20**parts,=0A+=09=09=09=09=09=09=09=09=09=20int=20= nparts,=0A+=09=09=09=09=09=09=09=09=09=20ParseState=20*pstate)=0A+{=0A+=09= PartitionKey=20key=20=3D=20RelationGetPartitionKey(parent);=0A+=09= PartitionBoundSpec=20*split_spec;=0A+=09PartitionBoundSpec=20= *new_specs[1];=0A+=09PartitionBoundSpec=20*old_specs[1];=0A+=09= PartitionBoundInfo=20new_boundinfo;=0A+=09PartitionBoundInfo=20= old_boundinfo;=0A+=09int=09=09=20=20=20*new_mapping;=0A+=09int=09=09=20=20= =20*old_mapping;=0A+=09MemoryContext=20old_cxt;=0A+=09MemoryContext=20= tmp_cxt;=0A+=09bool=09=09same_bound;=0A+=0A+=09if=20(nparts=20!=3D=201)=0A= +=09=09return;=0A+=0A+=09tmp_cxt=20=3D=20= AllocSetContextCreate(CurrentMemoryContext,=0A+=09=09=09=09=09=09=09=09=09= "split=20partition=20bound=20comparison",=0A+=09=09=09=09=09=09=09=09=09= ALLOCSET_SMALL_SIZES);=0A+=09old_cxt=20=3D=20= MemoryContextSwitchTo(tmp_cxt);=0A+=0A+=09split_spec=20=3D=20= get_partition_bound_spec(splitPartOid);=0A+=0A+=09new_specs[0]=20=3D=20= parts[0]->bound;=0A+=09new_boundinfo=20=3D=20= partition_bounds_create(new_specs,=201,=20key,=20&new_mapping);=0A+=0A+=09= old_specs[0]=20=3D=20split_spec;=0A+=09old_boundinfo=20=3D=20= partition_bounds_create(old_specs,=201,=20key,=20&old_mapping);=0A+=0A+=09= same_bound=20=3D=20partition_bounds_equal(key->partnatts,=20= key->parttyplen,=0A+=09=09=09=09=09=09=09=09=09=09key->parttypbyval,=0A+=09= =09=09=09=09=09=09=09=09=09new_boundinfo,=20old_boundinfo);=0A+=0A+=09= MemoryContextSwitchTo(old_cxt);=0A+=09MemoryContextDelete(tmp_cxt);=0A+=0A= +=09if=20(!same_bound)=0A+=09=09return;=0A+=0A+=09ereport(ERROR,=0A+=09=09= =09errcode(ERRCODE_INVALID_OBJECT_DEFINITION),=0A+=09=09=09= errmsg("cannot=20split=20partition=20\"%s\"=20only=20to=20add=20a=20= DEFAULT=20partition",=0A+=09=09=09=09=20=20=20= get_rel_name(splitPartOid)),=0A+=09=09=09errdetail("The=20non-DEFAULT=20= partition=20would=20keep=20the=20same=20partition=20bound."),=0A+=09=09=09= errhint("Use=20CREATE=20TABLE=20...=20PARTITION=20OF=20...=20DEFAULT=20= to=20add=20a=20DEFAULT=20partition."),=0A+=09=09=09= parser_errposition(pstate,=20parts[0]->name->location));=0A+}=0A+=0A=20= /*=0A=20=20*=20check_partitions_for_split=0A=20=20*=0A@@=20-5775,6=20= +5839,10=20@@=20check_partitions_for_split(Relation=20parent,=0A=20=09=09= Assert(nparts=20=3D=3D=20list_length(partlist)=20-=201);=0A=20=09}=0A=20=0A= +=09if=20(!isSplitPartDefault=20&&=20createDefaultPart)=0A+=09=09= check_split_partition_not_same_bound(parent,=20splitPartOid,=20= new_parts,=0A+=09=09=09=09=09=09=09=09=09=09=09=20nparts,=20pstate);=0A+=0A= =20=09if=20(strategy=20=3D=3D=20PARTITION_STRATEGY_RANGE)=0A=20=09{=0A=20= =09=09PartitionRangeBound=20**lower_bounds;=0Adiff=20--git=20= a/src/test/regress/expected/partition_split.out=20= b/src/test/regress/expected/partition_split.out=0Aindex=20= 961b37953c8..5d7380e28bb=20100644=0A---=20= a/src/test/regress/expected/partition_split.out=0A+++=20= b/src/test/regress/expected/partition_split.out=0A@@=20-1188,6=20= +1188,49=20@@=20SELECT=20tableoid::regclass,=20*=20FROM=20sales_range=20= ORDER=20BY=20tableoid::regclass::text=0A=20=0A=20DROP=20TABLE=20= sales_range;=0A=20--=0A+--=20Test=20that=20SPLIT=20PARTITION=20rejects=20= the=20degenerate=20case=20where=20the=20only=0A+--=20non-DEFAULT=20= replacement=20partition=20keeps=20the=20original=20bound=20and=20the=20= command=0A+--=20merely=20adds=20a=20DEFAULT=20partition.=0A+--=0A+CREATE=20= TABLE=20t=20(i=20int)=20PARTITION=20BY=20RANGE=20(i);=0A+CREATE=20TABLE=20= tp_0_50=20PARTITION=20OF=20t=20FOR=20VALUES=20FROM=20(0)=20TO=20(50);=0A= +INSERT=20INTO=20t=20VALUES=20(1);=0A+--=20ERROR=0A+ALTER=20TABLE=20t=20= SPLIT=20PARTITION=20tp_0_50=20INTO=0A+=20=20(PARTITION=20tp_0_50=20FOR=20= VALUES=20FROM=20(0)=20TO=20(50),=0A+=20=20=20PARTITION=20tp_default=20= DEFAULT);=0A+ERROR:=20=20cannot=20split=20partition=20"tp_0_50"=20only=20= to=20add=20a=20DEFAULT=20partition=0A+LINE=202:=20=20=20(PARTITION=20= tp_0_50=20FOR=20VALUES=20FROM=20(0)=20TO=20(50),=0A+=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20^=0A+DETAIL:=20=20The=20= non-DEFAULT=20partition=20would=20keep=20the=20same=20partition=20bound.=0A= +HINT:=20=20Use=20CREATE=20TABLE=20...=20PARTITION=20OF=20...=20DEFAULT=20= to=20add=20a=20DEFAULT=20partition.=0A+--=20ERROR=0A+ALTER=20TABLE=20t=20= SPLIT=20PARTITION=20tp_0_50=20INTO=0A+=20=20(PARTITION=20tp_0_5=20FOR=20= VALUES=20FROM=20(0)=20TO=20(5),=0A+=20=20=20PARTITION=20tp_6_51=20FOR=20= VALUES=20FROM=20(6)=20TO=20(51),=0A+=20=20=20PARTITION=20tp_default=20= DEFAULT);=0A+ERROR:=20=20upper=20bound=20of=20partition=20"tp_6_51"=20is=20= greater=20than=20upper=20bound=20of=20split=20partition=20"tp_0_50"=0A= +LINE=203:=20=20=20=20PARTITION=20tp_6_51=20FOR=20VALUES=20FROM=20(6)=20= TO=20(51),=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20^=0A+HINT:=20=20Explicit=20partition=20bounds=20= must=20be=20contained=20within=20the=20bounds=20of=20the=20split=20= partition=20when=20a=20DEFAULT=20partition=20is=20specified.=0A+DROP=20= TABLE=20t;=0A+--=0A+--=20Test=20that=20the=20explicit=20partition=20= bound=20cannot=20extend=20outside=20the=20split=0A+--=20partition's=20= bound=20when=20a=20DEFAULT=20partition=20is=20specified.=0A+--=0A+CREATE=20= TABLE=20t=20(i=20int)=20PARTITION=20BY=20RANGE=20(i);=0A+CREATE=20TABLE=20= tp_0_51=20PARTITION=20OF=20t=20FOR=20VALUES=20FROM=20(0)=20TO=20(51);=0A= +CREATE=20TABLE=20tp_51_100=20PARTITION=20OF=20t=20FOR=20VALUES=20FROM=20= (51)=20TO=20(100);=0A+--=20ERROR=0A+ALTER=20TABLE=20t=20SPLIT=20= PARTITION=20tp_0_51=20INTO=0A+=20=20(PARTITION=20tp_0_51=20FOR=20VALUES=20= FROM=20(0)=20TO=20(53),=0A+=20=20=20PARTITION=20tp_default=20DEFAULT);=0A= +ERROR:=20=20upper=20bound=20of=20partition=20"tp_0_51"=20is=20greater=20= than=20upper=20bound=20of=20split=20partition=20"tp_0_51"=0A+LINE=202:=20= =20=20(PARTITION=20tp_0_51=20FOR=20VALUES=20FROM=20(0)=20TO=20(53),=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20^=0A+HINT:=20=20Explicit=20partition=20bounds=20must=20be=20= contained=20within=20the=20bounds=20of=20the=20split=20partition=20when=20= a=20DEFAULT=20partition=20is=20specified.=0A+DROP=20TABLE=20t;=0A+--=0A=20= --=20Try=20to=20SPLIT=20partition=20of=20another=20table.=0A=20--=0A=20= CREATE=20TABLE=20t1(i=20int,=20t=20text)=20PARTITION=20BY=20LIST=20(t);=0A= diff=20--git=20a/src/test/regress/sql/partition_split.sql=20= b/src/test/regress/sql/partition_split.sql=0Aindex=20= a110fc87867..a925dacd205=20100644=0A---=20= a/src/test/regress/sql/partition_split.sql=0A+++=20= b/src/test/regress/sql/partition_split.sql=0A@@=20-834,6=20+834,43=20@@=20= SELECT=20tableoid::regclass,=20*=20FROM=20sales_range=20ORDER=20BY=20= tableoid::regclass::text=0A=20=0A=20DROP=20TABLE=20sales_range;=0A=20=0A= +--=0A+--=20Test=20that=20SPLIT=20PARTITION=20rejects=20the=20degenerate=20= case=20where=20the=20only=0A+--=20non-DEFAULT=20replacement=20partition=20= keeps=20the=20original=20bound=20and=20the=20command=0A+--=20merely=20= adds=20a=20DEFAULT=20partition.=0A+--=0A+CREATE=20TABLE=20t=20(i=20int)=20= PARTITION=20BY=20RANGE=20(i);=0A+CREATE=20TABLE=20tp_0_50=20PARTITION=20= OF=20t=20FOR=20VALUES=20FROM=20(0)=20TO=20(50);=0A+INSERT=20INTO=20t=20= VALUES=20(1);=0A+=0A+--=20ERROR=0A+ALTER=20TABLE=20t=20SPLIT=20PARTITION=20= tp_0_50=20INTO=0A+=20=20(PARTITION=20tp_0_50=20FOR=20VALUES=20FROM=20(0)=20= TO=20(50),=0A+=20=20=20PARTITION=20tp_default=20DEFAULT);=0A+=0A+--=20= ERROR=0A+ALTER=20TABLE=20t=20SPLIT=20PARTITION=20tp_0_50=20INTO=0A+=20=20= (PARTITION=20tp_0_5=20FOR=20VALUES=20FROM=20(0)=20TO=20(5),=0A+=20=20=20= PARTITION=20tp_6_51=20FOR=20VALUES=20FROM=20(6)=20TO=20(51),=0A+=20=20=20= PARTITION=20tp_default=20DEFAULT);=0A+=0A+DROP=20TABLE=20t;=0A+=0A+--=0A= +--=20Test=20that=20the=20explicit=20partition=20bound=20cannot=20extend=20= outside=20the=20split=0A+--=20partition's=20bound=20when=20a=20DEFAULT=20= partition=20is=20specified.=0A+--=0A+CREATE=20TABLE=20t=20(i=20int)=20= PARTITION=20BY=20RANGE=20(i);=0A+CREATE=20TABLE=20tp_0_51=20PARTITION=20= OF=20t=20FOR=20VALUES=20FROM=20(0)=20TO=20(51);=0A+CREATE=20TABLE=20= tp_51_100=20PARTITION=20OF=20t=20FOR=20VALUES=20FROM=20(51)=20TO=20= (100);=0A+=0A+--=20ERROR=0A+ALTER=20TABLE=20t=20SPLIT=20PARTITION=20= tp_0_51=20INTO=0A+=20=20(PARTITION=20tp_0_51=20FOR=20VALUES=20FROM=20(0)=20= TO=20(53),=0A+=20=20=20PARTITION=20tp_default=20DEFAULT);=0A+=0A+DROP=20= TABLE=20t;=0A+=0A=20--=0A=20--=20Try=20to=20SPLIT=20partition=20of=20= another=20table.=0A=20--=0A--=20=0A2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_81619F64-E55B-466D-A37B-6FCB85FD7AFB--