diff -cprN head/doc/src/sgml/catalogs.sgml work/doc/src/sgml/catalogs.sgml
*** head/doc/src/sgml/catalogs.sgml	2010-01-18 09:26:23.915594000 +0900
--- work/doc/src/sgml/catalogs.sgml	2010-01-18 14:38:33.968374454 +0900
***************
*** 194,199 ****
--- 194,204 ----
       </row>
  
       <row>
+       <entry><link linkend="catalog-pg-partition"><structname>pg_partition</structname></link></entry>
+       <entry>key definitions for partitioned tables</entry>
+      </row>
+ 
+      <row>
        <entry><link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link></entry>
        <entry>template data for procedural languages</entry>
       </row>
***************
*** 2996,3001 ****
--- 3001,3017 ----
         inherited columns are to be arranged.  The count starts at 1
        </entry>
       </row>
+ 
+      <row>
+       <entry><structfield>inhvalues</structfield></entry>
+       <entry><type>anyarray</type></entry>
+       <entry></entry>
+       <entry>
+        An array of partition values.
+        An empty array for overflow partitions.
+        NULL for non-partitioned tables.
+       </entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
***************
*** 3705,3710 ****
--- 3721,3787 ----
   </sect1>
  
  
+  <sect1 id="catalog-pg-partition">
+   <title><structname>pg_partition</structname></title>
+ 
+   <indexterm zone="catalog-pg-partition">
+    <primary>pg_partition</primary>
+   </indexterm>
+ 
+   <para>
+    The catalog <structname>pg_partition</structname> stores
+    <quote>key definitions</> for partitioned tables.
+   </para>
+ 
+   <table>
+    <title><structname>pg_partition</> Columns</title>
+ 
+    <tgroup cols="4">
+     <thead>
+      <row>
+       <entry>Name</entry>
+       <entry>Type</entry>
+       <entry>References</entry>
+       <entry>Description</entry>
+      </row>
+     </thead>
+ 
+     <tbody>
+      <row>
+       <entry><structfield>partrelid</structfield></entry>
+       <entry><type>oid</type></entry>
+       <entry>pg_class.oid</entry>
+       <entry>Partitioned table oid</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>partopclass</structfield></entry>
+       <entry><type>oid</type></entry>
+       <entry>pg_opclass.oid</entry>
+       <entry>Operator class to compare keys</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>partkind</structfield></entry>
+       <entry><type>char</type></entry>
+       <entry></entry>
+       <entry>Kind of partition: RANGE or LIST</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>partkey</structfield></entry>
+       <entry><type>text</type></entry>
+       <entry></entry>
+       <entry>Partition key expression</entry>
+      </row>
+ 
+     </tbody>
+    </tgroup>
+   </table>
+ 
+  </sect1>
+ 
+ 
   <sect1 id="catalog-pg-pltemplate">
    <title><structname>pg_pltemplate</structname></title>
  
diff -cprN head/doc/src/sgml/ddl.sgml work/doc/src/sgml/ddl.sgml
*** head/doc/src/sgml/ddl.sgml	2009-10-26 09:29:38.916447000 +0900
--- work/doc/src/sgml/ddl.sgml	2010-01-18 17:41:52.684540000 +0900
*************** VALUES ('New York', NULL, NULL, 'NY');
*** 2310,2315 ****
--- 2310,2321 ----
          example one might partition by date ranges, or by ranges of
          identifiers for particular business objects.
         </para>
+        <para>
+         The syntax for range partitioning is:
+ <programlisting>
+ PARTITION BY RANGE ( partitioning_key )
+ </programlisting>
+        </para>
        </listitem>
       </varlistentry>
  
*************** VALUES ('New York', NULL, NULL, 'NY');
*** 2321,2326 ****
--- 2327,2338 ----
          The table is partitioned by explicitly listing which key values
          appear in each partition.
         </para>
+        <para>
+         The syntax for list partitioning is:
+ <programlisting>
+ PARTITION BY LIST ( partitioning_key )
+ </programlisting>
+        </para>
        </listitem>
       </varlistentry>
      </variablelist>
*************** VALUES ('New York', NULL, NULL, 'NY');
*** 2341,2404 ****
         <para>
          This table will contain no data.  Do not define any check
          constraints on this table, unless you intend them to
!         be applied equally to all partitions.  There is no point
!         in defining any indexes or unique constraints on it, either.
         </para>
-       </listitem>
- 
-       <listitem>
         <para>
!         Create several <quote>child</quote> tables that each inherit from
!         the master table.  Normally, these tables will not add any columns
!         to the set inherited from the master.
         </para>
  
         <para>
!         We will refer to the child tables as partitions, though they
!         are in every way normal <productname>PostgreSQL</> tables.
         </para>
        </listitem>
  
        <listitem>
         <para>
!         Add table constraints to the partition tables to define the
!         allowed key values in each partition.
         </para>
  
         <para>
          Typical examples would be:
  <programlisting>
! CHECK ( x = 1 )
! CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
! CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
  </programlisting>
          Ensure that the constraints guarantee that there is no overlap
!         between the key values permitted in different partitions.  A common
!         mistake is to set up range constraints like:
! <programlisting>
! CHECK ( outletID BETWEEN 100 AND 200 )
! CHECK ( outletID BETWEEN 200 AND 300 )
! </programlisting>
!         This is wrong since it is not clear which partition the key value
!         200 belongs in.
         </para>
  
         <para>
!         Note that there is no difference in
!         syntax between range and list partitioning; those terms are
!         descriptive only.
         </para>
-       </listitem>
  
-       <listitem>
-        <para>
-         For each partition, create an index on the key column(s),
-         as well as any other indexes you might want.  (The key index is
-         not strictly necessary, but in most scenarios it is helpful.
-         If you intend the key values to be unique then you should
-         always create a unique or primary-key constraint for each
-         partition.)
-        </para>
        </listitem>
  
        <listitem>
--- 2353,2402 ----
         <para>
          This table will contain no data.  Do not define any check
          constraints on this table, unless you intend them to
!         be applied equally to all partitions.
         </para>
         <para>
!         Typical examples would be:
! <programlisting>
! CREATE TABLE address (...) PARTITION BY LIST ( county );
! CREATE TABLE outlet (...) PARTITION BY RANGE ( outletID );
! </programlisting>
         </para>
+       </listitem>
  
+       <listitem>
         <para>
!         Create an index on the key column(s) on the master table,
!         as well as any other indexes you might want.  (The key index is
!         not strictly necessary, but in most scenarios it is helpful.
!         If you intend the key values to be unique then you should
!         always create a unique or primary-key constraint on it.)
         </para>
        </listitem>
  
        <listitem>
         <para>
!         Create several <quote>child</quote> tables that each inherit from
!         the master table with table constraints to define the allowed key
!         values in each partition.  Normally, these tables will not add any
!         columns to the set inherited from the master.
         </para>
  
         <para>
          Typical examples would be:
  <programlisting>
! CREATE PARTITION address_ON address VALUES IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' );
! CREATE PARTITION outlet_100 ON outlet VALUES LESS THAN 200;
  </programlisting>
          Ensure that the constraints guarantee that there is no overlap
!         between the key values permitted in different partitions.
         </para>
  
         <para>
!         We will refer to the child tables as partitions, though they
!         are in every way normal <productname>PostgreSQL</> tables.
         </para>
  
        </listitem>
  
        <listitem>
*************** CREATE TABLE measurement (
*** 2460,2479 ****
  
        <listitem>
         <para>
          Next we create one partition for each active month:
  
  <programlisting>
! CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
! CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
  ...
! CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
! CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
! CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
  </programlisting>
  
          Each of the partitions are complete tables in their own right,
          but they inherit their definitions from the
!         <structname>measurement</> table.
         </para>
  
         <para>
--- 2458,2490 ----
  
        <listitem>
         <para>
+         Define a partition key to the master table and create an index on
+         the key column. If you need further indexes for each partition,
+         add them at this time.
+ <programlisting>
+ ALTER TABLE measurement PARTITION BY RANGE (logdate);
+ CREATE INDEX measurement_logdate ON measurement (logdate);
+ </programlisting>
+        </para>
+       </listitem>
+ 
+       <listitem>
+        <para>
          Next we create one partition for each active month:
  
  <programlisting>
! CREATE PARTITION measurement_y2006m02 ON measurement VALUES LESS THAN '2006-03-01';
! CREATE PARTITION measurement_y2006m03 ON measurement VALUES LESS THAN '2006-04-01';
  ...
! CREATE PARTITION measurement_y2007m11 ON measurement VALUES LESS THAN '2007-12-01';
! CREATE PARTITION measurement_y2007m12 ON measurement VALUES LESS THAN '2008-01-01';
! CREATE PARTITION measurement_y2008m01 ON measurement VALUES LESS THAN '2008-02-01';
  </programlisting>
  
          Each of the partitions are complete tables in their own right,
          but they inherit their definitions from the
!         <structname>measurement</> table including indexes.
!         They also have non-overlapping table constraints.
         </para>
  
         <para>
*************** CREATE TABLE measurement_y2008m01 ( ) IN
*** 2486,2535 ****
  
        <listitem>
         <para>
-         We must provide non-overlapping table constraints.  Rather than
-         just creating the partition tables as above, the table creation
-         script should really be:
- 
-  <programlisting>
- CREATE TABLE measurement_y2006m02 (
-     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
- ) INHERITS (measurement);
- CREATE TABLE measurement_y2006m03 (
-     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
- ) INHERITS (measurement);
- ...
- CREATE TABLE measurement_y2007m11 (
-     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
- ) INHERITS (measurement);
- CREATE TABLE measurement_y2007m12 (
-     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
- ) INHERITS (measurement);
- CREATE TABLE measurement_y2008m01 (
-     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
- ) INHERITS (measurement);
- </programlisting>
-        </para>
-       </listitem>
- 
-       <listitem>
-        <para>
-         We probably need indexes on the key columns too:
- 
-  <programlisting>
- CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
- CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
- ...
- CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
- CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
- CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
- </programlisting>
- 
-         We choose not to add further indexes at this time.
-        </para>
-       </listitem>
- 
-       <listitem>
-        <para>
          We want our application to be able to say <literal>INSERT INTO
          measurement ...</> and have the data be redirected into the
          appropriate partition table.  We can arrange that by attaching
--- 2497,2502 ----
*************** CREATE TRIGGER insert_measurement_trigge
*** 2571,2589 ****
  CREATE OR REPLACE FUNCTION measurement_insert_trigger()
  RETURNS TRIGGER AS $$
  BEGIN
!     IF ( NEW.logdate &gt;= DATE '2006-02-01' AND
!          NEW.logdate &lt; DATE '2006-03-01' ) THEN
!         INSERT INTO measurement_y2006m02 VALUES (NEW.*);
!     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
!             NEW.logdate &lt; DATE '2006-04-01' ) THEN
!         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
!     ...
!     ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
!             NEW.logdate &lt; DATE '2008-02-01' ) THEN
!         INSERT INTO measurement_y2008m01 VALUES (NEW.*);
!     ELSE
!         RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
!     END IF;
      RETURN NULL;
  END;
  $$
--- 2538,2546 ----
  CREATE OR REPLACE FUNCTION measurement_insert_trigger()
  RETURNS TRIGGER AS $$
  BEGIN
!     EXECUTE 'INSERT INTO '
!             || quote_ident(to_char(NEW.logdate, '"measurement_y"yyyy"m"mm'))
!             || ' VALUES ($1.*)' USING NEW;
      RETURN NULL;
  END;
  $$
*************** LANGUAGE plpgsql;
*** 2591,2625 ****
  </programlisting>
  
          The trigger definition is the same as before.
!         Note that each <literal>IF</literal> test must exactly match the
!         <literal>CHECK</literal> constraint for its partition.
         </para>
  
         <para>
          While this function is more complex than the single-month case,
!         it doesn't need to be updated as often, since branches can be
!         added in advance of being needed.
         </para>
- 
-        <note>
-         <para>
-          In practice it might be best to check the newest partition first,
-          if most inserts go into that partition.  For simplicity we have
-          shown the trigger's tests in the same order as in other parts
-          of this example.
-         </para>
-        </note>
        </listitem>
       </orderedlist>
      </para>
  
-     <para>
-      As we can see, a complex partitioning scheme could require a
-      substantial amount of DDL. In the above example we would be
-      creating a new partition each month, so it might be wise to write a
-      script that generates the required DDL automatically.
-     </para>
- 
     </sect2>
  
     <sect2 id="ddl-partitioning-managing-partitions">
--- 2548,2566 ----
  </programlisting>
  
          The trigger definition is the same as before.
!         Note that names generated by <literal>to_char</literal> must exactly
!         match the partition names.
         </para>
  
         <para>
          While this function is more complex than the single-month case,
!         it doesn't need to be updated as long as we continue to use the
!         same rule for partition names.
         </para>
        </listitem>
       </orderedlist>
      </para>
  
     </sect2>
  
     <sect2 id="ddl-partitioning-managing-partitions">
*************** DROP TABLE measurement_y2006m02;
*** 2651,2657 ****
       the partitioned table but retain access to it as a table in its own
       right:
  <programlisting>
! ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
  </programlisting>
       This allows further operations to be performed on the data before
       it is dropped. For example, this is often a useful time to back up
--- 2592,2598 ----
       the partitioned table but retain access to it as a table in its own
       right:
  <programlisting>
! ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
  </programlisting>
       This allows further operations to be performed on the data before
       it is dropped. For example, this is often a useful time to back up
*************** ALTER TABLE measurement_y2006m02 NO INHE
*** 2667,2675 ****
       were created above:
  
  <programlisting>
! CREATE TABLE measurement_y2008m02 (
!     CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' )
! ) INHERITS (measurement);
  </programlisting>
  
       As an alternative, it is sometimes more convenient to create the
--- 2608,2614 ----
       were created above:
  
  <programlisting>
! CREATE PARTITION measurement_y2008m02 ON measurement VALUES LESS THAN '2008-03-01';
  </programlisting>
  
       As an alternative, it is sometimes more convenient to create the
*************** CREATE TABLE measurement_y2008m02 (
*** 2678,2690 ****
       transformed prior to it appearing in the partitioned table:
  
  <programlisting>
! CREATE TABLE measurement_y2008m02
!   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
! ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
!    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
  \copy measurement_y2008m02 from 'measurement_y2008m02'
  -- possibly some other data preparation work
! ALTER TABLE measurement_y2008m02 INHERIT measurement;
  </programlisting>
      </para>
     </sect2>
--- 2617,2626 ----
       transformed prior to it appearing in the partitioned table:
  
  <programlisting>
! CREATE TABLE measurement_y2008m02 (LIKE measurement INCLUDING ALL);
  \copy measurement_y2008m02 from 'measurement_y2008m02'
  -- possibly some other data preparation work
! ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02 VALUES LESS THAN '2008-03-01';
  </programlisting>
      </para>
     </sect2>
diff -cprN head/doc/src/sgml/ref/allfiles.sgml work/doc/src/sgml/ref/allfiles.sgml
*** head/doc/src/sgml/ref/allfiles.sgml	2009-12-11 12:39:49.829461000 +0900
--- work/doc/src/sgml/ref/allfiles.sgml	2010-01-18 14:38:33.969376893 +0900
*************** Complete list of usable sgml source file
*** 58,63 ****
--- 58,64 ----
  <!entity createOperator     system "create_operator.sgml">
  <!entity createOperatorClass system "create_opclass.sgml">
  <!entity createOperatorFamily system "create_opfamily.sgml">
+ <!entity createPartition    system "create_partition.sgml">
  <!entity createRole         system "create_role.sgml">
  <!entity createRule         system "create_rule.sgml">
  <!entity createSchema       system "create_schema.sgml">
diff -cprN head/doc/src/sgml/ref/alter_table.sgml work/doc/src/sgml/ref/alter_table.sgml
*** head/doc/src/sgml/ref/alter_table.sgml	2009-09-28 09:25:40.046261000 +0900
--- work/doc/src/sgml/ref/alter_table.sgml	2010-01-18 14:38:33.969376893 +0900
*************** ALTER TABLE <replaceable class="PARAMETE
*** 61,66 ****
--- 61,70 ----
      NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
      OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
      SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
+     PARTITION BY { RANGE | LIST } ( key ) [ <replaceable class="PARAMETER">opclass</replaceable> ] [ (...) ]
+     NO PARTITION
+     ATTACH PARTITION <replaceable class="PARAMETER">child_table</replaceable> VALUES ...
+     DETACH PARTITION <replaceable class="PARAMETER">child_table</replaceable>
  </synopsis>
   </refsynopsisdiv>
  
*************** ALTER TABLE <replaceable class="PARAMETE
*** 444,449 ****
--- 448,496 ----
      </listitem>
     </varlistentry>
  
+    <varlistentry>
+     <term><literal>PARTITION BY</literal></term>
+     <listitem>
+      <para>
+       This form adds a partition key and optional partitions to the table.
+       Inheritance and check constraints are internally added to the partitions.
+       See <xref linkend="sql-createtable"> for more information.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>NO PARTITION</literal></term>
+     <listitem>
+      <para>
+       This form removes the partition key from the table. If the table has
+       some partitions, partition values are also removed but inheritance and
+       check constraints are kept.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>ATTACH PARTITION</literal></term>
+     <listitem>
+      <para>
+       This form attaches an existing table as a partition with specified partition values.
+       Inheritance and check constraints are internally added to the partitions.
+       See <xref linkend="sql-createtable"> for more information.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>DETACH PARTITION</literal></term>
+     <listitem>
+      <para>
+       This form removes the specified partition the list of partitions of
+       the target table. The removed partition can be accessed as a normal table.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
    </variablelist>
    </para>
  
diff -cprN head/doc/src/sgml/ref/create_partition.sgml work/doc/src/sgml/ref/create_partition.sgml
*** head/doc/src/sgml/ref/create_partition.sgml	1970-01-01 09:00:00.000000000 +0900
--- work/doc/src/sgml/ref/create_partition.sgml	2010-01-18 14:38:33.970346337 +0900
***************
*** 0 ****
--- 1,164 ----
+ <refentry id="SQL-CREATEPARTITION">
+  <refmeta>
+   <refentrytitle id="sql-createtable-title">CREATE PARTITION</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE PARTITION</refname>
+   <refpurpose>define a new table</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="sql-createpartition">
+   <primary>CREATE PARTITION</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE PARTITION <replaceable class="PARAMETER">partition_name</replaceable>
+   ON <replaceable class="PARAMETER">table_name</replaceable>
+   {
+       VALUES LESS THAN { <replaceable class="PARAMETER">value</replaceable> | MAXVALUE }
+     | VALUES [ IN ] [ ( ] { <replaceable class="PARAMETER">value</replaceable> [, ...] | DEFAULT } [ ) ]
+   }
+ [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
+ [ TABLESPACE <replaceable class="PARAMETER">tablespace</replaceable> ]
+ </synopsis>
+ 
+  </refsynopsisdiv>
+ 
+  <refsect1 id="SQL-CREATEPARTITION-description">
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE PARTITION</command> will create a new, initially empty
+    partition which has the same definitions with the parent table, and add
+    it as a partition to the parent table. The parent table must be
+    partitioned before the command. The partition will be owned by the user
+    issuing the command.
+   </para>
+ 
+   <para>
+    If a schema name is given (for example, <literal>CREATE PARTITION
+    myschema.mypartition ...</>) then the partition is created in the
+    specified schema.  Otherwise it is created in the same schema with
+    the parent. The name of the partition  must be distinct from the
+    name of any other partitions, table, sequence, index, or view
+    in the same schema.
+   </para>
+ 
+   <para>
+    <command>CREATE PARTITION</command> is a syntactic sugar for
+    <command>CREATE TABLE partition_name (LIKE parent_table)</command> and
+    <command>ALTER TABLE parent_table ATTACH PARTITION partition_name</command>
+    See <xref linkend="sql-createtable"> and  <xref linkend="sql-altertable">
+    for more information.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+ 
+    <varlistentry>
+     <term><replaceable class="PARAMETER">partition_name</replaceable></term>
+     <listitem>
+      <para>
+       The name (optionally schema-qualified) of the table to be created.
+       If schema is not specified, use the same schema with the parent table.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="PARAMETER">table_name</replaceable></term>
+     <listitem>
+      <para>
+       The name (optionally schema-qualified) of the parent table.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )</literal></term>
+     <listitem>
+      <para>
+       This clause specifies optional storage parameters for a table or index;
+       see <xref linkend="sql-createtable-storage-parameters"
+       endterm="sql-createtable-storage-parameters-title"> for more
+       information.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>TABLESPACE <replaceable class="PARAMETER">tablespace</replaceable></literal></term>
+     <listitem>
+      <para>
+       The <replaceable class="PARAMETER">tablespace</replaceable> is the name
+       of the tablespace in which the new table is to be created.
+       If not specified,
+       <xref linkend="guc-default-tablespace"> is consulted, or
+       <xref linkend="guc-temp-tablespaces"> if the table is temporary.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+   </variablelist>
+ 
+  </refsect1>
+ 
+  <refsect1 id="SQL-CREATEPARTITION-examples">
+   <title>Examples</title>
+ 
+   <para>
+    Create partitions for each year on parent table <structname>sales</>:
+ 
+ <programlisting>
+ CREATE TABLE sales (
+     salesman_id       integer PRIMARY KEY,
+     salesman_name     varchar(30),
+     sales_state       varchar(20),
+     sales_date        timestamp
+ )
+ PARTITION BY RANGE (sales_date);
+ 
+ CREATE PARTITION sales_2008 ON sales VALUES LESS THAN ('2009-01-01');
+ CREATE PARTITION sales_2009 ON sales VALUES LESS THAN ('2010-01-01');
+ CREATE PARTITION sales_2010 ON sales VALUES LESS THAN ('2011-01-01');
+ </programlisting>
+ 
+   </para>
+ 
+   <para>
+    Create a table with a 2-dimensional array:
+ 
+ <programlisting>
+ CREATE TABLE array_int (
+     vector  int[][]
+ );
+ </programlisting>
+   </para>
+ 
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    There is no <command>CREATE PARTITION</command> statement in the SQL standard.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-altertable" endterm="sql-altertable-title"></member>
+    <member><xref linkend="sql-droptable" endterm="sql-droptable-title"></member>
+    <member><xref linkend="sql-createtablespace" endterm="sql-createtablespace-title"></member>
+   </simplelist>
+  </refsect1>
+ </refentry>
diff -cprN head/doc/src/sgml/ref/create_table.sgml work/doc/src/sgml/ref/create_table.sgml
*** head/doc/src/sgml/ref/create_table.sgml	2009-12-09 13:45:22.745455000 +0900
--- work/doc/src/sgml/ref/create_table.sgml	2010-01-18 14:38:33.970346337 +0900
*************** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY 
*** 31,36 ****
--- 31,42 ----
  [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
  [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
  [ TABLESPACE <replaceable class="PARAMETER">tablespace</replaceable> ]
+ [ PARTITION BY { RANGE | LIST } ( key ) [ <replaceable class="PARAMETER">opclass</replaceable> ]
+   [ ( {
+         PARTITION <replaceable class="PARAMETER">partition</replaceable> VALUES LESS THAN { <replaceable class="PARAMETER">value</replaceable> | MAXVALUE }
+       | PARTITION <replaceable class="PARAMETER">partition</replaceable> VALUES [ IN ] [ ( ] { <replaceable class="PARAMETER">value</replaceable> [, ...] | DEFAULT } [ ) ]
+   } ) ]
+ ]
  
  <phrase>where <replaceable class="PARAMETER">column_constraint</replaceable> is:</phrase>
  
*************** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY 
*** 762,767 ****
--- 768,784 ----
      </listitem>
     </varlistentry>
  
+    <varlistentry>
+     <term><literal>PARTITION BY</literal></term>
+     <listitem>
+      <para>
+       This clause adds a <literal>RANGE</literal> or <literal>LIST</literal>
+       partition key and optional partitions into the table.
+       When a named partition exist as a table, it inherits this table.
+       If not exists, a new table is automatically created and used.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
  
    <refsect2 id="SQL-CREATETABLE-storage-parameters">
*************** CREATE TABLE cinemas (
*** 1182,1187 ****
--- 1199,1224 ----
  </programlisting>
    </para>
  
+   <para>
+    Create a table <literal>films</literal> with partitions for each
+    released year:
+ 
+ <programlisting>
+ CREATE TABLE films (
+     code        char(5) PRIMARY KEY,
+     title       varchar(40),
+     released    date,
+     kind        varchar(10),
+     len         interval hour to minute
+ )
+ PARTITION BY RANGE (released)
+ (
+     PARTITION films_2008 VALUES LESS THAN ('2009-01-01'),
+     PARTITION films_2009 VALUES LESS THAN ('2010-01-01'),
+     PARTITION films_2010 VALUES LESS THAN ('2011-01-01')
+ );
+ </programlisting>
+   </para>
   </refsect1>
  
   <refsect1 id="SQL-CREATETABLE-compatibility">
diff -cprN head/src/backend/catalog/Makefile work/src/backend/catalog/Makefile
*** head/src/backend/catalog/Makefile	2010-01-07 09:24:42.729068000 +0900
--- work/src/backend/catalog/Makefile	2010-01-18 14:38:33.971469780 +0900
*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr
*** 37,43 ****
  	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
  	pg_ts_parser.h pg_ts_template.h \
  	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
! 	pg_default_acl.h \
  	toasting.h indexing.h \
      )
  
--- 37,43 ----
  	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
  	pg_ts_parser.h pg_ts_template.h \
  	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
! 	pg_default_acl.h pg_partition.h \
  	toasting.h indexing.h \
      )
  
diff -cprN head/src/backend/catalog/dependency.c work/src/backend/catalog/dependency.c
*** head/src/backend/catalog/dependency.c	2010-01-04 09:10:26.638773000 +0900
--- work/src/backend/catalog/dependency.c	2010-01-18 14:38:33.972469881 +0900
***************
*** 42,47 ****
--- 42,48 ----
  #include "catalog/pg_opclass.h"
  #include "catalog/pg_operator.h"
  #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_partition.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_rewrite.h"
  #include "catalog/pg_tablespace.h"
*************** static const Oid object_classes[MAX_OCLA
*** 151,157 ****
  	ForeignDataWrapperRelationId,	/* OCLASS_FDW */
  	ForeignServerRelationId,	/* OCLASS_FOREIGN_SERVER */
  	UserMappingRelationId,		/* OCLASS_USER_MAPPING */
! 	DefaultAclRelationId		/* OCLASS_DEFACL */
  };
  
  
--- 152,159 ----
  	ForeignDataWrapperRelationId,	/* OCLASS_FDW */
  	ForeignServerRelationId,	/* OCLASS_FOREIGN_SERVER */
  	UserMappingRelationId,		/* OCLASS_USER_MAPPING */
! 	DefaultAclRelationId,		/* OCLASS_DEFACL */
! 	PartitionRelationId			/* OCLASS_PARTITION */
  };
  
  
*************** doDeletion(const ObjectAddress *object)
*** 1149,1154 ****
--- 1151,1160 ----
  			RemoveDefaultACLById(object->objectId);
  			break;
  
+ 		case OCLASS_PARTITION:
+ 			RemovePartition(object->objectId);
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized object class: %u",
  				 object->classId);
