mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
732 lines
15 KiB
C
732 lines
15 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* syscache.c
|
|
* System cache management routines
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.32 1999/07/20 16:48:55 momjian Exp $
|
|
*
|
|
* NOTES
|
|
* These routines allow the parser/planner/executor to perform
|
|
* rapid lookups on the contents of the system catalogs.
|
|
*
|
|
* see catalog/syscache.h for a list of the cache id's
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/heapam.h"
|
|
#include "catalog/catname.h"
|
|
#include "catalog/pg_aggregate.h"
|
|
#include "catalog/pg_amop.h"
|
|
#include "catalog/pg_group.h"
|
|
#include "catalog/pg_index.h"
|
|
#include "catalog/pg_inherits.h"
|
|
#include "catalog/pg_language.h"
|
|
#include "catalog/pg_listener.h"
|
|
#include "catalog/pg_opclass.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_rewrite.h"
|
|
#include "catalog/pg_shadow.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "utils/catcache.h"
|
|
|
|
extern bool AMI_OVERRIDE; /* XXX style */
|
|
|
|
#include "utils/syscache.h"
|
|
#include "catalog/indexing.h"
|
|
|
|
typedef HeapTuple (*ScanFunc) ();
|
|
|
|
/* ----------------
|
|
* Warning: cacheinfo[] below is changed, then be sure and
|
|
* update the magic constants in syscache.h!
|
|
* ----------------
|
|
*/
|
|
static struct cachedesc cacheinfo[] = {
|
|
{AccessMethodOperatorRelationName, /* AMOPOPID */
|
|
3,
|
|
{
|
|
Anum_pg_amop_amopclaid,
|
|
Anum_pg_amop_amopopr,
|
|
Anum_pg_amop_amopid,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_amop),
|
|
AccessMethodOpidIndex,
|
|
(ScanFunc) AccessMethodOpidIndexScan},
|
|
{AccessMethodOperatorRelationName, /* AMOPSTRATEGY */
|
|
3,
|
|
{
|
|
Anum_pg_amop_amopid,
|
|
Anum_pg_amop_amopclaid,
|
|
Anum_pg_amop_amopstrategy,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_amop),
|
|
AccessMethodStrategyIndex,
|
|
(ScanFunc) AccessMethodStrategyIndexScan},
|
|
{AttributeRelationName, /* ATTNAME */
|
|
2,
|
|
{
|
|
Anum_pg_attribute_attrelid,
|
|
Anum_pg_attribute_attname,
|
|
0,
|
|
0
|
|
},
|
|
ATTRIBUTE_TUPLE_SIZE,
|
|
AttributeNameIndex,
|
|
(ScanFunc) IndexRelidIndexScan},
|
|
{AttributeRelationName, /* ATTNUM */
|
|
2,
|
|
{
|
|
Anum_pg_attribute_attrelid,
|
|
Anum_pg_attribute_attnum,
|
|
0,
|
|
0
|
|
},
|
|
ATTRIBUTE_TUPLE_SIZE,
|
|
AttributeNumIndex,
|
|
(ScanFunc) AttributeNumIndexScan},
|
|
{IndexRelationName, /* INDEXRELID */
|
|
1,
|
|
{
|
|
Anum_pg_index_indexrelid,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_index, indpred),
|
|
IndexRelidIndex,
|
|
(ScanFunc) IndexRelidIndexScan},
|
|
{LanguageRelationName, /* LANNAME */
|
|
1,
|
|
{
|
|
Anum_pg_language_lanname,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_language, lancompiler),
|
|
NULL,
|
|
NULL},
|
|
{OperatorRelationName, /* OPRNAME */
|
|
4,
|
|
{
|
|
Anum_pg_operator_oprname,
|
|
Anum_pg_operator_oprleft,
|
|
Anum_pg_operator_oprright,
|
|
Anum_pg_operator_oprkind
|
|
},
|
|
sizeof(FormData_pg_operator),
|
|
NULL,
|
|
NULL},
|
|
{OperatorRelationName, /* OPROID */
|
|
1,
|
|
{
|
|
ObjectIdAttributeNumber,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_operator),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{ProcedureRelationName, /* PRONAME */
|
|
3,
|
|
{
|
|
Anum_pg_proc_proname,
|
|
Anum_pg_proc_pronargs,
|
|
Anum_pg_proc_proargtypes,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_proc, prosrc),
|
|
ProcedureNameIndex,
|
|
(ScanFunc) ProcedureNameIndexScan},
|
|
{ProcedureRelationName, /* PROOID */
|
|
1,
|
|
{
|
|
ObjectIdAttributeNumber,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_proc, prosrc),
|
|
ProcedureOidIndex,
|
|
(ScanFunc) ProcedureOidIndexScan},
|
|
{RelationRelationName, /* RELNAME */
|
|
1,
|
|
{
|
|
Anum_pg_class_relname,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
CLASS_TUPLE_SIZE,
|
|
ClassNameIndex,
|
|
(ScanFunc) ClassNameIndexScan},
|
|
{RelationRelationName, /* RELOID */
|
|
1,
|
|
{
|
|
ObjectIdAttributeNumber,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
CLASS_TUPLE_SIZE,
|
|
ClassOidIndex,
|
|
(ScanFunc) ClassOidIndexScan},
|
|
{TypeRelationName, /* TYPNAME */
|
|
1,
|
|
{
|
|
Anum_pg_type_typname,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_type, typalign) +sizeof(char),
|
|
TypeNameIndex,
|
|
TypeNameIndexScan},
|
|
{TypeRelationName, /* TYPOID */
|
|
1,
|
|
{
|
|
ObjectIdAttributeNumber,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_type, typalign) +sizeof(char),
|
|
TypeOidIndex,
|
|
TypeOidIndexScan},
|
|
{AccessMethodRelationName, /* AMNAME */
|
|
1,
|
|
{
|
|
Anum_pg_am_amname,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_am),
|
|
NULL,
|
|
NULL},
|
|
{OperatorClassRelationName, /* CLANAME */
|
|
1,
|
|
{
|
|
Anum_pg_opclass_opcname,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_opclass),
|
|
NULL,
|
|
NULL},
|
|
{InheritsRelationName, /* INHRELID */
|
|
2,
|
|
{
|
|
Anum_pg_inherits_inhrel,
|
|
Anum_pg_inherits_inhseqno,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_inherits),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{RewriteRelationName, /* RULOID */
|
|
1,
|
|
{
|
|
ObjectIdAttributeNumber,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_rewrite, ev_qual),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{AggregateRelationName, /* AGGNAME */
|
|
2,
|
|
{
|
|
Anum_pg_aggregate_aggname,
|
|
Anum_pg_aggregate_aggbasetype,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_aggregate, agginitval1),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{ListenerRelationName, /* LISTENREL */
|
|
2,
|
|
{
|
|
Anum_pg_listener_relname,
|
|
Anum_pg_listener_pid,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_listener),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{ShadowRelationName, /* USENAME */
|
|
1,
|
|
{
|
|
Anum_pg_shadow_usename,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_shadow),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{ShadowRelationName, /* USESYSID */
|
|
1,
|
|
{
|
|
Anum_pg_shadow_usesysid,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_shadow),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{GroupRelationName, /* GRONAME */
|
|
1,
|
|
{
|
|
Anum_pg_group_groname,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_group, grolist[0]),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{GroupRelationName, /* GROSYSID */
|
|
1,
|
|
{
|
|
Anum_pg_group_grosysid,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_group, grolist[0]),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{RewriteRelationName, /* REWRITENAME */
|
|
1,
|
|
{
|
|
Anum_pg_rewrite_rulename,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_rewrite, ev_qual),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{ProcedureRelationName, /* PROSRC */
|
|
1,
|
|
{
|
|
Anum_pg_proc_prosrc,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_proc, prosrc),
|
|
ProcedureSrcIndex,
|
|
(ScanFunc) ProcedureSrcIndexScan},
|
|
{OperatorClassRelationName, /* CLADEFTYPE */
|
|
1,
|
|
{
|
|
Anum_pg_opclass_opcdeftype,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
sizeof(FormData_pg_opclass),
|
|
NULL,
|
|
(ScanFunc) NULL},
|
|
{LanguageRelationName, /* LANOID */
|
|
1,
|
|
{
|
|
ObjectIdAttributeNumber,
|
|
0,
|
|
0,
|
|
0
|
|
},
|
|
offsetof(FormData_pg_language, lancompiler),
|
|
NULL,
|
|
NULL}
|
|
};
|
|
|
|
static struct catcache *SysCache[lengthof(cacheinfo)];
|
|
static int32 SysCacheSize = lengthof(cacheinfo);
|
|
|
|
|
|
/*
|
|
* zerocaches
|
|
*
|
|
* Make sure the SysCache structure is zero'd.
|
|
*/
|
|
void
|
|
zerocaches()
|
|
{
|
|
MemSet((char *) SysCache, 0, SysCacheSize * sizeof(struct catcache *));
|
|
}
|
|
|
|
/*
|
|
* Note:
|
|
* This function was written because the initialized catalog caches
|
|
* are used to determine which caches may contain tuples which need
|
|
* to be invalidated in other backends.
|
|
*/
|
|
void
|
|
InitCatalogCache()
|
|
{
|
|
int cacheId; /* XXX type */
|
|
|
|
if (!AMI_OVERRIDE)
|
|
{
|
|
for (cacheId = 0; cacheId < SysCacheSize; cacheId += 1)
|
|
{
|
|
|
|
Assert(!PointerIsValid((Pointer) SysCache[cacheId]));
|
|
|
|
SysCache[cacheId] = InitSysCache(cacheinfo[cacheId].name,
|
|
cacheinfo[cacheId].indname,
|
|
cacheId,
|
|
cacheinfo[cacheId].nkeys,
|
|
cacheinfo[cacheId].key,
|
|
cacheinfo[cacheId].iScanFunc);
|
|
if (!PointerIsValid((char *) SysCache[cacheId]))
|
|
{
|
|
elog(ERROR,
|
|
"InitCatalogCache: Can't init cache %s(%d)",
|
|
cacheinfo[cacheId].name,
|
|
cacheId);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SearchSysCacheTupleCopy
|
|
*
|
|
* This is like SearchSysCacheTuple, except it returns a copy of the tuple
|
|
* that the user is required to pfree().
|
|
*/
|
|
HeapTuple
|
|
SearchSysCacheTupleCopy(int cacheId, /* cache selection code */
|
|
Datum key1,
|
|
Datum key2,
|
|
Datum key3,
|
|
Datum key4)
|
|
{
|
|
HeapTuple cachetup;
|
|
|
|
cachetup = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
|
|
if (PointerIsValid(cachetup))
|
|
return heap_copytuple(cachetup);
|
|
else
|
|
return cachetup; /* NULL */
|
|
}
|
|
|
|
|
|
/*
|
|
* SearchSysCacheTuple
|
|
*
|
|
* A layer on top of SearchSysCache that does the initialization and
|
|
* key-setting for you.
|
|
*
|
|
* Returns the cache copy of the tuple if one is found, NULL if not.
|
|
* The tuple is the 'cache' copy.
|
|
*
|
|
* XXX The tuple that is returned is NOT supposed to be pfree'd!
|
|
*/
|
|
HeapTuple
|
|
SearchSysCacheTuple(int cacheId,/* cache selection code */
|
|
Datum key1,
|
|
Datum key2,
|
|
Datum key3,
|
|
Datum key4)
|
|
{
|
|
HeapTuple tp;
|
|
|
|
if (cacheId < 0 || cacheId >= SysCacheSize)
|
|
{
|
|
elog(ERROR, "SearchSysCacheTuple: Bad cache id %d", cacheId);
|
|
return (HeapTuple) NULL;
|
|
}
|
|
|
|
Assert(AMI_OVERRIDE || PointerIsValid(SysCache[cacheId]));
|
|
|
|
if (!PointerIsValid(SysCache[cacheId]))
|
|
{
|
|
SysCache[cacheId] = InitSysCache(cacheinfo[cacheId].name,
|
|
cacheinfo[cacheId].indname,
|
|
cacheId,
|
|
cacheinfo[cacheId].nkeys,
|
|
cacheinfo[cacheId].key,
|
|
cacheinfo[cacheId].iScanFunc);
|
|
if (!PointerIsValid(SysCache[cacheId]))
|
|
elog(ERROR,
|
|
"InitCatalogCache: Can't init cache %s(%d)",
|
|
cacheinfo[cacheId].name,
|
|
cacheId);
|
|
}
|
|
|
|
tp = SearchSysCache(SysCache[cacheId], key1, key2, key3, key4);
|
|
if (!HeapTupleIsValid(tp))
|
|
{
|
|
#ifdef CACHEDEBUG
|
|
elog(DEBUG,
|
|
"SearchSysCacheTuple: Search %s(%d) %d %d %d %d failed",
|
|
cacheinfo[cacheId].name,
|
|
cacheId, key1, key2, key3, key4);
|
|
#endif
|
|
return (HeapTuple) NULL;
|
|
}
|
|
return tp;
|
|
}
|
|
|
|
/*
|
|
* SearchSysCacheStruct
|
|
* Fills 's' with the information retrieved by calling SearchSysCache()
|
|
* with arguments key1...key4. Retrieves only the portion of the tuple
|
|
* which is not variable-length.
|
|
*
|
|
* NOTE: we are assuming that non-variable-length fields in the system
|
|
* catalogs will always be defined!
|
|
*
|
|
* Returns 1L if a tuple was found, 0L if not.
|
|
*/
|
|
int32
|
|
SearchSysCacheStruct(int cacheId, /* cache selection code */
|
|
char *returnStruct, /* (preallocated!) */
|
|
Datum key1,
|
|
Datum key2,
|
|
Datum key3,
|
|
Datum key4)
|
|
{
|
|
HeapTuple tp;
|
|
|
|
if (!PointerIsValid(returnStruct))
|
|
{
|
|
elog(ERROR, "SearchSysCacheStruct: No receiving struct");
|
|
return 0;
|
|
}
|
|
tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
|
|
if (!HeapTupleIsValid(tp))
|
|
return 0;
|
|
memcpy(returnStruct, (char *) GETSTRUCT(tp), cacheinfo[cacheId].size);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* SearchSysCacheGetAttribute
|
|
* Returns the attribute corresponding to 'attributeNumber' for
|
|
* a given cached tuple. This routine usually needs to be used for
|
|
* attributes that might be NULL or might be at a variable offset
|
|
* in the tuple.
|
|
*
|
|
* XXX This re-opens the relation, so this is slower than just pulling
|
|
* fixed-location fields out of the struct returned by SearchSysCacheTuple.
|
|
*
|
|
* [callers all assume this returns a (struct varlena *). -ay 10/94]
|
|
*/
|
|
void *
|
|
SearchSysCacheGetAttribute(int cacheId,
|
|
AttrNumber attributeNumber,
|
|
Datum key1,
|
|
Datum key2,
|
|
Datum key3,
|
|
Datum key4)
|
|
{
|
|
HeapTuple tp;
|
|
char *cacheName;
|
|
Relation relation;
|
|
int32 attributeLength,
|
|
attributeByValue;
|
|
bool isNull;
|
|
Datum attributeValue;
|
|
void *returnValue;
|
|
|
|
tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
|
|
cacheName = cacheinfo[cacheId].name;
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
{
|
|
#ifdef CACHEDEBUG
|
|
elog(DEBUG,
|
|
"SearchSysCacheGetAttribute: Lookup in %s(%d) failed",
|
|
cacheName, cacheId);
|
|
#endif /* defined(CACHEDEBUG) */
|
|
return NULL;
|
|
}
|
|
|
|
relation = heap_openr(cacheName);
|
|
|
|
if (attributeNumber < 0 &&
|
|
attributeNumber > FirstLowInvalidHeapAttributeNumber)
|
|
{
|
|
attributeLength = heap_sysattrlen(attributeNumber);
|
|
attributeByValue = heap_sysattrbyval(attributeNumber);
|
|
}
|
|
else if (attributeNumber > 0 &&
|
|
attributeNumber <= relation->rd_rel->relnatts)
|
|
{
|
|
attributeLength = relation->rd_att->attrs[attributeNumber - 1]->attlen;
|
|
attributeByValue = relation->rd_att->attrs[attributeNumber - 1]->attbyval;
|
|
}
|
|
else
|
|
{
|
|
elog(ERROR,
|
|
"SearchSysCacheGetAttribute: Bad attr # %d in %s(%d)",
|
|
attributeNumber, cacheName, cacheId);
|
|
return NULL;
|
|
}
|
|
|
|
attributeValue = heap_getattr(tp,
|
|
attributeNumber,
|
|
RelationGetDescr(relation),
|
|
&isNull);
|
|
|
|
if (isNull)
|
|
{
|
|
|
|
/*
|
|
* Used to be an elog(DEBUG, ...) here and a claim that it should
|
|
* be a FATAL error, I don't think either is warranted -mer 6/9/92
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
if (attributeByValue)
|
|
returnValue = (void *) attributeValue;
|
|
else
|
|
{
|
|
char *tmp;
|
|
int size = (attributeLength < 0)
|
|
? VARSIZE((struct varlena *) attributeValue) /* variable length */
|
|
: attributeLength; /* fixed length */
|
|
|
|
tmp = (char *) palloc(size);
|
|
memcpy(tmp, (void *) attributeValue, size);
|
|
returnValue = (void *) tmp;
|
|
}
|
|
|
|
heap_close(relation);
|
|
return returnValue;
|
|
}
|
|
|
|
/*
|
|
* TypeDefaultRetrieve
|
|
*
|
|
* Given a type OID, return the typdefault field associated with that
|
|
* type. The result is a Datum, and points to palloc'd storage for
|
|
* non-pass-by-value types.
|
|
*
|
|
* [identical to get_typdefault, expecting a (struct varlena *) as ret val.
|
|
* some day, either of the functions should be removed -ay 10/94]
|
|
*/
|
|
void *
|
|
TypeDefaultRetrieve(Oid typId)
|
|
{
|
|
struct varlena *typDefault;
|
|
int32 dataSize;
|
|
HeapTuple typeTuple;
|
|
Form_pg_type type;
|
|
int32 typByVal,
|
|
typLen;
|
|
void *returnValue;
|
|
|
|
/*
|
|
* First, see if there is a non-null typdefault field (usually there isn't)
|
|
*/
|
|
typDefault = (struct varlena *)
|
|
SearchSysCacheGetAttribute(TYPOID,
|
|
Anum_pg_type_typdefault,
|
|
ObjectIdGetDatum(typId),
|
|
0, 0, 0);
|
|
|
|
if (typDefault == NULL)
|
|
{
|
|
#ifdef CACHEDEBUG
|
|
elog(DEBUG, "TypeDefaultRetrieve: No extractable typdefault in %s(%d)",
|
|
cacheinfo[TYPOID].name, TYPOID);
|
|
#endif /* defined(CACHEDEBUG) */
|
|
return NULL;
|
|
}
|
|
|
|
dataSize = VARSIZE(typDefault) - VARHDRSZ;
|
|
|
|
/*
|
|
* Need the type's length and byVal fields.
|
|
*
|
|
* XXX silly to repeat the syscache search that SearchSysCacheGetAttribute
|
|
* just did --- but at present this path isn't taken often enough to
|
|
* make it worth fixing.
|
|
*/
|
|
typeTuple = SearchSysCacheTuple(TYPOID,
|
|
ObjectIdGetDatum(typId),
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(typeTuple))
|
|
{
|
|
/* should never get here, really... */
|
|
#ifdef CACHEDEBUG
|
|
elog(DEBUG, "TypeDefaultRetrieve: Lookup in %s(%d) failed",
|
|
cacheinfo[TYPOID].name, TYPOID);
|
|
#endif /* defined(CACHEDEBUG) */
|
|
return NULL;
|
|
}
|
|
|
|
type = (Form_pg_type) GETSTRUCT(typeTuple);
|
|
typLen = type->typlen;
|
|
typByVal = type->typbyval;
|
|
|
|
if (typByVal)
|
|
{
|
|
int8 i8;
|
|
int16 i16;
|
|
int32 i32 = 0;
|
|
|
|
if (dataSize == typLen)
|
|
{
|
|
switch (typLen)
|
|
{
|
|
case sizeof(int8):
|
|
memcpy((char *) &i8, VARDATA(typDefault), sizeof(int8));
|
|
i32 = i8;
|
|
break;
|
|
case sizeof(int16):
|
|
memcpy((char *) &i16, VARDATA(typDefault), sizeof(int16));
|
|
i32 = i16;
|
|
break;
|
|
case sizeof(int32):
|
|
memcpy((char *) &i32, VARDATA(typDefault), sizeof(int32));
|
|
break;
|
|
}
|
|
returnValue = (void *) i32;
|
|
}
|
|
else
|
|
returnValue = NULL;
|
|
}
|
|
else
|
|
{
|
|
if ((typLen < 0 && dataSize < 0) || dataSize != typLen)
|
|
returnValue = NULL;
|
|
else
|
|
{
|
|
returnValue = (void *) palloc(VARSIZE(typDefault));
|
|
memcpy((char *) returnValue,
|
|
(char *) typDefault,
|
|
(int) VARSIZE(typDefault));
|
|
}
|
|
}
|
|
|
|
return returnValue;
|
|
}
|