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

Create the system catalog infrastructure needed for KNNGIST.

This commit adds columns amoppurpose and amopsortfamily to pg_amop, and
column amcanorderbyop to pg_am.  For the moment all the entries in
amcanorderbyop are "false", since the underlying support isn't there yet.

Also, extend the CREATE OPERATOR CLASS/ALTER OPERATOR FAMILY commands with
[ FOR SEARCH | FOR ORDER BY sort_operator_family ] clauses to allow the new
columns of pg_amop to be populated, and create pg_dump support for dumping
that information.

I also added some documentation, although it's perhaps a bit premature
given that the feature doesn't do anything useful yet.

Teodor Sigaev, Robert Haas, Tom Lane
This commit is contained in:
Tom Lane
2010-11-24 14:20:39 -05:00
parent 4fc09ad00c
commit 725d52d0c2
21 changed files with 913 additions and 475 deletions

View File

@ -54,6 +54,7 @@ typedef struct
int number; /* strategy or support proc number */
Oid lefttype; /* lefttype */
Oid righttype; /* righttype */
Oid sortfamily; /* ordering operator's sort opfamily, or 0 */
} OpFamilyMember;
@ -457,6 +458,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
CreateOpClassItem *item = lfirst(l);
Oid operOid;
Oid funcOid;
Oid sortfamilyOid;
OpFamilyMember *member;
Assert(IsA(item, CreateOpClassItem));
@ -486,6 +488,13 @@ DefineOpClass(CreateOpClassStmt *stmt)
false, -1);
}
if (item->order_family)
sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
item->order_family,
false);
else
sortfamilyOid = InvalidOid;
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own operator and its underlying function */
@ -502,6 +511,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
member->object = operOid;
member->number = item->number;
member->sortfamily = sortfamilyOid;
assignOperTypes(member, amoid, typeoid);
addFamilyMember(&operators, member, false);
break;
@ -825,6 +835,7 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
CreateOpClassItem *item = lfirst(l);
Oid operOid;
Oid funcOid;
Oid sortfamilyOid;
OpFamilyMember *member;
Assert(IsA(item, CreateOpClassItem));
@ -854,6 +865,13 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
operOid = InvalidOid; /* keep compiler quiet */
}
if (item->order_family)
sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
item->order_family,
false);
else
sortfamilyOid = InvalidOid;
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own operator and its underlying function */
@ -870,6 +888,7 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
member->object = operOid;
member->number = item->number;
member->sortfamily = sortfamilyOid;
assignOperTypes(member, amoid, InvalidOid);
addFamilyMember(&operators, member, false);
break;
@ -1043,16 +1062,51 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
opform = (Form_pg_operator) GETSTRUCT(optup);
/*
* Opfamily operators must be binary ops returning boolean.
* Opfamily operators must be binary.
*/
if (opform->oprkind != 'b')
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("index operators must be binary")));
if (opform->oprresult != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("index operators must return boolean")));
if (OidIsValid(member->sortfamily))
{
/*
* Ordering op, check index supports that. (We could perhaps also
* check that the operator returns a type supported by the sortfamily,
* but that seems more trouble than it's worth here. If it does not,
* the operator will never be matchable to any ORDER BY clause, but
* no worse consequences can ensue. Also, trying to check that would
* create an ordering hazard during dump/reload: it's possible that
* the family has been created but not yet populated with the required
* operators.)
*/
HeapTuple amtup;
Form_pg_am pg_am;
amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
if (amtup == NULL)
elog(ERROR, "cache lookup failed for access method %u", amoid);
pg_am = (Form_pg_am) GETSTRUCT(amtup);
if (!pg_am->amcanorderbyop)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
NameStr(pg_am->amname))));
ReleaseSysCache(amtup);
}
else
{
/*
* Search operators must return boolean.
*/
if (opform->oprresult != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("index search operators must return boolean")));
}
/*
* If lefttype/righttype isn't specified, use the operator's input types
@ -1206,6 +1260,7 @@ storeOperators(List *opfamilyname, Oid amoid,
foreach(l, operators)
{
OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
char oppurpose;
/*
* If adding to an existing family, check for conflict with an
@ -1225,6 +1280,8 @@ storeOperators(List *opfamilyname, Oid amoid,
format_type_be(op->righttype),
NameListToString(opfamilyname))));
oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
/* Create the pg_amop entry */
memset(values, 0, sizeof(values));
memset(nulls, false, sizeof(nulls));
@ -1233,8 +1290,10 @@ storeOperators(List *opfamilyname, Oid amoid,
values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
tup = heap_form_tuple(rel->rd_att, values, nulls);
@ -1275,6 +1334,15 @@ storeOperators(List *opfamilyname, Oid amoid,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
/* A search operator also needs a dep on the referenced opfamily */
if (OidIsValid(op->sortfamily))
{
referenced.classId = OperatorFamilyRelationId;
referenced.objectId = op->sortfamily;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
}
heap_close(rel, RowExclusiveLock);

View File

@ -2991,6 +2991,7 @@ _copyCreateOpClassItem(CreateOpClassItem *from)
COPY_NODE_FIELD(name);
COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(number);
COPY_NODE_FIELD(order_family);
COPY_NODE_FIELD(class_args);
COPY_NODE_FIELD(storedtype);

View File

@ -1464,6 +1464,7 @@ _equalCreateOpClassItem(CreateOpClassItem *a, CreateOpClassItem *b)
COMPARE_NODE_FIELD(name);
COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(number);
COMPARE_NODE_FIELD(order_family);
COMPARE_NODE_FIELD(class_args);
COMPARE_NODE_FIELD(storedtype);

View File

@ -212,6 +212,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->relam = indexRelation->rd_rel->relam;
info->amcostestimate = indexRelation->rd_am->amcostestimate;
info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
info->amsearchnulls = indexRelation->rd_am->amsearchnulls;
info->amhasgettuple = OidIsValid(indexRelation->rd_am->amgettuple);

View File

@ -1661,8 +1661,9 @@ get_btree_test_op(Oid pred_op, Oid clause_op, bool refute_it)
* From the same opfamily, find a strategy number for the clause_op,
* if possible
*/
clause_tuple = SearchSysCache2(AMOPOPID,
clause_tuple = SearchSysCache3(AMOPOPID,
ObjectIdGetDatum(clause_op),
CharGetDatum(AMOP_SEARCH),
ObjectIdGetDatum(opfamily_id));
if (HeapTupleIsValid(clause_tuple))
{
@ -1677,8 +1678,9 @@ get_btree_test_op(Oid pred_op, Oid clause_op, bool refute_it)
}
else if (OidIsValid(clause_op_negator))
{
clause_tuple = SearchSysCache2(AMOPOPID,
ObjectIdGetDatum(clause_op_negator),
clause_tuple = SearchSysCache3(AMOPOPID,
ObjectIdGetDatum(clause_op_negator),
CharGetDatum(AMOP_SEARCH),
ObjectIdGetDatum(opfamily_id));
if (HeapTupleIsValid(clause_tuple))
{

View File

@ -295,7 +295,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
ctext_expr_list ctext_row def_list indirection opt_indirection
reloption_list group_clause TriggerFuncArgs select_limit
opt_select_limit opclass_item_list opclass_drop_list
opt_opfamily transaction_mode_list_or_empty
opclass_purpose opt_opfamily transaction_mode_list_or_empty
OptTableFuncElementList TableFuncElementList opt_type_modifiers
prep_type_clause
execute_param_clause using_clause returning_clause
@ -3935,22 +3935,25 @@ opclass_item_list:
;
opclass_item:
OPERATOR Iconst any_operator opt_recheck
OPERATOR Iconst any_operator opclass_purpose opt_recheck
{
CreateOpClassItem *n = makeNode(CreateOpClassItem);
n->itemtype = OPCLASS_ITEM_OPERATOR;
n->name = $3;
n->args = NIL;
n->number = $2;
n->order_family = $4;
$$ = (Node *) n;
}
| OPERATOR Iconst any_operator oper_argtypes opt_recheck
| OPERATOR Iconst any_operator oper_argtypes opclass_purpose
opt_recheck
{
CreateOpClassItem *n = makeNode(CreateOpClassItem);
n->itemtype = OPCLASS_ITEM_OPERATOR;
n->name = $3;
n->args = $4;
n->number = $2;
n->order_family = $5;
$$ = (Node *) n;
}
| FUNCTION Iconst func_name func_args
@ -3989,6 +3992,11 @@ opt_opfamily: FAMILY any_name { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
opclass_purpose: FOR SEARCH { $$ = NIL; }
| FOR ORDER BY any_name { $$ = $4; }
| /*EMPTY*/ { $$ = NIL; }
;
opt_recheck: RECHECK
{
/*

View File

@ -46,12 +46,15 @@ get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
* op_in_opfamily
*
* Return t iff operator 'opno' is in operator family 'opfamily'.
*
* This function only considers search operators, not ordering operators.
*/
bool
op_in_opfamily(Oid opno, Oid opfamily)
{
return SearchSysCacheExists2(AMOPOPID,
return SearchSysCacheExists3(AMOPOPID,
ObjectIdGetDatum(opno),
CharGetDatum(AMOP_SEARCH),
ObjectIdGetDatum(opfamily));
}
@ -60,6 +63,8 @@ op_in_opfamily(Oid opno, Oid opfamily)
*
* Get the operator's strategy number within the specified opfamily,
* or 0 if it's not a member of the opfamily.
*
* This function only considers search operators, not ordering operators.
*/
int
get_op_opfamily_strategy(Oid opno, Oid opfamily)
@ -68,8 +73,9 @@ get_op_opfamily_strategy(Oid opno, Oid opfamily)
Form_pg_amop amop_tup;
int result;
tp = SearchSysCache2(AMOPOPID,
tp = SearchSysCache3(AMOPOPID,
ObjectIdGetDatum(opno),
CharGetDatum(AMOP_SEARCH),
ObjectIdGetDatum(opfamily));
if (!HeapTupleIsValid(tp))
return 0;
@ -85,6 +91,8 @@ get_op_opfamily_strategy(Oid opno, Oid opfamily)
* Get the operator's strategy number and declared input data types
* within the specified opfamily.
*
* This function only considers search operators, not ordering operators.
*
* Caller should already have verified that opno is a member of opfamily,
* therefore we raise an error if the tuple is not found.
*/
@ -97,8 +105,9 @@ get_op_opfamily_properties(Oid opno, Oid opfamily,
HeapTuple tp;
Form_pg_amop amop_tup;
tp = SearchSysCache2(AMOPOPID,
tp = SearchSysCache3(AMOPOPID,
ObjectIdGetDatum(opno),
CharGetDatum(AMOP_SEARCH),
ObjectIdGetDatum(opfamily));
if (!HeapTupleIsValid(tp))
elog(ERROR, "operator %u is not a member of opfamily %u",

View File

@ -135,11 +135,11 @@ static const struct cachedesc cacheinfo[] = {
},
{AccessMethodOperatorRelationId, /* AMOPOPID */
AccessMethodOperatorIndexId,
2,
3,
{
Anum_pg_amop_amopopr,
Anum_pg_amop_amoppurpose,
Anum_pg_amop_amopfamily,
0,
0
},
64

View File

@ -8815,22 +8815,28 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
int i_opckeytype;
int i_opcdefault;
int i_opcfamily;
int i_opcfamilyname;
int i_opcfamilynsp;
int i_amname;
int i_amopstrategy;
int i_amopreqcheck;
int i_amopopr;
int i_sortfamily;
int i_sortfamilynsp;
int i_amprocnum;
int i_amproc;
char *opcintype;
char *opckeytype;
char *opcdefault;
char *opcfamily;
char *opcfamilyname;
char *opcfamilynsp;
char *amname;
char *amopstrategy;
char *amopreqcheck;
char *amopopr;
char *sortfamily;
char *sortfamilynsp;
char *amprocnum;
char *amproc;
bool needComma;
@ -8860,8 +8866,8 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
{
appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
"opckeytype::pg_catalog.regtype, "
"opcdefault, "
"opfname AS opcfamily, "
"opcdefault, opcfamily, "
"opfname AS opcfamilyname, "
"nspname AS opcfamilynsp, "
"(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
"FROM pg_catalog.pg_opclass c "
@ -8874,8 +8880,8 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
{
appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
"opckeytype::pg_catalog.regtype, "
"opcdefault, "
"NULL AS opcfamily, "
"opcdefault, NULL AS opcfamily, "
"NULL AS opcfamilyname, "
"NULL AS opcfamilynsp, "
"(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
"FROM pg_catalog.pg_opclass "
@ -8901,13 +8907,16 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
i_opckeytype = PQfnumber(res, "opckeytype");
i_opcdefault = PQfnumber(res, "opcdefault");
i_opcfamily = PQfnumber(res, "opcfamily");
i_opcfamilyname = PQfnumber(res, "opcfamilyname");
i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
i_amname = PQfnumber(res, "amname");
opcintype = PQgetvalue(res, 0, i_opcintype);
opckeytype = PQgetvalue(res, 0, i_opckeytype);
opcdefault = PQgetvalue(res, 0, i_opcdefault);
opcfamily = PQgetvalue(res, 0, i_opcfamily);
/* opcfamily will still be needed after we PQclear res */
opcfamily = strdup(PQgetvalue(res, 0, i_opcfamily));
opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
/* amname will still be needed after we PQclear res */
amname = strdup(PQgetvalue(res, 0, i_amname));
@ -8930,14 +8939,14 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
appendPQExpBuffer(q, "FOR TYPE %s USING %s",
opcintype,
fmtId(amname));
if (strlen(opcfamily) > 0 &&
(strcmp(opcfamily, opcinfo->dobj.name) != 0 ||
if (strlen(opcfamilyname) > 0 &&
(strcmp(opcfamilyname, opcinfo->dobj.name) != 0 ||
strcmp(opcfamilynsp, opcinfo->dobj.namespace->dobj.name) != 0))
{
appendPQExpBuffer(q, " FAMILY ");
if (strcmp(opcfamilynsp, opcinfo->dobj.namespace->dobj.name) != 0)
appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
appendPQExpBuffer(q, "%s", fmtId(opcfamily));
appendPQExpBuffer(q, "%s", fmtId(opcfamilyname));
}
appendPQExpBuffer(q, " AS\n ");
@ -8954,23 +8963,41 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
/*
* Now fetch and print the OPERATOR entries (pg_amop rows).
*
* Print only those opfamily members that are tied to the opclass by
* pg_depend entries.
*
* XXX RECHECK is gone as of 8.4, but we'll still print it if dumping
* an older server's opclass in which it is used. This is to avoid
* hard-to-detect breakage if a newer pg_dump is used to dump from an
* older server and then reload into that old version. This can go
* away once 8.3 is so old as to not be of interest to anyone.
*/
resetPQExpBuffer(query);
if (g_fout->remoteVersion >= 80400)
if (g_fout->remoteVersion >= 90100)
{
/*
* Print only those opfamily members that are tied to the opclass by
* pg_depend entries.
*
* XXX RECHECK is gone as of 8.4, but we'll still print it if dumping
* an older server's opclass in which it is used. This is to avoid
* hard-to-detect breakage if a newer pg_dump is used to dump from an
* older server and then reload into that old version. This can go
* away once 8.3 is so old as to not be of interest to anyone.
*/
appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
"amopopr::pg_catalog.regoperator "
"amopopr::pg_catalog.regoperator, "
"opfname AS sortfamily, "
"nspname AS sortfamilynsp "
"FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
"(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
"LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
"WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
"AND refobjid = '%u'::pg_catalog.oid "
"AND amopfamily = '%s'::pg_catalog.oid "
"ORDER BY amopstrategy",
opcinfo->dobj.catId.oid,
opcfamily);
}
else if (g_fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
"amopopr::pg_catalog.regoperator, "
"NULL AS sortfamily, "
"NULL AS sortfamilynsp "
"FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
"WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
"AND refobjid = '%u'::pg_catalog.oid "
@ -8981,12 +9008,10 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
}
else if (g_fout->remoteVersion >= 80300)
{
/*
* Print only those opfamily members that are tied to the opclass by
* pg_depend entries.
*/
appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
"amopopr::pg_catalog.regoperator "
"amopopr::pg_catalog.regoperator, "
"NULL AS sortfamily, "
"NULL AS sortfamilynsp "
"FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
"WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
"AND refobjid = '%u'::pg_catalog.oid "
@ -8997,8 +9022,14 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
}
else
{
/*
* Here, we print all entries since there are no opfamilies and
* hence no loose operators to worry about.
*/
appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
"amopopr::pg_catalog.regoperator "
"amopopr::pg_catalog.regoperator, "
"NULL AS sortfamily, "
"NULL AS sortfamilynsp "
"FROM pg_catalog.pg_amop "
"WHERE amopclaid = '%u'::pg_catalog.oid "
"ORDER BY amopstrategy",
@ -9013,18 +9044,31 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
i_amopstrategy = PQfnumber(res, "amopstrategy");
i_amopreqcheck = PQfnumber(res, "amopreqcheck");
i_amopopr = PQfnumber(res, "amopopr");
i_sortfamily = PQfnumber(res, "sortfamily");
i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
for (i = 0; i < ntups; i++)
{
amopstrategy = PQgetvalue(res, i, i_amopstrategy);
amopreqcheck = PQgetvalue(res, i, i_amopreqcheck);
amopopr = PQgetvalue(res, i, i_amopopr);
sortfamily = PQgetvalue(res, i, i_sortfamily);
sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
if (needComma)
appendPQExpBuffer(q, " ,\n ");
appendPQExpBuffer(q, "OPERATOR %s %s",
amopstrategy, amopopr);
if (strlen(sortfamily) > 0)
{
appendPQExpBuffer(q, " FOR ORDER BY ");
if (strcmp(sortfamilynsp, opcinfo->dobj.namespace->dobj.name) != 0)
appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
appendPQExpBuffer(q, "%s", fmtId(sortfamily));
}
if (strcmp(amopreqcheck, "t") == 0)
appendPQExpBuffer(q, " RECHECK");
@ -9035,15 +9079,14 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
/*
* Now fetch and print the FUNCTION entries (pg_amproc rows).
*
* Print only those opfamily members that are tied to the opclass by
* pg_depend entries.
*/
resetPQExpBuffer(query);
if (g_fout->remoteVersion >= 80300)
{
/*
* Print only those opfamily members that are tied to the opclass by
* pg_depend entries.
*/
appendPQExpBuffer(query, "SELECT amprocnum, "
"amproc::pg_catalog.regprocedure "
"FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
@ -9119,6 +9162,9 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
/*
* dumpOpfamily
* write out a single operator family definition
*
* Note: this also dumps any "loose" operator members that aren't bound to a
* specific opclass within the opfamily.
*/
static void
dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
@ -9134,6 +9180,8 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
int i_amopstrategy;
int i_amopreqcheck;
int i_amopopr;
int i_sortfamily;
int i_sortfamilynsp;
int i_amprocnum;
int i_amproc;
int i_amproclefttype;
@ -9142,6 +9190,8 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
char *amopstrategy;
char *amopreqcheck;
char *amopopr;
char *sortfamily;
char *sortfamilynsp;
char *amprocnum;
char *amproc;
char *amproclefttype;
@ -9172,18 +9222,36 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
/*
* Fetch only those opfamily members that are tied directly to the
* opfamily by pg_depend entries.
*
* XXX RECHECK is gone as of 8.4, but we'll still print it if dumping
* an older server's opclass in which it is used. This is to avoid
* hard-to-detect breakage if a newer pg_dump is used to dump from an
* older server and then reload into that old version. This can go
* away once 8.3 is so old as to not be of interest to anyone.
*/
if (g_fout->remoteVersion >= 80400)
if (g_fout->remoteVersion >= 90100)
{
/*
* XXX RECHECK is gone as of 8.4, but we'll still print it if dumping
* an older server's opclass in which it is used. This is to avoid
* hard-to-detect breakage if a newer pg_dump is used to dump from an
* older server and then reload into that old version. This can go
* away once 8.3 is so old as to not be of interest to anyone.
*/
appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
"amopopr::pg_catalog.regoperator "
"amopopr::pg_catalog.regoperator, "
"opfname AS sortfamily, "
"nspname AS sortfamilynsp "
"FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
"(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
"LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
"WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
"AND refobjid = '%u'::pg_catalog.oid "
"AND amopfamily = '%u'::pg_catalog.oid "
"ORDER BY amopstrategy",
opfinfo->dobj.catId.oid,
opfinfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT amopstrategy, false AS amopreqcheck, "
"amopopr::pg_catalog.regoperator, "
"NULL AS sortfamily, "
"NULL AS sortfamilynsp "
"FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
"WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
"AND refobjid = '%u'::pg_catalog.oid "
@ -9195,7 +9263,9 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
else
{
appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
"amopopr::pg_catalog.regoperator "
"amopopr::pg_catalog.regoperator, "
"NULL AS sortfamily, "
"NULL AS sortfamilynsp "
"FROM pg_catalog.pg_amop ao, pg_catalog.pg_depend "
"WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
"AND refobjid = '%u'::pg_catalog.oid "
@ -9323,18 +9393,31 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
i_amopreqcheck = PQfnumber(res_ops, "amopreqcheck");
i_amopopr = PQfnumber(res_ops, "amopopr");
i_sortfamily = PQfnumber(res_ops, "sortfamily");
i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
for (i = 0; i < ntups; i++)
{
amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
amopreqcheck = PQgetvalue(res_ops, i, i_amopreqcheck);
amopopr = PQgetvalue(res_ops, i, i_amopopr);
sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
if (needComma)
appendPQExpBuffer(q, " ,\n ");
appendPQExpBuffer(q, "OPERATOR %s %s",
amopstrategy, amopopr);
if (strlen(sortfamily) > 0)
{
appendPQExpBuffer(q, " FOR ORDER BY ");
if (strcmp(sortfamilynsp, opfinfo->dobj.namespace->dobj.name) != 0)
appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
appendPQExpBuffer(q, "%s", fmtId(sortfamily));
}
if (strcmp(amopreqcheck, "t") == 0)
appendPQExpBuffer(q, " RECHECK");

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201011211
#define CATALOG_VERSION_NO 201011241
#endif

View File

@ -67,7 +67,7 @@ DECLARE_UNIQUE_INDEX(pg_am_oid_index, 2652, on pg_am using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_amop_fam_strat_index, 2653, on pg_amop using btree(amopfamily oid_ops, amoplefttype oid_ops, amoprighttype oid_ops, amopstrategy int2_ops));
#define AccessMethodStrategyIndexId 2653
DECLARE_UNIQUE_INDEX(pg_amop_opr_fam_index, 2654, on pg_amop using btree(amopopr oid_ops, amopfamily oid_ops));
DECLARE_UNIQUE_INDEX(pg_amop_opr_fam_index, 2654, on pg_amop using btree(amopopr oid_ops, amoppurpose char_ops, amopfamily oid_ops));
#define AccessMethodOperatorIndexId 2654
DECLARE_UNIQUE_INDEX(pg_amop_oid_index, 2756, on pg_amop using btree(oid oid_ops));
#define AccessMethodOperatorOidIndexId 2756

View File

@ -40,7 +40,8 @@ CATALOG(pg_am,2601)
* assignments. */
int2 amsupport; /* total number of support functions that this
* AM uses */
bool amcanorder; /* does AM support ordered scan results? */
bool amcanorder; /* does AM support order by column value? */
bool amcanorderbyop; /* does AM support order by operator result? */
bool amcanbackward; /* does AM support backward scan? */
bool amcanunique; /* does AM support UNIQUE indexes? */
bool amcanmulticol; /* does AM support multi-column indexes? */
@ -76,49 +77,50 @@ typedef FormData_pg_am *Form_pg_am;
* compiler constants for pg_am
* ----------------
*/
#define Natts_pg_am 26
#define Natts_pg_am 27
#define Anum_pg_am_amname 1
#define Anum_pg_am_amstrategies 2
#define Anum_pg_am_amsupport 3
#define Anum_pg_am_amcanorder 4
#define Anum_pg_am_amcanbackward 5
#define Anum_pg_am_amcanunique 6
#define Anum_pg_am_amcanmulticol 7
#define Anum_pg_am_amoptionalkey 8
#define Anum_pg_am_amindexnulls 9
#define Anum_pg_am_amsearchnulls 10
#define Anum_pg_am_amstorage 11
#define Anum_pg_am_amclusterable 12
#define Anum_pg_am_amkeytype 13
#define Anum_pg_am_aminsert 14
#define Anum_pg_am_ambeginscan 15
#define Anum_pg_am_amgettuple 16
#define Anum_pg_am_amgetbitmap 17
#define Anum_pg_am_amrescan 18
#define Anum_pg_am_amendscan 19
#define Anum_pg_am_ammarkpos 20
#define Anum_pg_am_amrestrpos 21
#define Anum_pg_am_ambuild 22
#define Anum_pg_am_ambulkdelete 23
#define Anum_pg_am_amvacuumcleanup 24
#define Anum_pg_am_amcostestimate 25
#define Anum_pg_am_amoptions 26
#define Anum_pg_am_amcanorderbyop 5
#define Anum_pg_am_amcanbackward 6
#define Anum_pg_am_amcanunique 7
#define Anum_pg_am_amcanmulticol 8
#define Anum_pg_am_amoptionalkey 9
#define Anum_pg_am_amindexnulls 10
#define Anum_pg_am_amsearchnulls 11
#define Anum_pg_am_amstorage 12
#define Anum_pg_am_amclusterable 13
#define Anum_pg_am_amkeytype 14
#define Anum_pg_am_aminsert 15
#define Anum_pg_am_ambeginscan 16
#define Anum_pg_am_amgettuple 17
#define Anum_pg_am_amgetbitmap 18
#define Anum_pg_am_amrescan 19
#define Anum_pg_am_amendscan 20
#define Anum_pg_am_ammarkpos 21
#define Anum_pg_am_amrestrpos 22
#define Anum_pg_am_ambuild 23
#define Anum_pg_am_ambulkdelete 24
#define Anum_pg_am_amvacuumcleanup 25
#define Anum_pg_am_amcostestimate 26
#define Anum_pg_am_amoptions 27
/* ----------------
* initial contents of pg_am
* ----------------
*/
DATA(insert OID = 403 ( btree 5 1 t t t t t t t f t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
DATA(insert OID = 403 ( btree 5 1 t f t t t t t t f t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
DESCR("b-tree index access method");
#define BTREE_AM_OID 403
DATA(insert OID = 405 ( hash 1 1 f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
DESCR("hash index access method");
#define HASH_AM_OID 405
DATA(insert OID = 783 ( gist 0 7 f f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
DATA(insert OID = 783 ( gist 0 7 f f f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
DATA(insert OID = 2742 ( gin 0 5 f f f t t f f t f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
DATA(insert OID = 2742 ( gin 0 5 f f f f t t f f t f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742

File diff suppressed because it is too large Load Diff

View File

@ -1776,6 +1776,7 @@ typedef struct CreateOpClassItem
List *name; /* operator or function name */
List *args; /* argument types */
int number; /* strategy num or support proc num */
List *order_family; /* only used for ordering operators */
List *class_args; /* only used for functions */
/* fields used for a storagetype item: */
TypeName *storedtype; /* datatype stored in index */

View File

@ -469,6 +469,7 @@ typedef struct IndexOptInfo
bool predOK; /* true if predicate matches query */
bool unique; /* true if a unique index */
bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
bool amhasgettuple; /* does AM have amgettuple interface? */

View File

@ -849,6 +849,14 @@ WHERE p1.amopfamily = 0 OR p1.amoplefttype = 0 OR p1.amoprighttype = 0
------------+--------------
(0 rows)
SELECT p1.amopfamily, p1.amopstrategy
FROM pg_amop as p1
WHERE NOT ((p1.amoppurpose = 's' AND p1.amopsortfamily = 0) OR
(p1.amoppurpose = 'o' AND p1.amopsortfamily <> 0));
amopfamily | amopstrategy
------------+--------------
(0 rows)
-- amoplefttype/amoprighttype must match the operator
SELECT p1.oid, p2.oid
FROM pg_amop AS p1, pg_operator AS p2
@ -866,6 +874,25 @@ WHERE p1.amopfamily = p2.oid AND p1.amopmethod != p2.opfmethod;
-----+-----
(0 rows)
-- amopsortfamily, if present, must reference a btree family
SELECT p1.amopfamily, p1.amopstrategy
FROM pg_amop AS p1
WHERE p1.amopsortfamily <> 0 AND NOT EXISTS
(SELECT 1 from pg_opfamily op WHERE op.oid = p1.amopsortfamily
AND op.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree'));
amopfamily | amopstrategy
------------+--------------
(0 rows)
-- check for ordering operators not supported by parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
FROM pg_amop AS p1, pg_am AS p2
WHERE p1.amopmethod = p2.oid AND
p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
amopfamily | amopopr | oid | amname
------------+---------+-----+--------
(0 rows)
-- Cross-check amopstrategy index against parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
FROM pg_amop AS p1, pg_am AS p2
@ -885,17 +912,45 @@ WHERE p2.amopmethod = p1.oid AND
p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
WHERE p3.amopfamily = p2.amopfamily AND
p3.amoplefttype = p2.amoplefttype AND
p3.amoprighttype = p2.amoprighttype);
p3.amoprighttype = p2.amoprighttype AND
p3.amoppurpose = 's');
amname | amoplefttype | amoprighttype
--------+--------------+---------------
(0 rows)
-- Currently, none of the AMs with fixed strategy sets support ordering ops.
SELECT p1.amname, p2.amopfamily, p2.amopstrategy
FROM pg_am AS p1, pg_amop AS p2
WHERE p2.amopmethod = p1.oid AND
p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
amname | amopfamily | amopstrategy
--------+------------+--------------
(0 rows)
-- Check that amopopr points at a reasonable-looking operator, ie a binary
-- operator yielding boolean.
-- operator. If it's a search operator it had better yield boolean,
-- otherwise an input type of its sort opfamily.
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND
(p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype);
p2.oprkind != 'b';
amopfamily | amopopr | oid | oprname
------------+---------+-----+---------
(0 rows)
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
p2.oprresult != 'bool'::regtype;
amopfamily | amopopr | oid | oprname
------------+---------+-----+---------
(0 rows)
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 'o' AND NOT EXISTS
(SELECT 1 FROM pg_opclass op
WHERE opcfamily = p1.amopsortfamily AND opcintype = p2.oprresult);
amopfamily | amopopr | oid | oprname
------------+---------+-----+---------
(0 rows)
@ -950,12 +1005,12 @@ ORDER BY 1, 2, 3;
2742 | 4 | =
(39 rows)
-- Check that all operators linked to by opclass entries have selectivity
-- estimators. This is not absolutely required, but it seems a reasonable
-- thing to insist on for all standard datatypes.
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
-- to insist on for all standard datatypes.
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
(p2.oprrest = 0 OR p2.oprjoin = 0);
amopfamily | amopopr | oid | oprname
------------+---------+-----+---------

View File

@ -685,6 +685,11 @@ FROM pg_amop as p1
WHERE p1.amopfamily = 0 OR p1.amoplefttype = 0 OR p1.amoprighttype = 0
OR p1.amopopr = 0 OR p1.amopmethod = 0 OR p1.amopstrategy < 1;
SELECT p1.amopfamily, p1.amopstrategy
FROM pg_amop as p1
WHERE NOT ((p1.amoppurpose = 's' AND p1.amopsortfamily = 0) OR
(p1.amoppurpose = 'o' AND p1.amopsortfamily <> 0));
-- amoplefttype/amoprighttype must match the operator
SELECT p1.oid, p2.oid
@ -698,6 +703,21 @@ SELECT p1.oid, p2.oid
FROM pg_amop AS p1, pg_opfamily AS p2
WHERE p1.amopfamily = p2.oid AND p1.amopmethod != p2.opfmethod;
-- amopsortfamily, if present, must reference a btree family
SELECT p1.amopfamily, p1.amopstrategy
FROM pg_amop AS p1
WHERE p1.amopsortfamily <> 0 AND NOT EXISTS
(SELECT 1 from pg_opfamily op WHERE op.oid = p1.amopsortfamily
AND op.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree'));
-- check for ordering operators not supported by parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
FROM pg_amop AS p1, pg_am AS p2
WHERE p1.amopmethod = p2.oid AND
p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
-- Cross-check amopstrategy index against parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
@ -716,15 +736,35 @@ WHERE p2.amopmethod = p1.oid AND
p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
WHERE p3.amopfamily = p2.amopfamily AND
p3.amoplefttype = p2.amoplefttype AND
p3.amoprighttype = p2.amoprighttype);
p3.amoprighttype = p2.amoprighttype AND
p3.amoppurpose = 's');
-- Currently, none of the AMs with fixed strategy sets support ordering ops.
SELECT p1.amname, p2.amopfamily, p2.amopstrategy
FROM pg_am AS p1, pg_amop AS p2
WHERE p2.amopmethod = p1.oid AND
p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
-- Check that amopopr points at a reasonable-looking operator, ie a binary
-- operator yielding boolean.
-- operator. If it's a search operator it had better yield boolean,
-- otherwise an input type of its sort opfamily.
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND
(p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype);
p2.oprkind != 'b';
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
p2.oprresult != 'bool'::regtype;
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 'o' AND NOT EXISTS
(SELECT 1 FROM pg_opclass op
WHERE opcfamily = p1.amopsortfamily AND opcintype = p2.oprresult);
-- Make a list of all the distinct operator names being used in particular
-- strategy slots. This is a bit hokey, since the list might need to change
@ -735,13 +775,13 @@ SELECT DISTINCT amopmethod, amopstrategy, oprname
FROM pg_amop p1 LEFT JOIN pg_operator p2 ON amopopr = p2.oid
ORDER BY 1, 2, 3;
-- Check that all operators linked to by opclass entries have selectivity
-- estimators. This is not absolutely required, but it seems a reasonable
-- thing to insist on for all standard datatypes.
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
-- to insist on for all standard datatypes.
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
FROM pg_amop AS p1, pg_operator AS p2
WHERE p1.amopopr = p2.oid AND
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
(p2.oprrest = 0 OR p2.oprjoin = 0);
-- Check that each opclass in an opfamily has associated operators, that is