/* * getBaseType * If the given type is a domain, return its base type; * otherwise return the type's own OID. */ Oid getBaseType(Oid typid) { int32 typmod = -1; return getBaseTypeAndTypmod(typid, &typmod); }
/* * SendRowDescriptionMessage --- send a RowDescription message to the frontend * * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does * not have a plan. If the targetlist isn't NIL then it is a Query node's * targetlist; it is up to us to ignore resjunk columns in it. The formats[] * array pointer might be NULL (if we are doing Describe on a prepared stmt); * send zeroes for the format codes in that case. */ void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; ListCell *tlist_item = list_head(targetlist); pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { Oid atttypid = attrs[i]->atttypid; int32 atttypmod = attrs[i]->atttypmod; pq_sendstring(&buf, NameStr(attrs[i]->attname)); /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) tlist_item = lnext(tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); pq_sendint(&buf, tle->resorigtbl, 4); pq_sendint(&buf, tle->resorigcol, 2); tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } } /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendint(&buf, (int) atttypid, sizeof(atttypid)); pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, atttypmod, sizeof(atttypmod)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) { if (formats) pq_sendint(&buf, formats[i], 2); else pq_sendint(&buf, 0, 2); } } pq_endmessage(&buf); }
/* * transformArrayType() * Identify the types involved in a subscripting operation * * On entry, arrayType/arrayTypmod identify the type of the input value * to be subscripted (which could be a domain type). These are modified * if necessary to identify the actual array type and typmod, and the * array's element type is returned. An error is thrown if the input isn't * an array type. */ Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod) { Oid origArrayType = *arrayType; Oid elementType; HeapTuple type_tuple_array; Form_pg_type type_struct_array; /* * If the input is a domain, smash to base type, and extract the actual * typmod to be applied to the base type. Subscripting a domain is an * operation that necessarily works on the base array type, not the domain * itself. (Note that we provide no method whereby the creator of a * domain over an array type could hide its ability to be subscripted.) */ *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod); /* * We treat int2vector and oidvector as though they were domains over * int2[] and oid[]. This is needed because array slicing could create an * array that doesn't satisfy the dimensionality constraints of the * xxxvector type; so we want the result of a slice operation to be * considered to be of the more general type. */ if (*arrayType == INT2VECTOROID) *arrayType = INT2ARRAYOID; else if (*arrayType == OIDVECTOROID) *arrayType = OIDARRAYOID; /* Get the type tuple for the array */ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType)); if (!HeapTupleIsValid(type_tuple_array)) elog(ERROR, "cache lookup failed for type %u", *arrayType); type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array); /* needn't check typisdefined since this will fail anyway */ elementType = type_struct_array->typelem; if (elementType == InvalidOid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot subscript type %s because it is not an array", format_type_be(origArrayType)))); ReleaseSysCache(type_tuple_array); return elementType; }
/* * Send description for each column when using v2 protocol */ static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats) { int natts = typeinfo->natts; int i; for (i = 0; i < natts; ++i) { Form_pg_attribute att = TupleDescAttr(typeinfo, i); Oid atttypid = att->atttypid; int32 atttypmod = att->atttypmod; /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendstring(buf, NameStr(att->attname)); /* column ID only info appears in protocol 3.0 and up */ pq_sendint32(buf, atttypid); pq_sendint16(buf, att->attlen); pq_sendint32(buf, atttypmod); /* format info only appears in protocol 3.0 and up */ } }
/* * transformArrayType() * Identify the types involved in a subscripting operation * * On entry, arrayType/arrayTypmod identify the type of the input value * to be subscripted (which could be a domain type). These are modified * if necessary to identify the actual array type and typmod, and the * array's element type is returned. An error is thrown if the input isn't * an array type. */ Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod) { Oid origArrayType = *arrayType; Oid elementType; HeapTuple type_tuple_array; Form_pg_type type_struct_array; /* * If the input is a domain, smash to base type, and extract the actual * typmod to be applied to the base type. Subscripting a domain is an * operation that necessarily works on the base array type, not the domain * itself. (Note that we provide no method whereby the creator of a * domain over an array type could hide its ability to be subscripted.) */ *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod); /* Get the type tuple for the array */ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType)); if (!HeapTupleIsValid(type_tuple_array)) elog(ERROR, "cache lookup failed for type %u", *arrayType); type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array); /* needn't check typisdefined since this will fail anyway */ elementType = type_struct_array->typelem; if (elementType == InvalidOid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot subscript type %s because it is not an array", format_type_be(origArrayType)))); ReleaseSysCache(type_tuple_array); return elementType; }
/* * Send description for each column when using v3+ protocol */ static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats) { int natts = typeinfo->natts; int i; ListCell *tlist_item = list_head(targetlist); /* * Preallocate memory for the entire message to be sent. That allows to * use the significantly faster inline pqformat.h functions and to avoid * reallocations. * * Have to overestimate the size of the column-names, to account for * character set overhead. */ enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */ + sizeof(Oid) /* resorigtbl */ + sizeof(AttrNumber) /* resorigcol */ + sizeof(Oid) /* atttypid */ + sizeof(int16) /* attlen */ + sizeof(int32) /* attypmod */ + sizeof(int16) /* format */ ) * natts); for (i = 0; i < natts; ++i) { Form_pg_attribute att = TupleDescAttr(typeinfo, i); Oid atttypid = att->atttypid; int32 atttypmod = att->atttypmod; Oid resorigtbl; AttrNumber resorigcol; int16 format; /* * If column is a domain, send the base type and typmod instead. * Lookup before sending any ints, for efficiency. */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) tlist_item = lnext(tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); resorigtbl = tle->resorigtbl; resorigcol = tle->resorigcol; tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ resorigtbl = 0; resorigcol = 0; } if (formats) format = formats[i]; else format = 0; pq_writestring(buf, NameStr(att->attname)); pq_writeint32(buf, resorigtbl); pq_writeint16(buf, resorigcol); pq_writeint32(buf, atttypid); pq_writeint16(buf, att->attlen); pq_writeint32(buf, atttypmod); pq_writeint16(buf, format); } }
/* * SendRowDescriptionMessage --- send a RowDescription message to the frontend * * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does * not have a plan. If the targetlist isn't NIL then it is a Query node's * targetlist; it is up to us to ignore resjunk columns in it. The formats[] * array pointer might be NULL (if we are doing Describe on a prepared stmt); * send zeroes for the format codes in that case. */ void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; ListCell *tlist_item = list_head(targetlist); pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { Oid atttypid = attrs[i]->atttypid; int32 atttypmod = attrs[i]->atttypmod; pq_sendstring(&buf, NameStr(attrs[i]->attname)); #ifdef PGXC /* * Send the type name from a Postgres-XC backend node. * This preserves from OID inconsistencies as architecture is shared nothing. */ if (IsConnFromCoord()) { char *typenamespace = NULL; char *typename; Oid nspid; nspid = get_typenamespace(atttypid); if (OidIsValid(nspid) && nspid != PG_CATALOG_NAMESPACE) { typenamespace = get_namespace_name(nspid); } typename = get_typename(atttypid); pq_sendstring(&buf, quote_qualified_identifier(typenamespace, typename)); } #endif /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) tlist_item = lnext(tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); pq_sendint(&buf, tle->resorigtbl, 4); pq_sendint(&buf, tle->resorigcol, 2); tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } } /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendint(&buf, (int) atttypid, sizeof(atttypid)); pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, atttypmod, sizeof(atttypmod)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) { if (formats) pq_sendint(&buf, formats[i], 2); else pq_sendint(&buf, 0, 2); } } pq_endmessage(&buf); }