mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
16
src/backend/access/common/Makefile.inc
Normal file
16
src/backend/access/common/Makefile.inc
Normal file
@@ -0,0 +1,16 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for access/common
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/access/common/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
SUBSRCS+= heaptuple.c heapvalid.c indextuple.c indexvalid.c printtup.c \
|
||||
scankey.c tupdesc.c
|
||||
|
||||
1011
src/backend/access/common/heaptuple.c
Normal file
1011
src/backend/access/common/heaptuple.c
Normal file
File diff suppressed because it is too large
Load Diff
134
src/backend/access/common/heapvalid.c
Normal file
134
src/backend/access/common/heapvalid.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* heapvalid.c--
|
||||
* heap tuple qualification validity checking code
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "c.h"
|
||||
|
||||
#include "access/htup.h"
|
||||
#include "access/skey.h"
|
||||
#include "access/heapam.h"
|
||||
#include "utils/tqual.h"
|
||||
#include "access/valid.h" /* where the declarations go */
|
||||
#include "access/xact.h"
|
||||
|
||||
#include "storage/buf.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "storage/bufpage.h"
|
||||
#include "storage/itemid.h"
|
||||
#include "fmgr.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
/* ----------------
|
||||
* heap_keytest
|
||||
*
|
||||
* Test a heap tuple with respect to a scan key.
|
||||
* ----------------
|
||||
*/
|
||||
bool
|
||||
heap_keytest(HeapTuple t,
|
||||
TupleDesc tupdesc,
|
||||
int nkeys,
|
||||
ScanKey keys)
|
||||
{
|
||||
bool isnull;
|
||||
Datum atp;
|
||||
int test;
|
||||
|
||||
for (; nkeys--; keys++) {
|
||||
atp = (Datum)heap_getattr(t, InvalidBuffer,
|
||||
keys->sk_attno,
|
||||
tupdesc,
|
||||
&isnull);
|
||||
|
||||
if (isnull)
|
||||
/* XXX eventually should check if SK_ISNULL */
|
||||
return false;
|
||||
|
||||
if (keys->sk_flags & SK_COMMUTE)
|
||||
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
|
||||
keys->sk_argument, atp);
|
||||
else
|
||||
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
|
||||
atp, keys->sk_argument);
|
||||
|
||||
if (!test == !(keys->sk_flags & SK_NEGATE))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* heap_tuple_satisfies
|
||||
*
|
||||
* Returns a valid HeapTuple if it satisfies the timequal and keytest.
|
||||
* Returns NULL otherwise. Used to be heap_satisifies (sic) which
|
||||
* returned a boolean. It now returns a tuple so that we can avoid doing two
|
||||
* PageGetItem's per tuple.
|
||||
*
|
||||
* Complete check of validity including LP_CTUP and keytest.
|
||||
* This should perhaps be combined with valid somehow in the
|
||||
* future. (Also, additional rule tests/time range tests.)
|
||||
*
|
||||
* on 8/21/92 mao says: i rearranged the tests here to do keytest before
|
||||
* SatisfiesTimeQual. profiling indicated that even for vacuumed relations,
|
||||
* time qual checking was more expensive than key testing. time qual is
|
||||
* least likely to fail, too. we should really add the time qual test to
|
||||
* the restriction and optimize it in the normal way. this has interactions
|
||||
* with joey's expensive function work.
|
||||
* ----------------
|
||||
*/
|
||||
HeapTuple
|
||||
heap_tuple_satisfies(ItemId itemId,
|
||||
Relation relation,
|
||||
PageHeader disk_page,
|
||||
TimeQual qual,
|
||||
int nKeys,
|
||||
ScanKey key)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
bool res;
|
||||
|
||||
if (! ItemIdIsUsed(itemId))
|
||||
return NULL;
|
||||
|
||||
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
|
||||
|
||||
if (key != NULL)
|
||||
res = heap_keytest(tuple, RelationGetTupleDescriptor(relation),
|
||||
nKeys, key);
|
||||
else
|
||||
res = TRUE;
|
||||
|
||||
if (res && (relation->rd_rel->relkind == RELKIND_UNCATALOGED
|
||||
|| HeapTupleSatisfiesTimeQual(tuple,qual)))
|
||||
return tuple;
|
||||
|
||||
return (HeapTuple) NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has
|
||||
* already been updated once by the current transaction/command
|
||||
* pair.
|
||||
*/
|
||||
bool
|
||||
TupleUpdatedByCurXactAndCmd(HeapTuple t)
|
||||
{
|
||||
if (TransactionIdEquals(t->t_xmax,
|
||||
GetCurrentTransactionId()) &&
|
||||
t->t_cmax == GetCurrentCommandId())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
427
src/backend/access/common/indextuple.c
Normal file
427
src/backend/access/common/indextuple.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* indextuple.c--
|
||||
* This file contains index tuple accessor and mutator routines,
|
||||
* as well as a few various tuple utilities.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "c.h"
|
||||
#include "access/ibit.h"
|
||||
#include "access/itup.h" /* where the declarations go */
|
||||
#include "access/heapam.h"
|
||||
#include "access/genam.h"
|
||||
#include "access/tupdesc.h"
|
||||
#include "access/tupmacs.h"
|
||||
|
||||
#include "storage/itemptr.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/palloc.h"
|
||||
|
||||
static Size IndexInfoFindDataOffset(unsigned short t_info);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* index_ tuple interface routines
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* index_formtuple
|
||||
* ----------------
|
||||
*/
|
||||
IndexTuple
|
||||
index_formtuple(TupleDesc tupleDescriptor,
|
||||
Datum value[],
|
||||
char null[])
|
||||
{
|
||||
register char *tp; /* tuple pointer */
|
||||
IndexTuple tuple; /* return tuple */
|
||||
Size size, hoff;
|
||||
int i;
|
||||
unsigned short infomask = 0;
|
||||
bool hasnull = false;
|
||||
char tupmask = 0;
|
||||
int numberOfAttributes = tupleDescriptor->natts;
|
||||
|
||||
if (numberOfAttributes > MaxIndexAttributeNumber)
|
||||
elog(WARN, "index_formtuple: numberOfAttributes of %d > %d",
|
||||
numberOfAttributes, MaxIndexAttributeNumber);
|
||||
|
||||
|
||||
for (i = 0; i < numberOfAttributes && !hasnull; i++) {
|
||||
if (null[i] != ' ') hasnull = true;
|
||||
}
|
||||
|
||||
if (hasnull) infomask |= INDEX_NULL_MASK;
|
||||
|
||||
hoff = IndexInfoFindDataOffset(infomask);
|
||||
size = hoff
|
||||
+ ComputeDataSize(tupleDescriptor,
|
||||
value, null);
|
||||
size = DOUBLEALIGN(size); /* be conservative */
|
||||
|
||||
tp = (char *) palloc(size);
|
||||
tuple = (IndexTuple) tp;
|
||||
memset(tp,0,(int)size);
|
||||
|
||||
DataFill((char *)tp + hoff,
|
||||
tupleDescriptor,
|
||||
value,
|
||||
null,
|
||||
&tupmask,
|
||||
(hasnull ? (bits8*)tp + sizeof(*tuple) : NULL));
|
||||
|
||||
/*
|
||||
* We do this because DataFill wants to initialize a "tupmask" which
|
||||
* is used for HeapTuples, but we want an indextuple infomask. The only
|
||||
* "relevent" info is the "has variable attributes" field, which is in
|
||||
* mask position 0x02. We have already set the null mask above.
|
||||
*/
|
||||
|
||||
if (tupmask & 0x02) infomask |= INDEX_VAR_MASK;
|
||||
|
||||
/*
|
||||
* Here we make sure that we can actually hold the size. We also want
|
||||
* to make sure that size is not aligned oddly. This actually is a
|
||||
* rather odd way to make sure the size is not too large overall.
|
||||
*/
|
||||
|
||||
if (size & 0xE000)
|
||||
elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
|
||||
|
||||
|
||||
infomask |= size;
|
||||
|
||||
/* ----------------
|
||||
* initialize metadata
|
||||
* ----------------
|
||||
*/
|
||||
tuple->t_info = infomask;
|
||||
return (tuple);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* fastgetiattr
|
||||
*
|
||||
* This is a newer version of fastgetiattr which attempts to be
|
||||
* faster by caching attribute offsets in the attribute descriptor.
|
||||
*
|
||||
* an alternate way to speed things up would be to cache offsets
|
||||
* with the tuple, but that seems more difficult unless you take
|
||||
* the storage hit of actually putting those offsets into the
|
||||
* tuple you send to disk. Yuck.
|
||||
*
|
||||
* This scheme will be slightly slower than that, but should
|
||||
* preform well for queries which hit large #'s of tuples. After
|
||||
* you cache the offsets once, examining all the other tuples using
|
||||
* the same attribute descriptor will go much quicker. -cim 5/4/91
|
||||
* ----------------
|
||||
*/
|
||||
char *
|
||||
fastgetiattr(IndexTuple tup,
|
||||
int attnum,
|
||||
TupleDesc tupleDesc,
|
||||
bool *isnull)
|
||||
{
|
||||
register char *tp; /* ptr to att in tuple */
|
||||
register char *bp; /* ptr to att in tuple */
|
||||
int slow; /* do we have to walk nulls? */
|
||||
register int data_off; /* tuple data offset */
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
Assert(PointerIsValid(isnull));
|
||||
Assert(attnum > 0);
|
||||
|
||||
/* ----------------
|
||||
* Three cases:
|
||||
*
|
||||
* 1: No nulls and no variable length attributes.
|
||||
* 2: Has a null or a varlena AFTER att.
|
||||
* 3: Has nulls or varlenas BEFORE att.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
*isnull = false;
|
||||
data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup :
|
||||
IndexInfoFindDataOffset(tup->t_info);
|
||||
|
||||
if (IndexTupleNoNulls(tup)) {
|
||||
|
||||
/* first attribute is always at position zero */
|
||||
|
||||
if (attnum == 1) {
|
||||
return(fetchatt(&(tupleDesc->attrs[0]), (char *) tup + data_off));
|
||||
}
|
||||
attnum--;
|
||||
|
||||
if (tupleDesc->attrs[attnum]->attcacheoff > 0) {
|
||||
return(fetchatt(&(tupleDesc->attrs[attnum]),
|
||||
(char *) tup + data_off +
|
||||
tupleDesc->attrs[attnum]->attcacheoff));
|
||||
}
|
||||
|
||||
tp = (char *) tup + data_off;
|
||||
|
||||
slow = 0;
|
||||
}else { /* there's a null somewhere in the tuple */
|
||||
|
||||
bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are here! */
|
||||
slow = 0;
|
||||
/* ----------------
|
||||
* check to see if desired att is null
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
attnum--;
|
||||
{
|
||||
if (att_isnull(attnum, bp)) {
|
||||
*isnull = true;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* ----------------
|
||||
* Now check to see if any preceeding bits are null...
|
||||
* ----------------
|
||||
*/
|
||||
{
|
||||
register int i = 0; /* current offset in bp */
|
||||
register int mask; /* bit in byte we're looking at */
|
||||
register char n; /* current byte in bp */
|
||||
register int byte, finalbit;
|
||||
|
||||
byte = attnum >> 3;
|
||||
finalbit = attnum & 0x07;
|
||||
|
||||
for (; i <= byte; i++) {
|
||||
n = bp[i];
|
||||
if (i < byte) {
|
||||
/* check for nulls in any "earlier" bytes */
|
||||
if ((~n) != 0) {
|
||||
slow++;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* check for nulls "before" final bit of last byte*/
|
||||
mask = (finalbit << 1) - 1;
|
||||
if ((~n) & mask)
|
||||
slow++;
|
||||
}
|
||||
}
|
||||
}
|
||||
tp = (char *) tup + data_off;
|
||||
}
|
||||
|
||||
/* now check for any non-fixed length attrs before our attribute */
|
||||
|
||||
if (!slow) {
|
||||
if (tupleDesc->attrs[attnum]->attcacheoff > 0) {
|
||||
return(fetchatt(&(tupleDesc->attrs[attnum]),
|
||||
tp + tupleDesc->attrs[attnum]->attcacheoff));
|
||||
}else if (!IndexTupleAllFixed(tup)) {
|
||||
register int j = 0;
|
||||
|
||||
for (j = 0; j < attnum && !slow; j++)
|
||||
if (tupleDesc->attrs[j]->attlen < 1) slow = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if slow is zero, and we got here, we know that we have a tuple with
|
||||
* no nulls. We also know that we have to initialize the remainder of
|
||||
* the attribute cached offset values.
|
||||
*/
|
||||
|
||||
if (!slow) {
|
||||
register int j = 1;
|
||||
register long off;
|
||||
|
||||
/*
|
||||
* need to set cache for some atts
|
||||
*/
|
||||
|
||||
tupleDesc->attrs[0]->attcacheoff = 0;
|
||||
|
||||
while (tupleDesc->attrs[j]->attcacheoff > 0) j++;
|
||||
|
||||
off = tupleDesc->attrs[j-1]->attcacheoff +
|
||||
tupleDesc->attrs[j-1]->attlen;
|
||||
|
||||
for (; j < attnum + 1; j++) {
|
||||
/*
|
||||
* Fix me when going to a machine with more than a four-byte
|
||||
* word!
|
||||
*/
|
||||
|
||||
switch(tupleDesc->attrs[j]->attlen)
|
||||
{
|
||||
case -1:
|
||||
off = (tupleDesc->attrs[j]->attalign=='d')?
|
||||
DOUBLEALIGN(off):INTALIGN(off);
|
||||
break;
|
||||
case sizeof(char):
|
||||
break;
|
||||
case sizeof(short):
|
||||
off = SHORTALIGN(off);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
off = INTALIGN(off);
|
||||
break;
|
||||
default:
|
||||
if (tupleDesc->attrs[j]->attlen > sizeof(int32))
|
||||
off = (tupleDesc->attrs[j]->attalign=='d')?
|
||||
DOUBLEALIGN(off) : LONGALIGN(off);
|
||||
else
|
||||
elog(WARN, "fastgetiattr: attribute %d has len %d",
|
||||
j, tupleDesc->attrs[j]->attlen);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
tupleDesc->attrs[j]->attcacheoff = off;
|
||||
off += tupleDesc->attrs[j]->attlen;
|
||||
}
|
||||
|
||||
return(fetchatt( &(tupleDesc->attrs[attnum]),
|
||||
tp + tupleDesc->attrs[attnum]->attcacheoff));
|
||||
}else {
|
||||
register bool usecache = true;
|
||||
register int off = 0;
|
||||
register int i;
|
||||
|
||||
/*
|
||||
* Now we know that we have to walk the tuple CAREFULLY.
|
||||
*/
|
||||
|
||||
for (i = 0; i < attnum; i++) {
|
||||
if (!IndexTupleNoNulls(tup)) {
|
||||
if (att_isnull(i, bp)) {
|
||||
usecache = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (usecache && tupleDesc->attrs[i]->attcacheoff > 0) {
|
||||
off = tupleDesc->attrs[i]->attcacheoff;
|
||||
if (tupleDesc->attrs[i]->attlen == -1)
|
||||
usecache = false;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
if (usecache) tupleDesc->attrs[i]->attcacheoff = off;
|
||||
switch(tupleDesc->attrs[i]->attlen)
|
||||
{
|
||||
case sizeof(char):
|
||||
off++;
|
||||
break;
|
||||
case sizeof(short):
|
||||
off = SHORTALIGN(off) + sizeof(short);
|
||||
break;
|
||||
case -1:
|
||||
usecache = false;
|
||||
off = (tupleDesc->attrs[i]->attalign=='d')?
|
||||
DOUBLEALIGN(off):INTALIGN(off);
|
||||
off += VARSIZE(tp + off);
|
||||
break;
|
||||
default:
|
||||
if (tupleDesc->attrs[i]->attlen > sizeof(int32))
|
||||
off = (tupleDesc->attrs[i]->attalign=='d') ?
|
||||
DOUBLEALIGN(off) + tupleDesc->attrs[i]->attlen :
|
||||
LONGALIGN(off) + tupleDesc->attrs[i]->attlen;
|
||||
else
|
||||
elog(WARN, "fastgetiattr2: attribute %d has len %d",
|
||||
i, tupleDesc->attrs[i]->attlen);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(fetchatt(&tupleDesc->attrs[attnum], tp + off));
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_getattr
|
||||
* ----------------
|
||||
*/
|
||||
Datum
|
||||
index_getattr(IndexTuple tuple,
|
||||
AttrNumber attNum,
|
||||
TupleDesc tupDesc,
|
||||
bool *isNullOutP)
|
||||
{
|
||||
Assert (attNum > 0);
|
||||
|
||||
return (Datum)
|
||||
fastgetiattr(tuple, attNum, tupDesc, isNullOutP);
|
||||
}
|
||||
|
||||
RetrieveIndexResult
|
||||
FormRetrieveIndexResult(ItemPointer indexItemPointer,
|
||||
ItemPointer heapItemPointer)
|
||||
{
|
||||
RetrieveIndexResult result;
|
||||
|
||||
Assert(ItemPointerIsValid(indexItemPointer));
|
||||
Assert(ItemPointerIsValid(heapItemPointer));
|
||||
|
||||
result = (RetrieveIndexResult) palloc(sizeof *result);
|
||||
|
||||
result->index_iptr = *indexItemPointer;
|
||||
result->heap_iptr = *heapItemPointer;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes an infomask as argument (primarily because this needs to be usable
|
||||
* at index_formtuple time so enough space is allocated).
|
||||
*
|
||||
* Change me if adding an attribute to IndexTuples!!!!!!!!!!!
|
||||
*/
|
||||
static Size
|
||||
IndexInfoFindDataOffset(unsigned short t_info)
|
||||
{
|
||||
if (!(t_info & INDEX_NULL_MASK))
|
||||
return((Size) sizeof(IndexTupleData));
|
||||
else {
|
||||
Size size = sizeof(IndexTupleData);
|
||||
|
||||
if (t_info & INDEX_NULL_MASK) {
|
||||
size += sizeof(IndexAttributeBitMapData);
|
||||
}
|
||||
return DOUBLEALIGN(size); /* be conservative */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies source into target. If *target == NULL, we palloc space; otherwise
|
||||
* we assume we have space that is already palloc'ed.
|
||||
*/
|
||||
void
|
||||
CopyIndexTuple(IndexTuple source, IndexTuple *target)
|
||||
{
|
||||
Size size;
|
||||
IndexTuple ret;
|
||||
|
||||
size = IndexTupleSize(source);
|
||||
if (*target == NULL) {
|
||||
*target = (IndexTuple) palloc(size);
|
||||
}
|
||||
|
||||
ret = *target;
|
||||
memmove((char*)ret, (char*)source, size);
|
||||
}
|
||||
|
||||
84
src/backend/access/common/indexvalid.c
Normal file
84
src/backend/access/common/indexvalid.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* indexvalid.c--
|
||||
* index tuple qualification validity checking code
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "executor/execdebug.h"
|
||||
#include "access/genam.h"
|
||||
#include "access/iqual.h" /* where the declarations go */
|
||||
#include "access/itup.h"
|
||||
#include "access/skey.h"
|
||||
|
||||
#include "storage/buf.h"
|
||||
#include "storage/bufpage.h"
|
||||
#include "storage/itemid.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* index scan key qualification code
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
int NIndexTupleProcessed;
|
||||
|
||||
/* ----------------
|
||||
* index_keytest
|
||||
*
|
||||
* old comments
|
||||
* May eventually combine with other tests (like timeranges)?
|
||||
* Should have Buffer buffer; as an argument and pass it to amgetattr.
|
||||
* ----------------
|
||||
*/
|
||||
bool
|
||||
index_keytest(IndexTuple tuple,
|
||||
TupleDesc tupdesc,
|
||||
int scanKeySize,
|
||||
ScanKey key)
|
||||
{
|
||||
bool isNull;
|
||||
Datum datum;
|
||||
int test;
|
||||
|
||||
IncrIndexProcessed();
|
||||
|
||||
while (scanKeySize > 0) {
|
||||
datum = index_getattr(tuple,
|
||||
1,
|
||||
tupdesc,
|
||||
&isNull);
|
||||
|
||||
if (isNull) {
|
||||
/* XXX eventually should check if SK_ISNULL */
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_COMMUTE) {
|
||||
test = (int) (*(key[0].sk_func))
|
||||
(DatumGetPointer(key[0].sk_argument),
|
||||
datum);
|
||||
} else {
|
||||
test = (int) (*(key[0].sk_func))
|
||||
(datum,
|
||||
DatumGetPointer(key[0].sk_argument));
|
||||
}
|
||||
|
||||
if (!test == !(key[0].sk_flags & SK_NEGATE)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
scanKeySize -= 1;
|
||||
key++;
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
306
src/backend/access/common/printtup.c
Normal file
306
src/backend/access/common/printtup.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* printtup.c--
|
||||
* Routines to print out tuples to the destination (binary or non-binary
|
||||
* portals, frontend/interactive backend, etc.).
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <sys/file.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "access/htup.h"
|
||||
#include "access/skey.h"
|
||||
#include "access/printtup.h"
|
||||
#include "access/tupdesc.h"
|
||||
#include "storage/buf.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "fmgr.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
#include "utils/syscache.h"
|
||||
#include "catalog/pg_type.h"
|
||||
|
||||
#include "libpq/libpq.h"
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* printtup / debugtup support
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* typtoout - used by printtup and debugtup
|
||||
* ----------------
|
||||
*/
|
||||
Oid
|
||||
typtoout(Oid type)
|
||||
{
|
||||
HeapTuple typeTuple;
|
||||
|
||||
typeTuple = SearchSysCacheTuple(TYPOID,
|
||||
ObjectIdGetDatum(type),
|
||||
0, 0, 0);
|
||||
|
||||
if (HeapTupleIsValid(typeTuple))
|
||||
return((Oid)
|
||||
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
|
||||
|
||||
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
|
||||
return(InvalidOid);
|
||||
}
|
||||
|
||||
Oid
|
||||
gettypelem(Oid type)
|
||||
{
|
||||
HeapTuple typeTuple;
|
||||
|
||||
typeTuple = SearchSysCacheTuple(TYPOID,
|
||||
ObjectIdGetDatum(type),
|
||||
0,0,0);
|
||||
|
||||
if (HeapTupleIsValid(typeTuple))
|
||||
return((Oid)
|
||||
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
|
||||
|
||||
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
|
||||
return(InvalidOid);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printtup
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
printtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
{
|
||||
int i, j, k;
|
||||
char *outputstr, *attr;
|
||||
bool isnull;
|
||||
Oid typoutput;
|
||||
|
||||
/* ----------------
|
||||
* tell the frontend to expect new tuple data
|
||||
* ----------------
|
||||
*/
|
||||
pq_putnchar("D", 1);
|
||||
|
||||
/* ----------------
|
||||
* send a bitmap of which attributes are null
|
||||
* ----------------
|
||||
*/
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
for (i = 0; i < tuple->t_natts; ) {
|
||||
attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
|
||||
if (!isnull)
|
||||
j |= k;
|
||||
k >>= 1;
|
||||
if (!(i & 7)) {
|
||||
pq_putint(j, 1);
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
}
|
||||
}
|
||||
if (i & 7)
|
||||
pq_putint(j, 1);
|
||||
|
||||
/* ----------------
|
||||
* send the attributes of this tuple
|
||||
* ----------------
|
||||
*/
|
||||
for (i = 0; i < tuple->t_natts; ++i) {
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
|
||||
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
|
||||
|
||||
if (!isnull && OidIsValid(typoutput)) {
|
||||
outputstr = fmgr(typoutput, attr,
|
||||
gettypelem(typeinfo->attrs[i]->atttypid));
|
||||
pq_putint(strlen(outputstr)+4, 4);
|
||||
pq_putnchar(outputstr, strlen(outputstr));
|
||||
pfree(outputstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printatt
|
||||
* ----------------
|
||||
*/
|
||||
static void
|
||||
printatt(unsigned attributeId,
|
||||
AttributeTupleForm attributeP,
|
||||
char *value)
|
||||
{
|
||||
printf("\t%2d: %.*s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
|
||||
attributeId,
|
||||
NAMEDATALEN, /* attname is a char16 */
|
||||
attributeP->attname.data,
|
||||
value != NULL ? " = \"" : "",
|
||||
value != NULL ? value : "",
|
||||
value != NULL ? "\"" : "",
|
||||
(unsigned int) (attributeP->atttypid),
|
||||
attributeP->attlen,
|
||||
attributeP->attbyval ? 't' : 'f');
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* showatts
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
showatts(char *name, TupleDesc tupleDesc)
|
||||
{
|
||||
int i;
|
||||
int natts = tupleDesc->natts;
|
||||
AttributeTupleForm *attinfo = tupleDesc->attrs;
|
||||
|
||||
puts(name);
|
||||
for (i = 0; i < natts; ++i)
|
||||
printatt((unsigned) i+1, attinfo[i], (char *) NULL);
|
||||
printf("\t----\n");
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* debugtup
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
debugtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
{
|
||||
register int i;
|
||||
char *attr, *value;
|
||||
bool isnull;
|
||||
Oid typoutput;
|
||||
|
||||
for (i = 0; i < tuple->t_natts; ++i) {
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
|
||||
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
|
||||
|
||||
if (!isnull && OidIsValid(typoutput)) {
|
||||
value = fmgr(typoutput, attr,
|
||||
gettypelem(typeinfo->attrs[i]->atttypid));
|
||||
printatt((unsigned) i+1, typeinfo->attrs[i], value);
|
||||
pfree(value);
|
||||
}
|
||||
}
|
||||
printf("\t----\n");
|
||||
}
|
||||
|
||||
/*#define IPORTAL_DEBUG*/
|
||||
|
||||
/* ----------------
|
||||
* printtup_internal
|
||||
* Protocol expects either T, D, C, E, or N.
|
||||
* We use a different data prefix, e.g. 'B' instead of 'D' to
|
||||
* indicate a tuple in internal (binary) form.
|
||||
*
|
||||
* This is same as printtup, except we don't use the typout func.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
printtup_internal(HeapTuple tuple, TupleDesc typeinfo)
|
||||
{
|
||||
int i, j, k;
|
||||
char *attr;
|
||||
bool isnull;
|
||||
|
||||
/* ----------------
|
||||
* tell the frontend to expect new tuple data
|
||||
* ----------------
|
||||
*/
|
||||
pq_putnchar("B", 1);
|
||||
|
||||
/* ----------------
|
||||
* send a bitmap of which attributes are null
|
||||
* ----------------
|
||||
*/
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
for (i = 0; i < tuple->t_natts; ) {
|
||||
attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
|
||||
if (!isnull)
|
||||
j |= k;
|
||||
k >>= 1;
|
||||
if (!(i & 7)) {
|
||||
pq_putint(j, 1);
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
}
|
||||
}
|
||||
if (i & 7)
|
||||
pq_putint(j, 1);
|
||||
|
||||
/* ----------------
|
||||
* send the attributes of this tuple
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts);
|
||||
#endif
|
||||
for (i = 0; i < tuple->t_natts; ++i) {
|
||||
int32 len = typeinfo->attrs[i]->attlen;
|
||||
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
|
||||
if (!isnull) {
|
||||
/* # of bytes, and opaque data */
|
||||
if (len == -1) {
|
||||
/* variable length, assume a varlena structure */
|
||||
len = VARSIZE(attr) - VARHDRSZ;
|
||||
|
||||
pq_putint(len, sizeof(int32));
|
||||
pq_putnchar(VARDATA(attr), len);
|
||||
#ifdef IPORTAL_DEBUG
|
||||
{
|
||||
char *d = VARDATA(attr);
|
||||
|
||||
fprintf(stderr, "length %d data %x%x%x%x\n",
|
||||
len, *d, *(d+1), *(d+2), *(d+3));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* fixed size */
|
||||
if (typeinfo->attrs[i]->attbyval) {
|
||||
int8 i8;
|
||||
int16 i16;
|
||||
int32 i32;
|
||||
|
||||
pq_putint(len, sizeof(int32));
|
||||
switch (len) {
|
||||
case sizeof(int8):
|
||||
i8 = DatumGetChar(attr);
|
||||
pq_putnchar((char *) &i8, len);
|
||||
break;
|
||||
case sizeof(int16):
|
||||
i16 = DatumGetInt16(attr);
|
||||
pq_putnchar((char *) &i16, len);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
i32 = DatumGetInt32(attr);
|
||||
pq_putnchar((char *) &i32, len);
|
||||
break;
|
||||
}
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "byval length %d data %d\n", len, attr);
|
||||
#endif
|
||||
} else {
|
||||
pq_putint(len, sizeof(int32));
|
||||
pq_putnchar(attr, len);
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "byref length %d data %x\n", len, attr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/backend/access/common/scankey.c
Normal file
68
src/backend/access/common/scankey.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* scan.c--
|
||||
* scan direction and key code
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "c.h"
|
||||
#include "access/sdir.h"
|
||||
#include "access/attnum.h"
|
||||
#include "access/skey.h"
|
||||
|
||||
#include "fmgr.h"
|
||||
|
||||
/*
|
||||
* ScanKeyEntryIsLegal --
|
||||
* True iff the scan key entry is legal.
|
||||
*/
|
||||
#define ScanKeyEntryIsLegal(entry) \
|
||||
((bool) (AssertMacro(PointerIsValid(entry)) && \
|
||||
AttributeNumberIsValid(entry->sk_attno)))
|
||||
|
||||
/*
|
||||
* ScanKeyEntrySetIllegal --
|
||||
* Marks a scan key entry as illegal.
|
||||
*/
|
||||
void
|
||||
ScanKeyEntrySetIllegal(ScanKey entry)
|
||||
{
|
||||
|
||||
Assert(PointerIsValid(entry));
|
||||
|
||||
entry->sk_flags = 0; /* just in case... */
|
||||
entry->sk_attno = InvalidAttrNumber;
|
||||
entry->sk_procedure = 0; /* should be InvalidRegProcedure */
|
||||
}
|
||||
|
||||
/*
|
||||
* ScanKeyEntryInitialize --
|
||||
* Initializes an scan key entry.
|
||||
*
|
||||
* Note:
|
||||
* Assumes the scan key entry is valid.
|
||||
* Assumes the intialized scan key entry will be legal.
|
||||
*/
|
||||
void
|
||||
ScanKeyEntryInitialize(ScanKey entry,
|
||||
bits16 flags,
|
||||
AttrNumber attributeNumber,
|
||||
RegProcedure procedure,
|
||||
Datum argument)
|
||||
{
|
||||
Assert(PointerIsValid(entry));
|
||||
|
||||
entry->sk_flags = flags;
|
||||
entry->sk_attno = attributeNumber;
|
||||
entry->sk_procedure = procedure;
|
||||
entry->sk_argument = argument;
|
||||
fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs);
|
||||
|
||||
Assert(ScanKeyEntryIsLegal(entry));
|
||||
}
|
||||
398
src/backend/access/common/tupdesc.c
Normal file
398
src/backend/access/common/tupdesc.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tupdesc.c--
|
||||
* POSTGRES tuple descriptor support code
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $
|
||||
*
|
||||
* NOTES
|
||||
* some of the executor utility code such as "ExecTypeFromTL" should be
|
||||
* moved here.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <stdio.h> /* for sprintf() */
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
#include "access/attnum.h"
|
||||
#include "access/htup.h"
|
||||
#include "access/tupdesc.h"
|
||||
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/elog.h" /* XXX generate exceptions instead */
|
||||
#include "utils/palloc.h"
|
||||
|
||||
#include "utils/syscache.h"
|
||||
#include "catalog/pg_type.h"
|
||||
|
||||
#include "nodes/primnodes.h"
|
||||
|
||||
#include "parser/catalog_utils.h"
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* CreateTemplateTupleDesc
|
||||
*
|
||||
* This function allocates and zeros a tuple descriptor structure.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
CreateTemplateTupleDesc(int natts)
|
||||
{
|
||||
uint32 size;
|
||||
TupleDesc desc;
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(natts >= 1);
|
||||
|
||||
/* ----------------
|
||||
* allocate enough memory for the tuple descriptor and
|
||||
* zero it as TupleDescInitEntry assumes that the descriptor
|
||||
* is filled with NULL pointers.
|
||||
* ----------------
|
||||
*/
|
||||
size = natts * sizeof (AttributeTupleForm);
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->attrs = (AttributeTupleForm*) palloc(size);
|
||||
memset(desc->attrs, 0, size);
|
||||
|
||||
desc->natts = natts;
|
||||
|
||||
return (desc);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* CreateTupleDesc
|
||||
*
|
||||
* This function allocates a new TupleDesc from AttributeTupleForm array
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
CreateTupleDesc(int natts, AttributeTupleForm* attrs)
|
||||
{
|
||||
TupleDesc desc;
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(natts >= 1);
|
||||
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->attrs = attrs;
|
||||
desc->natts = natts;
|
||||
|
||||
|
||||
return (desc);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* CreateTupleDescCopy
|
||||
*
|
||||
* This function creates a new TupleDesc by copying from an existing
|
||||
* TupleDesc
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
CreateTupleDescCopy(TupleDesc tupdesc)
|
||||
{
|
||||
TupleDesc desc;
|
||||
int i, size;
|
||||
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->natts = tupdesc->natts;
|
||||
size = desc->natts * sizeof (AttributeTupleForm);
|
||||
desc->attrs = (AttributeTupleForm*) palloc(size);
|
||||
for (i=0;i<desc->natts;i++) {
|
||||
desc->attrs[i] =
|
||||
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
memmove(desc->attrs[i],
|
||||
tupdesc->attrs[i],
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TupleDescInitEntry
|
||||
*
|
||||
* This function initializes a single attribute structure in
|
||||
* a preallocated tuple descriptor.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
TupleDescInitEntry(TupleDesc desc,
|
||||
AttrNumber attributeNumber,
|
||||
char *attributeName,
|
||||
char *typeName,
|
||||
int attdim,
|
||||
bool attisset)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
TypeTupleForm typeForm;
|
||||
AttributeTupleForm att;
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(PointerIsValid(desc));
|
||||
AssertArg(attributeNumber >= 1);
|
||||
/* attributeName's are sometimes NULL,
|
||||
from resdom's. I don't know why that is, though -- Jolly */
|
||||
/* AssertArg(NameIsValid(attributeName));*/
|
||||
/* AssertArg(NameIsValid(typeName));*/
|
||||
|
||||
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
|
||||
|
||||
|
||||
/* ----------------
|
||||
* allocate storage for this attribute
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
desc->attrs[attributeNumber - 1] = att;
|
||||
|
||||
/* ----------------
|
||||
* initialize some of the attribute fields
|
||||
* ----------------
|
||||
*/
|
||||
att->attrelid = 0; /* dummy value */
|
||||
|
||||
if (attributeName != NULL)
|
||||
namestrcpy(&(att->attname), attributeName);
|
||||
else
|
||||
memset(att->attname.data,0,NAMEDATALEN);
|
||||
|
||||
|
||||
att->attdefrel = 0; /* dummy value */
|
||||
att->attnvals = 0; /* dummy value */
|
||||
att->atttyparg = 0; /* dummy value */
|
||||
att->attbound = 0; /* dummy value */
|
||||
att->attcanindex = 0; /* dummy value */
|
||||
att->attproc = 0; /* dummy value */
|
||||
att->attcacheoff = -1;
|
||||
|
||||
att->attnum = attributeNumber;
|
||||
att->attnelems = attdim;
|
||||
att->attisset = attisset;
|
||||
|
||||
/* ----------------
|
||||
* search the system cache for the type tuple of the attribute
|
||||
* we are creating so that we can get the typeid and some other
|
||||
* stuff.
|
||||
*
|
||||
* Note: in the special case of
|
||||
*
|
||||
* create EMP (name = char16, manager = EMP)
|
||||
*
|
||||
* RelationNameCreateHeapRelation() calls BuildDesc() which
|
||||
* calls this routine and since EMP does not exist yet, the
|
||||
* system cache lookup below fails. That's fine, but rather
|
||||
* then doing a elog(WARN) we just leave that information
|
||||
* uninitialized, return false, then fix things up later.
|
||||
* -cim 6/14/90
|
||||
* ----------------
|
||||
*/
|
||||
tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName),
|
||||
0,0,0);
|
||||
if (! HeapTupleIsValid(tuple)) {
|
||||
/* ----------------
|
||||
* here type info does not exist yet so we just fill
|
||||
* the attribute with dummy information and return false.
|
||||
* ----------------
|
||||
*/
|
||||
att->atttypid = InvalidOid;
|
||||
att->attlen = (int16) 0;
|
||||
att->attbyval = (bool) 0;
|
||||
att->attalign = 'i';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* type info exists so we initialize our attribute
|
||||
* information from the type tuple we found..
|
||||
* ----------------
|
||||
*/
|
||||
typeForm = (TypeTupleForm) GETSTRUCT(tuple);
|
||||
|
||||
att->atttypid = tuple->t_oid;
|
||||
att->attalign = typeForm->typalign;
|
||||
|
||||
/* ------------------------
|
||||
If this attribute is a set, what is really stored in the
|
||||
attribute is the OID of a tuple in the pg_proc catalog.
|
||||
The pg_proc tuple contains the query string which defines
|
||||
this set - i.e., the query to run to get the set.
|
||||
So the atttypid (just assigned above) refers to the type returned
|
||||
by this query, but the actual length of this attribute is the
|
||||
length (size) of an OID.
|
||||
|
||||
Why not just make the atttypid point to the OID type, instead
|
||||
of the type the query returns? Because the executor uses the atttypid
|
||||
to tell the front end what type will be returned (in BeginCommand),
|
||||
and in the end the type returned will be the result of the query, not
|
||||
an OID.
|
||||
|
||||
Why not wait until the return type of the set is known (i.e., the
|
||||
recursive call to the executor to execute the set has returned)
|
||||
before telling the front end what the return type will be? Because
|
||||
the executor is a delicate thing, and making sure that the correct
|
||||
order of front-end commands is maintained is messy, especially
|
||||
considering that target lists may change as inherited attributes
|
||||
are considered, etc. Ugh.
|
||||
-----------------------------------------
|
||||
*/
|
||||
if (attisset) {
|
||||
Type t = type("oid");
|
||||
att->attlen = tlen(t);
|
||||
att->attbyval = tbyval(t);
|
||||
} else {
|
||||
att->attlen = typeForm->typlen;
|
||||
att->attbyval = typeForm->typbyval;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TupleDescMakeSelfReference
|
||||
*
|
||||
* This function initializes a "self-referential" attribute like
|
||||
* manager in "create EMP (name=text, manager = EMP)".
|
||||
* It calls TypeShellMake() which inserts a "shell" type
|
||||
* tuple into pg_type. A self-reference is one kind of set, so
|
||||
* its size and byval are the same as for a set. See the comments
|
||||
* above in TupleDescInitEntry.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static void
|
||||
TupleDescMakeSelfReference(TupleDesc desc,
|
||||
AttrNumber attnum,
|
||||
char *relname)
|
||||
{
|
||||
AttributeTupleForm att;
|
||||
Type t = type("oid");
|
||||
|
||||
att = desc->attrs[attnum-1];
|
||||
att->atttypid = TypeShellMake(relname);
|
||||
att->attlen = tlen(t);
|
||||
att->attbyval = tbyval(t);
|
||||
att->attnelems = 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* BuildDescForRelation
|
||||
*
|
||||
* This is a general purpose function identical to BuildDesc
|
||||
* but is used by the DefineRelation() code to catch the
|
||||
* special case where you
|
||||
*
|
||||
* create FOO ( ..., x = FOO )
|
||||
*
|
||||
* here, the initial type lookup for "x = FOO" will fail
|
||||
* because FOO isn't in the catalogs yet. But since we
|
||||
* are creating FOO, instead of doing an elog() we add
|
||||
* a shell type tuple to pg_type and fix things later
|
||||
* in amcreate().
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
BuildDescForRelation(List *schema, char *relname)
|
||||
{
|
||||
int natts;
|
||||
AttrNumber attnum;
|
||||
List *p;
|
||||
TupleDesc desc;
|
||||
char *attname;
|
||||
char *typename;
|
||||
int attdim;
|
||||
bool attisset;
|
||||
|
||||
/* ----------------
|
||||
* allocate a new tuple descriptor
|
||||
* ----------------
|
||||
*/
|
||||
natts = length(schema);
|
||||
desc = CreateTemplateTupleDesc(natts);
|
||||
|
||||
attnum = 0;
|
||||
|
||||
typename = palloc(NAMEDATALEN+1);
|
||||
|
||||
foreach(p, schema) {
|
||||
ColumnDef *entry;
|
||||
List *arry;
|
||||
|
||||
/* ----------------
|
||||
* for each entry in the list, get the name and type
|
||||
* information from the list and have TupleDescInitEntry
|
||||
* fill in the attribute information we need.
|
||||
* ----------------
|
||||
*/
|
||||
attnum++;
|
||||
|
||||
entry = lfirst(p);
|
||||
attname = entry->colname;
|
||||
arry = entry->typename->arrayBounds;
|
||||
attisset = entry->typename->setof;
|
||||
|
||||
if (arry != NIL) {
|
||||
char buf[20];
|
||||
|
||||
attdim = length(arry);
|
||||
|
||||
/* array of XXX is _XXX (inherited from release 3) */
|
||||
sprintf(buf, "_%.*s", NAMEDATALEN, entry->typename->name);
|
||||
strcpy(typename, buf);
|
||||
} else {
|
||||
strcpy(typename, entry->typename->name);
|
||||
attdim = 0;
|
||||
}
|
||||
|
||||
if (! TupleDescInitEntry(desc, attnum, attname,
|
||||
typename, attdim, attisset)) {
|
||||
/* ----------------
|
||||
* if TupleDescInitEntry() fails, it means there is
|
||||
* no type in the system catalogs. So now we check if
|
||||
* the type name equals the relation name. If so we
|
||||
* have a self reference, otherwise it's an error.
|
||||
* ----------------
|
||||
*/
|
||||
if (!strcmp(typename, relname)) {
|
||||
TupleDescMakeSelfReference(desc, attnum, relname);
|
||||
} else
|
||||
elog(WARN, "DefineRelation: no such type %.*s",
|
||||
NAMEDATALEN, typename);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is for char() and varchar(). When an entry is of type
|
||||
* char() or varchar(), typlen is set to the appropriate length,
|
||||
* which we'll use here instead. (The catalog lookup only returns
|
||||
* the length of bpchar and varchar which is not what we want!)
|
||||
* - ay 6/95
|
||||
*/
|
||||
if (entry->typename->typlen > 0) {
|
||||
desc->attrs[attnum - 1]->attlen = entry->typename->typlen;
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user