mirror of
https://github.com/postgres/postgres.git
synced 2025-11-18 02:02:55 +03:00
Restructure pg_opclass, pg_amop, and pg_amproc per previous discussions in
pgsql-hackers. pg_opclass now has a row for each opclass supported by each index AM, not a row for each opclass name. This allows pg_opclass to show directly whether an AM supports an opclass, and furthermore makes it possible to store additional information about an opclass that might be AM-dependent. pg_opclass and pg_amop now store "lossy" and "haskeytype" information that we previously expected the user to remember to provide in CREATE INDEX commands. Lossiness is no longer an index-level property, but is associated with the use of a particular operator in a particular index opclass. Along the way, IndexSupportInitialize now uses the syscaches to retrieve pg_amop and pg_amproc entries. I find this reduces backend launch time by about ten percent, at the cost of a couple more special cases in catcache.c's IndexScanOK. Initial work by Oleg Bartunov and Teodor Sigaev, further hacking by Tom Lane. initdb forced.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.81 2001/08/10 14:34:28 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.82 2001/08/21 16:35:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -20,8 +20,10 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
#include "executor/executor.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "access/xlogutils.h"
|
||||
|
||||
@@ -1377,10 +1379,9 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
|
||||
|
||||
for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i))
|
||||
{
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
|
||||
sum_grow=0;
|
||||
for (j=0; j<r->rd_att->natts; j++) {
|
||||
IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
|
||||
|
||||
datum = index_getattr(itup, j+1, r->rd_att, &IsNull);
|
||||
gistdentryinit(giststate, j, &entry, datum, r, p, i, ATTSIZE( datum, r, j+1, IsNull ), FALSE, IsNull);
|
||||
gistpenalty( giststate, j, &entry, IsNull, &identry[j], isnull[j], &usize);
|
||||
@@ -1548,20 +1549,32 @@ initGISTstate(GISTSTATE *giststate, Relation index)
|
||||
RegProcedure consistent_proc,
|
||||
union_proc,
|
||||
compress_proc,
|
||||
decompress_proc;
|
||||
RegProcedure penalty_proc,
|
||||
decompress_proc,
|
||||
penalty_proc,
|
||||
picksplit_proc,
|
||||
equal_proc;
|
||||
HeapTuple htup;
|
||||
HeapTuple itup;
|
||||
HeapTuple ctup;
|
||||
Form_pg_index itupform;
|
||||
Oid indexrelid;
|
||||
Form_pg_opclass opclassform;
|
||||
Oid inputtype;
|
||||
Oid keytype;
|
||||
int i;
|
||||
|
||||
if (index->rd_att->natts >= INDEX_MAX_KEYS)
|
||||
elog(ERROR, "initGISTstate: numberOfAttributes %d > %d",
|
||||
index->rd_att->natts, INDEX_MAX_KEYS);
|
||||
if (index->rd_att->natts > INDEX_MAX_KEYS)
|
||||
elog(ERROR, "initGISTstate: numberOfAttributes %d > %d",
|
||||
index->rd_att->natts, INDEX_MAX_KEYS);
|
||||
|
||||
for(i=0; i<index->rd_att->natts; i++) {
|
||||
itup = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(RelationGetRelid(index)),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(itup))
|
||||
elog(ERROR, "initGISTstate: index %u not found",
|
||||
RelationGetRelid(index));
|
||||
itupform = (Form_pg_index) GETSTRUCT(itup);
|
||||
|
||||
for (i = 0; i < index->rd_att->natts; i++)
|
||||
{
|
||||
consistent_proc = index_getprocid(index, i+1, GIST_CONSISTENT_PROC );
|
||||
union_proc = index_getprocid(index, i+1, GIST_UNION_PROC );
|
||||
compress_proc = index_getprocid(index, i+1, GIST_COMPRESS_PROC );
|
||||
@@ -1577,37 +1590,35 @@ initGISTstate(GISTSTATE *giststate, Relation index)
|
||||
fmgr_info(picksplit_proc, &((giststate->picksplitFn)[i]) );
|
||||
fmgr_info(equal_proc, &((giststate->equalFn)[i]) );
|
||||
|
||||
giststate->attbyval[i] =
|
||||
index->rd_att->attrs[i]->attbyval;
|
||||
/* Check opclass entry to see if there is a keytype */
|
||||
ctup = SearchSysCache(CLAOID,
|
||||
ObjectIdGetDatum(itupform->indclass[i]),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(ctup))
|
||||
elog(ERROR, "cache lookup failed for opclass %u",
|
||||
itupform->indclass[i]);
|
||||
opclassform = (Form_pg_opclass) GETSTRUCT(ctup);
|
||||
inputtype = opclassform->opcintype;
|
||||
keytype = opclassform->opckeytype;
|
||||
ReleaseSysCache(ctup);
|
||||
|
||||
if (OidIsValid(keytype))
|
||||
{
|
||||
/* index column type is (possibly) different from input data */
|
||||
giststate->haskeytype[i] = true;
|
||||
giststate->attbyval[i] = get_typbyval(inputtype);
|
||||
giststate->keytypbyval[i] = index->rd_att->attrs[i]->attbyval;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Normal case where index column type is same as input data */
|
||||
giststate->haskeytype[i] = false;
|
||||
giststate->attbyval[i] = index->rd_att->attrs[i]->attbyval;
|
||||
giststate->keytypbyval[i] = false; /* not actually used */
|
||||
}
|
||||
}
|
||||
|
||||
/* see if key type is different from type of attribute being indexed */
|
||||
htup = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(RelationGetRelid(index)),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(htup))
|
||||
elog(ERROR, "initGISTstate: index %u not found",
|
||||
RelationGetRelid(index));
|
||||
itupform = (Form_pg_index) GETSTRUCT(htup);
|
||||
giststate->haskeytype = itupform->indhaskeytype;
|
||||
indexrelid = itupform->indexrelid;
|
||||
ReleaseSysCache(htup);
|
||||
|
||||
if (giststate->haskeytype)
|
||||
{
|
||||
/* key type is different -- is it byval? */
|
||||
htup = SearchSysCache(ATTNUM,
|
||||
ObjectIdGetDatum(indexrelid),
|
||||
UInt16GetDatum(FirstOffsetNumber),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(htup))
|
||||
elog(ERROR, "initGISTstate: no attribute tuple %u %d",
|
||||
indexrelid, FirstOffsetNumber);
|
||||
giststate->keytypbyval = (((Form_pg_attribute) htup)->attbyval);
|
||||
ReleaseSysCache(htup);
|
||||
}
|
||||
else
|
||||
giststate->keytypbyval = FALSE;
|
||||
ReleaseSysCache(itup);
|
||||
}
|
||||
|
||||
#ifdef GIST_PAGEADDITEM
|
||||
@@ -1670,7 +1681,7 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
|
||||
GISTENTRY *dep;
|
||||
|
||||
gistentryinit(*e, k, r, pg, o, b, l);
|
||||
if (giststate->haskeytype)
|
||||
if (giststate->haskeytype[nkey])
|
||||
{
|
||||
if ( b && ! isNull ) {
|
||||
dep = (GISTENTRY *)
|
||||
@@ -1698,7 +1709,7 @@ gistcentryinit(GISTSTATE *giststate, int nkey,
|
||||
GISTENTRY *cep;
|
||||
|
||||
gistentryinit(*e, k, r, pg, o, b, l);
|
||||
if (giststate->haskeytype)
|
||||
if (giststate->haskeytype[nkey])
|
||||
{
|
||||
if ( ! isNull ) {
|
||||
cep = (GISTENTRY *)
|
||||
@@ -1792,7 +1803,7 @@ gistpenalty( GISTSTATE *giststate, int attno,
|
||||
FunctionCall3(&giststate->penaltyFn[attno],
|
||||
PointerGetDatum(key1),
|
||||
PointerGetDatum(key2),
|
||||
PointerGetDatum(&penalty));
|
||||
PointerGetDatum(penalty));
|
||||
}
|
||||
|
||||
#ifdef GISTDEBUG
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.51 2001/06/01 02:41:35 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.52 2001/08/21 16:36:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -457,61 +457,32 @@ RelationInvokeStrategy(Relation relation,
|
||||
#endif
|
||||
|
||||
/* ----------------
|
||||
* OperatorRelationFillScanKeyEntry
|
||||
* FillScanKeyEntry
|
||||
*
|
||||
* Initialize a ScanKey entry given already-opened pg_operator relation.
|
||||
* Initialize a ScanKey entry for the given operator OID.
|
||||
* ----------------
|
||||
*/
|
||||
static void
|
||||
OperatorRelationFillScanKeyEntry(Relation operatorRelation,
|
||||
Oid operatorObjectId,
|
||||
ScanKey entry)
|
||||
FillScanKeyEntry(Oid operatorObjectId, ScanKey entry)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
HeapScanDesc scan = NULL;
|
||||
bool cachesearch = (!IsBootstrapProcessingMode()) && IsCacheInitialized();
|
||||
|
||||
if (cachesearch)
|
||||
{
|
||||
tuple = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(operatorObjectId),
|
||||
0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScanKeyData scanKeyData;
|
||||
|
||||
ScanKeyEntryInitialize(&scanKeyData, 0,
|
||||
ObjectIdAttributeNumber,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(operatorObjectId));
|
||||
|
||||
scan = heap_beginscan(operatorRelation, false, SnapshotNow,
|
||||
1, &scanKeyData);
|
||||
|
||||
tuple = heap_getnext(scan, 0);
|
||||
}
|
||||
tuple = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(operatorObjectId),
|
||||
0, 0, 0);
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
{
|
||||
if (!cachesearch)
|
||||
heap_endscan(scan);
|
||||
elog(ERROR, "OperatorRelationFillScanKeyEntry: unknown operator %u",
|
||||
elog(ERROR, "FillScanKeyEntry: unknown operator %u",
|
||||
operatorObjectId);
|
||||
}
|
||||
|
||||
MemSet(entry, 0, sizeof(*entry));
|
||||
entry->sk_flags = 0;
|
||||
entry->sk_procedure = ((Form_pg_operator) GETSTRUCT(tuple))->oprcode;
|
||||
|
||||
if (cachesearch)
|
||||
ReleaseSysCache(tuple);
|
||||
else
|
||||
heap_endscan(scan);
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
if (!RegProcedureIsValid(entry->sk_procedure))
|
||||
elog(ERROR,
|
||||
"OperatorRelationFillScanKeyEntry: no procedure for operator %u",
|
||||
elog(ERROR, "FillScanKeyEntry: no procedure for operator %u",
|
||||
operatorObjectId);
|
||||
|
||||
/*
|
||||
@@ -548,160 +519,99 @@ IndexSupportInitialize(IndexStrategy indexStrategy,
|
||||
StrategyNumber maxSupportNumber,
|
||||
AttrNumber maxAttributeNumber)
|
||||
{
|
||||
Relation relation = NULL;
|
||||
HeapScanDesc scan = NULL;
|
||||
ScanKeyData entry[2];
|
||||
Relation operatorRelation;
|
||||
HeapTuple tuple;
|
||||
Form_pg_index iform;
|
||||
StrategyMap map;
|
||||
AttrNumber attNumber;
|
||||
int attIndex;
|
||||
Oid operatorClassObjectId[INDEX_MAX_KEYS];
|
||||
bool cachesearch = (!IsBootstrapProcessingMode()) && IsCacheInitialized();
|
||||
|
||||
if (cachesearch)
|
||||
{
|
||||
tuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(indexObjectId),
|
||||
0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_index_indexrelid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(indexObjectId));
|
||||
|
||||
relation = heap_openr(IndexRelationName, AccessShareLock);
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 1, entry);
|
||||
tuple = heap_getnext(scan, 0);
|
||||
}
|
||||
maxStrategyNumber = AMStrategies(maxStrategyNumber);
|
||||
|
||||
tuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(indexObjectId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "IndexSupportInitialize: no pg_index entry for index %u",
|
||||
indexObjectId);
|
||||
|
||||
iform = (Form_pg_index) GETSTRUCT(tuple);
|
||||
|
||||
*isUnique = iform->indisunique;
|
||||
|
||||
maxStrategyNumber = AMStrategies(maxStrategyNumber);
|
||||
|
||||
/*
|
||||
* XXX note that the following assumes the INDEX tuple is well formed
|
||||
* and that the *key and *class are 0 terminated.
|
||||
*/
|
||||
for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
|
||||
{
|
||||
if (!OidIsValid(iform->indkey[attIndex]))
|
||||
{
|
||||
if (attIndex == InvalidAttrNumber)
|
||||
elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple");
|
||||
break;
|
||||
}
|
||||
|
||||
if (iform->indkey[attIndex] == InvalidAttrNumber ||
|
||||
!OidIsValid(iform->indclass[attIndex]))
|
||||
elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple");
|
||||
operatorClassObjectId[attIndex] = iform->indclass[attIndex];
|
||||
}
|
||||
|
||||
if (cachesearch)
|
||||
ReleaseSysCache(tuple);
|
||||
else
|
||||
{
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
}
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/* if support routines exist for this access method, load them */
|
||||
if (maxSupportNumber > 0)
|
||||
{
|
||||
ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_amproc_amid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(accessMethodObjectId));
|
||||
|
||||
ScanKeyEntryInitialize(&entry[1], 0, Anum_pg_amproc_amopclaid,
|
||||
F_OIDEQ,
|
||||
InvalidOid); /* will set below */
|
||||
|
||||
relation = heap_openr(AccessMethodProcedureRelationName,
|
||||
AccessShareLock);
|
||||
|
||||
for (attNumber = 1; attNumber <= maxAttributeNumber; attNumber++)
|
||||
for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
|
||||
{
|
||||
Oid opclass = operatorClassObjectId[attIndex];
|
||||
RegProcedure *loc;
|
||||
StrategyNumber support;
|
||||
|
||||
loc = &indexSupport[((attNumber - 1) * maxSupportNumber)];
|
||||
loc = &indexSupport[attIndex * maxSupportNumber];
|
||||
|
||||
for (support = 0; support < maxSupportNumber; ++support)
|
||||
loc[support] = InvalidOid;
|
||||
|
||||
entry[1].sk_argument =
|
||||
ObjectIdGetDatum(operatorClassObjectId[attNumber - 1]);
|
||||
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
|
||||
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
Form_pg_amproc aform;
|
||||
tuple = SearchSysCache(AMPROCNUM,
|
||||
ObjectIdGetDatum(opclass),
|
||||
Int16GetDatum(support+1),
|
||||
0, 0);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
Form_pg_amproc amprocform;
|
||||
|
||||
aform = (Form_pg_amproc) GETSTRUCT(tuple);
|
||||
support = aform->amprocnum;
|
||||
Assert(support > 0 && support <= maxSupportNumber);
|
||||
loc[support - 1] = aform->amproc;
|
||||
amprocform = (Form_pg_amproc) GETSTRUCT(tuple);
|
||||
loc[support] = amprocform->amproc;
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
else
|
||||
loc[support] = InvalidOid;
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
}
|
||||
heap_close(relation, AccessShareLock);
|
||||
}
|
||||
|
||||
/* Now load the strategy information for the index operators */
|
||||
ScanKeyEntryInitialize(&entry[0], 0,
|
||||
Anum_pg_amop_amopid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(accessMethodObjectId));
|
||||
|
||||
ScanKeyEntryInitialize(&entry[1], 0,
|
||||
Anum_pg_amop_amopclaid,
|
||||
F_OIDEQ,
|
||||
0); /* will fill below */
|
||||
|
||||
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
||||
operatorRelation = heap_openr(OperatorRelationName, AccessShareLock);
|
||||
|
||||
for (attNumber = maxAttributeNumber; attNumber > 0; attNumber--)
|
||||
for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
|
||||
{
|
||||
Oid opclass = operatorClassObjectId[attIndex];
|
||||
StrategyMap map;
|
||||
StrategyNumber strategy;
|
||||
|
||||
entry[1].sk_argument =
|
||||
ObjectIdGetDatum(operatorClassObjectId[attNumber - 1]);
|
||||
|
||||
map = IndexStrategyGetStrategyMap(indexStrategy,
|
||||
maxStrategyNumber,
|
||||
attNumber);
|
||||
attIndex + 1);
|
||||
|
||||
for (strategy = 1; strategy <= maxStrategyNumber; strategy++)
|
||||
ScanKeyEntrySetIllegal(StrategyMapGetScanKeyEntry(map, strategy));
|
||||
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
|
||||
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
Form_pg_amop aform;
|
||||
ScanKey mapentry = StrategyMapGetScanKeyEntry(map, strategy);
|
||||
|
||||
aform = (Form_pg_amop) GETSTRUCT(tuple);
|
||||
strategy = aform->amopstrategy;
|
||||
Assert(strategy > 0 && strategy <= maxStrategyNumber);
|
||||
OperatorRelationFillScanKeyEntry(operatorRelation,
|
||||
aform->amopopr,
|
||||
StrategyMapGetScanKeyEntry(map, strategy));
|
||||
tuple = SearchSysCache(AMOPSTRATEGY,
|
||||
ObjectIdGetDatum(opclass),
|
||||
Int16GetDatum(strategy),
|
||||
0, 0);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
Form_pg_amop amopform;
|
||||
|
||||
amopform = (Form_pg_amop) GETSTRUCT(tuple);
|
||||
FillScanKeyEntry(amopform->amopopr, mapentry);
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
else
|
||||
ScanKeyEntrySetIllegal(mapentry);
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
}
|
||||
|
||||
heap_close(operatorRelation, AccessShareLock);
|
||||
heap_close(relation, AccessShareLock);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.37 2001/08/10 18:57:33 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.38 2001/08/21 16:36:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -246,7 +246,7 @@ Boot_DeclareIndexStmt:
|
||||
DefineIndex(LexIDStr($5),
|
||||
LexIDStr($3),
|
||||
LexIDStr($7),
|
||||
$9, NIL, 0, 0, 0, NIL);
|
||||
$9, false, false, NULL, NIL);
|
||||
do_end();
|
||||
}
|
||||
;
|
||||
@@ -259,7 +259,7 @@ Boot_DeclareUniqueIndexStmt:
|
||||
DefineIndex(LexIDStr($6),
|
||||
LexIDStr($4),
|
||||
LexIDStr($8),
|
||||
$10, NIL, 1, 0, 0, NIL);
|
||||
$10, true, false, NULL, NIL);
|
||||
do_end();
|
||||
}
|
||||
;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.160 2001/08/17 23:50:00 inoue Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.161 2001/08/21 16:36:00 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@@ -71,9 +71,9 @@ static void InitializeAttributeOids(Relation indexRelation,
|
||||
int numatts, Oid indexoid);
|
||||
static void AppendAttributeTuples(Relation indexRelation, int numatts);
|
||||
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
|
||||
IndexInfo *indexInfo,
|
||||
Oid *classOids,
|
||||
bool islossy, bool primary);
|
||||
IndexInfo *indexInfo,
|
||||
Oid *classOids,
|
||||
bool primary);
|
||||
static Oid IndexGetRelation(Oid indexId);
|
||||
static bool activate_index(Oid indexId, bool activate, bool inplace);
|
||||
|
||||
@@ -495,7 +495,6 @@ UpdateIndexRelation(Oid indexoid,
|
||||
Oid heapoid,
|
||||
IndexInfo *indexInfo,
|
||||
Oid *classOids,
|
||||
bool islossy,
|
||||
bool primary)
|
||||
{
|
||||
Form_pg_index indexForm;
|
||||
@@ -535,8 +534,6 @@ UpdateIndexRelation(Oid indexoid,
|
||||
indexForm->indrelid = heapoid;
|
||||
indexForm->indproc = indexInfo->ii_FuncOid;
|
||||
indexForm->indisclustered = false; /* not used */
|
||||
indexForm->indislossy = islossy;
|
||||
indexForm->indhaskeytype = true; /* used by GIST */
|
||||
indexForm->indisunique = indexInfo->ii_Unique;
|
||||
indexForm->indisprimary = primary;
|
||||
memcpy((char *) &indexForm->indpred, (char *) predText, predLen);
|
||||
@@ -671,7 +668,6 @@ index_create(char *heapRelationName,
|
||||
IndexInfo *indexInfo,
|
||||
Oid accessMethodObjectId,
|
||||
Oid *classObjectId,
|
||||
bool islossy,
|
||||
bool primary,
|
||||
bool allow_system_table_mods)
|
||||
{
|
||||
@@ -779,7 +775,7 @@ index_create(char *heapRelationName,
|
||||
* ----------------
|
||||
*/
|
||||
UpdateIndexRelation(indexoid, heapoid, indexInfo,
|
||||
classObjectId, islossy, primary);
|
||||
classObjectId, primary);
|
||||
|
||||
/*
|
||||
* initialize the index strategy
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.81 2001/08/10 18:57:34 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.82 2001/08/21 16:36:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -36,9 +36,9 @@ char *Name_pg_aggregate_indices[Num_pg_aggregate_indices] =
|
||||
char *Name_pg_am_indices[Num_pg_am_indices] =
|
||||
{AmNameIndex, AmOidIndex};
|
||||
char *Name_pg_amop_indices[Num_pg_amop_indices] =
|
||||
{AccessMethodOpidIndex, AccessMethodStrategyIndex};
|
||||
{AccessMethodOperatorIndex, AccessMethodStrategyIndex};
|
||||
char *Name_pg_amproc_indices[Num_pg_amproc_indices] =
|
||||
{AccessProcedureIndex};
|
||||
{AccessMethodProcedureIndex};
|
||||
char *Name_pg_attr_indices[Num_pg_attr_indices] =
|
||||
{AttributeRelidNameIndex, AttributeRelidNumIndex};
|
||||
char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] =
|
||||
@@ -58,7 +58,7 @@ char *Name_pg_language_indices[Num_pg_language_indices] =
|
||||
char *Name_pg_largeobject_indices[Num_pg_largeobject_indices] =
|
||||
{LargeObjectLOidPNIndex};
|
||||
char *Name_pg_opclass_indices[Num_pg_opclass_indices] =
|
||||
{OpclassDeftypeIndex, OpclassNameIndex, OpclassOidIndex};
|
||||
{OpclassAmNameIndex, OpclassOidIndex};
|
||||
char *Name_pg_operator_indices[Num_pg_operator_indices] =
|
||||
{OperatorOidIndex, OperatorNameIndex};
|
||||
char *Name_pg_proc_indices[Num_pg_proc_indices] =
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.68 2001/08/10 18:57:34 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.69 2001/08/21 16:36:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -225,7 +225,6 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName)
|
||||
indexInfo,
|
||||
Old_pg_index_relation_Form->relam,
|
||||
Old_pg_index_Form->indclass,
|
||||
Old_pg_index_Form->indislossy,
|
||||
Old_pg_index_Form->indisprimary,
|
||||
allowSystemTableMods);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.140 2001/08/10 18:57:34 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.141 2001/08/21 16:36:01 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The PerformAddAttribute() code, like most of the relation
|
||||
@@ -1902,12 +1902,12 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
||||
indexInfo->ii_FuncOid = InvalidOid;
|
||||
indexInfo->ii_Unique = true;
|
||||
|
||||
classObjectId[0] = OID_OPS_OID;
|
||||
classObjectId[1] = INT4_OPS_OID;
|
||||
classObjectId[0] = OID_BTREE_OPS_OID;
|
||||
classObjectId[1] = INT4_BTREE_OPS_OID;
|
||||
|
||||
toast_idxid = index_create(toast_relname, toast_idxname, indexInfo,
|
||||
BTREE_AM_OID, classObjectId,
|
||||
false, true, true);
|
||||
true, true);
|
||||
|
||||
/*
|
||||
* Update toast rel's pg_class entry to show that it has an index.
|
||||
|
||||
@@ -8,26 +8,19 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.56 2001/08/10 18:57:34 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.57 2001/08/21 16:36:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/pg_am.h"
|
||||
#include "catalog/pg_amop.h"
|
||||
#include "catalog/pg_database.h"
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "miscadmin.h"
|
||||
#include "optimizer/clauses.h"
|
||||
@@ -36,7 +29,6 @@
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
@@ -57,7 +49,7 @@ static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
|
||||
char *accessMethodName, Oid accessMethodId);
|
||||
static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
char *accessMethodName, Oid accessMethodId);
|
||||
static char *GetDefaultOpClass(Oid atttypid);
|
||||
static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);
|
||||
|
||||
/*
|
||||
* DefineIndex
|
||||
@@ -65,16 +57,14 @@ static char *GetDefaultOpClass(Oid atttypid);
|
||||
*
|
||||
* 'attributeList' is a list of IndexElem specifying either a functional
|
||||
* index or a list of attributes to index on.
|
||||
* 'parameterList' is a list of DefElem specified in the with clause.
|
||||
* 'predicate' is the qual specified in the where clause.
|
||||
* 'rangetable' is needed to interpret the predicate
|
||||
* 'rangetable' is needed to interpret the predicate.
|
||||
*/
|
||||
void
|
||||
DefineIndex(char *heapRelationName,
|
||||
char *indexRelationName,
|
||||
char *accessMethodName,
|
||||
List *attributeList,
|
||||
List *parameterList,
|
||||
bool unique,
|
||||
bool primary,
|
||||
Expr *predicate,
|
||||
@@ -88,8 +78,6 @@ DefineIndex(char *heapRelationName,
|
||||
IndexInfo *indexInfo;
|
||||
int numberOfAttributes;
|
||||
List *cnfPred = NIL;
|
||||
bool lossy = false;
|
||||
List *pl;
|
||||
|
||||
/*
|
||||
* count attributes in index
|
||||
@@ -129,20 +117,6 @@ DefineIndex(char *heapRelationName,
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
|
||||
*/
|
||||
foreach(pl, parameterList)
|
||||
{
|
||||
DefElem *param = (DefElem *) lfirst(pl);
|
||||
|
||||
if (!strcasecmp(param->defname, "islossy"))
|
||||
lossy = true;
|
||||
else
|
||||
elog(NOTICE, "Unrecognized index attribute \"%s\" ignored",
|
||||
param->defname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the partial-index predicate from parsetree form to
|
||||
* an implicit-AND qual expression, for easier evaluation at runtime.
|
||||
@@ -203,7 +177,7 @@ DefineIndex(char *heapRelationName,
|
||||
|
||||
index_create(heapRelationName, indexRelationName,
|
||||
indexInfo, accessMethodId, classObjectId,
|
||||
lossy, primary, allowSystemTableMods);
|
||||
primary, allowSystemTableMods);
|
||||
|
||||
/*
|
||||
* We update the relation's pg_class tuple even if it already has
|
||||
@@ -390,111 +364,109 @@ static Oid
|
||||
GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
char *accessMethodName, Oid accessMethodId)
|
||||
{
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
ScanKeyData entry[2];
|
||||
HeapTuple tuple;
|
||||
Oid opClassId,
|
||||
oprId;
|
||||
bool doTypeCheck = true;
|
||||
opInputType;
|
||||
|
||||
if (attribute->class == NULL)
|
||||
{
|
||||
/* no operator class specified, so find the default */
|
||||
attribute->class = GetDefaultOpClass(attrType);
|
||||
if (attribute->class == NULL)
|
||||
elog(ERROR, "data type %s has no default operator class"
|
||||
opClassId = GetDefaultOpClass(attrType, accessMethodId);
|
||||
if (!OidIsValid(opClassId))
|
||||
elog(ERROR, "data type %s has no default operator class for access method \"%s\""
|
||||
"\n\tYou must specify an operator class for the index or define a"
|
||||
"\n\tdefault operator class for the data type",
|
||||
format_type_be(attrType));
|
||||
/* assume we need not check type compatibility */
|
||||
doTypeCheck = false;
|
||||
format_type_be(attrType), accessMethodName);
|
||||
return opClassId;
|
||||
}
|
||||
|
||||
opClassId = GetSysCacheOid(CLANAME,
|
||||
PointerGetDatum(attribute->class),
|
||||
0, 0, 0);
|
||||
if (!OidIsValid(opClassId))
|
||||
elog(ERROR, "DefineIndex: opclass \"%s\" not found",
|
||||
attribute->class);
|
||||
|
||||
/*
|
||||
* Assume the opclass is supported by this index access method if we
|
||||
* can find at least one relevant entry in pg_amop.
|
||||
* Find the index operator class and verify that it accepts this
|
||||
* datatype. Note we will accept binary compatibility.
|
||||
*/
|
||||
ScanKeyEntryInitialize(&entry[0], 0,
|
||||
Anum_pg_amop_amopid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(accessMethodId));
|
||||
ScanKeyEntryInitialize(&entry[1], 0,
|
||||
Anum_pg_amop_amopclaid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(opClassId));
|
||||
|
||||
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
|
||||
|
||||
if (!HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
elog(ERROR, "DefineIndex: opclass \"%s\" not supported by access method \"%s\"",
|
||||
tuple = SearchSysCache(CLAAMNAME,
|
||||
ObjectIdGetDatum(accessMethodId),
|
||||
PointerGetDatum(attribute->class),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "DefineIndex: operator class \"%s\" not supported by access method \"%s\"",
|
||||
attribute->class, accessMethodName);
|
||||
opClassId = tuple->t_data->t_oid;
|
||||
opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype;
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
oprId = ((Form_pg_amop) GETSTRUCT(tuple))->amopopr;
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Make sure the operators associated with this opclass actually
|
||||
* accept the column data type. This prevents possible coredumps
|
||||
* caused by user errors like applying text_ops to an int4 column. We
|
||||
* will accept an opclass as OK if the operator's input datatype is
|
||||
* binary-compatible with the actual column datatype. Note we assume
|
||||
* that all the operators associated with an opclass accept the same
|
||||
* datatypes, so checking the first one we happened to find in the
|
||||
* table is sufficient.
|
||||
*
|
||||
* If the opclass was the default for the datatype, assume we can skip
|
||||
* this check --- that saves a few cycles in the most common case. If
|
||||
* pg_opclass is wrong then we're probably screwed anyway...
|
||||
*/
|
||||
if (doTypeCheck)
|
||||
{
|
||||
tuple = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(oprId),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tuple);
|
||||
Oid opInputType = (optup->oprkind == 'l') ?
|
||||
optup->oprright : optup->oprleft;
|
||||
|
||||
if (attrType != opInputType &&
|
||||
!IS_BINARY_COMPATIBLE(attrType, opInputType))
|
||||
elog(ERROR, "operator class \"%s\" does not accept data type %s",
|
||||
attribute->class, format_type_be(attrType));
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
}
|
||||
if (attrType != opInputType &&
|
||||
!IS_BINARY_COMPATIBLE(attrType, opInputType))
|
||||
elog(ERROR, "operator class \"%s\" does not accept data type %s",
|
||||
attribute->class, format_type_be(attrType));
|
||||
|
||||
return opClassId;
|
||||
}
|
||||
|
||||
static char *
|
||||
GetDefaultOpClass(Oid atttypid)
|
||||
static Oid
|
||||
GetDefaultOpClass(Oid attrType, Oid accessMethodId)
|
||||
{
|
||||
Relation relation;
|
||||
ScanKeyData entry[1];
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
char *result;
|
||||
int nexact = 0;
|
||||
int ncompatible = 0;
|
||||
Oid exactOid = InvalidOid;
|
||||
Oid compatibleOid = InvalidOid;
|
||||
|
||||
tuple = SearchSysCache(CLADEFTYPE,
|
||||
ObjectIdGetDatum(atttypid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
return NULL;
|
||||
/*
|
||||
* We scan through all the opclasses available for the access method,
|
||||
* looking for one that is marked default and matches the target type
|
||||
* (either exactly or binary-compatibly, but prefer an exact match).
|
||||
*
|
||||
* We could find more than one binary-compatible match, in which case we
|
||||
* require the user to specify which one he wants. If we find more than
|
||||
* one exact match, then someone put bogus entries in pg_opclass.
|
||||
*
|
||||
* We could use an indexscan here, but since pg_opclass is small
|
||||
* and a scan on opcamid won't be very selective, the indexscan would
|
||||
* probably actually be slower than heapscan.
|
||||
*/
|
||||
ScanKeyEntryInitialize(&entry[0], 0x0,
|
||||
Anum_pg_opclass_opcamid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(accessMethodId));
|
||||
|
||||
result = pstrdup(NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname));
|
||||
relation = heap_openr(OperatorClassRelationName, AccessShareLock);
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 1, entry);
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
return result;
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tuple);
|
||||
|
||||
if (opclass->opcdefault)
|
||||
{
|
||||
if (opclass->opcintype == attrType)
|
||||
{
|
||||
nexact++;
|
||||
exactOid = tuple->t_data->t_oid;
|
||||
}
|
||||
else if (IS_BINARY_COMPATIBLE(opclass->opcintype, attrType))
|
||||
{
|
||||
ncompatible++;
|
||||
compatibleOid = tuple->t_data->t_oid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
|
||||
if (nexact == 1)
|
||||
return exactOid;
|
||||
if (nexact != 0)
|
||||
elog(ERROR, "pg_opclass contains multiple default opclasses for data tyype %s",
|
||||
format_type_be(attrType));
|
||||
if (ncompatible == 1)
|
||||
return compatibleOid;
|
||||
|
||||
return InvalidOid;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.153 2001/08/16 20:38:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.154 2001/08/21 16:36:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1166,7 +1166,6 @@ _copyIndexOptInfo(IndexOptInfo *from)
|
||||
newnode->indproc = from->indproc;
|
||||
Node_Copy(from, newnode, indpred);
|
||||
newnode->unique = from->unique;
|
||||
newnode->lossy = from->lossy;
|
||||
|
||||
return newnode;
|
||||
}
|
||||
@@ -2059,7 +2058,6 @@ _copyIndexStmt(IndexStmt *from)
|
||||
newnode->relname = pstrdup(from->relname);
|
||||
newnode->accessMethod = pstrdup(from->accessMethod);
|
||||
Node_Copy(from, newnode, indexParams);
|
||||
Node_Copy(from, newnode, withClause);
|
||||
Node_Copy(from, newnode, whereClause);
|
||||
Node_Copy(from, newnode, rangetable);
|
||||
newnode->unique = from->unique;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.101 2001/08/16 20:38:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.102 2001/08/21 16:36:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -926,8 +926,6 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
|
||||
return false;
|
||||
if (!equal(a->indexParams, b->indexParams))
|
||||
return false;
|
||||
if (!equal(a->withClause, b->withClause))
|
||||
return false;
|
||||
if (!equal(a->whereClause, b->whereClause))
|
||||
return false;
|
||||
if (!equal(a->rangetable, b->rangetable))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.144 2001/08/16 20:38:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.145 2001/08/21 16:36:02 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||
@@ -133,16 +133,10 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
|
||||
_outToken(str, node->accessMethod);
|
||||
appendStringInfo(str, " :indexParams ");
|
||||
_outNode(str, node->indexParams);
|
||||
|
||||
appendStringInfo(str, " :withClause ");
|
||||
_outNode(str, node->withClause);
|
||||
|
||||
appendStringInfo(str, " :whereClause ");
|
||||
_outNode(str, node->whereClause);
|
||||
|
||||
appendStringInfo(str, " :rangetable ");
|
||||
_outNode(str, node->rangetable);
|
||||
|
||||
appendStringInfo(str, " :unique %s :primary %s ",
|
||||
booltostr(node->unique),
|
||||
booltostr(node->primary));
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.77 2001/06/11 00:17:08 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.78 2001/08/21 16:36:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -339,16 +339,16 @@ cost_index(Path *path, Query *root,
|
||||
*
|
||||
* Normally the indexquals will be removed from the list of
|
||||
* restriction clauses that we have to evaluate as qpquals, so we
|
||||
* should subtract their costs from baserestrictcost. For a lossy
|
||||
* index, however, we will have to recheck all the quals and so
|
||||
* mustn't subtract anything. Also, if we are doing a join then some
|
||||
* of the indexquals are join clauses and shouldn't be subtracted.
|
||||
* Rather than work out exactly how much to subtract, we don't
|
||||
* subtract anything in that case either.
|
||||
* should subtract their costs from baserestrictcost. XXX For a lossy
|
||||
* index, not all the quals will be removed and so we really shouldn't
|
||||
* subtract their costs; but detecting that seems more expensive than
|
||||
* it's worth. Also, if we are doing a join then some of the indexquals
|
||||
* are join clauses and shouldn't be subtracted. Rather than work out
|
||||
* exactly how much to subtract, we don't subtract anything.
|
||||
*/
|
||||
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
|
||||
|
||||
if (!index->lossy && !is_injoin)
|
||||
if (!is_injoin)
|
||||
cpu_per_tuple -= cost_qual_eval(indexQuals);
|
||||
|
||||
run_cost += cpu_per_tuple * tuples_fetched;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.110 2001/08/06 18:09:45 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.111 2001/08/21 16:36:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -46,16 +46,16 @@
|
||||
*
|
||||
* Determine whether we should continue matching index keys in a clause.
|
||||
* Depends on if there are more to match or if this is a functional index.
|
||||
* In the latter case we stop after the first match since the there can
|
||||
* be only key (i.e. the function's return value) and the attributes in
|
||||
* In the latter case we stop after the first match since there can
|
||||
* be only 1 key (i.e. the function's return value) and the attributes in
|
||||
* keys list represent the arguments to the function. -mer 3 Oct. 1991
|
||||
*/
|
||||
#define DoneMatchingIndexKeys(indexkeys, index) \
|
||||
(indexkeys[0] == 0 || \
|
||||
(index->indproc != InvalidOid))
|
||||
|
||||
#define is_indexable_operator(clause,opclass,relam,indexkey_on_left) \
|
||||
(indexable_operator(clause,opclass,relam,indexkey_on_left) != InvalidOid)
|
||||
#define is_indexable_operator(clause,opclass,indexkey_on_left) \
|
||||
(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
|
||||
|
||||
|
||||
static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
|
||||
@@ -92,7 +92,7 @@ static bool match_index_to_operand(int indexkey, Var *operand,
|
||||
RelOptInfo *rel, IndexOptInfo *index);
|
||||
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
|
||||
IndexOptInfo *index);
|
||||
static bool match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
static bool match_special_index_operator(Expr *clause, Oid opclass,
|
||||
bool indexkey_on_left);
|
||||
static List *prefix_quals(Var *leftop, Oid expr_op,
|
||||
char *prefix, Pattern_Prefix_Status pstatus);
|
||||
@@ -754,30 +754,28 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
if (match_index_to_operand(indexkey, leftop, rel, index) &&
|
||||
is_pseudo_constant_clause((Node *) rightop))
|
||||
{
|
||||
if (is_indexable_operator(clause, opclass, index->relam, true))
|
||||
if (is_indexable_operator(clause, opclass, true))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If we didn't find a member of the index's opclass, see
|
||||
* whether it is a "special" indexable operator.
|
||||
*/
|
||||
if (match_special_index_operator(clause, opclass, index->relam,
|
||||
true))
|
||||
if (match_special_index_operator(clause, opclass, true))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (match_index_to_operand(indexkey, rightop, rel, index) &&
|
||||
is_pseudo_constant_clause((Node *) leftop))
|
||||
{
|
||||
if (is_indexable_operator(clause, opclass, index->relam, false))
|
||||
if (is_indexable_operator(clause, opclass, false))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If we didn't find a member of the index's opclass, see
|
||||
* whether it is a "special" indexable operator.
|
||||
*/
|
||||
if (match_special_index_operator(clause, opclass, index->relam,
|
||||
false))
|
||||
if (match_special_index_operator(clause, opclass, false))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -799,7 +797,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
isIndexable =
|
||||
!intMember(lfirsti(rel->relids), othervarnos) &&
|
||||
!contain_noncachable_functions((Node *) rightop) &&
|
||||
is_indexable_operator(clause, opclass, index->relam, true);
|
||||
is_indexable_operator(clause, opclass, true);
|
||||
freeList(othervarnos);
|
||||
return isIndexable;
|
||||
}
|
||||
@@ -811,7 +809,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
isIndexable =
|
||||
!intMember(lfirsti(rel->relids), othervarnos) &&
|
||||
!contain_noncachable_functions((Node *) leftop) &&
|
||||
is_indexable_operator(clause, opclass, index->relam, false);
|
||||
is_indexable_operator(clause, opclass, false);
|
||||
freeList(othervarnos);
|
||||
return isIndexable;
|
||||
}
|
||||
@@ -822,12 +820,11 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
|
||||
/*
|
||||
* indexable_operator
|
||||
* Does a binary opclause contain an operator matching the index's
|
||||
* access method?
|
||||
* Does a binary opclause contain an operator matching the index opclass?
|
||||
*
|
||||
* If the indexkey is on the right, what we actually want to know
|
||||
* is whether the operator has a commutator operator that matches
|
||||
* the index's access method.
|
||||
* the index's opclass.
|
||||
*
|
||||
* We try both the straightforward match and matches that rely on
|
||||
* recognizing binary-compatible datatypes. For example, if we have
|
||||
@@ -844,8 +841,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
* OID is *not* commuted; it can be plugged directly into the given clause.
|
||||
*/
|
||||
Oid
|
||||
indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
bool indexkey_on_left)
|
||||
indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left)
|
||||
{
|
||||
Oid expr_op = ((Oper *) clause->oper)->opno;
|
||||
Oid commuted_op,
|
||||
@@ -865,8 +861,8 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
if (commuted_op == InvalidOid)
|
||||
return InvalidOid;
|
||||
|
||||
/* Done if the (commuted) operator is a member of the index's AM */
|
||||
if (op_class(commuted_op, opclass, relam))
|
||||
/* Done if the (commuted) operator is a member of the index's opclass */
|
||||
if (op_in_opclass(commuted_op, opclass))
|
||||
return expr_op;
|
||||
|
||||
/*
|
||||
@@ -937,7 +933,7 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
if (commuted_op == InvalidOid)
|
||||
return InvalidOid;
|
||||
|
||||
if (op_class(commuted_op, opclass, relam))
|
||||
if (op_in_opclass(commuted_op, opclass))
|
||||
return new_op;
|
||||
}
|
||||
}
|
||||
@@ -1171,8 +1167,8 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
Oid pred_op,
|
||||
clause_op,
|
||||
test_op;
|
||||
Oid opclass_id;
|
||||
StrategyNumber pred_strategy,
|
||||
Oid opclass_id = InvalidOid;
|
||||
StrategyNumber pred_strategy = 0,
|
||||
clause_strategy,
|
||||
test_strategy;
|
||||
Oper *test_oper;
|
||||
@@ -1182,7 +1178,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
ScanKeyData entry[3];
|
||||
ScanKeyData entry[1];
|
||||
Form_pg_amop aform;
|
||||
ExprContext *econtext;
|
||||
|
||||
@@ -1227,23 +1223,6 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
/*
|
||||
* 1. Find a "btree" strategy number for the pred_op
|
||||
*
|
||||
* XXX consider using syscache lookups for these searches. Right
|
||||
* now we don't have caches that match all of the search conditions,
|
||||
* but reconsider it after upcoming restructuring of pg_opclass.
|
||||
*/
|
||||
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
||||
|
||||
ScanKeyEntryInitialize(&entry[0], 0,
|
||||
Anum_pg_amop_amopid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(BTREE_AM_OID));
|
||||
|
||||
ScanKeyEntryInitialize(&entry[1], 0,
|
||||
Anum_pg_amop_amopopr,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(pred_op));
|
||||
|
||||
/*
|
||||
* The following assumes that any given operator will only be in a
|
||||
* single btree operator class. This is true at least for all the
|
||||
* pre-defined operator classes. If it isn't true, then whichever
|
||||
@@ -1251,46 +1230,47 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
* will be used to find the associated strategy numbers for the test.
|
||||
* --Nels, Jan '93
|
||||
*/
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
|
||||
tuple = heap_getnext(scan, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ScanKeyEntryInitialize(&entry[0], 0x0,
|
||||
Anum_pg_amop_amopopr,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(pred_op));
|
||||
|
||||
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 1, entry);
|
||||
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
/* predicate operator isn't btree-indexable */
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
return false;
|
||||
aform = (Form_pg_amop) GETSTRUCT(tuple);
|
||||
if (opclass_is_btree(aform->amopclaid))
|
||||
{
|
||||
/* Get the predicate operator's btree strategy number (1 to 5) */
|
||||
pred_strategy = (StrategyNumber) aform->amopstrategy;
|
||||
Assert(pred_strategy >= 1 && pred_strategy <= 5);
|
||||
/* Remember which operator class this strategy number came from */
|
||||
opclass_id = aform->amopclaid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aform = (Form_pg_amop) GETSTRUCT(tuple);
|
||||
|
||||
/* Get the predicate operator's btree strategy number (1 to 5) */
|
||||
pred_strategy = (StrategyNumber) aform->amopstrategy;
|
||||
Assert(pred_strategy >= 1 && pred_strategy <= 5);
|
||||
|
||||
/* Remember which operator class this strategy number came from */
|
||||
opclass_id = aform->amopclaid;
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
|
||||
if (!OidIsValid(opclass_id))
|
||||
{
|
||||
/* predicate operator isn't btree-indexable */
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2. From the same opclass, find a strategy num for the clause_op
|
||||
*/
|
||||
ScanKeyEntryInitialize(&entry[1], 0,
|
||||
Anum_pg_amop_amopclaid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(opclass_id));
|
||||
|
||||
ScanKeyEntryInitialize(&entry[2], 0,
|
||||
Anum_pg_amop_amopopr,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(clause_op));
|
||||
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 3, entry);
|
||||
tuple = heap_getnext(scan, 0);
|
||||
tuple = SearchSysCache(AMOPOPID,
|
||||
ObjectIdGetDatum(opclass_id),
|
||||
ObjectIdGetDatum(clause_op),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
{
|
||||
/* clause operator isn't btree-indexable, or isn't in this opclass */
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
return false;
|
||||
}
|
||||
aform = (Form_pg_amop) GETSTRUCT(tuple);
|
||||
@@ -1299,7 +1279,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
clause_strategy = (StrategyNumber) aform->amopstrategy;
|
||||
Assert(clause_strategy >= 1 && clause_strategy <= 5);
|
||||
|
||||
heap_endscan(scan);
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* 3. Look up the "test" strategy number in the implication table
|
||||
@@ -1307,26 +1287,20 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
|
||||
if (test_strategy == 0)
|
||||
{
|
||||
heap_close(relation, AccessShareLock);
|
||||
return false; /* the implication cannot be determined */
|
||||
}
|
||||
|
||||
/*
|
||||
* 4. From the same opclass, find the operator for the test strategy
|
||||
*/
|
||||
ScanKeyEntryInitialize(&entry[2], 0,
|
||||
Anum_pg_amop_amopstrategy,
|
||||
F_INT2EQ,
|
||||
Int16GetDatum(test_strategy));
|
||||
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 3, entry);
|
||||
tuple = heap_getnext(scan, 0);
|
||||
tuple = SearchSysCache(AMOPSTRATEGY,
|
||||
ObjectIdGetDatum(opclass_id),
|
||||
Int16GetDatum(test_strategy),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
{
|
||||
/* this probably shouldn't fail? */
|
||||
elog(DEBUG, "pred_test_simple_clause: unknown test_op");
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
return false;
|
||||
}
|
||||
aform = (Form_pg_amop) GETSTRUCT(tuple);
|
||||
@@ -1334,9 +1308,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
/* Get the test operator */
|
||||
test_op = aform->amopopr;
|
||||
|
||||
heap_endscan(scan);
|
||||
|
||||
heap_close(relation, AccessShareLock);
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* 5. Evaluate the test
|
||||
@@ -1681,7 +1653,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
|
||||
* Return 'true' if we can do something with it anyway.
|
||||
*/
|
||||
static bool
|
||||
match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
match_special_index_operator(Expr *clause, Oid opclass,
|
||||
bool indexkey_on_left)
|
||||
{
|
||||
bool isIndexable = false;
|
||||
@@ -1806,8 +1778,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
case OID_TEXT_ICLIKE_OP:
|
||||
case OID_TEXT_REGEXEQ_OP:
|
||||
case OID_TEXT_ICREGEXEQ_OP:
|
||||
if (!op_class(find_operator(">=", TEXTOID), opclass, relam) ||
|
||||
!op_class(find_operator("<", TEXTOID), opclass, relam))
|
||||
if (!op_in_opclass(find_operator(">=", TEXTOID), opclass) ||
|
||||
!op_in_opclass(find_operator("<", TEXTOID), opclass))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
@@ -1815,8 +1787,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
case OID_BPCHAR_ICLIKE_OP:
|
||||
case OID_BPCHAR_REGEXEQ_OP:
|
||||
case OID_BPCHAR_ICREGEXEQ_OP:
|
||||
if (!op_class(find_operator(">=", BPCHAROID), opclass, relam) ||
|
||||
!op_class(find_operator("<", BPCHAROID), opclass, relam))
|
||||
if (!op_in_opclass(find_operator(">=", BPCHAROID), opclass) ||
|
||||
!op_in_opclass(find_operator("<", BPCHAROID), opclass))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
@@ -1824,8 +1796,8 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
case OID_VARCHAR_ICLIKE_OP:
|
||||
case OID_VARCHAR_REGEXEQ_OP:
|
||||
case OID_VARCHAR_ICREGEXEQ_OP:
|
||||
if (!op_class(find_operator(">=", VARCHAROID), opclass, relam) ||
|
||||
!op_class(find_operator("<", VARCHAROID), opclass, relam))
|
||||
if (!op_in_opclass(find_operator(">=", VARCHAROID), opclass) ||
|
||||
!op_in_opclass(find_operator("<", VARCHAROID), opclass))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
@@ -1833,24 +1805,24 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
case OID_NAME_ICLIKE_OP:
|
||||
case OID_NAME_REGEXEQ_OP:
|
||||
case OID_NAME_ICREGEXEQ_OP:
|
||||
if (!op_class(find_operator(">=", NAMEOID), opclass, relam) ||
|
||||
!op_class(find_operator("<", NAMEOID), opclass, relam))
|
||||
if (!op_in_opclass(find_operator(">=", NAMEOID), opclass) ||
|
||||
!op_in_opclass(find_operator("<", NAMEOID), opclass))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
case OID_INET_SUB_OP:
|
||||
case OID_INET_SUBEQ_OP:
|
||||
/* for SUB we actually need ">" not ">=", but this should do */
|
||||
if (!op_class(find_operator(">=", INETOID), opclass, relam) ||
|
||||
!op_class(find_operator("<=", INETOID), opclass, relam))
|
||||
if (!op_in_opclass(find_operator(">=", INETOID), opclass) ||
|
||||
!op_in_opclass(find_operator("<=", INETOID), opclass))
|
||||
isIndexable = false;
|
||||
break;
|
||||
|
||||
case OID_CIDR_SUB_OP:
|
||||
case OID_CIDR_SUBEQ_OP:
|
||||
/* for SUB we actually need ">" not ">=", but this should do */
|
||||
if (!op_class(find_operator(">=", CIDROID), opclass, relam) ||
|
||||
!op_class(find_operator("<=", CIDROID), opclass, relam))
|
||||
if (!op_in_opclass(find_operator(">=", CIDROID), opclass) ||
|
||||
!op_in_opclass(find_operator("<=", CIDROID), opclass))
|
||||
isIndexable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.107 2001/06/05 05:26:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.108 2001/08/21 16:36:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -56,9 +56,12 @@ static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
|
||||
List *joinclauses, List *otherclauses,
|
||||
Plan *outer_plan, List *outer_tlist,
|
||||
Plan *inner_plan, List *inner_tlist);
|
||||
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
|
||||
static List *fix_indxqual_sublist(List *indexqual, int baserelid,
|
||||
IndexOptInfo *index);
|
||||
static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
|
||||
List **fixed_indexquals,
|
||||
List **recheck_indexquals);
|
||||
static void fix_indxqual_sublist(List *indexqual, int baserelid,
|
||||
IndexOptInfo *index,
|
||||
List **fixed_quals, List **recheck_quals);
|
||||
static Node *fix_indxqual_operand(Node *node, int baserelid,
|
||||
IndexOptInfo *index,
|
||||
Oid *opclass);
|
||||
@@ -381,11 +384,12 @@ create_indexscan_plan(Query *root,
|
||||
List *indxqual = best_path->indexqual;
|
||||
Index baserelid;
|
||||
List *qpqual;
|
||||
Expr *indxqual_or_expr = NULL;
|
||||
List *fixed_indxqual;
|
||||
List *recheck_indxqual;
|
||||
List *indexids;
|
||||
List *ixinfo;
|
||||
IndexScan *scan_plan;
|
||||
bool lossy;
|
||||
|
||||
/* there should be exactly one base rel involved... */
|
||||
Assert(length(best_path->path.parent->relids) == 1);
|
||||
@@ -394,25 +398,23 @@ create_indexscan_plan(Query *root,
|
||||
baserelid = lfirsti(best_path->path.parent->relids);
|
||||
|
||||
/*
|
||||
* Build list of index OIDs, and check to see if any of the indices
|
||||
* are lossy.
|
||||
* Build list of index OIDs.
|
||||
*/
|
||||
indexids = NIL;
|
||||
lossy = false;
|
||||
foreach(ixinfo, best_path->indexinfo)
|
||||
{
|
||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
|
||||
|
||||
indexids = lappendi(indexids, index->indexoid);
|
||||
lossy |= index->lossy;
|
||||
}
|
||||
|
||||
/*
|
||||
* The qpqual list must contain all restrictions not automatically
|
||||
* handled by the index. Note that for non-lossy indices, the
|
||||
* predicates in the indxqual are checked fully by the index, while
|
||||
* for lossy indices the indxqual predicates need to be double-checked
|
||||
* after the index fetches the best-guess tuples.
|
||||
* handled by the index. Normally the predicates in the indxqual
|
||||
* are checked fully by the index, but if the index is "lossy" for
|
||||
* a particular operator (as signaled by the amopreqcheck flag in
|
||||
* pg_amop), then we need to double-check that predicate in qpqual,
|
||||
* because the index may return more tuples than match the predicate.
|
||||
*
|
||||
* Since the indexquals were generated from the restriction clauses given
|
||||
* by scan_clauses, there will normally be some duplications between
|
||||
@@ -420,7 +422,6 @@ create_indexscan_plan(Query *root,
|
||||
*/
|
||||
if (length(indxqual) > 1)
|
||||
{
|
||||
|
||||
/*
|
||||
* Build an expression representation of the indexqual, expanding
|
||||
* the implicit OR and AND semantics of the first- and
|
||||
@@ -428,32 +429,24 @@ create_indexscan_plan(Query *root,
|
||||
*/
|
||||
List *orclauses = NIL;
|
||||
List *orclause;
|
||||
Expr *indxqual_expr;
|
||||
|
||||
foreach(orclause, indxqual)
|
||||
{
|
||||
orclauses = lappend(orclauses,
|
||||
make_ands_explicit(lfirst(orclause)));
|
||||
indxqual_expr = make_orclause(orclauses);
|
||||
}
|
||||
indxqual_or_expr = make_orclause(orclauses);
|
||||
|
||||
qpqual = set_difference(scan_clauses, makeList1(indxqual_expr));
|
||||
|
||||
if (lossy)
|
||||
qpqual = lappend(qpqual, copyObject(indxqual_expr));
|
||||
qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr));
|
||||
}
|
||||
else if (indxqual != NIL)
|
||||
{
|
||||
|
||||
/*
|
||||
* Here, we can simply treat the first sublist as an independent
|
||||
* set of qual expressions, since there is no top-level OR
|
||||
* behavior.
|
||||
*/
|
||||
List *indxqual_list = lfirst(indxqual);
|
||||
|
||||
qpqual = set_difference(scan_clauses, indxqual_list);
|
||||
|
||||
if (lossy)
|
||||
qpqual = nconc(qpqual, (List *) copyObject(indxqual_list));
|
||||
qpqual = set_difference(scan_clauses, lfirst(indxqual));
|
||||
}
|
||||
else
|
||||
qpqual = scan_clauses;
|
||||
@@ -461,9 +454,35 @@ create_indexscan_plan(Query *root,
|
||||
/*
|
||||
* The executor needs a copy with the indexkey on the left of each
|
||||
* clause and with index attr numbers substituted for table ones.
|
||||
* This pass also looks for "lossy" operators.
|
||||
*/
|
||||
fixed_indxqual = fix_indxqual_references(indxqual, best_path);
|
||||
fix_indxqual_references(indxqual, best_path,
|
||||
&fixed_indxqual, &recheck_indxqual);
|
||||
|
||||
/*
|
||||
* If there were any "lossy" operators, need to add back the appropriate
|
||||
* qual clauses to the qpqual. When there is just one indexscan being
|
||||
* performed (ie, we have simple AND semantics), we can just add the
|
||||
* lossy clauses themselves to qpqual. If we have OR-of-ANDs, we'd
|
||||
* better add the entire original indexqual to make sure that the
|
||||
* semantics are correct.
|
||||
*/
|
||||
if (recheck_indxqual != NIL)
|
||||
{
|
||||
if (indxqual_or_expr)
|
||||
{
|
||||
/* Better do a deep copy of the original scanclauses */
|
||||
qpqual = lappend(qpqual, copyObject(indxqual_or_expr));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Subroutine already copied quals, so just append to list */
|
||||
Assert(length(recheck_indxqual) == 1);
|
||||
qpqual = nconc(qpqual, (List *) lfirst(recheck_indxqual));
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally ready to build the plan node */
|
||||
scan_plan = make_indexscan(tlist,
|
||||
qpqual,
|
||||
baserelid,
|
||||
@@ -868,9 +887,9 @@ create_hashjoin_plan(HashPath *best_path,
|
||||
/*
|
||||
* fix_indxqual_references
|
||||
* Adjust indexqual clauses to the form the executor's indexqual
|
||||
* machinery needs.
|
||||
* machinery needs, and check for recheckable (lossy) index conditions.
|
||||
*
|
||||
* We have three tasks here:
|
||||
* We have four tasks here:
|
||||
* * Index keys must be represented by Var nodes with varattno set to the
|
||||
* index's attribute number, not the attribute number in the original rel.
|
||||
* * indxpath.c may have selected an index that is binary-compatible with
|
||||
@@ -879,20 +898,34 @@ create_hashjoin_plan(HashPath *best_path,
|
||||
* equivalent operator that the index will recognize.
|
||||
* * If the index key is on the right, commute the clause to put it on the
|
||||
* left. (Someday the executor might not need this, but for now it does.)
|
||||
* * If the indexable operator is marked 'amopreqcheck' in pg_amop, then
|
||||
* the index is "lossy" for this operator: it may return more tuples than
|
||||
* actually satisfy the operator condition. For each such operator, we
|
||||
* must add (the original form of) the indexqual clause to the "qpquals"
|
||||
* of the indexscan node, where the operator will be re-evaluated to
|
||||
* ensure it passes.
|
||||
*
|
||||
* This code used to be entirely bogus for multi-index scans. Now it keeps
|
||||
* track of which index applies to each subgroup of index qual clauses...
|
||||
*
|
||||
* Returns a modified copy of the indexqual list --- the original is not
|
||||
* changed. Note also that the copy shares no substructure with the
|
||||
* original; this is needed in case there is a subplan in it (we need
|
||||
* Both the input list and the output lists have the form of lists of sublists
|
||||
* of qual clauses --- the top-level list has one entry for each indexscan
|
||||
* to be performed. The semantics are OR-of-ANDs.
|
||||
*
|
||||
* fixed_indexquals receives a modified copy of the indexqual list --- the
|
||||
* original is not changed. Note also that the copy shares no substructure
|
||||
* with the original; this is needed in case there is a subplan in it (we need
|
||||
* two separate copies of the subplan tree, or things will go awry).
|
||||
*
|
||||
* recheck_indexquals similarly receives a full copy of whichever clauses
|
||||
* need rechecking.
|
||||
*/
|
||||
|
||||
static List *
|
||||
fix_indxqual_references(List *indexquals, IndexPath *index_path)
|
||||
static void
|
||||
fix_indxqual_references(List *indexquals, IndexPath *index_path,
|
||||
List **fixed_indexquals, List **recheck_indexquals)
|
||||
{
|
||||
List *fixed_quals = NIL;
|
||||
List *recheck_quals = NIL;
|
||||
int baserelid = lfirsti(index_path->path.parent->relids);
|
||||
List *ixinfo = index_path->indexinfo;
|
||||
List *i;
|
||||
@@ -901,14 +934,20 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path)
|
||||
{
|
||||
List *indexqual = lfirst(i);
|
||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
|
||||
List *fixed_qual;
|
||||
List *recheck_qual;
|
||||
|
||||
fix_indxqual_sublist(indexqual, baserelid, index,
|
||||
&fixed_qual, &recheck_qual);
|
||||
fixed_quals = lappend(fixed_quals, fixed_qual);
|
||||
if (recheck_qual != NIL)
|
||||
recheck_quals = lappend(recheck_quals, recheck_qual);
|
||||
|
||||
fixed_quals = lappend(fixed_quals,
|
||||
fix_indxqual_sublist(indexqual,
|
||||
baserelid,
|
||||
index));
|
||||
ixinfo = lnext(ixinfo);
|
||||
}
|
||||
return fixed_quals;
|
||||
|
||||
*fixed_indexquals = fixed_quals;
|
||||
*recheck_indexquals = recheck_quals;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -916,12 +955,19 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path)
|
||||
*
|
||||
* For each qual clause, commute if needed to put the indexkey operand on the
|
||||
* left, and then fix its varattno. (We do not need to change the other side
|
||||
* of the clause.) Also change the operator if necessary.
|
||||
* of the clause.) Also change the operator if necessary, and check for
|
||||
* lossy index behavior.
|
||||
*
|
||||
* Returns two lists: the list of fixed indexquals, and the list (usually
|
||||
* empty) of original clauses that must be rechecked as qpquals because
|
||||
* the index is lossy for this operator type.
|
||||
*/
|
||||
static List *
|
||||
fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index)
|
||||
static void
|
||||
fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index,
|
||||
List **fixed_quals, List **recheck_quals)
|
||||
{
|
||||
List *fixed_qual = NIL;
|
||||
List *recheck_qual = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, indexqual)
|
||||
@@ -968,14 +1014,24 @@ fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index)
|
||||
* is merely binary-compatible with the index. This shouldn't
|
||||
* fail, since indxpath.c found it before...
|
||||
*/
|
||||
newopno = indexable_operator(newclause, opclass, index->relam, true);
|
||||
newopno = indexable_operator(newclause, opclass, true);
|
||||
if (newopno == InvalidOid)
|
||||
elog(ERROR, "fix_indxqual_sublist: failed to find substitute op");
|
||||
((Oper *) newclause->oper)->opno = newopno;
|
||||
|
||||
fixed_qual = lappend(fixed_qual, newclause);
|
||||
|
||||
/*
|
||||
* Finally, check to see if index is lossy for this operator.
|
||||
* If so, add (a copy of) original form of clause to recheck list.
|
||||
*/
|
||||
if (op_requires_recheck(newopno, opclass))
|
||||
recheck_qual = lappend(recheck_qual,
|
||||
copyObject((Node *) clause));
|
||||
}
|
||||
return fixed_qual;
|
||||
|
||||
*fixed_quals = fixed_qual;
|
||||
*recheck_quals = recheck_qual;
|
||||
}
|
||||
|
||||
static Node *
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.67 2001/07/15 22:48:18 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.68 2001/08/21 16:36:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -103,8 +103,7 @@ find_secondary_indexes(Oid relationObjectId)
|
||||
IndexOptInfo *info;
|
||||
int i;
|
||||
Relation indexRelation;
|
||||
Oid relam;
|
||||
uint16 amorderstrategy;
|
||||
int16 amorderstrategy;
|
||||
|
||||
indexTuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(indexoid),
|
||||
@@ -138,7 +137,6 @@ find_secondary_indexes(Oid relationObjectId)
|
||||
else
|
||||
info->indpred = NIL;
|
||||
info->unique = index->indisunique;
|
||||
info->lossy = index->indislossy;
|
||||
|
||||
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
||||
{
|
||||
@@ -160,8 +158,7 @@ find_secondary_indexes(Oid relationObjectId)
|
||||
|
||||
/* Extract info from the relation descriptor for the index */
|
||||
indexRelation = index_open(index->indexrelid);
|
||||
relam = indexRelation->rd_rel->relam;
|
||||
info->relam = relam;
|
||||
info->relam = indexRelation->rd_rel->relam;
|
||||
info->pages = indexRelation->rd_rel->relpages;
|
||||
info->tuples = indexRelation->rd_rel->reltuples;
|
||||
info->amcostestimate = index_cost_estimator(indexRelation);
|
||||
@@ -181,14 +178,12 @@ find_secondary_indexes(Oid relationObjectId)
|
||||
|
||||
amopTuple =
|
||||
SearchSysCache(AMOPSTRATEGY,
|
||||
ObjectIdGetDatum(relam),
|
||||
ObjectIdGetDatum(index->indclass[i]),
|
||||
UInt16GetDatum(amorderstrategy),
|
||||
0);
|
||||
Int16GetDatum(amorderstrategy),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(amopTuple))
|
||||
elog(ERROR, "find_secondary_indexes: no amop %u %u %d",
|
||||
relam, index->indclass[i],
|
||||
(int) amorderstrategy);
|
||||
elog(ERROR, "find_secondary_indexes: no amop %u %d",
|
||||
index->indclass[i], (int) amorderstrategy);
|
||||
amop = (Form_pg_amop) GETSTRUCT(amopTuple);
|
||||
info->ordering[i] = amop->amopopr;
|
||||
ReleaseSysCache(amopTuple);
|
||||
@@ -370,7 +365,7 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
|
||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
||||
|
||||
/*
|
||||
* Note: ignore functional, partial, or lossy indexes, since they
|
||||
* Note: ignore functional and partial indexes, since they
|
||||
* don't allow us to conclude that all attr values are distinct.
|
||||
* Also, a multicolumn unique index doesn't allow us to conclude
|
||||
* that just the specified attr is unique.
|
||||
@@ -379,8 +374,7 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
|
||||
index->nkeys == 1 &&
|
||||
index->indexkeys[0] == attno &&
|
||||
index->indproc == InvalidOid &&
|
||||
index->indpred == NIL &&
|
||||
!index->lossy)
|
||||
index->indpred == NIL)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.195 2001/08/16 20:38:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.196 2001/08/21 16:36:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -995,7 +995,6 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
||||
index->relname = stmt->relname;
|
||||
index->accessMethod = "btree";
|
||||
index->indexParams = NIL;
|
||||
index->withClause = NIL;
|
||||
index->whereClause = NULL;
|
||||
|
||||
foreach(keys, constraint->keys)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.246 2001/08/16 20:38:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.247 2001/08/21 16:36:03 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -2399,13 +2399,12 @@ RevokeStmt: REVOKE privileges ON opt_table relation_name_list FROM grantee_list
|
||||
* QUERY:
|
||||
* create index <indexname> on <relname>
|
||||
* [ using <access> ] "(" (<col> with <op>)+ ")"
|
||||
* [ with <parameters> ]
|
||||
* [ where <predicate> ]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
IndexStmt: CREATE index_opt_unique INDEX index_name ON relation_name
|
||||
access_method_clause '(' index_params ')' opt_with where_clause
|
||||
access_method_clause '(' index_params ')' where_clause
|
||||
{
|
||||
IndexStmt *n = makeNode(IndexStmt);
|
||||
n->unique = $2;
|
||||
@@ -2413,8 +2412,7 @@ IndexStmt: CREATE index_opt_unique INDEX index_name ON relation_name
|
||||
n->relname = $6;
|
||||
n->accessMethod = $7;
|
||||
n->indexParams = $9;
|
||||
n->withClause = $11;
|
||||
n->whereClause = $12;
|
||||
n->whereClause = $11;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.115 2001/07/16 05:06:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.116 2001/08/21 16:36:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -538,7 +538,6 @@ ProcessUtility(Node *parsetree,
|
||||
stmt->idxname, /* index name */
|
||||
stmt->accessMethod, /* am name */
|
||||
stmt->indexParams, /* parameters */
|
||||
stmt->withClause,
|
||||
stmt->unique,
|
||||
stmt->primary,
|
||||
(Expr *) stmt->whereClause,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.62 2001/06/22 19:16:23 wieck Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.63 2001/08/21 16:36:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -19,12 +19,12 @@
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* USER I/O ROUTINES *
|
||||
*****************************************************************************/
|
||||
@@ -32,126 +32,103 @@
|
||||
/*
|
||||
* regprocin - converts "proname" or "proid" to proid
|
||||
*
|
||||
* We need to accept an OID for cases where the name is ambiguous.
|
||||
*
|
||||
* proid of '-' signifies unknown, for consistency with regprocout
|
||||
*/
|
||||
Datum
|
||||
regprocin(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *pro_name_or_oid = PG_GETARG_CSTRING(0);
|
||||
HeapTuple proctup;
|
||||
HeapTupleData tuple;
|
||||
RegProcedure result = InvalidOid;
|
||||
int matches = 0;
|
||||
ScanKeyData skey[1];
|
||||
|
||||
if (pro_name_or_oid[0] == '-' && pro_name_or_oid[1] == '\0')
|
||||
PG_RETURN_OID(InvalidOid);
|
||||
|
||||
if (!IsIgnoringSystemIndexes())
|
||||
if (pro_name_or_oid[0] >= '0' &&
|
||||
pro_name_or_oid[0] <= '9')
|
||||
{
|
||||
Oid searchOid;
|
||||
|
||||
/*
|
||||
* we need to use the oid because there can be multiple entries
|
||||
* with the same name. We accept int4eq_1323 and 1323.
|
||||
*/
|
||||
if (pro_name_or_oid[0] >= '0' &&
|
||||
pro_name_or_oid[0] <= '9')
|
||||
searchOid = DatumGetObjectId(DirectFunctionCall1(oidin,
|
||||
CStringGetDatum(pro_name_or_oid)));
|
||||
result = (RegProcedure) GetSysCacheOid(PROCOID,
|
||||
ObjectIdGetDatum(searchOid),
|
||||
0, 0, 0);
|
||||
if (!RegProcedureIsValid(result))
|
||||
elog(ERROR, "No procedure with oid %s", pro_name_or_oid);
|
||||
matches = 1;
|
||||
}
|
||||
else if (!IsIgnoringSystemIndexes())
|
||||
{
|
||||
Relation hdesc;
|
||||
Relation idesc;
|
||||
IndexScanDesc sd;
|
||||
RetrieveIndexResult indexRes;
|
||||
HeapTupleData tuple;
|
||||
Buffer buffer;
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
(AttrNumber) 1,
|
||||
(RegProcedure) F_NAMEEQ,
|
||||
CStringGetDatum(pro_name_or_oid));
|
||||
|
||||
hdesc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
idesc = index_openr(ProcedureNameIndex);
|
||||
sd = index_beginscan(idesc, false, 1, skey);
|
||||
|
||||
while ((indexRes = index_getnext(sd, ForwardScanDirection)))
|
||||
{
|
||||
result = (RegProcedure)
|
||||
GetSysCacheOid(PROCOID,
|
||||
DirectFunctionCall1(oidin,
|
||||
CStringGetDatum(pro_name_or_oid)),
|
||||
0, 0, 0);
|
||||
if (!RegProcedureIsValid(result))
|
||||
elog(ERROR, "No procedure with oid %s", pro_name_or_oid);
|
||||
}
|
||||
else
|
||||
{
|
||||
Relation hdesc;
|
||||
Relation idesc;
|
||||
IndexScanDesc sd;
|
||||
ScanKeyData skey[1];
|
||||
RetrieveIndexResult indexRes;
|
||||
Buffer buffer;
|
||||
int matches = 0;
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 1,
|
||||
(RegProcedure) F_NAMEEQ,
|
||||
CStringGetDatum(pro_name_or_oid));
|
||||
|
||||
hdesc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
idesc = index_openr(ProcedureNameIndex);
|
||||
|
||||
sd = index_beginscan(idesc, false, 1, skey);
|
||||
while ((indexRes = index_getnext(sd, ForwardScanDirection)))
|
||||
tuple.t_datamcxt = NULL;
|
||||
tuple.t_data = NULL;
|
||||
tuple.t_self = indexRes->heap_iptr;
|
||||
heap_fetch(hdesc, SnapshotNow, &tuple, &buffer, sd);
|
||||
pfree(indexRes);
|
||||
if (tuple.t_data != NULL)
|
||||
{
|
||||
tuple.t_datamcxt = NULL;
|
||||
tuple.t_data = NULL;
|
||||
tuple.t_self = indexRes->heap_iptr;
|
||||
heap_fetch(hdesc, SnapshotNow,
|
||||
&tuple,
|
||||
&buffer,
|
||||
sd);
|
||||
pfree(indexRes);
|
||||
if (tuple.t_data != NULL)
|
||||
{
|
||||
result = (RegProcedure) tuple.t_data->t_oid;
|
||||
ReleaseBuffer(buffer);
|
||||
|
||||
if (++matches > 1)
|
||||
break;
|
||||
}
|
||||
result = (RegProcedure) tuple.t_data->t_oid;
|
||||
ReleaseBuffer(buffer);
|
||||
if (++matches > 1)
|
||||
break;
|
||||
}
|
||||
|
||||
index_endscan(sd);
|
||||
index_close(idesc);
|
||||
heap_close(hdesc, AccessShareLock);
|
||||
|
||||
if (matches > 1)
|
||||
elog(ERROR, "There is more than one procedure named %s.\n\tSupply the pg_proc oid inside single quotes.", pro_name_or_oid);
|
||||
else if (matches == 0)
|
||||
elog(ERROR, "No procedure with name %s", pro_name_or_oid);
|
||||
}
|
||||
|
||||
index_endscan(sd);
|
||||
index_close(idesc);
|
||||
heap_close(hdesc, AccessShareLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
Relation proc;
|
||||
HeapScanDesc procscan;
|
||||
ScanKeyData key;
|
||||
bool isnull;
|
||||
HeapTuple proctup;
|
||||
|
||||
proc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
ScanKeyEntryInitialize(&key,
|
||||
(bits16) 0,
|
||||
(AttrNumber) 1,
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
(AttrNumber) Anum_pg_proc_proname,
|
||||
(RegProcedure) F_NAMEEQ,
|
||||
CStringGetDatum(pro_name_or_oid));
|
||||
|
||||
procscan = heap_beginscan(proc, 0, SnapshotNow, 1, &key);
|
||||
if (!HeapScanIsValid(procscan))
|
||||
proc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
procscan = heap_beginscan(proc, 0, SnapshotNow, 1, skey);
|
||||
|
||||
while (HeapTupleIsValid(proctup = heap_getnext(procscan, 0)))
|
||||
{
|
||||
heap_close(proc, AccessShareLock);
|
||||
elog(ERROR, "regprocin: could not begin scan of %s",
|
||||
ProcedureRelationName);
|
||||
PG_RETURN_OID(InvalidOid);
|
||||
result = proctup->t_data->t_oid;
|
||||
if (++matches > 1)
|
||||
break;
|
||||
}
|
||||
proctup = heap_getnext(procscan, 0);
|
||||
if (HeapTupleIsValid(proctup))
|
||||
{
|
||||
result = (RegProcedure) heap_getattr(proctup,
|
||||
ObjectIdAttributeNumber,
|
||||
RelationGetDescr(proc),
|
||||
&isnull);
|
||||
if (isnull)
|
||||
elog(ERROR, "regprocin: null procedure %s", pro_name_or_oid);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "No procedure with name %s", pro_name_or_oid);
|
||||
|
||||
heap_endscan(procscan);
|
||||
heap_close(proc, AccessShareLock);
|
||||
}
|
||||
|
||||
if (matches > 1)
|
||||
elog(ERROR, "There is more than one procedure named %s.\n\tSupply the pg_proc oid inside single quotes.", pro_name_or_oid);
|
||||
else if (matches == 0)
|
||||
elog(ERROR, "No procedure with name %s", pro_name_or_oid);
|
||||
|
||||
PG_RETURN_OID(result);
|
||||
}
|
||||
|
||||
@@ -174,66 +151,22 @@ regprocout(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_CSTRING(result);
|
||||
}
|
||||
|
||||
if (!IsBootstrapProcessingMode())
|
||||
proctup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(proid),
|
||||
0, 0, 0);
|
||||
|
||||
if (HeapTupleIsValid(proctup))
|
||||
{
|
||||
proctup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(proid),
|
||||
0, 0, 0);
|
||||
char *s;
|
||||
|
||||
if (HeapTupleIsValid(proctup))
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname);
|
||||
StrNCpy(result, s, NAMEDATALEN);
|
||||
ReleaseSysCache(proctup);
|
||||
}
|
||||
else
|
||||
{
|
||||
result[0] = '-';
|
||||
result[1] = '\0';
|
||||
}
|
||||
s = NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname);
|
||||
StrNCpy(result, s, NAMEDATALEN);
|
||||
ReleaseSysCache(proctup);
|
||||
}
|
||||
else
|
||||
{
|
||||
Relation proc;
|
||||
HeapScanDesc procscan;
|
||||
ScanKeyData key;
|
||||
|
||||
proc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
ScanKeyEntryInitialize(&key,
|
||||
(bits16) 0,
|
||||
(AttrNumber) ObjectIdAttributeNumber,
|
||||
(RegProcedure) F_INT4EQ,
|
||||
ObjectIdGetDatum(proid));
|
||||
|
||||
procscan = heap_beginscan(proc, 0, SnapshotNow, 1, &key);
|
||||
if (!HeapScanIsValid(procscan))
|
||||
{
|
||||
heap_close(proc, AccessShareLock);
|
||||
elog(ERROR, "regprocout: could not begin scan of %s",
|
||||
ProcedureRelationName);
|
||||
}
|
||||
proctup = heap_getnext(procscan, 0);
|
||||
if (HeapTupleIsValid(proctup))
|
||||
{
|
||||
char *s;
|
||||
bool isnull;
|
||||
|
||||
s = (char *) heap_getattr(proctup, 1,
|
||||
RelationGetDescr(proc), &isnull);
|
||||
if (!isnull)
|
||||
StrNCpy(result, s, NAMEDATALEN);
|
||||
else
|
||||
elog(ERROR, "regprocout: null procedure %u", proid);
|
||||
}
|
||||
else
|
||||
{
|
||||
result[0] = '-';
|
||||
result[1] = '\0';
|
||||
}
|
||||
heap_endscan(procscan);
|
||||
heap_close(proc, AccessShareLock);
|
||||
result[0] = '-';
|
||||
result[1] = '\0';
|
||||
}
|
||||
|
||||
PG_RETURN_CSTRING(result);
|
||||
|
||||
17
src/backend/utils/cache/catcache.c
vendored
17
src/backend/utils/cache/catcache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.81 2001/06/22 19:16:23 wieck Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.82 2001/08/21 16:36:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "access/hash.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/valid.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "catalog/catname.h"
|
||||
@@ -812,7 +813,7 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
|
||||
sd = heap_beginscan(rel, false, SnapshotNow, 1, &key);
|
||||
ntp = heap_getnext(sd, 0);
|
||||
if (!HeapTupleIsValid(ntp))
|
||||
elog(ERROR, "SearchSelfReferences: %s not found in %s",
|
||||
elog(ERROR, "IndexScanOK: %s not found in %s",
|
||||
IndexRelidIndex, RelationRelationName);
|
||||
indexSelfOid = ntp->t_data->t_oid;
|
||||
heap_endscan(sd);
|
||||
@@ -823,6 +824,16 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
|
||||
if (DatumGetObjectId(cur_skey[0].sk_argument) == indexSelfOid)
|
||||
return false;
|
||||
}
|
||||
else if (cache->id == AMOPSTRATEGY ||
|
||||
cache->id == AMPROCNUM)
|
||||
{
|
||||
/* Looking for an OID or INT2 btree operator or function? */
|
||||
Oid lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument);
|
||||
|
||||
if (lookup_oid == OID_BTREE_OPS_OID ||
|
||||
lookup_oid == INT2_BTREE_OPS_OID)
|
||||
return false;
|
||||
}
|
||||
else if (cache->id == OPEROID)
|
||||
{
|
||||
/* Looking for an OID comparison function? */
|
||||
@@ -858,7 +869,7 @@ SearchCatCache(CatCache *cache,
|
||||
MemoryContext oldcxt;
|
||||
|
||||
/*
|
||||
* one-time startup overhead
|
||||
* one-time startup overhead for each cache
|
||||
*/
|
||||
if (cache->cc_tupdesc == NULL)
|
||||
CatalogCacheInitializeCache(cache);
|
||||
|
||||
72
src/backend/utils/cache/lsyscache.c
vendored
72
src/backend/utils/cache/lsyscache.c
vendored
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.56 2001/06/14 01:09:22 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.57 2001/08/21 16:36:05 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/tupmacs.h"
|
||||
#include "catalog/pg_amop.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_shadow.h"
|
||||
@@ -30,19 +32,48 @@
|
||||
/* ---------- AMOP CACHES ---------- */
|
||||
|
||||
/*
|
||||
* op_class
|
||||
* op_in_opclass
|
||||
*
|
||||
* Return t iff operator 'opno' is in operator class 'opclass' for
|
||||
* access method 'amopid'.
|
||||
* Return t iff operator 'opno' is in operator class 'opclass'.
|
||||
*/
|
||||
bool
|
||||
op_class(Oid opno, Oid opclass, Oid amopid)
|
||||
op_in_opclass(Oid opno, Oid opclass)
|
||||
{
|
||||
return SearchSysCacheExists(AMOPOPID,
|
||||
ObjectIdGetDatum(opclass),
|
||||
ObjectIdGetDatum(opno),
|
||||
ObjectIdGetDatum(amopid),
|
||||
0);
|
||||
0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* op_requires_recheck
|
||||
*
|
||||
* Return t if operator 'opno' requires a recheck when used as a
|
||||
* member of opclass 'opclass' (ie, this opclass is lossy for this
|
||||
* operator).
|
||||
*
|
||||
* Caller should already have verified that opno is a member of opclass,
|
||||
* therefore we raise an error if the tuple is not found.
|
||||
*/
|
||||
bool
|
||||
op_requires_recheck(Oid opno, Oid opclass)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_amop amop_tup;
|
||||
bool result;
|
||||
|
||||
tp = SearchSysCache(AMOPOPID,
|
||||
ObjectIdGetDatum(opclass),
|
||||
ObjectIdGetDatum(opno),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "op_requires_recheck: op %u is not a member of opclass %u",
|
||||
opno, opclass);
|
||||
amop_tup = (Form_pg_amop) GETSTRUCT(tp);
|
||||
|
||||
result = amop_tup->amopreqcheck;
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ---------- ATTRIBUTE CACHES ---------- */
|
||||
@@ -222,6 +253,33 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
|
||||
/* watch this space...
|
||||
*/
|
||||
|
||||
/* ---------- OPCLASS CACHE ---------- */
|
||||
|
||||
/*
|
||||
* opclass_is_btree
|
||||
*
|
||||
* Returns TRUE iff the specified opclass is associated with the
|
||||
* btree index access method.
|
||||
*/
|
||||
bool
|
||||
opclass_is_btree(Oid opclass)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_opclass cla_tup;
|
||||
bool result;
|
||||
|
||||
tp = SearchSysCache(CLAOID,
|
||||
ObjectIdGetDatum(opclass),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "cache lookup failed for opclass %u", opclass);
|
||||
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
|
||||
|
||||
result = (cla_tup->opcamid == BTREE_AM_OID);
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ---------- OPERATOR CACHE ---------- */
|
||||
|
||||
/*
|
||||
|
||||
39
src/backend/utils/cache/syscache.c
vendored
39
src/backend/utils/cache/syscache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.64 2001/08/10 18:57:37 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.65 2001/08/21 16:36:05 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* These routines allow the parser/planner/executor to perform
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/pg_amop.h"
|
||||
#include "catalog/pg_amproc.h"
|
||||
#include "catalog/pg_group.h"
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_inherits.h"
|
||||
@@ -113,23 +114,33 @@ static struct cachedesc cacheinfo[] = {
|
||||
0
|
||||
}},
|
||||
{AccessMethodOperatorRelationName, /* AMOPOPID */
|
||||
AccessMethodOpidIndex,
|
||||
AccessMethodOperatorIndex,
|
||||
0,
|
||||
3,
|
||||
2,
|
||||
{
|
||||
Anum_pg_amop_amopclaid,
|
||||
Anum_pg_amop_amopopr,
|
||||
Anum_pg_amop_amopid,
|
||||
0,
|
||||
0
|
||||
}},
|
||||
{AccessMethodOperatorRelationName, /* AMOPSTRATEGY */
|
||||
AccessMethodStrategyIndex,
|
||||
0,
|
||||
3,
|
||||
2,
|
||||
{
|
||||
Anum_pg_amop_amopid,
|
||||
Anum_pg_amop_amopclaid,
|
||||
Anum_pg_amop_amopstrategy,
|
||||
0,
|
||||
0
|
||||
}},
|
||||
{AccessMethodProcedureRelationName, /* AMPROCNUM */
|
||||
AccessMethodProcedureIndex,
|
||||
0,
|
||||
2,
|
||||
{
|
||||
Anum_pg_amproc_amopclaid,
|
||||
Anum_pg_amproc_amprocnum,
|
||||
0,
|
||||
0
|
||||
}},
|
||||
{AttributeRelationName, /* ATTNAME */
|
||||
@@ -152,22 +163,22 @@ static struct cachedesc cacheinfo[] = {
|
||||
0,
|
||||
0
|
||||
}},
|
||||
{OperatorClassRelationName, /* CLADEFTYPE */
|
||||
OpclassDeftypeIndex,
|
||||
{OperatorClassRelationName, /* CLAAMNAME */
|
||||
OpclassAmNameIndex,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
{
|
||||
Anum_pg_opclass_opcdeftype,
|
||||
0,
|
||||
Anum_pg_opclass_opcamid,
|
||||
Anum_pg_opclass_opcname,
|
||||
0,
|
||||
0
|
||||
}},
|
||||
{OperatorClassRelationName, /* CLANAME */
|
||||
OpclassNameIndex,
|
||||
{OperatorClassRelationName, /* CLAOID */
|
||||
OpclassOidIndex,
|
||||
0,
|
||||
1,
|
||||
{
|
||||
Anum_pg_opclass_opcname,
|
||||
ObjectIdAttributeNumber,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.17 2001/06/02 19:01:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.18 2001/08/21 16:36:05 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2055,7 +2055,7 @@ SelectSortFunction(Oid sortOperator,
|
||||
{
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
ScanKeyData skey[3];
|
||||
ScanKeyData skey[1];
|
||||
HeapTuple tuple;
|
||||
Form_pg_operator optup;
|
||||
Oid opclass = InvalidOid;
|
||||
@@ -2068,25 +2068,20 @@ SelectSortFunction(Oid sortOperator,
|
||||
* If the operator is registered the same way in multiple opclasses,
|
||||
* assume we can use the associated comparator function from any one.
|
||||
*/
|
||||
relation = heap_openr(AccessMethodOperatorRelationName,
|
||||
AccessShareLock);
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0], 0,
|
||||
Anum_pg_amop_amopid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(BTREE_AM_OID));
|
||||
|
||||
ScanKeyEntryInitialize(&skey[1], 0,
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
Anum_pg_amop_amopopr,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(sortOperator));
|
||||
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 2, skey);
|
||||
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 1, skey);
|
||||
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
|
||||
|
||||
if (!opclass_is_btree(aform->amopclaid))
|
||||
continue;
|
||||
if (aform->amopstrategy == BTLessStrategyNumber)
|
||||
{
|
||||
opclass = aform->amopclaid;
|
||||
@@ -2107,39 +2102,18 @@ SelectSortFunction(Oid sortOperator,
|
||||
if (OidIsValid(opclass))
|
||||
{
|
||||
/* Found a suitable opclass, get its comparator support function */
|
||||
relation = heap_openr(AccessMethodProcedureRelationName,
|
||||
AccessShareLock);
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0], 0,
|
||||
Anum_pg_amproc_amid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(BTREE_AM_OID));
|
||||
|
||||
ScanKeyEntryInitialize(&skey[1], 0,
|
||||
Anum_pg_amproc_amopclaid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(opclass));
|
||||
|
||||
ScanKeyEntryInitialize(&skey[2], 0,
|
||||
Anum_pg_amproc_amprocnum,
|
||||
F_INT2EQ,
|
||||
Int16GetDatum(BTORDER_PROC));
|
||||
|
||||
scan = heap_beginscan(relation, false, SnapshotNow, 3, skey);
|
||||
|
||||
*sortFunction = InvalidOid;
|
||||
|
||||
if (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
tuple = SearchSysCache(AMPROCNUM,
|
||||
ObjectIdGetDatum(opclass),
|
||||
Int16GetDatum(BTORDER_PROC),
|
||||
0, 0);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
|
||||
*sortFunction = aform->amproc;
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(relation, AccessShareLock);
|
||||
|
||||
if (RegProcedureIsValid(*sortFunction))
|
||||
ReleaseSysCache(tuple);
|
||||
Assert(RegProcedureIsValid(*sortFunction));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2158,7 +2132,7 @@ SelectSortFunction(Oid sortOperator,
|
||||
*kind = SORTFUNC_REVLT;
|
||||
else
|
||||
*kind = SORTFUNC_LT;
|
||||
*sortFunction = optup->oprcode;
|
||||
*sortFunction = optup->oprcode;
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
Assert(RegProcedureIsValid(*sortFunction));
|
||||
|
||||
Reference in New Issue
Block a user