diff -cprN head/src/backend/catalog/Makefile work/src/backend/catalog/Makefile *** head/src/backend/catalog/Makefile 2009-10-08 07:14:16.000000000 +0900 --- work/src/backend/catalog/Makefile 2009-11-02 09:22:16.251471873 +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 2009-10-06 04:24:35.000000000 +0900 --- work/src/backend/catalog/dependency.c 2009-11-02 14:01:00.145461433 +0900 *************** *** 41,46 **** --- 41,47 ---- #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 *** 149,155 **** ForeignDataWrapperRelationId, /* OCLASS_FDW */ ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ UserMappingRelationId, /* OCLASS_USER_MAPPING */ ! DefaultAclRelationId /* OCLASS_DEFACL */ }; --- 150,157 ---- ForeignDataWrapperRelationId, /* OCLASS_FDW */ ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ UserMappingRelationId, /* OCLASS_USER_MAPPING */ ! DefaultAclRelationId, /* OCLASS_DEFACL */ ! PartitionRelationId /* OCLASS_PARTITION */ }; *************** doDeletion(const ObjectAddress *object) *** 1143,1148 **** --- 1145,1154 ---- RemoveDefaultACLById(object->objectId); break; + case OCLASS_PARTITION: + RemovePartitionById(object->objectId); + break; + default: elog(ERROR, "unrecognized object class: %u", object->classId); *************** getObjectClass(const ObjectAddress *obje *** 2066,2071 **** --- 2072,2081 ---- 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 *** 2671,2676 **** --- 2681,2730 ---- 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 2009-10-06 04:24:35.000000000 +0900 --- work/src/backend/catalog/heap.c 2009-11-02 14:59:14.606458455 +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" *************** RemoveStatistics(Oid relid, AttrNumber a *** 2361,2366 **** --- 2362,2392 ---- /* + * Remove a pg_partition entry + */ + void + RemovePartitionById(Oid relid) + { + Relation rel; + HeapTuple tup; + + 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); + } + + + /* * RelationTruncateIndexes - truncate all indexes associated * with the heap relation to zero tuples. * diff -cprN head/src/backend/commands/tablecmds.c work/src/backend/commands/tablecmds.c *** head/src/backend/commands/tablecmds.c 2009-10-15 07:14:21.000000000 +0900 --- work/src/backend/commands/tablecmds.c 2009-11-02 14:29:59.330453022 +0900 *************** *** 32,37 **** --- 32,38 ---- #include "catalog/pg_inherits_fn.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" + #include "catalog/pg_partition.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" *************** *** 65,70 **** --- 66,72 ---- #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/fmgroids.h" *************** static void ATExecEnableDisableRule(Rela *** 331,336 **** --- 333,339 ---- 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 copy_relation_data(SMgrRelation rel, SMgrRelation dst, ForkNumber forkNum, bool istemp); static const char *storage_name(char c); *************** RenameRelationInternal(Oid myrelid, cons *** 2234,2239 **** --- 2237,2281 ---- } /* + * CREATE PARTITION partition ON table + */ + void + CreatePartition(CreatePartitionStmt *stmt, const char *queryString) + { + Oid relid; + Node *key; + Oid oprid; + List *opr; + List *stmts; + ListCell *cell; + + relid = RangeVarGetRelid(stmt->parent, false); + key = get_partition_key(relid, &oprid); + if (key == NULL) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + (stmt->parent->schemaname + ? errmsg("relation \"%s.%s\" does not have partition key", + stmt->parent->schemaname, stmt->parent->relname) + : errmsg("relation \"%s\" does not have partition key", + stmt->parent->relname)), + errhint("Use ALTER TABLE PARTITION BY first"))); + } + + if ((opr = get_opfullname(oprid)) == NIL) + elog(ERROR, "cache lookup failed for operator %u", oprid); + + stmts = transformPartition(stmt->def, stmt->parent, key, opr); + + foreach(cell, stmts) + { + ProcessUtility((Node *) lfirst(cell), + queryString, NULL, false, NULL, NULL); + } + } + + /* * 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 *** 2593,2598 **** --- 2635,2641 ---- case AT_DisableRule: case AT_AddInherit: /* INHERIT / NO INHERIT */ case AT_DropInherit: + case AT_PartitionBy: /* PARTITION BY / NO PARTITION */ ATSimplePermissions(rel, false); /* These commands never recurse */ /* No command-specific prep needed */ *************** ATExecCmd(List **wqueue, AlteredTableInf *** 2838,2843 **** --- 2881,2889 ---- case AT_DropInherit: ATExecDropInherit(rel, (RangeVar *) cmd->def); break; + case AT_PartitionBy: /* PARTITION BY / NO PARTITION */ + ATExecPartitionBy(rel, (PartitionBy *) cmd->def); + break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); *************** ATExecAlterColumnType(AlteredTableInfo * *** 6164,6169 **** --- 6210,6220 ---- getObjectDescription(&foundObject)); break; + case OCLASS_PARTITION: + /* TODO: recreate all partitions */ + elog(ERROR, "cannot change partition key"); + break; + default: elog(ERROR, "unrecognized object class: %u", foundObject.classId); *************** ATExecAddInherit(Relation child_rel, Ran *** 7147,7152 **** --- 7198,7205 ---- HeapTuple inheritsTuple; int32 inhseqno; List *children; + Node *partkey; + Oid partopr; /* * AccessShareLock on the parent is what's obtained during normal CREATE *************** ATExecAddInherit(Relation child_rel, Ran *** 7238,7243 **** --- 7291,7314 ---- /* Match up the constraints and bump coninhcount as needed */ MergeConstraintsIntoExisting(child_rel, parent_rel); + /* If the parent has parition key, add dependency to partition key */ + partkey = get_partition_key(RelationGetRelid(parent_rel), &partopr); + if (partkey) + { + ObjectAddress partitionObj; + ObjectAddress childObj; + + /* TODO: Match up the partition key */ + + partitionObj.classId = PartitionRelationId; + partitionObj.objectId = RelationGetRelid(child_rel); + partitionObj.objectSubId = 0; + childObj.classId = RelationRelationId; + childObj.objectId = RelationGetRelid(child_rel); + childObj.objectSubId = 0; + recordDependencyOn(&childObj, &partitionObj, DEPENDENCY_NORMAL); + } + /* * OK, it looks valid. Make the catalog entries that show inheritance. */ *************** ATExecDropInherit(Relation rel, RangeVar *** 7717,7722 **** --- 7788,7866 ---- /* + * ALTER TABLE PARTITION BY / NO PARTITION + */ + static void + ATExecPartitionBy(Relation rel, PartitionBy *defs) + { + Oid relid = RelationGetRelid(rel); + ObjectAddress myself; + + myself.classId = PartitionRelationId; + myself.objectId = relid; + myself.objectSubId = 0; + + if (defs == NULL) + { + /* ALTER TABLE NO PARTITION */ + performDeletion(&myself, DROP_RESTRICT); + } + else + { + Relation catalogRel; + TupleDesc catalogDesc; + Datum datum[Natts_pg_partition]; + bool nulls[Natts_pg_partition]; + HeapTuple tuple; + Node *partkey; + Oid keytype; + Oid partopr; + ParseState *pstate; + RangeTblEntry *rte; + + Assert(defs->key != NULL); + Assert(defs->opr != NIL); + + /* 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); + partopr = compatible_oper_opid(defs->opr, keytype, keytype, false); + + /* + * Make the pg_partition entry + */ + memset(nulls, 0, sizeof(nulls)); + datum[0] = ObjectIdGetDatum(relid); /* partrelid */ + datum[1] = ObjectIdGetDatum(partopr); /* partopr */ + 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); + + heap_close(catalogRel, RowExclusiveLock); + + /* Caller will create partition tables */ + } + } + + + /* * Execute ALTER TABLE SET SCHEMA * * Note: caller must have checked ownership of the relation already diff -cprN head/src/backend/nodes/copyfuncs.c work/src/backend/nodes/copyfuncs.c *** head/src/backend/nodes/copyfuncs.c 2009-10-28 23:55:38.000000000 +0900 --- work/src/backend/nodes/copyfuncs.c 2009-11-02 09:22:16.255470382 +0900 *************** _copyCreateStmt(CreateStmt *from) *** 2503,2508 **** --- 2503,2509 ---- COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); + COPY_NODE_FIELD(partitions); return newnode; } *************** _copyAlterTSConfigurationStmt(AlterTSCon *** 3450,3455 **** --- 3451,3494 ---- return newnode; } + static Partition * + _copyPartition(Partition *from) + { + Partition *newnode = makeNode(Partition); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(name); + COPY_NODE_FIELD(value); + 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(opr); + 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) *** 4117,4122 **** --- 4156,4170 ---- case T_AlterTSConfigurationStmt: retval = _copyAlterTSConfigurationStmt(from); break; + case T_Partition: + retval = _copyPartition(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 2009-10-28 23:55:38.000000000 +0900 --- work/src/backend/nodes/equalfuncs.c 2009-11-02 09:22:16.255470382 +0900 *************** _equalCreateStmt(CreateStmt *a, CreateSt *** 1101,1106 **** --- 1101,1107 ---- COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(oncommit); COMPARE_STRING_FIELD(tablespacename); + COMPARE_NODE_FIELD(partitions); return true; } *************** _equalAlterTSConfigurationStmt(AlterTSCo *** 1900,1905 **** --- 1901,1938 ---- } static bool + _equalPartition(Partition *a, Partition *b) + { + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(name); + COMPARE_NODE_FIELD(value); + 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(opr); + 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) *** 2810,2815 **** --- 2843,2857 ---- case T_AlterTSConfigurationStmt: retval = _equalAlterTSConfigurationStmt(a, b); break; + case T_Partition: + retval = _equalPartition(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 2009-10-28 23:55:38.000000000 +0900 --- work/src/backend/nodes/outfuncs.c 2009-11-02 09:22:16.256471643 +0900 *************** _outCreateStmt(StringInfo str, CreateStm *** 1782,1787 **** --- 1782,1788 ---- WRITE_NODE_FIELD(options); WRITE_ENUM_FIELD(oncommit, OnCommitAction); WRITE_STRING_FIELD(tablespacename); + WRITE_NODE_FIELD(partitions); } static void *************** _outConstraint(StringInfo str, Constrain *** 2419,2424 **** --- 2420,2448 ---- } } + static void + _outPartition(StringInfo str, Partition *node) + { + WRITE_NODE_TYPE("PARTITION"); + + WRITE_CHAR_FIELD(kind); + WRITE_NODE_FIELD(name); + WRITE_NODE_FIELD(value); + 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(opr); + WRITE_NODE_FIELD(defs); + } + /* * _outNode - *************** _outNode(StringInfo str, void *obj) *** 2872,2877 **** --- 2896,2907 ---- case T_XmlSerialize: _outXmlSerialize(str, obj); break; + case T_Partition: + _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 2009-10-15 07:14:22.000000000 +0900 --- work/src/backend/parser/gram.y 2009-11-02 11:38:00.829508450 +0900 *************** static TypeName *TableFuncTypeName(List *** 183,188 **** --- 183,190 ---- InsertStmt *istmt; VariableSetStmt *vsetstmt; + Partition *partition; + PartitionBy *partitionby; } %type stmt schema_stmt *************** static TypeName *TableFuncTypeName(List *** 198,204 **** 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 --- 200,206 ---- 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 *** 435,440 **** --- 437,447 ---- %type opt_existing_window_name %type opt_frame_clause frame_extent frame_bound + %type RangePartition ListPartition + %type PartitionBy OptPartition + %type RangeUpper + %type OptUsingOp OptRangePartitions RangePartitions + OptListPartitions ListPartitions ListValues ConstValues /* * Non-keyword token types. These are hard-wired into the "flex" lexer. *************** static TypeName *TableFuncTypeName(List *** 497,503 **** 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 *** 525,531 **** 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 : *** 678,683 **** --- 685,691 ---- | CreateRoleStmt | CreateUserStmt | CreateUserMappingStmt + | CreatePartitionStmt | CreatedbStmt | DeallocateStmt | DeclareCursorStmt *************** schema_stmt: *** 1136,1141 **** --- 1144,1150 ---- | CreateTrigStmt | GrantStmt | ViewStmt + | CreatePartitionStmt ; *************** AlterTableStmt: *** 1561,1566 **** --- 1570,1583 ---- n->relkind = OBJECT_TABLE; $$ = (Node *)n; } + | ALTER PARTITION relation_expr alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = $4; + n->relkind = OBJECT_TABLE; + $$ = (Node *)n; + } | ALTER INDEX qualified_name alter_table_cmds { AlterTableStmt *n = makeNode(AlterTableStmt); *************** alter_table_cmd: *** 1884,1889 **** --- 1901,1922 ---- n->def = (Node *)$2; $$ = (Node *)n; } + /* ALTER TABLE PARTITION BY ... */ + | PartitionBy + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_PartitionBy; + n->def = (Node *)$1; + $$ = (Node *)n; + } + /* ALTER TABLE NO PARTITION */ + | NO PARTITION + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_PartitionBy; + n->def = NULL; + $$ = (Node *)n; + } ; alter_column_default: *************** copy_generic_opt_arg_list_item: *** 2171,2177 **** *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' ! OptInherit OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; --- 2204,2210 ---- *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' ! OptInherit OptWith OnCommitOption OptTableSpace OptPartition { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; *************** CreateStmt: CREATE OptTemp TABLE qualifi *** 2182,2191 **** 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 (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... --- 2215,2225 ---- 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 (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... *************** CreateStmt: CREATE OptTemp TABLE qualifi *** 2199,2208 **** --- 2233,2344 ---- 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 OptUsingOp ')' OptRangePartitions + { + PartitionBy *n = makeNode(PartitionBy); + + n->kind = PARTITION_BY_RANGE; + n->key = $5; + n->opr = ($6 != NIL ? $6 : list_make1(makeString("<"))); + n->defs = $8; + $$ = n; + } + | PARTITION BY LIST '(' a_expr OptUsingOp ')' OptListPartitions + { + PartitionBy *n = makeNode(PartitionBy); + n->kind = PARTITION_BY_LIST; + n->key = $5; + n->opr = ($6 != NIL ? $6 : list_make1(makeString("="))); + n->defs = $8; + $$ = n; + } + ; + + OptUsingOp: + USING qual_all_Op { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + + 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 + { + Partition *n = makeNode(Partition); + n->kind = PARTITION_BY_RANGE; + n->name = $2; + n->value = $6; + n->options = $7; + n->tablespacename = $8; + $$ = n; + } + ; + + RangeUpper: + AexprConst { $$ = $1; } + | '(' AexprConst ')' { $$ = $2; } + | MAXVALUE { $$ = NULL; } + | '(' MAXVALUE ')' { $$ = NULL; } + ; + + 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 + { + Partition *n = makeNode(Partition); + n->kind = PARTITION_BY_LIST; + n->name = $2; + n->value = (Node *) $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); } + ; + /* * Redundancy here is needed to avoid shift/reduce conflicts, * since TEMP is not a reserved word. See also OptTempTableName. *************** opt_with_data: *** 2684,2689 **** --- 2820,2861 ---- /***************************************************************************** * * 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(Partition); + n->def->kind = PARTITION_BY_RANGE; + n->def->name = $3; + n->def->value = $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(Partition); + n->def->kind = PARTITION_BY_RANGE; + n->def->name = $3; + n->def->value = (Node *) $8; + n->def->options = $9; + n->def->tablespacename = $10; + $$ = (Node *)n; + } + ; + + /***************************************************************************** + * + * QUERY : * CREATE SEQUENCE seqname * ALTER SEQUENCE seqname * *************** DropStmt: DROP drop_type IF_P EXISTS any *** 3930,3935 **** --- 4102,4108 ---- drop_type: TABLE { $$ = OBJECT_TABLE; } + | PARTITION { $$ = OBJECT_TABLE; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | VIEW { $$ = OBJECT_VIEW; } | INDEX { $$ = OBJECT_INDEX; } *************** RenameStmt: ALTER AGGREGATE func_name ag *** 5551,5556 **** --- 5724,5738 ---- n->newname = $6; $$ = (Node *)n; } + | ALTER PARTITION relation_expr RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TABLE; + n->relation = $3; + n->subname = NULL; + n->newname = $6; + $$ = (Node *)n; + } | ALTER SEQUENCE qualified_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); *************** AlterObjectSchemaStmt: *** 5711,5716 **** --- 5893,5906 ---- n->newschema = $6; $$ = (Node *)n; } + | ALTER PARTITION relation_expr SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TABLE; + n->relation = $3; + n->newschema = $6; + $$ = (Node *)n; + } | ALTER SEQUENCE qualified_name SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); *************** unreserved_keyword: *** 10628,10634 **** --- 10818,10826 ---- | LAST_P | LC_COLLATE_P | LC_CTYPE_P + | LESS | LEVEL + | LIST | LISTEN | LOAD | LOCAL *************** unreserved_keyword: *** 10734,10739 **** --- 10926,10932 ---- | 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 2009-10-13 09:53:08.000000000 +0900 --- work/src/backend/parser/parse_utilcmd.c 2009-11-02 14:52:58.409860006 +0900 *************** static void transformFKConstraints(Parse *** 114,123 **** CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint); 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 - --- 114,126 ---- 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); ! static Constraint *makeCheckRange(Node *key, List *opr, Node *lower, Node *upper); ! static Constraint *makeCheckList(Node *key, List *opr, Node *values); ! static Node *getCurrentUpper(Relation rel); /* * transformCreateStmt - *************** transformCreateStmt(CreateStmt *stmt, co *** 233,238 **** --- 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 *** 1421,1426 **** --- 1442,1601 ---- } } + + /* + * transformPartition + * handle PARTITION clause + */ + List * + transformPartition(Partition *def, RangeVar *parent, Node *key, List *opr) + { + InhRelation *like; + Constraint *check = NULL; + CreateStmt *create; + AlterTableStmt *alter; + AlterTableCmd *cmd; + + Assert(def != NULL); + Assert(IsA(def, Partition)); + Assert(parent != NULL); + Assert(key != NULL); + Assert(opr != NIL); + + switch (def->kind) + { + case PARTITION_BY_RANGE: + check = makeCheckRange(key, opr, getCurrentUpper(NULL), def->value); + break; + case PARTITION_BY_LIST: + check = makeCheckList(key, opr, def->value); + break; + default: + elog(ERROR, "unknown partition kind: %d", def->kind); + } + + /* Use the same schema as the parent if not specified. */ + Assert(def->name != NULL); + if (def->name->schemaname == NULL) + def->name->schemaname = parent->schemaname; + def->name->istemp = parent->istemp; + + /* CREATE TABLE partition (LIKE parent INCLUDING ALL, CHECK(...) ) */ + like = makeNode(InhRelation); + like->relation = parent; + like->options = CREATE_TABLE_LIKE_ALL; + create = makeNode(CreateStmt); + create->relation = def->name; + create->tableElts = list_make1(like); + if (check != NULL) + create->tableElts = lappend(create->tableElts, check); + create->inhRelations = NIL; + create->constraints = NIL; + create->options = def->options; + create->oncommit = ONCOMMIT_NOOP; + create->tablespacename = def->tablespacename; + create->partitions = NULL; + + /* ALTER TABLE partition INHERIT parent */ + cmd = makeNode(AlterTableCmd); + cmd->subtype = AT_AddInherit; + cmd->def = (Node *) parent; + alter = makeNode(AlterTableStmt); + alter->relation = def->name; + alter->cmds = list_make1(cmd); + alter->relkind = OBJECT_TABLE; + + return list_make2(create, alter); + } + + /* PARTITION BY / NO PARTITION */ + static void + transformPartitionBy(ParseState *pstate, CreateStmtContext *cxt, + PartitionBy *partitions) + { + ListCell *cell; + + if (partitions == NULL) + return; + + Assert(partitions->opr != NIL); + + /* Expand partition clauses to CREATE TABLE. */ + foreach (cell, partitions->defs) + { + Partition *def = (Partition *) lfirst(cell); + + cxt->alist = list_concat(cxt->alist, transformPartition(def, + cxt->relation, partitions->key, partitions->opr)); + } + } + + static Node * + getCurrentUpper(Relation rel) + { + /* TODO: extract current upper bound from existing paritions */ + elog(DEBUG2, "NOT IMPLEMENTED: %s(%d)", __FUNCTION__, __LINE__); + + return NULL; + } + + static Constraint * + makeCheckRange(Node *key, List *opr, Node *lower, Node *upper) + { + Constraint *check; + Node *lt; + Node *ge; + + Assert(key != NULL); + Assert(opr != NULL); + if (lower == NULL && upper == NULL) + return NULL; /* overflow partition no CHECK constraint */ + + /* key < upper */ + if (upper != NULL) + lt = (Node *) makeA_Expr(AEXPR_OP, opr, key, upper, -1); + + /* NOT (key < lower) */ + if (lower != NULL) + ge = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, + (Node *) makeA_Expr(AEXPR_OP, opr, key, lower, -1), -1); + + /* CHECK ( lower <= key AND key < upper ) */ + check = makeNode(Constraint); + check->contype = CONSTR_CHECK; + check->location = -1; + check->cooked_expr = NULL; + if (lower == NULL) /* key < upper */ + check->raw_expr = lt; + else if (upper == NULL) /* NOT (key < lower) */ + check->raw_expr = ge; + else /* NOT (key < lower) AND key < upper */ + check->raw_expr = (Node *) makeA_Expr(AEXPR_AND, NIL, ge, lt, -1); + + return check; + } + + static Constraint * + makeCheckList(Node *key, List *opr, Node *values) + { + Constraint *check; + + Assert(key != NULL); + Assert(opr != NULL); + if (values == NULL) + return NULL; /* overflow partition has no CHECK constraint */ + + check = makeNode(Constraint); + check->contype = CONSTR_CHECK; + check->location = -1; + check->cooked_expr = NULL; + + /* CHECK ( key = ANY ( values ) ) */ + check->raw_expr = (Node *) makeA_Expr(AEXPR_IN, opr, key, values, -1); + + return check; + } + /* * transformIndexStmt - parse analysis for CREATE INDEX * *************** transformAlterTableStmt(AlterTableStmt * *** 1905,1910 **** --- 2080,2090 ---- 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 2009-10-26 11:26:40.000000000 +0900 --- work/src/backend/tcop/utility.c 2009-11-02 09:22:16.262471704 +0900 *************** check_xact_readonly(Node *parsetree) *** 214,219 **** --- 214,220 ---- case T_CreateUserMappingStmt: case T_AlterUserMappingStmt: case T_DropUserMappingStmt: + case T_CreatePartitionStmt: ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("transaction is read-only"))); *************** ProcessUtility(Node *parsetree, *** 516,521 **** --- 517,526 ---- RemoveUserMapping((DropUserMappingStmt *) parsetree); break; + case T_CreatePartitionStmt: + CreatePartition((CreatePartitionStmt *) parsetree, queryString); + break; + case T_DropStmt: { DropStmt *stmt = (DropStmt *) parsetree; *************** CreateCommandTag(Node *parsetree) *** 1422,1427 **** --- 1427,1436 ---- tag = "DROP USER MAPPING"; break; + case T_CreatePartitionStmt: + tag = "CREATE PARTITION"; + break; + case T_DropStmt: switch (((DropStmt *) parsetree)->removeType) { *************** GetCommandLogLevel(Node *parsetree) *** 2174,2179 **** --- 2183,2189 ---- case T_CreateUserMappingStmt: case T_AlterUserMappingStmt: case T_DropUserMappingStmt: + case T_CreatePartitionStmt: lev = LOGSTMT_DDL; break; diff -cprN head/src/backend/utils/cache/lsyscache.c work/src/backend/utils/cache/lsyscache.c *** head/src/backend/utils/cache/lsyscache.c 2009-08-10 14:46:50.000000000 +0900 --- work/src/backend/utils/cache/lsyscache.c 2009-11-02 14:15:50.967507015 +0900 *************** *** 24,29 **** --- 24,30 ---- #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" + #include "catalog/pg_partition.h" #include "catalog/pg_proc.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" *************** get_opname(Oid opno) *** 1059,1064 **** --- 1060,1091 ---- } /* + * get_opfullname + * returns the full name of the operator, or NULL if not found. + */ + 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 NIL; + } + + /* * op_input_types * * Returns the left and right input datatypes for an operator *************** get_roleid_checked(const char *rolname) *** 2776,2778 **** --- 2803,2840 ---- errmsg("role \"%s\" does not exist", rolname))); return roleid; } + + /* + * get_partition_key + * Returns the partition key of a given relation + */ + Node * + get_partition_key(Oid relid, Oid *opr) + { + HeapTuple tp; + Datum datum; + bool isNull; + Node *expr = NULL; + + if (opr) + *opr = InvalidOid; + + tp = SearchSysCache(PARTITIONKEY, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + datum = SysCacheGetAttr(PARTITIONKEY, tp, + Anum_pg_partition_partkey, &isNull); + if (!isNull) + expr = stringToNode(TextDatumGetCString(datum)); + + if (opr) + *opr = DatumGetObjectId(SysCacheGetAttr(PARTITIONKEY, tp, + Anum_pg_partition_partopr, &isNull)); + + ReleaseSysCache(tp); + } + + return expr; + } diff -cprN head/src/backend/utils/cache/syscache.c work/src/backend/utils/cache/syscache.c *** head/src/backend/utils/cache/syscache.c 2009-10-06 04:24:45.000000000 +0900 --- work/src/backend/utils/cache/syscache.c 2009-11-02 13:54:19.046445070 +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[ *** 764,774 **** 0 }, 128 } }; ! static CatCache *SysCache[ ! lengthof(cacheinfo)]; static int SysCacheSize = lengthof(cacheinfo); static bool CacheInitialized = false; --- 765,786 ---- 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/include/catalog/dependency.h work/src/include/catalog/dependency.h *** head/src/include/catalog/dependency.h 2009-10-08 07:14:24.000000000 +0900 --- work/src/include/catalog/dependency.h 2009-11-02 13:24:58.876494856 +0900 *************** typedef enum ObjectClass *** 147,152 **** --- 147,153 ---- 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 2009-10-06 04:24:48.000000000 +0900 --- work/src/include/catalog/heap.h 2009-11-02 13:31:37.379468821 +0900 *************** extern void RemoveAttrDefault(Oid relid, *** 101,106 **** --- 101,107 ---- DropBehavior behavior, bool complain); extern void RemoveAttrDefaultById(Oid attrdefId); extern void RemoveStatistics(Oid relid, AttrNumber attnum); + extern void RemovePartitionById(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 2009-10-08 07:14:25.000000000 +0900 --- work/src/include/catalog/indexing.h 2009-11-02 09:22:16.264474579 +0900 *************** DECLARE_UNIQUE_INDEX(pg_default_acl_oid_ *** 275,280 **** --- 275,283 ---- 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, 2337, on pg_partition using btree(partrelid oid_ops)); + #define PartitionRelidIndexId 2337 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES 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 2009-11-02 09:42:21.773470507 +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 2336 + + CATALOG(pg_partition,2336) BKI_WITHOUT_OIDS + { + Oid partrelid; /* partitioned table oid */ + Oid partopr; /* operator 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_partopr 2 + #define Anum_pg_partition_partkind 3 + #define Anum_pg_partition_partkey 4 + + #endif /* PG_PARTITIONS_H */ diff -cprN head/src/include/commands/tablecmds.h work/src/include/commands/tablecmds.h *** head/src/include/commands/tablecmds.h 2009-07-16 15:33:45.000000000 +0900 --- work/src/include/commands/tablecmds.h 2009-11-02 09:22:16.264474579 +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 2009-10-26 11:26:41.000000000 +0900 --- work/src/include/nodes/nodes.h 2009-11-02 09:22:16.264474579 +0900 *************** typedef enum NodeTag *** 346,351 **** --- 346,352 ---- T_CreateUserMappingStmt, T_AlterUserMappingStmt, T_DropUserMappingStmt, + T_CreatePartitionStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) *************** typedef enum NodeTag *** 384,389 **** --- 385,392 ---- T_XmlSerialize, T_WithClause, T_CommonTableExpr, + T_Partition, + T_PartitionBy, /* * 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 2009-10-28 23:55:46.000000000 +0900 --- work/src/include/nodes/parsenodes.h 2009-11-02 09:22:16.265497236 +0900 *************** typedef enum AlterTableType *** 1132,1138 **** 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 */ --- 1132,1139 ---- 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 */ } AlterTableType; typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ *************** typedef struct VariableShowStmt *** 1333,1338 **** --- 1334,1373 ---- char *name; } VariableShowStmt; + /* ---------- + * Partitioning definitions + * ---------- + */ + + #define PARTITION_BY_RANGE 'R' + #define PARTITION_BY_LIST 'L' + + typedef struct Partition + { + NodeTag type; + char kind; /* PARTITION_BY_xxx */ + RangeVar *name; /* name of partition */ + Node *value; /* for RANGE and LIST */ + List *options; /* options from WITH clause */ + char *tablespacename; /* table space to use, or NULL */ + } Partition; + + typedef struct PartitionBy + { + NodeTag type; + char kind; /* PARTITION_BY_xxx */ + Node *key; /* partition key expr */ + List *opr; /* partition key operator */ + List *defs; /* list of Partition */ + } PartitionBy; + + typedef struct CreatePartitionStmt + { + NodeTag type; + RangeVar *parent; /* parent of the partition */ + Partition *def; + } CreatePartitionStmt; + /* ---------------------- * Create Table Statement * *************** typedef struct CreateStmt *** 1355,1360 **** --- 1390,1396 ---- 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 2009-10-13 05:39:42.000000000 +0900 --- work/src/include/parser/kwlist.h 2009-11-02 09:22:16.266466337 +0900 *************** PG_KEYWORD("lc_ctype", LC_CTYPE_P, UNRES *** 215,223 **** --- 215,225 ---- 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 **** --- 367,373 ---- 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 2009-01-02 02:24:00.000000000 +0900 --- work/src/include/parser/parse_utilcmd.h 2009-11-02 09:22:16.266466337 +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 *transformPartition(Partition *def, RangeVar *parent, Node *key, List *opr); #endif /* PARSE_UTILCMD_H */ diff -cprN head/src/include/utils/lsyscache.h work/src/include/utils/lsyscache.h *** head/src/include/utils/lsyscache.h 2009-08-10 14:46:50.000000000 +0900 --- work/src/include/utils/lsyscache.h 2009-11-02 10:53:03.724820136 +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); *************** extern void free_attstatsslot(Oid atttyp *** 137,142 **** --- 138,144 ---- extern char *get_namespace_name(Oid nspid); extern Oid get_roleid(const char *rolname); extern Oid get_roleid_checked(const char *rolname); + extern Node *get_partition_key(Oid relid, Oid *opr); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) diff -cprN head/src/include/utils/syscache.h work/src/include/utils/syscache.h *** head/src/include/utils/syscache.h 2009-10-06 04:24:49.000000000 +0900 --- work/src/include/utils/syscache.h 2009-11-02 09:22:16.267490580 +0900 *************** enum SysCacheIdentifier *** 83,89 **** TYPENAMENSP, TYPEOID, USERMAPPINGOID, ! USERMAPPINGUSERSERVER }; extern void InitCatalogCache(void); --- 83,90 ---- 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 2009-11-02 15:03:46.594453000 +0900 *************** *** 0 **** --- 1,264 ---- + -- + -- RANGE PARTITIONING + -- + 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 | + Child tables: sales_2006, + sales_2007, + sales_2008, + sales_max + 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 (sales_date < 'Tue Jan 01 00:00:00 2008'::timestamp without time zone) + Inherits: sales_range + Has OIDs: no + + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + relname | partkind + -------------+---------- + sales_range | R + (1 row) + + DROP TABLE IF EXISTS 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 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 USING < ) + ( + 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 | + Child tables: sales_2006, + sales_2007, + sales_2008, + sales_max + 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 (sales_date < 'Tue Jan 01 00:00:00 2008'::timestamp without time zone) + Inherits: sales_range + Has OIDs: no + + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + relname | partkind + -------------+---------- + sales_range | R + (1 row) + + DROP TABLE IF EXISTS 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 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 ); + CREATE PARTITION sales_2006 ON sales_range VALUES LESS THAN '2007-01-01'; + CREATE PARTITION sales_2007 ON sales_range VALUES LESS THAN '2008-01-01'; + CREATE PARTITION sales_2008 ON sales_range VALUES LESS THAN '2009-01-01'; + CREATE PARTITION sales_max ON sales_range 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 | + Child tables: sales_2006, + sales_2007, + sales_2008, + sales_max + 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 (sales_date < 'Tue Jan 01 00:00:00 2008'::timestamp without time zone) + Inherits: sales_range + Has OIDs: no + + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + relname | partkind + -------------+---------- + sales_range | R + (1 row) + + DROP TABLE IF EXISTS 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 + -- + -- LIST PARTITIONING + -- + CREATE TABLE sales_list ( + salesman_id numeric(5), + salesman_name varchar(30), + sales_state varchar(20), + sales_date timestamp + ) + PARTITION BY LIST ( sales_state ) + ( + 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 | + Child tables: sales_asia, + sales_euro, + sales_other, + sales_us + 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 + + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + relname | partkind + ------------+---------- + sales_list | L + (1 row) + + DROP TABLE IF EXISTS 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 + 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 ( sales_state USING = ) + ( + 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 | + Child tables: sales_asia, + sales_euro, + sales_other, + sales_us + 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 + + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + relname | partkind + ------------+---------- + sales_list | L + (1 row) + + DROP TABLE IF EXISTS 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 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 2009-10-08 07:14:26.000000000 +0900 --- work/src/test/regress/expected/sanity_check.out 2009-11-02 14:53:22.654506000 +0900 *************** SELECT relname, relhasindex *** 111,116 **** --- 111,117 ---- pg_opclass | t pg_operator | t pg_opfamily | t + pg_partition | t pg_pltemplate | t pg_proc | t pg_rewrite | t *************** SELECT relname, relhasindex *** 153,159 **** timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (142 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 154,160 ---- timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (143 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-24 12:10:16.000000000 +0900 --- work/src/test/regress/parallel_schedule 2009-11-02 14:56:03.617468480 +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-24 12:10:16.000000000 +0900 --- work/src/test/regress/serial_schedule 2009-11-02 14:56:13.319808016 +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 2009-11-02 15:03:30.816824063 +0900 *************** *** 0 **** --- 1,93 ---- + -- + -- RANGE PARTITIONING + -- + 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_2007 + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + DROP TABLE IF EXISTS sales_range CASCADE; + + 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 USING < ) + ( + 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_2007 + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + DROP TABLE IF EXISTS sales_range CASCADE; + + 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 ); + CREATE PARTITION sales_2006 ON sales_range VALUES LESS THAN '2007-01-01'; + CREATE PARTITION sales_2007 ON sales_range VALUES LESS THAN '2008-01-01'; + CREATE PARTITION sales_2008 ON sales_range VALUES LESS THAN '2009-01-01'; + CREATE PARTITION sales_max ON sales_range VALUES LESS THAN MAXVALUE; + \d+ sales_range + \d+ sales_2007 + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + DROP TABLE IF EXISTS sales_range CASCADE; + + -- + -- LIST PARTITIONING + -- + CREATE TABLE sales_list ( + salesman_id numeric(5), + salesman_name varchar(30), + sales_state varchar(20), + sales_date timestamp + ) + PARTITION BY LIST ( sales_state ) + ( + 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_us + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + DROP TABLE IF EXISTS sales_list CASCADE; + + 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 ( sales_state USING = ) + ( + 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_us + SELECT relname, partkind FROM pg_partition, pg_class WHERE partrelid = oid; + DROP TABLE IF EXISTS sales_list CASCADE;