postgresql/src/backend/parser/parse_type.c
Neil Conway d0b4399d81 Reimplement the linked list data structure used throughout the backend.
In the past, we used a 'Lispy' linked list implementation: a "list" was
merely a pointer to the head node of the list. The problem with that
design is that it makes lappend() and length() linear time. This patch
fixes that problem (and others) by maintaining a count of the list
length and a pointer to the tail node along with each head node pointer.
A "list" is now a pointer to a structure containing some meta-data
about the list; the head and tail pointers in that structure refer
to ListCell structures that maintain the actual linked list of nodes.

The function names of the list API have also been changed to, I hope,
be more logically consistent. By default, the old function names are
still available; they will be disabled-by-default once the rest of
the tree has been updated to use the new API names.
2004-05-26 04:41:50 +00:00

537 lines
13 KiB
C

/*-------------------------------------------------------------------------
*
* parse_type.c
* handle type operations for parser
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.66 2004/05/26 04:41:30 neilc Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/parsenodes.h"
#include "parser/parser.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/*
* LookupTypeName
* Given a TypeName object, get the OID of the referenced type.
* Returns InvalidOid if no such type can be found.
*
* NB: even if the returned OID is not InvalidOid, the type might be
* just a shell. Caller should check typisdefined before using the type.
*/
Oid
LookupTypeName(const TypeName *typename)
{
Oid restype;
/* Easy if it's an internally generated TypeName */
if (typename->names == NIL)
return typename->typeid;
if (typename->pct_type)
{
/* Handle %TYPE reference to type of an existing field */
RangeVar *rel = makeRangeVar(NULL, NULL);
char *field = NULL;
Oid relid;
AttrNumber attnum;
/* deconstruct the name list */
switch (length(typename->names))
{
case 1:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper %%TYPE reference (too few dotted names): %s",
NameListToString(typename->names))));
break;
case 2:
rel->relname = strVal(linitial(typename->names));
field = strVal(lsecond(typename->names));
break;
case 3:
rel->schemaname = strVal(linitial(typename->names));
rel->relname = strVal(lsecond(typename->names));
field = strVal(lthird(typename->names));
break;
case 4:
rel->catalogname = strVal(linitial(typename->names));
rel->schemaname = strVal(lsecond(typename->names));
rel->relname = strVal(lthird(typename->names));
field = strVal(lfourth(typename->names));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper %%TYPE reference (too many dotted names): %s",
NameListToString(typename->names))));
break;
}
/* look up the field */
relid = RangeVarGetRelid(rel, false);
attnum = get_attnum(relid, field);
if (attnum == InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
field, rel->relname)));
restype = get_atttype(relid, attnum);
/* this construct should never have an array indicator */
Assert(typename->arrayBounds == NIL);
/* emit nuisance notice */
ereport(NOTICE,
(errmsg("type reference %s converted to %s",
TypeNameToString(typename),
format_type_be(restype))));
}
else
{
/* Normal reference to a type name */
char *schemaname;
char *typname;
/* deconstruct the name list */
DeconstructQualifiedName(typename->names, &schemaname, &typname);
/* If an array reference, look up the array type instead */
if (typename->arrayBounds != NIL)
typname = makeArrayTypeName(typname);
if (schemaname)
{
/* Look in specific schema only */
Oid namespaceId;
namespaceId = LookupExplicitNamespace(schemaname);
restype = GetSysCacheOid(TYPENAMENSP,
PointerGetDatum(typname),
ObjectIdGetDatum(namespaceId),
0, 0);
}
else
{
/* Unqualified type name, so search the search path */
restype = TypenameGetTypid(typname);
}
}
return restype;
}
/*
* TypeNameToString
* Produce a string representing the name of a TypeName.
*
* NB: this must work on TypeNames that do not describe any actual type;
* it is mostly used for reporting lookup errors.
*/
char *
TypeNameToString(const TypeName *typename)
{
StringInfoData string;
initStringInfo(&string);
if (typename->names != NIL)
{
/* Emit possibly-qualified name as-is */
ListCell *l;
foreach(l, typename->names)
{
if (l != list_head(typename->names))
appendStringInfoChar(&string, '.');
appendStringInfoString(&string, strVal(lfirst(l)));
}
}
else
{
/* Look up internally-specified type */
appendStringInfoString(&string, format_type_be(typename->typeid));
}
/*
* Add decoration as needed, but only for fields considered by
* LookupTypeName
*/
if (typename->pct_type)
appendStringInfoString(&string, "%TYPE");
if (typename->arrayBounds != NIL)
appendStringInfoString(&string, "[]");
return string.data;
}
/*
* typenameTypeId - given a TypeName, return the type's OID
*
* This is equivalent to LookupTypeName, except that this will report
* a suitable error message if the type cannot be found or is not defined.
*/
Oid
typenameTypeId(const TypeName *typename)
{
Oid typoid;
typoid = LookupTypeName(typename);
if (!OidIsValid(typoid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" does not exist",
TypeNameToString(typename))));
if (!get_typisdefined(typoid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" is only a shell",
TypeNameToString(typename))));
return typoid;
}
/*
* typenameType - given a TypeName, return a Type structure
*
* This is equivalent to typenameTypeId + syscache fetch of Type tuple.
* NB: caller must ReleaseSysCache the type tuple when done with it.
*/
Type
typenameType(const TypeName *typename)
{
Oid typoid;
HeapTuple tup;
typoid = LookupTypeName(typename);
if (!OidIsValid(typoid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" does not exist",
TypeNameToString(typename))));
tup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typoid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for type %u", typoid);
if (!((Form_pg_type) GETSTRUCT(tup))->typisdefined)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" is only a shell",
TypeNameToString(typename))));
return (Type) tup;
}
/* check to see if a type id is valid, returns true if it is */
bool
typeidIsValid(Oid id)
{
return SearchSysCacheExists(TYPEOID,
ObjectIdGetDatum(id),
0, 0, 0);
}
/* return a Type structure, given a type id */
/* NB: caller must ReleaseSysCache the type tuple when done with it */
Type
typeidType(Oid id)
{
HeapTuple tup;
tup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(id),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for type %u", id);
return (Type) tup;
}
/* given type (as type struct), return the type OID */
Oid
typeTypeId(Type tp)
{
if (tp == NULL) /* probably useless */
elog(ERROR, "typeTypeId() called with NULL type struct");
return HeapTupleGetOid(tp);
}
/* given type (as type struct), return the length of type */
int16
typeLen(Type t)
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
return typ->typlen;
}
/* given type (as type struct), return the value of its 'byval' attribute.*/
bool
typeByVal(Type t)
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
return typ->typbyval;
}
/* given type (as type struct), return the value of its 'typtype' attribute.*/
char
typeTypType(Type t)
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
return typ->typtype;
}
/* given type (as type struct), return the name of type */
char *
typeTypeName(Type t)
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
/* pstrdup here because result may need to outlive the syscache entry */
return pstrdup(NameStr(typ->typname));
}
/* given a type, return its typetype ('c' for 'c'atalog types) */
char
typeTypeFlag(Type t)
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
return typ->typtype;
}
Oid
typeTypeRelid(Type typ)
{
Form_pg_type typtup;
typtup = (Form_pg_type) GETSTRUCT(typ);
return typtup->typrelid;
}
#ifdef NOT_USED
Oid
typeTypElem(Type typ)
{
Form_pg_type typtup;
typtup = (Form_pg_type) GETSTRUCT(typ);
return typtup->typelem;
}
#endif
#ifdef NOT_USED
/* Given a type structure, return the in-conversion function of the type */
Oid
typeInfunc(Type typ)
{
Form_pg_type typtup;
typtup = (Form_pg_type) GETSTRUCT(typ);
return typtup->typinput;
}
#endif
#ifdef NOT_USED
/* Given a type structure, return the out-conversion function of the type */
Oid
typeOutfunc(Type typ)
{
Form_pg_type typtup;
typtup = (Form_pg_type) GETSTRUCT(typ);
return typtup->typoutput;
}
#endif
/* Given a type structure and a string, returns the internal form of
that string */
Datum
stringTypeDatum(Type tp, char *string, int32 atttypmod)
{
Oid op;
Oid typelem;
op = ((Form_pg_type) GETSTRUCT(tp))->typinput;
typelem = ((Form_pg_type) GETSTRUCT(tp))->typelem; /* XXX - used for
* array_in */
return OidFunctionCall3(op,
CStringGetDatum(string),
ObjectIdGetDatum(typelem),
Int32GetDatum(atttypmod));
}
/* Given a type id, returns the out-conversion function of the type */
#ifdef NOT_USED
Oid
typeidOutfunc(Oid type_id)
{
HeapTuple typeTuple;
Form_pg_type type;
Oid outfunc;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(type_id),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", type_id);
type = (Form_pg_type) GETSTRUCT(typeTuple);
outfunc = type->typoutput;
ReleaseSysCache(typeTuple);
return outfunc;
}
#endif
/* given a typeid, return the type's typrelid (associated relation, if any) */
Oid
typeidTypeRelid(Oid type_id)
{
HeapTuple typeTuple;
Form_pg_type type;
Oid result;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(type_id),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", type_id);
type = (Form_pg_type) GETSTRUCT(typeTuple);
result = type->typrelid;
ReleaseSysCache(typeTuple);
return result;
}
/*
* error context callback for parse failure during parseTypeString()
*/
static void
pts_error_callback(void *arg)
{
const char *str = (const char *) arg;
errcontext("invalid type name \"%s\"", str);
/*
* Currently we just suppress any syntax error position report,
* rather than transforming to an "internal query" error. It's
* unlikely that a type name is complex enough to need positioning.
*/
errposition(0);
}
/*
* Given a string that is supposed to be a SQL-compatible type declaration,
* such as "int4" or "integer" or "character varying(32)", parse
* the string and convert it to a type OID and type modifier.
*/
void
parseTypeString(const char *str, Oid *type_id, int32 *typmod)
{
StringInfoData buf;
List *raw_parsetree_list;
SelectStmt *stmt;
ResTarget *restarget;
TypeCast *typecast;
TypeName *typename;
ErrorContextCallback ptserrcontext;
/* make sure we give useful error for empty input */
if (strspn(str, " \t\n\r\f") == strlen(str))
goto fail;
initStringInfo(&buf);
appendStringInfo(&buf, "SELECT NULL::%s", str);
/*
* Setup error traceback support in case of ereport() during parse
*/
ptserrcontext.callback = pts_error_callback;
ptserrcontext.arg = (void *) str;
ptserrcontext.previous = error_context_stack;
error_context_stack = &ptserrcontext;
raw_parsetree_list = raw_parser(buf.data);
error_context_stack = ptserrcontext.previous;
/*
* Make sure we got back exactly what we expected and no more;
* paranoia is justified since the string might contain anything.
*/
if (length(raw_parsetree_list) != 1)
goto fail;
stmt = (SelectStmt *) linitial(raw_parsetree_list);
if (stmt == NULL ||
!IsA(stmt, SelectStmt) ||
stmt->distinctClause != NIL ||
stmt->into != NULL ||
stmt->fromClause != NIL ||
stmt->whereClause != NULL ||
stmt->groupClause != NIL ||
stmt->havingClause != NULL ||
stmt->sortClause != NIL ||
stmt->limitOffset != NULL ||
stmt->limitCount != NULL ||
stmt->forUpdate != NIL ||
stmt->op != SETOP_NONE)
goto fail;
if (length(stmt->targetList) != 1)
goto fail;
restarget = (ResTarget *) linitial(stmt->targetList);
if (restarget == NULL ||
!IsA(restarget, ResTarget) ||
restarget->name != NULL ||
restarget->indirection != NIL)
goto fail;
typecast = (TypeCast *) restarget->val;
if (typecast == NULL ||
!IsA(typecast, TypeCast) ||
typecast->arg == NULL ||
!IsA(typecast->arg, A_Const))
goto fail;
typename = typecast->typename;
if (typename == NULL ||
!IsA(typename, TypeName))
goto fail;
*type_id = typenameTypeId(typename);
*typmod = typename->typmod;
pfree(buf.data);
return;
fail:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid type name \"%s\"", str)));
}