コード例 #1
0
ファイル: variant.c プロジェクト: decibel/variant
Datum
variant_image_eq(PG_FUNCTION_ARGS)
{
    Variant	l = (Variant) PG_GETARG_DATUM(0);
    Variant	r = (Variant) PG_GETARG_DATUM(1);
    int			cmp;

    /*
     * To avoid detoasting we use _ANY variations on VAR*, but that means we must
     * make sure to use VARSIZE_ANY_EXHDR, *not* VARSIZE_ANY!
     */
    if(VARSIZE_ANY_EXHDR(l) != VARSIZE_ANY_EXHDR(r))
        PG_RETURN_BOOL(false);

    /*
     * At this point we need to detoast. We could theoretically leave data
     * compressed, but since there's no direct support for that we don't bother.
     */
    l = (Variant) PG_DETOAST_DATUM_PACKED(l);
    r = (Variant) PG_DETOAST_DATUM_PACKED(r);
    cmp = memcmp(VARDATA_ANY(l), VARDATA_ANY(r), VARSIZE_ANY_EXHDR(l));

    PG_FREE_IF_COPY(l, 0);
    PG_FREE_IF_COPY(r, 1);

    PG_RETURN_BOOL( cmp == 0 ? true : false);
}
コード例 #2
0
ファイル: variant.c プロジェクト: decibel/variant
Datum
variant_hash(PG_FUNCTION_ARGS)
{
    Variant	v = (Variant) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
    char	   *data;
    int			len;
    Datum		result;

    Assert(fcinfo->flinfo->fn_strict); /* Must be strict */

    data = VARDATA_ANY(v);
    len = VARSIZE_ANY_EXHDR(v);

    result = hash_any((unsigned char *) data, len);

    /* Avoid leaking memory for toasted inputs */
    PG_FREE_IF_COPY(v, 0);

    return result;
}
コード例 #3
0
ファイル: variant.c プロジェクト: decibel/variant
/*
 * Create an external variant from our internal representation
 */
static Variant
make_variant(VariantInt vi, FunctionCallInfo fcinfo, IOFuncSelector func)
{
    VariantCache	*cache;
    Variant				v;
    bool					oid_overflow=OID_TOO_LARGE(vi->typid);
    long					variant_length, data_length; /* long because we subtract */
    Pointer				data_ptr = 0;
    uint					flags = 0;

    cache = get_cache(fcinfo, vi, func);
    Assert(cache->typid = vi->typid);

#ifdef VARIANT_TEST_OID
    vi->typid += OID_MASK;
    oid_overflow=OID_TOO_LARGE(vi->typid);
#endif

    if(vi->isnull)
    {
        flags |= VAR_ISNULL;
        data_length = 0;
    }
    else if(cache->typlen == -1) /* varlena */
    {
        /*
         * Short varlena is OK, but we need to make sure it's not external. It's OK
         * to leave compressed varlena's alone too, but detoast_packed will
         * uncompress them. We'll just follow rangetype.c's lead here.
         */
        vi->data = PointerGetDatum( PG_DETOAST_DATUM_PACKED(vi->data) );
        data_ptr = DatumGetPointer(vi->data);

        /*
         * Because we don't store varlena aligned or with it's header, our
         * data_length is simply the varlena length.
         */
        data_length = VARSIZE_ANY_EXHDR(vi->data);
        data_ptr = VARDATA_ANY(data_ptr);
    }
    else if(cache->typlen == -2) /* cstring */
    {
        data_length = strlen(DatumGetCString(vi->data)); /* We don't store NUL terminator */
        data_ptr = DatumGetPointer(vi->data);
    }
    else
    {
        Assert(cache->typlen >= 0);
        if(cache->typbyval)
        {
            data_length = VHDRSZ; /* Start with header size to make sure alignment is correct */
            data_length = (long) VDATAPTR_ALIGN(data_length, cache->typalign);
            data_length += cache->typlen;
            data_length -= VHDRSZ;
        }
        else /* fixed length, pass by reference */
        {
            data_length = cache->typlen;
            data_ptr = DatumGetPointer(vi->data);
        }
    }

    /* If typid is too large then we need an extra byte */
    variant_length = VHDRSZ + data_length + (oid_overflow ? sizeof(char) : 0);
    if( variant_length < 0 )
        elog(ERROR, "Negative variant_length %li", variant_length);

    v = palloc0(variant_length);
    SET_VARSIZE(v, variant_length);
    v->pOid = vi->typid;
    v->typmod = vi->typmod;

    if(oid_overflow)
    {
        flags |= VAR_OVERFLOW;

        /* Reset high pOid byte to zero */
        v->pOid &= 0x00FFFFFF;

        /* Store high byte of OID at the end of our structure */
        *((char *) v + VARSIZE(v) - 1) = vi->typid >> 24;
    }

    /*
     * Be careful not to overwrite the valid OID data
     */
    v->pOid |= flags;
    Assert( get_oid(v, &flags) == vi->typid );

    if(!vi->isnull)
    {
        if(cache->typbyval)
        {
            Pointer p = VDATAPTR_ALIGN(v, cache->typalign);
            store_att_byval(p, vi->data, cache->typlen);
        }
        else
            memcpy(VDATAPTR(v), data_ptr, data_length);
    }

    return v;
}
コード例 #4
0
ファイル: rowtypes.c プロジェクト: PJMODOS/postgres
/*
 * record_image_eq :
 *		  compares two records for identical contents, based on byte images
 * result :
 *		  returns true if the records are identical, false otherwise.
 *
 * Note: we do not use record_image_cmp here, since we can avoid
 * de-toasting for unequal lengths this way.
 */
