Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6f6b99d

Browse files
committed
Allow a partitioned table to have a default partition.
Any tuples that don't route to any other partition will route to the default partition. Jeevan Ladhe, Beena Emerson, Ashutosh Bapat, Rahila Syed, and Robert Haas, with review and testing at various stages by (at least) Rushabh Lathia, Keith Fiske, Amit Langote, Amul Sul, Rajkumar Raghuanshi, Sven Kunze, Kyotaro Horiguchi, Thom Brown, Rafia Sabih, and Dilip Kumar. Discussion: http://postgr.es/m/CAH2L28tbN4SYyhS7YV1YBWcitkqbhSWfQCy0G=apRcC_PEO-bg@mail.gmail.com Discussion: http://postgr.es/m/CAOG9ApEYj34fWMcvBMBQ-YtqR9fTdXhdN82QEKG0SVZ6zeL1xg@mail.gmail.com
1 parent 2cf15ec commit 6f6b99d

31 files changed

+1367
-135
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4738,6 +4738,17 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</>:<replaceable>&lt;salt&gt;<
47384738
<entry>The number of columns in partition key</entry>
47394739
</row>
47404740

4741+
<row>
4742+
<entry><structfield>partdefid</structfield></entry>
4743+
<entry><type>oid</type></entry>
4744+
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
4745+
<entry>
4746+
The OID of the <structname>pg_class</> entry for the default partition
4747+
of this partitioned table, or zero if this partitioned table does not
4748+
have a default partition.
4749+
</entry>
4750+
</row>
4751+
47414752
<row>
47424753
<entry><structfield>partattrs</structfield></entry>
47434754
<entry><type>int2vector</type></entry>

doc/src/sgml/ref/alter_table.sgml

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
3434
ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable> [ OWNED BY <replaceable class="PARAMETER">role_name</replaceable> [, ... ] ]
3535
SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable> [ NOWAIT ]
3636
ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
37-
ATTACH PARTITION <replaceable class="PARAMETER">partition_name</replaceable> FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable>
37+
ATTACH PARTITION <replaceable class="PARAMETER">partition_name</replaceable> { FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> | DEFAULT }
3838
ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
3939
DETACH PARTITION <replaceable class="PARAMETER">partition_name</replaceable>
4040

@@ -765,11 +765,18 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
765765
</varlistentry>
766766

767767
<varlistentry>
768-
<term><literal>ATTACH PARTITION <replaceable class="PARAMETER">partition_name</replaceable> FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable></literal></term>
768+
<term><literal>ATTACH PARTITION <replaceable class="PARAMETER">partition_name</replaceable> { FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> | DEFAULT }</literal></term>
769769
<listitem>
770770
<para>
771771
This form attaches an existing table (which might itself be partitioned)
772-
as a partition of the target table using the same syntax for
772+
as a partition of the target table. The table can be attached
773+
as a partition for specific values using <literal>FOR VALUES
774+
</literal> or as a default partition by using <literal>DEFAULT
775+
</literal>.
776+
</para>
777+
778+
<para>
779+
A partition using <literal>FOR VALUES</literal> uses same syntax for
773780
<replaceable class="PARAMETER">partition_bound_spec</replaceable> as
774781
<xref linkend="sql-createtable">. The partition bound specification
775782
must correspond to the partitioning strategy and partition key of the
@@ -806,6 +813,17 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
806813
(See the discussion in <xref linkend="SQL-CREATEFOREIGNTABLE"> about
807814
constraints on the foreign table.)
808815
</para>
816+
817+
<para>
818+
When a table has a default partition, defining a new partition changes
819+
the partition constraint for the default partition. The default
820+
partition can't contain any rows that would need to be moved to the new
821+
partition, and will be scanned to verify that none are present. This
822+
scan, like the scan of the new partition, can be avoided if an
823+
appropriate <literal>CHECK</literal> constraint is present. Also like
824+
the scan of the new partition, it is always skipped when the default
825+
partition is a foreign table.
826+
</para>
809827
</listitem>
810828
</varlistentry>
811829

