cross-col-syntax.patch

text/plain

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 <ival>	opt_lock lock_type cast_context
  %type <ival>	vacuum_option_list vacuum_option_elem
  %type <boolean>	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 <ival>	opt_lock lock_type cast_context opt_stattarget
  %type <ival>	vacuum_option_list vacuum_option_elem
  %type <boolean>	opt_force opt_or_replace
  				opt_grant_grant_option opt_grant_admin_option
*************** static void processCASbits(int cas_bits,
*** 325,330 ****
--- 325,332 ----
  %type <list>	opt_fdw_options fdw_options
  %type <defelt>	fdw_option
  
+ %type <list>	cc_column_list
+ 
  %type <range>	OptTempTableName
  %type <into>	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 */