*************** getObjectClass(const ObjectAddress *obje
*** 2076,2081 ****
--- 2082,2091 ----
  		case DefaultAclRelationId:
  			Assert(object->objectSubId == 0);
  			return OCLASS_DEFACL;
+ 
+ 		case PartitionRelationId:
+ 			Assert(object->objectSubId == 0);
+ 			return OCLASS_PARTITION;
  	}
  
  	/* shouldn't get here */
*************** getObjectDescription(const ObjectAddress
*** 2685,2690 ****
--- 2695,2744 ----
  				break;
  			}
  
+ 		case OCLASS_PARTITION:
+ 			{
+ 				Relation	rel;
+ 				ScanKeyData skey[1];
+ 				SysScanDesc rcscan;
+ 				HeapTuple	tup;
+ 				Form_pg_partition partition;
+ 
+ 				rel = heap_open(PartitionRelationId, AccessShareLock);
+ 
+ 				ScanKeyInit(&skey[0],
+ 							Anum_pg_partition_partrelid,
+ 							BTEqualStrategyNumber, F_OIDEQ,
+ 							ObjectIdGetDatum(object->objectId));
+ 
+ 				rcscan = systable_beginscan(rel, PartitionRelidIndexId,
+ 											true, SnapshotNow, 1, skey);
+ 
+ 				tup = systable_getnext(rcscan);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for partition %u",
+ 						 object->objectId);
+ 
+ 				partition = (Form_pg_partition) GETSTRUCT(tup);
+ 
+ 				switch (partition->partkind)
+ 				{
+ 					case PARTITION_BY_RANGE:
+ 						appendStringInfo(&buffer, _("range partition"));
+ 						break;
+ 					case PARTITION_BY_LIST:
+ 						appendStringInfo(&buffer, _("list partition"));
+ 						break;
+ 					default:
+ 						appendStringInfo(&buffer, _("partition"));
+ 						break;
+ 				}
+ 
+ 				systable_endscan(rcscan);
+ 				heap_close(rel, AccessShareLock);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff -cprN head/src/backend/catalog/heap.c work/src/backend/catalog/heap.c
*** head/src/backend/catalog/heap.c	2010-01-06 14:42:58.290140000 +0900
--- work/src/backend/catalog/heap.c	2010-01-18 14:38:33.973063002 +0900
***************
*** 43,48 ****
--- 43,49 ----
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_inherits.h"
  #include "catalog/pg_namespace.h"
+ #include "catalog/pg_partition.h"
  #include "catalog/pg_statistic.h"
  #include "catalog/pg_tablespace.h"
  #include "catalog/pg_type.h"
*************** static void StoreConstraints(Relation re
*** 91,99 ****
  static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
  							bool allow_merge, bool is_local);
  static void SetRelationNumChecks(Relation rel, int numchecks);
- static Node *cookConstraint(ParseState *pstate,
- 			   Node *raw_constraint,
- 			   char *relname);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
  
  Oid binary_upgrade_next_heap_relfilenode = InvalidOid;
