提交 25cd73c6 编写于 作者: H Haozhou Wang

Fix the PR #518: arrays of composite types

- unify source-code of #518 commit according to upstream
- add regression tests (previously stored in TINC)
- fix parameter type of the function AddNewRelationType()

    Thanks to Heikki Linnakangas for suggesting
上级 88e9baba
......@@ -105,8 +105,8 @@ static Oid AddNewRelationType(const char *typeName,
Oid typeNamespace,
Oid new_rel_oid,
char new_rel_kind,
char new_array_type,
Oid ownerid);
Oid ownerid,
Oid new_array_type);
static void RelationRemoveInheritance(Oid relid);
static Oid StoreRelCheck(Relation rel, char *ccname, char *ccbin, Oid conoid);
static Node* cookConstraint (ParseState *pstate,
......@@ -533,18 +533,17 @@ CheckAttributeType(const char *attname, Oid atttypid)
if (Gp_role != GP_ROLE_EXECUTE)
{
/*
* Warn user, but don't fail, if column to be created has UNKNOWN type
* (usually as a result of a 'retrieve into' - jolly)
*
* Refuse any attempt to create a pseudo-type column.
*/
if (atttypid == UNKNOWNOID)
{
/*
* Warn user, but don't fail, if column to be created has UNKNOWN type
* (usually as a result of a 'retrieve into' - jolly)
*/
ereport(WARNING,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("column \"%s\" has type \"unknown\"", attname),
errdetail("Proceeding with relation creation anyway.")));
}
else if (att_typtype == TYPTYPE_PSEUDO)
{
/*
......@@ -580,6 +579,7 @@ CheckAttributeType(const char *attname, Oid atttypid)
continue;
CheckAttributeType(NameStr(attr->attname), attr->atttypid);
}
relation_close(relation, AccessShareLock);
}
}
......@@ -1151,8 +1151,8 @@ AddNewRelationType(const char *typeName,
Oid typeNamespace,
Oid new_rel_oid,
char new_rel_kind,
char new_array_type,
Oid ownerid)
Oid ownerid,
Oid new_array_type)
{
return
TypeCreate(typeName, /* type name */
......@@ -1372,7 +1372,7 @@ heap_create_with_catalog(const char *relname,
Relation gp_relation_node_desc;
Relation new_rel_desc;
Oid new_type_oid;
Oid new_array_oid = InvalidOid;
Oid new_array_oid = InvalidOid;
bool appendOnlyRel;
StdRdOptions *stdRdOptions;
int safefswritesize = gp_safefswritesize;
......@@ -1381,7 +1381,7 @@ heap_create_with_catalog(const char *relname,
{
new_array_oid = *comptypeArrayOid;
}
pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock);
if (!IsBootstrapProcessingMode())
......@@ -1506,7 +1506,7 @@ heap_create_with_catalog(const char *relname,
}
/*
* since defining a relation also defines a complex type, we add a new
* Since defining a relation also defines a complex type, we add a new
* system type corresponding to the new relation.
*
* NOTE: we could get a unique-index failure here, in case the same name
......@@ -1527,8 +1527,8 @@ heap_create_with_catalog(const char *relname,
relnamespace,
relid,
relkind,
new_array_oid,
ownerid);
ownerid,
new_array_oid);
else
{
new_type_oid = TypeCreateWithOid(
......@@ -1573,37 +1573,39 @@ heap_create_with_catalog(const char *relname,
if (OidIsValid(new_array_oid))
{
char *relarrayname;
relarrayname = makeArrayTypeName(relname, relnamespace);
TypeCreateWithOid(
relarrayname, /* type name */
relnamespace, /* type namespace */
InvalidOid, /* relation oid */
0, /* relation kind */
ownerid,
-1, /* internal size (varlena) */
TYPTYPE_BASE, /* type-type (complex) */
DEFAULT_TYPDELIM, /* default array delimiter */
F_ARRAY_IN, /* input procedure */
F_ARRAY_OUT, /* output procedure */
F_ARRAY_RECV, /* receive procedure */
F_ARRAY_SEND, /* send procedure */
InvalidOid, /* typmodin procedure */
InvalidOid, /* typmodout procedure */
InvalidOid, /* analyze procedure - default */
new_type_oid, /* array element type - irrelevant */
true, /* this is not an array type */
InvalidOid, /* array type if any */
InvalidOid, /* domain base type - irrelevant */
NULL, /* default value - none */
NULL, /* default binary representation */
false, /* passed by reference */
'd', /* alignment - must be the largest! */
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
new_array_oid,
0);
InvalidOid, /* relation oid */
0, /* relation kind */
ownerid,
-1, /* internal size (varlena) */
TYPTYPE_BASE, /* type-type (complex) */
DEFAULT_TYPDELIM, /* default array delimiter */
F_ARRAY_IN, /* input procedure */
F_ARRAY_OUT, /* output procedure */
F_ARRAY_RECV, /* receive procedure */
F_ARRAY_SEND, /* send procedure */
InvalidOid, /* typmodin procedure */
InvalidOid, /* typmodout procedure */
InvalidOid, /* analyze procedure - default */
new_type_oid, /* array element type - irrelevant */
true, /* this is not an array type */
InvalidOid, /* array type if any */
InvalidOid, /* domain base type - irrelevant */
NULL, /* default value - none */
NULL, /* default binary representation */
false, /* passed by reference */
'd', /* alignment - must be the largest! */
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
new_array_oid,
0);
if (PointerIsValid(comptypeArrayOid))
{
......
......@@ -297,9 +297,9 @@ TypeCreateWithOid(const char *typeName,
values[i++] = CharGetDatum(typeType); /* typtype */
values[i++] = BoolGetDatum(true); /* typisdefined */
values[i++] = CharGetDatum(typDelim); /* typdelim */
values[i++] = ObjectIdGetDatum(relationOid); /* typrelid */
values[i++] = ObjectIdGetDatum(relationOid); /* typrelid */
values[i++] = ObjectIdGetDatum(elementType); /* typelem */
values[i++] = ObjectIdGetDatum(arrayType); /* typarray */
values[i++] = ObjectIdGetDatum(arrayType); /* typarray */
values[i++] = ObjectIdGetDatum(inputProcedure); /* typinput */
values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */
values[i++] = ObjectIdGetDatum(receiveProcedure); /* typreceive */
......@@ -477,28 +477,28 @@ Oid TypeCreate(const char *typeName,
ownerId,
internalSize,
typeType,
typDelim,
inputProcedure,
outputProcedure,
receiveProcedure,
sendProcedure,
typmodinProcedure,
typmodoutProcedure,
analyzeProcedure,
elementType,
typDelim,
inputProcedure,
outputProcedure,
receiveProcedure,
sendProcedure,
typmodinProcedure,
typmodoutProcedure,
analyzeProcedure,
elementType,
isImplicitArray,
arrayType,
baseType,
defaultTypeValue,
defaultTypeBin,
passedByValue,
alignment,
storage,
typeMod,
typNDims,
typeNotNull,
InvalidOid,
0);
baseType,
defaultTypeValue,
defaultTypeBin,
passedByValue,
alignment,
storage,
typeMod,
typNDims,
typeNotNull,
InvalidOid,
0);
}
/*
......@@ -540,8 +540,14 @@ GenerateTypeDependencies(Oid typeNamespace,
myself.objectId = typeObjectId;
myself.objectSubId = 0;
/* dependency on namespace */
/* skip for relation rowtype, since we have indirect dependency */
/*
* Make dependency on namespace and shared dependency on owner.
*
* For a relation rowtype (that's not a composite type), we should skip
* these because we'll depend on them indirectly through the pg_class
* entry. Likewise, skip for implicit arrays since we'll depend on them
* through the element type.
*/
if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) &&
!isImplicitArray)
{
......@@ -632,17 +638,17 @@ GenerateTypeDependencies(Oid typeNamespace,
}
/*
* If the type is an array type, mark it auto-dependent on the base type.
* (This is a compromise between the typical case where the array type is
* automatically generated and the case where it is manually created: we'd
* prefer INTERNAL for the former case and NORMAL for the latter.)
* If the type is an implicitly-created array type, mark it as internally
* dependent on the element type. Otherwise, if it has an element type,
* the dependency is a normal one.
*/
if (OidIsValid(elementType))
{
referenced.classId = TypeRelationId;
referenced.objectId = elementType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
recordDependencyOn(&myself, &referenced,
isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL);
}
/* Normal dependency from a domain to its base type. */
......@@ -657,7 +663,6 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
}
/*
......@@ -732,10 +737,14 @@ makeArrayTypeName(const char *typeName, Oid typeNamespace)
int i;
Relation pg_type_desc;
if (!typeName)
return NULL;
/*
* The idea is to prepend underscores as needed until we make a name that
* doesn't collide with anything...
*/
arr = (char*)palloc(NAMEDATALEN);
pg_type_desc = heap_open(TypeRelationId, AccessShareLock);
for (i = 1; i < NAMEDATALEN - 1; i++)
{
arr[i - 1] = '_';
......@@ -747,7 +756,9 @@ makeArrayTypeName(const char *typeName, Oid typeNamespace)
0, 0))
break;
}
heap_close(pg_type_desc, AccessShareLock);
if (i >= NAMEDATALEN-1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
......
......@@ -144,16 +144,6 @@ DefineType(List *names, List *parameters, Oid newOid, Oid shadowOid)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(typeNamespace));
/*
* Type names must be one character shorter than other names, allowing
* room to create the corresponding array type name with prepended "_".
*/
if (strlen(typeName) > (NAMEDATALEN - 2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_NAME),
errmsg("type names must be %d characters or less",
NAMEDATALEN - 2)));
/*
* Look to see if type already exists (presumably as a shell; if not,
* TypeCreate will complain). If it doesn't, create it as a shell, so
......@@ -442,6 +432,7 @@ DefineType(List *names, List *parameters, Oid newOid, Oid shadowOid)
if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(analyzeName));
/* Preassign array type OID so we can insert it in pg_type.typarray */
pg_type = heap_open(TypeRelationId, AccessShareLock);
array_oid = shadowOid;
......@@ -504,7 +495,7 @@ DefineType(List *names, List *parameters, Oid newOid, Oid shadowOid)
InvalidOid, /* relation oid (n/a here) */
0, /* relation kind (ditto) */
GetUserId(), /* owner's ID */
-1, /* internal size */
-1, /* internal size (always varlena) */
TYPTYPE_BASE, /* type-type (base type) */
delimiter, /* array element delimiter */
F_ARRAY_IN, /* input procedure */
......@@ -701,19 +692,6 @@ DefineDomain(CreateDomainStmt *stmt)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(domainNamespace));
/*
* Domainnames, unlike typenames don't need to account for the '_' prefix.
* So they can be one character longer. (This test is presently useless
* since the parser will have truncated the name to fit. But leave it
* here since we may someday support arrays of domains, in which case
* we'll be back to needing to enforce NAMEDATALEN-2.)
*/
if (strlen(domainName) > (NAMEDATALEN - 1))
ereport(ERROR,
(errcode(ERRCODE_INVALID_NAME),
errmsg("domain names must be %d characters or less",
NAMEDATALEN - 1)));
/*
* Look up the base type.
*/
......@@ -1501,7 +1479,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
/* Rebuild dependencies */
GenerateTypeDependencies(typTup->typnamespace,
domainoid,
typTup->typrelid,
InvalidOid, /* typrelid is n/a */
0, /* relation kind is n/a */
typTup->typowner,
typTup->typinput,
......@@ -1512,7 +1490,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
typTup->typmodout,
typTup->typanalyze,
typTup->typelem,
false,
false, /* a domain isn't an implicit array */
typTup->typbasetype,
defaultExpr,
true); /* Rebuild is true */
......@@ -2372,7 +2350,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
/*
* If it's a composite type, we need to check that it really is a
* free-standing composite type, and not a table's underlying type. We
* free-standing composite type, and not a table's rowtype. We
* want people to use ALTER TABLE not ALTER TYPE for that case.
*/
if (typTup->typtype == TYPTYPE_COMPOSITE &&
......@@ -2382,6 +2360,16 @@ AlterTypeOwner(List *names, Oid newOwnerId)
errmsg("\"%s\" is a table's row type",
TypeNameToString(typename))));
/* don't allow direct alteration of array types, either */
if (OidIsValid(typTup->typelem) &&
get_array_type(typTup->typelem) == typeOid)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot alter array type %s",
format_type_be(typeOid)),
errhint("You can alter type %s, which will alter the array type as well.",
format_type_be(typTup->typelem))));
/*
* If the new owner is the same as the existing owner, consider the
* command to have succeeded. This is for dump restoration purposes.
......@@ -2428,6 +2416,10 @@ AlterTypeOwner(List *names, Oid newOwnerId)
/* Update owner dependency reference */
changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
/* If it has an array type, update that too */
if (OidIsValid(typTup->typarray))
AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
}
}
......@@ -2482,6 +2474,10 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
if (hasDependEntry)
changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
/* If it has an array type, update that too */
if (OidIsValid(typTup->typarray))
AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
/* Clean up */
heap_close(rel, RowExclusiveLock);
}
......@@ -2495,6 +2491,7 @@ AlterTypeNamespace(List *names, const char *newschema)
TypeName *typename;
Oid typeOid;
Oid nspOid;
Oid elemOid;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
......@@ -2508,6 +2505,16 @@ AlterTypeNamespace(List *names, const char *newschema)
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
/* don't allow direct alteration of array types */
elemOid = get_element_type(typeOid);
if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot alter array type %s",
format_type_be(typeOid)),
errhint("You can alter type %s, which will alter the array type as well.",
format_type_be(elemOid))));
/* and do the work */
AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
}
......@@ -2517,6 +2524,10 @@ AlterTypeNamespace(List *names, const char *newschema)
*
* Caller must have already checked privileges.
*
* The function automatically recurses to process the type's array type,
* if any. isImplicitArray should be TRUE only when doing this internal
* recursion (outside callers must never try to move an array type directly).
*
* If errorOnTableType is TRUE, the function errors out if the type is
* a table type. ALTER TABLE has to be used to move a table to a new
* namespace.
......@@ -2661,6 +2672,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
......
......@@ -2927,7 +2927,7 @@ get_element_type(Oid typid)
/*
* get_array_type
*
* Given the type OID, get the corresponding "true" array type.
* Given the type OID, get the corresponding "true" array type.
* Returns InvalidOid if no array type can be found.
*
*/
......
......@@ -1181,9 +1181,8 @@ selectDumpableType(TypeInfo *tinfo)
else if (!tinfo->isDefined)
tinfo->dobj.dump = false;
/* skip all array types that start w/ underscore */
else if ((tinfo->dobj.name[0] == '_') &&
OidIsValid(tinfo->typelem))
/* skip auto-generated array types */
else if (tinfo->isArray)
tinfo->dobj.dump = false;
else
......@@ -2065,6 +2064,7 @@ getTypes(int *numTypes)
int i_typrelkind;
int i_typtype;
int i_typisdefined;
int i_isarray;
/*
* we include even the built-in types because those may be used as array
......@@ -2072,7 +2072,14 @@ getTypes(int *numTypes)
*
* we filter out the built-in types when we dump out the types
*
* same approach for undefined (shell) types
* same approach for undefined (shell) types and array types
*
* Note: as of 8.3 we can reliably detect whether a type is an
* auto-generated array type by checking the element type's typarray.
* (Before that the test is capable of generating false positives.) We
* still check for name beginning with '_', though, so as to avoid the
* cost of the subselect probe for all standard types. This would have to
* be revisited if the backend ever allows renaming of array types.
*/
/* Make sure we are in proper schema */
......@@ -2085,7 +2092,9 @@ getTypes(int *numTypes)
"typoutput::oid as typoutput, typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
"ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
"typtype, typisdefined "
"typtype, typisdefined, "
"typname[0] = '_' AND typelem != 0 AND "
"(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
"FROM pg_type",
username_subquery);
......@@ -2108,6 +2117,7 @@ getTypes(int *numTypes)
i_typrelkind = PQfnumber(res, "typrelkind");
i_typtype = PQfnumber(res, "typtype");
i_typisdefined = PQfnumber(res, "typisdefined");
i_isarray = PQfnumber(res, "isarray");
for (i = 0; i < ntups; i++)
{
......@@ -2135,20 +2145,16 @@ getTypes(int *numTypes)
tinfo[i].typrelkind != RELKIND_COMPOSITE_TYPE)
tinfo[i].dobj.objType = DO_TABLE_TYPE;
/*
* check for user-defined array types, omit system generated ones
*/
if (OidIsValid(tinfo[i].typelem) &&
tinfo[i].dobj.name[0] != '_')
tinfo[i].isArray = true;
else
tinfo[i].isArray = false;
if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
tinfo[i].isDefined = true;
else
tinfo[i].isDefined = false;
if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
tinfo[i].isArray = true;
else
tinfo[i].isArray = false;
/* Decide whether we want to dump it */
selectDumpableType(&tinfo[i]);
......@@ -5074,7 +5080,7 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
appendPQExpBufferStr(q, typdefault);
}
if (tinfo->isArray)
if (OidIsValid(tinfo->typelem))
{
char *elemType;
......
......@@ -156,7 +156,7 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP
/*
* If there is a "true" array type having this type as element type,
* typarray links to it. Zero if no associated "true" array type.
*/
*/
Oid typarray;
/*
......@@ -788,7 +788,7 @@ extern Oid TypeCreateWithOid(const char *typeName,
Oid typmodoutProcedure,
Oid analyzeProcedure,
Oid elementType,
bool isImplicitArray,
bool isImplicitArray,
Oid arrayType,
Oid baseType,
const char *defaultTypeValue,
......@@ -811,8 +811,8 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
Oid outputProcedure,
Oid receiveProcedure,
Oid sendProcedure,
Oid typmodinProcedure,
Oid typmodoutProcedure,
Oid typmodinProcedure,
Oid typmodoutProcedure,
Oid analyzeProcedure,
Oid elementType,
bool isImplicitArray,
......
......@@ -40,7 +40,7 @@ extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
extern void AlterTypeNamespace(List *names, const char *newschema);
extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType);
bool errorOnTableType);
extern void AlterType(AlterTypeStmt *stmt);
extern void AlterType(AlterTypeStmt *stmt);
......
......@@ -85,7 +85,7 @@ where ordinal_position = 20;
pg_catalog | pg_statistic | stavalues3 | 20
pg_catalog | pg_partitions | parenttablespace | 20
information_schema | attributes | datetime_precision | 20
pg_catalog | pg_type | typstorage | 20
pg_catalog | pg_type | typstorage | 20
pg_catalog | pg_proc | proargnames | 20
pg_catalog | pg_class | reltriggers | 20
pg_catalog | pg_am | ambuild | 20
......
......@@ -85,7 +85,7 @@ where ordinal_position = 20;
pg_catalog | pg_statistic | stavalues3 | 20
pg_catalog | pg_partitions | parenttablespace | 20
information_schema | attributes | datetime_precision | 20
pg_catalog | pg_type | typstorage | 20
pg_catalog | pg_type | typstorage | 20
pg_catalog | pg_proc | proargnames | 20
pg_catalog | pg_class | reltriggers | 20
pg_catalog | pg_am | ambuild | 20
......
......@@ -721,6 +721,14 @@ WHERE typelem != 0 AND
------+---------
(0 rows)
SELECT ctid, typarray
FROM pg_catalog.pg_type fk
WHERE typarray != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typarray);
ctid | typarray
------+----------
(0 rows)
SELECT ctid, typinput
FROM pg_catalog.pg_type fk
WHERE typinput != 0 AND
......
......@@ -117,6 +117,16 @@ ORDER BY 1;
30 | oidvector | 54 | oidvectorin
(2 rows)
-- Make sure typarray points to a varlena array type of our own base
SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
p2.typelem, p2.typlen
FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid)
WHERE p1.typarray <> 0 AND
(p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1);
oid | basetype | arraytype | typelem | typlen
-----+----------+-----------+---------+--------
(0 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines
......
......@@ -361,6 +361,10 @@ SELECT ctid, typelem
FROM pg_catalog.pg_type fk
WHERE typelem != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typelem);
SELECT ctid, typarray
FROM pg_catalog.pg_type fk
WHERE typarray != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typarray);
SELECT ctid, typinput
FROM pg_catalog.pg_type fk
WHERE typinput != 0 AND
......
......@@ -94,6 +94,13 @@ WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND
(p2.oid = 'array_in'::regproc)
ORDER BY 1;
-- Make sure typarray points to a varlena array type of our own base
SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
p2.typelem, p2.typlen
FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid)
WHERE p1.typarray <> 0 AND
(p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1);
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册