Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1Zs6cf-0002eL-HK for pgsql-hackers@arkaria.postgresql.org; Fri, 30 Oct 2015 10:09:26 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84) (envelope-from ) id 1Zs6ce-0000Gz-Js for pgsql-hackers@arkaria.postgresql.org; Fri, 30 Oct 2015 10:09:24 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84) (envelope-from ) id 1Zs6cd-0000Gs-0a for pgsql-hackers@postgresql.org; Fri, 30 Oct 2015 10:09:23 +0000 Received: from tama50.ecl.ntt.co.jp ([129.60.39.147]) by magus.postgresql.org with esmtp (Exim 4.84) (envelope-from ) id 1Zs6cT-0007Wf-19 for pgsql-hackers@postgresql.org; Fri, 30 Oct 2015 10:09:21 +0000 Received: from vc2.ecl.ntt.co.jp (vc2.ecl.ntt.co.jp [129.60.86.154]) by tama50.ecl.ntt.co.jp (8.13.8/8.13.8) with ESMTP id t9UA8r8t013870; Fri, 30 Oct 2015 19:08:53 +0900 Received: from vc2.ecl.ntt.co.jp (localhost [127.0.0.1]) by vc2.ecl.ntt.co.jp (Postfix) with ESMTP id B05215F611; Fri, 30 Oct 2015 19:08:52 +0900 (JST) Received: from imail3.m.ecl.ntt.co.jp (imail3.m.ecl.ntt.co.jp [129.60.5.248]) by vc2.ecl.ntt.co.jp (Postfix) with ESMTP id 947EA5F594; Fri, 30 Oct 2015 19:08:52 +0900 (JST) Received: from [127.0.0.1] ([129.60.241.61]) by imail3.m.ecl.ntt.co.jp (8.13.8/8.13.8) with ESMTP id t9UA8peR030940; Fri, 30 Oct 2015 19:08:52 +0900 Subject: Re: Declarative partitioning To: Simon Riggs , pgsql-hackers@postgresql.org References: <55D3093C.5010800@lab.ntt.co.jp> <55D3093C.5010800@lab.ntt.co.jp> <20150826140158.3678.53116.pgcf@coridan.postgresql.org> From: Amit Langote Message-ID: <563341AE.5010207@lab.ntt.co.jp> Date: Fri, 30 Oct 2015 19:08:46 +0900 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0 MIME-Version: 1.0 In-Reply-To: <20150826140158.3678.53116.pgcf@coridan.postgresql.org> Content-Type: multipart/mixed; boundary="------------090507020209010101040805" X-TM-AS-MML: disable X-Pg-Spam-Score: -3.6 (---) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-hackers Precedence: bulk Sender: pgsql-hackers-owner@postgresql.org This is a multi-part message in MIME format. --------------090507020209010101040805 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit On 2015/08/26 23:01, Simon Riggs wrote: > The following review has been posted through the commitfest application: > ... > > Needs planner work and tests of that. ALTER TABLE etc can wait. > > The new status of this patch is: Waiting on Author Alright, after spending much time on this I came up with the attached. I'm sincerely sorry this took so long. I'll describe below what's in those patches. The DDL and catalogs part are not much different from what I had last described though I took a few steps to simplify things. I dropped the multi-level partitioning bit and changed range partition creation syntax a little. Ability to specify both min and max for every range partition had made life much difficult to construct and use the internal representation. Anyway, attached document patch (and regression tests to some degree) might give a picture of what user commands look like as of now, although things are very much subject to overhaul in this general area of user visible commands and features. As pointed out by Simon, there is a need to invent a better internal representation of partitioning constraints/metadata to address the planning considerations more effectively and scalably. To that end, I have tried to implement a number of things - There is a proposed set_partitioned_rel_size() that calls planner's partitioning module (proposed) which takes RelOptInfo of a partitioned table and returns a list of OIDs of partitions that are selected to be scanned based on the baserestrictinfo (other partitions are pruned!) The partitioned rel size is then computed using size estimates for selected partitions. Later, proposed set_partitioned_rel_pathlist() will create a Append/MergeAppend path with cheapest paths for individual partitions as its subpaths. It seems important to point out that the selected partitions each get simple_rte_array and a simple_rel_array slots (note that those arrays are required to be repalloc'd to make those slots available). The partition reloptinfos are of kind RELOPT_PARTITION_REL which are functionally quite similar to RELOPT_OTHER_MEMBER_REL but different in that they do not require using any auxiliary structures like AppendRelInfo and append_rel_list. The parent-partition mapping would still be necessary and is achieved using a field called parent_relid of partition reloptinfo. The proposed partitioning module (in the planner) figures out which of the clauses to use for partition pruning. The rules of matching clauses to partition key look similar to those used to match them to an index, at least with respect to the key representation. Key info includes attribute number, expression tree (for expression key columns), operator family OID, per key column. While clauses matched to an index would be returned as it is to make index paths with, things work differently for clauses matched to partition key column(s). For each matched clause, the Const operand and the operator would be added to operand list and operator list, respectively, for the matched column. Operands are later evaluated to datums. The datum arrays along with operator OIDs are passed along to another (general-purpose) partitioning module (proposed). That module returns a list of OIDs of selected partitions after comparing the clause operand datums with the partition bounds. The operator strategies (of passed operators) coupled with partitioning method (list or range, currently) determine how partitions are selected. For some partitioning methods, faster partition selection is enabled, for example, being able to use binary search helps make selecting range partitions reasonably fast. Note that the clauses matched for a given column are effectively ANDed with one another. At the moment, using OR clauses disables partition pruning. But it would be a matter of improving the structure used to pass around the operands and operators to also convey the logical structure of clauses. Also, even though range partitioning allows multi-column key, only the clauses referencing the first key are used to select partitions. Multi-column range partition pruning implementation is saved for later. Note that partition bounds are themselves stored as arrays of datums computed on-demand (cached in RelationData as part of a structure called PartitionDesc which stores other info about partitions like OIDs.) There are not many executor changes currently except for those made to ensure sane I/U/D behaviors (made to respective ExecI/U/D functions). We can certainly think about implementing specialized scan node for partitioned tables but I put it off for now. Attached user documentation and regression test patches, though not quite comprehensive, should help one get started. Thanks, Amit --------------090507020209010101040805 Content-Type: application/gzip; name="declarative-partitioning-wip-1.patch.tar.gz" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="declarative-partitioning-wip-1.patch.tar.gz" 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 FFjCg36lRVjVpni3qV0z1lx9GzpkFqMAuGYETsPeGsE3pWC63eKqWIrxQ0CY DV0JNWAGdO3PwGdawgPKeUGYU7Yt0qNbQ0oh1SlQJoinrzV7nkxvBlPjcjSZ osVri38t+hrQ8ldAe5gyZOvee+PjmdwXgHYlwE78b0B1M9TgiLxW5qlKgB38 4GZgMYtnk+3iihRmV0Jg6hOyLXIxED3FHorgLF8S9dZwTHo6r2HYCuDC4/I6 NeAD+oLNWWm2YhBuqC85pyuk96JE4tXRKO2GpAze2D8ffGpudqW3tn32AAOs pQBlWiAPdQF8H8/cBLmNdNPVLcUGFIpRpMzNFDGoVn77s1G2N4Px+yELV/dW a2SbkVVFqx98qMeD8U+Dm5vBT1c6eI4Ne/3iZ9aWwqPv7e+VvqSSwU1TSMo2 glFqsEYuqnJugoe2AOlKhrYObKJ3fP75F2J+tSst42G5QWzmVVGnMrOMoFSa fREw9jhI7BFg2JYwWJEzFt2lyKbMtlKIFXj8ZWX85hqeq/fyjY7eunJ+i1qe 9X1NLc8Ejwsdm4v4//v6fat6nRf9VfV6ymJNvc75cn7iNooKeTM1K33XLzOk dzCLFyd6WZdzPjVbmraYyRivci19iN0ynQV+0+wWrUa/JZF3H9ClMshwTHBb hCjK+Rw2CO94YL0O2jcZiYTntYLyAC/B3+Hd/5kUEaHFSV79qksnjNeqPwVw t3QgGaAulCCq74Grr+rQ2V4oZ2nC4TCjUEFsZAYIVtJZJq+gJ8TAjfwu49eK Dw+QMqKvGgAb/84rsnrA6yDIB28tinOu5s/x680yHHlWSJfrilZWv2WlcoS+ tV+82FIwvRAvgZcMDx6AiPTqnOUGz7kyTEsvt2ySVeS9wSJ5iIpJ4pltv43f 2i/E7W3jVZY4Vy2VB/wCU62pzdeV5Jls8PSxILaOeD+cQtbz8XyaziJ3KVm3 8gUkrEtK95HUgUFThwK9yl90p8Km0uZdI3gf55I6FXJG+dwwxt1KGaT3UrIo ZyUhJRKcBtWGt3SEnKF+WPu74j65dQJSeNuJbpUd83d52Bgz6yAkBaGGO/Mh G3Yt7K2yq1L806ajOch0spv8I26pXpaSGpbhS5XffOL7rREmGPiBrxdU8Vs+ L4qYS/ZlIQ5WkAnHpoMgkeOJyPXjgunXzqYCd4md4BZzh9SSBP/1kynevJgA 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 Mzw95z+d4KbgjzPosf44E9/DCwHnHzzC7HEomn+wdPqIjB+OXh9/ODqARuh7 /B5w/px+NxTjwxF+Ev79DrDww9G3R8c/4J9yKOAXwWwY6vzwXfDh5AA/SVT8 wxnuPRcX2OZUwp2dzXqXLHjEdbIg0x+Mhy8k9/pk4xUmmDIeRn9pCaNY+ctf bMejiTz+PeP8ylc5b8xXrDh4pe5Skzv4K8T9V/JOpQLADOicnQXVRqNR46kH P9MwvwRf8TTtd9i/9Z/5kTNSXob1C2z5jWQK8Ver7CE8SNLZHw+DZxPbUOO0 6bIpeugDANgzhlrepQEaTaGVjVfp4oJYyK9h5v29+TwcXOuuqhsMtfGKZv91 8JeO81RKXsPzrjWou2Mo3VmNtgQL722qptx2rq+8nXyHFm/nz2r/5EV3B4Nf MnuYGy6zh1m5+1++bwfRHfbtjvvTXWF/SKuZgcoLarcesOWj8o+ABoJjHPwu 8Mbime9Pzn+CHXD6kB0uUF/IjQ2vZbc1s1Gyo9wL58ercnuZKXXwYwF+DEgh 0yN2hlHy9QvJdtyShNFbokQZJNPbPhXdiVloBmmbBGdyr3zx+MWdYX6cEgPV aS8q+0j2e6iGOI/GU4FGBhZPq08Dn1rmae0pKUgsbYxWa0z2k/E4noteQ719 Ng0HjPTuO7bIvNIQAZ8bpuZmPZlDYxpqmvJ3yTA9xcyV6Zw8r2H72i/V9uxi HSTcn516izboMYGl76U5t86J/G29NA3x5Fo5KFHbjtWWXKIKSw6lrmnPbagx +F4RVE8bAfcCzSx/2bWGpMDBWRhP5tpEvaE/N9Wz2LUfTwa0LXQQm1YDJyXF 3VF0o9WyZmEN12raz+3x7Bc847VtSNlqGJNu23O/m+s9QJLh3cTDN8DNHB2j 7ALcyJlnUxWm+zY2WHoCFM6v8Pbys0DLefB5YITvbNVbTUT4nW2p2fv5EX63 BOFbrcfHeAdvVkR5fMeP851ynO8W4HynCOe75Ti/WYLz7OJeivK8y5vkrdve bW5xjv3PsseZrRSPFt9WIkLin0fwJxop8CeOV/3LVs37zsarUTJQyf7+v617 oYHy9CnGA/PhqZ8AW9u2qw7QNjtC7rY3/+Sg3V0FtLt/IGjdo24fCX0db28z cDvdeuczQvfux2q98CbJCm1aEjh+k2kjaGaFOtcetJSN0rLDajRf2PEiHkhv 78yk1nSkM73txygF/GW7CFvw2JTiim704IqPfO4WUc9mOfW0GYoCjsEjojgS yv04hj/lvm/fZd+zm2vtuwN1d+MdiD/GzrfaRVvfKd/6bsnWm1wvhTuvhLnn z8T43SIfic5mcxM4JiJGhm97ERwefdM7PQQ8QIrj7jebRoHi5KRFEmBJflXi qxFgD99ZIuU6Ri4RTMJRJnrPsgejzOoykC9sA6Rffjaf94rOa+vucBqtX/8U nO4dvYVjcURrtuyZRF0dLLV5WhdNrRZTL5m0FHN04rtCxDEZgM/OT/fO+/Td DJqd8JdZnNIt9tXWMsPTOl010T/dpZG2/o9fGX72cRdGbdYKXhjjsoNvpHLA ubeqf2nVDII4i68LMORt9aoUHfpLqw6oXbNxR/d+Eewno8MhaVIGWAu2AIp4 RxfBEdtcSOqzbkFGokozxIYS2aRplt5lwViMHeTUQNF97CAwwkSBn2U1mZnL ev7SeszVIFqHXNMeBdA/ch1t/zq6d1/HS3YzDw4MBaR0BYY+IkHMXLSs/7O1 9MZhIqPOI1DZPS33iXxXmo0Zi79D/iITArfxsCghInKPFoCaW11g47k1mnT7 SiRXsXH4W/52vQt8rTXzksi6iZamP2xV7uTtZd0ba3hfXlQqaAEXOqg2PKNS /od5yyyWQcEnaCUyasOprs7eChQUFdw/HJ5/g1khiCWqoUfrlNJGzLAYNzUe Hx6cUTo4/AvtqPwAs7RHV+HgFv1XgX6j9lf4TLaR0cvG281aBxzT4HdhfLh0 Z2ertSkaImeMLAtTyL9klkEJyJxMq6liYAwjzHBeNs08K5OdhoKmy1e/CPCK Z1sleT9YLx6LDbOPzw27ZvVX/hmnxz+ceV7jZmz0vX1y2jvrnX7fK3hbNev3 KZqns7W5S0FTnZ1NKUu2mMwiinYa9sWTSe2Kdgsyf4tzkHpwLoBju6N+Kj5C 8rfyC1J/kneQTGe72eXp7Kryl8XTUeAyf7IrS/bvM5mU9m2hP7R/i34gXi7m b/R1UfPaoorMMK+tDjszFM9L3ET0X+gsov5Qzh0yJaR8uun73jv1GHg29Zgs 2eqP470D/Su6evDkdrZ5D7vNneYyoClTv/UnGfz132j2l2mQ8V+eGwO//QTN /PpvMvMv9U6nH/3wKl+vLd9B/NQ3L5rDza2dRuNiaysMByXF+jxDFDqr252o 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 AuH6CYQeQ/3Wx0+pQ0hfEx9GYRsu0T48gDRqiL5CQQ4XWB60gDVCCqGSrpgK Xwu1VMNeVp1qvVSq2fX6eb22Ty2lFZCllNKS0ALiGDutxE3a/rsyTbsVeS30 JGdzhM3mXqQx78Lq3/IFruNauVmsVFKWsdCrstreICYFvUJbq8AzYPuBoj0K AA1FB2+Kws01QqNW7hOIYKjuwVcs0XPo5xcGScFCEHbyxKKOp9LtziXB0Cih xFgJrpDW7L89l+FaHFgKkzy4n53Nd6e4lRpICQ6tatb/vnc5Djb5P3CXL1jG umQYPSvYoW3+jkyf2NOLykBl4e75ZuFYGs410HsYdo569sXhAdn6HrQGfy5g K9pRUXiDh42gtltLhFtZqxXjpQ5vcoi95eGmVlirtQeLCYebigvn4w2NI6ZZ YFCLzcOzGzxCO+YvDOnCi8RwTqjt8M31GRvMLprY81MAh9bOo1xymjQhQKEb 8S5QTZuYMu5j7mAfPtBUVt5gZw60JN2Ocn0jLdYMco0KvaFxo+m0yN85ht1T KlKGAYbNo8KYPXnDPpPVuzdFbiL2ciO3eguDT+EwQln3A+TB2r0eHvCEbCnx XUKtxncJjZS7Do024eg0e8D+zq7aExO+FsMnGJUw+mQ4vTPHs77Zv4I/9Oqd fBXqJ6JZrs32NXzley5Zq3SbXW82qZGtavqxH166QIESrrv4MvZgBwLm7u1y 8cXn78gDkLtB3Ojh8Wzru5/xshI1mLjfd2N+RHWl5LHrlIpGWkfEcUn3NKQp UmQs+KQkAQsW4F16tQn8rjhR49Kn6LZkTvC0EleKpMYQj6kl4kCTp2C49UUh vOOpKfS05FWXLtXnL4Tj9Z6BqbStTUB7IFJ78NfY2YmrxnfQfZDNLeHuiyfU VO7CMwYAPAYGCnOEXAjxIYjjlwrt8wZN/flFq1itZuEjwslnRtNaP1oBML1z d7e2tqxFRwhAu88L2FqrlwbhUGFbciTjqZeYCqaQtZG2AOXDtXTqFndCayXn cY82QZWdpuvBVaQoXXCwQspAU6XTlbt5klsmVXYUaEtDo7Yo66W3WlPHbsQN ShztrV2s6MDsgtIKIv4iwsVqmGwiuSPGaIg7ldDSmJmvpbV2VwLXPexeUzaF uaaXrtBW6EjuqGIyFW7NmfAWTaCn456Bf03Mg1C40MZr/vQMW8UKYdYdduSW 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 r5/Xz3/W538Bc/wHLwCwBAA= --------------090507020209010101040805 Content-Type: text/x-diff; name="decl-partitioning-docs.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="decl-partitioning-docs.patch" diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 97ef618..ce02af9 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -214,6 +214,16 @@ + pg_partition + information about partitions, including partition boundary + + + + pg_partitioned_rel + information about partitioned tables, including the partition key + + + pg_pltemplate template data for procedural languages @@ -4629,6 +4639,161 @@ + + <structname>pg_partition</structname> + + + pg_partition + + + + The catalog pg_partition stores + information about partitions of partitioned tables. + + + + <structname>pg_partition</> Columns + + + + + Name + Type + References + Description + + + + + + + partrelid + oid + pg_class.oid + The OID of the pg_class entry for this partition + + + + partparent + oid + pg_class.oid + The OID of the partitioned table this partition is part of + + + + partlistvals + anyarray + + For partitions of a list partitioned table, list of values assigned to this partition + + + + partrangemaxs + anyarray + + For partitions of a range partitioned table, array of maximum values per partition key column + + + + +
+ +
+ + + <structname>pg_partitioned_rel</structname> + + + pg_partitioned_rel + + + + The catalog pg_partitioned stores + information about partition key of partitioned tables. + + + + <structname>pg_partitioned_rel</> Columns + + + + + Name + Type + References + Description + + + + + + + partrelid + oid + pg_class.oid + The OID of the pg_class entry for this partitioned table + + + + partstrat + char + + + Partitioning strategy (method); l = list partitioned table, + r = range partitioned table + + + + + partnatts + int2 + + The number of columns in partition key + + + + partkey + int2vector + pg_attribute.attnum + + This is an array of partnatts values that + correspond to table columns used as partition key. For example, a value + of 1 3 would mean that the first and the third table + columns make up the partition key. A zero in this array indicates that the + corresponding partition key column is an expression over the table columns, + rather than a simple column reference. + + + + + partclass + oidvector + pg_opclass.oid + + For each column in the partition key, this contains the OID of + the operator class to use. See + pg_opclass for details. + + + + + partexprs + pg_node_tree + + + Expression trees (in nodeToString() + representation) for partition key columns that are not simple column + references. This is a list with one element for each zero + entry in partkey. Null if all partition key columns + are simple references. + + + + + +
+ +
<structname>pg_pltemplate</structname> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index aca40f5..07b3000 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -77,12 +77,23 @@ ALTER TABLE ALL IN TABLESPACE name NOT OF OWNER TO { new_owner | CURRENT_USER | SESSION_USER } REPLICA IDENTITY { DEFAULT | USING INDEX index_name | FULL | NOTHING } + ATTACH PARTITION partition_name FOR VALUES { list_partition_values | range_partition_values } USING [ TABLE ] table_name + DETACH PARTITION partition_name [ USING [ TABLE ] table_name ] and table_constraint_using_index is: [ CONSTRAINT constraint_name ] { UNIQUE | PRIMARY KEY } USING INDEX index_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] + +and list_partition_values is: + +[ IN ] ( { expression } [, ...] ) + +and range_partition_values is: + +LESS THAN ( { expression } [, ...] ) + @@ -704,6 +715,32 @@ ALTER TABLE ALL IN TABLESPACE name + + ATTACH PARTITION partition_name FOR VALUES { list_partition_values | range_partition_values } USING [ TABLE ] table_name + + + This form attaches a table as partition of the target table (presumably + partitioned.) To be attached as a partition, the source table must + already contain all the same columns as the parent. The columns must + have matching data types, and if they have NOT NULL + constraints in the target table then they must also have + NOT NULL constraints in the source table. The table is renamed + to the specified partition name. [Note: this command is currently + unimplemented.] + + + + + + DETACH PARTITION partition_name [ USING [ TABLE ] table_name ] + + + This form detaches a partition from the target table (presumably + partitioned.) The resulting table can optionally have a different + name after being detached. + + + diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index a2d0b0c..2fa7bc8 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -28,6 +28,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [, ... ] ] ) [ INHERITS ( parent_table [, ... ] ) ] +[ PARTITION BY {RANGE | LIST} ON ( { column_name | ( expression ) } [ opclass ] [, ...] )] [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace_name ] @@ -42,6 +43,13 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace_name ] +CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name + PARTITION OF table_name + FOR VALUES { list_partition_values | range_partition_values } +[ WITH ( storage_parameter [= value] [, ... ] ) ] +[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] +[ TABLESPACE tablespace_name ] + where column_constraint is: [ CONSTRAINT constraint_name ] @@ -70,6 +78,14 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } +and list_partition_values is: + +[ IN ] ( { expression } [, ...] ) + +and range_partition_values is: + +LESS THAN ( { expression } [, ...] ) + index_parameters in UNIQUE, PRIMARY KEY, and EXCLUDE constraints are: [ WITH ( storage_parameter [= value] [, ... ] ) ] @@ -230,6 +246,40 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI + PARTITION OF table_name FOR VALUES { list_partition_values | range_partition_values } + + + Creates the table as a partition of the + specified partitioned table. + + + + When a table is created as partition, the data types of the + columns, constraints are determined by the underlying partitioned + table and are not specified by the CREATE TABLE + command nor can they be added or altered after the fact using + ALTER TABLE. But the CREATE TABLE command + command can specify table storage parameters and tablespace for the + partition (and can be altered after the fact). The + FOR VALUES specification describes partition bounds, + that is, either a list of values or of maximum values of partition + key columns for list partitions and range partitions, respectively. + + + + Care must be taken to specify correct values for the bounds. + Specifying bounds such that new partition overlaps with some existing + partition results in error. Rules for whether such overlaps occurs + are different based on the partitioning method used. For list + bounds, make sure that the new partition's list of values does + not contain any values already present in some existing partition's + list. For range bounds, make sure that the new partition's rangemax + value is greater than the last existing partition's rangemax. + + + + + column_name @@ -314,6 +364,32 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI + PARTITION BY {RANGE | LIST} ON ( { column_name | ( expression ) } [ opclass ] [, ...] ) + + + The optional PARTITION BY clause specifies a list + of columns (or expressions) to use as the partition key of table. + Currently, LIST and RANGE can be specified + as the partitioning method. Such a table (hereafter referred to as + partitioned table) is divided into sub-tables called partitions which + are created after the fact. + + + + Scanning a partitioned table results in scanning its partitions + and appending the results. Rows inserted into a partitioned table + are mapped to and stored in a partition based on values of partition + key columns unless an appropriate partition has not been defined yet. + + + + There are currently a number of limitations on constraints that + can be defined on partitioned tables as detailed elsewhere. + + + + + LIKE source_table [ like_option ... ] @@ -498,6 +574,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI primary key constraint defined for the table. (Otherwise it would just be the same constraint listed twice.) + + + UNIQUE constraints are not supported on partitioned tables at the moment. + @@ -526,6 +606,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI different from other sets of columns named by any unique constraint defined for the same table. + + + Primary key constraints are not supported on partitioned tables at the moment. + @@ -576,6 +660,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI exclusion constraint on a subset of the table; internally this creates a partial index. Note that parentheses are required around the predicate. + + + Exclusion constraints are not supported on partitioned tables at the moment. + @@ -699,6 +787,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI associated with the foreign key constraint can be performed more efficiently. + + + Partitioned tables cannot use the REFERENCES clause at the moment. + @@ -1119,7 +1211,9 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI Unique constraints and primary keys are not inherited in the current implementation. This makes the combination of - inheritance and unique constraints rather dysfunctional. + inheritance and unique constraints rather dysfunctional. As + noted before, they cannot be used with partitioned tables + either. @@ -1341,6 +1435,76 @@ CREATE TABLE employees OF employee_type ( salary WITH OPTIONS DEFAULT 1000 ); + + + Create a range partitioned table and partitions: + +CREATE TABLE employee ( + name text, + age int NOT NULL +) +PARTITION BY RANGE ON (age); + +CREATE TABLE employees_less_than_30 + PARTITION OF employee + FOR VALUES LESS THAN (30) + WITH (fillfactor=70) + TABLESPACE diskvol1; + +CREATE TABLE employees_between_30_and_40 + PARTITION OF employee + FOR VALUES LESS THAN (40) + WITH (fillfactor=70) + TABLESPACE diskvol2; + + + + Create a list partitioned table and partitions: + +CREATE TABLE store ( + id int, + city text NOT NULL +) +PARTITION BY LIST ON (city); + +CREATE TABLE store_east + PARTITION OF store + FOR VALUES IN ('New York', 'Boston'); + +CREATE TABLE store_west + PARTITION OF store + FOR VALUES ('San Fransisco', 'Seattle'); + + + + Create a multi-column range partitioned table and partitions: + +CREATE TABLE sales ( + tos timestamp NOT NULL, + item text +) +PARTITION BY RANGE ON (extract(year from tos), extract(month from tos)); + +CREATE TABLE sales_before_2015 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 1); + +CREATE TABLE sales_2015_q1 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 4); + +CREATE TABLE sales_2015_q2 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 7); + +CREATE TABLE sales_2015_q3 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 10); + +CREATE TABLE sales_2015_q4 + PARTITION OF sales + FOR VALUES LESS THAN (2016, 1); + @@ -1511,6 +1675,16 @@ CREATE TABLE employees OF employee_type ( effect can be had using the OID feature. + + + Partitioned Tables + + + PARTITION BY and PARTITION OF (including + FOR VALUES) are PostgreSQL + extensions. + + --------------090507020209010101040805 Content-Type: text/x-diff; name="decl-partitioning-tests.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="decl-partitioning-tests.patch" diff --git a/src/test/regress/expected/partitioned_table.out b/src/test/regress/expected/partitioned_table.out new file mode 100644 index 0000000..da2e784 --- /dev/null +++ b/src/test/regress/expected/partitioned_table.out @@ -0,0 +1,1044 @@ +-- +-- create a range partitioned table +-- +CREATE TABLE person_partitioned ( + name text, + age int, + city text +) PARTITION BY RANGE ON (age); +-- select (empty result yet) +EXPLAIN (costs off) SELECT * FROM person_partitioned; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +SELECT * FROM person_partitioned; + name | age | city +------+-----+------ +(0 rows) + +-- cannot insert data into it without creating a partition +INSERT INTO person_partitioned VALUES ('Bob', 23, 'Boston'); -- fail +ERROR: cannot find partition for the new row for relation "person_partitioned" +DETAIL: Failing row contains (Bob, 23, Boston). +-- create a couple of range partitions +CREATE TABLE person_partitioned_30 + PARTITION OF person_partitioned + FOR VALUES LESS THAN (30); +CREATE TABLE person_partitioned_50 + PARTITION OF person_partitioned + FOR VALUES LESS THAN (50); +INSERT INTO person_partitioned VALUES ('Bob', 23, 'Boston'); -- ok +INSERT INTO person_partitioned VALUES ('Alice', 32, 'Austin'); -- ok +INSERT INTO person_partitioned VALUES ('Jon', 52, 'Seattle'); -- fail +ERROR: cannot find partition for the new row for relation "person_partitioned" +DETAIL: Failing row contains (Jon, 52, Seattle). +-- select (non-empty result) +EXPLAIN (costs off) SELECT * FROM person_partitioned; + QUERY PLAN +----------------------------------------- + Append + -> Seq Scan on person_partitioned_30 + -> Seq Scan on person_partitioned_50 +(3 rows) + +SELECT * FROM person_partitioned; + name | age | city +-------+-----+-------- + Bob | 23 | Boston + Alice | 32 | Austin +(2 rows) + +-- no partition selected +EXPLAIN (costs off) SELECT * FROM person_partitioned WHERE age > 50; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +SELECT * FROM person_partitioned WHERE age > 50; + name | age | city +------+-----+------ +(0 rows) + +-- one partition selected +EXPLAIN (costs off) SELECT * FROM person_partitioned WHERE age < 25; + QUERY PLAN +----------------------------------------- + Append + -> Seq Scan on person_partitioned_30 + Filter: (age < 25) +(3 rows) + +SELECT * FROM person_partitioned WHERE age < 25; + name | age | city +------+-----+-------- + Bob | 23 | Boston +(1 row) + +-- cannot update partition directly +UPDATE person_partitioned_50 SET city = 'Dallas' WHERE name = 'Alice'; +ERROR: cannot update a partition of another table +HINT: Perform UPDATE on the parent instead. +-- so update parent +EXPLAIN (costs off) UPDATE person_partitioned SET city = 'Dallas' WHERE name = 'Alice'; + QUERY PLAN +----------------------------------------------------- + Update on person_partitioned + -> Result + -> Append + -> Seq Scan on person_partitioned_30 + Filter: (name = 'Alice'::text) + -> Seq Scan on person_partitioned_50 + Filter: (name = 'Alice'::text) +(7 rows) + +UPDATE person_partitioned SET city = 'Dallas' WHERE name = 'Alice'; +-- following update changes the value of partition key +UPDATE person_partitioned SET age = 52 WHERE name = 'Alice'; -- fail +ERROR: cannot perform update that causes a row to change partition +UPDATE person_partitioned SET age = 42 WHERE name = 'Alice'; -- ok +-- +-- create a list partitioend table +-- +CREATE TABLE city_partitioned ( + name text +) PARTITION BY LIST ON (name); +-- select (empty result yet) +EXPLAIN (costs off) SELECT * FROM city_partitioned; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +SELECT * FROM city_partitioned; + name +------ +(0 rows) + +-- cannot insert data into it without creating a partition +INSERT INTO city_partitioned VALUES ('Boston'); -- fail +ERROR: cannot find partition for the new row for relation "city_partitioned" +DETAIL: Failing row contains (Boston). +-- create a couple of list partitions +CREATE TABLE city_east + PARTITION OF city_partitioned + FOR VALUES IN ('New York', 'Boston'); +CREATE TABLE city_west + PARTITION OF city_partitioned + FOR VALUES IN ('Los Angeles'); +INSERT INTO city_partitioned VALUES ('Boston'); -- ok +INSERT INTO city_partitioned VALUES ('Austin'); -- fail +ERROR: cannot find partition for the new row for relation "city_partitioned" +DETAIL: Failing row contains (Austin). +-- select (non-empty result) +EXPLAIN (costs off) SELECT * FROM city_partitioned; + QUERY PLAN +----------------------------- + Append + -> Seq Scan on city_east + -> Seq Scan on city_west +(3 rows) + +SELECT * FROM city_partitioned; + name +-------- + Boston +(1 row) + +-- no partition selected +EXPLAIN (costs off) SELECT * FROM city_partitioned WHERE name = 'Austin'; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +SELECT * FROM city_partitioned WHERE name = 'Austin'; + name +------ +(0 rows) + +-- one partition selected +EXPLAIN (costs off) SELECT * FROM city_partitioned WHERE name = 'Boston'; + QUERY PLAN +----------------------------------------- + Append + -> Seq Scan on city_east + Filter: (name = 'Boston'::text) +(3 rows) + +SELECT * FROM city_partitioned WHERE name = 'Boston'; + name +-------- + Boston +(1 row) + +-- can delete from partition directly +DELETE FROM city_east WHERE name = 'Boston'; -- ok +-- or from parent +DELETE FROM city_east WHERE name = 'Boston'; -- 0 deleted +-- dropping partitioned table requires CASCADE if partitions exist +DROP TABLE person_partitioned; -- fail +ERROR: cannot drop partitioned table person_partitioned because other objects depend on it +DETAIL: partition person_partitioned_30 depends on partitioned table person_partitioned +partition person_partitioned_50 depends on partitioned table person_partitioned +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP TABLE person_partitioned CASCADE; -- ok +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to partition person_partitioned_30 +drop cascades to partition person_partitioned_50 +DROP TABLE city_partitioned CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to partition city_east +drop cascades to partition city_west +-- +-- test some planner bits +-- +CREATE TABLE rangetab (a int) PARTITION BY RANGE ON (a); +CREATE TABLE rangetab1 PARTITION OF rangetab FOR VALUES LESS THAN (10); +CREATE INDEX rangetab1_a_idx ON rangetab1 (a); +CREATE TABLE rangetab2 PARTITION OF rangetab FOR VALUES LESS THAN (20); +CREATE INDEX rangetab2_a_idx ON rangetab2 (a); +CREATE TABLE rangetab3 PARTITION OF rangetab FOR VALUES LESS THAN (30); +CREATE INDEX rangetab3_a_idx ON rangetab3 (a); +CREATE TABLE rangetab4 PARTITION OF rangetab FOR VALUES LESS THAN (40); +CREATE INDEX rangetab4_a_idx ON rangetab3 (a); +INSERT INTO rangetab SELECT generate_series(1, 39); +-- scan rangetab2 and rangetab3 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a BETWEEN 15 AND 25; + QUERY PLAN +----------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab2 + Recheck Cond: ((a >= 15) AND (a <= 25)) + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: ((a >= 15) AND (a <= 25)) + -> Bitmap Heap Scan on rangetab3 + Recheck Cond: ((a >= 15) AND (a <= 25)) + -> Bitmap Index Scan on rangetab4_a_idx + Index Cond: ((a >= 15) AND (a <= 25)) +(9 rows) + +-- scan rangetab1 and rangetab2 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a <= 10; + QUERY PLAN +-------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab1 + Recheck Cond: (a <= 10) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: (a <= 10) + -> Bitmap Heap Scan on rangetab2 + Recheck Cond: (a <= 10) + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: (a <= 10) +(9 rows) + +-- scan rangetab2, rangetab3, rangetab4 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 10; + QUERY PLAN +-------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab2 + Recheck Cond: (a > 10) + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: (a > 10) + -> Bitmap Heap Scan on rangetab3 + Recheck Cond: (a > 10) + -> Bitmap Index Scan on rangetab4_a_idx + Index Cond: (a > 10) + -> Seq Scan on rangetab4 + Filter: (a > 10) +(11 rows) + +-- scan rangetab1 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a < 10 AND a < 20; + QUERY PLAN +--------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab1 + Recheck Cond: ((a < 10) AND (a < 20)) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: ((a < 10) AND (a < 20)) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 1 AND a < 20; + QUERY PLAN +-------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab1 + Recheck Cond: ((a < 20) AND (a = 1)) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: ((a < 20) AND (a = 1)) +(5 rows) + +-- scan rangetab4 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 35 AND a > 25; + QUERY PLAN +----------------------------------------- + Append + -> Seq Scan on rangetab4 + Filter: ((a > 35) AND (a > 25)) +(3 rows) + +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 35 AND a > 20; + QUERY PLAN +----------------------------------------- + Append + -> Seq Scan on rangetab4 + Filter: ((a > 20) AND (a = 35)) +(3 rows) + +-- scan nothing +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 10 AND a < 5; -- mutually refute + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 10 AND a > 15; -- ditto + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 40; -- no partitions beyond 40 + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +-- scan everything +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 1; + QUERY PLAN +-------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab1 + Recheck Cond: (a > 1) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: (a > 1) + -> Bitmap Heap Scan on rangetab2 + Recheck Cond: (a > 1) + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: (a > 1) + -> Bitmap Heap Scan on rangetab3 + Recheck Cond: (a > 1) + -> Bitmap Index Scan on rangetab4_a_idx + Index Cond: (a > 1) + -> Seq Scan on rangetab4 + Filter: (a > 1) +(15 rows) + +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 10 OR a > 1; -- OR clause turns pruning off (for now) + QUERY PLAN +-------------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab1 + Recheck Cond: ((a = 10) OR (a > 1)) + -> BitmapOr + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: (a = 10) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: (a > 1) + -> Bitmap Heap Scan on rangetab2 + Recheck Cond: ((a = 10) OR (a > 1)) + -> BitmapOr + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: (a = 10) + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: (a > 1) + -> Bitmap Heap Scan on rangetab3 + Recheck Cond: ((a = 10) OR (a > 1)) + -> BitmapOr + -> Bitmap Index Scan on rangetab4_a_idx + Index Cond: (a = 10) + -> Bitmap Index Scan on rangetab4_a_idx + Index Cond: (a > 1) + -> Seq Scan on rangetab4 + Filter: ((a = 10) OR (a > 1)) +(24 rows) + +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a IN (1, 11, 21); -- array op turns pruning off (for now) + QUERY PLAN +-------------------------------------------------------------- + Append + -> Bitmap Heap Scan on rangetab1 + Recheck Cond: (a = ANY ('{1,11,21}'::integer[])) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: (a = ANY ('{1,11,21}'::integer[])) + -> Bitmap Heap Scan on rangetab2 + Recheck Cond: (a = ANY ('{1,11,21}'::integer[])) + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: (a = ANY ('{1,11,21}'::integer[])) + -> Bitmap Heap Scan on rangetab3 + Recheck Cond: (a = ANY ('{1,11,21}'::integer[])) + -> Bitmap Index Scan on rangetab4_a_idx + Index Cond: (a = ANY ('{1,11,21}'::integer[])) + -> Seq Scan on rangetab4 + Filter: (a = ANY ('{1,11,21}'::integer[])) +(15 rows) + +CREATE TABLE plaintab (a int, b text); +CREATE INDEX plaintab_a_idx ON plaintab (a); +INSERT INTO plaintab SELECT generate_series(1, 15), 'c'; +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = (SELECT max(a) FROM plaintab); -- only use Consts to prune + QUERY PLAN +--------------------------------------------------------------------------------- + Append + InitPlan 2 (returns $1) + -> Result + InitPlan 1 (returns $0) + -> Limit + -> Index Only Scan Backward using plaintab_a_idx on plaintab + Index Cond: (a IS NOT NULL) + -> Bitmap Heap Scan on rangetab1 + Recheck Cond: (a = $1) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: (a = $1) + -> Bitmap Heap Scan on rangetab2 + Recheck Cond: (a = $1) + -> Bitmap Index Scan on rangetab2_a_idx + Index Cond: (a = $1) + -> Bitmap Heap Scan on rangetab3 + Recheck Cond: (a = $1) + -> Bitmap Index Scan on rangetab4_a_idx + Index Cond: (a = $1) + -> Seq Scan on rangetab4 + Filter: (a = $1) +(21 rows) + +-- might as well check prepared statement +PREPARE x AS SELECT * FROM rangetab WHERE a = $1; +EXPLAIN EXECUTE x(1); -- scan rangetab + QUERY PLAN +------------------------------------------------------------------------------------- + Append (cost=4.26..14.95 rows=13 width=4) + -> Bitmap Heap Scan on rangetab1 (cost=4.26..14.95 rows=13 width=4) + Recheck Cond: (a = 1) + -> Bitmap Index Scan on rangetab1_a_idx (cost=0.00..4.25 rows=13 width=0) + Index Cond: (a = 1) +(5 rows) + +EXPLAIN EXECUTE x(10); -- scan rangetab2 + QUERY PLAN +------------------------------------------------------------------------------------- + Append (cost=4.26..14.95 rows=13 width=4) + -> Bitmap Heap Scan on rangetab2 (cost=4.26..14.95 rows=13 width=4) + Recheck Cond: (a = 10) + -> Bitmap Index Scan on rangetab2_a_idx (cost=0.00..4.25 rows=13 width=0) + Index Cond: (a = 10) +(5 rows) + +DEALLOCATE x; +-- check equivalence processing +EXPLAIN (costs off) SELECT * FROM rangetab t1, plaintab t2 WHERE t1.a = t2.a AND t2.a = 1; -- scan only rangetab1 + QUERY PLAN +-------------------------------------------------------- + Nested Loop + -> Append + -> Bitmap Heap Scan on rangetab1 t1 + Recheck Cond: (a = 1) + -> Bitmap Index Scan on rangetab1_a_idx + Index Cond: (a = 1) + -> Materialize + -> Bitmap Heap Scan on plaintab t2 + Recheck Cond: (a = 1) + -> Bitmap Index Scan on plaintab_a_idx + Index Cond: (a = 1) +(11 rows) + +DROP TABLE rangetab CASCADE; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to partition rangetab1 +drop cascades to partition rangetab2 +drop cascades to partition rangetab3 +drop cascades to partition rangetab4 +-- why not check for list partitioned case, too +CREATE TABLE listtab (a text) PARTITION BY LIST ON (a); +CREATE TABLE listtab1 PARTITION OF listtab FOR VALUES IN ('a', 'b'); +CREATE INDEX listtab1_a_idx ON listtab1 (a); +CREATE TABLE listtab2 PARTITION OF listtab FOR VALUES IN ('c'); +CREATE INDEX listtab2_a_idx ON listtab2 (a); +-- one might think only scans parttab1 - scans everything +EXPLAIN (costs off) SELECT * FROM listtab WHERE a IN ('a', 'b'); -- array op turns off pruning (for now) + QUERY PLAN +------------------------------------------------------- + Append + -> Bitmap Heap Scan on listtab1 + Recheck Cond: (a = ANY ('{a,b}'::text[])) + -> Bitmap Index Scan on listtab1_a_idx + Index Cond: (a = ANY ('{a,b}'::text[])) + -> Bitmap Heap Scan on listtab2 + Recheck Cond: (a = ANY ('{a,b}'::text[])) + -> Bitmap Index Scan on listtab2_a_idx + Index Cond: (a = ANY ('{a,b}'::text[])) +(9 rows) + +EXPLAIN (costs off) SELECT * FROM listtab WHERE a = 'a' OR a = 'b'; -- OR turns off pruning (for now) + QUERY PLAN +------------------------------------------------------------ + Append + -> Bitmap Heap Scan on listtab1 + Recheck Cond: ((a = 'a'::text) OR (a = 'b'::text)) + -> BitmapOr + -> Bitmap Index Scan on listtab1_a_idx + Index Cond: (a = 'a'::text) + -> Bitmap Index Scan on listtab1_a_idx + Index Cond: (a = 'b'::text) + -> Bitmap Heap Scan on listtab2 + Recheck Cond: ((a = 'a'::text) OR (a = 'b'::text)) + -> BitmapOr + -> Bitmap Index Scan on listtab2_a_idx + Index Cond: (a = 'a'::text) + -> Bitmap Index Scan on listtab2_a_idx + Index Cond: (a = 'b'::text) +(15 rows) + +-- scan listtab1 +EXPLAIN (costs off) SELECT * FROM listtab WHERE a = 'b'; + QUERY PLAN +------------------------------------------------- + Append + -> Bitmap Heap Scan on listtab1 + Recheck Cond: (a = 'b'::text) + -> Bitmap Index Scan on listtab1_a_idx + Index Cond: (a = 'b'::text) +(5 rows) + +-- check equivalence processing +EXPLAIN (costs off) SELECT * FROM listtab t1, plaintab t2 WHERE t1.a = t2.b AND t2.b = 'c'; -- scan only listtab2 + QUERY PLAN +------------------------------------------------------- + Nested Loop + -> Append + -> Bitmap Heap Scan on listtab2 t1 + Recheck Cond: (a = 'c'::text) + -> Bitmap Index Scan on listtab2_a_idx + Index Cond: (a = 'c'::text) + -> Materialize + -> Seq Scan on plaintab t2 + Filter: (b = 'c'::text) +(9 rows) + +DROP TABLE plaintab; +DROP TABLE listtab CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to partition listtab1 +drop cascades to partition listtab2 +-- +-- ddl related tests +-- +-- cannot use system columns in partition key +CREATE TABLE fail_oid_pk ( + a int +) PARTITION BY RANGE ON (oid) WITH OIDS; -- fail +ERROR: cannot use system column "oid" in partition key +-- cannot use more than 1 column as partition key for list partitioned table +CREATE TABLE two_col_tab ( + a1 int, + a2 int +) PARTITION BY LIST ON (a1, a2); -- fail +ERROR: cannot use more than one column in partition key +DETAIL: Only one column allowed with list partitioning. +-- cannot use more than 16 columns as partition key for range partitioned table +CREATE TABLE wide_tab ( + a1 int, + a2 int, + a3 int, + a4 int, + a5 int, + a6 int, + a7 int, + a8 int, + a9 int, + a10 int, + a11 int, + a12 int, + a13 int, + a14 int, + a15 int, + a16 int +) PARTITION BY RANGE ON (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16); -- ok +DROP TABLE wide_tab CASCADE; +CREATE TABLE extra_wide_tab ( + a1 int, + a2 int, + a3 int, + a4 int, + a5 int, + a6 int, + a7 int, + a8 int, + a9 int, + a10 int, + a11 int, + a12 int, + a13 int, + a14 int, + a15 int, + a16 int, + a17 int +) PARTITION BY RANGE ON (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17); -- fail +ERROR: cannot use more than 16 columns in partition key +-- At the moment, partitioning requires to use columns with data type having +-- a suitable btree operator class +CREATE TABLE point_tab (a point) PARTITION BY LIST ON (a); -- fail +ERROR: data type point has no default btree operator class +HINT: You must specify an existing btree operator class or define one for the data type. +CREATE TABLE point_tab (a point) PARTITION BY LIST ON (a USING point_ops); -- fail +ERROR: operator class "point_ops" does not exist for access method "btree" +CREATE TABLE point_tab (a point) PARTITION BY RANGE ON (a); -- fail +ERROR: data type point has no default btree operator class +HINT: You must specify an existing btree operator class or define one for the data type. +CREATE TABLE point_tab (a point) PARTITION BY RANGE ON (a USING point_ops); -- fail +ERROR: operator class "point_ops" does not exist for access method "btree" +-- fail to create a partitioned table with PRIMARY KEY constraint +CREATE TABLE pk_fail (a int PRIMARY KEY) PARTITION BY RANGE ON (a); +ERROR: primary key constraints are not supported on partitioned tables +LINE 1: CREATE TABLE pk_fail (a int PRIMARY KEY) PARTITION BY RANGE ... + ^ +-- fail to create a partitioned table with foreign key constraint +CREATE TABLE pk_rel(a int PRIMARY KEY); +CREATE TABLE pk_fail (a int REFERENCES pk_rel(a)) PARTITION BY RANGE ON (a); +ERROR: foreign key constraints are not supported on partitioned tables +LINE 1: CREATE TABLE pk_fail (a int REFERENCES pk_rel(a)) PARTITION ... + ^ +DROP TABLE pk_rel; +-- fail to let a partitioned table be referenced in a foreign key +CREATE TABLE foo (a int) PARTITION BY RANGE ON (a); +CREATE TABLE bar_ref_foo (a int REFERENCES foo(a)); +ERROR: cannot reference relation "foo" +DETAIL: Referencing partitioned tables in foreign key constraints is not supported. +DROP TABLE foo CASCADE; +-- fail to create a partitioned table with prohibited expressions in key +CREATE TABLE fail_const_key (a int) PARTITION BY RANGE ON (('a')); +ERROR: cannot use a constant expression as partition key +CREATE TABLE fail_agg_in_key (a int) PARTITION BY RANGE ON ((avg(a))); +ERROR: aggregate functions are not allowed in partition key expressions +CREATE TABLE fail_window_fun_in_key (a int, b int) PARTITION BY RANGE ON ((avg(a) OVER (PARTITION BY b))); +ERROR: window functions are not allowed in partition key expressions +-- +-- creating partitions with sane FOR VALUES specification +-- +CREATE TABLE range_partitioned ( + a int +) PARTITION BY RANGE ON (a); +CREATE TABLE fail_partition + PARTITION OF range_partitioned + FOR VALUES IN (1, 2); -- fail +ERROR: invalid FOR VALUES specification for a range partition +DROP TABLE range_partitioned CASCADE; +CREATE TABLE list_partitioned ( + a int +) PARTITION BY LIST ON (a); +CREATE TABLE fail_partition + PARTITION OF list_partitioned + FOR VALUES LESS THAN (100); -- fail +ERROR: invalid FOR VALUES specification for a list partition +DROP TABLE list_partitioned CASCADE; +-- +-- creating range partitions sanely +-- +CREATE TABLE sales ( + tos timestamp NOT NULL, + item text +) PARTITION BY RANGE ON (extract(year from tos), extract(month from tos)); +CREATE TABLE sales_before_2015 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 1); -- ok +CREATE TABLE sales_2015_q1 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 4); -- ok +-- effective partition range empty or overlapping +CREATE TABLE sales_2015_q2 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 4); -- fail +ERROR: cannot create range partition with empty range +HINT: Please specify END value that is greater than max bound of the last partition +CREATE TABLE sales_2015_q2 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 3); -- fail +ERROR: cannot create range partition with range overlapping existing partitions +HINT: Please specify END value that is greater than max bound of the last partition +-- more bound values than key columns +CREATE TABLE sales_2015_q2 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 3, 1); -- fail +ERROR: range partition FOR VALUES specification does not match the partition key +DROP TABLE sales CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to partition sales_before_2015 +drop cascades to partition sales_2015_q1 +-- +-- creating list partitions sanely +-- +CREATE TABLE store ( + id int, + city text NOT NULL +) PARTITION BY LIST ON (city); +CREATE TABLE store_east + PARTITION OF store + FOR VALUES IN ('New York', 'Boston'); -- ok +CREATE TABLE store_west + PARTITION OF store + FOR VALUES ('San Fransisco', 'Seattle'); -- ok +-- overlap with some existing partition +CREATE TABLE store_ca + PARTITION OF store + FOR VALUES ('Los Angeles', 'San Fransisco'); -- fail +ERROR: cannot create list partition that overlaps with an existing partition +DETAIL: New partition's definition overlaps with partition "store_west". +DROP TABLE store CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to partition store_east +drop cascades to partition store_west +-- +-- relpersistence of partitioned table and partitions +-- +CREATE TEMP TABLE foo ( + a int +) PARTITION BY RANGE ON (a); +CREATE TEMP TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- ok +-- cannot be non-temp partition of temp table +CREATE TABLE foo_2 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- fail +ERROR: cannot create a non-temporary partition of temporary table "foo" +DROP TABLE foo CASCADE; +NOTICE: drop cascades to partition foo_1 +CREATE UNLOGGED TABLE foo ( + a int +) PARTITION BY RANGE ON (a); +CREATE UNLOGGED TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (100); +-- cannot be logged/permanent partition of unlogged table +CREATE TABLE foo_2 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- fail +ERROR: cannot create a permanent partition of unlogged table "foo" +DROP TABLE foo CASCADE; +NOTICE: drop cascades to partition foo_1 +CREATE TABLE foo ( + a int +) PARTITION BY RANGE ON (a); +-- cannot be temp partition of non-temp table +CREATE TEMP TABLE foo_2 + PARTITION OF foo + FOR VALUES LESS THAN (200); -- fail +ERROR: cannot create a temporary partition of non-temporary table +-- cannot be unlogged partition of permanent table +CREATE UNLOGGED TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- fail +ERROR: cannot create a unlogged partition of permanent table "foo" +DROP TABLE foo CASCADE; +-- +-- ALTER TABLE tests +-- +CREATE TABLE foo ( + a int +) PARTITION BY RANGE ON (a); +CREATE TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (10); +-- cannot add column to a partition +ALTER TABLE foo_1 ADD COLUMN b char; -- fail +ERROR: "foo_1" is not a table, composite type, or foreign table +ALTER TABLE foo ADD COLUMN b char; -- ok +SELECT * FROM foo; + a | b +---+--- +(0 rows) + +-- cannot drop a column from a partition +ALTER TABLE foo_1 DROP COLUMN b; -- fail +ERROR: "foo_1" is not a table, composite type, or foreign table +ALTER TABLE foo DROP COLUMN b; -- ok +SELECT * FROM foo; + a +--- +(0 rows) + +-- cannot drop a partition key column +ALTER TABLE foo DROP COLUMN a; +ERROR: cannot drop partition key column "a" +-- cannot alter NOT NULL constraint of column of a partition +ALTER TABLE foo_1 ALTER a SET NOT NULL; -- fail +ERROR: "foo_1" is not a table or foreign table +ALTER TABLE foo ALTER a SET NOT NULL; -- ok +\d foo_1; + Table "public.foo_1" + Column | Type | Modifiers +--------+---------+----------- + a | integer | not null + +ALTER TABLE foo_1 ALTER a DROP NOT NULL; -- fail +ERROR: "foo_1" is not a table or foreign table +ALTER TABLE foo ALTER a DROP NOT NULL; -- ok +\d foo_1; + Table "public.foo_1" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + +-- cannot alter column default of a partition +ALTER TABLE foo_1 ALTER COLUMN a SET DEFAULT 0; -- fail +ERROR: "foo_1" is not a table, view, or foreign table +ALTER TABLE foo ALTER COLUMN a SET DEFAULT 0; -- ok +INSERT INTO foo DEFAULT VALUES; +SELECT * FROM foo; + a +--- + 0 +(1 row) + +ALTER TABLE foo_1 ALTER COLUMN a DROP DEFAULT; -- fail +ERROR: "foo_1" is not a table, view, or foreign table +ALTER TABLE foo ALTER COLUMN a DROP DEFAULT; -- ok +INSERT INTO foo DEFAULT VALUES; -- fail (null default) +ERROR: null value for partition key column "a" not allowed +-- cannot alter column type +ALTER TABLE foo ADD COLUMN b text; +INSERT INTO foo VALUES (9, 'ab'); +ALTER TABLE foo_1 ALTER b TYPE char; -- fail +ERROR: "foo_1" is not a table, composite type, or foreign table +ALTER TABLE foo ALTER b TYPE char; -- fail (found values too long) +ERROR: value too long for type character(1) +UPDATE foo SET b = 'a' WHERE length(b) > 1; +ALTER TABLE foo ALTER b TYPE char; -- ok +SELECT * FROM foo; + a | b +---+--- + 0 | + 9 | a +(2 rows) + +-- cannot alter type of a partition key column +ALTER TABLE foo ALTER a TYPE char; +ERROR: cannot alter type of partition key column "a" +-- cannot add/drop a table constraint on a partition +ALTER TABLE foo_1 ADD CONSTRAINT foo_chk_a CHECK (a >= 0); -- fail +ERROR: "foo_1" is not a table or foreign table +ALTER TABLE foo ADD CONSTRAINT foo_chk_a CHECK (a >= 0); -- ok +INSERT INTO foo VALUES (-1); -- fails +ERROR: new row for relation "foo" violates check constraint "foo_chk_a" +DETAIL: Failing row contains (-1, null). +INSERT INTO foo VALUES (0); +UPDATE foo SET a = -1 WHERE = a = 0 -- fails +-- cannot change persistence of a partition +ALTER TABLE foo_1 SET UNLOGGED; -- fail +ERROR: syntax error at or near "=" +LINE 1: UPDATE foo SET a = -1 WHERE = a = 0 + ^ +ALTER TABLE foo SET UNLOGGED; -- ok +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | + +-- +-- OTOH, there are some AT commands that are OK to do on partitions +-- +-- can validate a constraint +ALTER TABLE foo DROP CONSTRAINT foo_chk_a; +INSERT INTO foo VALUES (-1); +ALTER TABLE foo ADD CONSTRAINT foo_chk_a CHECK (a >= 0) NOT VALID; +ALTER TABLE foo_1 VALIDATE CONSTRAINT foo_chk_a; -- fail +ERROR: check constraint "foo_chk_a" is violated by some row +DELETE FROM foo WHERE a = -1; +ALTER TABLE foo_1 VALIDATE CONSTRAINT foo_chk_a; -- ok +-- or validate it on the whole partitioned table +ALTER TABLE foo DROP CONSTRAINT foo_chk_a; +INSERT INTO foo VALUES (-1); -- ok +ALTER TABLE foo ADD CONSTRAINT foo_chk_a CHECK (a >= 0) NOT VALID; +ALTER TABLE foo VALIDATE CONSTRAINT foo_chk_a; -- fail +ERROR: check constraint "foo_chk_a" is violated by some row +DELETE FROM foo WHERE a = -1; +ALTER TABLE foo VALIDATE CONSTRAINT foo_chk_a; -- ok +-- can set/reset attribute options +ALTER TABLE foo_1 ALTER a SET (n_distinct=10); -- ok +-- can set attribute storage +ALTER TABLE foo ADD COLUMN b text; +ERROR: column "b" of relation "foo" already exists +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | + +ALTER TABLE foo_1 ALTER b SET STORAGE EXTERNAL; +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | + +-- can create index-creating constraint +DELETE FROM foo; +ALTER TABLE foo_1 ADD UNIQUE (a); +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | +Indexes: + "foo_1_a_key" UNIQUE CONSTRAINT, btree (a) + +-- can create a constraint using index +CREATE INDEX foo_1_b_idx ON foo_1 (b); +ALTER TABLE foo_1 ADD UNIQUE USING foo_1_b_idx; +ERROR: syntax error at or near "foo_1_b_idx" +LINE 1: ALTER TABLE foo_1 ADD UNIQUE USING foo_1_b_idx; + ^ +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | +Indexes: + "foo_1_a_key" UNIQUE CONSTRAINT, btree (a) + "foo_1_b_idx" btree (b) + +-- can drop index constraints (others cannot be created at all) +ALTER TABLE foo_1 DROP CONSTRAINT foo_1_a_key; +-- can set/reset CLUSTER ON +ALTER TABLE foo_1 CLUSTER ON foo_1_a_idx; +ERROR: index "foo_1_a_idx" for table "foo_1" does not exist +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | +Indexes: + "foo_1_b_idx" btree (b) + +ALTER TABLE foo_1 SET WITHOUT CLUSTER; +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | +Indexes: + "foo_1_b_idx" btree (b) + +-- can set/reset table storage options +ALTER TABLE foo_1 SET (fillfactor=80) +\d+ foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers | Storage | Stats target | Description +--------+--------------+-----------+----------+--------------+------------- + a | integer | | plain | | + b | character(1) | | external | | +Indexes: + "foo_1_b_idx" btree (b) + +ALTER TABLE foo_1 RESET (fillfactor) +\d+ foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers | Storage | Stats target | Description +--------+--------------+-----------+----------+--------------+------------- + a | integer | | plain | | + b | character(1) | | external | | +Indexes: + "foo_1_b_idx" btree (b) + +-- can set replica identity +ALTER TABLE foo ALTER a SET NOT NULL; +ERROR: syntax error at or near "ALTER" +LINE 2: ALTER TABLE foo_1 RESET (fillfactor) + ^ +ALTER TABLE foo_1 REPLICA IDENTITY USING INDEX foo_1_a_idx; +ERROR: index "foo_1_a_idx" for table "foo_1" does not exist +\d foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers +--------+--------------+----------- + a | integer | + b | character(1) | +Indexes: + "foo_1_b_idx" btree (b) + +ALTER TABLE foo_1 REPLICA IDENTITY NOTHING; +\d+ foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers | Storage | Stats target | Description +--------+--------------+-----------+----------+--------------+------------- + a | integer | | plain | | + b | character(1) | | external | | +Indexes: + "foo_1_b_idx" btree (b) +Replica Identity: NOTHING + +-- can set statistics on a column +ALTER TABLE foo_1 ALTER a SET STATISTICS 1; +\d+ foo_1 + Unlogged table "public.foo_1" + Column | Type | Modifiers | Storage | Stats target | Description +--------+--------------+-----------+----------+--------------+------------- + a | integer | | plain | 1 | + b | character(1) | | external | | +Indexes: + "foo_1_b_idx" btree (b) +Replica Identity: NOTHING + +-- +-- some more tests +-- +-- cannot drop paritions +DROP TABLE foo_1; -- fail +ERROR: cannot drop table "foo_1" +DETAIL: "foo_1" is a partition of table "foo". +HINT: You will need to detach the partition first. +-- detach partition +ALTER TABLE foo DETACH PARTITION foo_1 USING foo_1_table; +-- now drop 'table' foo_1_table +DROP TABLE foo_1_table; -- ok +-- feature not implemented message +ALTER TABLE foo ATTACH PARTITION foo_1 FOR VALUES LESS THAN (10) USING TABLE foo_1_table; +ERROR: ALTER TABLE ATTACH PARTITION (...) USING TABLE not implemented +DROP TABLE foo CASCADE; +-- +-- some commands are not ready for either partitioned tables or partitions +-- +CREATE TABLE command_fail_tab (a int) PARTITION BY RANGE ON (a); +CREATE TABLE command_fail_partition PARTITION OF command_fail_tab FOR VALUES LESS THAN (10); +VACUUM command_fail_tab; -- fail +ERROR: cannot vacuum partitioned table "command_fail_tab" +HINT: Run VACUUM on individual partitions one-by-one. +CLUSTER command_fail_tab; -- fail +ERROR: cannot cluster partitioned table "command_fail_tab" +HINT: Run CLUSTER on individual partitions one-by-one. +REINDEX TABLE command_fail_tab; -- fail +ERROR: cannot reindex partitioned table "command_fail_tab" +HINT: Run REINDEX on individual partitions one-by-one. +ANALYZE command_fail_tab; -- fail +ERROR: cannot analyze partitioned table "command_fail_tab" +DETAIL: ANALYZE on partitioned tables is not implemented yet. +HINT: ANALYZE individual partitions separately. +COPY command_fail_tab TO stdout; -- fail; +ERROR: cannot copy from partitioned table "command_fail_tab" +HINT: Try the COPY (SELECT ...) TO variant. +COPY command_fail_partition FROM stdin; -- fail +ERROR: cannot copy to a partition of another table +HINT: Perform COPY on the parent instead. +DROP TABLE command_fail_tab CASCADE; +NOTICE: drop cascades to partition command_fail_partition diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index c63abf4..38d78b8 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -60,7 +60,7 @@ test: create_index create_view # ---------- # Another group of parallel tests # ---------- -test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes +test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes partitioned_table # ---------- # sanity_check does a vacuum, affecting the sort order of SELECT * diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 88dcd64..b98865e 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -159,3 +159,4 @@ test: with test: xml test: event_trigger test: stats +test: partitioned_table diff --git a/src/test/regress/sql/partitioned_table.sql b/src/test/regress/sql/partitioned_table.sql new file mode 100644 index 0000000..e58e427 --- /dev/null +++ b/src/test/regress/sql/partitioned_table.sql @@ -0,0 +1,551 @@ +-- +-- create a range partitioned table +-- +CREATE TABLE person_partitioned ( + name text, + age int, + city text +) PARTITION BY RANGE ON (age); + +-- select (empty result yet) +EXPLAIN (costs off) SELECT * FROM person_partitioned; +SELECT * FROM person_partitioned; + +-- cannot insert data into it without creating a partition +INSERT INTO person_partitioned VALUES ('Bob', 23, 'Boston'); -- fail + +-- create a couple of range partitions +CREATE TABLE person_partitioned_30 + PARTITION OF person_partitioned + FOR VALUES LESS THAN (30); +CREATE TABLE person_partitioned_50 + PARTITION OF person_partitioned + FOR VALUES LESS THAN (50); + +INSERT INTO person_partitioned VALUES ('Bob', 23, 'Boston'); -- ok +INSERT INTO person_partitioned VALUES ('Alice', 32, 'Austin'); -- ok +INSERT INTO person_partitioned VALUES ('Jon', 52, 'Seattle'); -- fail + +-- select (non-empty result) +EXPLAIN (costs off) SELECT * FROM person_partitioned; +SELECT * FROM person_partitioned; + +-- no partition selected +EXPLAIN (costs off) SELECT * FROM person_partitioned WHERE age > 50; +SELECT * FROM person_partitioned WHERE age > 50; + +-- one partition selected +EXPLAIN (costs off) SELECT * FROM person_partitioned WHERE age < 25; +SELECT * FROM person_partitioned WHERE age < 25; + +-- cannot update partition directly +UPDATE person_partitioned_50 SET city = 'Dallas' WHERE name = 'Alice'; + +-- so update parent +EXPLAIN (costs off) UPDATE person_partitioned SET city = 'Dallas' WHERE name = 'Alice'; +UPDATE person_partitioned SET city = 'Dallas' WHERE name = 'Alice'; + +-- following update changes the value of partition key +UPDATE person_partitioned SET age = 52 WHERE name = 'Alice'; -- fail +UPDATE person_partitioned SET age = 42 WHERE name = 'Alice'; -- ok + +-- +-- create a list partitioend table +-- +CREATE TABLE city_partitioned ( + name text +) PARTITION BY LIST ON (name); + +-- select (empty result yet) +EXPLAIN (costs off) SELECT * FROM city_partitioned; +SELECT * FROM city_partitioned; + +-- cannot insert data into it without creating a partition +INSERT INTO city_partitioned VALUES ('Boston'); -- fail + +-- create a couple of list partitions +CREATE TABLE city_east + PARTITION OF city_partitioned + FOR VALUES IN ('New York', 'Boston'); +CREATE TABLE city_west + PARTITION OF city_partitioned + FOR VALUES IN ('Los Angeles'); + +INSERT INTO city_partitioned VALUES ('Boston'); -- ok +INSERT INTO city_partitioned VALUES ('Austin'); -- fail + +-- select (non-empty result) +EXPLAIN (costs off) SELECT * FROM city_partitioned; +SELECT * FROM city_partitioned; + +-- no partition selected +EXPLAIN (costs off) SELECT * FROM city_partitioned WHERE name = 'Austin'; +SELECT * FROM city_partitioned WHERE name = 'Austin'; + +-- one partition selected +EXPLAIN (costs off) SELECT * FROM city_partitioned WHERE name = 'Boston'; +SELECT * FROM city_partitioned WHERE name = 'Boston'; + +-- can delete from partition directly +DELETE FROM city_east WHERE name = 'Boston'; -- ok + +-- or from parent +DELETE FROM city_east WHERE name = 'Boston'; -- 0 deleted + +-- dropping partitioned table requires CASCADE if partitions exist +DROP TABLE person_partitioned; -- fail +DROP TABLE person_partitioned CASCADE; -- ok +DROP TABLE city_partitioned CASCADE; + + +-- +-- test some planner bits +-- +CREATE TABLE rangetab (a int) PARTITION BY RANGE ON (a); +CREATE TABLE rangetab1 PARTITION OF rangetab FOR VALUES LESS THAN (10); +CREATE INDEX rangetab1_a_idx ON rangetab1 (a); +CREATE TABLE rangetab2 PARTITION OF rangetab FOR VALUES LESS THAN (20); +CREATE INDEX rangetab2_a_idx ON rangetab2 (a); +CREATE TABLE rangetab3 PARTITION OF rangetab FOR VALUES LESS THAN (30); +CREATE INDEX rangetab3_a_idx ON rangetab3 (a); +CREATE TABLE rangetab4 PARTITION OF rangetab FOR VALUES LESS THAN (40); +CREATE INDEX rangetab4_a_idx ON rangetab3 (a); +INSERT INTO rangetab SELECT generate_series(1, 39); + +-- scan rangetab2 and rangetab3 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a BETWEEN 15 AND 25; + +-- scan rangetab1 and rangetab2 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a <= 10; + +-- scan rangetab2, rangetab3, rangetab4 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 10; + +-- scan rangetab1 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a < 10 AND a < 20; +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 1 AND a < 20; + +-- scan rangetab4 +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 35 AND a > 25; +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 35 AND a > 20; + +-- scan nothing +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 10 AND a < 5; -- mutually refute +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 10 AND a > 15; -- ditto +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 40; -- no partitions beyond 40 + +-- scan everything +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a > 1; +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = 10 OR a > 1; -- OR clause turns pruning off (for now) +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a IN (1, 11, 21); -- array op turns pruning off (for now) + +CREATE TABLE plaintab (a int, b text); +CREATE INDEX plaintab_a_idx ON plaintab (a); +INSERT INTO plaintab SELECT generate_series(1, 15), 'c'; + +EXPLAIN (costs off) SELECT * FROM rangetab WHERE a = (SELECT max(a) FROM plaintab); -- only use Consts to prune + +-- might as well check prepared statement +PREPARE x AS SELECT * FROM rangetab WHERE a = $1; +EXPLAIN EXECUTE x(1); -- scan rangetab +EXPLAIN EXECUTE x(10); -- scan rangetab2 +DEALLOCATE x; + +-- check equivalence processing +EXPLAIN (costs off) SELECT * FROM rangetab t1, plaintab t2 WHERE t1.a = t2.a AND t2.a = 1; -- scan only rangetab1 + +DROP TABLE rangetab CASCADE; + +-- why not check for list partitioned case, too +CREATE TABLE listtab (a text) PARTITION BY LIST ON (a); +CREATE TABLE listtab1 PARTITION OF listtab FOR VALUES IN ('a', 'b'); +CREATE INDEX listtab1_a_idx ON listtab1 (a); +CREATE TABLE listtab2 PARTITION OF listtab FOR VALUES IN ('c'); +CREATE INDEX listtab2_a_idx ON listtab2 (a); + +-- one might think only scans parttab1 - scans everything +EXPLAIN (costs off) SELECT * FROM listtab WHERE a IN ('a', 'b'); -- array op turns off pruning (for now) +EXPLAIN (costs off) SELECT * FROM listtab WHERE a = 'a' OR a = 'b'; -- OR turns off pruning (for now) + +-- scan listtab1 +EXPLAIN (costs off) SELECT * FROM listtab WHERE a = 'b'; + +-- check equivalence processing +EXPLAIN (costs off) SELECT * FROM listtab t1, plaintab t2 WHERE t1.a = t2.b AND t2.b = 'c'; -- scan only listtab2 + +DROP TABLE plaintab; +DROP TABLE listtab CASCADE; + +-- +-- ddl related tests +-- + +-- cannot use system columns in partition key +CREATE TABLE fail_oid_pk ( + a int +) PARTITION BY RANGE ON (oid) WITH OIDS; -- fail + +-- cannot use more than 1 column as partition key for list partitioned table +CREATE TABLE two_col_tab ( + a1 int, + a2 int +) PARTITION BY LIST ON (a1, a2); -- fail + +-- cannot use more than 16 columns as partition key for range partitioned table +CREATE TABLE wide_tab ( + a1 int, + a2 int, + a3 int, + a4 int, + a5 int, + a6 int, + a7 int, + a8 int, + a9 int, + a10 int, + a11 int, + a12 int, + a13 int, + a14 int, + a15 int, + a16 int +) PARTITION BY RANGE ON (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16); -- ok +DROP TABLE wide_tab CASCADE; + +CREATE TABLE extra_wide_tab ( + a1 int, + a2 int, + a3 int, + a4 int, + a5 int, + a6 int, + a7 int, + a8 int, + a9 int, + a10 int, + a11 int, + a12 int, + a13 int, + a14 int, + a15 int, + a16 int, + a17 int +) PARTITION BY RANGE ON (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17); -- fail + +-- At the moment, partitioning requires to use columns with data type having +-- a suitable btree operator class +CREATE TABLE point_tab (a point) PARTITION BY LIST ON (a); -- fail +CREATE TABLE point_tab (a point) PARTITION BY LIST ON (a USING point_ops); -- fail +CREATE TABLE point_tab (a point) PARTITION BY RANGE ON (a); -- fail +CREATE TABLE point_tab (a point) PARTITION BY RANGE ON (a USING point_ops); -- fail + +-- fail to create a partitioned table with PRIMARY KEY constraint +CREATE TABLE pk_fail (a int PRIMARY KEY) PARTITION BY RANGE ON (a); + +-- fail to create a partitioned table with foreign key constraint +CREATE TABLE pk_rel(a int PRIMARY KEY); +CREATE TABLE pk_fail (a int REFERENCES pk_rel(a)) PARTITION BY RANGE ON (a); +DROP TABLE pk_rel; + +-- fail to let a partitioned table be referenced in a foreign key +CREATE TABLE foo (a int) PARTITION BY RANGE ON (a); +CREATE TABLE bar_ref_foo (a int REFERENCES foo(a)); +DROP TABLE foo CASCADE; + +-- fail to create a partitioned table with prohibited expressions in key +CREATE TABLE fail_const_key (a int) PARTITION BY RANGE ON (('a')); +CREATE TABLE fail_agg_in_key (a int) PARTITION BY RANGE ON ((avg(a))); +CREATE TABLE fail_window_fun_in_key (a int, b int) PARTITION BY RANGE ON ((avg(a) OVER (PARTITION BY b))); + +-- +-- creating partitions with sane FOR VALUES specification +-- +CREATE TABLE range_partitioned ( + a int +) PARTITION BY RANGE ON (a); + +CREATE TABLE fail_partition + PARTITION OF range_partitioned + FOR VALUES IN (1, 2); -- fail +DROP TABLE range_partitioned CASCADE; + +CREATE TABLE list_partitioned ( + a int +) PARTITION BY LIST ON (a); + +CREATE TABLE fail_partition + PARTITION OF list_partitioned + FOR VALUES LESS THAN (100); -- fail +DROP TABLE list_partitioned CASCADE; + +-- +-- creating range partitions sanely +-- +CREATE TABLE sales ( + tos timestamp NOT NULL, + item text +) PARTITION BY RANGE ON (extract(year from tos), extract(month from tos)); + +CREATE TABLE sales_before_2015 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 1); -- ok + +CREATE TABLE sales_2015_q1 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 4); -- ok + +-- effective partition range empty or overlapping +CREATE TABLE sales_2015_q2 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 4); -- fail +CREATE TABLE sales_2015_q2 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 3); -- fail + +-- more bound values than key columns +CREATE TABLE sales_2015_q2 + PARTITION OF sales + FOR VALUES LESS THAN (2015, 3, 1); -- fail +DROP TABLE sales CASCADE; + +-- +-- creating list partitions sanely +-- +CREATE TABLE store ( + id int, + city text NOT NULL +) PARTITION BY LIST ON (city); + +CREATE TABLE store_east + PARTITION OF store + FOR VALUES IN ('New York', 'Boston'); -- ok + +CREATE TABLE store_west + PARTITION OF store + FOR VALUES ('San Fransisco', 'Seattle'); -- ok + +-- overlap with some existing partition +CREATE TABLE store_ca + PARTITION OF store + FOR VALUES ('Los Angeles', 'San Fransisco'); -- fail + +DROP TABLE store CASCADE; + +-- +-- relpersistence of partitioned table and partitions +-- +CREATE TEMP TABLE foo ( + a int +) PARTITION BY RANGE ON (a); + +CREATE TEMP TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- ok + +-- cannot be non-temp partition of temp table +CREATE TABLE foo_2 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- fail +DROP TABLE foo CASCADE; + +CREATE UNLOGGED TABLE foo ( + a int +) PARTITION BY RANGE ON (a); + +CREATE UNLOGGED TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (100); + +-- cannot be logged/permanent partition of unlogged table +CREATE TABLE foo_2 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- fail +DROP TABLE foo CASCADE; + +CREATE TABLE foo ( + a int +) PARTITION BY RANGE ON (a); + +-- cannot be temp partition of non-temp table +CREATE TEMP TABLE foo_2 + PARTITION OF foo + FOR VALUES LESS THAN (200); -- fail + +-- cannot be unlogged partition of permanent table +CREATE UNLOGGED TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (100); -- fail +DROP TABLE foo CASCADE; + +-- +-- ALTER TABLE tests +-- +CREATE TABLE foo ( + a int +) PARTITION BY RANGE ON (a); + +CREATE TABLE foo_1 + PARTITION OF foo + FOR VALUES LESS THAN (10); + +-- cannot add column to a partition +ALTER TABLE foo_1 ADD COLUMN b char; -- fail +ALTER TABLE foo ADD COLUMN b char; -- ok +SELECT * FROM foo; + +-- cannot drop a column from a partition +ALTER TABLE foo_1 DROP COLUMN b; -- fail +ALTER TABLE foo DROP COLUMN b; -- ok +SELECT * FROM foo; + +-- cannot drop a partition key column +ALTER TABLE foo DROP COLUMN a; + +-- cannot alter NOT NULL constraint of column of a partition +ALTER TABLE foo_1 ALTER a SET NOT NULL; -- fail +ALTER TABLE foo ALTER a SET NOT NULL; -- ok +\d foo_1; + +ALTER TABLE foo_1 ALTER a DROP NOT NULL; -- fail +ALTER TABLE foo ALTER a DROP NOT NULL; -- ok +\d foo_1; + +-- cannot alter column default of a partition +ALTER TABLE foo_1 ALTER COLUMN a SET DEFAULT 0; -- fail +ALTER TABLE foo ALTER COLUMN a SET DEFAULT 0; -- ok + +INSERT INTO foo DEFAULT VALUES; +SELECT * FROM foo; + +ALTER TABLE foo_1 ALTER COLUMN a DROP DEFAULT; -- fail +ALTER TABLE foo ALTER COLUMN a DROP DEFAULT; -- ok + +INSERT INTO foo DEFAULT VALUES; -- fail (null default) + +-- cannot alter column type +ALTER TABLE foo ADD COLUMN b text; +INSERT INTO foo VALUES (9, 'ab'); +ALTER TABLE foo_1 ALTER b TYPE char; -- fail +ALTER TABLE foo ALTER b TYPE char; -- fail (found values too long) +UPDATE foo SET b = 'a' WHERE length(b) > 1; +ALTER TABLE foo ALTER b TYPE char; -- ok +SELECT * FROM foo; + +-- cannot alter type of a partition key column +ALTER TABLE foo ALTER a TYPE char; + +-- cannot add/drop a table constraint on a partition +ALTER TABLE foo_1 ADD CONSTRAINT foo_chk_a CHECK (a >= 0); -- fail +ALTER TABLE foo ADD CONSTRAINT foo_chk_a CHECK (a >= 0); -- ok + +INSERT INTO foo VALUES (-1); -- fails +INSERT INTO foo VALUES (0); +UPDATE foo SET a = -1 WHERE = a = 0 -- fails + +-- cannot change persistence of a partition +ALTER TABLE foo_1 SET UNLOGGED; -- fail +ALTER TABLE foo SET UNLOGGED; -- ok +\d foo_1 + +-- +-- OTOH, there are some AT commands that are OK to do on partitions +-- + +-- can validate a constraint +ALTER TABLE foo DROP CONSTRAINT foo_chk_a; +INSERT INTO foo VALUES (-1); +ALTER TABLE foo ADD CONSTRAINT foo_chk_a CHECK (a >= 0) NOT VALID; +ALTER TABLE foo_1 VALIDATE CONSTRAINT foo_chk_a; -- fail +DELETE FROM foo WHERE a = -1; +ALTER TABLE foo_1 VALIDATE CONSTRAINT foo_chk_a; -- ok + +-- or validate it on the whole partitioned table +ALTER TABLE foo DROP CONSTRAINT foo_chk_a; +INSERT INTO foo VALUES (-1); -- ok +ALTER TABLE foo ADD CONSTRAINT foo_chk_a CHECK (a >= 0) NOT VALID; +ALTER TABLE foo VALIDATE CONSTRAINT foo_chk_a; -- fail +DELETE FROM foo WHERE a = -1; +ALTER TABLE foo VALIDATE CONSTRAINT foo_chk_a; -- ok + +-- can set/reset attribute options +ALTER TABLE foo_1 ALTER a SET (n_distinct=10); -- ok + +-- can set attribute storage +ALTER TABLE foo ADD COLUMN b text; +\d foo_1 +ALTER TABLE foo_1 ALTER b SET STORAGE EXTERNAL; +\d foo_1 + +-- can create index-creating constraint +DELETE FROM foo; +ALTER TABLE foo_1 ADD UNIQUE (a); +\d foo_1 + +-- can create a constraint using index +CREATE INDEX foo_1_b_idx ON foo_1 (b); +ALTER TABLE foo_1 ADD UNIQUE USING foo_1_b_idx; +\d foo_1 + +-- can drop index constraints (others cannot be created at all) +ALTER TABLE foo_1 DROP CONSTRAINT foo_1_a_key; + +-- can set/reset CLUSTER ON +ALTER TABLE foo_1 CLUSTER ON foo_1_a_idx; +\d foo_1 +ALTER TABLE foo_1 SET WITHOUT CLUSTER; +\d foo_1 + +-- can set/reset table storage options +ALTER TABLE foo_1 SET (fillfactor=80) +\d+ foo_1 +ALTER TABLE foo_1 RESET (fillfactor) +\d+ foo_1 + +-- can set replica identity +ALTER TABLE foo ALTER a SET NOT NULL; +ALTER TABLE foo_1 REPLICA IDENTITY USING INDEX foo_1_a_idx; +\d foo_1 + +ALTER TABLE foo_1 REPLICA IDENTITY NOTHING; +\d+ foo_1 + +-- can set statistics on a column +ALTER TABLE foo_1 ALTER a SET STATISTICS 1; +\d+ foo_1 + +-- +-- some more tests +-- + +-- cannot drop paritions +DROP TABLE foo_1; -- fail + +-- detach partition +ALTER TABLE foo DETACH PARTITION foo_1 USING foo_1_table; + +-- now drop 'table' foo_1_table +DROP TABLE foo_1_table; -- ok + +-- feature not implemented message +ALTER TABLE foo ATTACH PARTITION foo_1 FOR VALUES LESS THAN (10) USING TABLE foo_1_table; + +DROP TABLE foo CASCADE; + +-- +-- some commands are not ready for either partitioned tables or partitions +-- +CREATE TABLE command_fail_tab (a int) PARTITION BY RANGE ON (a); +CREATE TABLE command_fail_partition PARTITION OF command_fail_tab FOR VALUES LESS THAN (10); + +VACUUM command_fail_tab; -- fail +CLUSTER command_fail_tab; -- fail +REINDEX TABLE command_fail_tab; -- fail +ANALYZE command_fail_tab; -- fail + +COPY command_fail_tab TO stdout; -- fail; +COPY command_fail_partition FROM stdin; -- fail +1 +2 +\. + +DROP TABLE command_fail_tab CASCADE; --------------090507020209010101040805 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------090507020209010101040805--