--- 92,97 ----
*************** cookDefault(ParseState *pstate,
*** 2283,2289 ****
   * Parse state must be set up to recognize any vars that might appear
   * in the expression.
   */
! static Node *
  cookConstraint(ParseState *pstate,
  			   Node *raw_constraint,
  			   char *relname)
--- 2281,2287 ----
   * Parse state must be set up to recognize any vars that might appear
   * in the expression.
   */
! Node *
  cookConstraint(ParseState *pstate,
  			   Node *raw_constraint,
  			   char *relname)
*************** RemoveStatistics(Oid relid, AttrNumber a
*** 2376,2381 ****
--- 2374,2444 ----
  
  
  /*
+  * Remove a pg_partition entry
+  */
+ void
+ RemovePartition(Oid relid)
+ {
+ 	Relation		rel;
+ 	HeapScanDesc	scan;
+ 	ScanKeyData		key;
+ 	HeapTuple		tup;
+ 
+ 	/* DELETE FROM pg_partition WHERE partrelid = :relid */
+ 	rel = heap_open(PartitionRelationId, RowExclusiveLock);
+ 	tup = SearchSysCache(PARTITIONKEY,
+ 						 ObjectIdGetDatum(relid),
+ 						 0, 0, 0);
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for partition %u", relid);
+ 	simple_heap_delete(rel, &tup->t_self);
+ 	ReleaseSysCache(tup);
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	/* UPDATE pg_inherits SET inhvalues = NULL WHERE inhparent = :relid */
+ 	rel = heap_open(InheritsRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&key,
+ 				Anum_pg_inherits_inhparent,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(relid));
+ 	scan = heap_beginscan(rel, SnapshotNow, 1, &key);
+ 
+ 	while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ 	{
+ 		Datum	datum;
+ 		bool	isnull;
+ 
+ 		datum = heap_getattr(tup, Anum_pg_inherits_inhvalues,
+ 						 RelationGetDescr(rel), &isnull);
+ 		if (!isnull)
+ 		{
+ 			Datum		values[Natts_pg_inherits];
+ 			bool		nulls[Natts_pg_inherits];
+ 			bool		replaces[Natts_pg_inherits];
+ 			HeapTuple	newtup;
+ 
+ 			MemSet(values, 0, sizeof(values));
+ 			MemSet(nulls, false, sizeof(nulls));
+ 			MemSet(replaces, false, sizeof(replaces));
+ 
+ 			nulls[Anum_pg_inherits_inhvalues - 1] = true;
+ 			replaces[Anum_pg_inherits_inhvalues - 1] = true;
+ 
+ 			newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
+ 										values, nulls, replaces);
+ 			simple_heap_update(rel, &newtup->t_self, newtup);
+ 			CatalogUpdateIndexes(rel, newtup);
+ 
+ 			heap_freetuple(newtup);
+ 		}
+ 	}
+ 	heap_endscan(scan);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ }
+ 
+ 
+ /*
   * RelationTruncateIndexes - truncate all indexes associated
   * with the heap relation to zero tuples.
   *
diff -cprN head/src/backend/catalog/pg_inherits.c work/src/backend/catalog/pg_inherits.c
*** head/src/backend/catalog/pg_inherits.c	2010-01-04 09:10:26.638773000 +0900
--- work/src/backend/catalog/pg_inherits.c	2010-01-18 14:38:33.974031974 +0900
***************
*** 21,37 ****
--- 21,46 ----
  
  #include "access/genam.h"
  #include "access/heapam.h"
+ #include "access/nbtree.h"
  #include "catalog/indexing.h"
  #include "catalog/pg_class.h"
  #include "catalog/pg_inherits.h"
  #include "catalog/pg_inherits_fn.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_partition.h"
+ #include "nodes/nodeFuncs.h"
  #include "parser/parse_type.h"
  #include "storage/lmgr.h"
+ #include "utils/array.h"
+ #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
  
  static int	oid_cmp(const void *p1, const void *p2);
+ static int	partition_cmp(const void *a, const void *b, void *arg);
  
  
  /*
*************** oid_cmp(const void *p1, const void *p2)
*** 341,343 ****
--- 350,513 ----
  		return 1;
  	return 0;
  }
+ 
+ 
+ /*
+  * get_partitions - Gather information of a partition.
+  *
+  * Returns a list of Partition.
+  */
+ List *
+ get_partitions(Oid parentrelId, char *kind, Node **key, Oid *opclass)
+ {
+ 	Relation		inhrel;
+ 	SysScanDesc		scan;
+ 	ScanKeyData		skey[1];
+ 	HeapTuple		tp;
+ 	Datum			datum;
+ 	bool			isnull;
+ 	List		   *partitions = NIL;
+ 	Form_pg_opclass	form;
+ 	Oid				opfamily;
+ 	Oid				opcintype;
+ 	Oid				typid;
+ 	int16			typlen;
+ 	bool			typbyval;
+ 	char			typalign;
+ 	int				i,
+ 					n;
+ 
+ 	*kind = 0;
+ 	*key = NULL;
+ 	*opclass = InvalidOid;
+ 
+ 	/* Get partition key and operator. */
+ 	tp = SearchSysCache(PARTITIONKEY, ObjectIdGetDatum(parentrelId), 0, 0, 0);
+ 	if (!HeapTupleIsValid(tp))
+ 		return NIL;	/* not partitioned */
+ 
+ 	*kind = DatumGetChar(SysCacheGetAttr(PARTITIONKEY, tp,
+ 							Anum_pg_partition_partkind, &isnull));
+ 	*key = stringToNode(TextDatumGetCString(SysCacheGetAttr(
+ 					PARTITIONKEY, tp, Anum_pg_partition_partkey, &isnull)));
+ 	*opclass = DatumGetObjectId(SysCacheGetAttr(PARTITIONKEY, tp,
+ 							Anum_pg_partition_partopclass, &isnull));
+ 	ReleaseSysCache(tp);
+ 
+ 	tp = SearchSysCache(CLAOID, ObjectIdGetDatum(*opclass), 0, 0, 0);
+ 	if (!HeapTupleIsValid(tp))
+ 		elog(ERROR, "cache lookup failed for opclass %u", *opclass);
+ 	form = (Form_pg_opclass) GETSTRUCT(tp);
+ 	opfamily = form->opcfamily;
+ 	opcintype = form->opcintype;
+ 	ReleaseSysCache(tp);
+ 
+ 	typid = exprType(*key);
+ 	get_typlenbyvalalign(typid, &typlen, &typbyval, &typalign);
+ 
+ 	if (!has_subclass(parentrelId))
+ 		return NIL;	/* has no partitions */
+ 
+ 	/* Gather values from existing partition. */
+ 	inhrel = heap_open(InheritsRelationId, AccessShareLock);
+ 	ScanKeyInit(&skey[0],
+ 				Anum_pg_inherits_inhparent,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(parentrelId));
+ 	scan = systable_beginscan(inhrel, InheritsParentIndexId, true,
+ 							  SnapshotNow, 1, skey);
+ 
+ 	while ((tp = systable_getnext(scan)) != NULL)
+ 	{
+ 		Form_pg_inherits	inh = (Form_pg_inherits) GETSTRUCT(tp);
+ 		Partition	   *p;
+ 
+ 		datum = heap_getattr(tp, Anum_pg_inherits_inhvalues,
+ 						 RelationGetDescr(inhrel), &isnull);
+ 		if (isnull)
+ 			continue;	/* non-partition inheritance */
+ 
+ 		p = (Partition *) palloc(sizeof(Partition));
+ 		p->relid = inh->inhrelid;
+ 		deconstruct_array(DatumGetArrayTypeP(datum), typid, typlen,
+ 						typbyval, typalign, &p->values, NULL, &p->nvalues);
+ 
+ 		/* copy array elements if passed-by-reference */
+ 		if (!typbyval)
+ 		{
+ 			for (i = 0; i < p->nvalues; i++)
+ 			{
+ 				p->values[i] = (typlen == -1
+ 					? PointerGetDatum(PG_DETOAST_DATUM_COPY(p->values[i]))
+ 					: datumCopy(p->values[i], false, typlen));
+ 			}
+ 		}
+ 
+ 		partitions = lappend(partitions, p);
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(inhrel, AccessShareLock);
+ 
+ 	/*
+ 	 * Sort partitions compared with each first value. They are the upper range
+ 	 * of the partition for range partitions. List partitions don't always need
+ 	 * to sort whole of the list, but overflow partitions should be placed at
+ 	 * end of the list.
+ 	 */
+ 	n = list_length(partitions);
+ 	if (n >= 2)
+ 	{
+ 		Oid			cmptype = (opcintype != InvalidOid ? opcintype : typid);
+ 		Oid			cmpid;
+ 		FmgrInfo	cmpfn;
+ 		Partition **sorted;
+ 		ListCell   *cell;
+ 
+ 		cmpid = get_opfamily_proc(opfamily, cmptype, cmptype, BTORDER_PROC);
+ 		if (!OidIsValid(cmpid))
+ 			elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
+ 				 BTORDER_PROC, cmptype, cmptype, opfamily);
+ 		fmgr_info(cmpid, &cmpfn);
+ 
+ 		/*
+ 		 * We flatten list to array, sort items in array, and rebuild a list.
+ 		 */
+ 		sorted = (Partition **) palloc(sizeof(Partition *) * n);
+ 		i = 0;
+ 		foreach(cell, partitions)
+ 			sorted[i++] = (Partition *) lfirst(cell);
+ 		list_free(partitions);
+ 
+ 		qsort_arg(sorted, n, sizeof(Partition *), partition_cmp, &cmpfn);
+ 
+ 		partitions = NIL;
+ 		for (i = 0; i < n; i++)
+ 			partitions = lappend(partitions, sorted[i]);
+ 		pfree(sorted);
+ 	}
+ 
+ 	return partitions;
+ }
+ 
+ 
+ /*
+  * qsort function to compare partitions with the first value. Overflow
+  * partitions are always larger than normal partitions.
+  */
+ static int
+ partition_cmp(const void *a, const void *b, void *arg)
+ {
+ 	const Partition *lhs = *(const Partition **) a;
+ 	const Partition *rhs = *(const Partition **) b;
+ 	FmgrInfo *cmpfn = (FmgrInfo *) arg;
+ 
+ 	if (lhs->nvalues == 0 && rhs->nvalues == 0)
+ 		return 0;	/* should not ocuur */
+ 	else if (rhs->nvalues == 0)
+ 		return -1;	/* rhs is an overflow partition */
+ 	else if (lhs->nvalues == 0)
+ 		return +1;	/* lhs is an overflow partition */
+ 
+ 	return DatumGetInt32(FunctionCall2(cmpfn, lhs->values[0], rhs->values[0]));
+ }
diff -cprN head/src/backend/commands/indexcmds.c work/src/backend/commands/indexcmds.c
*** head/src/backend/commands/indexcmds.c	2010-01-04 09:10:26.638773000 +0900
--- work/src/backend/commands/indexcmds.c	2010-01-18 14:38:33.974031974 +0900
*************** static void ComputeIndexAttrs(IndexInfo 
*** 65,72 ****
  				  char *accessMethodName, Oid accessMethodId,
  				  bool amcanorder,
  				  bool isconstraint);
- static Oid GetIndexOpClass(List *opclass, Oid attrType,
- 				char *accessMethodName, Oid accessMethodId);
  static char *ChooseIndexNameAddition(List *colnames);
  static bool relationHasPrimaryKey(Relation rel);
  
--- 65,70 ----
*************** ComputeIndexAttrs(IndexInfo *indexInfo,
*** 1044,1052 ****
  /*
   * Resolve possibly-defaulted operator class specification
   */
! static Oid
  GetIndexOpClass(List *opclass, Oid attrType,
! 				char *accessMethodName, Oid accessMethodId)
  {
  	char	   *schemaname;
  	char	   *opcname;
--- 1042,1050 ----
  /*
   * Resolve possibly-defaulted operator class specification
   */
! Oid
  GetIndexOpClass(List *opclass, Oid attrType,
! 				const char *accessMethodName, Oid accessMethodId)
  {
  	char	   *schemaname;
  	char	   *opcname;
diff -cprN head/src/backend/commands/tablecmds.c work/src/backend/commands/tablecmds.c
*** head/src/backend/commands/tablecmds.c	2010-01-18 09:26:23.915594000 +0900
--- work/src/backend/commands/tablecmds.c	2010-01-18 14:38:33.977063927 +0900
***************
*** 32,37 ****
--- 32,39 ----
  #include "catalog/pg_inherits_fn.h"
  #include "catalog/pg_namespace.h"
  #include "catalog/pg_opclass.h"
+ #include "catalog/pg_operator.h"
+ #include "catalog/pg_partition.h"
  #include "catalog/pg_tablespace.h"
  #include "catalog/pg_trigger.h"
  #include "catalog/pg_type.h"
***************
*** 65,72 ****
--- 67,76 ----
  #include "storage/bufmgr.h"
  #include "storage/lmgr.h"
  #include "storage/smgr.h"
+ #include "tcop/utility.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/fmgroids.h"
  #include "utils/inval.h"
  #include "utils/lsyscache.h"
*************** static void MergeAttributesIntoExisting(
*** 230,236 ****
  static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
  static void StoreCatalogInheritance(Oid relationId, List *supers);
  static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
! 						 int16 seqNumber, Relation inhRelation);
  static int	findAttrByName(const char *attributeName, List *schema);
  static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
  static void AlterIndexNamespaces(Relation classRel, Relation rel,
--- 234,240 ----
  static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
  static void StoreCatalogInheritance(Oid relationId, List *supers);
  static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
! 					int16 seqNumber, ArrayType *inhvalues, Relation inhRelation);
  static int	findAttrByName(const char *attributeName, List *schema);
  static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
  static void AlterIndexNamespaces(Relation classRel, Relation rel,
*************** static void ATExecEnableDisableRule(Rela
*** 331,340 ****
  						char fires_when);
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
  				   ForkNumber forkNum, bool istemp);
  static const char *storage_name(char c);
! 
  
  /* ----------------------------------------------------------------
   *		DefineRelation
--- 335,360 ----
  						char fires_when);
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
+ static void ATExecPartitionBy(Relation rel, PartitionBy *defs);
+ static void ATExecAttachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionDef *def);
+ static void ATExecDetachPartition(Relation rel, RangeVar *child);
  static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
  				   ForkNumber forkNum, bool istemp);
  static const char *storage_name(char c);
! static void addInherit(Relation child_rel, Relation parent_rel,
! 					   List **wqueue, AlteredTableInfo *tab, PartitionDef *def);
! static void dropInherit(Relation rel, Relation parent_rel);
! static ArrayType *addPartitionConstraint(List **wqueue, AlteredTableInfo *tab,
! 		PartitionDef *def, Relation parent, Relation child);
! static void findRangePartition(Datum value, List *partitions, FmgrInfo *ltfn,
! 							   Partition **left, Partition **right);
! static Partition *findOverlappedListPartition(const Datum *values,
! 								int nvalues, List *partitions, FmgrInfo *eqfn);
! static List *gatherPartitionValues(List *partitions, Oid elmtype, int elmlen,
! 								   bool elmbyval);
! static Datum *evaluateValues(Oid typid, int typlen, bool typbyval,
! 							 List *values, int *length);
! static const char *getPartitionKindName(char partkind);
  
  /* ----------------------------------------------------------------
   *		DefineRelation
*************** StoreCatalogInheritance(Oid relationId, 
*** 1777,1783 ****
  	{
  		Oid			parentOid = lfirst_oid(entry);
  
! 		StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation);
  		seqNumber++;
  	}
  
--- 1797,1803 ----
  	{
  		Oid			parentOid = lfirst_oid(entry);
  
! 		StoreCatalogInheritance1(relationId, parentOid, seqNumber, NULL, relation);
  		seqNumber++;
  	}
  
*************** StoreCatalogInheritance(Oid relationId, 
*** 1789,1796 ****
   * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
   */
  static void
! StoreCatalogInheritance1(Oid relationId, Oid parentOid,
! 						 int16 seqNumber, Relation inhRelation)
  {
  	TupleDesc	desc = RelationGetDescr(inhRelation);
  	Datum		values[Natts_pg_inherits];
--- 1809,1816 ----
   * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
   */
  static void
! StoreCatalogInheritance1(Oid relationId, Oid parentOid, int16 seqNumber,
! 						 ArrayType *inhvalues, Relation inhRelation)
  {
  	TupleDesc	desc = RelationGetDescr(inhRelation);
  	Datum		values[Natts_pg_inherits];
*************** StoreCatalogInheritance1(Oid relationId,
*** 1805,1812 ****
--- 1825,1834 ----
  	values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
  	values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
  	values[Anum_pg_inherits_inhseqno - 1] = Int16GetDatum(seqNumber);
+ 	values[Anum_pg_inherits_inhvalues - 1] = PointerGetDatum(inhvalues);
  
  	memset(nulls, 0, sizeof(nulls));
+ 	nulls[Anum_pg_inherits_inhvalues - 1] = (inhvalues == NULL);
  
  	tuple = heap_form_tuple(desc, values, nulls);
  
*************** RenameRelationInternal(Oid myrelid, cons
*** 2174,2179 ****
--- 2196,2220 ----
  }
  
  /*
+  * CREATE PARTITION partition ON table
+  */
+ void
+ CreatePartition(CreatePartitionStmt *stmt, const char *queryString)
+ {
+ 	List		   *stmts;
+ 	ListCell	   *cell;
+ 
+ 	stmts = transformCreatePartition(stmt->def, stmt->parent);
+ 
+ 	foreach(cell, stmts)
+ 	{
+ 		ProcessUtility((Node *) lfirst(cell),
+ 					   queryString, NULL, false, NULL, NULL);
+ 		CommandCounterIncrement();
+ 	}
+ }
+ 
+ /*
   * Disallow ALTER TABLE (and similar commands) when the current backend has
   * any open reference to the target table besides the one just acquired by
   * the calling command; this implies there's an open cursor or active plan.
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2533,2543 ****
--- 2574,2590 ----
  		case AT_DisableRule:
  		case AT_AddInherit:		/* INHERIT / NO INHERIT */
  		case AT_DropInherit:
+ 		case AT_DetachPartition:	/* DETACH PARTITION */
  			ATSimplePermissions(rel, false);
  			/* These commands never recurse */
  			/* No command-specific prep needed */
  			pass = AT_PASS_MISC;
  			break;
+ 		case AT_PartitionBy:		/* PARTITION BY / NO PARTITION */
+ 		case AT_AttachPartition:	/* ATTACH PARTITION */
+ 			ATSimplePermissions(rel, false);
+ 			pass = AT_PASS_ADD_CONSTR;
+ 			break;
  		default:				/* oops */
  			elog(ERROR, "unrecognized alter table type: %d",
  				 (int) cmd->subtype);
*************** ATExecCmd(List **wqueue, AlteredTableInf
*** 2777,2782 ****
--- 2824,2838 ----
  		case AT_DropInherit:
  			ATExecDropInherit(rel, (RangeVar *) cmd->def);
  			break;
+ 		case AT_PartitionBy:	/* PARTITION BY / NO PARTITION */
+ 			ATExecPartitionBy(rel, (PartitionBy *) cmd->def);
+ 			break;
+ 		case AT_AttachPartition:
+ 			ATExecAttachPartition(wqueue, tab, rel, (PartitionDef *) cmd->def);
+ 			break;
+ 		case AT_DetachPartition:
+ 			ATExecDetachPartition(rel, (RangeVar *) cmd->def);
+ 			break;
  		default:				/* oops */
  			elog(ERROR, "unrecognized alter table type: %d",
  				 (int) cmd->subtype);
*************** ATExecAlterColumnType(AlteredTableInfo *
*** 6140,6145 ****
--- 6196,6208 ----
  					 getObjectDescription(&foundObject));
  				break;
  
+ 			case OCLASS_PARTITION:
+ 				/* TODO: recreate all partitions */
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 						 errmsg("cannot alter type of a column used in partition keys")));
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized object class: %u",
  					 foundObject.classId);
*************** ATExecEnableDisableRule(Relation rel, ch
*** 7107,7113 ****
  }
  
  /*
!  * ALTER TABLE INHERIT
   *
   * Add a parent to the child's parents. This verifies that all the columns and
   * check constraints of the parent appear in the child and that they have the
--- 7170,7176 ----
  }
  
  /*
!  * ALTER TABLE INHERIT / ATTACH PARTITION
   *
   * Add a parent to the child's parents. This verifies that all the columns and
   * check constraints of the parent appear in the child and that they have the
*************** ATExecEnableDisableRule(Relation rel, ch
*** 7116,7128 ****
  static void
  ATExecAddInherit(Relation child_rel, RangeVar *parent)
  {
! 	Relation	parent_rel,
! 				catalogRelation;
! 	SysScanDesc scan;
! 	ScanKeyData key;
! 	HeapTuple	inheritsTuple;
! 	int32		inhseqno;
! 	List	   *children;
  
  	/*
  	 * AccessShareLock on the parent is what's obtained during normal CREATE
--- 7179,7185 ----
  static void
  ATExecAddInherit(Relation child_rel, RangeVar *parent)
  {
! 	Relation	parent_rel;
  
  	/*
  	 * AccessShareLock on the parent is what's obtained during normal CREATE
*************** ATExecAddInherit(Relation child_rel, Ran
*** 7130,7135 ****
--- 7187,7228 ----
  	 */
  	parent_rel = heap_openrv(parent, AccessShareLock);
  
+ 	addInherit(child_rel, parent_rel, NULL, NULL, NULL);
+ 
+ 	/* keep our lock on the parent relation until commit */
+ 	heap_close(parent_rel, NoLock);
+ }
+ 
+ static void
+ ATExecAttachPartition(List **wqueue, AlteredTableInfo *tab,
+ 					  Relation parent_rel, PartitionDef *def)
+ {
+ 	Relation	child_rel;
+ 
+ 	/*
+ 	 * AccessShareLock on the parent is what's obtained during normal CREATE
+ 	 * TABLE ... INHERITS ..., so should be enough here.
+ 	 */
+ 	child_rel = heap_openrv(def->name, AccessShareLock);
+ 
+ 	addInherit(child_rel, parent_rel, wqueue, tab, def);
+ 
+ 	/* keep our lock on the parent relation until commit */
+ 	heap_close(child_rel, NoLock);
+ }
+ 
+ static void
+ addInherit(Relation child_rel, Relation parent_rel,
+ 		   List **wqueue, AlteredTableInfo *tab, PartitionDef *def)
+ {
+ 	Relation	catalogRelation;
+ 	SysScanDesc scan;
+ 	ScanKeyData key;
+ 	HeapTuple	inheritsTuple;
+ 	int32		inhseqno;
+ 	List	   *children;
+ 	ArrayType  *inhvalues;
+ 
  	/*
  	 * Must be owner of both parent and child -- child was checked by
  	 * ATSimplePermissions call in ATPrepCmd
*************** ATExecAddInherit(Relation child_rel, Ran
*** 7197,7203 ****
  				(errcode(ERRCODE_DUPLICATE_TABLE),
  				 errmsg("circular inheritance not allowed"),
  				 errdetail("\"%s\" is already a child of \"%s\".",
! 						   parent->relname,
  						   RelationGetRelationName(child_rel))));
  
  	/* If parent has OIDs then child must have OIDs */
--- 7290,7296 ----
  				(errcode(ERRCODE_DUPLICATE_TABLE),
  				 errmsg("circular inheritance not allowed"),
  				 errdetail("\"%s\" is already a child of \"%s\".",
! 						   RelationGetRelationName(parent_rel),
  						   RelationGetRelationName(child_rel))));
  
  	/* If parent has OIDs then child must have OIDs */
*************** ATExecAddInherit(Relation child_rel, Ran
*** 7214,7232 ****
  	/* Match up the constraints and bump coninhcount as needed */
  	MergeConstraintsIntoExisting(child_rel, parent_rel);
  
  	/*
  	 * OK, it looks valid.	Make the catalog entries that show inheritance.
  	 */
  	StoreCatalogInheritance1(RelationGetRelid(child_rel),
  							 RelationGetRelid(parent_rel),
  							 inhseqno + 1,
  							 catalogRelation);
  
  	/* Now we're done with pg_inherits */
  	heap_close(catalogRelation, RowExclusiveLock);
  
! 	/* keep our lock on the parent relation until commit */
! 	heap_close(parent_rel, NoLock);
  }
  
  /*
--- 7307,7732 ----
  	/* Match up the constraints and bump coninhcount as needed */
  	MergeConstraintsIntoExisting(child_rel, parent_rel);
  
+ 	/* Add CHECK constraint for partition */
+ 	if (def != NULL)
+ 		inhvalues = addPartitionConstraint(
+ 							wqueue, tab, def, parent_rel, child_rel);
+ 	else
+ 		inhvalues = NULL;
+ 
  	/*
  	 * OK, it looks valid.	Make the catalog entries that show inheritance.
  	 */
  	StoreCatalogInheritance1(RelationGetRelid(child_rel),
  							 RelationGetRelid(parent_rel),
  							 inhseqno + 1,
+ 							 inhvalues,
  							 catalogRelation);
  
  	/* Now we're done with pg_inherits */
  	heap_close(catalogRelation, RowExclusiveLock);
+ }
  
! /*
!  * addPartitionConstraint - Add check constraint for partition.
!  */
! static ArrayType *
! addPartitionConstraint(List **wqueue, AlteredTableInfo *tab, PartitionDef *def,
! 					   Relation parent, Relation child)
! {
! 	HeapTuple		tp;
! 	Form_pg_opclass	opclassTup;
! 	char			partkind;
! 	Node		   *partkey;
! 	Oid				opclass;
! 	Oid				opfamily;
! 	Oid				opcintype;
! 	List		   *partitions;
! 	Oid				elmtype;
! 	int16			elmlen;
! 	bool			elmbyval;
! 	char			elmalign;
! 	int16			strategy;
! 	Oid				oprid;
! 	List		   *opr;
! 	FmgrInfo		opfn;
! 	Node		   *check_expr;
! 	Datum		   *values;
! 	int				nvalues;
! 
! 	/* Retrieve partition information. */
! 	partitions = get_partitions(RelationGetRelid(parent),
! 								&partkind, &partkey, &opclass);
! 	if (partkind == 0)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 				 errmsg("table \"%s\" is not partitioned",
! 						RelationGetRelationName(parent))));
! 	if (partkind != def->kind)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 				 errmsg("cannot add %s partition \"%s\" to %s partitioned table \"%s\"",
! 						getPartitionKindName(def->kind),
! 						RelationGetRelationName(child),
! 						getPartitionKindName(partkind),
! 						RelationGetRelationName(parent))));
! 
! 	/* Check for duplicated overflow partitions */
! 	if (list_length(partitions) > 0 && 
! 		((Partition *) llast(partitions))->nvalues == 0)
! 	{
! 		if (def->values == NIL)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_DUPLICATE_TABLE),
! 					 errmsg("duplicated overflow partition")));
! 		else
! 			ereport(ERROR,
! 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("cannot add partition to \"%s\", that has an overflow partition",
! 							RelationGetRelationName(parent))));
! 	}
! 
! 	/* Retrieve type information and convert expressions into datum array. */
! 	elmtype = exprType(partkey);
! 	get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
! 	values = evaluateValues(elmtype, elmlen, elmbyval, def->values, &nvalues);
! 
! 	/* Retrieve operator information. */
! 	tp = SearchSysCache(CLAOID, ObjectIdGetDatum(opclass), 0, 0, 0);
! 	if (!HeapTupleIsValid(tp))
! 		elog(ERROR, "cache lookup failed for opclass %u", opclass);
! 	opclassTup = (Form_pg_opclass) GETSTRUCT(tp);
! 	opfamily = opclassTup->opcfamily;
! 	opcintype = opclassTup->opcintype;
! 	ReleaseSysCache(tp);
! 
! 	if (!OidIsValid(opcintype))
! 		opcintype = elmtype;
! 
! 	switch (partkind)
! 	{
! 		case PARTITION_BY_RANGE:
! 			strategy = BTLessStrategyNumber;
! 			break;
! 		case PARTITION_BY_LIST:
! 			strategy = BTEqualStrategyNumber;
! 			break;
! 		default:
! 			strategy = 0;
! 	}
! 	oprid = get_opfamily_member(opfamily, opcintype, opcintype, strategy);
! 	if (!OidIsValid(oprid))
! 		elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
! 			 strategy, opcintype, opcintype, opfamily);
! 	opr = get_opfullname(oprid);
! 	fmgr_info(get_opcode(oprid), &opfn);
! 
! 	/* ALTER TABLE ... ADD CHECK */
! 	switch (partkind)
! 	{
! 		case PARTITION_BY_RANGE:
! 		{
! 			Partition  *left;
! 			Partition  *right;
! 			Node	   *lt = NULL;
! 			Node	   *ge = NULL;
! 
! 			/* Check for overlapped list partition values. */
! 			if (nvalues > 0)
! 			{
! 				Assert(nvalues == 1);
! 				lt = (Node *) makeA_Expr(
! 					AEXPR_OP, opr, partkey, linitial(def->values), -1);
! 				findRangePartition(values[0], partitions, &opfn, &left, &right);
! 			}
! 			else
! 			{
! 				left = (list_length(partitions) > 0 ? llast(partitions) : NULL);
! 				right = NULL;
! 			}
! 
! 			/* TODO: split overlapped partition */
! 			if (right != NULL)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 						 errmsg("cannot split partition \"%s\"",
! 								get_rel_name(right->relid))));
! 
! 			if (left)
! 			{
! 				Oid		oprle;
! 				Node   *lower;
! 				
! 				oprle = get_opfamily_member(opfamily, opcintype, opcintype,
! 											BTLessEqualStrategyNumber);
! 				if (!OidIsValid(oprid))
! 					elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
! 						 BTLessEqualStrategyNumber, opcintype, opcintype, opfamily);
! 				lower = (Node *) makeConst(elmtype, -1, elmlen, left->values[0],
! 										   false, elmbyval);
! 				/* lower <= key */
! 				ge = (Node *) makeA_Expr(AEXPR_OP, get_opfullname(oprle),
! 										 lower, partkey, -1);
! 			}
! 
! 			if (ge == NULL)			/* key < upper */
! 				check_expr = lt;
! 			else if (lt == NULL)	/* key >= lower */
! 				check_expr = ge;
! 			else					/* key >= lower AND key < upper */
! 				check_expr = (Node *) makeA_Expr(AEXPR_AND, NIL, ge, lt, -1);
! 			break;
! 		}
! 		case PARTITION_BY_LIST:
! 		{
! 			if (nvalues > 0)
! 			{
! 				Partition *overlapped;
! 
! 				/* Check for overlapped list partition values. */
! 				overlapped = findOverlappedListPartition(values, nvalues,
! 														 partitions, &opfn);
! 
! 				/* TODO: split overlapped partition */
! 				if (overlapped != NULL)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 							errmsg("partition values overlapped with \"%s\"",
! 								get_rel_name(overlapped->relid))));
! 
! 				/* CHECK ( key = ANY ( values ) ) */
! 				check_expr = (Node *) makeA_Expr(AEXPR_IN, opr, partkey,
! 												 (Node *) def->values, -1);
! 			}
! 			else
! 			{
! 				List *all_values = gatherPartitionValues(partitions, elmtype,
! 														 elmlen, elmbyval);
! 
! 				/* CHECK ( NOT (key = ANY ( values ) ) ) */
! 				if (all_values == NIL)
! 					check_expr = NULL;
! 				else
! 					check_expr = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
! 									(Node *) makeA_Expr(AEXPR_IN, opr, partkey,
! 										(Node *) all_values, -1), -1);
! 			}
! 
! 			break;
! 		}
! 		default:
! 			elog(ERROR, "unknown partition kind: %d", partkind);
! 			check_expr = NULL;	/* keep compiler quiet */
! 	}
! 
! 	/* Add partition constraint if there are no same check constraint. */
! 	if (check_expr != NULL)
! 	{
! 		TupleConstr	   *constr;
! 		Node		   *cooked_expr = NULL;
! 		bool			found = false;
! 
! 		if ((constr = RelationGetDescr(child)->constr) != NULL)
! 		{
! 			ParseState	   *pstate;
! 			RangeTblEntry  *rte;
! 			int				i;
! 
! 			pstate = make_parsestate(NULL);
! 			rte = addRangeTableEntryForRelation(pstate, child, NULL, false, true);
! 			addRTEtoQuery(pstate, rte, true, true, true);
! 			cooked_expr = cookConstraint(pstate, check_expr,
! 										 RelationGetRelationName(child));
! 			free_parsestate(pstate);
! 
! 			for (i = 0; i < constr->num_check; i++)
! 			{
! 				if (equal(cooked_expr, stringToNode(constr->check[i].ccbin)))
! 				{
! 					found = true;
! 					break;
! 				}
! 			}
! 		}
! 
! 		/* Add a new check constraint only when not found. */
! 		if (!found)
! 		{
! 			Constraint	   *check;
! 
! 			check = makeNode(Constraint);
! 			check->contype = CONSTR_CHECK;
! 			check->location = -1;
! 			check->raw_expr = (cooked_expr ? NULL : check_expr);
! 			check->cooked_expr = (cooked_expr ? nodeToString(cooked_expr) : NULL);
! 			ATAddCheckConstraint(wqueue, tab, child, check, false, false);
! 		}
! 	}
! 
! 	/* overflow partition has an empty array. */
! 	return construct_array(values, nvalues, elmtype, elmlen, elmbyval, elmalign);
! }
! 
! /*
!  * Return left and right range partitions.
!  */
! static void
! findRangePartition(Datum value, List *partitions, FmgrInfo *ltfn,
! 				   Partition **left, Partition **right)
! {
! 	ListCell		   *cell;
! 
! 	Assert(left != NULL);
! 	Assert(right != NULL);
! 
! 	*left = *right = NULL;
! 
! 	foreach(cell, partitions)
! 	{
! 		Partition *p = (Partition *) lfirst(cell);
! 
! 		if (p->nvalues > 0 &&
! 			DatumGetBool(FunctionCall2(ltfn, p->values[0], value)))
! 		{
! 			/* left < p < value */
! 			if (*left == NULL || DatumGetBool(FunctionCall2(ltfn,
! 									(*left)->values[0], p->values[0])))
! 				*left = p;
! 		}
! 		else
! 		{
! 			/* value <= p < right */
! 			if (*right == NULL || (*right)->nvalues == 0 ||
! 				(p->nvalues > 0 && DatumGetBool(FunctionCall2(ltfn,
! 										p->values[0], (*right)->values[0]))))
! 				*right = p;
! 		}
! 	}
! }
! 
! /*
!  * Return an overlapped list partition, or NULL if not found.
!  */
! static Partition *
! findOverlappedListPartition(const Datum *values, int nvalues,
! 							List *partitions, FmgrInfo *eqfn)
! {
! 	ListCell   *cell;
! 	int			i;
! 	int			j;
! 
! 	foreach(cell, partitions)
! 	{
! 		Partition *p = (Partition *) lfirst(cell);
! 
! 		for (i = 0; i < p->nvalues; i++)
! 			for (j = 0; j < nvalues; j++)
! 				if (DatumGetBool(FunctionCall2(eqfn, p->values[i], values[j])))
! 					return p;
! 	}
! 
! 	return NULL;
! }
! 
! /*
!  * Gather partition values as a list of Const nodes.
!  */
! static List *
! gatherPartitionValues(List *partitions, Oid elmtype, int elmlen, bool elmbyval)
! {
! 	List	   *all_values = NIL;
! 	ListCell   *cell;
! 	int			i;
! 
! 	foreach(cell, partitions)
! 	{
! 		Partition *p = (Partition *) lfirst(cell);
! 
! 		for (i = 0; i < p->nvalues; i++)
! 		{
! 			Const *value = makeConst(elmtype, -1, elmlen,
! 				p->values[i], false, elmbyval);
! 			all_values = lappend(all_values, value);
! 		}
! 	}
! 
! 	return all_values;
! }
! 
! /*
!  * evaluateValues - evaluate a list of expressions to build a datum array.
!  */
! static Datum *
! evaluateValues(Oid typid, int typlen, bool typbyval, List *values, int *length)
! {
! 	ListCell	   *cell;
! 	ParseState	   *pstate;
! 	EState		   *estate;
! 	ExprContext	   *econtext;
! 	int				i;
! 	Datum		   *datum;
! 
! 	*length = list_length(values);
! 	if (*length < 1)
! 		return NULL;
! 
! 	datum = (Datum *) palloc(*length * sizeof(Datum));
! 	pstate = make_parsestate(NULL);
! 	estate = CreateExecutorState();
! 	econtext = GetPerTupleExprContext(estate);
! 
! 	i = 0;
! 	foreach(cell, values)
! 	{
! 		Node		   *value = (Node *) lfirst(cell);
! 		bool			isnull;
! 		ExprState	   *expr;
! 		MemoryContext	oldcxt;
! 
! 		oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
! 
! 		value = transformExpr(pstate, value);
! 		value = coerce_to_specific_type(pstate, value, typid, "PARTITION");
! 		expr = ExecPrepareExpr((Expr *) value, estate);
! 
! 		datum[i] = ExecEvalExpr(expr, econtext, &isnull, NULL);
! 		if (isnull)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 					 errmsg("partition key must not be NULL")));
! 
! 		MemoryContextSwitchTo(oldcxt);
! 
! 		if (!typbyval)
! 		{
! 			if (typlen == -1)
! 				datum[i] = PointerGetDatum(PG_DETOAST_DATUM_COPY(datum[i]));
! 			else
! 				datum[i] = datumCopy(datum[i], false, typlen);
! 		}
! 
! 		ResetPerTupleExprContext(estate);
! 		i++;
! 	}
! 
! 	FreeExecutorState(estate);
! 	free_parsestate(pstate);
! 
! 	return datum;
! }
! 
! static const char *
! getPartitionKindName(char partkind)
! {
! 	switch (partkind)
! 	{
! 		case PARTITION_BY_RANGE:
! 			return "RANGE";
! 		case PARTITION_BY_LIST:
! 			return "LIST";
! 		default:
! 			return "???";
! 	}
  }
  
  /*
*************** MergeConstraintsIntoExisting(Relation ch
*** 7459,7465 ****
  }
  
  /*
!  * ALTER TABLE NO INHERIT
   *
   * Drop a parent from the child's parents. This just adjusts the attinhcount
   * and attislocal of the columns and removes the pg_inherit and pg_depend
--- 7959,7965 ----
  }
  
  /*
!  * ALTER TABLE NO INHERIT / DETACH PARTITION
   *
   * Drop a parent from the child's parents. This just adjusts the attinhcount
   * and attislocal of the columns and removes the pg_inherit and pg_depend
*************** static void
*** 7478,7483 ****
--- 7978,8013 ----
  ATExecDropInherit(Relation rel, RangeVar *parent)
  {
  	Relation	parent_rel;
+ 
+ 	/*
+ 	 * AccessShareLock on the parent is probably enough, seeing that DROP
+ 	 * TABLE doesn't lock parent tables at all.  We need some lock since we'll
+ 	 * be inspecting the parent's schema.
+ 	 */
+ 	parent_rel = heap_openrv(parent, AccessShareLock);
+ 
+ 	dropInherit(rel, parent_rel);
+ 
+ 	/* close rel, but keep lock until commit */
+ 	relation_close(parent_rel, NoLock);
+ }
+ 
+ static void
+ ATExecDetachPartition(Relation rel, RangeVar *child)
+ {
+ 	Relation	child_rel;
+ 
+ 	child_rel = heap_openrv(child, AccessExclusiveLock);
+ 
+ 	dropInherit(child_rel, rel);
+ 
+ 	/* close rel, but keep lock until commit */
+ 	relation_close(child_rel, NoLock);
+ }
+ 
+ static void
+ dropInherit(Relation rel, Relation parent_rel)
+ {
  	Relation	catalogRelation;
  	SysScanDesc scan;
  	ScanKeyData key[3];
*************** ATExecDropInherit(Relation rel, RangeVar
*** 7489,7501 ****
  	bool		found = false;
  
  	/*
- 	 * AccessShareLock on the parent is probably enough, seeing that DROP
- 	 * TABLE doesn't lock parent tables at all.  We need some lock since we'll
- 	 * be inspecting the parent's schema.
- 	 */
- 	parent_rel = heap_openrv(parent, AccessShareLock);
- 
- 	/*
  	 * We don't bother to check ownership of the parent table --- ownership of
  	 * the child is presumed enough rights.
  	 */
--- 8019,8024 ----
*************** ATExecDropInherit(Relation rel, RangeVar
*** 7686,7694 ****
  
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
! 	/* keep our lock on the parent relation until commit */
! 	heap_close(parent_rel, NoLock);
  }
  
  
--- 8209,8309 ----
  
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
+ }
  
! 
! /*
!  * ALTER TABLE PARTITION BY / NO PARTITION
!  */
! static void
! ATExecPartitionBy(Relation rel, PartitionBy *defs)
! {
! 	Oid				relid = RelationGetRelid(rel);
! 	HeapTuple		tp;
! 	ObjectAddress	myself;
! 
! 	tp = SearchSysCache(PARTITIONKEY, ObjectIdGetDatum(relid), 0, 0, 0);
! 	if (HeapTupleIsValid(tp))
! 	{
! 		if (defs != NULL)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 			 errmsg("multiple partition keys for table \"%s\" are not allowed",
! 					RelationGetRelationName(rel))));
! 		ReleaseSysCache(tp);
! 	}
! 	else if (defs == NULL)
! 	{
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 				 errmsg("table \"%s\" has no partition key",
! 					RelationGetRelationName(rel))));
! 	}
! 
! 	myself.classId = PartitionRelationId;
! 	myself.objectId = relid;
! 	myself.objectSubId = 0;
! 
! 	if (defs == NULL)
! 	{
! 		/* ALTER TABLE <name> NO PARTITION */
! 		performDeletion(&myself, DROP_RESTRICT);
! 	}
! 	else
! 	{
! 		/* ALTER TABLE <name> PARTITION BY ... */
! 		Relation		catalogRel;
! 		TupleDesc		catalogDesc;
! 		Datum			datum[Natts_pg_partition];
! 		bool			nulls[Natts_pg_partition];
! 		HeapTuple		tuple;
! 		Node		   *partkey;
! 		Oid				keytype;
! 		Oid				opclass;
! 		ParseState	   *pstate;
! 		RangeTblEntry  *rte;
! 		ObjectAddress	opclassObject;
! 
! 		Assert(defs->key != NULL);
! 
! 		/* Transform the expression of partition key. */
! 		pstate = make_parsestate(NULL);
! 		rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
! 		addRTEtoQuery(pstate, rte, true, true, true);
! 		partkey = transformExpr(pstate, defs->key);
! 		free_parsestate(pstate);
! 
! 		/* Extract operator oid to compare partition keys */
! 		keytype = exprType(partkey);
! 		opclass = GetIndexOpClass(defs->opclass, keytype, "btree", BTREE_AM_OID);
! 
! 		/*
! 		 * Make the pg_partition entry
! 		 */
! 		memset(nulls, 0, sizeof(nulls));
! 		datum[0] = ObjectIdGetDatum(relid);			/* partrelid */
! 		datum[1] = ObjectIdGetDatum(opclass);		/* partopclass */
! 		datum[2] = CharGetDatum(defs->kind);		/* partkind */
! 		datum[3] = CStringGetTextDatum(nodeToString(partkey));	/* partkey */
! 
! 		catalogRel = heap_open(PartitionRelationId, RowExclusiveLock);
! 		catalogDesc = RelationGetDescr(catalogRel);
! 
! 		tuple = heap_form_tuple(catalogDesc, datum, nulls);
! 		simple_heap_insert(catalogRel, tuple);
! 		CatalogUpdateIndexes(catalogRel, tuple);
! 		heap_freetuple(tuple);
! 
! 		/* Store a dependency */
! 		recordDependencyOnSingleRelExpr(&myself, partkey, relid,
! 										DEPENDENCY_NORMAL, DEPENDENCY_AUTO);
! 		opclassObject.classId = OperatorClassRelationId;
! 		opclassObject.objectId = opclass;
! 		opclassObject.objectSubId = 0;
! 		recordDependencyOn(&myself, &opclassObject, DEPENDENCY_NORMAL);
! 
! 		heap_close(catalogRel, RowExclusiveLock);
! 	}
  }
  
  
