diff -dcrpN postgresql.4/src/backend/commands/analyze.c postgresql.5/src/backend/commands/analyze.c *** postgresql.4/src/backend/commands/analyze.c 2011-08-02 11:51:06.071322632 +0200 --- postgresql.5/src/backend/commands/analyze.c 2011-08-02 14:59:37.136374568 +0200 *************** *** 21,26 **** --- 21,27 ---- #include "access/tupconvert.h" #include "access/tuptoaster.h" #include "access/xact.h" + #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/namespace.h" *************** *** 28,33 **** --- 29,35 ---- #include "catalog/pg_inherits_fn.h" #include "catalog/pg_namespace.h" #include "commands/dbcommands.h" + #include "commands/defrem.h" #include "commands/vacuum.h" #include "executor/executor.h" #include "miscadmin.h" *************** compare_mcvs(const void *a, const void * *** 2779,2781 **** --- 2781,2852 ---- return da - db; } + + /* + * ExtraColStat + * Add or remove one extra entry in pg_statistics + */ + void ExtraStatistics(ExtraStatStmt *stmt) + { + Oid relId; + int len, i, j; + bool differ = false; + AttrNumber *attnums; + AttrNumber *sorted_attnums; + ListCell *l; + + relId = RangeVarGetRelid(stmt->relation, AccessExclusiveLock, false, false); + + len = list_length(stmt->columns); + if (len < 2) + elog(ERROR, "cross column statistics need at least two columns"); + + attnums = (int2 *)palloc(len * sizeof(AttrNumber)); + sorted_attnums = (int2 *)palloc(len * sizeof(AttrNumber)); + + i = 0; + foreach(l, stmt->columns) + { + Node *node = (Node *) lfirst(l); + Var *var; + + if (!IsA(node, Var)) + elog(ERROR, "not a column reference"); + + var = (Var *) node; + + if (var->varattno == 0) + elog(ERROR, "row expansion via \"*\" is not supported here"); + + sorted_attnums[i] = attnums[i] = var->varattno; + + i++; + } + + for (i = 0; i < len - 1; i++) + for (j = i+1; j < len; j++) + if (sorted_attnums[i] > sorted_attnums[j]) + { + AttrNumber tmp = sorted_attnums[i]; + + sorted_attnums[i] = sorted_attnums[j]; + sorted_attnums[j] = tmp; + } + + for (i = 0; i < len; i++) + { + if (!differ && attnums[i] != sorted_attnums[i]) + differ = true; + + if ((i < len - 1) && sorted_attnums[i] == sorted_attnums[i+1]) + elog(ERROR, "column list must contain every column exactly once"); + + } + if (differ) + elog(WARNING, "the column list was reordered in the order of table attributes"); + + if (stmt->create) + AddStatistics(relId, sorted_attnums, len, false, stmt->statistics_target); + else + RemoveStatistics(relId, sorted_attnums, len); + } diff -dcrpN postgresql.4/src/backend/nodes/copyfuncs.c postgresql.5/src/backend/nodes/copyfuncs.c *** postgresql.4/src/backend/nodes/copyfuncs.c 2011-07-24 18:16:45.269678833 +0200 --- postgresql.5/src/backend/nodes/copyfuncs.c 2011-08-02 14:07:24.223043799 +0200 *************** _copyCreateForeignTableStmt(CreateForeig *** 3459,3464 **** --- 3459,3477 ---- return newnode; } + static ExtraStatStmt * + _copyExtraStatStmt(ExtraStatStmt *from) + { + ExtraStatStmt *newnode = makeNode(ExtraStatStmt); + + COPY_SCALAR_FIELD(create); + newnode->relation = _copyRangeVar(from->relation); + COPY_NODE_FIELD(columns); + COPY_SCALAR_FIELD(statistics_target); + + return newnode; + } + static CreateTrigStmt * _copyCreateTrigStmt(CreateTrigStmt *from) { *************** copyObject(void *from) *** 4378,4383 **** --- 4391,4399 ---- case T_CreateForeignTableStmt: retval = _copyCreateForeignTableStmt(from); break; + case T_ExtraStatStmt: + retval = _copyExtraStatStmt(from); + break; case T_CreateTrigStmt: retval = _copyCreateTrigStmt(from); break; diff -dcrpN postgresql.4/src/backend/nodes/equalfuncs.c postgresql.5/src/backend/nodes/equalfuncs.c *** postgresql.4/src/backend/nodes/equalfuncs.c 2011-07-24 18:16:45.269678833 +0200 --- postgresql.5/src/backend/nodes/equalfuncs.c 2011-08-02 14:07:24.246042121 +0200 *************** _equalCreateForeignTableStmt(CreateForei *** 1796,1801 **** --- 1796,1813 ---- } static bool + _equalExtraStatStmt(ExtraStatStmt *a, ExtraStatStmt *b) + { + COMPARE_SCALAR_FIELD(create); + if (!_equalRangeVar(a->relation, b->relation)) + return FALSE; + COMPARE_NODE_FIELD(columns); + COMPARE_SCALAR_FIELD(statistics_target); + + return true; + } + + static bool _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b) { COMPARE_STRING_FIELD(trigname); *************** equal(void *a, void *b) *** 2931,2936 **** --- 2943,2951 ---- case T_CreateForeignTableStmt: retval = _equalCreateForeignTableStmt(a, b); break; + case T_ExtraStatStmt: + retval = _equalExtraStatStmt(a, b); + break; case T_CreateTrigStmt: retval = _equalCreateTrigStmt(a, b); break; diff -dcrpN postgresql.4/src/backend/parser/gram.y postgresql.5/src/backend/parser/gram.y *** postgresql.4/src/backend/parser/gram.y 2011-07-24 18:16:45.272678682 +0200 --- postgresql.5/src/backend/parser/gram.y 2011-08-02 14:07:24.282039495 +0200 *************** static void processCASbits(int cas_bits, *** 214,220 **** DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt ! DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt --- 214,220 ---- DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt ! DropForeignServerStmt DropUserMappingStmt ExplainStmt ExtraStatStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt *************** static void processCASbits(int cas_bits, *** 246,252 **** transaction_mode_item create_extension_opt_item alter_extension_opt_item ! %type opt_lock lock_type cast_context %type vacuum_option_list vacuum_option_elem %type opt_force opt_or_replace opt_grant_grant_option opt_grant_admin_option --- 246,252 ---- transaction_mode_item create_extension_opt_item alter_extension_opt_item ! %type opt_lock lock_type cast_context opt_stattarget %type vacuum_option_list vacuum_option_elem %type opt_force opt_or_replace opt_grant_grant_option opt_grant_admin_option *************** static void processCASbits(int cas_bits, *** 325,330 **** --- 325,332 ---- %type opt_fdw_options fdw_options %type fdw_option + %type cc_column_list + %type OptTempTableName %type into_clause create_as_target *************** stmt : *** 756,761 **** --- 758,764 ---- | DropdbStmt | ExecuteStmt | ExplainStmt + | ExtraStatStmt | FetchStmt | GrantStmt | GrantRoleStmt *************** schema_stmt: *** 1200,1205 **** --- 1203,1276 ---- /***************************************************************************** * + * Add / drop extra statistics + * + *****************************************************************************/ + + ExtraStatStmt: + CREATE CROSS COLUMN STATISTICS ON TABLE qualified_name '(' cc_column_list ')' opt_stattarget + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'r'; + n->create = true; + n->relation = $7; + n->columns = $9; + n->statistics_target = $11; + $$ = (Node *)n; + } + | DROP CROSS COLUMN STATISTICS ON TABLE qualified_name '(' cc_column_list ')' + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'r'; + n->create = false; + n->relation = $7; + n->columns = $9; + $$ = (Node *)n; + } + | CREATE CROSS COLUMN STATISTICS ON INDEX qualified_name opt_stattarget + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'i'; + n->create = true; + n->relation = $7; + n->columns = NIL; + n->statistics_target = $8; + $$ = (Node *)n; + } + | DROP CROSS COLUMN STATISTICS ON INDEX qualified_name + { + ExtraStatStmt *n = makeNode(ExtraStatStmt); + + n->relkind = 'i'; + n->create = false; + n->relation = $7; + n->columns = NIL; + $$ = (Node *)n; + } + ; + + cc_column_list: + columnref + { + $$ = list_make1($1); + } + | cc_column_list ',' columnref + { + $$ = lappend($1, $3); + } + ; + + opt_stattarget: + WITH '(' Iconst ')' { $$ = $3; } + | /* EMPTY */ { $$ = -1; } + ; + + + /***************************************************************************** + * * Set PG internal variable * SET name TO 'var_value' * Include SQL92 syntax (thomas 1997-10-22): diff -dcrpN postgresql.4/src/backend/parser/parse_utilcmd.c postgresql.5/src/backend/parser/parse_utilcmd.c *** postgresql.4/src/backend/parser/parse_utilcmd.c 2011-07-18 15:42:00.045377085 +0200 --- postgresql.5/src/backend/parser/parse_utilcmd.c 2011-08-02 15:00:29.005542805 +0200 *************** setSchemaName(char *context_schema, char *** 2710,2712 **** --- 2710,2804 ---- "different from the one being created (%s)", *stmt_schema_name, context_schema))); } + + /* + * transformExtraStatistics + * Transform the column list or the expression into a form + * usable by the executor. + */ + ExtraStatStmt * + transformExtraStatistics(ExtraStatStmt *stmt, const char *queryString) + { + ParseState *pstate; + RangeTblEntry *rte; + ExtraStatStmt *newstmt; + List *columns = NIL; + ListCell *cell; + Oid relId; + HeapTuple tuple; + HeapTuple attuple; + Form_pg_class classptr; + Form_pg_index indexptr; + Form_pg_attribute attptr; + AttrNumber i; + + switch (stmt->relkind) + { + case 'r': + pstate = make_parsestate(NULL); + pstate->p_sourcetext = queryString; + + rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); + addRTEtoQuery(pstate, rte, true, true, true); + + foreach(cell, stmt->columns) + { + Node *col = lfirst(cell); + + columns = lappend(columns, transformExpr(pstate, col)); + } + + break; + + case 'i': + relId = RangeVarGetRelid(stmt->relation, ShareLock, false, false); + + tuple = SearchSysCache1(RELOID, relId); + classptr = (Form_pg_class) GETSTRUCT(tuple); + + if (classptr->relkind != 'i') + elog(ERROR, "not an index"); + + ReleaseSysCache(tuple); + + tuple = SearchSysCache1(INDEXRELID, relId); + indexptr = (Form_pg_index) GETSTRUCT(tuple); + + if (indexptr->indnatts < 2) + { + ReleaseSysCache(tuple); + + elog(ERROR, "cross column statistics are only usable on multi-column indexes"); + } + + for (i = 1; i <= indexptr->indnatts; i++) + { + attuple = SearchSysCache2(ATTNUM, relId, i); + if (!HeapTupleIsValid(attuple)) + elog(ERROR, "pg_attribute row not found for index"); + + attptr = (Form_pg_attribute) GETSTRUCT(attuple); + + columns = lappend(columns, makeVar(0, i, + attptr->atttypid, + attptr->atttypmod, + InvalidOid, 0)); + + ReleaseSysCache(attuple); + } + + ReleaseSysCache(tuple); + break; + + default: + elog(ERROR, "invalid relkind"); + } + + newstmt = makeNode(ExtraStatStmt); + newstmt->relkind = stmt->relkind; + newstmt->create = stmt->create; + newstmt->relation = copyObject(stmt->relation); + newstmt->columns = columns; + + return newstmt; + } diff -dcrpN postgresql.4/src/backend/tcop/utility.c postgresql.5/src/backend/tcop/utility.c *** postgresql.4/src/backend/tcop/utility.c 2011-07-24 18:16:45.276678481 +0200 --- postgresql.5/src/backend/tcop/utility.c 2011-08-02 14:07:24.319036796 +0200 *************** check_xact_readonly(Node *parsetree) *** 237,242 **** --- 237,243 ---- case T_AlterTableSpaceOptionsStmt: case T_CreateForeignTableStmt: case T_SecLabelStmt: + case T_ExtraStatStmt: PreventCommandIfReadOnly(CreateCommandTag(parsetree)); break; default: *************** standard_ProcessUtility(Node *parsetree, *** 581,586 **** --- 582,595 ---- } break; + case T_ExtraStatStmt: + { + ExtraStatStmt *newstmt = transformExtraStatistics((ExtraStatStmt *)parsetree, queryString); + + ExtraStatistics(newstmt); + } + break; + case T_CreateTableSpaceStmt: PreventTransactionChain(isTopLevel, "CREATE TABLESPACE"); CreateTableSpace((CreateTableSpaceStmt *) parsetree); *************** CreateCommandTag(Node *parsetree) *** 1744,1749 **** --- 1753,1769 ---- tag = "CREATE FOREIGN TABLE"; break; + case T_ExtraStatStmt: + { + ExtraStatStmt *stmt = (ExtraStatStmt *)parsetree; + + if (stmt->create) + tag = "CREATE CROSS COLUMN STATISTICS"; + else + tag = "DROP CROSS COLUMN STATISTICS"; + } + break; + case T_DropStmt: switch (((DropStmt *) parsetree)->removeType) { diff -dcrpN postgresql.4/src/include/commands/defrem.h postgresql.5/src/include/commands/defrem.h *** postgresql.4/src/include/commands/defrem.h 2011-07-24 18:16:45.287677928 +0200 --- postgresql.5/src/include/commands/defrem.h 2011-08-02 14:07:24.332035848 +0200 *************** extern void RemoveAggregate(RemoveFuncSt *** 93,98 **** --- 93,101 ---- extern void RenameAggregate(List *name, List *args, const char *newname); extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId); + /* commands/analyze.c */ + extern void ExtraStatistics(ExtraStatStmt *stmt); + /* commands/opclasscmds.c */ extern void DefineOpClass(CreateOpClassStmt *stmt); extern void DefineOpFamily(CreateOpFamilyStmt *stmt); diff -dcrpN postgresql.4/src/include/nodes/nodes.h postgresql.5/src/include/nodes/nodes.h *** postgresql.4/src/include/nodes/nodes.h 2011-03-22 17:53:48.045903422 +0100 --- postgresql.5/src/include/nodes/nodes.h 2011-08-02 14:07:24.340035264 +0200 *************** typedef enum NodeTag *** 362,367 **** --- 362,368 ---- T_CreateExtensionStmt, T_AlterExtensionStmt, T_AlterExtensionContentsStmt, + T_ExtraStatStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff -dcrpN postgresql.4/src/include/nodes/parsenodes.h postgresql.5/src/include/nodes/parsenodes.h *** postgresql.4/src/include/nodes/parsenodes.h 2011-07-24 18:16:45.287677928 +0200 --- postgresql.5/src/include/nodes/parsenodes.h 2011-08-02 14:07:24.351034462 +0200 *************** typedef enum DropBehavior *** 1160,1165 **** --- 1160,1179 ---- } DropBehavior; /* ---------------------- + * Create Cross Column Statistics + * ---------------------- + */ + typedef struct ExtraStatStmt + { + NodeTag type; + char relkind; + bool create; + RangeVar *relation; + List *columns; + int statistics_target; + } ExtraStatStmt; + + /* ---------------------- * Alter Table * ---------------------- */ diff -dcrpN postgresql.4/src/include/parser/parse_utilcmd.h postgresql.5/src/include/parser/parse_utilcmd.h *** postgresql.4/src/include/parser/parse_utilcmd.h 2011-01-04 15:13:16.163549374 +0100 --- postgresql.5/src/include/parser/parse_utilcmd.h 2011-08-02 14:07:24.365033441 +0200 *************** extern void transformRuleStmt(RuleStmt * *** 25,28 **** --- 25,31 ---- List **actions, Node **whereClause); extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt); + extern ExtraStatStmt *transformExtraStatistics(ExtraStatStmt *stmt, + const char *queryString); + #endif /* PARSE_UTILCMD_H */