RelableType是PG中的一个节点类型,如下所示
test=# \d poly100w
Table "sde.poly100w"
Column | Type | Collation | Nullable | Default
-------------------+-----------------------+-----------+----------+-------------------------------------------------------------
objectid | integer | | not null |
globalid | character varying(38) | | not null | '{00000000-0000-0000-0000-000000000000}'::character varying
gdb_geomattr_data | bytea | | |
shape | geometry | | |
Indexes:
"a2_ix1" gist (shape)
"poly100w_upper_idx" btree (upper(globalid::text))
"r13_sde_rowid_uk" UNIQUE, btree (objectid) WITH (fillfactor='75')
"uuid_13" UNIQUE, btree (globalid) WITH (fillfactor='75')
Check constraints:
"enforce_srid_shape" CHECK (st_srid(shape) = 3857)
//globalid的类型为varchar,但是执行计划op两边都转换成text,再进行比较,这时候坐标的node就会包装为一个RelableType的节点,而右表就是个Const节点
test=# explain select length(st_asewkt(shape)) from poly100w where globalid='{077559D3-DDAA-4328-980A-FB4980927B20}';
QUERY PLAN
-----------------------------------------------------------------------------------
Index Scan using uuid_13 on poly100w (cost=0.43..9.07 rows=1 width=4)
Index Cond: ((globalid)::text = '{077559D3-DDAA-4328-980A-FB4980927B20}'::text)
(2 rows)
这部分内容是在make_op函数中实现
/*
* make_op()
* Operator expression construction.
*
* Transform operator expression ensuring type compatibility.
* This is where some type conversion happens.
*
* last_srf should be a copy of pstate->p_last_srf from just before we
* started transforming the operator's arguments; this is used for nested-SRF
* detection. If the caller will throw an error anyway for a set-returning
* expression, it's okay to cheat and just pass pstate->p_last_srf.
*/
/*
ltree:
p *(Var*)ltree
$4 = {xpr = {type = T_Var}, varno = 1, varattno = 2, vartype = 1043//varchar, vartypmod = 42, varcollid = 100, varnullingrels = 0x0,
varlevelsup = 0, varnosyn = 1, varattnosyn = 2, location = 52}
rtree
p *(Var*)rtree
$8 = {xpr = {type = T_Const}, varno = 705//unknown, varattno = -1, vartype = 0, vartypmod = -2, varcollid = 0, varnullingrels = 0x16c6d98,
varlevelsup = 0, varnosyn = 61, varattnosyn = 28719, location = 778134120}
*/
Expr *
make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
Node *last_srf, int location)
{
Oid ltypeId,
rtypeId;
Operator tup;
Form_pg_operator opform;
Oid actual_arg_types[2];
Oid declared_arg_types[2];
int nargs;
List *args;
Oid rettype;
OpExpr *result;
if (ltree == NULL)
{
/* prefix operator */
rtypeId = exprType(rtree);
ltypeId = InvalidOid;
tup = left_oper(pstate, opname, rtypeId, false, location);
}
else
{
/* otherwise, binary operator */
ltypeId = exprType(ltree); //1043
rtypeId = exprType(rtree); /unknown
//oper函数是具体匹配类型的函数,具体代码见代码2
tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
}
代码2: oper
/* oper() -- search for a binary operator
* Given operator name, types of arg1 and arg2, return oper struct.
*
* IMPORTANT: the returned operator (if any) is only promised to be
* coercion-compatible with the input datatypes. Do not use this if
* you need an exact- or binary-compatible match; see compatible_oper.
*
* If no matching operator found, return NULL if noError is true,
* raise an error if it is false. pstate and location are used only to report
* the error position; pass NULL/-1 if not available.
*
* NOTE: on success, the returned object is a syscache entry. The caller
* must ReleaseSysCache() the entry when done with it.
*/
Operator
oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
bool noError, int location)
{
Oid operOid;
OprCacheKey key;
bool key_ok;
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
HeapTuple tup = NULL;
/*
* Try to find the mapping in the lookaside cache.
*/
//根据opname,ltypeid,rtypeid构造key,该例子构造出来的key为
/*p *key
$4 = {oprname = "=", '\000' <repeats 62 times>, left_arg = 1043, right_arg = 705, search_path = {11, 6787818, 2200, 0 <repeats 13 times>}}*/
key_ok = make_oper_cache_key(pstate, &key, opname, ltypeId, rtypeId, location);
if (key_ok)
{
//根据key到cache中查找,该cache为Operator lookup cache,从TopMemoryContext中开辟
operOid = find_oper_cache_entry(&key);
if (OidIsValid(operOid))
{
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
if (HeapTupleIsValid(tup))
return (Operator) tup;
}
}
/*
* First try for an "exact" match.
*/
//精确匹配下,看看能不能匹配到,具体见代码3,由于1043在pg_operator中没有记录,所以operOid为0
operOid = binary_oper_exact(opname, ltypeId, rtypeId);
if (!OidIsValid(operOid))
{
/*
* Otherwise, search for the most suitable candidate.
*/
FuncCandidateList clist;
/* Get binary operators of given name */
// 只根据opname到pg_operator中进行模糊查询
// 具体看代码4,搜索出很多候选的,此例中是64个
// select * from pg_operator where oprname='='
clist = OpernameGetCandidates(opname, 'b', false);
/* No operators found? Then fail... */
if (clist != NULL)
{
/*
* Unspecified type for one of the arguments? then use the other
* (XXX this is probably dead code?)
*/
Oid inputOids[2];
if (rtypeId == InvalidOid)
rtypeId = ltypeId;
else if (ltypeId == InvalidOid)
ltypeId = rtypeId;
//ltypeId:1043&&rtypeId:705
inputOids[0] = ltypeId;
inputOids[1] = rtypeId;
//从clist里面选出一个匹配的第一条记录,具体查看代码5部分
fdresult = oper_select_candidate(2, inputOids, clist, &operOid);
}
}
if (OidIsValid(operOid))
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
if (HeapTupleIsValid(tup))
{
if (key_ok)
make_oper_cache_entry(&key, operOid);
}
else if (!noError)
op_error(pstate, opname, ltypeId, rtypeId, fdresult, location);
return (Operator) tup;
}
代码3: binary_oper_exact
/* binary_oper_exact()
* Check for an "exact" match to the specified operand types.
*
* If one operand is an unknown literal, assume it should be taken to be
* the same type as the other operand for this purpose. Also, consider
* the possibility that the other operand is a domain type that needs to
* be reduced to its base type to find an "exact" match.
*/
static Oid
binary_oper_exact(List *opname, Oid arg1, Oid arg2)
{
Oid result;
bool was_unknown = false;
/* Unspecified type for one of the arguments? then use the other */
//此例子中arg2为705也就是UNKNOWNOID
if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid))
{
arg1 = arg2;
was_unknown = true;
}
//进入这个分支 arg2==arg1==1043(varchar)
else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid))
{
arg2 = arg1;
was_unknown = true;
}
//根据opname,arg1,arg2到pg_operator中查找
// select * from pg_operator where oprname='=' and oprleft=1043 and oprright=1043
// 1043没有记录,因此此例子result为0
result = OpernameGetOprid(opname, arg1, arg2);
if (OidIsValid(result))
return result;
if (was_unknown)
{
/* arg1 and arg2 are the same here, need only look at arg1 */
Oid basetype = getBaseType(arg1);
if (basetype != arg1)
{
result = OpernameGetOprid(opname, basetype, basetype);
if (OidIsValid(result))
return result;
}
}
return InvalidOid;
}
代码4: OpernameGetCandidates
/*
* OpernameGetCandidates
* Given a possibly-qualified operator name and operator kind,
* retrieve a list of the possible matches.
*
* If oprkind is '\0', we return all operators matching the given name,
* regardless of arguments.
*
* We search a single namespace if the operator name is qualified, else
* all namespaces in the search path. The return list will never contain
* multiple entries with identical argument lists --- in the multiple-
* namespace case, we arrange for entries in earlier namespaces to mask
* identical entries in later namespaces.
*
* The returned items always have two args[] entries --- the first will be
* InvalidOid for a prefix oprkind. nargs is always 2, too.
*/
FuncCandidateList
OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
{
FuncCandidateList resultList = NULL;
char *resultSpace = NULL;
int nextResult = 0;
char *schemaname;
char *opername;
Oid namespaceId;
CatCList *catlist;
int i;
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &opername);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
if (missing_schema_ok && !OidIsValid(namespaceId))
return NULL;
}
else
{
/* flag to indicate we need namespace search */
namespaceId = InvalidOid;
recomputeNamespacePath();
}
/* Search syscache by name only */
catlist = SearchSysCacheList1(OPERNAMENSP, CStringGetDatum(opername));
/*
* In typical scenarios, most if not all of the operators found by the
* catcache search will end up getting returned; and there can be quite a
* few, for common operator names such as '=' or '+'. To reduce the time
* spent in palloc, we allocate the result space as an array large enough
* to hold all the operators. The original coding of this routine did a
* separate palloc for each operator, but profiling revealed that the
* pallocs used an unreasonably large fraction of parsing time.
*/
#define SPACE_PER_OP MAXALIGN(offsetof(struct _FuncCandidateList, args) + \
2 * sizeof(Oid))
if (catlist->n_members > 0)
resultSpace = palloc(catlist->n_members * SPACE_PER_OP);
for (i = 0; i < catlist->n_members; i++)
{
HeapTuple opertup = &catlist->members[i]->tuple;
Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
int pathpos = 0;
FuncCandidateList newResult;
/* Ignore operators of wrong kind, if specific kind requested */
if (oprkind && operform->oprkind != oprkind)
continue;
if (OidIsValid(namespaceId))
{
/* Consider only opers in specified namespace */
if (operform->oprnamespace != namespaceId)
continue;
/* No need to check args, they must all be different */
}
else
{
/*
* Consider only opers that are in the search path and are not in
* the temp namespace.
*/
ListCell *nsp;
foreach(nsp, activeSearchPath)
{
if (operform->oprnamespace == lfirst_oid(nsp) &&
operform->oprnamespace != myTempNamespace)
break;
pathpos++;
}
if (nsp == NULL)
continue; /* oper is not in search path */
/*
* Okay, it's in the search path, but does it have the same
* arguments as something we already accepted? If so, keep only
* the one that appears earlier in the search path.
*
* If we have an ordered list from SearchSysCacheList (the normal
* case), then any conflicting oper must immediately adjoin this
* one in the list, so we only need to look at the newest result
* item. If we have an unordered list, we have to scan the whole
* result list.
*/
if (resultList)
{
FuncCandidateList prevResult;
if (catlist->ordered)
{
if (operform->oprleft == resultList->args[0] &&
operform->oprright == resultList->args[1])
prevResult = resultList;
else
prevResult = NULL;
}
else
{
for (prevResult = resultList;
prevResult;
prevResult = prevResult->next)
{
if (operform->oprleft == prevResult->args[0] &&
operform->oprright == prevResult->args[1])
break;
}
}
if (prevResult)
{
/* We have a match with a previous result */
Assert(pathpos != prevResult->pathpos);
if (pathpos > prevResult->pathpos)
continue; /* keep previous result */
/* replace previous result */
prevResult->pathpos = pathpos;
prevResult->oid = operform->oid;
continue; /* args are same, of course */
}
}
}
/*
* Okay to add it to result list
*/
newResult = (FuncCandidateList) (resultSpace + nextResult);
nextResult += SPACE_PER_OP;
newResult->pathpos = pathpos;
newResult->oid = operform->oid;
newResult->nominalnargs = 2;
newResult->nargs = 2;
newResult->nvargs = 0;
newResult->ndargs = 0;
newResult->argnumbers = NULL;
newResult->args[0] = operform->oprleft;
newResult->args[1] = operform->oprright;
newResult->next = resultList;
resultList = newResult;
}
ReleaseSysCacheList(catlist);
return resultList;
}
代码5: oper_select_candidate
/* oper_select_candidate()
* Given the input argtype array and one or more candidates
* for the operator, attempt to resolve the conflict.
*
* Returns FUNCDETAIL_NOTFOUND, FUNCDETAIL_MULTIPLE, or FUNCDETAIL_NORMAL.
* In the success case the Oid of the best candidate is stored in *operOid.
*
* Note that the caller has already determined that there is no candidate
* exactly matching the input argtype(s). Incompatible candidates are not yet
* pruned away, however.
*/
static FuncDetailCode
oper_select_candidate(int nargs,
Oid *input_typeids,
FuncCandidateList candidates,
Oid *operOid) /* output argument */
{
int ncandidates;
/*
* Delete any candidates that cannot actually accept the given input
* types, whether directly or by coercion.
*/
//根据类型匹配
ncandidates = func_match_argtypes(nargs, input_typeids,
candidates, &candidates);
/* Done if no candidate or only one candidate survives */
if (ncandidates == 0)
{
*operOid = InvalidOid;
return FUNCDETAIL_NOTFOUND;
}
if (ncandidates == 1)
{
*operOid = candidates->oid;
return FUNCDETAIL_NORMAL;
}
/*
* Use the same heuristics as for ambiguous functions to resolve the
* conflict.
*/
candidates = func_select_candidate(nargs, input_typeids, candidates);
if (candidates)
{
*operOid = candidates->oid;
return FUNCDETAIL_NORMAL;
}
*operOid = InvalidOid;
return FUNCDETAIL_MULTIPLE; /* failed to select a best candidate */
}
/* func_match_argtypes()
*
* Given a list of candidate functions (having the right name and number
* of arguments) and an array of input datatype OIDs, produce a shortlist of
* those candidates that actually accept the input datatypes (either exactly
* or by coercion), and return the number of such candidates.
*
* Note that can_coerce_type will assume that UNKNOWN inputs are coercible to
* anything, so candidates will not be eliminated on that basis.
*
* NB: okay to modify input list structure, as long as we find at least
* one match. If no match at all, the list must remain unmodified.
*/
int
func_match_argtypes(int nargs,
Oid *input_typeids,
FuncCandidateList raw_candidates,
FuncCandidateList *candidates) /* return value */
{
FuncCandidateList current_candidate;
FuncCandidateList next_candidate;
int ncandidates = 0;
*candidates = NULL;
for (current_candidate = raw_candidates;
current_candidate != NULL;
current_candidate = next_candidate)
{
//循环遍历,根据类型进行匹配
next_candidate = current_candidate->next;
if (can_coerce_type(nargs, input_typeids, current_candidate->args,
COERCION_IMPLICIT))
{
current_candidate->next = *candidates;
*candidates = current_candidate;
ncandidates++;
}
}
return ncandidates;
}
/*
* can_coerce_type()
* Can input_typeids be coerced to target_typeids?
*
* We must be told the context (CAST construct, assignment, implicit coercion)
* as this determines the set of available casts.
*/
bool
can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids,
CoercionContext ccontext)
{
bool have_generics = false;
int i;
/* run through argument list... */
for (i = 0; i < nargs; i++)
{
Oid inputTypeId = input_typeids[i];
Oid targetTypeId = target_typeids[i];
CoercionPathType pathtype;
Oid funcId;
/* no problem if same type */
if (inputTypeId == targetTypeId)
continue;
/* accept if target is ANY */
if (targetTypeId == ANYOID)
continue;
/* accept if target is polymorphic, for now */
if (IsPolymorphicType(targetTypeId))
{
have_generics = true; /* do more checking later */
continue;
}
/*
* If input is an untyped string constant, assume we can convert it to
* anything.
*/
if (inputTypeId == UNKNOWNOID)
continue;
/*
* If pg_cast shows that we can coerce, accept. This test now covers
* both binary-compatible and coercion-function cases.
*/
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
if (pathtype != COERCION_PATH_NONE)
continue;
/*
* If input is RECORD and target is a composite type, assume we can
* coerce (may need tighter checking here)
*/
if (inputTypeId == RECORDOID &&
ISCOMPLEX(targetTypeId))
continue;
/*
* If input is a composite type and target is RECORD, accept
*/
if (targetTypeId == RECORDOID &&
ISCOMPLEX(inputTypeId))
continue;
#ifdef NOT_USED /* not implemented yet */
/*
* If input is record[] and target is a composite array type, assume
* we can coerce (may need tighter checking here)
*/
if (inputTypeId == RECORDARRAYOID &&
is_complex_array(targetTypeId))
continue;
#endif
/*
* If input is a composite array type and target is record[], accept
*/
if (targetTypeId == RECORDARRAYOID &&
is_complex_array(inputTypeId))
continue;
/*
* If input is a class type that inherits from target, accept
*/
if (typeInheritsFrom(inputTypeId, targetTypeId)
|| typeIsOfTypedTable(inputTypeId, targetTypeId))
continue;
/*
* Else, cannot coerce at this argument position
*/
return false;
}
/* If we found any generic argument types, cross-check them */
if (have_generics)
{
if (!check_generic_type_consistency(input_typeids, target_typeids,
nargs))
return false;
}
return true;
}
/*
* find_coercion_pathway
* Look for a coercion pathway between two types.
*
* Currently, this deals only with scalar-type cases; it does not consider
* polymorphic types nor casts between composite types. (Perhaps fold
* those in someday?)
*
* ccontext determines the set of available casts.
*
* The possible result codes are:
* COERCION_PATH_NONE: failed to find any coercion pathway
* *funcid is set to InvalidOid
* COERCION_PATH_FUNC: apply the coercion function returned in *funcid
* COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed
* *funcid is set to InvalidOid
* COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node
* *funcid is set to InvalidOid
* COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node
* *funcid is set to InvalidOid
*
* Note: COERCION_PATH_RELABELTYPE does not necessarily mean that no work is
* needed to do the coercion; if the target is a domain then we may need to
* apply domain constraint checking. If you want to check for a zero-effort
* conversion then use IsBinaryCoercible().
*/
CoercionPathType
find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
CoercionContext ccontext,
Oid *funcid)
{
CoercionPathType result = COERCION_PATH_NONE;
HeapTuple tuple;
*funcid = InvalidOid;
/* Perhaps the types are domains; if so, look at their base types */
if (OidIsValid(sourceTypeId))
sourceTypeId = getBaseType(sourceTypeId);
if (OidIsValid(targetTypeId))
targetTypeId = getBaseType(targetTypeId);
/* Domains are always coercible to and from their base type */
if (sourceTypeId == targetTypeId)
return COERCION_PATH_RELABELTYPE;
/* Look in pg_cast */
tuple = SearchSysCache2(CASTSOURCETARGET,
ObjectIdGetDatum(sourceTypeId),
ObjectIdGetDatum(targetTypeId));
if (HeapTupleIsValid(tuple))
{
Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
CoercionContext castcontext;
/* convert char value for castcontext to CoercionContext enum */
switch (castForm->castcontext)
{
case COERCION_CODE_IMPLICIT:
castcontext = COERCION_IMPLICIT;
break;
case COERCION_CODE_ASSIGNMENT:
castcontext = COERCION_ASSIGNMENT;
break;
case COERCION_CODE_EXPLICIT:
castcontext = COERCION_EXPLICIT;
break;
default:
elog(ERROR, "unrecognized castcontext: %d",
(int) castForm->castcontext);
castcontext = 0; /* keep compiler quiet */
break;
}
/* Rely on ordering of enum for correct behavior here */
if (ccontext >= castcontext)
{
switch (castForm->castmethod)
{
case COERCION_METHOD_FUNCTION:
result = COERCION_PATH_FUNC;
*funcid = castForm->castfunc;
break;
case COERCION_METHOD_INOUT:
result = COERCION_PATH_COERCEVIAIO;
break;
case COERCION_METHOD_BINARY:
result = COERCION_PATH_RELABELTYPE;
break;
default:
elog(ERROR, "unrecognized castmethod: %d",
(int) castForm->castmethod);
break;
}
}
ReleaseSysCache(tuple);
}
else
{
/*
* If there's no pg_cast entry, perhaps we are dealing with a pair of
* array types. If so, and if their element types have a conversion
* pathway, report that we can coerce with an ArrayCoerceExpr.
*
* Hack: disallow coercions to oidvector and int2vector, which
* otherwise tend to capture coercions that should go to "real" array
* types. We want those types to be considered "real" arrays for many
* purposes, but not this one. (Also, ArrayCoerceExpr isn't
* guaranteed to produce an output that meets the restrictions of
* these datatypes, such as being 1-dimensional.)
*/
if (targetTypeId != OIDVECTOROID && targetTypeId != INT2VECTOROID)
{
Oid targetElem;
Oid sourceElem;
if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
(sourceElem = get_element_type(sourceTypeId)) != InvalidOid)
{
CoercionPathType elempathtype;
Oid elemfuncid;
elempathtype = find_coercion_pathway(targetElem,
sourceElem,
ccontext,
&elemfuncid);
if (elempathtype != COERCION_PATH_NONE)
{
result = COERCION_PATH_ARRAYCOERCE;
}
}
}
/*
* If we still haven't found a possibility, consider automatic casting
* using I/O functions. We allow assignment casts to string types and
* explicit casts from string types to be handled this way. (The
* CoerceViaIO mechanism is a lot more general than that, but this is
* all we want to allow in the absence of a pg_cast entry.) It would
* probably be better to insist on explicit casts in both directions,
* but this is a compromise to preserve something of the pre-8.3
* behavior that many types had implicit (yipes!) casts to text.
*/
if (result == COERCION_PATH_NONE)
{
if (ccontext >= COERCION_ASSIGNMENT &&
TypeCategory(targetTypeId) == TYPCATEGORY_STRING)
result = COERCION_PATH_COERCEVIAIO;
else if (ccontext >= COERCION_EXPLICIT &&
TypeCategory(sourceTypeId) == TYPCATEGORY_STRING)
result = COERCION_PATH_COERCEVIAIO;
}
}
/*
* When parsing PL/pgSQL assignments, allow an I/O cast to be used
* whenever no normal coercion is available.
*/
if (result == COERCION_PATH_NONE &&
ccontext == COERCION_PLPGSQL)
result = COERCION_PATH_COERCEVIAIO;
return result;
}
最终候选出多个满足条件的类型,比如char, name ,text类型,后续还需要根据
pg_type的
typcategory
Code | Category |
---|---|
A |
Array types |
B |
Boolean types |
C |
Composite types |
D |
Date/time types |
E |
Enum types |
G |
Geometric types |
I |
Network address types |
N |
Numeric types |
P |
Pseudo-types |
R |
Range types |
S |
String types |
T |
Timespan types |
U |
User-defined types |
V |
Bit-string types |
X |
unknown type |
Z |
Internal-use types |
typispreferred
属于一个大类并且typispreferred为t的优先被匹配
如text和varchar同属于‘S'并且typisprefered='t' 因此被匹配上了