diff -cprN head/src/backend/nodes/copyfuncs.c work/src/backend/nodes/copyfuncs.c
*** head/src/backend/nodes/copyfuncs.c	2010-01-06 09:46:25.748744000 +0900
--- work/src/backend/nodes/copyfuncs.c	2010-01-18 14:38:33.979469788 +0900
*************** _copyCreateStmt(CreateStmt *from)
*** 2511,2516 ****
--- 2511,2517 ----
  	COPY_NODE_FIELD(options);
  	COPY_SCALAR_FIELD(oncommit);
  	COPY_STRING_FIELD(tablespacename);
+ 	COPY_NODE_FIELD(partitions);
  
  	return newnode;
  }
*************** _copyAlterTSConfigurationStmt(AlterTSCon
*** 3469,3474 ****
--- 3470,3513 ----
  	return newnode;
  }
  
+ static PartitionDef *
+ _copyPartitionDef(PartitionDef *from)
+ {
+ 	PartitionDef *newnode = makeNode(PartitionDef);
+ 
+ 	COPY_SCALAR_FIELD(kind);
+ 	COPY_NODE_FIELD(name);
+ 	COPY_NODE_FIELD(values);
+ 	COPY_NODE_FIELD(options);
+ 	COPY_STRING_FIELD(tablespacename);
+ 
+ 	return newnode;
+ }
+ 
+ static PartitionBy *
+ _copyPartitionBy(PartitionBy *from)
+ {
+ 	PartitionBy *newnode = makeNode(PartitionBy);
+ 
+ 	COPY_SCALAR_FIELD(kind);
+ 	COPY_NODE_FIELD(key);
+ 	COPY_NODE_FIELD(opclass);
+ 	COPY_NODE_FIELD(defs);
+ 
+ 	return newnode;
+ }
+ 
+ static CreatePartitionStmt *
+ _copyCreatePartitionStmt(CreatePartitionStmt *from)
+ {
+ 	CreatePartitionStmt *newnode = makeNode(CreatePartitionStmt);
+ 
+ 	COPY_NODE_FIELD(parent);
+ 	COPY_NODE_FIELD(def);
+ 
+ 	return newnode;
+ }
+ 
  /* ****************************************************************
   *					pg_list.h copy functions
   * ****************************************************************
*************** copyObject(void *from)
*** 4139,4144 ****
--- 4178,4192 ----
  		case T_AlterTSConfigurationStmt:
  			retval = _copyAlterTSConfigurationStmt(from);
  			break;
+ 		case T_PartitionDef:
+ 			retval = _copyPartitionDef(from);
+ 			break;
+ 		case T_PartitionBy:
+ 			retval = _copyPartitionBy(from);
+ 			break;
+ 		case T_CreatePartitionStmt:
+ 			retval = _copyCreatePartitionStmt(from);
+ 			break;
  
  		case T_A_Expr:
  			retval = _copyAExpr(from);
diff -cprN head/src/backend/nodes/equalfuncs.c work/src/backend/nodes/equalfuncs.c
*** head/src/backend/nodes/equalfuncs.c	2010-01-06 09:46:25.748744000 +0900
--- work/src/backend/nodes/equalfuncs.c	2010-01-18 14:38:33.980469684 +0900
*************** _equalCreateStmt(CreateStmt *a, CreateSt
*** 1103,1108 ****
--- 1103,1109 ----
  	COMPARE_NODE_FIELD(options);
  	COMPARE_SCALAR_FIELD(oncommit);
  	COMPARE_STRING_FIELD(tablespacename);
+ 	COMPARE_NODE_FIELD(partitions);
  
  	return true;
  }
*************** _equalAlterTSConfigurationStmt(AlterTSCo
*** 1912,1917 ****
--- 1913,1950 ----
  }
  
  static bool
+ _equalPartitionDef(PartitionDef *a, PartitionDef *b)
+ {
+ 	COMPARE_SCALAR_FIELD(kind);
+ 	COMPARE_NODE_FIELD(name);
+ 	COMPARE_NODE_FIELD(values);
+ 	COMPARE_NODE_FIELD(options);
+ 	COMPARE_STRING_FIELD(tablespacename);
+ 
+ 	return true;
+ }
+ 
+ static bool
+ _equalPartitionBy(PartitionBy *a, PartitionBy *b)
+ {
+ 	COMPARE_SCALAR_FIELD(kind);
+ 	COMPARE_NODE_FIELD(key);
+ 	COMPARE_NODE_FIELD(opclass);
+ 	COMPARE_NODE_FIELD(defs);
+ 
+ 	return true;
+ }
+ 
+ static bool
+ _equalCreatePartitionStmt(CreatePartitionStmt *a, CreatePartitionStmt *b)
+ {
+ 	COMPARE_NODE_FIELD(parent);
+ 	COMPARE_NODE_FIELD(def);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalAExpr(A_Expr *a, A_Expr *b)
  {
  	COMPARE_SCALAR_FIELD(kind);
*************** equal(void *a, void *b)
*** 2830,2835 ****
--- 2863,2877 ----
  		case T_AlterTSConfigurationStmt:
  			retval = _equalAlterTSConfigurationStmt(a, b);
  			break;
+ 		case T_PartitionDef:
+ 			retval = _equalPartitionDef(a, b);
+ 			break;
+ 		case T_PartitionBy:
+ 			retval = _equalPartitionBy(a, b);
+ 			break;
+ 		case T_CreatePartitionStmt:
+ 			retval = _equalCreatePartitionStmt(a, b);
+ 			break;
  
  		case T_A_Expr:
  			retval = _equalAExpr(a, b);
diff -cprN head/src/backend/nodes/outfuncs.c work/src/backend/nodes/outfuncs.c
*** head/src/backend/nodes/outfuncs.c	2010-01-06 09:46:25.748744000 +0900
--- work/src/backend/nodes/outfuncs.c	2010-01-18 14:38:33.981064825 +0900
*************** _outCreateStmt(StringInfo str, CreateStm
*** 1788,1793 ****
--- 1788,1794 ----
  	WRITE_NODE_FIELD(options);
  	WRITE_ENUM_FIELD(oncommit, OnCommitAction);
  	WRITE_STRING_FIELD(tablespacename);
+ 	WRITE_NODE_FIELD(partitions);
  }
  
  static void
*************** _outConstraint(StringInfo str, Constrain
*** 2439,2444 ****
--- 2440,2468 ----
  	}
  }
  
+ static void
+ _outPartition(StringInfo str, PartitionDef *node)
+ {
+ 	WRITE_NODE_TYPE("PARTITIONDEF");
+ 
+ 	WRITE_CHAR_FIELD(kind);
+ 	WRITE_NODE_FIELD(name);
+ 	WRITE_NODE_FIELD(values);
+ 	WRITE_NODE_FIELD(options);
+ 	WRITE_STRING_FIELD(tablespacename);
+ }
+ 
+ static void
+ _outPartitionBy(StringInfo str, PartitionBy *node)
+ {
+ 	WRITE_NODE_TYPE("PARTITIONBY");
+ 
+ 	WRITE_CHAR_FIELD(kind);
+ 	WRITE_NODE_FIELD(key);
+ 	WRITE_NODE_FIELD(opclass);
+ 	WRITE_NODE_FIELD(defs);
+ }
+ 
  
  /*
   * _outNode -
*************** _outNode(StringInfo str, void *obj)
*** 2892,2897 ****
--- 2916,2927 ----
  			case T_XmlSerialize:
  				_outXmlSerialize(str, obj);
  				break;
+ 			case T_PartitionDef:
+ 				_outPartition(str, obj);
+ 				break;
+ 			case T_PartitionBy:
+ 				_outPartitionBy(str, obj);
+ 				break;
  
  			default:
  
diff -cprN head/src/backend/parser/gram.y work/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y	2010-01-06 14:42:58.290140000 +0900
--- work/src/backend/parser/gram.y	2010-01-18 16:57:14.145088423 +0900
*************** static TypeName *TableFuncTypeName(List 
*** 178,183 ****
--- 178,185 ----
  	AccessPriv			*accesspriv;
  	InsertStmt			*istmt;
  	VariableSetStmt		*vsetstmt;
+ 	PartitionDef		*partition;
+ 	PartitionBy			*partitionby;
  }
  
  %type <node>	stmt schema_stmt
*************** static TypeName *TableFuncTypeName(List 
*** 193,199 ****
  		CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
  		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
  		CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
! 		CreateUserStmt CreateUserMappingStmt CreateRoleStmt
  		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
  		DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
  		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
--- 195,201 ----
  		CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
  		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
  		CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
! 		CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePartitionStmt
  		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
  		DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
  		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
*************** static TypeName *TableFuncTypeName(List 
*** 436,441 ****
--- 438,447 ----
  %type <str>		opt_existing_window_name
  %type <ival>	opt_frame_clause frame_extent frame_bound
  
+ %type <partition> RangePartition ListPartition AnyPartition
+ %type <partitionby> PartitionBy OptPartition
+ %type <list>	OptRangePartitions RangePartitions RangeUpper
+ 				OptListPartitions ListPartitions ListValues ConstValues
  
  /*
   * Non-keyword token types.  These are hard-wired into the "flex" lexer.
*************** static TypeName *TableFuncTypeName(List 
*** 460,466 ****
  /* ordinary key words in alphabetical order */
  %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
  	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
! 	ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION
  
  	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
  	BOOLEAN_P BOTH BY
--- 466,472 ----
  /* ordinary key words in alphabetical order */
  %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
  	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
! 	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH AUTHORIZATION
  
  	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
  	BOOLEAN_P BOTH BY