Datum
record_image_eq(PG_FUNCTION_ARGS)
{
	HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
	HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
	bool		result = true;
	Oid			tupType1;
	Oid			tupType2;
	int32		tupTypmod1;
	int32		tupTypmod2;
	TupleDesc	tupdesc1;
	TupleDesc	tupdesc2;
	HeapTupleData tuple1;
	HeapTupleData tuple2;
	int			ncolumns1;
	int			ncolumns2;
	RecordCompareData *my_extra;
	int			ncols;
	Datum	   *values1;
	Datum	   *values2;
	bool	   *nulls1;
	bool	   *nulls2;
	int			i1;
	int			i2;
	int			j;

	/* Extract type info from the tuples */
	tupType1 = HeapTupleHeaderGetTypeId(record1);
	tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
	tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
	ncolumns1 = tupdesc1->natts;
	tupType2 = HeapTupleHeaderGetTypeId(record2);
	tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
	tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
	ncolumns2 = tupdesc2->natts;

	/* Build temporary HeapTuple control structures */
	tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
	ItemPointerSetInvalid(&(tuple1.t_self));
	tuple1.t_tableOid = InvalidOid;
	tuple1.t_data = record1;
	tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
	ItemPointerSetInvalid(&(tuple2.t_self));
	tuple2.t_tableOid = InvalidOid;
	tuple2.t_data = record2;

	/*
	 * We arrange to look up the needed comparison info just once per series
	 * of calls, assuming the record types don't change underneath us.
	 */
	ncols = Max(ncolumns1, ncolumns2);
	my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
	if (my_extra == NULL ||
		my_extra->ncolumns < ncols)
	{
		fcinfo->flinfo->fn_extra =
			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
							   offsetof(RecordCompareData, columns) +
							   ncols * sizeof(ColumnCompareData));
		my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
		my_extra->ncolumns = ncols;
		my_extra->record1_type = InvalidOid;
		my_extra->record1_typmod = 0;
		my_extra->record2_type = InvalidOid;
		my_extra->record2_typmod = 0;
	}

	if (my_extra->record1_type != tupType1 ||
		my_extra->record1_typmod != tupTypmod1 ||
		my_extra->record2_type != tupType2 ||
		my_extra->record2_typmod != tupTypmod2)
	{
		MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
		my_extra->record1_type = tupType1;
		my_extra->record1_typmod = tupTypmod1;
		my_extra->record2_type = tupType2;
		my_extra->record2_typmod = tupTypmod2;
	}

	/* Break down the tuples into fields */
	values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
	nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
	heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
	values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
	nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
	heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);

	/*
	 * Scan corresponding columns, allowing for dropped columns in different
	 * places in the two rows.  i1 and i2 are physical column indexes, j is
	 * the logical column index.
	 */
	i1 = i2 = j = 0;
	while (i1 < ncolumns1 || i2 < ncolumns2)
	{
		/*
		 * Skip dropped columns
		 */
		if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
		{
			i1++;
			continue;
		}
		if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
		{
			i2++;
			continue;
		}
		if (i1 >= ncolumns1 || i2 >= ncolumns2)
			break;				/* we'll deal with mismatch below loop */

		/*
		 * Have two matching columns, they must be same type
		 */
		if (tupdesc1->attrs[i1]->atttypid !=
			tupdesc2->attrs[i2]->atttypid)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("cannot compare dissimilar column types %s and %s at record column %d",
							format_type_be(tupdesc1->attrs[i1]->atttypid),
							format_type_be(tupdesc2->attrs[i2]->atttypid),
							j + 1)));

		/*
		 * We consider two NULLs equal; NULL > not-NULL.
		 */
		if (!nulls1[i1] || !nulls2[i2])
		{
			if (nulls1[i1] || nulls2[i2])
			{
				result = false;
				break;
			}

			/* Compare the pair of elements */
			if (tupdesc1->attrs[i1]->attlen == -1)
			{
				Size		len1,
							len2;

				len1 = toast_raw_datum_size(values1[i1]);
				len2 = toast_raw_datum_size(values2[i2]);
				/* No need to de-toast if lengths don't match. */
				if (len1 != len2)
					result = false;
				else
				{
					struct varlena *arg1val;
					struct varlena *arg2val;

					arg1val = PG_DETOAST_DATUM_PACKED(values1[i1]);
					arg2val = PG_DETOAST_DATUM_PACKED(values2[i2]);

					result = (memcmp(VARDATA_ANY(arg1val),
									 VARDATA_ANY(arg2val),
									 len1 - VARHDRSZ) == 0);

					/* Only free memory if it's a copy made here. */
					if ((Pointer) arg1val != (Pointer) values1[i1])
						pfree(arg1val);
					if ((Pointer) arg2val != (Pointer) values2[i2])
						pfree(arg2val);
				}
			}
			else if (tupdesc1->attrs[i1]->attbyval)
			{
				switch (tupdesc1->attrs[i1]->attlen)
				{
					case 1:
						result = (GET_1_BYTE(values1[i1]) ==
								  GET_1_BYTE(values2[i2]));
						break;
					case 2:
						result = (GET_2_BYTES(values1[i1]) ==
								  GET_2_BYTES(values2[i2]));
						break;
					case 4:
						result = (GET_4_BYTES(values1[i1]) ==
								  GET_4_BYTES(values2[i2]));
						break;
#if SIZEOF_DATUM == 8
					case 8:
						result = (GET_8_BYTES(values1[i1]) ==
								  GET_8_BYTES(values2[i2]));
						break;
#endif
					default:
						Assert(false);	/* cannot happen */
				}
			}
			else
			{
				result = (memcmp(DatumGetPointer(values1[i1]),
								 DatumGetPointer(values2[i2]),
								 tupdesc1->attrs[i1]->attlen) == 0);
			}
			if (!result)
				break;
		}

		/* equal, so continue to next column */
		i1++, i2++, j++;
	}

	/*
	 * If we didn't break out of the loop early, check for column count
	 * mismatch.  (We do not report such mismatch if we found unequal column
	 * values; is that a feature or a bug?)
	 */
	if (result)
	{
		if (i1 != ncolumns1 || i2 != ncolumns2)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("cannot compare record types with different numbers of columns")));
	}

	pfree(values1);
	pfree(nulls1);
	pfree(values2);
	pfree(nulls2);
	ReleaseTupleDesc(tupdesc1);
	ReleaseTupleDesc(tupdesc2);

	/* Avoid leaking memory when handed toasted input. */
	PG_FREE_IF_COPY(record1, 0);
	PG_FREE_IF_COPY(record2, 1);

	PG_RETURN_BOOL(result);
}
コード例 #5
0
ファイル: tupser.c プロジェクト: phan-pivotal/gpdb
/*
 * Convert a HeapTuple into a byte-sequence, and store it directly
 * into a chunklist for transmission.
 *
 * This code is based on the printtup_internal_20() function in printtup.c.
 */
