cross-column-v5.patch
application/octet-stream
Filename: cross-column-v5.patch
Type: application/octet-stream
Part: 0
Message:
WIP: cross column correlation ...
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v5
| File | + | − |
|---|---|---|
| src/backend/catalog/Makefile | 1 | 1 |
| src/backend/commands/indexcmds.c | 141 | 0 |
| src/backend/nodes/copyfuncs.c | 28 | 0 |
| src/backend/nodes/equalfuncs.c | 25 | 0 |
| src/backend/optimizer/path/clausesel.c | 401 | 0 |
| src/backend/parser/gram.y | 50 | 3 |
| src/backend/tcop/utility.c | 12 | 0 |
| src/backend/utils/cache/syscache.c | 1 | 0 |
| src/include/catalog/indexing.h | 4 | 0 |
| src/include/catalog/pg_statistic2.h | 265 | 0 |
| src/include/commands/defrem.h | 1 | 0 |
| src/include/nodes/nodes.h | 2 | 0 |
| src/include/nodes/parsenodes.h | 18 | 0 |
diff -durpN postgresql.orig/src/backend/catalog/Makefile postgresql/src/backend/catalog/Makefile
--- postgresql.orig/src/backend/catalog/Makefile 2011-01-04 15:13:15.852565371 +0100
+++ postgresql/src/backend/catalog/Makefile 2011-01-27 10:32:41.796567782 +0100
@@ -31,7 +31,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_sr
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
- pg_statistic.h pg_rewrite.h pg_trigger.h pg_description.h \
+ pg_statistic.h pg_statistic2.h pg_rewrite.h pg_trigger.h pg_description.h \
pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
diff -durpN postgresql.orig/src/backend/commands/indexcmds.c postgresql/src/backend/commands/indexcmds.c
--- postgresql.orig/src/backend/commands/indexcmds.c 2011-01-26 14:17:23.911759868 +0100
+++ postgresql/src/backend/commands/indexcmds.c 2011-02-22 13:32:08.034596118 +0100
@@ -26,7 +26,9 @@
#include "catalog/indexing.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_statistic2.h"
#include "catalog/pg_tablespace.h"
+#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
@@ -1639,3 +1641,142 @@ ReindexDatabase(const char *databaseName
MemoryContextDelete(private_context);
}
+
+/*
+ * CrossColStat
+ * Add or remove one in pg_statistic2
+ */
+void CrossColStat(CrossColStatStmt *stmt)
+{
+ Oid relId;
+ Relation rel;
+ ListCell *l;
+ int len, i, j;
+ bool differ = false;
+ AttrNumber *attnums;
+ AttrNumber *sorted_attnums;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ Datum *datum_attnums;
+ ArrayType *arr_attnums;
+ ScanKeyData scanKey[2];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_statistic2];
+ bool nulls[Natts_pg_statistic2];
+
+ relId = RangeVarGetRelid(stmt->relation, 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));
+ datum_attnums = (Datum *)palloc(len * sizeof(Datum));
+
+ i = 0;
+ foreach(l, stmt->columns)
+ {
+ CrossColStatColumn *col = (CrossColStatColumn *) lfirst(l);
+
+ attnums[i++] = get_attnum(relId, col->colname);
+ }
+
+ for (i = 0; i < len; i++)
+ sorted_attnums[i] = attnums[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");
+
+ datum_attnums[i] = Int16GetDatum(sorted_attnums[i]);
+ }
+
+ if (differ)
+ elog(WARNING, "the column list was reordered in the order of table attributes");
+
+ get_typlenbyvalalign(INT2OID, &typlen, &typbyval, &typalign);
+ arr_attnums = construct_array(datum_attnums, len,
+ INT2OID, typlen, typbyval, typalign);
+
+ rel = heap_open(Statistic2RelationId, RowExclusiveLock);
+
+ /*
+ * There's no syscache for pg_statistic2,
+ * arrays aren't supported there as search keys.
+ * We need to do the hard way.
+ */
+ ScanKeyInit(&scanKey[0],
+ Anum_pg_statistic2_sta2relid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relId));
+ ScanKeyInit(&scanKey[1],
+ Anum_pg_statistic2_sta2attnums,
+ BTEqualStrategyNumber, F_ARRAY_EQ,
+ PointerGetDatum(arr_attnums));
+
+ scan = systable_beginscan(rel, Statistic2RelidAttnumsInhIndexId, true,
+ SnapshotNow, 2, scanKey);
+
+ tuple = systable_getnext(scan);
+
+ if (stmt->create)
+ {
+ if (HeapTupleIsValid(tuple))
+ {
+ systable_endscan(scan);
+ elog(ERROR, "pg_statistic2 entry already exists for this table and set of columns");
+ }
+ systable_endscan(scan);
+
+ for (i = 0; i < Natts_pg_statistic2; i++)
+ nulls[i] = TRUE;
+
+ values[Anum_pg_statistic2_sta2relid - 1] = ObjectIdGetDatum(relId);
+ nulls[Anum_pg_statistic2_sta2relid - 1] = FALSE;
+
+ values[Anum_pg_statistic2_sta2attnums - 1] = PointerGetDatum(arr_attnums);
+ nulls[Anum_pg_statistic2_sta2attnums - 1] = FALSE;
+
+ values[Anum_pg_statistic2_sta2inherit - 1] = BoolGetDatum(FALSE);
+ nulls[Anum_pg_statistic2_sta2inherit - 1] = FALSE;
+
+ tupDesc = RelationGetDescr(rel);
+
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+
+ simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+ }
+ else
+ {
+ if (!HeapTupleIsValid(tuple))
+ {
+ systable_endscan(scan);
+ elog(ERROR, "pg_statistic2 entry doesn't exist for this table and set of columns");
+ }
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ systable_endscan(scan);
+ }
+
+ relation_close(rel, NoLock);
+}
diff -durpN postgresql.orig/src/backend/nodes/copyfuncs.c postgresql/src/backend/nodes/copyfuncs.c
--- postgresql.orig/src/backend/nodes/copyfuncs.c 2011-01-26 14:17:23.922759114 +0100
+++ postgresql/src/backend/nodes/copyfuncs.c 2011-02-22 08:24:27.628837801 +0100
@@ -3321,6 +3321,28 @@ _copyCreateForeignTableStmt(CreateForeig
return newnode;
}
+static CrossColStatStmt *
+_copyCrossColStatStmt(CrossColStatStmt *from)
+{
+ CrossColStatStmt *newnode = makeNode(CrossColStatStmt);
+
+ COPY_SCALAR_FIELD(create);
+ newnode->relation = _copyRangeVar(from->relation);
+ COPY_NODE_FIELD(columns);
+
+ return newnode;
+}
+
+static CrossColStatColumn *
+_copyCrossColStatColumn(CrossColStatColumn *from)
+{
+ CrossColStatColumn *newnode = makeNode(CrossColStatColumn);
+
+ COPY_STRING_FIELD(colname);
+
+ return newnode;
+}
+
static CreateTrigStmt *
_copyCreateTrigStmt(CreateTrigStmt *from)
{
@@ -4225,6 +4247,12 @@ copyObject(void *from)
case T_CreateForeignTableStmt:
retval = _copyCreateForeignTableStmt(from);
break;
+ case T_CrossColStatStmt:
+ retval = _copyCrossColStatStmt(from);
+ break;
+ case T_CrossColStatColumn:
+ retval = _copyCrossColStatColumn(from);
+ break;
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
diff -durpN postgresql.orig/src/backend/nodes/equalfuncs.c postgresql/src/backend/nodes/equalfuncs.c
--- postgresql.orig/src/backend/nodes/equalfuncs.c 2011-01-26 14:17:23.924758978 +0100
+++ postgresql/src/backend/nodes/equalfuncs.c 2011-02-22 06:11:54.259890037 +0100
@@ -1732,6 +1732,25 @@ _equalCreateForeignTableStmt(CreateForei
}
static bool
+_equalCrossColStatStmt(CrossColStatStmt *a, CrossColStatStmt *b)
+{
+ COMPARE_SCALAR_FIELD(create);
+ if (!_equalRangeVar(a->relation, b->relation))
+ return FALSE;
+ COMPARE_NODE_FIELD(columns);
+
+ return true;
+}
+
+static bool
+_equalCrossColStatColumn(CrossColStatColumn *a, CrossColStatColumn *b)
+{
+ COMPARE_STRING_FIELD(colname);
+
+ return true;
+}
+
+static bool
_equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
{
COMPARE_STRING_FIELD(trigname);
@@ -2838,6 +2857,12 @@ equal(void *a, void *b)
case T_CreateForeignTableStmt:
retval = _equalCreateForeignTableStmt(a, b);
break;
+ case T_CrossColStatStmt:
+ retval = _equalCrossColStatStmt(a, b);
+ break;
+ case T_CrossColStatColumn:
+ retval = _equalCrossColStatColumn(a, b);
+ break;
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
diff -durpN postgresql.orig/src/backend/optimizer/path/clausesel.c postgresql/src/backend/optimizer/path/clausesel.c
--- postgresql.orig/src/backend/optimizer/path/clausesel.c 2011-01-04 15:13:15.940560845 +0100
+++ postgresql/src/backend/optimizer/path/clausesel.c 2011-02-22 17:22:43.761445055 +0100
@@ -13,17 +13,27 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "postgres_ext.h"
+#include "access/skey.h"
+#include "access/relscan.h"
+#include "catalog/indexing.h"
#include "catalog/pg_operator.h"
+#include "catalog/pg_statistic2.h"
+#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
+#include "nodes/pg_list.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/plancat.h"
+#include "optimizer/var.h"
#include "parser/parsetree.h"
+#include "utils/array.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
+#include "utils/tqual.h"
/*
@@ -34,6 +44,7 @@ typedef struct RangeQueryClause
{
struct RangeQueryClause *next; /* next in linked list */
Node *var; /* The common variable of the clauses */
+ AttrNumber varattno; /* for finding cross-column statistics */
bool have_lobound; /* found a low-bound clause yet? */
bool have_hibound; /* found a high-bound clause yet? */
Selectivity lobound; /* Selectivity of a var > something clause */
@@ -43,6 +54,21 @@ typedef struct RangeQueryClause
static void addRangeClause(RangeQueryClause **rqlist, Node *clause,
bool varonleft, bool isLTsel, Selectivity s2);
+typedef struct CrossColumnClause
+{
+ struct CrossColumnClause *next;
+ Node *var;
+ AttrNumber varattno;
+ Node *expr;
+ Selectivity sel;
+} CrossColumnClause;
+
+static void addXCClause(CrossColumnClause **xclist, Node *clause,
+ bool varonleft, Selectivity s2);
+
+static bool crosscolumn_selectivity(Oid relId,
+ CrossColumnClause **xcnext, RangeQueryClause **rqlist,
+ Selectivity *result_sel);
/****************************************************************************
* ROUTINES TO COMPUTE SELECTIVITIES
@@ -99,8 +125,12 @@ clauselist_selectivity(PlannerInfo *root
{
Selectivity s1 = 1.0;
RangeQueryClause *rqlist = NULL;
+ CrossColumnClause *xclist = NULL;
+ Oid relId = InvalidOid;
ListCell *l;
+ elog(NOTICE, "clauselist_selectivity varRelid %d, list length %d", varRelid, list_length(clauses));
+
/*
* If there's exactly one clause, then no use in trying to match up pairs,
* so just go directly to clause_selectivity().
@@ -173,6 +203,15 @@ clauselist_selectivity(PlannerInfo *root
if (ok)
{
+
+ if (!OidIsValid(relId))
+ {
+ int relid = bms_singleton_member(rinfo->clause_relids);
+
+ relId = root->simple_rte_array[relid]->relid;
+ elog(NOTICE, "clauselist_selectivity: oprrest %d, relid %d (%d)", get_oprrest(expr->opno),
+ relid, relId);
+ }
/*
* If it's not a "<" or ">" operator, just merge the
* selectivity in generically. But if it's the right oprrest,
@@ -188,6 +227,10 @@ clauselist_selectivity(PlannerInfo *root
addRangeClause(&rqlist, clause,
varonleft, false, s2);
break;
+ case F_EQSEL:
+ addXCClause(&xclist, clause,
+ varonleft, s2);
+ break;
default:
/* Just merge the selectivity in generically */
s1 = s1 * s2;
@@ -202,11 +245,32 @@ clauselist_selectivity(PlannerInfo *root
}
/*
+ * Scan xclist and rqlist recursively and filter out
+ * all possible cross-column selectivities.
+ */
+ crosscolumn_selectivity(relId, &xclist, &rqlist, &s1);
+
+ while (xclist != NULL)
+ {
+ CrossColumnClause *xcnext;
+
+ s1 = s1 * xclist->sel;
+
+ xcnext = xclist->next;
+ pfree(xclist);
+ xclist = xcnext;
+ }
+
+ /*
* Now scan the rangequery pair list.
*/
while (rqlist != NULL)
{
RangeQueryClause *rqnext;
+ char *nodetostr = nodeToString(rqlist->var);
+
+ elog(NOTICE, "%s", nodetostr);
+ pfree(nodetostr);
if (rqlist->have_lobound && rqlist->have_hibound)
{
@@ -279,6 +343,22 @@ clauselist_selectivity(PlannerInfo *root
return s1;
}
+static AttrNumber
+var_get_attno(Node *clause)
+{
+ Var *var;
+
+ if (IsA(clause, Var))
+ {
+ var = (Var *)clause;
+ elog(NOTICE, "var_get_attno varattno %d", var->varattno);
+ return var->varattno;
+ }
+
+ elog(NOTICE, "var_get_attno default 0");
+ return 0;
+}
+
/*
* addRangeClause --- add a new range clause for clauselist_selectivity
*
@@ -358,6 +438,8 @@ addRangeClause(RangeQueryClause **rqlist
/* No matching var found, so make a new clause-pair data structure */
rqelem = (RangeQueryClause *) palloc(sizeof(RangeQueryClause));
rqelem->var = var;
+ rqelem->varattno = var_get_attno(var);
+
if (is_lobound)
{
rqelem->have_lobound = true;
@@ -375,6 +457,38 @@ addRangeClause(RangeQueryClause **rqlist
}
/*
+ * addXCClause - add a new clause to the list of clauses for cross-column stats inspection
+ *
+ */
+static void
+addXCClause(CrossColumnClause **xclist, Node *clause,
+ bool varonleft, Selectivity s)
+{
+ CrossColumnClause *xcelem;
+ Node *var;
+ Node *expr;
+
+ if (varonleft)
+ {
+ var = get_leftop((Expr *) clause);
+ expr = get_rightop((Expr *) clause);
+ }
+ else
+ {
+ var = get_rightop((Expr *) clause);
+ expr = get_leftop((Expr *) clause);
+ }
+
+ xcelem = (CrossColumnClause *) palloc(sizeof(CrossColumnClause));
+ xcelem->var = var;
+ xcelem->varattno = var_get_attno(var);
+ xcelem->expr = expr;
+ xcelem->sel = s;
+ xcelem->next = *xclist;
+ *xclist = xcelem;
+}
+
+/*
* bms_is_subset_singleton
*
* Same result as bms_is_subset(s, bms_make_singleton(x)),
@@ -499,6 +613,8 @@ clause_selectivity(PlannerInfo *root,
{
rinfo = (RestrictInfo *) clause;
+// elog(NOTICE, "RestrictInfo, %s", nodeToString(rinfo->clause));
+
/*
* If the clause is marked pseudoconstant, then it will be used as a
* gating qual and should not affect selectivity estimates; hence
@@ -779,3 +895,288 @@ clause_selectivity(PlannerInfo *root,
return s1;
}
+
+static bool
+has_xcol_selectivity(Oid relId, int natts, AttrNumber *attnums, Selectivity *result_sel)
+{
+ Relation rel;
+ Datum *datums = (Datum *)palloc(natts * sizeof(Datum));
+ ArrayType *arr_attnums;
+ int i;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ ScanKeyData scanKey[2];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ bool result;
+ Selectivity sel = 1e-5; /* fixed selectivity for now */
+
+ for (i = 0; i < natts; i++)
+ datums[i] = Int16GetDatum(attnums[i]);
+
+ get_typlenbyvalalign(INT2OID, &typlen, &typbyval, &typalign);
+ arr_attnums = construct_array(datums, natts,
+ INT2OID, typlen, typbyval, typalign);
+
+ rel = heap_open(Statistic2RelationId, AccessShareLock);
+
+ ScanKeyInit(&scanKey[0],
+ Anum_pg_statistic2_sta2relid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relId));
+ ScanKeyInit(&scanKey[1],
+ Anum_pg_statistic2_sta2attnums,
+ BTEqualStrategyNumber, F_ARRAY_EQ,
+ PointerGetDatum(arr_attnums));
+
+ scan = systable_beginscan(rel, Statistic2RelidAttnumsInhIndexId, true,
+ SnapshotNow, 2, scanKey);
+
+ tuple = systable_getnext(scan);
+
+ result = HeapTupleIsValid(tuple);
+
+ systable_endscan(scan);
+
+ heap_close(rel, NoLock);
+
+ pfree(datums);
+
+ if (result)
+ *result_sel = sel;
+
+ return result;
+}
+
+typedef struct {
+ CrossColumnClause *xc;
+ RangeQueryClause *rq;
+} reclist;
+
+typedef struct {
+ int len;
+ reclist *rclist;
+ AttrNumber *attnums;
+} reclist2;
+
+
+/* add rclist to our list, so the ordered attnums arrays are unique */
+static void
+add_reclist(int len, reclist *rclist, List **results)
+{
+ ListCell *lc;
+ int i, j;
+ reclist2 *rclist2;
+ AttrNumber *attnums = (AttrNumber *) palloc(len * sizeof(AttrNumber));
+
+ /* collect the ordered varattnos from the Vars */
+ for (i = 0; i < len; i++)
+ {
+ if (rclist[i].xc)
+ attnums[i] = rclist[i].xc->varattno;
+ else
+ attnums[i] = rclist[i].rq->varattno;
+ }
+ for (i = 0; i < len - 1; i++)
+ for (j = i + 1; j < len; j++)
+ {
+ AttrNumber tmp = attnums[i];
+ attnums[i] = attnums[j];
+ attnums[j] = tmp;
+ }
+
+ /* match this ordered attnum list against the current list of attnum arrays */
+ foreach(lc, *results)
+ {
+ reclist2 *rc2 = (reclist2 *) lfirst(lc);
+
+ if (len != rc2->len)
+ continue;
+
+ for (i = 0; i < len; i++)
+ if (attnums[i] != rc2->attnums[i])
+ break;
+ if (i < len)
+ continue;
+
+ /* found */
+ return;
+ }
+
+ /* not found, add it to the list */
+ rclist2 = (reclist2 *) palloc(sizeof(reclist2));
+ rclist2->len = len;
+ rclist2->rclist = (reclist *) palloc(len * sizeof(reclist));
+ for (i = 0; i < len; i++)
+ {
+ rclist2->rclist[i].xc = rclist[i].xc;
+ rclist2->rclist[i].rq = rclist[i].rq;
+ }
+ rclist2->attnums = attnums;
+
+ *results = lappend(*results, rclist2);
+}
+
+static int
+compare_reclist2(reclist2 *a, reclist2 *b)
+{
+ int i;
+
+ if (a->len < b->len)
+ return -1;
+ else if (a->len > b->len)
+ return 1;
+
+ for (i = 0; i < a->len; i++)
+ {
+ if (a->attnums[i] < b->attnums[i])
+ return -1;
+ else if (a->attnums[i] > b->attnums[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+static bool
+add_reclist2(int *len, reclist2 **p_reclist2, reclist2 *rclist2)
+{
+ int curr_len = *len;
+ int i, j;
+
+ if (curr_len == 0)
+ {
+ p_reclist2[i] = rclist2;
+ curr_len++;
+ *len = curr_len;
+ return true;
+ }
+
+ for (i = 0; i < curr_len; i++)
+ {
+ if (compare_reclist2(rclist2, p_reclist2[i]) > 0)
+ {
+ for (j = curr_len; j > i; j--)
+ p_reclist2[j] = p_reclist2[j - 1];
+ p_reclist2[i] = rclist2;
+ curr_len++;
+ *len = curr_len;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void
+collect_xcol_lists(int curr_depth, CrossColumnClause *xclist, RangeQueryClause *rqlist, reclist *rclist, List **results)
+{
+ CrossColumnClause *xc_tmp;
+ RangeQueryClause *rq_tmp;
+
+ for (xc_tmp = xclist; xc_tmp; xc_tmp = xc_tmp->next)
+ {
+ if (xc_tmp->varattno == 0)
+ continue;
+
+ rclist[curr_depth].xc = xc_tmp;
+ collect_xcol_lists(curr_depth + 1, xc_tmp->next, rqlist, rclist, results);
+ add_reclist(curr_depth + 1, rclist, results);
+ rclist[curr_depth].xc = NULL;
+ }
+
+ for (rq_tmp = rqlist; rq_tmp; rq_tmp = rq_tmp->next)
+ {
+ if (rq_tmp->varattno == 0)
+ continue;
+
+ rclist[curr_depth].rq = rq_tmp;
+ collect_xcol_lists(curr_depth + 1, (xclist ? xclist->next : xclist), rq_tmp->next, rclist, results);
+ add_reclist(curr_depth + 1, rclist, results);
+ rclist[curr_depth].rq = NULL;
+ }
+}
+
+static bool
+crosscolumn_selectivity(Oid relId, CrossColumnClause **xclist, RangeQueryClause **rqlist, Selectivity *result_sel)
+{
+ CrossColumnClause *xc;
+ RangeQueryClause *rq;
+ List *resultlist = NIL;
+ ListCell *lc;
+ reclist *rclist;
+ reclist2 **p_rclist2;
+ int max_len, i;
+ Selectivity sel = 1.0;
+ bool found_xc_sel = false;
+
+ max_len = 0;
+ for (rq = *rqlist; rq; max_len++, rq = rq->next)
+ ;
+ for (xc = *xclist; xc; max_len++, xc = xc->next)
+ ;
+
+ elog(NOTICE, "crosscolumn_selectivity max length of array %d", max_len);
+
+ rclist = (reclist *) palloc(max_len * sizeof(reclist));
+ for (i = 0; i < max_len; i++)
+ {
+ rclist[i].xc = NULL;
+ rclist[i].rq = NULL;
+ }
+
+ collect_xcol_lists(0, *xclist, *rqlist, rclist, &resultlist);
+
+ pfree(rclist);
+
+ max_len = list_length(resultlist);
+ elog(NOTICE, "crosscolumn_selectivity list length of arrays %d", max_len);
+ p_rclist2 = (reclist2 **) palloc(max_len * sizeof(reclist2 *));
+
+ max_len = 0;
+ foreach (lc, resultlist)
+ {
+ reclist2 *rclist2 = (reclist2 *) lfirst(lc);
+
+ if (!add_reclist2(&max_len, p_rclist2, rclist2))
+ {
+ pfree(rclist2->rclist);
+ pfree(rclist2->attnums);
+ pfree(rclist2);
+ }
+ }
+ elog(NOTICE, "crosscolumn_selectivity length of ordered/unique array of previous list %d", max_len);
+
+ list_free(resultlist);
+
+ for (i = 0; i < max_len; i++)
+ {
+ if (p_rclist2[i] == NULL)
+ continue;
+
+ if (has_xcol_selectivity(relId, p_rclist2[i]->len, p_rclist2[i]->attnums, &sel))
+ {
+ int j;
+
+ /* remove the xclist and rqlist members found in p_rclist2[i] */
+ for (j = 0; j < p_rclist2[i]->len; j++)
+ {
+ /* TODO ... */
+ }
+
+ /* also, remove later elements in p_rclist2 that has any of the removed elements */
+ /* TODO ... */
+
+ elog(NOTICE, "crosscolumn_selectivity found xc selectivity %lf", sel);
+ found_xc_sel = true;
+ *result_sel *= sel;
+ }
+
+ pfree(p_rclist2[i]->rclist);
+ pfree(p_rclist2[i]->attnums);
+ pfree(p_rclist2[i]);
+ }
+
+ return found_xc_sel;
+}
diff -durpN postgresql.orig/src/backend/parser/gram.y postgresql/src/backend/parser/gram.y
--- postgresql.orig/src/backend/parser/gram.y 2011-01-26 14:17:23.934758294 +0100
+++ postgresql/src/backend/parser/gram.y 2011-02-22 08:34:41.549462096 +0100
@@ -196,10 +196,10 @@ static RangeVar *makeRangeVarFromAnyName
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertStmt CreateTrigStmt
- CreateUserStmt CreateUserMappingStmt CreateRoleStmt
- CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
+ CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatedbStmt CreateCCStmt
+ DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
- DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
+ DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropCCStmt DropRoleStmt
DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
@@ -306,6 +306,8 @@ static RangeVar *makeRangeVarFromAnyName
create_generic_options alter_generic_options
relation_expr_list dostmt_opt_list
+%type <list> cc_column_list
+
%type <range> OptTempTableName
%type <into> into_clause create_as_target
@@ -689,6 +691,7 @@ stmt :
| CreateAssertStmt
| CreateCastStmt
| CreateConversionStmt
+ | CreateCCStmt
| CreateDomainStmt
| CreateFdwStmt
| CreateForeignServerStmt
@@ -716,6 +719,7 @@ stmt :
| DoStmt
| DropAssertStmt
| DropCastStmt
+ | DropCCStmt
| DropFdwStmt
| DropForeignServerStmt
| DropGroupStmt
@@ -1179,6 +1183,49 @@ schema_stmt:
/*****************************************************************************
*
+ * Create cross column statistics
+ *
+ *****************************************************************************/
+
+CreateCCStmt:
+ CREATE CROSS COLUMN STATISTICS ON qualified_name '(' cc_column_list ')'
+ {
+ CrossColStatStmt *n = makeNode(CrossColStatStmt);
+ n->create = TRUE;
+ n->relation = $6;
+ n->columns = $8;
+ $$ = (Node *)n;
+ }
+ ;
+
+DropCCStmt: DROP CROSS COLUMN STATISTICS ON qualified_name '(' cc_column_list ')'
+ {
+ CrossColStatStmt *n = makeNode(CrossColStatStmt);
+ n->create = FALSE;
+ n->relation = $6;
+ n->columns = $8;
+ $$ = (Node *)n;
+ }
+ ;
+
+cc_column_list:
+ ColId
+ {
+ CrossColStatColumn *n = makeNode(CrossColStatColumn);
+ n->colname = $1;
+ $$ = list_make1((Node *) n);
+ }
+ | cc_column_list ',' ColId
+ {
+ CrossColStatColumn *n = makeNode(CrossColStatColumn);
+ n->colname = $3;
+ $$ = lappend($1, n);
+ }
+ ;
+
+
+/*****************************************************************************
+ *
* Set PG internal variable
* SET name TO 'var_value'
* Include SQL92 syntax (thomas 1997-10-22):
diff -durpN postgresql.orig/src/backend/tcop/utility.c postgresql/src/backend/tcop/utility.c
--- postgresql.orig/src/backend/tcop/utility.c 2011-01-04 15:13:16.009557296 +0100
+++ postgresql/src/backend/tcop/utility.c 2011-02-22 08:30:01.524693402 +0100
@@ -222,6 +222,7 @@ check_xact_readonly(Node *parsetree)
case T_AlterTableSpaceOptionsStmt:
case T_CreateForeignTableStmt:
case T_SecLabelStmt:
+ case T_CrossColStatStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
default:
@@ -576,6 +577,10 @@ standard_ProcessUtility(Node *parsetree,
}
break;
+ case T_CrossColStatStmt:
+ CrossColStat((CrossColStatStmt *)parsetree);
+ break;
+
case T_CreateTableSpaceStmt:
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
@@ -1580,6 +1585,13 @@ CreateCommandTag(Node *parsetree)
tag = "CREATE FOREIGN TABLE";
break;
+ case T_CrossColStatStmt:
+ if (((CrossColStatStmt *)parsetree)->create)
+ tag = "CREATE CROSS COLUMN STATISTICS";
+ else
+ tag = "DROP CROSS COLUMN STATISTICS";
+ break;
+
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
diff -durpN postgresql.orig/src/backend/utils/cache/syscache.c postgresql/src/backend/utils/cache/syscache.c
--- postgresql.orig/src/backend/utils/cache/syscache.c 2011-01-04 15:13:16.056554877 +0100
+++ postgresql/src/backend/utils/cache/syscache.c 2011-02-22 10:09:25.215145794 +0100
@@ -44,6 +44,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic.h"
+#include "catalog/pg_statistic2.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_config_map.h"
diff -durpN postgresql.orig/src/include/catalog/indexing.h postgresql/src/include/catalog/indexing.h
--- postgresql.orig/src/include/catalog/indexing.h 2011-01-04 15:13:16.118551689 +0100
+++ postgresql/src/include/catalog/indexing.h 2011-02-22 09:27:45.371295981 +0100
@@ -289,6 +289,10 @@ DECLARE_UNIQUE_INDEX(pg_db_role_setting_
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
#define SecLabelObjectIndexId 3597
+DECLARE_UNIQUE_INDEX(pg_statistic2_relid_att_inh_index, 3072, on pg_statistic2 using btree(sta2relid oid_ops, sta2attnums array_ops, sta2inherit bool_ops));
+#define Statistic2RelidAttnumsInhIndexId 3072
+
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff -durpN postgresql.orig/src/include/catalog/pg_statistic2.h postgresql/src/include/catalog/pg_statistic2.h
--- postgresql.orig/src/include/catalog/pg_statistic2.h 1970-01-01 01:00:00.000000000 +0100
+++ postgresql/src/include/catalog/pg_statistic2.h 2011-02-22 09:35:58.817165678 +0100
@@ -0,0 +1,265 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_statistic2.h
+ * definition of the system "cross-column statistic" relation (pg_statistic2)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_statistic2.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_STATISTIC2_H
+#define PG_STATISTIC2_H
+
+#include "catalog/genbki.h"
+
+/*
+ * The CATALOG definition has to refer to the type of stavaluesN as
+ * "anyarray" so that bootstrap mode recognizes it. There is no real
+ * typedef for that, however. Since the fields are potentially-null and
+ * therefore can't be accessed directly from C code, there is no particular
+ * need for the C struct definition to show a valid field type --- instead
+ * we just make it int.
+ */
+#define anyarray int
+
+/* ----------------
+ * pg_statistic definition. cpp turns this into
+ * typedef struct FormData_pg_statistic
+ * ----------------
+ */
+#define Statistic2RelationId 3071
+
+CATALOG(pg_statistic2,3071) BKI_WITHOUT_OIDS
+{
+ /* These fields form the unique key for the entry: */
+ Oid sta2relid; /* relation containing attribute */
+ int2 sta2attnums[1]; /* attribute (column) stats are for */
+ bool sta2inherit; /* true if inheritance children are included */
+
+ /* the fraction of the column's entries that are NULL: */
+ float4 sta2nullfrac;
+
+ /*
+ * stawidth is the average width in bytes of non-null entries. For
+ * fixed-width datatypes this is of course the same as the typlen, but for
+ * var-width types it is more useful. Note that this is the average width
+ * of the data as actually stored, post-TOASTing (eg, for a
+ * moved-out-of-line value, only the size of the pointer object is
+ * counted). This is the appropriate definition for the primary use of
+ * the statistic, which is to estimate sizes of in-memory hash tables of
+ * tuples.
+ */
+ int4 sta2width;
+
+ /* ----------------
+ * stadistinct indicates the (approximate) number of distinct non-null
+ * data values in the column. The interpretation is:
+ * 0 unknown or not computed
+ * > 0 actual number of distinct values
+ * < 0 negative of multiplier for number of rows
+ * The special negative case allows us to cope with columns that are
+ * unique (stadistinct = -1) or nearly so (for example, a column in
+ * which values appear about twice on the average could be represented
+ * by stadistinct = -0.5). Because the number-of-rows statistic in
+ * pg_class may be updated more frequently than pg_statistic is, it's
+ * important to be able to describe such situations as a multiple of
+ * the number of rows, rather than a fixed number of distinct values.
+ * But in other cases a fixed number is correct (eg, a boolean column).
+ * ----------------
+ */
+ float4 sta2distinct;
+
+ /* ----------------
+ * To allow keeping statistics on different kinds of datatypes,
+ * we do not hard-wire any particular meaning for the remaining
+ * statistical fields. Instead, we provide several "slots" in which
+ * statistical data can be placed. Each slot includes:
+ * kind integer code identifying kind of data
+ * op OID of associated operator, if needed
+ * numbers float4 array (for statistical values)
+ * values anyarray (for representations of data values)
+ * The ID and operator fields are never NULL; they are zeroes in an
+ * unused slot. The numbers and values fields are NULL in an unused
+ * slot, and might also be NULL in a used slot if the slot kind has
+ * no need for one or the other.
+ * ----------------
+ */
+
+ int2 sta2kind1;
+ int2 sta2kind2;
+ int2 sta2kind3;
+ int2 sta2kind4;
+
+ Oid sta2op1;
+ Oid sta2op2;
+ Oid sta2op3;
+ Oid sta2op4;
+
+ /*
+ * THE REST OF THESE ARE VARIABLE LENGTH FIELDS, and may even be absent
+ * (NULL). They cannot be accessed as C struct entries; you have to use
+ * the full field access machinery (heap_getattr) for them. We declare
+ * them here for the catalog machinery.
+ */
+
+ float4 sta2numbers1[1];
+ float4 sta2numbers2[1];
+ float4 sta2numbers3[1];
+ float4 sta2numbers4[1];
+
+ /*
+ * Values in these arrays are values of the column's data type. We
+ * presently have to cheat quite a bit to allow polymorphic arrays of this
+ * kind, but perhaps someday it'll be a less bogus facility.
+ */
+ anyarray sta2values1;
+ anyarray sta2values2;
+ anyarray sta2values3;
+ anyarray sta2values4;
+} FormData_pg_statistic2;
+
+#define STATISTIC_NUM_SLOTS 4
+
+#undef anyarray
+
+
+/* ----------------
+ * Form_pg_statistic corresponds to a pointer to a tuple with
+ * the format of pg_statistic relation.
+ * ----------------
+ */
+typedef FormData_pg_statistic2 *Form_pg_statistic2;
+
+/* ----------------
+ * compiler constants for pg_statistic
+ * ----------------
+ */
+#define Natts_pg_statistic2 22
+#define Anum_pg_statistic2_sta2relid 1
+#define Anum_pg_statistic2_sta2attnums 2
+#define Anum_pg_statistic2_sta2inherit 3
+#define Anum_pg_statistic2_sta2nullfrac 4
+#define Anum_pg_statistic2_sta2width 5
+#define Anum_pg_statistic2_sta2distinct 6
+#define Anum_pg_statistic2_sta2kind1 7
+#define Anum_pg_statistic2_sta2kind2 8
+#define Anum_pg_statistic2_sta2kind3 9
+#define Anum_pg_statistic2_sta2kind4 10
+#define Anum_pg_statistic2_sta2op1 11
+#define Anum_pg_statistic2_sta2op2 12
+#define Anum_pg_statistic2_sta2op3 13
+#define Anum_pg_statistic2_sta2op4 14
+#define Anum_pg_statistic2_sta2numbers1 15
+#define Anum_pg_statistic2_sta2numbers2 16
+#define Anum_pg_statistic2_sta2numbers3 17
+#define Anum_pg_statistic2_sta2numbers4 18
+#define Anum_pg_statistic2_sta2values1 19
+#define Anum_pg_statistic2_sta2values2 20
+#define Anum_pg_statistic2_sta2values3 21
+#define Anum_pg_statistic2_sta2values4 22
+
+#if 0
+
+/*
+ * Currently, three statistical slot "kinds" are defined: most common values,
+ * histogram, and correlation. Additional "kinds" will probably appear in
+ * future to help cope with non-scalar datatypes. Also, custom data types
+ * can define their own "kind" codes by mutual agreement between a custom
+ * typanalyze routine and the selectivity estimation functions of the type's
+ * operators.
+ *
+ * Code reading the pg_statistic relation should not assume that a particular
+ * data "kind" will appear in any particular slot. Instead, search the
+ * stakind fields to see if the desired data is available. (The standard
+ * function get_attstatsslot() may be used for this.)
+ */
+
+/*
+ * The present allocation of "kind" codes is:
+ *
+ * 1-99: reserved for assignment by the core PostgreSQL project
+ * (values in this range will be documented in this file)
+ * 100-199: reserved for assignment by the PostGIS project
+ * (values to be documented in PostGIS documentation)
+ * 200-299: reserved for assignment by the ESRI ST_Geometry project
+ * (values to be documented in ESRI ST_Geometry documentation)
+ * 300-9999: reserved for future public assignments
+ *
+ * For private use you may choose a "kind" code at random in the range
+ * 10000-30000. However, for code that is to be widely disseminated it is
+ * better to obtain a publicly defined "kind" code by request from the
+ * PostgreSQL Global Development Group.
+ */
+
+/*
+ * In a "most common values" slot, staop is the OID of the "=" operator
+ * used to decide whether values are the same or not. stavalues contains
+ * the K most common non-null values appearing in the column, and stanumbers
+ * contains their frequencies (fractions of total row count). The values
+ * shall be ordered in decreasing frequency. Note that since the arrays are
+ * variable-size, K may be chosen by the statistics collector. Values should
+ * not appear in MCV unless they have been observed to occur more than once;
+ * a unique column will have no MCV slot.
+ */
+#define STATISTIC_KIND_MCV 1
+
+/*
+ * A "histogram" slot describes the distribution of scalar data. staop is
+ * the OID of the "<" operator that describes the sort ordering. (In theory,
+ * more than one histogram could appear, if a datatype has more than one
+ * useful sort operator.) stavalues contains M (>=2) non-null values that
+ * divide the non-null column data values into M-1 bins of approximately equal
+ * population. The first stavalues item is the MIN and the last is the MAX.
+ * stanumbers is not used and should be NULL. IMPORTANT POINT: if an MCV
+ * slot is also provided, then the histogram describes the data distribution
+ * *after removing the values listed in MCV* (thus, it's a "compressed
+ * histogram" in the technical parlance). This allows a more accurate
+ * representation of the distribution of a column with some very-common
+ * values. In a column with only a few distinct values, it's possible that
+ * the MCV list describes the entire data population; in this case the
+ * histogram reduces to empty and should be omitted.
+ */
+#define STATISTIC_KIND_HISTOGRAM 2
+
+/*
+ * A "correlation" slot describes the correlation between the physical order
+ * of table tuples and the ordering of data values of this column, as seen
+ * by the "<" operator identified by staop. (As with the histogram, more
+ * than one entry could theoretically appear.) stavalues is not used and
+ * should be NULL. stanumbers contains a single entry, the correlation
+ * coefficient between the sequence of data values and the sequence of
+ * their actual tuple positions. The coefficient ranges from +1 to -1.
+ */
+#define STATISTIC_KIND_CORRELATION 3
+
+/*
+ * A "most common elements" slot is similar to a "most common values" slot,
+ * except that it stores the most common non-null *elements* of the column
+ * values. This is useful when the column datatype is an array or some other
+ * type with identifiable elements (for instance, tsvector). staop contains
+ * the equality operator appropriate to the element type. stavalues contains
+ * the most common element values, and stanumbers their frequencies. Unlike
+ * MCV slots, the values are sorted into order (to support binary search
+ * for a particular value). Since this puts the minimum and maximum
+ * frequencies at unpredictable spots in stanumbers, there are two extra
+ * members of stanumbers, holding copies of the minimum and maximum
+ * frequencies.
+ *
+ * Note: in current usage for tsvector columns, the stavalues elements are of
+ * type text, even though their representation within tsvector is not
+ * exactly text.
+ */
+#define STATISTIC_KIND_MCELEM 4
+
+#endif
+
+#endif /* PG_STATISTIC2_H */
diff -durpN postgresql.orig/src/include/commands/defrem.h postgresql/src/include/commands/defrem.h
--- postgresql.orig/src/include/commands/defrem.h 2011-01-19 11:15:56.583064592 +0100
+++ postgresql/src/include/commands/defrem.h 2011-02-22 05:36:33.159517868 +0100
@@ -50,6 +50,7 @@ extern char *ChooseIndexName(const char
bool primary, bool isconstraint);
extern List *ChooseIndexColumnNames(List *indexElems);
extern Oid GetDefaultOpClass(Oid type_id, Oid am_id);
+extern void CrossColStat(CrossColStatStmt *stmt);
/* commands/functioncmds.c */
extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString);
diff -durpN postgresql.orig/src/include/nodes/nodes.h postgresql/src/include/nodes/nodes.h
--- postgresql.orig/src/include/nodes/nodes.h 2011-01-04 15:13:16.151549992 +0100
+++ postgresql/src/include/nodes/nodes.h 2011-02-22 06:09:34.349625597 +0100
@@ -354,6 +354,8 @@ typedef enum NodeTag
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
T_CreateForeignTableStmt,
+ T_CrossColStatStmt,
+ T_CrossColStatColumn,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff -durpN postgresql.orig/src/include/nodes/parsenodes.h postgresql/src/include/nodes/parsenodes.h
--- postgresql.orig/src/include/nodes/parsenodes.h 2011-01-26 14:17:23.956756790 +0100
+++ postgresql/src/include/nodes/parsenodes.h 2011-02-22 06:09:22.638440685 +0100
@@ -1112,6 +1112,24 @@ typedef enum DropBehavior
} DropBehavior;
/* ----------------------
+ * Create Cross Column Statistics
+ * ----------------------
+ */
+typedef struct CrossColStatStmt
+{
+ NodeTag type;
+ bool create;
+ RangeVar *relation;
+ List *columns;
+} CrossColStatStmt;
+
+typedef struct CrossColStatColumn
+{
+ NodeTag type;
+ char *colname;
+} CrossColStatColumn;
+
+/* ----------------------
* Alter Table
* ----------------------
*/