*************** static TypeName *TableFuncTypeName(List 
*** 475,481 ****
  	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
  	DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
! 	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC
  	DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
  
  	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
--- 481,487 ----
  	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
  	DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
! 	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC DETACH
  	DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
  
  	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
*************** static TypeName *TableFuncTypeName(List 
*** 498,504 ****
  	KEY
  
  	LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
! 	LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
  	LOCATION LOCK_P LOGIN_P
  
  	MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
--- 504,510 ----
  	KEY
  
  	LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
! 	LEAST LEFT LESS LEVEL LIKE LIMIT LIST LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
  	LOCATION LOCK_P LOGIN_P
  
  	MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
*************** static TypeName *TableFuncTypeName(List 
*** 526,532 ****
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
  	SYMMETRIC SYSID SYSTEM_P
  
! 	TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
  	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P
  
--- 532,538 ----
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
  	SYMMETRIC SYSID SYSTEM_P
  
! 	TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THAN THEN TIME TIMESTAMP
  	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P
  
*************** stmt :
*** 680,685 ****
--- 686,692 ----
  			| CreateRoleStmt
  			| CreateUserStmt
  			| CreateUserMappingStmt
+ 			| CreatePartitionStmt
  			| CreatedbStmt
  			| DeallocateStmt
  			| DeclareCursorStmt
*************** schema_stmt:
*** 1138,1143 ****
--- 1145,1151 ----
  			| CreateTrigStmt
  			| GrantStmt
  			| ViewStmt
+ 			| CreatePartitionStmt
  		;
  
  
*************** alter_table_cmd:
*** 1894,1899 ****
--- 1902,1939 ----
  					n->def = (Node *)$2;
  					$$ = (Node *)n;
  				}
+ 			/* ALTER TABLE <name> PARTITION BY ... */
+ 			| PartitionBy
+ 				{
+ 					AlterTableCmd *n = makeNode(AlterTableCmd);
+ 					n->subtype = AT_PartitionBy;
+ 					n->def = (Node *)$1;
+ 					$$ = (Node *)n;
+ 				}
+ 			/* ALTER TABLE <name> NO PARTITION */
+ 			| NO PARTITION
+ 				{
+ 					AlterTableCmd *n = makeNode(AlterTableCmd);
+ 					n->subtype = AT_PartitionBy;
+ 					n->def = NULL;
+ 					$$ = (Node *)n;
+ 				}
+ 			/* ALTER TABLE <name> ATTACH PARTITION <child> */
+ 			| ATTACH AnyPartition
+ 				{
+ 					AlterTableCmd *n = makeNode(AlterTableCmd);
+ 					n->subtype = AT_AttachPartition;
+ 					n->def = (Node *) $2;
+ 					$$ = (Node *)n;
+ 				}
+ 			/* ALTER TABLE <name> DETACH PARTITION <child> */
+ 			| DETACH PARTITION qualified_name
+ 				{
+ 					AlterTableCmd *n = makeNode(AlterTableCmd);
+ 					n->subtype = AT_DetachPartition;
+ 					n->def = (Node *) $3;
+ 					$$ = (Node *)n;
+ 				}
  		;
  
  alter_column_default:
*************** copy_generic_opt_arg_list_item:
*** 2181,2187 ****
   *****************************************************************************/
  
  CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
! 			OptInherit OptWith OnCommitOption OptTableSpace
  				{
  					CreateStmt *n = makeNode(CreateStmt);
  					$4->istemp = $2;
--- 2221,2227 ----
   *****************************************************************************/
  
  CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
! 			OptInherit OptWith OnCommitOption OptTableSpace OptPartition
  				{
  					CreateStmt *n = makeNode(CreateStmt);
  					$4->istemp = $2;
*************** CreateStmt:	CREATE OptTemp TABLE qualifi
*** 2192,2201 ****
  					n->options = $9;
  					n->oncommit = $10;
  					n->tablespacename = $11;
  					$$ = (Node *)n;
  				}
  		| CREATE OptTemp TABLE qualified_name OF qualified_name
! 			'(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace
  				{
  					/* SQL99 CREATE TABLE OF <UDT> (cols) seems to be satisfied
  					 * by our inheritance capabilities. Let's try it...
--- 2232,2242 ----
  					n->options = $9;
  					n->oncommit = $10;
  					n->tablespacename = $11;
+ 					n->partitions = $12;
  					$$ = (Node *)n;
  				}
  		| CREATE OptTemp TABLE qualified_name OF qualified_name
! 			'(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptPartition
  				{
  					/* SQL99 CREATE TABLE OF <UDT> (cols) seems to be satisfied
  					 * by our inheritance capabilities. Let's try it...
*************** CreateStmt:	CREATE OptTemp TABLE qualifi
*** 2209,2218 ****
--- 2250,2375 ----
  					n->options = $10;
  					n->oncommit = $11;
  					n->tablespacename = $12;
+ 					n->partitions = $13;
  					$$ = (Node *)n;
  				}
  		;
  
+ OptPartition:
+ 			PartitionBy				{ $$ = $1; }
+ 			| /*EMPTY*/				{ $$ = NULL; }
+ 		;
+ 
+ PartitionBy:
+ 			PARTITION BY RANGE '(' a_expr ')' opt_class OptRangePartitions
+ 				{
+ 					PartitionBy *n = makeNode(PartitionBy);
+ 					
+ 					n->kind = PARTITION_BY_RANGE;
+ 					n->key = $5;
+ 					n->opclass = $7;
+ 					n->defs = $8;
+ 					$$ = n;
+ 				}
+ 			| PARTITION BY LIST '(' a_expr ')' opt_class OptListPartitions
+ 				{
+ 					PartitionBy *n = makeNode(PartitionBy);
+ 					n->kind = PARTITION_BY_LIST;
+ 					n->key = $5;
+ 					n->opclass = $7;
+ 					n->defs = $8;
+ 					$$ = n;
+ 				}
+ 		;
+ 
+ OptRangePartitions:
+ 			'(' RangePartitions ')'					{ $$ = $2; }
+ 			| /*EMPTY*/								{ $$ = NIL; }
+ 		;
+ 
+ RangePartitions:
+ 			RangePartition							{ $$ = list_make1($1); }
+ 			| RangePartitions ',' RangePartition	{ $$ = lappend($1, $3); }
+ 		;
+ 
+ RangePartition:
+ 			PARTITION qualified_name VALUES LESS THAN RangeUpper OptWith OptTableSpace
+ 				{
+ 					PartitionDef *n = makeNode(PartitionDef);
+ 					n->kind = PARTITION_BY_RANGE;
+ 					n->name = $2;
+ 					n->values = $6;
+ 					n->options = $7;
+ 					n->tablespacename = $8;
+ 					$$ = n;
+ 				}
+ 		;
+ 
+ RangeUpper:
+ 			AexprConst						{ $$ = list_make1($1); }
+ 			| '(' AexprConst ')'			{ $$ = list_make1($2); }
+ 			| MAXVALUE						{ $$ = NIL; }
+ 			| '(' MAXVALUE ')'				{ $$ = NIL; }
+ 		;
+ 
+ OptListPartitions:
+ 			'(' ListPartitions ')'					{ $$ = $2; }
+ 			| /*EMPTY*/								{ $$ = NIL; }
+ 		;
+ 
+ ListPartitions:
+ 			ListPartition							{ $$ = list_make1($1); }
+ 			| ListPartitions ',' ListPartition		{ $$ = lappend($1, $3); }
+ 		;
+ 
+ ListPartition:
+ 			PARTITION qualified_name VALUES opt_in ListValues OptWith OptTableSpace
+ 				{
+ 					PartitionDef *n = makeNode(PartitionDef);
+ 					n->kind = PARTITION_BY_LIST;
+ 					n->name = $2;
+ 					n->values = $5;
+ 					n->options = $6;
+ 					n->tablespacename = $7;
+ 					$$ = n;
+ 				}
+ 		;
+ 
+ opt_in:		IN_P									{}
+ 			| /*EMPTY*/								{}
+ 		;
+ 
+ ListValues:
+ 			DEFAULT							{ $$ = NIL; }
+ 			| '(' DEFAULT ')'				{ $$ = NIL; }
+ 			| '(' ConstValues ')'			{ $$ = $2; }
+ 		;
+ 
+ ConstValues:
+ 			AexprConst						{ $$ = list_make1($1); }
+ 			| ConstValues ',' AexprConst	{ $$ = lappend($1, $3); }
+ 		;
+ 
+ /* for ALTER TABLE ATTACH PARTITION. No WITH and TABLESPACE supported. */
+ AnyPartition:
+ 			PARTITION qualified_name VALUES LESS THAN RangeUpper
+ 				{
+ 					PartitionDef *n = makeNode(PartitionDef);
+ 					n->kind = PARTITION_BY_RANGE;
+ 					n->name = $2;
+ 					n->values = $6;
+ 					$$ = n;
+ 				}
+ 			| PARTITION qualified_name VALUES opt_in ListValues
+ 				{
+ 					PartitionDef *n = makeNode(PartitionDef);
+ 					n->kind = PARTITION_BY_LIST;
+ 					n->name = $2;
+ 					n->values = $5;
+ 					$$ = n;
+ 				}
+ 		;
+ 
  /*
   * Redundancy here is needed to avoid shift/reduce conflicts,
   * since TEMP is not a reserved word.  See also OptTempTableName.
*************** opt_with_data:
*** 2732,2737 ****
--- 2889,2930 ----
  /*****************************************************************************
   *
   *		QUERY :
+  *				CREATE PARTITION name
+  *
+  *****************************************************************************/
+ 
+ CreatePartitionStmt:
+ 			CREATE PARTITION qualified_name ON qualified_name
+ 				VALUES LESS THAN RangeUpper OptWith OptTableSpace
+ 				{
+ 					CreatePartitionStmt *n = makeNode(CreatePartitionStmt);
+ 					n->parent = $5;
+ 					n->def = makeNode(PartitionDef);
+ 					n->def->kind = PARTITION_BY_RANGE;
+ 					n->def->name = $3;
+ 					n->def->values = $9;
+ 					n->def->options = $10;
+ 					n->def->tablespacename = $11;
+ 					$$ = (Node *)n;
+ 				}
+ 			| CREATE PARTITION qualified_name ON qualified_name
+ 				VALUES opt_in ListValues OptWith OptTableSpace
+ 				{
+ 					CreatePartitionStmt *n = makeNode(CreatePartitionStmt);
+ 					n->parent = $5;
+ 					n->def = makeNode(PartitionDef);
+ 					n->def->kind = PARTITION_BY_LIST;
+ 					n->def->name = $3;
+ 					n->def->values = $8;
+ 					n->def->options = $9;
+ 					n->def->tablespacename = $10;
+ 					$$ = (Node *)n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
   *				CREATE SEQUENCE seqname
   *				ALTER SEQUENCE seqname
   *
*************** unreserved_keyword:
*** 10693,10698 ****
--- 10886,10892 ----
  			| ASSERTION
  			| ASSIGNMENT
  			| AT
+ 			| ATTACH
  			| BACKWARD
  			| BEFORE
  			| BEGIN_P
*************** unreserved_keyword:
*** 10738,10743 ****
--- 10932,10938 ----
  			| DELETE_P
  			| DELIMITER
  			| DELIMITERS
+ 			| DETACH
  			| DICTIONARY
  			| DISABLE_P
  			| DISCARD
*************** unreserved_keyword:
*** 10795,10801 ****
--- 10990,10998 ----
  			| LAST_P
  			| LC_COLLATE_P
  			| LC_CTYPE_P
+ 			| LESS
  			| LEVEL
+ 			| LIST
  			| LISTEN
  			| LOAD
  			| LOCAL
*************** unreserved_keyword:
*** 10901,10906 ****
--- 11098,11104 ----
  			| TEMPLATE
  			| TEMPORARY
  			| TEXT_P
+ 			| THAN
  			| TRANSACTION
  			| TRIGGER
  			| TRUNCATE
diff -cprN head/src/backend/parser/parse_utilcmd.c work/src/backend/parser/parse_utilcmd.c
*** head/src/backend/parser/parse_utilcmd.c	2010-01-04 09:10:26.638773000 +0900
--- work/src/backend/parser/parse_utilcmd.c	2010-01-18 14:38:33.984110069 +0900
*************** static void transformFKConstraints(Parse
*** 116,121 ****
--- 116,122 ----
  					   CreateStmtContext *cxt,
  					   bool skipValidation,
  					   bool isAddConstraint);
+ static void transformPartitionBy(ParseState *pstate, CreateStmtContext *cxt, PartitionBy *defs);
  static void transformConstraintAttrs(ParseState *pstate, List *constraintList);
  static void transformColumnType(ParseState *pstate, ColumnDef *column);
  static void setSchemaName(char *context_schema, char **stmt_schema_name);
*************** transformCreateStmt(CreateStmt *stmt, co
*** 235,240 ****
--- 236,259 ----
  	transformFKConstraints(pstate, &cxt, true, false);
  
  	/*
+ 	 * Postprocess partition related information.
+ 	 */
+ 	if (stmt->partitions)
+ 	{
+ 		AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
+ 		AlterTableCmd  *cmd = makeNode(AlterTableCmd);
+ 
+ 		cmd->subtype = AT_PartitionBy;
+ 		cmd->def = (Node *) stmt->partitions;
+ 
+ 		alterstmt->relation = cxt.relation;
+ 		alterstmt->relkind = OBJECT_TABLE;
+ 		alterstmt->cmds = list_make1(cmd);
+ 
+ 		cxt.alist = lappend(cxt.alist, alterstmt);
+ 	}
+ 
+ 	/*
  	 * Output results.
  	 */
  	stmt->tableElts = cxt.columns;
*************** transformFKConstraints(ParseState *pstat
*** 1482,1487 ****
--- 1501,1574 ----
  	}
  }
  
+ /* CREATE PARTITION */
+ List *
+ transformCreatePartition(PartitionDef *def, RangeVar *parent)
+ {
+ 	List		   *result = NIL;
+ 	CreateStmt	   *create;
+ 	InhRelation	   *like;
+ 	AlterTableStmt *alter;
+ 	AlterTableCmd  *attach;
+ 	PartitionDef   *partition;
+ 
+ 	/* Use the same schema as the parent if not specified. */
+ 	if (def->name->schemaname == NULL)
+ 		def->name->schemaname = parent->schemaname;
+ 	def->name->istemp = parent->istemp;
+ 
+ 	/* CREATE TABLE partition (LIKE parent INCLUDING ALL) */
+ 	like = makeNode(InhRelation);
+ 	like->relation = parent;
+ 	like->options = CREATE_TABLE_LIKE_ALL;
+ 	create = makeNode(CreateStmt);
+ 	create->relation = def->name;
+ 	create->tableElts = list_make1(like);
+ 	create->inhRelations = NIL;
+ 	create->constraints = NIL;
+ 	create->options = def->options;
+ 	create->oncommit = ONCOMMIT_NOOP;
+ 	create->tablespacename = def->tablespacename;
+ 	create->partitions = NULL;
+ 	result = lappend(result, create);
+ 
+ 	/* ALTER TABLE partition INHERIT parent (AS PARTITION) */
+ 	partition = makeNode(PartitionDef);
+ 	partition->kind = def->kind;;
+ 	partition->name = def->name;
+ 	partition->values = def->values;
+ 	attach = makeNode(AlterTableCmd);
+ 	attach->subtype = AT_AttachPartition;
+ 	attach->def = (Node *) partition;
+ 	alter = makeNode(AlterTableStmt);
+ 	alter->relation = parent;
+ 	alter->cmds = list_make1(attach);
+ 	alter->relkind = OBJECT_TABLE;
+ 	result = lappend(result, alter);
+ 
+ 	return result;
+ }
+ 
+ /* PARTITION BY / NO PARTITION */
+ static void
+ transformPartitionBy(ParseState *pstate, CreateStmtContext *cxt,
+ 					 PartitionBy *partitions)
+ {
+ 	ListCell	   *cell;
+ 
+ 	if (partitions == NULL)
+ 		return;
+ 
+ 	/* Expand partition clauses to CREATE TABLE. */
+ 	foreach (cell, partitions->defs)
+ 	{
+ 		PartitionDef *def = (PartitionDef *) lfirst(cell);
+ 
+ 		cxt->alist = list_concat(cxt->alist,
+ 				transformCreatePartition(def, cxt->relation));
+ 	}
+ }
+ 
  /*
   * transformIndexStmt - parse analysis for CREATE INDEX
   *
*************** transformAlterTableStmt(AlterTableStmt *
*** 1971,1976 ****
--- 2058,2068 ----
  				newcmds = lappend(newcmds, cmd);
  				break;
  
+ 			case AT_PartitionBy:
+ 				newcmds = lappend(newcmds, cmd);
+ 				transformPartitionBy(pstate, &cxt, (PartitionBy *) cmd->def);
+ 				break;
+ 
  			default:
  				newcmds = lappend(newcmds, cmd);
  				break;
diff -cprN head/src/backend/tcop/utility.c work/src/backend/tcop/utility.c
*** head/src/backend/tcop/utility.c	2010-01-18 09:26:23.915594000 +0900
--- work/src/backend/tcop/utility.c	2010-01-18 14:38:33.984110069 +0900
*************** check_xact_readonly(Node *parsetree)
*** 219,224 ****
--- 219,225 ----
  		case T_AlterUserMappingStmt:
  		case T_DropUserMappingStmt:
  		case T_AlterTableSpaceOptionsStmt:
+ 		case T_CreatePartitionStmt:
  			ereport(ERROR,
  					(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
  					 errmsg("transaction is read-only")));
*************** standard_ProcessUtility(Node *parsetree,
*** 565,570 ****
--- 566,575 ----
  			RemoveUserMapping((DropUserMappingStmt *) parsetree);
  			break;
  
+ 		case T_CreatePartitionStmt:
+ 			CreatePartition((CreatePartitionStmt *) parsetree, queryString);
+ 			break;
+ 
  		case T_DropStmt:
  			{
  				DropStmt   *stmt = (DropStmt *) parsetree;
*************** CreateCommandTag(Node *parsetree)
*** 1497,1502 ****
--- 1502,1511 ----
  			tag = "DROP USER MAPPING";
  			break;
  
+ 		case T_CreatePartitionStmt:
+ 			tag = "CREATE PARTITION";
+ 			break;
+ 
  		case T_DropStmt:
  			switch (((DropStmt *) parsetree)->removeType)
  			{
*************** GetCommandLogLevel(Node *parsetree)
*** 2256,2261 ****
--- 2265,2271 ----
  		case T_CreateUserMappingStmt:
  		case T_AlterUserMappingStmt:
  		case T_DropUserMappingStmt:
+ 		case T_CreatePartitionStmt:
  			lev = LOGSTMT_DDL;
  			break;
  
diff -cprN head/src/backend/utils/adt/ruleutils.c work/src/backend/utils/adt/ruleutils.c
*** head/src/backend/utils/adt/ruleutils.c	2010-01-18 09:26:23.915594000 +0900
--- work/src/backend/utils/adt/ruleutils.c	2010-01-18 14:38:33.985107957 +0900
***************
*** 26,31 ****
--- 26,32 ----
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_depend.h"
  #include "catalog/pg_language.h"
+ #include "catalog/pg_inherits_fn.h"
  #include "catalog/pg_opclass.h"
  #include "catalog/pg_operator.h"
  #include "catalog/pg_proc.h"
*************** pg_get_function_result(PG_FUNCTION_ARGS)
*** 1882,1887 ****
--- 1883,2023 ----
  }
  
  /*
+  * pg_get_partitiondef - Get the definition of a partition
+  */
+ Datum
+ pg_get_partitiondef(PG_FUNCTION_ARGS)
+ {
+ 	Oid			parentOid = PG_GETARG_OID(0);
+ 	bool		alter_fmt = PG_GETARG_OID(1);
+ 
+ 	Oid			parentNsp = get_rel_namespace(parentOid);
+ 	char	   *parentName;
+ 	char		partkind;
+ 	Node	   *partkey;
+ 	Oid			partopclass;
+ 	List	   *partitions;
+ 	Oid			typid;
+ 	const char *prefix;
+ 	const char *suffix;
+ 	const char *overflow;
+ 	StringInfoData buf;
+ 
+ 	partitions = get_partitions(parentOid, &partkind, &partkey, &partopclass);
+ 	if (partkind == 0)
+ 		PG_RETURN_NULL();	/* not partitioned */
+ 
+ 	typid = exprType(partkey);
+ 
+ 	initStringInfo(&buf);
+ 
+ 	/* Append definition of partition key. */
+ 	if (alter_fmt)
+ 	{
+ 		parentName = quote_qualified_identifier(
+ 			get_namespace_name(parentNsp), get_rel_name(parentOid));
+ 
+ 		appendStringInfo(&buf, "ALTER TABLE %s PARTITION BY ", parentName);
+ 	}
+ 	else
+ 		parentName = NULL;
+ 
+ 	switch (partkind)
+ 	{
+ 		case PARTITION_BY_RANGE:
+ 			appendStringInfoString(&buf, "RANGE");
+ 			prefix = "LESS THAN ";
+ 			suffix = "";
+ 			overflow = "MAXVALUE";
+ 			break;
+ 		case PARTITION_BY_LIST:
+ 			appendStringInfoString(&buf, "LIST");
+ 			prefix = "(";
+ 			suffix = ")";
+ 			overflow = "DEFAULT";
+ 			break;
+ 		default:
+ 			elog(ERROR, "unknown partition kind: %d", partkind);
+ 			prefix = suffix = overflow = NULL;
+ 			break;
+ 	}
+ 	appendStringInfoString(&buf, " (");
+ 	appendStringInfoString(&buf, deparse_expression_pretty(
+ 		partkey, deparse_context_for(get_rel_name(parentOid), parentOid),
+ 		false, false, 0, 0));
+ 	appendStringInfoChar(&buf, ')');
+ 	get_opclass_name(partopclass, typid, &buf);
+ 
+ 	/* Append definitions of partitions. */
+ 	if (list_length(partitions) > 0)
+ 	{
+ 		ListCell   *cell;
+ 		Oid			typoutput;
+ 		bool		typIsVarlena;
+ 		FmgrInfo	outfn;
+ 		bool		needcomma = false;
+ 
+ 		getTypeOutputInfo(typid, &typoutput, &typIsVarlena);
+ 		fmgr_info(typoutput, &outfn);
+ 
+ 		if (alter_fmt)
+ 			appendStringInfoString(&buf, ";\n");
+ 		else
+ 			appendStringInfoString(&buf, "\n(\n");
+ 
+ 		foreach(cell, partitions)
+ 		{
+ 			Partition  *p = (Partition *) lfirst(cell);
+ 			Oid				childNsp;
+ 			char		   *nspname;
+ 			char		   *name;
+ 
+ 			/* Hide namespace if parent and child are in the same namespace. */
+ 			if ((childNsp = get_rel_namespace(p->relid)) != parentNsp)
+ 				nspname = get_namespace_name(childNsp);
+ 			else
+ 				nspname = NULL;
+ 			name = quote_qualified_identifier(nspname, get_rel_name(p->relid));
+ 
+ 			if (needcomma)
+ 				appendStringInfoString(&buf, ",\n");
+ 
+ 			if (alter_fmt)
+ 				appendStringInfo(&buf, "ALTER TABLE %s ATTACH ", parentName);
+ 			else
+ 				appendStringInfoString(&buf, "    ");
+ 			appendStringInfo(&buf, "PARTITION %s VALUES ", name);
+ 
+ 			appendStringInfoString(&buf, prefix);
+ 			if (p->nvalues > 0)
+ 			{
+ 				int		i;
+ 
+ 				for (i = 0; i < p->nvalues; i++)
+ 				{
+ 					if (i > 0)
+ 						appendStringInfo(&buf, ", ");
+ 					simple_quote_literal(&buf, DatumGetCString(
+ 						OutputFunctionCall(&outfn, p->values[i])));
+ 				}
+ 			}
+ 			else
+ 				appendStringInfoString(&buf, overflow);
+ 			appendStringInfoString(&buf, suffix);
+ 
+ 			if (alter_fmt)
+ 				appendStringInfoString(&buf, ";\n");
+ 			else
+ 				needcomma = true;
+ 		}
+ 		if (!alter_fmt)
+ 			appendStringInfoString(&buf, "\n)");
+ 	}
+ 
+ 	PG_RETURN_TEXT_P(string_to_text(buf.data));
+ }
+ 
+ /*
   * Guts of pg_get_function_result: append the function's return type
   * to the specified buffer.
   */
diff -cprN head/src/backend/utils/cache/lsyscache.c work/src/backend/utils/cache/lsyscache.c
*** head/src/backend/utils/cache/lsyscache.c	2010-01-05 09:17:58.505600000 +0900
--- work/src/backend/utils/cache/lsyscache.c	2010-01-18 14:38:33.986058042 +0900
***************
*** 32,39 ****
--- 32,41 ----
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/datum.h"
+ #include "utils/fmgroids.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
+ #include "utils/tqual.h"
  
  /* Hook for plugins to get control in get_attavgwidth() */
  get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
*************** get_opname(Oid opno)
*** 1058,1063 ****
--- 1060,1088 ----
  		return NULL;
  }
  
+ List *
+ get_opfullname(Oid opno)
+ {
+ 	HeapTuple	tp;
+ 
+ 	tp = SearchSysCache(OPEROID,
+ 						ObjectIdGetDatum(opno),
+ 						0, 0, 0);
+ 	if (HeapTupleIsValid(tp))
+ 	{
+ 		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ 		List	   *result;
+ 
+ 		result = list_make2(
+ 			makeString(get_namespace_name(optup->oprnamespace)),
+ 			makeString(pstrdup(NameStr(optup->oprname))));
+ 		ReleaseSysCache(tp);
+ 		return result;
+ 	}
+ 	else
+ 		return NULL;
+ }
+ 
  /*
   * op_input_types
   *
*************** get_roleid_checked(const char *rolname)
*** 2785,2787 ****
--- 2810,2813 ----
  				 errmsg("role \"%s\" does not exist", rolname)));
  	return roleid;
  }
+ 
diff -cprN head/src/backend/utils/cache/syscache.c work/src/backend/utils/cache/syscache.c
*** head/src/backend/utils/cache/syscache.c	2010-01-06 09:46:25.748744000 +0900
--- work/src/backend/utils/cache/syscache.c	2010-01-18 14:38:33.986058042 +0900
***************
*** 40,45 ****
--- 40,46 ----
  #include "catalog/pg_opclass.h"
  #include "catalog/pg_operator.h"
  #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_partition.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_rewrite.h"
  #include "catalog/pg_statistic.h"
*************** static const struct cachedesc cacheinfo[
*** 777,787 ****
  			0
  		},
  		128
  	}
  };
  
! static CatCache *SysCache[
! 						  lengthof(cacheinfo)];
  static int	SysCacheSize = lengthof(cacheinfo);
  static bool CacheInitialized = false;
  
--- 778,799 ----
  			0
  		},
  		128
+ 	},
+ 	{PartitionRelationId,		/* PARTITIONKEY */
+ 		PartitionRelidIndexId,
+ 		Anum_pg_partition_partrelid,
+ 		1,
+ 		{
+ 			Anum_pg_partition_partrelid,
+ 			0,
+ 			0,
+ 			0
+ 		},
+ 		64
  	}
  };
  
! static CatCache *SysCache[lengthof(cacheinfo)];
  static int	SysCacheSize = lengthof(cacheinfo);
  static bool CacheInitialized = false;
  
diff -cprN head/src/bin/pg_dump/common.c work/src/bin/pg_dump/common.c
*** head/src/bin/pg_dump/common.c	2010-01-04 09:10:26.638773000 +0900
--- work/src/bin/pg_dump/common.c	2010-01-18 14:38:33.986058042 +0900
*************** getSchemaData(int *numTablesPtr)
*** 222,227 ****
--- 222,231 ----
  		write_msg(NULL, "reading triggers\n");
  	getTriggers(tblinfo, numTables);
  
+ 	if (g_verbose)
+ 		write_msg(NULL, "reading partitions\n");
+ 	getPartitions(tblinfo, numTables);
+ 
  	*numTablesPtr = numTables;
  	return tblinfo;
  }
diff -cprN head/src/bin/pg_dump/pg_dump.c work/src/bin/pg_dump/pg_dump.c
*** head/src/bin/pg_dump/pg_dump.c	2010-01-18 09:26:23.915594000 +0900
--- work/src/bin/pg_dump/pg_dump.c	2010-01-18 14:38:33.989050564 +0900
*************** static void dumpUserMappings(Archive *fo
*** 165,170 ****
--- 165,171 ----
  				 const char *servername, const char *namespace,
  				 const char *owner, CatalogId catalogId, DumpId dumpId);
  static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo);
