mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +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:
parent
4fc09ad00c
commit
725d52d0c2
@ -423,7 +423,16 @@
|
||||
<entry><structfield>amcanorder</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
<entry></entry>
|
||||
<entry>Does the access method support ordered scans?</entry>
|
||||
<entry>Does the access method support ordered scans sorted by the
|
||||
indexed column's value?</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>amcanorderbyop</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
<entry></entry>
|
||||
<entry>Does the access method support ordered scans sorted by the result
|
||||
of an operator on the indexed column?</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@ -598,9 +607,13 @@
|
||||
<para>
|
||||
The catalog <structname>pg_amop</structname> stores information about
|
||||
operators associated with access method operator families. There is one
|
||||
row for each operator that is a member of an operator family. An operator
|
||||
row for each operator that is a member of an operator family. A family
|
||||
member can be either a <firstterm>search</> operator or an
|
||||
<firstterm>ordering</> operator. An operator
|
||||
can appear in more than one family, but cannot appear in more than one
|
||||
position within a family.
|
||||
search position nor more than one ordering position within a family.
|
||||
(It is allowed, though unlikely, for an operator to be used for both
|
||||
search and ordering purposes.)
|
||||
</para>
|
||||
|
||||
<table>
|
||||
@ -645,6 +658,14 @@
|
||||
<entry>Operator strategy number</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>amoppurpose</structfield></entry>
|
||||
<entry><type>char</type></entry>
|
||||
<entry></entry>
|
||||
<entry>Operator purpose, either <literal>s</> for search or
|
||||
<literal>o</> for ordering</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>amopopr</structfield></entry>
|
||||
<entry><type>oid</type></entry>
|
||||
@ -659,10 +680,52 @@
|
||||
<entry>Index access method operator family is for</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>amopsortfamily</structfield></entry>
|
||||
<entry><type>oid</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-opfamily"><structname>pg_opfamily</structname></link>.oid</literal></entry>
|
||||
<entry>The btree operator family this entry sorts according to, if an
|
||||
ordering operator; zero if a search operator</entry>
|
||||
</row>
|
||||
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>
|
||||
A <quote>search</> operator entry indicates that an index of this operator
|
||||
family can be searched to find all rows satisfying
|
||||
<literal>WHERE</>
|
||||
<replaceable>indexed_column</>
|
||||
<replaceable>operator</>
|
||||
<replaceable>constant</>.
|
||||
Obviously, such an operator must return boolean, and its left-hand input
|
||||
type must match the index's column data type.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
An <quote>ordering</> operator entry indicates that an index of this
|
||||
operator family can be scanned to return rows in the order represented by
|
||||
<literal>ORDER BY</>
|
||||
<replaceable>indexed_column</>
|
||||
<replaceable>operator</>
|
||||
<replaceable>constant</>.
|
||||
Such an operator could return any sortable data type, though again
|
||||
its left-hand input type must match the index's column data type.
|
||||
The exact semantics of the <literal>ORDER BY</> are specified by the
|
||||
<structfield>amopsortfamily</structfield> column, which must reference
|
||||
a btree operator family for the operator's result type.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
At present, it's assumed that the sort order for an ordering operator
|
||||
is the default for the referenced opfamily, i.e., <literal>ASC NULLS
|
||||
LAST</>. This might someday be relaxed by adding additional columns
|
||||
to specify sort options explicitly.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
An entry's <structfield>amopmethod</> must match the
|
||||
<structname>opfmethod</> of its containing operator family (including
|
||||
|
@ -22,7 +22,7 @@ PostgreSQL documentation
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="parameter">index_method</replaceable> ADD
|
||||
{ OPERATOR <replaceable class="parameter">strategy_number</replaceable> <replaceable class="parameter">operator_name</replaceable> ( <replaceable class="parameter">op_type</replaceable>, <replaceable class="parameter">op_type</replaceable> )
|
||||
{ OPERATOR <replaceable class="parameter">strategy_number</replaceable> <replaceable class="parameter">operator_name</replaceable> ( <replaceable class="parameter">op_type</replaceable>, <replaceable class="parameter">op_type</replaceable> ) [ FOR SEARCH | FOR ORDER BY <replaceable class="parameter">sort_family_name</replaceable> ]
|
||||
| FUNCTION <replaceable class="parameter">support_number</replaceable> [ ( <replaceable class="parameter">op_type</replaceable> [ , <replaceable class="parameter">op_type</replaceable> ] ) ] <replaceable class="parameter">function_name</replaceable> ( <replaceable class="parameter">argument_type</replaceable> [, ...] )
|
||||
} [, ... ]
|
||||
ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="parameter">index_method</replaceable> DROP
|
||||
@ -154,6 +154,22 @@ ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="parameter">sort_family_name</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The name (optionally schema-qualified) of an existing btree operator
|
||||
family that describes the sort ordering associated with an ordering
|
||||
operator.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If neither <literal>FOR SEARCH</> nor <literal>FOR ORDER BY</> is
|
||||
specified, <literal>FOR SEARCH</> is the default.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="parameter">support_number</replaceable></term>
|
||||
<listitem>
|
||||
|
@ -23,7 +23,7 @@ PostgreSQL documentation
|
||||
<synopsis>
|
||||
CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAULT ] FOR TYPE <replaceable class="parameter">data_type</replaceable>
|
||||
USING <replaceable class="parameter">index_method</replaceable> [ FAMILY <replaceable class="parameter">family_name</replaceable> ] AS
|
||||
{ OPERATOR <replaceable class="parameter">strategy_number</replaceable> <replaceable class="parameter">operator_name</replaceable> [ ( <replaceable class="parameter">op_type</replaceable>, <replaceable class="parameter">op_type</replaceable> ) ]
|
||||
{ OPERATOR <replaceable class="parameter">strategy_number</replaceable> <replaceable class="parameter">operator_name</replaceable> [ ( <replaceable class="parameter">op_type</replaceable>, <replaceable class="parameter">op_type</replaceable> ) ] [ FOR SEARCH | FOR ORDER BY <replaceable class="parameter">sort_family_name</replaceable> ]
|
||||
| FUNCTION <replaceable class="parameter">support_number</replaceable> [ ( <replaceable class="parameter">op_type</replaceable> [ , <replaceable class="parameter">op_type</replaceable> ] ) ] <replaceable class="parameter">function_name</replaceable> ( <replaceable class="parameter">argument_type</replaceable> [, ...] )
|
||||
| STORAGE <replaceable class="parameter">storage_type</replaceable>
|
||||
} [, ... ]
|
||||
@ -180,6 +180,22 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="parameter">sort_family_name</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The name (optionally schema-qualified) of an existing btree operator
|
||||
family that describes the sort ordering associated with an ordering
|
||||
operator.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If neither <literal>FOR SEARCH</> nor <literal>FOR ORDER BY</> is
|
||||
specified, <literal>FOR SEARCH</> is the default.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="parameter">support_number</replaceable></term>
|
||||
<listitem>
|
||||
|
@ -276,10 +276,13 @@
|
||||
</table>
|
||||
|
||||
<para>
|
||||
Notice that all strategy operators return Boolean values. In
|
||||
practice, all operators defined as index method strategies must
|
||||
Notice that all the operators listed above return Boolean values. In
|
||||
practice, all operators defined as index method search operators must
|
||||
return type <type>boolean</type>, since they must appear at the top
|
||||
level of a <literal>WHERE</> clause to be used with an index.
|
||||
(Some index access methods also support <firstterm>ordering operators</>,
|
||||
which typically don't return Boolean values; that feature is discussed
|
||||
in <xref linkend="xindex-ordering-ops">.)
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@ -464,7 +467,7 @@
|
||||
</table>
|
||||
|
||||
<para>
|
||||
Unlike strategy operators, support functions return whichever data
|
||||
Unlike search operators, support functions return whichever data
|
||||
type the particular index method expects; for example in the case
|
||||
of the comparison function for B-trees, a signed integer. The number
|
||||
and types of the arguments to each support function are likewise
|
||||
@ -921,6 +924,62 @@ ALTER OPERATOR FAMILY integer_ops USING btree ADD
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="xindex-ordering-ops">
|
||||
<title>Ordering Operators</title>
|
||||
|
||||
<para>
|
||||
Some index access methods (currently, only GiST) support the concept of
|
||||
<firstterm>ordering operators</>. What we have been discussing so far
|
||||
are <firstterm>search operators</>. A search operator is one for which
|
||||
the index can be searched to find all rows satisfying
|
||||
<literal>WHERE</>
|
||||
<replaceable>indexed_column</>
|
||||
<replaceable>operator</>
|
||||
<replaceable>constant</>.
|
||||
Note that nothing is promised about the order in which the matching rows
|
||||
will be returned. In contrast, an ordering operator does not restrict the
|
||||
set of rows that can be returned, but instead determines their order.
|
||||
An ordering operator is one for which the index can be scanned to return
|
||||
rows in the order represented by
|
||||
<literal>ORDER BY</>
|
||||
<replaceable>indexed_column</>
|
||||
<replaceable>operator</>
|
||||
<replaceable>constant</>.
|
||||
The reason for defining ordering operators that way is that it supports
|
||||
nearest-neighbor searches, if the operator is one that measures distance.
|
||||
For example, a query like
|
||||
<programlisting><![CDATA[
|
||||
SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
|
||||
]]>
|
||||
</programlisting>
|
||||
finds the ten places closest to a given target point. A GiST index
|
||||
on the location column can do this efficiently because
|
||||
<literal><-></> is an ordering operator.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
While search operators have to return Boolean results, ordering operators
|
||||
usually return some other type, such as float or numeric for distances.
|
||||
This type is normally not the same as the data type being indexed.
|
||||
To avoid hard-wiring assumptions about the behavior of different data
|
||||
types, the definition of an ordering operator is required to name
|
||||
a B-tree operator family that specifies the sort ordering of the result
|
||||
data type. As was stated in the previous section, B-tree operator families
|
||||
define <productname>PostgreSQL</productname>'s notion of ordering, so
|
||||
this is a natural representation. Since the point <literal><-></>
|
||||
operator returns <type>float8</>, it could be specified in an operator
|
||||
class creation command like this:
|
||||
<programlisting><![CDATA[
|
||||
OPERATOR 15 <-> (point, point) FOR ORDER BY float_ops
|
||||
]]>
|
||||
</programlisting>
|
||||
where <literal>float_ops</> is the built-in operator family that includes
|
||||
operations on <type>float8</>. This declaration states that the index
|
||||
is able to return rows in order of increasing values of the
|
||||
<literal><-></> operator.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="xindex-opclass-features">
|
||||
<title>Special Features of Operator Classes</title>
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
/*
|
||||
|
15
src/backend/utils/cache/lsyscache.c
vendored
15
src/backend/utils/cache/lsyscache.c
vendored
@ -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",
|
||||
|
4
src/backend/utils/cache/syscache.c
vendored
4
src/backend/utils/cache/syscache.c
vendored
@ -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
|
||||
|
@ -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");
|
||||
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201011211
|
||||
#define CATALOG_VERSION_NO 201011241
|
||||
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -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
@ -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 */
|
||||
|
@ -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? */
|
||||
|
@ -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
|
||||
------------+---------+-----+---------
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user