void
SerializeTupleIntoChunks(GenericTuple gtuple, SerTupInfo *pSerInfo, TupleChunkList tcList)
{
	TupleChunkListItem tcItem = NULL;
	MemoryContext oldCtxt;
	TupleDesc	tupdesc;
	int			i,
		natts;

	AssertArg(tcList != NULL);
	AssertArg(gtuple != NULL);
	AssertArg(pSerInfo != NULL);

	tupdesc = pSerInfo->tupdesc;
	natts = tupdesc->natts;

	/* get ready to go */
	tcList->p_first = NULL;
	tcList->p_last = NULL;
	tcList->num_chunks = 0;
	tcList->serialized_data_length = 0;
	tcList->max_chunk_length = Gp_max_tuple_chunk_size;

	if (natts == 0)
	{
		tcItem = getChunkFromCache(&pSerInfo->chunkCache);
		if (tcItem == NULL)
		{
			ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY),
							errmsg("Could not allocate space for first chunk item in new chunk list.")));
		}

		/* TC_EMTPY is just one chunk */
		SetChunkType(tcItem->chunk_data, TC_EMPTY);
		tcItem->chunk_length = TUPLE_CHUNK_HEADER_SIZE;
		appendChunkToTCList(tcList, tcItem);

		return;
	}

	tcItem = getChunkFromCache(&pSerInfo->chunkCache);
	if (tcItem == NULL)
	{
		ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY),
						errmsg("Could not allocate space for first chunk item in new chunk list.")));
	}

	/* assume that we'll take a single chunk */
	SetChunkType(tcItem->chunk_data, TC_WHOLE);
	tcItem->chunk_length = TUPLE_CHUNK_HEADER_SIZE;
	appendChunkToTCList(tcList, tcItem);

	AssertState(s_tupSerMemCtxt != NULL);

	if (is_memtuple(gtuple))
	{
		MemTuple mtuple = (MemTuple) gtuple;
		addByteStringToChunkList(tcList, (char *) mtuple, memtuple_get_size(mtuple), &pSerInfo->chunkCache);
		addPadding(tcList, &pSerInfo->chunkCache, memtuple_get_size(mtuple));
	}
	else
	{
		HeapTuple tuple = (HeapTuple) gtuple;
		HeapTupleHeader t_data = tuple->t_data;
		TupSerHeader tsh;

		unsigned int	datalen;
		unsigned int	nullslen;

		datalen = tuple->t_len - t_data->t_hoff;
		if (HeapTupleHasNulls(tuple))
			nullslen = BITMAPLEN(HeapTupleHeaderGetNatts(t_data));
		else
			nullslen = 0;

		tsh.tuplen = sizeof(TupSerHeader) + TYPEALIGN(TUPLE_CHUNK_ALIGN,nullslen) + datalen;
		tsh.natts = HeapTupleHeaderGetNatts(t_data);
		tsh.infomask = t_data->t_infomask;

		addByteStringToChunkList(tcList, (char *)&tsh, sizeof(TupSerHeader), &pSerInfo->chunkCache);
		/* If we don't have any attributes which have been toasted, we
		 * can be very very simple: just send the raw data. */
		if ((tsh.infomask & HEAP_HASEXTERNAL) == 0)
		{
			if (nullslen)
			{
				addByteStringToChunkList(tcList, (char *)t_data->t_bits, nullslen, &pSerInfo->chunkCache);
				addPadding(tcList,&pSerInfo->chunkCache,nullslen);
			}

			addByteStringToChunkList(tcList, (char *)t_data + t_data->t_hoff, datalen, &pSerInfo->chunkCache);
			addPadding(tcList,&pSerInfo->chunkCache,datalen);
		}
		else
		{
			/* We have to be more careful when we have tuples that
			 * have been toasted. Ideally we'd like to send the
			 * untoasted attributes in as "raw" a format as possible
			 * but that makes rebuilding the tuple harder .
			 */
			oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt);

			/* deconstruct the tuple (faster than a heap_getattr loop) */
			heap_deform_tuple(tuple, tupdesc, pSerInfo->values, pSerInfo->nulls);

			MemoryContextSwitchTo(oldCtxt);

			/* Send the nulls character-array. */
			addByteStringToChunkList(tcList, pSerInfo->nulls, natts, &pSerInfo->chunkCache);
			addPadding(tcList,&pSerInfo->chunkCache,natts);

			/*
			 * send the attributes of this tuple: NOTE anything which allocates
			 * temporary space (e.g. could result in a PG_DETOAST_DATUM) should be
			 * executed with the memory context set to s_tupSerMemCtxt
			 */
			for (i = 0; i < natts; ++i)
			{
				SerAttrInfo *attrInfo = pSerInfo->myinfo + i;
				Datum		origattr = pSerInfo->values[i],
					attr;

				/* skip null attributes (already taken care of above) */
				if (pSerInfo->nulls[i])
					continue;

				if (attrInfo->typlen == -1)
				{
					int32		sz;
					char	   *data;

					/*
					 * If we have a toasted datum, forcibly detoast it here to avoid
					 * memory leakage: we want to force the detoast allocation(s) to
					 * happen in our reset-able serialization context.
					 */
					oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt);
					attr = PointerGetDatum(PG_DETOAST_DATUM_PACKED(origattr));
					MemoryContextSwitchTo(oldCtxt);

					sz = VARSIZE_ANY_EXHDR(attr);
					data = VARDATA_ANY(attr);

					/* Send length first, then data */
					addInt32ToChunkList(tcList, sz, &pSerInfo->chunkCache);
					addByteStringToChunkList(tcList, data, sz, &pSerInfo->chunkCache);
					addPadding(tcList, &pSerInfo->chunkCache, sz);
				}
				else if (attrInfo->typlen == -2)
				{
					int32		sz;
					char	   *data;

					/* CString, we would send the string with the terminating '\0' */
					data = DatumGetCString(origattr);
					sz = strlen(data) + 1;

					/* Send length first, then data */
					addInt32ToChunkList(tcList, sz, &pSerInfo->chunkCache);
					addByteStringToChunkList(tcList, data, sz, &pSerInfo->chunkCache);
					addPadding(tcList, &pSerInfo->chunkCache, sz);
				}
				else if (attrInfo->typbyval)
				{
					/*
					 * We send a full-width Datum for all pass-by-value types, regardless of
					 * the actual size.
					 */
					addByteStringToChunkList(tcList, (char *) &origattr, sizeof(Datum), &pSerInfo->chunkCache);
					addPadding(tcList, &pSerInfo->chunkCache, sizeof(Datum));
				}
				else
				{
					addByteStringToChunkList(tcList, DatumGetPointer(origattr), attrInfo->typlen, &pSerInfo->chunkCache);
					addPadding(tcList, &pSerInfo->chunkCache, attrInfo->typlen);

					attr = origattr;
				}
			}

			MemoryContextReset(s_tupSerMemCtxt);
		}
	}

	/*
	 * if we have more than 1 chunk we have to set the chunk types on our
	 * first chunk and last chunk
	 */
	if (tcList->num_chunks > 1)
	{
		TupleChunkListItem first,
			last;

		first = tcList->p_first;
		last = tcList->p_last;

		Assert(first != NULL);
		Assert(first != last);
		Assert(last != NULL);

		SetChunkType(first->chunk_data, TC_PARTIAL_START);
		SetChunkType(last->chunk_data, TC_PARTIAL_END);

		/*
		 * any intervening chunks are already set to TC_PARTIAL_MID when
		 * allocated
		 */
	}

	return;
}