1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

Replace functional-index facility with expressional indexes. Any column

of an index can now be a computed expression instead of a simple variable.
Restrictions on expressions are the same as for predicates (only immutable
functions, no sub-selects).  This fixes problems recently introduced with
inlining SQL functions, because the inlining transformation is applied to
both expression trees so the planner can still match them up.  Along the
way, improve efficiency of handling index predicates (both predicates and
index expressions are now cached by the relcache) and fix 7.3 oversight
that didn't record dependencies of predicate expressions.
This commit is contained in:
Tom Lane
2003-05-28 16:04:02 +00:00
parent e5f19598e0
commit fc8d970cbc
50 changed files with 1351 additions and 1283 deletions

View File

@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.271 2003/05/06 00:20:32 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.272 2003/05/28 16:03:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1311,8 +1311,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
/* OK, add it to the index definition */
iparam = makeNode(IndexElem);
iparam->name = pstrdup(key);
iparam->funcname = NIL;
iparam->args = NIL;
iparam->expr = NULL;
iparam->opclass = NIL;
index->indexParams = lappend(index->indexParams, iparam);
}
@@ -1386,11 +1385,13 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
if (index->idxname == NULL && index->indexParams != NIL)
{
iparam = lfirst(index->indexParams);
iparam = (IndexElem *) lfirst(index->indexParams);
/* we should never see an expression item here */
Assert(iparam->expr == NULL);
index->idxname = CreateIndexName(cxt->relation->relname,
iparam->name ? iparam->name :
strVal(llast(iparam->funcname)),
"key", cxt->alist);
iparam->name,
"key",
cxt->alist);
}
if (index->idxname == NULL) /* should not happen */
elog(ERROR, "%s: failed to make implicit index name",
@@ -1454,7 +1455,8 @@ static Query *
transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
{
Query *qry;
RangeTblEntry *rte;
RangeTblEntry *rte = NULL;
List *l;
qry = makeNode(Query);
qry->commandType = CMD_UTILITY;
@@ -1477,6 +1479,32 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
}
/* take care of any index expressions */
foreach(l, stmt->indexParams)
{
IndexElem *ielem = (IndexElem *) lfirst(l);
if (ielem->expr)
{
/* Set up rtable as for predicate, see notes above */
if (rte == NULL)
{
rte = addRangeTableEntry(pstate, stmt->relation, NULL,
false, true);
/* no to join list, yes to namespace */
addRTEtoQuery(pstate, rte, false, true);
}
ielem->expr = transformExpr(pstate, ielem->expr);
/*
* We check only that the result type is legitimate; this is
* for consistency with what transformWhereClause() checks for
* the predicate. DefineIndex() will make more checks.
*/
if (expression_returns_set(ielem->expr))
elog(ERROR, "index expression may not return a set");
}
}
qry->hasSubLinks = pstate->p_hasSubLinks;
stmt->rangetable = pstate->p_rtable;

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.414 2003/05/15 16:35:28 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.415 2003/05/28 16:03:57 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -210,7 +210,7 @@ static void doNegateFloat(Value *v);
oper_argtypes RuleActionList RuleActionMulti
opt_column_list columnList opt_name_list
sort_clause opt_sort_clause sortby_list index_params
index_list name_list from_clause from_list opt_array_bounds
name_list from_clause from_list opt_array_bounds
qualified_name_list any_name any_name_list
any_operator expr_list dotted_name attrs
target_list update_target_list insert_column_list
@@ -276,7 +276,7 @@ static void doNegateFloat(Value *v);
%type <columnref> columnref
%type <alias> alias_clause
%type <sortgroupby> sortby
%type <ielem> index_elem func_index
%type <ielem> index_elem
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
@@ -408,7 +408,7 @@ static void doNegateFloat(Value *v);
%token UNIONJOIN
/* Special keywords, not in the query language - see the "lex" file */
%token <str> IDENT FCONST SCONST NCONST BCONST XCONST Op
%token <str> IDENT FCONST SCONST BCONST XCONST Op
%token <ival> ICONST PARAM
/* precedence: lowest to highest */
@@ -2932,7 +2932,7 @@ function_with_argtypes:
*
* QUERY:
* create index <indexname> on <relname>
* [ using <access> ] "(" (<col> with <op>)+ ")"
* [ using <access> ] "(" ( <col> [ using <opclass> ] )+ ")"
* [ where <predicate> ]
*
*****************************************************************************/
@@ -2958,70 +2958,48 @@ index_opt_unique:
access_method_clause:
USING access_method { $$ = $2; }
/* If btree changes as our default, update pg_get_indexdef() */
| /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; }
;
index_params:
index_list { $$ = $1; }
| func_index { $$ = makeList1($1); }
index_params: index_elem { $$ = makeList1($1); }
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
index_list: index_elem { $$ = makeList1($1); }
| index_list ',' index_elem { $$ = lappend($1, $3); }
;
func_index: func_name '(' name_list ')' opt_class
{
$$ = makeNode(IndexElem);
$$->name = NULL;
$$->funcname = $1;
$$->args = $3;
$$->opclass = $5;
}
;
index_elem: attr_name opt_class
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
index_elem: attr_name opt_class
{
$$ = makeNode(IndexElem);
$$->name = $1;
$$->funcname = NIL;
$$->args = NIL;
$$->expr = NULL;
$$->opclass = $2;
}
| func_name '(' expr_list ')' opt_class
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = makeNode(IndexElem);
$$->name = NULL;
$$->expr = (Node *)n;
$$->opclass = $5;
}
| '(' a_expr ')' opt_class
{
$$ = makeNode(IndexElem);
$$->name = NULL;
$$->expr = $2;
$$->opclass = $4;
}
;
opt_class: any_name
{
/*
* Release 7.0 removed network_ops, timespan_ops, and
* datetime_ops, so we suppress it from being passed to
* the parser so the default *_ops is used. This can be
* removed in some later release. bjm 2000/02/07
*
* Release 7.1 removes lztext_ops, so suppress that too
* for a while. tgl 2000/07/30
*
* Release 7.2 renames timestamp_ops to timestamptz_ops,
* so suppress that too for awhile. I'm starting to
* think we need a better approach. tgl 2000/10/01
*/
if (length($1) == 1)
{
char *claname = strVal(lfirst($1));
if (strcmp(claname, "network_ops") != 0 &&
strcmp(claname, "timespan_ops") != 0 &&
strcmp(claname, "datetime_ops") != 0 &&
strcmp(claname, "lztext_ops") != 0 &&
strcmp(claname, "timestamp_ops") != 0)
$$ = $1;
else
$$ = NIL;
}
else
$$ = $1;
}
opt_class: any_name { $$ = $1; }
| USING any_name { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;