mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
Adjust creation/destruction of TupleDesc data structure to reduce the
number of palloc calls. This has a salutory impact on plpgsql operations with record variables (which create and destroy tupdescs constantly) and probably helps a bit in some other cases too.
This commit is contained in:
parent
e3d7de6b99
commit
a52b4fb131
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.108 2004/12/31 21:59:07 pgsql Exp $
|
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.109 2005/03/07 04:42:16 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* some of the executor utility code such as "ExecTypeFromTL" should be
|
* some of the executor utility code such as "ExecTypeFromTL" should be
|
||||||
@ -31,19 +31,19 @@
|
|||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/*
|
||||||
* CreateTemplateTupleDesc
|
* CreateTemplateTupleDesc
|
||||||
*
|
* This function allocates an empty tuple descriptor structure.
|
||||||
* This function allocates and zeros a tuple descriptor structure.
|
|
||||||
*
|
*
|
||||||
* Tuple type ID information is initially set for an anonymous record type;
|
* Tuple type ID information is initially set for an anonymous record type;
|
||||||
* caller can overwrite this if needed.
|
* caller can overwrite this if needed.
|
||||||
* ----------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
TupleDesc
|
TupleDesc
|
||||||
CreateTemplateTupleDesc(int natts, bool hasoid)
|
CreateTemplateTupleDesc(int natts, bool hasoid)
|
||||||
{
|
{
|
||||||
TupleDesc desc;
|
TupleDesc desc;
|
||||||
|
char *stg;
|
||||||
|
int attroffset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sanity checks
|
* sanity checks
|
||||||
@ -51,15 +51,33 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
|
|||||||
AssertArg(natts >= 0);
|
AssertArg(natts >= 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate enough memory for the tuple descriptor, and zero the
|
* Allocate enough memory for the tuple descriptor, including the
|
||||||
* attrs[] array since TupleDescInitEntry assumes that the array is
|
* attribute rows, and set up the attribute row pointers.
|
||||||
* filled with NULL pointers.
|
*
|
||||||
|
* Note: we assume that sizeof(struct tupleDesc) is a multiple of
|
||||||
|
* the struct pointer alignment requirement, and hence we don't need
|
||||||
|
* to insert alignment padding between the struct and the array of
|
||||||
|
* attribute row pointers.
|
||||||
*/
|
*/
|
||||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
attroffset = sizeof(struct tupleDesc) + natts * sizeof(Form_pg_attribute);
|
||||||
|
attroffset = MAXALIGN(attroffset);
|
||||||
|
stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_TUPLE_SIZE));
|
||||||
|
desc = (TupleDesc) stg;
|
||||||
|
|
||||||
if (natts > 0)
|
if (natts > 0)
|
||||||
desc->attrs = (Form_pg_attribute *)
|
{
|
||||||
palloc0(natts * sizeof(Form_pg_attribute));
|
Form_pg_attribute *attrs;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
attrs = (Form_pg_attribute *) (stg + sizeof(struct tupleDesc));
|
||||||
|
desc->attrs = attrs;
|
||||||
|
stg += attroffset;
|
||||||
|
for (i = 0; i < natts; i++)
|
||||||
|
{
|
||||||
|
attrs[i] = (Form_pg_attribute) stg;
|
||||||
|
stg += MAXALIGN(ATTRIBUTE_TUPLE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
desc->attrs = NULL;
|
desc->attrs = NULL;
|
||||||
|
|
||||||
@ -75,15 +93,16 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/*
|
||||||
* CreateTupleDesc
|
* CreateTupleDesc
|
||||||
*
|
|
||||||
* This function allocates a new TupleDesc pointing to a given
|
* This function allocates a new TupleDesc pointing to a given
|
||||||
* Form_pg_attribute array
|
* Form_pg_attribute array.
|
||||||
|
*
|
||||||
|
* Note: if the TupleDesc is ever freed, the Form_pg_attribute array
|
||||||
|
* will not be freed thereby.
|
||||||
*
|
*
|
||||||
* Tuple type ID information is initially set for an anonymous record type;
|
* Tuple type ID information is initially set for an anonymous record type;
|
||||||
* caller can overwrite this if needed.
|
* caller can overwrite this if needed.
|
||||||
* ----------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
TupleDesc
|
TupleDesc
|
||||||
CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
|
CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
|
||||||
@ -106,14 +125,12 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/*
|
||||||
* CreateTupleDescCopy
|
* CreateTupleDescCopy
|
||||||
*
|
|
||||||
* This function creates a new TupleDesc by copying from an existing
|
* This function creates a new TupleDesc by copying from an existing
|
||||||
* TupleDesc
|
* TupleDesc.
|
||||||
*
|
*
|
||||||
* !!! Constraints and defaults are not copied !!!
|
* !!! Constraints and defaults are not copied !!!
|
||||||
* ----------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
TupleDesc
|
TupleDesc
|
||||||
CreateTupleDescCopy(TupleDesc tupdesc)
|
CreateTupleDescCopy(TupleDesc tupdesc)
|
||||||
@ -121,38 +138,25 @@ CreateTupleDescCopy(TupleDesc tupdesc)
|
|||||||
TupleDesc desc;
|
TupleDesc desc;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
|
||||||
desc->natts = tupdesc->natts;
|
|
||||||
if (desc->natts > 0)
|
|
||||||
{
|
|
||||||
desc->attrs = (Form_pg_attribute *)
|
|
||||||
palloc(desc->natts * sizeof(Form_pg_attribute));
|
|
||||||
for (i = 0; i < desc->natts; i++)
|
for (i = 0; i < desc->natts; i++)
|
||||||
{
|
{
|
||||||
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
||||||
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
|
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
|
||||||
desc->attrs[i]->attnotnull = false;
|
desc->attrs[i]->attnotnull = false;
|
||||||
desc->attrs[i]->atthasdef = false;
|
desc->attrs[i]->atthasdef = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
desc->attrs = NULL;
|
|
||||||
|
|
||||||
desc->constr = NULL;
|
|
||||||
|
|
||||||
desc->tdtypeid = tupdesc->tdtypeid;
|
desc->tdtypeid = tupdesc->tdtypeid;
|
||||||
desc->tdtypmod = tupdesc->tdtypmod;
|
desc->tdtypmod = tupdesc->tdtypmod;
|
||||||
desc->tdhasoid = tupdesc->tdhasoid;
|
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/*
|
||||||
* CreateTupleDescCopyConstr
|
* CreateTupleDescCopyConstr
|
||||||
*
|
|
||||||
* This function creates a new TupleDesc by copying from an existing
|
* This function creates a new TupleDesc by copying from an existing
|
||||||
* TupleDesc (including its constraints and defaults)
|
* TupleDesc (including its constraints and defaults).
|
||||||
* ----------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
TupleDesc
|
TupleDesc
|
||||||
CreateTupleDescCopyConstr(TupleDesc tupdesc)
|
CreateTupleDescCopyConstr(TupleDesc tupdesc)
|
||||||
@ -161,20 +165,12 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
|
|||||||
TupleConstr *constr = tupdesc->constr;
|
TupleConstr *constr = tupdesc->constr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
|
||||||
desc->natts = tupdesc->natts;
|
|
||||||
if (desc->natts > 0)
|
|
||||||
{
|
|
||||||
desc->attrs = (Form_pg_attribute *)
|
|
||||||
palloc(desc->natts * sizeof(Form_pg_attribute));
|
|
||||||
for (i = 0; i < desc->natts; i++)
|
for (i = 0; i < desc->natts; i++)
|
||||||
{
|
{
|
||||||
desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
||||||
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
|
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
desc->attrs = NULL;
|
|
||||||
|
|
||||||
if (constr)
|
if (constr)
|
||||||
{
|
{
|
||||||
@ -208,12 +204,9 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
|
|||||||
|
|
||||||
desc->constr = cpy;
|
desc->constr = cpy;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
desc->constr = NULL;
|
|
||||||
|
|
||||||
desc->tdtypeid = tupdesc->tdtypeid;
|
desc->tdtypeid = tupdesc->tdtypeid;
|
||||||
desc->tdtypmod = tupdesc->tdtypmod;
|
desc->tdtypmod = tupdesc->tdtypmod;
|
||||||
desc->tdhasoid = tupdesc->tdhasoid;
|
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
@ -226,10 +219,6 @@ FreeTupleDesc(TupleDesc tupdesc)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < tupdesc->natts; i++)
|
|
||||||
pfree(tupdesc->attrs[i]);
|
|
||||||
if (tupdesc->attrs)
|
|
||||||
pfree(tupdesc->attrs);
|
|
||||||
if (tupdesc->constr)
|
if (tupdesc->constr)
|
||||||
{
|
{
|
||||||
if (tupdesc->constr->num_defval > 0)
|
if (tupdesc->constr->num_defval > 0)
|
||||||
@ -379,12 +368,10 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/*
|
||||||
* TupleDescInitEntry
|
* TupleDescInitEntry
|
||||||
*
|
|
||||||
* This function initializes a single attribute structure in
|
* This function initializes a single attribute structure in
|
||||||
* a preallocated tuple descriptor.
|
* a previously allocated tuple descriptor.
|
||||||
* ----------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TupleDescInitEntry(TupleDesc desc,
|
TupleDescInitEntry(TupleDesc desc,
|
||||||
@ -404,18 +391,12 @@ TupleDescInitEntry(TupleDesc desc,
|
|||||||
AssertArg(PointerIsValid(desc));
|
AssertArg(PointerIsValid(desc));
|
||||||
AssertArg(attributeNumber >= 1);
|
AssertArg(attributeNumber >= 1);
|
||||||
AssertArg(attributeNumber <= desc->natts);
|
AssertArg(attributeNumber <= desc->natts);
|
||||||
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* allocate storage for this attribute
|
|
||||||
*/
|
|
||||||
|
|
||||||
att = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
||||||
desc->attrs[attributeNumber - 1] = att;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* initialize the attribute fields
|
* initialize the attribute fields
|
||||||
*/
|
*/
|
||||||
|
att = desc->attrs[attributeNumber - 1];
|
||||||
|
|
||||||
att->attrelid = 0; /* dummy value */
|
att->attrelid = 0; /* dummy value */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.245 2005/03/04 20:21:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.246 2005/03/07 04:42:16 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
@ -97,14 +97,11 @@ ConstructTupleDescriptor(Relation heapRelation,
|
|||||||
for (i = 0; i < numatts; i++)
|
for (i = 0; i < numatts; i++)
|
||||||
{
|
{
|
||||||
AttrNumber atnum = indexInfo->ii_KeyAttrNumbers[i];
|
AttrNumber atnum = indexInfo->ii_KeyAttrNumbers[i];
|
||||||
Form_pg_attribute to;
|
Form_pg_attribute to = indexTupDesc->attrs[i];
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Form_pg_type typeTup;
|
Form_pg_type typeTup;
|
||||||
Oid keyType;
|
Oid keyType;
|
||||||
|
|
||||||
indexTupDesc->attrs[i] = to =
|
|
||||||
(Form_pg_attribute) palloc0(ATTRIBUTE_TUPLE_SIZE);
|
|
||||||
|
|
||||||
if (atnum != 0)
|
if (atnum != 0)
|
||||||
{
|
{
|
||||||
/* Simple index column */
|
/* Simple index column */
|
||||||
@ -152,6 +149,8 @@ ConstructTupleDescriptor(Relation heapRelation,
|
|||||||
/* Expressional index */
|
/* Expressional index */
|
||||||
Node *indexkey;
|
Node *indexkey;
|
||||||
|
|
||||||
|
MemSet(to, 0, ATTRIBUTE_TUPLE_SIZE);
|
||||||
|
|
||||||
if (indexpr_item == NULL) /* shouldn't happen */
|
if (indexpr_item == NULL) /* shouldn't happen */
|
||||||
elog(ERROR, "too few entries in indexprs list");
|
elog(ERROR, "too few entries in indexprs list");
|
||||||
indexkey = (Node *) lfirst(indexpr_item);
|
indexkey = (Node *) lfirst(indexpr_item);
|
||||||
|
20
src/backend/utils/cache/relcache.c
vendored
20
src/backend/utils/cache/relcache.c
vendored
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.215 2005/01/10 20:02:23 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.216 2005/03/07 04:42:16 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -491,12 +491,8 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
|||||||
elog(ERROR, "invalid attribute number %d for %s",
|
elog(ERROR, "invalid attribute number %d for %s",
|
||||||
attp->attnum, RelationGetRelationName(relation));
|
attp->attnum, RelationGetRelationName(relation));
|
||||||
|
|
||||||
relation->rd_att->attrs[attp->attnum - 1] =
|
memcpy(relation->rd_att->attrs[attp->attnum - 1],
|
||||||
(Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
|
attp,
|
||||||
ATTRIBUTE_TUPLE_SIZE);
|
|
||||||
|
|
||||||
memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]),
|
|
||||||
(char *) attp,
|
|
||||||
ATTRIBUTE_TUPLE_SIZE);
|
ATTRIBUTE_TUPLE_SIZE);
|
||||||
|
|
||||||
/* Update constraint/default info */
|
/* Update constraint/default info */
|
||||||
@ -1338,9 +1334,8 @@ formrdesc(const char *relationName, Oid relationReltype,
|
|||||||
has_not_null = false;
|
has_not_null = false;
|
||||||
for (i = 0; i < natts; i++)
|
for (i = 0; i < natts; i++)
|
||||||
{
|
{
|
||||||
relation->rd_att->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
memcpy(relation->rd_att->attrs[i],
|
||||||
memcpy((char *) relation->rd_att->attrs[i],
|
&att[i],
|
||||||
(char *) &att[i],
|
|
||||||
ATTRIBUTE_TUPLE_SIZE);
|
ATTRIBUTE_TUPLE_SIZE);
|
||||||
has_not_null |= att[i].attnotnull;
|
has_not_null |= att[i].attnotnull;
|
||||||
/* make sure attcacheoff is valid */
|
/* make sure attcacheoff is valid */
|
||||||
@ -3044,9 +3039,8 @@ load_relcache_init_file(void)
|
|||||||
{
|
{
|
||||||
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
|
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
|
||||||
goto read_failed;
|
goto read_failed;
|
||||||
|
if (len != ATTRIBUTE_TUPLE_SIZE)
|
||||||
rel->rd_att->attrs[i] = (Form_pg_attribute) palloc(len);
|
goto read_failed;
|
||||||
|
|
||||||
if ((nread = fread(rel->rd_att->attrs[i], 1, len, fp)) != len)
|
if ((nread = fread(rel->rd_att->attrs[i], 1, len, fp)) != len)
|
||||||
goto read_failed;
|
goto read_failed;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.46 2004/12/31 22:03:21 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.47 2005/03/07 04:42:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -42,13 +42,17 @@ typedef struct tupleConstr
|
|||||||
} TupleConstr;
|
} TupleConstr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This structure contains all information (i.e. from Classes
|
* This struct is passed around within the backend to describe the structure
|
||||||
* pg_attribute, pg_attrdef, pg_constraint) for the structure of a tuple.
|
* of tuples. For tuples coming from on-disk relations, the information is
|
||||||
|
* collected from the pg_attribute, pg_attrdef, and pg_constraint catalogs.
|
||||||
|
* Transient row types (such as the result of a join query) have anonymous
|
||||||
|
* TupleDesc structs that generally omit any constraint info; therefore the
|
||||||
|
* structure is designed to let the constraints be omitted efficiently.
|
||||||
*
|
*
|
||||||
* Note that only user attributes, not system attributes, are mentioned in
|
* Note that only user attributes, not system attributes, are mentioned in
|
||||||
* TupleDesc; with the exception that tdhasoid indicates if OID is present.
|
* TupleDesc; with the exception that tdhasoid indicates if OID is present.
|
||||||
*
|
*
|
||||||
* If the tuple is known to correspond to a named rowtype (such as a table's
|
* If the tupdesc is known to correspond to a named rowtype (such as a table's
|
||||||
* rowtype) then tdtypeid identifies that type and tdtypmod is -1. Otherwise
|
* rowtype) then tdtypeid identifies that type and tdtypmod is -1. Otherwise
|
||||||
* tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous
|
* tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous
|
||||||
* row type, or a value >= 0 to allow the rowtype to be looked up in the
|
* row type, or a value >= 0 to allow the rowtype to be looked up in the
|
||||||
@ -56,13 +60,13 @@ typedef struct tupleConstr
|
|||||||
*/
|
*/
|
||||||
typedef struct tupleDesc
|
typedef struct tupleDesc
|
||||||
{
|
{
|
||||||
int natts; /* Number of attributes in the tuple */
|
int natts; /* number of attributes in the tuple */
|
||||||
Form_pg_attribute *attrs;
|
Form_pg_attribute *attrs;
|
||||||
/* attrs[N] is a pointer to the description of Attribute Number N+1. */
|
/* attrs[N] is a pointer to the description of Attribute Number N+1 */
|
||||||
TupleConstr *constr;
|
TupleConstr *constr; /* constraints, or NULL if none */
|
||||||
Oid tdtypeid; /* composite type ID for tuple type */
|
Oid tdtypeid; /* composite type ID for tuple type */
|
||||||
int32 tdtypmod; /* typmod for tuple type */
|
int32 tdtypmod; /* typmod for tuple type */
|
||||||
bool tdhasoid; /* Tuple has oid attribute in its header */
|
bool tdhasoid; /* tuple has oid attribute in its header */
|
||||||
} *TupleDesc;
|
} *TupleDesc;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user