+ static void dumpPartition(Archive *fout, PartitionInfo *partinfo);
  
  static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
  		const char *type, const char *name, const char *subname,
*************** getInherits(int *numInherits)
*** 3853,3859 ****
  
  	/* find all the inheritance information */
  
! 	appendPQExpBuffer(query, "SELECT inhrelid, inhparent FROM pg_inherits");
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 3854,3864 ----
  
  	/* find all the inheritance information */
  
! 	if (g_fout->remoteVersion >= 80500)
! 		appendPQExpBuffer(query, "SELECT inhrelid, inhparent FROM pg_inherits "
! 								 "WHERE inhvalues IS NULL");
! 	else
! 		appendPQExpBuffer(query, "SELECT inhrelid, inhparent FROM pg_inherits");
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
*************** getDefaultACLs(int *numDefaultACLs)
*** 6063,6068 ****
--- 6068,6141 ----
  }
  
  /*
+  * getTriggers
+  *	  get information about every partition on a dumpable table
+  *
+  * Note: partition data is not returned directly to the caller, but it
+  * does get entered into the DumpableObject tables.
+  */
+ void
+ getPartitions(TableInfo tblinfo[], int numTables)
+ {
+ 	int			i,
+ 				j;
+ 	PQExpBuffer query = createPQExpBuffer();
+ 	PGresult   *res;
+ 	PartitionInfo *partinfo;
+ 	int			i_partrelid,
+ 				i_partdef;
+ 	int			ntups;
+ 
+ 	if (g_fout->remoteVersion < 80500)
+ 		return;
+ 
+ 	appendPQExpBuffer(query,
+ 			  "SELECT "
+ 			  "partrelid, "
+ 			  "pg_get_partitiondef(partrelid, true) AS partdef "
+ 			  "FROM pg_partition");
+ 	res = PQexec(g_conn, query->data);
+ 	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ 
+ 	i_partrelid = PQfnumber(res, "partrelid");
+ 	i_partdef = PQfnumber(res, "partdef");
+ 
+ 	ntups = PQntuples(res);
+ 	partinfo = (PartitionInfo *) malloc(ntups * sizeof(PartitionInfo));
+ 
+ 	for (j = 0; j < ntups; j++)
+ 	{
+ 		Oid		relid = atooid(PQgetvalue(res, j, i_partrelid));
+ 
+ 		for (i = 0; i < numTables; i++)
+ 		{
+ 			TableInfo  *tbinfo = &tblinfo[i];
+ 
+ 			if (tbinfo->dobj.catId.oid == relid)
+ 			{
+ 				if (g_verbose)
+ 					write_msg(NULL, "reading partition for table \"%s\"\n",
+ 							  tbinfo->dobj.name);
+ 
+ 				partinfo[j].dobj.objType = DO_PARTITION;
+ 				partinfo[j].dobj.catId.tableoid = 0;
+ 				partinfo[j].dobj.catId.oid = tbinfo->dobj.catId.oid;
+ 				AssignDumpId(&partinfo[j].dobj);
+ 				partinfo[j].dobj.name = tbinfo->dobj.name;
+ 				partinfo[j].dobj.namespace = tbinfo->dobj.namespace;
+ 				partinfo[j].dobj.dump = tbinfo->dobj.dump;
+ 				partinfo[j].parttable = tbinfo;
+ 				partinfo[j].partdef = strdup(PQgetvalue(res, j, i_partdef));
+ 				break;
+ 			}
+ 		}
+ 	}
+ 
+ 	PQclear(res);
+ 	destroyPQExpBuffer(query);
+ }
+ 
+ /*
   * dumpComment --
   *
   * This routine is used to dump any comments associated with the
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 6494,6499 ****
--- 6567,6575 ----
  						 dobj->dependencies, dobj->nDeps,
  						 dumpBlobComments, NULL);
  			break;
+ 		case DO_PARTITION:
+ 			dumpPartition(fout, (PartitionInfo *) dobj);
+ 			break;
  	}
  }
  
*************** dumpRule(Archive *fout, RuleInfo *rinfo)
*** 11931,11936 ****
--- 12007,12047 ----
  }
  
  /*
+  * dumpPartition
+  *		Dump a partition
+  */
+ static void
+ dumpPartition(Archive *fout, PartitionInfo *partinfo)
+ {
+ 	TableInfo  *tbinfo = partinfo->parttable;
+ 	PQExpBuffer cmd;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!partinfo->dobj.dump || dataOnly)
+ 		return;
+ 
+ 	cmd = createPQExpBuffer();
+ 
+ 	appendPQExpBufferStr(cmd, partinfo->partdef);
+ 
+ 	/*
+ 	 * DROP must be fully qualified in case same name appears in pg_catalog
+ 	 */
+ 	ArchiveEntry(fout, partinfo->dobj.catId, partinfo->dobj.dumpId,
+ 				 partinfo->dobj.name,
+ 				 tbinfo->dobj.namespace->dobj.name,
+ 				 NULL,
+ 				 tbinfo->rolname, false,
+ 				 "PARTITION", SECTION_POST_DATA,
+ 				 cmd->data, "", NULL,
+ 				 partinfo->dobj.dependencies, partinfo->dobj.nDeps,
+ 				 NULL, NULL);
+ 
+ 	destroyPQExpBuffer(cmd);
+ }
+ 
+ 
+ /*
   * getDependencies --- obtain available dependency data
   */
  static void
diff -cprN head/src/bin/pg_dump/pg_dump.h work/src/bin/pg_dump/pg_dump.h
*** head/src/bin/pg_dump/pg_dump.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/bin/pg_dump/pg_dump.h	2010-01-18 14:38:33.989050564 +0900
*************** typedef enum
*** 116,122 ****
  	DO_FOREIGN_SERVER,
  	DO_DEFAULT_ACL,
  	DO_BLOBS,
! 	DO_BLOB_COMMENTS
  } DumpableObjectType;
  
  typedef struct _dumpableObject
--- 116,123 ----
  	DO_FOREIGN_SERVER,
  	DO_DEFAULT_ACL,
  	DO_BLOBS,
! 	DO_BLOB_COMMENTS,
! 	DO_PARTITION
  } DumpableObjectType;
  
  typedef struct _dumpableObject
*************** typedef struct _defaultACLInfo
*** 442,447 ****
--- 443,455 ----
  	char	   *defaclacl;
  } DefaultACLInfo;
  
+ typedef struct _partitionInfo
+ {
+ 	DumpableObject dobj;
+ 	TableInfo  *parttable;		/* link to table the partition is for */
+ 	char	   *partdef;
+ } PartitionInfo;
+ 
  /* global decls */
  extern bool force_quotes;		/* double-quotes for identifiers flag */
  extern bool g_verbose;			/* verbose flag */
*************** extern TSConfigInfo *getTSConfigurations
*** 527,531 ****
--- 535,540 ----
  extern FdwInfo *getForeignDataWrappers(int *numForeignDataWrappers);
  extern ForeignServerInfo *getForeignServers(int *numForeignServers);
  extern DefaultACLInfo *getDefaultACLs(int *numDefaultACLs);
+ extern void getPartitions(TableInfo tblinfo[], int numTables);
  
  #endif   /* PG_DUMP_H */
diff -cprN head/src/bin/pg_dump/pg_dump_sort.c work/src/bin/pg_dump/pg_dump_sort.c
*** head/src/bin/pg_dump/pg_dump_sort.c	2010-01-04 09:10:26.638773000 +0900
--- work/src/bin/pg_dump/pg_dump_sort.c	2010-01-18 14:38:33.989050564 +0900
*************** static const int oldObjectTypePriority[]
*** 56,62 ****
  	4,							/* DO_FOREIGN_SERVER */
  	17,							/* DO_DEFAULT_ACL */
  	10,							/* DO_BLOBS */
! 	11							/* DO_BLOB_COMMENTS */
  };
  
  /*
--- 56,63 ----
  	4,							/* DO_FOREIGN_SERVER */
  	17,							/* DO_DEFAULT_ACL */
  	10,							/* DO_BLOBS */
! 	11,							/* DO_BLOB_COMMENTS */
! 	18							/* DO_PARTITION */
  };
  
  /*
*************** static const int newObjectTypePriority[]
*** 76,89 ****
  	9,							/* DO_CONVERSION */
  	16,							/* DO_TABLE */
  	18,							/* DO_ATTRDEF */
! 	23,							/* DO_INDEX */
! 	24,							/* DO_RULE */
! 	25,							/* DO_TRIGGER */
! 	22,							/* DO_CONSTRAINT */
! 	26,							/* DO_FK_CONSTRAINT */
  	2,							/* DO_PROCLANG */
  	8,							/* DO_CAST */
! 	19,							/* DO_TABLE_DATA */
  	17,							/* DO_DUMMY_TYPE */
  	10,							/* DO_TSPARSER */
  	12,							/* DO_TSDICT */
--- 77,90 ----
  	9,							/* DO_CONVERSION */
  	16,							/* DO_TABLE */
  	18,							/* DO_ATTRDEF */
! 	24,							/* DO_INDEX */
! 	25,							/* DO_RULE */
! 	26,							/* DO_TRIGGER */
! 	23,							/* DO_CONSTRAINT */
! 	27,							/* DO_FK_CONSTRAINT */
  	2,							/* DO_PROCLANG */
  	8,							/* DO_CAST */
! 	20,							/* DO_TABLE_DATA */
  	17,							/* DO_DUMMY_TYPE */
  	10,							/* DO_TSPARSER */
  	12,							/* DO_TSDICT */
*************** static const int newObjectTypePriority[]
*** 91,99 ****
  	13,							/* DO_TSCONFIG */
  	14,							/* DO_FDW */
  	15,							/* DO_FOREIGN_SERVER */
! 	27,							/* DO_DEFAULT_ACL */
! 	20,							/* DO_BLOBS */
! 	21							/* DO_BLOB_COMMENTS */
  };
  
  
--- 92,101 ----
  	13,							/* DO_TSCONFIG */
  	14,							/* DO_FDW */
  	15,							/* DO_FOREIGN_SERVER */
! 	28,							/* DO_DEFAULT_ACL */
! 	21,							/* DO_BLOBS */
! 	22,							/* DO_BLOB_COMMENTS */
! 	19							/* DO_PARTITION */
  };
  
  
*************** describeDumpableObject(DumpableObject *o
*** 1156,1161 ****
--- 1158,1168 ----
  					 "BLOB COMMENTS  (ID %d)",
  					 obj->dumpId);
  			return;
+ 		case DO_PARTITION:
+ 			snprintf(buf, bufsize,
+ 					 "PARTITION %s  (ID %d OID %u)",
+ 					 obj->name, obj->dumpId, obj->catId.oid);
+ 			return;
  	}
  	/* shouldn't get here */
  	snprintf(buf, bufsize,
diff -cprN head/src/bin/psql/describe.c work/src/bin/psql/describe.c
*** head/src/bin/psql/describe.c	2010-01-18 09:26:23.915594000 +0900
--- work/src/bin/psql/describe.c	2010-01-18 14:38:33.990414828 +0900
*************** describeOneTableDetails(const char *sche
*** 1987,1994 ****
  		}
  		PQclear(result);
  
  		/* print child tables */
! 		if (pset.sversion >= 80300)
  			printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid);
  		else
  			printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.relname;", oid);
--- 1987,2010 ----
  		}
  		PQclear(result);
  
+ 		/* print partitions */
+ 		if (pset.sversion >= 80500)
+ 		{
+ 			printfPQExpBuffer(&buf, "SELECT pg_catalog.pg_get_partitiondef('%s', false);", oid);
+ 			result = PSQLexec(buf.data, false);
+ 			if (!result)
+ 				goto error_return;
+ 			if (PQntuples(result) > 0 && !PQgetisnull(result, 0, 0))
+ 			{
+ 				printfPQExpBuffer(&buf, _("Partitions: %s"), PQgetvalue(result, 0, 0));
+ 				printTableAddFooter(&cont, buf.data);
+ 			}
+ 		}
+ 
  		/* print child tables */
! 		if (pset.sversion >= 80500)
! 			printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' AND i.inhvalues IS NULL ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid);
! 		else if (pset.sversion >= 80300)
  			printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid);
  		else
  			printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.relname;", oid);
diff -cprN head/src/include/catalog/dependency.h work/src/include/catalog/dependency.h
*** head/src/include/catalog/dependency.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/include/catalog/dependency.h	2010-01-18 14:38:33.991041919 +0900
*************** typedef enum ObjectClass
*** 148,153 ****
--- 148,154 ----
  	OCLASS_FOREIGN_SERVER,		/* pg_foreign_server */
  	OCLASS_USER_MAPPING,		/* pg_user_mapping */
  	OCLASS_DEFACL,				/* pg_default_acl */
+ 	OCLASS_PARTITION,			/* pg_partition */
  	MAX_OCLASS					/* MUST BE LAST */
  } ObjectClass;
  
diff -cprN head/src/include/catalog/heap.h work/src/include/catalog/heap.h
*** head/src/include/catalog/heap.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/include/catalog/heap.h	2010-01-18 14:38:33.991041919 +0900
*************** extern Node *cookDefault(ParseState *pst
*** 93,98 ****
--- 93,101 ----
  			Oid atttypid,
  			int32 atttypmod,
  			char *attname);
+ extern Node *cookConstraint(ParseState *pstate,
+ 			   Node *raw_constraint,
+ 			   char *relname);
  
  extern void DeleteRelationTuple(Oid relid);
  extern void DeleteAttributeTuples(Oid relid);
*************** extern void RemoveAttrDefault(Oid relid,
*** 101,106 ****
--- 104,110 ----
  				  DropBehavior behavior, bool complain);
  extern void RemoveAttrDefaultById(Oid attrdefId);
  extern void RemoveStatistics(Oid relid, AttrNumber attnum);