@@ -1395,6 +1413,13 @@ ALTER TABLE cities
13951413
ATTACH PARTITION cities_ab FOR VALUES IN ('a', 'b');
13961414
</programlisting></para>
13971415

1416+
<para>
1417+
Attach a default partition to a partitioned table:
1418+
<programlisting>
1419+
ALTER TABLE cities
1420+
ATTACH PARTITION cities_partdef DEFAULT;
1421+
</programlisting></para>
1422+
13981423
<para>
13991424
Detach a partition from partitioned table:
14001425
<programlisting>

doc/src/sgml/ref/create_table.sgml

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
4949
{ <replaceable class="PARAMETER">column_name</replaceable> [ WITH OPTIONS ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
5050
| <replaceable>table_constraint</replaceable> }
5151
[, ... ]
52-
) ] FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable>
52+
) ] { FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> | DEFAULT }
5353
[ PARTITION BY { RANGE | LIST } ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [, ... ] ) ]
5454
[ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
5555
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
@@ -250,11 +250,13 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
250250
</varlistentry>
251251

252252
<varlistentry id="SQL-CREATETABLE-PARTITION">
253-
<term><literal>PARTITION OF <replaceable class="PARAMETER">parent_table</replaceable> FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable></literal></term>
253+
<term><literal>PARTITION OF <replaceable class="PARAMETER">parent_table</replaceable> { FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> | DEFAULT }</literal></term>
254254
<listitem>
255255
<para>
256256
Creates the table as a <firstterm>partition</firstterm> of the specified
257-
parent table.
257+
parent table. The table can be created either as a partition for specific
258+
values using <literal>FOR VALUES</literal> or as a default partition
259+
using <literal>DEFAULT</literal>.
258260
</para>
259261

260262
<para>
@@ -342,6 +344,26 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
342344
allows precisely one value to be stored &mdash; "infinity".
343345
</para>
344346

347+
<para>
348+
If <literal>DEFAULT</literal> is specified, the table will be
349+
created as a default partition of the parent table. The parent can
350+
either be a list or range partitioned table. A partition key value
351+
not fitting into any other partition of the given parent will be
352+
routed to the default partition. There can be only one default
353+
partition for a given parent table.
354+
</para>
355+
356+
<para>
357+
When a table has an existing <literal>DEFAULT</literal> partition and
358+
a new partition is added to it, the existing default partition must
359+
be scanned to verify that it does not contain any rows which properly
360+
belong in the new partition. If the default partition contains a
361+
large number of rows, this may be slow. The scan will be skipped if
362+
the default partition is a foreign table or if it has a constraint which
363+
proves that it cannot contain rows which should be placed in the new
364+
partition.
365+
</para>
366+
345367
<para>
346368
A partition must have the same column names and types as the partitioned
347369
table to which it belongs. If the parent is specified <literal>WITH
@@ -1679,6 +1701,13 @@ CREATE TABLE cities_ab
16791701
CREATE TABLE cities_ab_10000_to_100000
16801702
PARTITION OF cities_ab FOR VALUES FROM (10000) TO (100000);
16811703
</programlisting></para>
1704+
1705+
<para>
1706+
Create a default partition:
1707+
<programlisting>
1708+
CREATE TABLE cities_partdef
1709+
PARTITION OF cities DEFAULT;
1710+
</programlisting></para>
16821711
</refsect1>
16831712

16841713
<refsect1 id="SQL-CREATETABLE-compatibility">

src/backend/catalog/heap.c

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,7 +1759,8 @@ heap_drop_with_catalog(Oid relid)
17591759
{
17601760
Relation rel;
17611761
HeapTuple tuple;
1762-
Oid parentOid = InvalidOid;
1762+
Oid parentOid = InvalidOid,
1763+
defaultPartOid = InvalidOid;
17631764

17641765
/*
17651766
* To drop a partition safely, we must grab exclusive lock on its parent,
@@ -1775,6 +1776,14 @@ heap_drop_with_catalog(Oid relid)
17751776
{
17761777
parentOid = get_partition_parent(relid);
17771778
LockRelationOid(parentOid, AccessExclusiveLock);
1779+
1780+
/*
1781+
* If this is not the default partition, dropping it will change the
1782+
* default partition's partition constraint, so we must lock it.
1783+
*/
1784+
defaultPartOid = get_default_partition_oid(parentOid);
1785+
if (OidIsValid(defaultPartOid) && relid != defaultPartOid)
1786+
LockRelationOid(defaultPartOid, AccessExclusiveLock);
17781787
}
17791788

17801789
ReleaseSysCache(tuple);
@@ -1825,6 +1834,13 @@ heap_drop_with_catalog(Oid relid)
18251834
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18261835
RemovePartitionKeyByRelId(relid);
18271836

1837+
/*
1838+
* If the relation being dropped is the default partition itself,
1839+
* invalidate its entry in pg_partitioned_table.
1840+
*/
1841+
if (relid == defaultPartOid)
1842+
update_default_partition_oid(parentOid, InvalidOid);
1843+
18281844
/*
18291845
* Schedule unlinking of the relation's physical files at commit.
18301846
*/
@@ -1884,6 +1900,14 @@ heap_drop_with_catalog(Oid relid)
18841900

18851901
if (OidIsValid(parentOid))
18861902
{
1903+
/*
1904+
* If this is not the default partition, the partition constraint of
1905+
* the default partition has changed to include the portion of the key
1906+
* space previously covered by the dropped partition.
1907+
*/
1908+
if (OidIsValid(defaultPartOid) && relid != defaultPartOid)
1909+
CacheInvalidateRelcacheByRelid(defaultPartOid);
1910+
18871911
/*
18881912
* Invalidate the parent's relcache so that the partition is no longer
18891913
* included in its partition descriptor.
@@ -3138,6 +3162,7 @@ StorePartitionKey(Relation rel,
31383162
values[Anum_pg_partitioned_table_partrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
31393163
values[Anum_pg_partitioned_table_partstrat - 1] = CharGetDatum(strategy);
31403164
values[Anum_pg_partitioned_table_partnatts - 1] = Int16GetDatum(partnatts);
3165+
values[Anum_pg_partitioned_table_partdefid - 1] = ObjectIdGetDatum(InvalidOid);
31413166
values[Anum_pg_partitioned_table_partattrs - 1] = PointerGetDatum(partattrs_vec);
31423167
values[Anum_pg_partitioned_table_partclass - 1] = PointerGetDatum(partopclass_vec);
31433168
values[Anum_pg_partitioned_table_partcollation - 1] = PointerGetDatum(partcollation_vec);
@@ -3223,7 +3248,8 @@ RemovePartitionKeyByRelId(Oid relid)
32233248
* relispartition to true
32243249
*
32253250
* Also, invalidate the parent's relcache, so that the next rebuild will load
3226-
* the new partition's info into its partition descriptor.
3251+
* the new partition's info into its partition descriptor.  If there is a
3252+
* default partition, we must invalidate its relcache entry as well.
32273253
*/
32283254
void
32293255
StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
@@ -3234,6 +3260,7 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
32343260
Datum new_val[Natts_pg_class];
32353261
bool new_null[Natts_pg_class],
32363262
new_repl[Natts_pg_class];
3263+
Oid defaultPartOid;
32373264

32383265
/* Update pg_class tuple */
32393266
classRel = heap_open(RelationRelationId, RowExclusiveLock);
@@ -3271,5 +3298,15 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
32713298
heap_freetuple(newtuple);
32723299
heap_close(classRel, RowExclusiveLock);
32733300

3301+
/*
3302+
* The partition constraint for the default partition depends on the
3303+
* partition bounds of every other partition, so we must invalidate the
3304+
* relcache entry for that partition every time a partition is added or
3305+
* removed.
3306+
*/
3307+
defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(parent));
3308+
if (OidIsValid(defaultPartOid))
3309+
CacheInvalidateRelcacheByRelid(defaultPartOid);
3310+
32743311
CacheInvalidateRelcache(parent);
32753312
}

0 commit comments

Comments
 (0)