+ extern void RemovePartition(Oid relid);
  
  extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
  						  bool relhasoids);
diff -cprN head/src/include/catalog/indexing.h work/src/include/catalog/indexing.h
*** head/src/include/catalog/indexing.h	2010-01-18 09:26:23.915594000 +0900
--- work/src/include/catalog/indexing.h	2010-01-18 14:38:33.991041919 +0900
*************** DECLARE_UNIQUE_INDEX(pg_default_acl_oid_
*** 281,286 ****
--- 281,289 ----
  DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_role_setting using btree(setdatabase oid_ops, setrole oid_ops));
  #define DbRoleSettingDatidRolidIndexId	2965
  
+ DECLARE_UNIQUE_INDEX(pg_partition_relid_index, 2998, on pg_partition using btree(partrelid oid_ops));
+ #define PartitionRelidIndexId	2998
+ 
  /* last step of initialization script: build the indexes declared above */
  BUILD_INDICES
  
diff -cprN head/src/include/catalog/pg_inherits.h work/src/include/catalog/pg_inherits.h
*** head/src/include/catalog/pg_inherits.h	2010-01-05 10:25:20.891034000 +0900
--- work/src/include/catalog/pg_inherits.h	2010-01-18 14:38:33.991041919 +0900
***************
*** 21,26 ****
--- 21,29 ----
  
  #include "catalog/genbki.h"
  
+ /* See comments in pg_statistic.h. */
+ #define anyarray int
+ 
  /* ----------------
   *		pg_inherits definition.  cpp turns this into
   *		typedef struct FormData_pg_inherits
*************** CATALOG(pg_inherits,2611) BKI_WITHOUT_OI
*** 33,38 ****
--- 36,42 ----
  	Oid			inhrelid;
  	Oid			inhparent;
  	int4		inhseqno;
+ 	anyarray	inhvalues;	/* values for partition */
  } FormData_pg_inherits;
  
  /* ----------------
*************** typedef FormData_pg_inherits *Form_pg_in
*** 46,55 ****
   *		compiler constants for pg_inherits
   * ----------------
   */
! #define Natts_pg_inherits				3
  #define Anum_pg_inherits_inhrelid		1
  #define Anum_pg_inherits_inhparent		2
  #define Anum_pg_inherits_inhseqno		3
  
  /* ----------------
   *		pg_inherits has no initial contents
--- 50,60 ----
   *		compiler constants for pg_inherits
   * ----------------
   */
! #define Natts_pg_inherits				4
  #define Anum_pg_inherits_inhrelid		1
  #define Anum_pg_inherits_inhparent		2
  #define Anum_pg_inherits_inhseqno		3
+ #define Anum_pg_inherits_inhvalues		4
  
  /* ----------------
   *		pg_inherits has no initial contents
diff -cprN head/src/include/catalog/pg_inherits_fn.h work/src/include/catalog/pg_inherits_fn.h
*** head/src/include/catalog/pg_inherits_fn.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/include/catalog/pg_inherits_fn.h	2010-01-18 14:38:33.991041919 +0900
***************
*** 17,24 ****
--- 17,36 ----
  #include "nodes/pg_list.h"
  #include "storage/lock.h"
  
+ /*
+  * Partition - store partition values.
+  */
+ typedef struct Partition
+ {
+ 	Oid		relid;
+ 	int		nvalues;
+ 	Datum  *values;
+ } Partition;
+ 
  extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);
  extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode);
+ extern List *get_partitions(Oid parentrelId,
+ 							 char *kind, Node **key, Oid *opclass);
  extern bool has_subclass(Oid relationId);
  extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
  
diff -cprN head/src/include/catalog/pg_partition.h work/src/include/catalog/pg_partition.h
*** head/src/include/catalog/pg_partition.h	1970-01-01 09:00:00.000000000 +0900
--- work/src/include/catalog/pg_partition.h	2010-01-18 14:38:33.991041919 +0900
***************
*** 0 ****
--- 1,55 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_partition.h
+  *	  definition of the system "partition" relation (pg_partition)
+  *	  along with the relation's initial contents.
+  *
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  *
+  * $PostgreSQL: pgsql/src/include/catalog/pg_partition.h $
+  *
+  * NOTES
+  *	  the genbki.sh script reads this file and generates .bki
+  *	  information from the DATA() statements.
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PG_PARTITION_H
+ #define PG_PARTITION_H
+ 
+ #include "catalog/genbki.h"
+ 
+ /* ----------------
+  *		pg_partition definition.  cpp turns this into
+  *		typedef struct FormData_pg_partitions
+  * ----------------
+  */
+ #define PartitionRelationId 2997
+ 
+ CATALOG(pg_partition,2997) BKI_WITHOUT_OIDS
+ {
+ 	Oid		partrelid;		/* partitioned table oid */
+ 	Oid		partopclass;	/* operator class to compare keys */
+ 	char	partkind;		/* kind of partition: RANGE or LIST */
+ 	text	partkey;		/* partition key expression */
+ } FormData_pg_partition;
+ 
+ /* ----------------
+  *      Form_pg_partitions corresponds to a pointer to a tuple with
+  *      the format of pg_partitions relation.
+  * ----------------
+  */
+ typedef FormData_pg_partition *Form_pg_partition;
+ 
+ /* ----------------
+  *      compiler constants for pg_partitions
+  * ----------------
+  */
+ #define Natts_pg_partition				4
+ #define Anum_pg_partition_partrelid		1
+ #define Anum_pg_partition_partopclass	2
+ #define Anum_pg_partition_partkind		3
+ #define Anum_pg_partition_partkey		4
+ 
+ #endif   /* PG_PARTITIONS_H */
diff -cprN head/src/include/catalog/pg_proc.h work/src/include/catalog/pg_proc.h
*** head/src/include/catalog/pg_proc.h	2010-01-18 09:26:23.915594000 +0900
--- work/src/include/catalog/pg_proc.h	2010-01-18 14:38:33.993117056 +0900
*************** DATA(insert OID = 2232 (  pg_get_functio
*** 2315,2320 ****
--- 2315,2322 ----
  DESCR("identity argument list of a function");
  DATA(insert OID = 2165 (  pg_get_function_result	   PGNSP PGUID 12 1 0 0 f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_get_function_result _null_ _null_ _null_ ));
  DESCR("result type of a function");
+ DATA(insert OID = 2999 (  pg_get_partitiondef	PGNSP PGUID 12 1 0 0 f f f t f s 2 0 25 "26 16" _null_ _null_ _null_ _null_	pg_get_partitiondef _null_ _null_ _null_ ));
+ DESCR("definition of a partition");
  
  DATA(insert OID = 1686 (  pg_get_keywords		PGNSP PGUID 12 10 400 0 f f f t t s 0 0 2249 "" "{25,18,25}" "{o,o,o}" "{word,catcode,catdesc}" _null_ pg_get_keywords _null_ _null_ _null_ ));
  DESCR("list of SQL keywords");
diff -cprN head/src/include/commands/defrem.h work/src/include/commands/defrem.h
*** head/src/include/commands/defrem.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/include/commands/defrem.h	2010-01-18 14:38:33.993117056 +0900
*************** extern char *ChooseIndexName(const char 
*** 50,55 ****
--- 50,57 ----
  							 bool primary, bool isconstraint);
  extern List *ChooseIndexColumnNames(List *indexElems);
  extern Oid	GetDefaultOpClass(Oid type_id, Oid am_id);
+ extern Oid	GetIndexOpClass(List *opclass, Oid attrType,
+ 				const char *accessMethodName, Oid accessMethodId);
  
  /* commands/functioncmds.c */
  extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString);
diff -cprN head/src/include/commands/tablecmds.h work/src/include/commands/tablecmds.h
*** head/src/include/commands/tablecmds.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/include/commands/tablecmds.h	2010-01-18 14:38:33.994086938 +0900
*************** extern void RenameRelationInternal(Oid m
*** 53,58 ****
--- 53,60 ----
  					   const char *newrelname,
  					   Oid namespaceId);
  
+ extern void CreatePartition(CreatePartitionStmt *stmt, const char *queryString);
+ 
  extern void find_composite_type_dependencies(Oid typeOid,
  								 const char *origTblName,
  								 const char *origTypeName);
diff -cprN head/src/include/nodes/nodes.h work/src/include/nodes/nodes.h
*** head/src/include/nodes/nodes.h	2010-01-06 09:46:25.748744000 +0900
--- work/src/include/nodes/nodes.h	2010-01-18 14:38:33.994086938 +0900
*************** typedef enum NodeTag
*** 347,352 ****
--- 347,353 ----
  	T_AlterUserMappingStmt,
  	T_DropUserMappingStmt,
  	T_AlterTableSpaceOptionsStmt,
+ 	T_CreatePartitionStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
*************** typedef enum NodeTag
*** 385,390 ****
--- 386,394 ----
  	T_XmlSerialize,
  	T_WithClause,
  	T_CommonTableExpr,
+ 	T_PartitionDef,
+ 	T_PartitionBy,
+ 	T_AddInherit,
  
  	/*
  	 * TAGS FOR RANDOM OTHER STUFF
diff -cprN head/src/include/nodes/parsenodes.h work/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h	2010-01-18 09:26:23.915594000 +0900
--- work/src/include/nodes/parsenodes.h	2010-01-18 14:38:33.994086938 +0900
*************** typedef enum AlterTableType
*** 1135,1141 ****
  	AT_EnableReplicaRule,		/* ENABLE REPLICA RULE name */
  	AT_DisableRule,				/* DISABLE RULE name */
  	AT_AddInherit,				/* INHERIT parent */
! 	AT_DropInherit				/* NO INHERIT parent */
  } AlterTableType;
  
  typedef struct AlterTableCmd	/* one subcommand of an ALTER TABLE */
--- 1135,1144 ----
  	AT_EnableReplicaRule,		/* ENABLE REPLICA RULE name */
  	AT_DisableRule,				/* DISABLE RULE name */
  	AT_AddInherit,				/* INHERIT parent */
! 	AT_DropInherit,				/* NO INHERIT parent */
! 	AT_PartitionBy,				/* PARTITION BY / NO PARTITION */
! 	AT_AttachPartition,			/* ATTACH PARTITION */
! 	AT_DetachPartition			/* DETACH PARTITION */
  } AlterTableType;
  
  typedef struct AlterTableCmd	/* one subcommand of an ALTER TABLE */
*************** typedef struct VariableShowStmt
*** 1337,1342 ****
--- 1340,1379 ----
  	char	   *name;
  } VariableShowStmt;
  
+ /* ----------
+  * Partitioning definitions
+  * ----------
+  */
+ 
+ #define	PARTITION_BY_RANGE		'r'
+ #define	PARTITION_BY_LIST		'l'
+ 
+ typedef struct PartitionDef
+ {
+ 	NodeTag			type;
+ 	char			kind;			/* PARTITION_BY_xxx */
+ 	RangeVar	   *name;			/* name of partition */
+ 	List		   *values;			/* for RANGE and LIST */
+ 	List		   *options;		/* options from WITH clause */
+ 	char		   *tablespacename; /* table space to use, or NULL */
+ } PartitionDef;
+ 
+ typedef struct PartitionBy
+ {
+ 	NodeTag			type;
+ 	char			kind;			/* PARTITION_BY_xxx */
+ 	Node		   *key;			/* partition key expr */
+ 	List		   *opclass;		/* partition key operator class */
+ 	List		   *defs;			/* list of PartitionDef */
+ } PartitionBy;
+ 
+ typedef struct CreatePartitionStmt
+ {
+ 	NodeTag			type;
+ 	RangeVar	   *parent;			/* parent of the partition */
+ 	PartitionDef   *def;
+ } CreatePartitionStmt;
+ 
  /* ----------------------
   *		Create Table Statement
   *
*************** typedef struct CreateStmt
*** 1359,1364 ****
--- 1396,1402 ----
  	List	   *options;		/* options from WITH clause */
  	OnCommitAction oncommit;	/* what do we do at COMMIT? */
  	char	   *tablespacename; /* table space to use, or NULL */
+ 	PartitionBy *partitions;	/* partitioning definition, or NULL */
  } CreateStmt;
  
  /* ----------
diff -cprN head/src/include/parser/kwlist.h work/src/include/parser/kwlist.h
*** head/src/include/parser/kwlist.h	2010-01-06 14:42:58.290140000 +0900
--- work/src/include/parser/kwlist.h	2010-01-18 14:38:33.995186934 +0900
*************** PG_KEYWORD("assertion", ASSERTION, UNRES
*** 49,54 ****
--- 49,55 ----
  PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD)
  PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD)
  PG_KEYWORD("at", AT, UNRESERVED_KEYWORD)
+ PG_KEYWORD("attach", ATTACH, UNRESERVED_KEYWORD)
  PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
  PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
  PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD)
*************** PG_KEYWORD("delete", DELETE_P, UNRESERVE
*** 125,130 ****
--- 126,132 ----
  PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD)
  PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD)
  PG_KEYWORD("desc", DESC, RESERVED_KEYWORD)
+ PG_KEYWORD("detach", DETACH, UNRESERVED_KEYWORD)
  PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD)
  PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("discard", DISCARD, UNRESERVED_KEYWORD)
*************** PG_KEYWORD("lc_ctype", LC_CTYPE_P, UNRES
*** 217,225 ****
--- 219,229 ----
  PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
  PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
  PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+ PG_KEYWORD("less", LESS, UNRESERVED_KEYWORD)
  PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
  PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
  PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
+ PG_KEYWORD("list", LIST, UNRESERVED_KEYWORD)
  PG_KEYWORD("listen", LISTEN, UNRESERVED_KEYWORD)
  PG_KEYWORD("load", LOAD, UNRESERVED_KEYWORD)
  PG_KEYWORD("local", LOCAL, UNRESERVED_KEYWORD)
*************** PG_KEYWORD("temp", TEMP, UNRESERVED_KEYW
*** 365,370 ****
--- 369,375 ----
  PG_KEYWORD("template", TEMPLATE, UNRESERVED_KEYWORD)
  PG_KEYWORD("temporary", TEMPORARY, UNRESERVED_KEYWORD)
  PG_KEYWORD("text", TEXT_P, UNRESERVED_KEYWORD)
+ PG_KEYWORD("than", THAN, UNRESERVED_KEYWORD)
  PG_KEYWORD("then", THEN, RESERVED_KEYWORD)
  PG_KEYWORD("time", TIME, COL_NAME_KEYWORD)
  PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD)
diff -cprN head/src/include/parser/parse_utilcmd.h work/src/include/parser/parse_utilcmd.h
*** head/src/include/parser/parse_utilcmd.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/include/parser/parse_utilcmd.h	2010-01-18 14:38:33.995186934 +0900
*************** extern IndexStmt *transformIndexStmt(Ind
*** 24,28 ****
--- 24,29 ----
  extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
  				  List **actions, Node **whereClause);
  extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
+ extern List *transformCreatePartition(PartitionDef *def, RangeVar *parent);
  
  #endif   /* PARSE_UTILCMD_H */
diff -cprN head/src/include/utils/builtins.h work/src/include/utils/builtins.h
*** head/src/include/utils/builtins.h	2010-01-04 09:10:26.638773000 +0900
--- work/src/include/utils/builtins.h	2010-01-18 14:38:33.995186934 +0900
*************** extern Datum pg_get_functiondef(PG_FUNCT
*** 601,606 ****
--- 601,607 ----
  extern Datum pg_get_function_arguments(PG_FUNCTION_ARGS);
  extern Datum pg_get_function_identity_arguments(PG_FUNCTION_ARGS);
  extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
+ extern Datum pg_get_partitiondef(PG_FUNCTION_ARGS);
  extern char *deparse_expression(Node *expr, List *dpcontext,
  				   bool forceprefix, bool showimplicit);
  extern List *deparse_context_for(const char *aliasname, Oid relid);
diff -cprN head/src/include/utils/lsyscache.h work/src/include/utils/lsyscache.h
*** head/src/include/utils/lsyscache.h	2010-01-05 09:17:58.505600000 +0900
--- work/src/include/utils/lsyscache.h	2010-01-18 14:38:33.995186934 +0900
*************** extern Oid	get_opclass_family(Oid opclas
*** 66,71 ****
--- 66,72 ----
  extern Oid	get_opclass_input_type(Oid opclass);
  extern RegProcedure get_opcode(Oid opno);
  extern char *get_opname(Oid opno);
+ extern List *get_opfullname(Oid opno);
  extern void op_input_types(Oid opno, Oid *lefttype, Oid *righttype);
  extern bool op_mergejoinable(Oid opno);
  extern bool op_hashjoinable(Oid opno);
diff -cprN head/src/include/utils/syscache.h work/src/include/utils/syscache.h
*** head/src/include/utils/syscache.h	2010-01-06 09:46:25.748744000 +0900
--- work/src/include/utils/syscache.h	2010-01-18 14:38:33.996065868 +0900
*************** enum SysCacheIdentifier
*** 84,90 ****
  	TYPENAMENSP,
  	TYPEOID,
  	USERMAPPINGOID,
! 	USERMAPPINGUSERSERVER
  };
  
  extern void InitCatalogCache(void);
--- 84,91 ----
  	TYPENAMENSP,
  	TYPEOID,
  	USERMAPPINGOID,
! 	USERMAPPINGUSERSERVER,
! 	PARTITIONKEY
  };
  
  extern void InitCatalogCache(void);
diff -cprN head/src/test/regress/expected/partition.out work/src/test/regress/expected/partition.out
*** head/src/test/regress/expected/partition.out	1970-01-01 09:00:00.000000000 +0900
--- work/src/test/regress/expected/partition.out	2010-01-18 14:38:33.996065868 +0900
***************
*** 0 ****
--- 1,458 ----
+ --
+ -- RANGE PARTITION with timestamp
+ --
+ CREATE TABLE sales_range (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ )
+ PARTITION BY RANGE (sales_date)
+ (
+   PARTITION sales_2006 VALUES LESS THAN ('2007-01-01'),
+   PARTITION sales_2007 VALUES LESS THAN ('2008-01-01'),
+   PARTITION sales_2008 VALUES LESS THAN ('2009-01-01'),
+   PARTITION sales_max  VALUES LESS THAN (MAXVALUE)
+ );
+ \d+ sales_range
+                             Table "public.sales_range"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Partitions: RANGE (sales_date)
+ (
+     PARTITION sales_2006 VALUES LESS THAN 'Mon Jan 01 00:00:00 2007',
+     PARTITION sales_2007 VALUES LESS THAN 'Tue Jan 01 00:00:00 2008',
+     PARTITION sales_2008 VALUES LESS THAN 'Thu Jan 01 00:00:00 2009',
+     PARTITION sales_max VALUES LESS THAN MAXVALUE
+ )
+ Has OIDs: no
+ 
+ \d+ sales_2006
+                             Table "public.sales_2006"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_2006_sales_date_check" CHECK (sales_date < 'Mon Jan 01 00:00:00 2007'::timestamp without time zone)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ \d+ sales_2007
+                             Table "public.sales_2007"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_2007_sales_date_check" CHECK ('Mon Jan 01 00:00:00 2007'::timestamp without time zone <= sales_date AND sales_date < 'Tue Jan 01 00:00:00 2008'::timestamp without time zone)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ \d+ sales_2008
+                             Table "public.sales_2008"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_2008_sales_date_check" CHECK ('Tue Jan 01 00:00:00 2008'::timestamp without time zone <= sales_date AND sales_date < 'Thu Jan 01 00:00:00 2009'::timestamp without time zone)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ \d+ sales_max
+                              Table "public.sales_max"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_max_sales_date_check" CHECK ('Thu Jan 01 00:00:00 2009'::timestamp without time zone <= sales_date)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_range'::regclass ORDER BY 1;
+   inhrelid  | inhseqno |          inhvalues           
+ ------------+----------+------------------------------
+  sales_2006 |        1 | {"Mon Jan 01 00:00:00 2007"}
+  sales_2007 |        1 | {"Tue Jan 01 00:00:00 2008"}
+  sales_2008 |        1 | {"Thu Jan 01 00:00:00 2009"}
+  sales_max  |        1 | {}
+ (4 rows)
+ 
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+   partrelid  | partkind | pg_get_expr 
+ -------------+----------+-------------
+  sales_range | r        | sales_date
+ (1 row)
+ 
+ DROP TABLE sales_range CASCADE;
+ NOTICE:  drop cascades to 4 other objects
+ DETAIL:  drop cascades to table sales_2006
+ drop cascades to table sales_2007
+ drop cascades to table sales_2008
+ drop cascades to table sales_max
+ --
+ -- CREATE TABLE + ALTER TABLE PARTITION BY RANGE + ATTACH PARTITION
+ --
+ CREATE TABLE sales_range (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ );
+ ALTER TABLE sales_range PARTITION BY RANGE (sales_date) timestamp_ops
+ (
+   PARTITION sales_2006 VALUES LESS THAN '2007-01-01',
+   PARTITION sales_2007 VALUES LESS THAN '2008-01-01'
+ );
+ CREATE TABLE sales_2008 (LIKE sales_range INCLUDING ALL);
+ ALTER TABLE sales_range ATTACH PARTITION sales_2008 VALUES LESS THAN '2009-01-01';
+ -- ERROR: cannot split partition "sales_2008"
+ CREATE PARTITION sales_error ON sales_range VALUES LESS THAN '2008-06-06';
+ ERROR:  cannot split partition "sales_2008"
+ CREATE TABLE sales_max  (LIKE sales_range INCLUDING ALL);
+ ALTER TABLE sales_range ATTACH PARTITION sales_max  VALUES LESS THAN MAXVALUE;
+ \d+ sales_range
+                             Table "public.sales_range"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Partitions: RANGE (sales_date)
+ (
+     PARTITION sales_2006 VALUES LESS THAN 'Mon Jan 01 00:00:00 2007',
+     PARTITION sales_2007 VALUES LESS THAN 'Tue Jan 01 00:00:00 2008',
+     PARTITION sales_2008 VALUES LESS THAN 'Thu Jan 01 00:00:00 2009',
+     PARTITION sales_max VALUES LESS THAN MAXVALUE
+ )
+ Has OIDs: no
+ 
+ \d+ sales_2006
+                             Table "public.sales_2006"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_2006_sales_date_check" CHECK (sales_date < 'Mon Jan 01 00:00:00 2007'::timestamp without time zone)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ \d+ sales_2007
+                             Table "public.sales_2007"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_2007_sales_date_check" CHECK ('Mon Jan 01 00:00:00 2007'::timestamp without time zone <= sales_date AND sales_date < 'Tue Jan 01 00:00:00 2008'::timestamp without time zone)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ \d+ sales_2008
+                             Table "public.sales_2008"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_2008_sales_date_check" CHECK ('Tue Jan 01 00:00:00 2008'::timestamp without time zone <= sales_date AND sales_date < 'Thu Jan 01 00:00:00 2009'::timestamp without time zone)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ \d+ sales_max
+                              Table "public.sales_max"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_max_sales_date_check" CHECK ('Thu Jan 01 00:00:00 2009'::timestamp without time zone <= sales_date)
+ Inherits: sales_range
+ Has OIDs: no
+ 
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_range'::regclass ORDER BY 1;
+   inhrelid  | inhseqno |          inhvalues           
+ ------------+----------+------------------------------
+  sales_2006 |        1 | {"Mon Jan 01 00:00:00 2007"}
+  sales_2007 |        1 | {"Tue Jan 01 00:00:00 2008"}
+  sales_2008 |        1 | {"Thu Jan 01 00:00:00 2009"}
+  sales_max  |        1 | {}
+ (4 rows)
+ 
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+   partrelid  | partkind | pg_get_expr 
+ -------------+----------+-------------
+  sales_range | r        | sales_date
+ (1 row)
+ 
+ -- ERROR: cannot add partition to "sales_range", that has an overflow partition
+ CREATE PARTITION sales_error ON sales_range VALUES LESS THAN '2008-06-06';
+ ERROR:  cannot add partition to "sales_range", that has an overflow partition
+ -- ERROR: multiple partition keys for table "sales_range" are not allowed
+ ALTER TABLE sales_range PARTITION BY RANGE (salesman_id);
+ ERROR:  multiple partition keys for table "sales_range" are not allowed
+ -- ERROR: cannot add LIST partition "sales_error" to RANGE partitioned table "sales_range"
+ CREATE PARTITION sales_error ON sales_range VALUES IN ('list');
+ ERROR:  cannot add LIST partition "sales_error" to RANGE partitioned table "sales_range"
+ -- NO PARTITION will drop all inhvalues.
+ ALTER TABLE sales_range NO PARTITION;
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_range'::regclass ORDER BY 1;
+   inhrelid  | inhseqno | inhvalues 
+ ------------+----------+-----------
+  sales_2006 |        1 | 
+  sales_2007 |        1 | 
+  sales_2008 |        1 | 
+  sales_max  |        1 | 
+ (4 rows)
+ 
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+  partrelid | partkind | pg_get_expr 
+ -----------+----------+-------------
+ (0 rows)
+ 
+ DROP TABLE sales_range CASCADE;
+ NOTICE:  drop cascades to 4 other objects
+ DETAIL:  drop cascades to table sales_2006
+ drop cascades to table sales_2007
+ drop cascades to table sales_2008
+ drop cascades to table sales_max
+ --
+ -- 1000 RANGE PARTITIONS
+ --
+ CREATE TABLE sales_range (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ );
+ ALTER TABLE sales_range PARTITION BY RANGE (sales_date);
+ DO
+ $$
+ BEGIN
+   FOR i IN 1..1000 LOOP
+     EXECUTE 'CREATE PARTITION sales_' || i
+          || ' ON sales_range VALUES LESS THAN '
+          || quote_literal('2000-01-01'::timestamp + i * '1 day'::interval);
+   END LOOP;
+ END;
+ $$;
+ CREATE PARTITION sales_max ON sales_range VALUES LESS THAN MAXVALUE;
+ SET client_min_messages = WARNING;
+ DROP TABLE sales_range CASCADE;
+ RESET client_min_messages;
+ --
+ -- LIST PARTITION with varchar
+ --
+ CREATE TABLE sales_list (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ )
+ PARTITION BY LIST (sales_state) varchar_ops
+ (
+   PARTITION sales_asia  VALUES ('asia'),
+   PARTITION sales_euro  VALUES ('eu'),
+   PARTITION sales_us    VALUES ('usa', 'canada'),
+   PARTITION sales_other VALUES (DEFAULT)
+ );
+ \d+ sales_list
+                             Table "public.sales_list"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Partitions: LIST (sales_state) varchar_ops
+ (
+     PARTITION sales_asia VALUES ('asia'),
+     PARTITION sales_euro VALUES ('eu'),
+     PARTITION sales_us VALUES ('usa', 'canada'),
+     PARTITION sales_other VALUES (DEFAULT)
+ )
+ Has OIDs: no
+ 
+ \d+ sales_asia
+                             Table "public.sales_asia"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_asia_sales_state_check" CHECK (sales_state::text = 'asia'::text)
+ Inherits: sales_list
+ Has OIDs: no
+ 
+ \d+ sales_euro
+                             Table "public.sales_euro"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_euro_sales_state_check" CHECK (sales_state::text = 'eu'::text)
+ Inherits: sales_list
+ Has OIDs: no
+ 
+ \d+ sales_us
+                              Table "public.sales_us"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_us_sales_state_check" CHECK (sales_state::text = ANY (ARRAY['usa'::character varying, 'canada'::character varying]::text[]))
+ Inherits: sales_list
+ Has OIDs: no
+ 
+ \d+ sales_other
+                             Table "public.sales_other"
+     Column     |            Type             | Modifiers | Storage  | Description 
+ ---------------+-----------------------------+-----------+----------+-------------
+  salesman_id   | numeric(5,0)                |           | main     | 
+  salesman_name | character varying(30)       |           | extended | 
+  sales_state   | character varying(20)       |           | extended | 
+  sales_date    | timestamp without time zone |           | plain    | 
+ Check constraints:
+     "sales_other_sales_state_check" CHECK (NOT (sales_state::text = ANY (ARRAY['asia'::character varying, 'eu'::character varying, 'usa'::character varying, 'canada'::character varying]::text[])))
+ Inherits: sales_list
+ Has OIDs: no
+ 
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_list'::regclass ORDER BY 1;
+   inhrelid   | inhseqno |  inhvalues   
+ -------------+----------+--------------
+  sales_asia  |        1 | {asia}
+  sales_euro  |        1 | {eu}
+  sales_us    |        1 | {usa,canada}
+  sales_other |        1 | {}
+ (4 rows)
+ 
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+  partrelid  | partkind | pg_get_expr 
+ ------------+----------+-------------
+  sales_list | l        | sales_state
+ (1 row)
+ 
+ DROP TABLE sales_list CASCADE;
+ NOTICE:  drop cascades to 4 other objects
+ DETAIL:  drop cascades to table sales_asia
+ drop cascades to table sales_euro
+ drop cascades to table sales_us
+ drop cascades to table sales_other
+ --
+ -- 1000 LIST PARTITIONS
+ --
+ CREATE TABLE sales_list (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ );
+ ALTER TABLE sales_list PARTITION BY LIST (lower(sales_state)) text_ops;
+ DO
+ $$
+ BEGIN
+   FOR i IN 1..1000 LOOP
+     EXECUTE 'CREATE PARTITION sales_' || i
+          || ' ON sales_list VALUES (' || quote_literal(i) || ')';
+   END LOOP;
+ END;
+ $$;
+ -- ERROR: partition values overlapped with "sales_123"
+ CREATE PARTITION sales_error ON sales_list VALUES ('1001', '123');
+ ERROR:  partition values overlapped with "sales_123"
+ CREATE PARTITION sales_other ON sales_list VALUES DEFAULT;
+ SET client_min_messages = WARNING;
+ DROP TABLE sales_list CASCADE;
+ RESET client_min_messages;
+ --
+ -- LIST PARTITION with boolean
+ --
+ CREATE TABLE answers (
+   id     integer,
+   answer boolean
+ )
+ PARTITION BY LIST (answer)
+ (
+   PARTITION ans_yes     VALUES (true),
+   PARTITION ans_no      VALUES (false)
+ );
+ -- ERROR: partition key must not be NULL
+ CREATE PARTITION ans_unknown ON answers VALUES (NULL);
+ ERROR:  partition key must not be NULL
+ CREATE PARTITION ans_unknown ON answers VALUES (DEFAULT);
+ \d+ answers
+                 Table "public.answers"
+  Column |  Type   | Modifiers | Storage | Description 
+ --------+---------+-----------+---------+-------------
+  id     | integer |           | plain   | 
+  answer | boolean |           | plain   | 
+ Partitions: LIST (answer)
+ (
+     PARTITION ans_no VALUES ('f'),
+     PARTITION ans_yes VALUES ('t'),
+     PARTITION ans_unknown VALUES (DEFAULT)
+ )
+ Has OIDs: no
+ 
+ -- INSERTs with conditional triggers
+ CREATE FUNCTION insert_into() RETURNS trigger AS
+ $$
+ BEGIN
+   EXECUTE 'INSERT INTO ' || quote_ident(TG_ARGV[0]) || ' VALUES($1.*)' USING NEW;
+   RETURN NULL;
+ END;
+ $$
+ LANGUAGE plpgsql;
+ CREATE TRIGGER insert_yes BEFORE INSERT ON answers FOR EACH ROW
+   WHEN (NEW.answer) EXECUTE PROCEDURE insert_into('ans_yes');
+ CREATE TRIGGER insert_no BEFORE INSERT ON answers FOR EACH ROW
+   WHEN (NOT NEW.answer) EXECUTE PROCEDURE insert_into('ans_no');
+ CREATE TRIGGER insert_unknown BEFORE INSERT ON answers FOR EACH ROW
+   WHEN (NEW.answer IS NULL) EXECUTE PROCEDURE insert_into('ans_unknown');
+ INSERT INTO answers VALUES(1, true), (2, false), (3, NULL);
+ SELECT tableoid::regclass, * FROM answers ORDER BY id;
+   tableoid   | id | answer 
+ -------------+----+--------
+  ans_yes     |  1 | t
+  ans_no      |  2 | f
+  ans_unknown |  3 | 
+ (3 rows)
+ 
+ DROP TABLE answers CASCADE;
+ NOTICE:  drop cascades to 3 other objects
+ DETAIL:  drop cascades to table ans_yes
+ drop cascades to table ans_no
+ drop cascades to table ans_unknown
+ DROP FUNCTION insert_into();
diff -cprN head/src/test/regress/expected/sanity_check.out work/src/test/regress/expected/sanity_check.out
*** head/src/test/regress/expected/sanity_check.out	2010-01-15 09:59:20.573996000 +0900
--- work/src/test/regress/expected/sanity_check.out	2010-01-18 14:38:33.997046457 +0900
*************** SELECT relname, relhasindex
*** 112,117 ****
--- 112,118 ----
   pg_opclass              | t
   pg_operator             | t
   pg_opfamily             | t
+  pg_partition            | t
   pg_pltemplate           | t
   pg_proc                 | t
   pg_rewrite              | t
*************** SELECT relname, relhasindex
*** 154,160 ****
   timetz_tbl              | f
   tinterval_tbl           | f
   varchar_tbl             | f
! (143 rows)
  
  --
  -- another sanity check: every system catalog that has OIDs should have
--- 155,161 ----
   timetz_tbl              | f
   tinterval_tbl           | f
   varchar_tbl             | f
! (144 rows)
  
  --
  -- another sanity check: every system catalog that has OIDs should have
diff -cprN head/src/test/regress/parallel_schedule work/src/test/regress/parallel_schedule
*** head/src/test/regress/parallel_schedule	2009-08-25 09:55:55.804564000 +0900
--- work/src/test/regress/parallel_schedule	2010-01-18 14:38:33.997046457 +0900
*************** test: copy copyselect
*** 52,58 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: constraints triggers create_misc create_aggregate create_operator inherit vacuum drop_if_exists create_cast
  
  # Depends on the above
  test: create_index create_view
--- 52,58 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: constraints triggers create_misc create_aggregate create_operator inherit partition vacuum drop_if_exists create_cast
  
  # Depends on the above
  test: create_index create_view
diff -cprN head/src/test/regress/serial_schedule work/src/test/regress/serial_schedule
*** head/src/test/regress/serial_schedule	2009-08-25 09:55:55.804564000 +0900
--- work/src/test/regress/serial_schedule	2010-01-18 14:38:33.997046457 +0900
*************** test: create_operator
*** 60,65 ****
--- 60,66 ----
  test: create_index
  test: drop_if_exists
  test: inherit
+ test: partition
  test: vacuum
  test: create_view
  test: sanity_check
diff -cprN head/src/test/regress/sql/partition.sql work/src/test/regress/sql/partition.sql
*** head/src/test/regress/sql/partition.sql	1970-01-01 09:00:00.000000000 +0900
--- work/src/test/regress/sql/partition.sql	2010-01-18 14:38:33.997046457 +0900
***************
*** 0 ****
--- 1,188 ----
+ --
+ -- RANGE PARTITION with timestamp
+ --
+ CREATE TABLE sales_range (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ )
+ PARTITION BY RANGE (sales_date)
+ (
+   PARTITION sales_2006 VALUES LESS THAN ('2007-01-01'),
+   PARTITION sales_2007 VALUES LESS THAN ('2008-01-01'),
+   PARTITION sales_2008 VALUES LESS THAN ('2009-01-01'),
+   PARTITION sales_max  VALUES LESS THAN (MAXVALUE)
+ );
+ \d+ sales_range
+ \d+ sales_2006
+ \d+ sales_2007
+ \d+ sales_2008
+ \d+ sales_max
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_range'::regclass ORDER BY 1;
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+ DROP TABLE sales_range CASCADE;
+ 
+ --
+ -- CREATE TABLE + ALTER TABLE PARTITION BY RANGE + ATTACH PARTITION
+ --
+ CREATE TABLE sales_range (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ );
+ ALTER TABLE sales_range PARTITION BY RANGE (sales_date) timestamp_ops
+ (
+   PARTITION sales_2006 VALUES LESS THAN '2007-01-01',
+   PARTITION sales_2007 VALUES LESS THAN '2008-01-01'
+ );
+ CREATE TABLE sales_2008 (LIKE sales_range INCLUDING ALL);
+ ALTER TABLE sales_range ATTACH PARTITION sales_2008 VALUES LESS THAN '2009-01-01';
+ 
+ -- ERROR: cannot split partition "sales_2008"
+ CREATE PARTITION sales_error ON sales_range VALUES LESS THAN '2008-06-06';
+ 
+ CREATE TABLE sales_max  (LIKE sales_range INCLUDING ALL);
+ ALTER TABLE sales_range ATTACH PARTITION sales_max  VALUES LESS THAN MAXVALUE;
+ 
+ \d+ sales_range
+ \d+ sales_2006
+ \d+ sales_2007
+ \d+ sales_2008
+ \d+ sales_max
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_range'::regclass ORDER BY 1;
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+ 
+ -- ERROR: cannot add partition to "sales_range", that has an overflow partition
+ CREATE PARTITION sales_error ON sales_range VALUES LESS THAN '2008-06-06';
+ -- ERROR: multiple partition keys for table "sales_range" are not allowed
+ ALTER TABLE sales_range PARTITION BY RANGE (salesman_id);
+ -- ERROR: cannot add LIST partition "sales_error" to RANGE partitioned table "sales_range"
+ CREATE PARTITION sales_error ON sales_range VALUES IN ('list');
+ 
+ -- NO PARTITION will drop all inhvalues.
+ ALTER TABLE sales_range NO PARTITION;
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_range'::regclass ORDER BY 1;
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+ DROP TABLE sales_range CASCADE;
+ 
+ --
+ -- 1000 RANGE PARTITIONS
+ --
+ CREATE TABLE sales_range (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ );
+ ALTER TABLE sales_range PARTITION BY RANGE (sales_date);
+ DO
+ $$
+ BEGIN
+   FOR i IN 1..1000 LOOP
+     EXECUTE 'CREATE PARTITION sales_' || i
+          || ' ON sales_range VALUES LESS THAN '
+          || quote_literal('2000-01-01'::timestamp + i * '1 day'::interval);
+   END LOOP;
+ END;
+ $$;
+ CREATE PARTITION sales_max ON sales_range VALUES LESS THAN MAXVALUE;
+ SET client_min_messages = WARNING;
+ DROP TABLE sales_range CASCADE;
+ RESET client_min_messages;
+ 
+ --
+ -- LIST PARTITION with varchar
+ --
+ CREATE TABLE sales_list (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ )
+ PARTITION BY LIST (sales_state) varchar_ops
+ (
+   PARTITION sales_asia  VALUES ('asia'),
+   PARTITION sales_euro  VALUES ('eu'),
+   PARTITION sales_us    VALUES ('usa', 'canada'),
+   PARTITION sales_other VALUES (DEFAULT)
+ );
+ \d+ sales_list
+ \d+ sales_asia
+ \d+ sales_euro
+ \d+ sales_us
+ \d+ sales_other
+ SELECT inhrelid::regclass, inhseqno, inhvalues FROM pg_inherits WHERE inhparent = 'sales_list'::regclass ORDER BY 1;
+ SELECT partrelid::regclass, partkind, pg_get_expr(partkey, partrelid) FROM pg_partition;
+ DROP TABLE sales_list CASCADE;
+ 
+ --
+ -- 1000 LIST PARTITIONS
+ --
+ CREATE TABLE sales_list (
+   salesman_id       numeric(5),
+   salesman_name     varchar(30),
+   sales_state       varchar(20),
+   sales_date        timestamp
+ );
+ ALTER TABLE sales_list PARTITION BY LIST (lower(sales_state)) text_ops;
+ DO
+ $$
+ BEGIN
+   FOR i IN 1..1000 LOOP
+     EXECUTE 'CREATE PARTITION sales_' || i
+          || ' ON sales_list VALUES (' || quote_literal(i) || ')';
+   END LOOP;
+ END;
+ $$;
+ 
+ -- ERROR: partition values overlapped with "sales_123"
+ CREATE PARTITION sales_error ON sales_list VALUES ('1001', '123');
+ 
+ CREATE PARTITION sales_other ON sales_list VALUES DEFAULT;
+ SET client_min_messages = WARNING;
+ DROP TABLE sales_list CASCADE;
+ RESET client_min_messages;
+ 
+ --
+ -- LIST PARTITION with boolean
+ --
+ CREATE TABLE answers (
+   id     integer,
+   answer boolean
+ )
+ PARTITION BY LIST (answer)
+ (
+   PARTITION ans_yes     VALUES (true),
+   PARTITION ans_no      VALUES (false)
+ );
+ 
+ -- ERROR: partition key must not be NULL
+ CREATE PARTITION ans_unknown ON answers VALUES (NULL);
+ 
+ CREATE PARTITION ans_unknown ON answers VALUES (DEFAULT);
+ \d+ answers
+ 
+ -- INSERTs with conditional triggers
+ CREATE FUNCTION insert_into() RETURNS trigger AS
+ $$
+ BEGIN
+   EXECUTE 'INSERT INTO ' || quote_ident(TG_ARGV[0]) || ' VALUES($1.*)' USING NEW;
+   RETURN NULL;
+ END;
+ $$
+ LANGUAGE plpgsql;
+ 
+ CREATE TRIGGER insert_yes BEFORE INSERT ON answers FOR EACH ROW
+   WHEN (NEW.answer) EXECUTE PROCEDURE insert_into('ans_yes');
+ CREATE TRIGGER insert_no BEFORE INSERT ON answers FOR EACH ROW
+   WHEN (NOT NEW.answer) EXECUTE PROCEDURE insert_into('ans_no');
+ CREATE TRIGGER insert_unknown BEFORE INSERT ON answers FOR EACH ROW
+   WHEN (NEW.answer IS NULL) EXECUTE PROCEDURE insert_into('ans_unknown');
+ 
+ INSERT INTO answers VALUES(1, true), (2, false), (3, NULL);
+ SELECT tableoid::regclass, * FROM answers ORDER BY id;
+ 
+ DROP TABLE answers CASCADE;
+ DROP FUNCTION insert_into();