Пример #1
0
void dump_bvf1_inputs(ArrayType *broker_list_p, text *sector_name_p) {
	int ndim, nitems;
	int *dim;

	int16 typlen;
	bool typbyval;
	char typalign;

	int i;

	char *broker_list;

	ndim = ARR_NDIM(broker_list_p);
	dim = ARR_DIMS(broker_list_p);
	nitems = ArrayGetNItems(ndim, dim);
	get_typlenbyvalalign(ARR_ELEMTYPE(broker_list_p), &typlen, &typbyval,
			&typalign);

	broker_list = ARR_DATA_PTR(broker_list_p);
	elog(NOTICE, "BVF1: INPUTS START");
	for (i = 0; i < nitems; i++) {
		elog(NOTICE, "BVF1: broker_list[%d] %s", i,
				DatumGetCString(DirectFunctionCall1(textout,
				PointerGetDatum(broker_list))));
		broker_list = att_addlength_pointer(broker_list, typlen,
				broker_list);
		broker_list = (char *) att_align_nominal(broker_list, typalign);
	}
	elog(NOTICE, "BVF1: sector_name %s",
			DatumGetCString(DirectFunctionCall1(textout,
			PointerGetDatum(sector_name_p))));
	elog(NOTICE, "BVF1: INPUTS END");
}
Пример #2
0
/*
 * brin_deconstruct_tuple
 *		Guts of attribute extraction from an on-disk BRIN tuple.
 *
 * Its arguments are:
 *	brdesc		BRIN descriptor for the stored tuple
 *	tp			pointer to the tuple data area
 *	nullbits	pointer to the tuple nulls bitmask
 *	nulls		"has nulls" bit in tuple infomask
 *	values		output values, array of size brdesc->bd_totalstored
 *	allnulls	output "allnulls", size brdesc->bd_tupdesc->natts
 *	hasnulls	output "hasnulls", size brdesc->bd_tupdesc->natts
 *
 * Output arrays must have been allocated by caller.
 */
static inline void
brin_deconstruct_tuple(BrinDesc *brdesc,
					   char *tp, bits8 *nullbits, bool nulls,
					   Datum *values, bool *allnulls, bool *hasnulls)
{
	int			attnum;
	int			stored;
	TupleDesc	diskdsc;
	long		off;

	/*
	 * First iterate to natts to obtain both null flags for each attribute.
	 * Note that we reverse the sense of the att_isnull test, because we store
	 * 1 for a null value (rather than a 1 for a not null value as is the
	 * att_isnull convention used elsewhere.)  See brin_form_tuple.
	 */
	for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++)
	{
		/*
		 * the "all nulls" bit means that all values in the page range for
		 * this column are nulls.  Therefore there are no values in the tuple
		 * data area.
		 */
		allnulls[attnum] = nulls && !att_isnull(attnum, nullbits);

		/*
		 * the "has nulls" bit means that some tuples have nulls, but others
		 * have not-null values.  Therefore we know the tuple contains data
		 * for this column.
		 *
		 * The hasnulls bits follow the allnulls bits in the same bitmask.
		 */
		hasnulls[attnum] =
			nulls && !att_isnull(brdesc->bd_tupdesc->natts + attnum, nullbits);
	}

	/*
	 * Iterate to obtain each attribute's stored values.  Note that since we
	 * may reuse attribute entries for more than one column, we cannot cache
	 * offsets here.
	 */
	diskdsc = brtuple_disk_tupdesc(brdesc);
	stored = 0;
	off = 0;
	for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++)
	{
		int			datumno;

		if (allnulls[attnum])
		{
			stored += brdesc->bd_info[attnum]->oi_nstored;
			continue;
		}

		for (datumno = 0;
			 datumno < brdesc->bd_info[attnum]->oi_nstored;
			 datumno++)
		{
			Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored);

			if (thisatt->attlen == -1)
			{
				off = att_align_pointer(off, thisatt->attalign, -1,
										tp + off);
			}
			else
			{
				/* not varlena, so safe to use att_align_nominal */
				off = att_align_nominal(off, thisatt->attalign);
			}

			values[stored++] = fetchatt(thisatt, tp + off);

			off = att_addlength_pointer(off, thisatt->attlen, tp + off);
		}
	}
}
Пример #3
0
/* ----------------
 *		nocache_index_getattr
 *
 *		This gets called from index_getattr() macro, and only in cases
 *		where we can't use cacheoffset and the value is not null.
 *
 *		This caches attribute offsets in the attribute descriptor.
 *
 *		An alternative 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
 *		perform 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
 * ----------------
 */
Datum
nocache_index_getattr(IndexTuple tup,
					  int attnum,
					  TupleDesc tupleDesc)
{
	Form_pg_attribute *att = tupleDesc->attrs;
	char	   *tp;				/* ptr to data part of tuple */
	bits8	   *bp = NULL;		/* ptr to null bitmap in tuple */
	bool		slow = false;	/* do we have to walk attrs? */
	int			data_off;		/* tuple data offset */
	int			off;			/* current offset within data */

	/* ----------------
	 *	 Three cases:
	 *
	 *	 1: No nulls and no variable-width attributes.
	 *	 2: Has a null or a var-width AFTER att.
	 *	 3: Has nulls or var-widths BEFORE att.
	 * ----------------
	 */

	data_off = IndexInfoFindDataOffset(tup->t_info);

	attnum--;

	if (IndexTupleHasNulls(tup))
	{
		/*
		 * there's a null somewhere in the tuple
		 *
		 * check to see if desired att is null
		 */

		/* XXX "knows" t_bits are just after fixed tuple header! */
		bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData));

		/*
		 * Now check to see if any preceding bits are null...
		 */
		{
			int			byte = attnum >> 3;
			int			finalbit = attnum & 0x07;

			/* check for nulls "before" final bit of last byte */
			if ((~bp[byte]) & ((1 << finalbit) - 1))
				slow = true;
			else
			{
				/* check for nulls in any "earlier" bytes */
				int			i;

				for (i = 0; i < byte; i++)
				{
					if (bp[i] != 0xFF)
					{
						slow = true;
						break;
					}
				}
			}
		}
	}

	tp = (char *) tup + data_off;

	if (!slow)
	{
		/*
		 * If we get here, there are no nulls up to and including the target
		 * attribute.  If we have a cached offset, we can use it.
		 */
		if (att[attnum]->attcacheoff >= 0)
		{
			return fetchatt(att[attnum],
							tp + att[attnum]->attcacheoff);
		}

		/*
		 * Otherwise, check for non-fixed-length attrs up to and including
		 * target.  If there aren't any, it's safe to cheaply initialize the
		 * cached offsets for these attrs.
		 */
		if (IndexTupleHasVarwidths(tup))
		{
			int			j;

			for (j = 0; j <= attnum; j++)
			{
				if (att[j]->attlen <= 0)
				{
					slow = true;
					break;
				}
			}
		}
	}

	if (!slow)
	{
		int			natts = tupleDesc->natts;
		int			j = 1;

		/*
		 * If we get here, we have a tuple with no nulls or var-widths up to
		 * and including the target attribute, so we can use the cached offset
		 * ... only we don't have it yet, or we'd not have got here.  Since
		 * it's cheap to compute offsets for fixed-width columns, we take the
		 * opportunity to initialize the cached offsets for *all* the leading
		 * fixed-width columns, in hope of avoiding future visits to this
		 * routine.
		 */
		att[0]->attcacheoff = 0;

		/* we might have set some offsets in the slow path previously */
		while (j < natts && att[j]->attcacheoff > 0)
			j++;

		off = att[j - 1]->attcacheoff + att[j - 1]->attlen;

		for (; j < natts; j++)
		{
			if (att[j]->attlen <= 0)
				break;

			off = att_align_nominal(off, att[j]->attalign);

			att[j]->attcacheoff = off;

			off += att[j]->attlen;
		}

		Assert(j > attnum);

		off = att[attnum]->attcacheoff;
	}
	else
	{
		bool		usecache = true;
		int			i;

		/*
		 * Now we know that we have to walk the tuple CAREFULLY.  But we still
		 * might be able to cache some offsets for next time.
		 *
		 * Note - This loop is a little tricky.  For each non-null attribute,
		 * we have to first account for alignment padding before the attr,
		 * then advance over the attr based on its length.  Nulls have no
		 * storage and no alignment padding either.  We can use/set
		 * attcacheoff until we reach either a null or a var-width attribute.
		 */
		off = 0;
		for (i = 0;; i++)		/* loop exit is at "break" */
		{
			if (IndexTupleHasNulls(tup) && att_isnull(i, bp))
			{
				usecache = false;
				continue;		/* this cannot be the target att */
			}

			/* If we know the next offset, we can skip the rest */
			if (usecache && att[i]->attcacheoff >= 0)
				off = att[i]->attcacheoff;
			else if (att[i]->attlen == -1)
			{
				/*
				 * We can only cache the offset for a varlena attribute if the
				 * offset is already suitably aligned, so that there would be
				 * no pad bytes in any case: then the offset will be valid for
				 * either an aligned or unaligned value.
				 */
				if (usecache &&
					off == att_align_nominal(off, att[i]->attalign))
					att[i]->attcacheoff = off;
				else
				{
					off = att_align_pointer(off, att[i]->attalign, -1,
											tp + off);
					usecache = false;
				}
			}
			else
			{
				/* not varlena, so safe to use att_align_nominal */
				off = att_align_nominal(off, att[i]->attalign);

				if (usecache)
					att[i]->attcacheoff = off;
			}

			if (i == attnum)
				break;

			off = att_addlength_pointer(off, att[i]->attlen, tp + off);

			if (usecache && att[i]->attlen <= 0)
				usecache = false;
		}
	}

	return fetchatt(att[attnum], tp + off);
}
Пример #4
0
/*
 * Convert an index tuple into Datum/isnull arrays.
 *
 * The caller must allocate sufficient storage for the output arrays.
 * (INDEX_MAX_KEYS entries should be enough.)
 *
 * This is nearly the same as heap_deform_tuple(), but for IndexTuples.
 * One difference is that the tuple should never have any missing columns.
 */
void
index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor,
				   Datum *values, bool *isnull)
{
	int			hasnulls = IndexTupleHasNulls(tup);
	int			natts = tupleDescriptor->natts; /* number of atts to extract */
	int			attnum;
	char	   *tp;				/* ptr to tuple data */
	int			off;			/* offset in tuple data */
	bits8	   *bp;				/* ptr to null bitmap in tuple */
	bool		slow = false;	/* can we use/set attcacheoff? */

	/* Assert to protect callers who allocate fixed-size arrays */
	Assert(natts <= INDEX_MAX_KEYS);

	/* XXX "knows" t_bits are just after fixed tuple header! */
	bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData));

	tp = (char *) tup + IndexInfoFindDataOffset(tup->t_info);
	off = 0;

	for (attnum = 0; attnum < natts; attnum++)
	{
		Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, attnum);

		if (hasnulls && att_isnull(attnum, bp))
		{
			values[attnum] = (Datum) 0;
			isnull[attnum] = true;
			slow = true;		/* can't use attcacheoff anymore */
			continue;
		}

		isnull[attnum] = false;

		if (!slow && thisatt->attcacheoff >= 0)
			off = thisatt->attcacheoff;
		else if (thisatt->attlen == -1)
		{
			/*
			 * We can only cache the offset for a varlena attribute if the
			 * offset is already suitably aligned, so that there would be no
			 * pad bytes in any case: then the offset will be valid for either
			 * an aligned or unaligned value.
			 */
			if (!slow &&
				off == att_align_nominal(off, thisatt->attalign))
				thisatt->attcacheoff = off;
			else
			{
				off = att_align_pointer(off, thisatt->attalign, -1,
										tp + off);
				slow = true;
			}
		}
		else
		{
			/* not varlena, so safe to use att_align_nominal */
			off = att_align_nominal(off, thisatt->attalign);

			if (!slow)
				thisatt->attcacheoff = off;
		}

		values[attnum] = fetchatt(thisatt, tp + off);

		off = att_addlength_pointer(off, thisatt->attlen, tp + off);

		if (thisatt->attlen <= 0)
			slow = true;		/* can't use attcacheoff anymore */
	}
}
Пример #5
0
/*
 * tuple_data_split_internal
 *
 * Split raw tuple data taken directly from a page into an array of bytea
 * elements. This routine does a lookup on NULL values and creates array
 * elements accordingly. This is a reimplementation of nocachegetattr()
 * in heaptuple.c simplified for educational purposes.
 */
static Datum
tuple_data_split_internal(Oid relid, char *tupdata,
						  uint16 tupdata_len, uint16 t_infomask,
						  uint16 t_infomask2, bits8 *t_bits,
						  bool do_detoast)
{
	ArrayBuildState *raw_attrs;
	int			nattrs;
	int			i;
	int			off = 0;
	Relation	rel;
	TupleDesc	tupdesc;

	/* Get tuple descriptor from relation OID */
	rel = relation_open(relid, AccessShareLock);
	tupdesc = RelationGetDescr(rel);

	raw_attrs = initArrayResult(BYTEAOID, CurrentMemoryContext, false);
	nattrs = tupdesc->natts;

	if (nattrs < (t_infomask2 & HEAP_NATTS_MASK))
		ereport(ERROR,
				(errcode(ERRCODE_DATA_CORRUPTED),
				 errmsg("number of attributes in tuple header is greater than number of attributes in tuple descriptor")));

	for (i = 0; i < nattrs; i++)
	{
		Form_pg_attribute attr;
		bool		is_null;
		bytea	   *attr_data = NULL;

		attr = TupleDescAttr(tupdesc, i);

		/*
		 * Tuple header can specify less attributes than tuple descriptor as
		 * ALTER TABLE ADD COLUMN without DEFAULT keyword does not actually
		 * change tuples in pages, so attributes with numbers greater than
		 * (t_infomask2 & HEAP_NATTS_MASK) should be treated as NULL.
		 */
		if (i >= (t_infomask2 & HEAP_NATTS_MASK))
			is_null = true;
		else
			is_null = (t_infomask & HEAP_HASNULL) && att_isnull(i, t_bits);

		if (!is_null)
		{
			int			len;

			if (attr->attlen == -1)
			{
				off = att_align_pointer(off, attr->attalign, -1,
										tupdata + off);

				/*
				 * As VARSIZE_ANY throws an exception if it can't properly
				 * detect the type of external storage in macros VARTAG_SIZE,
				 * this check is repeated to have a nicer error handling.
				 */
				if (VARATT_IS_EXTERNAL(tupdata + off) &&
					!VARATT_IS_EXTERNAL_ONDISK(tupdata + off) &&
					!VARATT_IS_EXTERNAL_INDIRECT(tupdata + off))
					ereport(ERROR,
							(errcode(ERRCODE_DATA_CORRUPTED),
							 errmsg("first byte of varlena attribute is incorrect for attribute %d", i)));

				len = VARSIZE_ANY(tupdata + off);
			}
			else
			{
				off = att_align_nominal(off, attr->attalign);
				len = attr->attlen;
			}

			if (tupdata_len < off + len)
				ereport(ERROR,
						(errcode(ERRCODE_DATA_CORRUPTED),
						 errmsg("unexpected end of tuple data")));

			if (attr->attlen == -1 && do_detoast)
				attr_data = DatumGetByteaPCopy(tupdata + off);
			else
			{
				attr_data = (bytea *) palloc(len + VARHDRSZ);
				SET_VARSIZE(attr_data, len + VARHDRSZ);
				memcpy(VARDATA(attr_data), tupdata + off, len);
			}

			off = att_addlength_pointer(off, attr->attlen,
										tupdata + off);
		}

		raw_attrs = accumArrayResult(raw_attrs, PointerGetDatum(attr_data),
									 is_null, BYTEAOID, CurrentMemoryContext);
		if (attr_data)
			pfree(attr_data);
	}

	if (tupdata_len != off)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_CORRUPTED),
				 errmsg("end of tuple reached without looking at all its data")));

	relation_close(rel, AccessShareLock);

	return makeArrayResult(raw_attrs, CurrentMemoryContext);
}
Пример #6
0
static void
convert_heaptuple(HeapTupleHeader htup)
{
	/* HEAP_HASEXTENDED and HEAP_HASCOMPRESSED flags were removed. Clear them out */
	htup->t_infomask &= ~(VERSION4_HEAP_HASEXTENDED | VERSION4_HEAP_HASCOMPRESSED);

	/* HEAP_HASOID flag was moved */
	if (htup->t_infomask & VERSION4_HEAP_HASOID)
	{
		htup->t_infomask &= ~(VERSION4_HEAP_HASOID);
		htup->t_infomask |= HEAP_HASOID;
	}

	/* Any numeric columns? */
	if (curr_hasnumerics)
	{
		/* This is like heap_deform_tuple() */

		bool		hasnulls = (htup->t_infomask & HEAP_HASNULL) != 0;
		int			attnum;
		char	   *tp;
		long		off;
		bits8	   *bp = htup->t_bits;		/* ptr to null bitmap in tuple */

		tp = (char *) htup + htup->t_hoff;

		off = 0;

		for (attnum = 0; attnum < curr_natts; attnum++)
		{
			AttInfo	   *thisatt = &curr_atts[attnum];

			if (hasnulls && att_isnull(attnum, bp))
				continue;

			if (thisatt->attlen == -1)
			{
				off = att_align_pointer(off, thisatt->attalign, -1,
										tp + off);
			}
			else
			{
				/* not varlena, so safe to use att_align_nominal */
				off = att_align_nominal(off, thisatt->attalign);
			}

			if (thisatt->is_numeric)
			{
				/*
				 * Before PostgreSQL 8.3, the n_weight and n_sign_dscale fields
				 * were the other way 'round. Swap them.
				 *
				 * FIXME: need to handle toasted datums here.
				 */
				Datum		datum = PointerGetDatum(tp + off);

				if (VARATT_IS_COMPRESSED(datum))
					pg_log(PG_FATAL, "converting compressed numeric datums is not implemented\n");
				else if (VARATT_IS_EXTERNAL(datum))
					pg_log(PG_FATAL, "converting toasted numeric datums is not implemented\n");
				else
				{
					char	   *numericdata = VARDATA_ANY(DatumGetPointer(datum));
					int			sz = VARSIZE_ANY_EXHDR(DatumGetPointer(datum));
					uint16		tmp;

					if (sz < 4)
						pg_log(PG_FATAL, "unexpected size for numeric datum: %d\n", sz);

					memcpy(&tmp, &numericdata[0], 2);
					memcpy(&numericdata[0], &numericdata[2], 2);
					memcpy(&numericdata[2], &tmp, 2);
				}
			}

			off = att_addlength_pointer(off, thisatt->attlen, tp + off);
		}
	}
}
Пример #7
0
/*
 * Reading quantiles from an input array, based mostly on
 * array_to_text_internal (it's a modified copy). This expects
 * to receive a single-dimensional float8 array as input, fails
 * otherwise.
 */
static double *
array_to_double(FunctionCallInfo fcinfo, ArrayType *v, int * len)
{
    double     *result;
    int         nitems,
               *dims,
                ndims;
    Oid         element_type;
    int         typlen;
    bool        typbyval;
    char        typalign;
    char       *p;
    int         i, idx = 0;
    
    ArrayMetaState *my_extra;

    ndims = ARR_NDIM(v);
    dims = ARR_DIMS(v);
    nitems = ArrayGetNItems(ndims, dims);
    
    /* this is a special-purpose function for single-dimensional arrays */
    if (ndims != 1) {
        elog(ERROR, "error, array_to_double expects a single-dimensional array"
                    "(dims = %d)", ndims);
    }

    /* if there are no elements, set the length to 0 and return NULL */
    if (nitems == 0) {
        (*len) = 0;
        return NULL;
    }
    
    element_type = ARR_ELEMTYPE(v);
    
    result = (double*)palloc(nitems * sizeof(double));

    /*
     * We arrange to look up info about element type, including its output
     * conversion proc, only once per series of calls, assuming the element
     * type doesn't change underneath us.
     */
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
    if (my_extra == NULL)
    {
        fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                                                      sizeof(ArrayMetaState));
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
        my_extra->element_type = ~element_type;
    }

    if (my_extra->element_type != element_type)
    {
        /*
         * Get info about element type, including its output conversion proc
         */
        get_type_io_data(element_type, IOFunc_output,
                         &my_extra->typlen, &my_extra->typbyval,
                         &my_extra->typalign, &my_extra->typdelim,
                         &my_extra->typioparam, &my_extra->typiofunc);
        fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
                      fcinfo->flinfo->fn_mcxt);
        my_extra->element_type = element_type;
    }
    
    typlen = my_extra->typlen;
    typbyval = my_extra->typbyval;
    typalign = my_extra->typalign;
    
    p = ARR_DATA_PTR(v);

    for (i = 0; i < nitems; i++) {

        Datum itemvalue = fetch_att(p, typbyval, typlen);
        
        double val = DatumGetFloat8(itemvalue);
        
        result[idx++] = val;
        
        p = att_addlength_pointer(p, typlen, p);
        p = (char *) att_align_nominal(p, typalign);

    }
    
    (*len) = idx;

    return result;
}
Пример #8
0
/* Clause 3.3.1.3 */
Datum BrokerVolumeFrame1(PG_FUNCTION_ARGS)
{
	FuncCallContext *funcctx;
	AttInMetadata *attinmeta;
	int call_cntr;
	int max_calls;

	int i;

	int ndim, nitems;
	int *dim;
	char *broker_list;	

	char **values = NULL;

	/* Stuff done only on the first call of the function. */
	if (SRF_IS_FIRSTCALL()) {
		MemoryContext oldcontext;

		ArrayType *broker_list_p = PG_GETARG_ARRAYTYPE_P(0);
		text *sector_name_p = PG_GETARG_TEXT_P(1);

		enum bvf1 { i_broker_name=0, i_list_len, i_volume };

		int16 typlen;
		bool typbyval;
		char typalign;

		int ret;
		TupleDesc tupdesc;
		SPITupleTable *tuptable = NULL;
		HeapTuple tuple = NULL;
#ifdef DEBUG
		char sql[2048];
#endif
		char broker_list_array[(B_NAME_LEN + 3) * 40 + 5] = "'{";
		Datum args[2];
		char nulls[2] = { ' ', ' ' };

		/*
		 * Prepare a values array for building the returned tuple.
		 * This should be an array of C strings, which will
		 * be processed later by the type input functions.
		 */
		values = (char **) palloc(sizeof(char *) * 3);
		values[i_list_len] = (char *) palloc((SMALLINT_LEN + 1) * sizeof(char));

		/*
		 * This might be overkill since we always expect single dimensions
		 * arrays.
		 */
		ndim = ARR_NDIM(broker_list_p);
		dim = ARR_DIMS(broker_list_p);
		nitems = ArrayGetNItems(ndim, dim);
		get_typlenbyvalalign(ARR_ELEMTYPE(broker_list_p), &typlen, &typbyval,
				&typalign);
		broker_list = ARR_DATA_PTR(broker_list_p);
		/* Turn the broker_list input into an array format. */
		if (nitems > 0) {
			strcat(broker_list_array, "\"");
			strcat(broker_list_array,
					DatumGetCString(DirectFunctionCall1(textout,
					PointerGetDatum(broker_list))));
			broker_list = att_addlength_pointer(broker_list, typlen,
					broker_list);
			broker_list = (char *) att_align_nominal(broker_list,
					typalign);
			strcat(broker_list_array, "\"");
		}
		for (i = 1; i < nitems; i++) {
			strcat(broker_list_array, ",\"");
			strcat(broker_list_array,
					DatumGetCString(DirectFunctionCall1(textout,
					PointerGetDatum(broker_list))));
			broker_list = att_addlength_pointer(broker_list, typlen,
					broker_list);
			broker_list = (char *) att_align_nominal(broker_list,
					typalign);
			strcat(broker_list_array, "\"");
		}
		strcat(broker_list_array, "}'");
#ifdef DEBUG
		dump_bvf1_inputs(broker_list_p, sector_name_p);
#endif

		/* create a function context for cross-call persistence */
		funcctx = SRF_FIRSTCALL_INIT();
		funcctx->max_calls = 1;

		/* switch to memory context appropriate for multiple function calls */
		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

		SPI_connect();
		plan_queries(BVF1_statements);
#ifdef DEBUG
		sprintf(sql, SQLBVF1_1, broker_list_array,
				DatumGetCString(DirectFunctionCall1(textout,
				PointerGetDatum(sector_name_p))));
		elog(NOTICE, "SQL\n%s", sql);
#endif /* DEBUG */
		args[0] = PointerGetDatum(broker_list_p);
		args[1] = PointerGetDatum(sector_name_p);
		ret = SPI_execute_plan(BVF1_1, args, nulls, true, 0);
		if (ret == SPI_OK_SELECT) {
			tupdesc = SPI_tuptable->tupdesc;
			tuptable = SPI_tuptable;
			tuple = tuptable->vals[0];
		} else {
			dump_bvf1_inputs(broker_list_p, sector_name_p);
			FAIL_FRAME_SET(&funcctx->max_calls, BVF1_statements[0].sql);
		}

		sprintf(values[i_list_len], "%d", SPI_processed);
		values[i_broker_name] = (char *) palloc(((B_NAME_LEN + 2) *
				(SPI_processed + 1) + 3) * sizeof(char));
		values[i_volume] = (char *) palloc((INTEGER_LEN *
				(SPI_processed + 1) + 3) * sizeof(char));

		if (SPI_processed == 0) {
			strcpy(values[i_broker_name], "{}");
			strcpy(values[i_volume], "{}");
		} else {
			strcpy(values[i_broker_name], "{");
			strcpy(values[i_volume], "{");

			if (SPI_processed > 0) {
				strcat(values[i_broker_name], "\"");
				strcat(values[i_broker_name], SPI_getvalue(tuple, tupdesc, 1));
				strcat(values[i_broker_name], "\"");
				strcat(values[i_volume], SPI_getvalue(tuple, tupdesc, 2));
			}
			for (i = 1; i < SPI_processed; i++) {
				tuple = tuptable->vals[i];
				strcat(values[i_broker_name], ",");
				strcat(values[i_broker_name], "\"");
				strcat(values[i_broker_name], SPI_getvalue(tuple, tupdesc, 1));
				strcat(values[i_broker_name], "\"");

				strcat(values[i_volume], ",");
				strcat(values[i_volume], SPI_getvalue(tuple, tupdesc, 2));
			}
			strcat(values[i_broker_name], "}");
			strcat(values[i_volume], "}");
		}

		/* Build a tuple descriptor for our result type */
		if (get_call_result_type(fcinfo, NULL, &tupdesc) !=
				TYPEFUNC_COMPOSITE) {
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					errmsg("function returning record called in context "
							"that cannot accept type record")));
		}

		/*
		 * generate attribute metadata needed later to produce tuples from raw
		 * C strings
		 */
		attinmeta = TupleDescGetAttInMetadata(tupdesc);
		funcctx->attinmeta = attinmeta;

		MemoryContextSwitchTo(oldcontext);
	}

	/* stuff done on every call of the function */
	funcctx = SRF_PERCALL_SETUP();

	call_cntr = funcctx->call_cntr;
	max_calls = funcctx->max_calls;
	attinmeta = funcctx->attinmeta;

	if (call_cntr < max_calls) {
		/* do when there is more left to send */
		HeapTuple tuple;
		Datum result;

#ifdef DEBUG                                                                    
		for (i = 0; i < 3; i++) {
			elog(NOTICE, "BVF1 OUT: %d %s", i, values[i]);
		}
#endif /* DEBUG */

		/* Build a tuple. */
		tuple = BuildTupleFromCStrings(attinmeta, values);

		/* Make the tuple into a datum. */
		result = HeapTupleGetDatum(tuple);

		SRF_RETURN_NEXT(funcctx, result);
	} else {
		/* Do when there is no more left. */
		SPI_finish();
		SRF_RETURN_DONE(funcctx);
	}
}
Пример #9
0
/*
 *		heap_deformtuple
 *
 *		Given a tuple, extract data into values/nulls arrays; this is
 *		the inverse of heap_formtuple.
 *
 *		Storage for the values/nulls arrays is provided by the caller;
 *		it should be sized according to tupleDesc->natts not tuple->t_natts.
 *
 *		Note that for pass-by-reference datatypes, the pointer placed
 *		in the Datum will point into the given tuple.
 *
 *		When all or most of a tuple's fields need to be extracted,
 *		this routine will be significantly quicker than a loop around
 *		heap_getattr; the loop will become O(N^2) as soon as any
 *		noncacheable attribute offsets are involved.
 *
 * OLD API with char 'n'/' ' convention for indicating nulls.
 * This is deprecated and should not be used in new code, but we keep it
 * around for use by old add-on modules.
 */
void
heap_deformtuple(HeapTuple tuple,
				 TupleDesc tupleDesc,
				 Datum *values,
				 char *nulls)
{
	HeapTupleHeader tup = tuple->t_data;
	bool		hasnulls = HeapTupleHasNulls(tuple);
	Form_pg_attribute *att = tupleDesc->attrs;
	int			tdesc_natts = tupleDesc->natts;
	int			natts;			/* number of atts to extract */
	int			attnum;
	char	   *tp;				/* ptr to tuple data */
	long		off;			/* offset in tuple data */
	bits8	   *bp = tup->t_bits;		/* ptr to null bitmap in tuple */
	bool		slow = false;	/* can we use/set attcacheoff? */

	natts = HeapTupleHeaderGetNatts(tup);

	/*
	 * In inheritance situations, it is possible that the given tuple actually
	 * has more fields than the caller is expecting.  Don't run off the end of
	 * the caller's arrays.
	 */
	natts = Min(natts, tdesc_natts);

	tp = (char *) tup + tup->t_hoff;

	off = 0;

	for (attnum = 0; attnum < natts; attnum++)
	{
		Form_pg_attribute thisatt = att[attnum];

		if (hasnulls && att_isnull(attnum, bp))
		{
			values[attnum] = (Datum) 0;
			nulls[attnum] = 'n';
			slow = true;		/* can't use attcacheoff anymore */
			continue;
		}

		nulls[attnum] = ' ';

		if (!slow && thisatt->attcacheoff >= 0)
			off = thisatt->attcacheoff;
		else if (thisatt->attlen == -1)
		{
			/*
			 * We can only cache the offset for a varlena attribute if the
			 * offset is already suitably aligned, so that there would be no
			 * pad bytes in any case: then the offset will be valid for either
			 * an aligned or unaligned value.
			 */
			if (!slow &&
				off == att_align_nominal(off, thisatt->attalign))
				thisatt->attcacheoff = off;
			else
			{
				off = att_align_pointer(off, thisatt->attalign, -1,
										tp + off);
				slow = true;
			}
		}
		else
		{
			/* not varlena, so safe to use att_align_nominal */
			off = att_align_nominal(off, thisatt->attalign);

			if (!slow)
				thisatt->attcacheoff = off;
		}

		values[attnum] = fetchatt(thisatt, tp + off);

		off = att_addlength_pointer(off, thisatt->attlen, tp + off);

		if (thisatt->attlen <= 0)
			slow = true;		/* can't use attcacheoff anymore */
	}

	/*
	 * If tuple doesn't have all the atts indicated by tupleDesc, read the
	 * rest as null
	 */
	for (; attnum < tdesc_natts; attnum++)
	{
		values[attnum] = (Datum) 0;
		nulls[attnum] = 'n';
	}
}
Пример #10
0
/*
 * heap_deform_tuple
 *		Given a tuple, extract data into values/isnull arrays; this is
 *		the inverse of heap_form_tuple.
 *
 *		Storage for the values/isnull arrays is provided by the caller;
 *		it should be sized according to tupleDesc->natts not tuple->t_natts.
 *
 *		Note that for pass-by-reference datatypes, the pointer placed
 *		in the Datum will point into the given tuple.
 *
 *		When all or most of a tuple's fields need to be extracted,
 *		this routine will be significantly quicker than a loop around
 *		heap_getattr; the loop will become O(N^2) as soon as any
 *		noncacheable attribute offsets are involved.
 */
void
heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
				  Datum *values, bool *isnull)
{
	HeapTupleHeader tup = tuple->t_data;
	bool		hasnulls = HeapTupleHasNulls(tuple);
	Form_pg_attribute *att = tupleDesc->attrs;
	int			tdesc_natts = tupleDesc->natts;
	int			natts;			/* number of atts to extract */
	int			attnum;
	char	   *tp;				/* ptr to tuple data */
	long		off;			/* offset in tuple data */
	bits8	   *bp = tup->t_bits;		/* ptr to null bitmap in tuple */
	bool		slow = false;	/* can we use/set attcacheoff? */

	Assert(!is_heaptuple_memtuple(tuple));
	natts = HeapTupleHeaderGetNatts(tup);

	/*
	 * In inheritance situations, it is possible that the given tuple actually
	 * has more fields than the caller is expecting.  Don't run off the end of
	 * the caller's arrays.
	 */
	natts = Min(natts, tdesc_natts);

	tp = (char *) tup + tup->t_hoff;

	off = 0;

	for (attnum = 0; attnum < natts; attnum++)
	{
		Form_pg_attribute thisatt = att[attnum];

		if (hasnulls && att_isnull(attnum, bp))
		{
			values[attnum] = (Datum) 0;
			isnull[attnum] = true;
			slow = true;		/* can't use attcacheoff anymore */
			continue;
		}

		isnull[attnum] = false;

		if (!slow && thisatt->attcacheoff >= 0)
			off = thisatt->attcacheoff;
		else if (thisatt->attlen == -1)
		{
			/*
			 * We can only cache the offset for a varlena attribute if the
			 * offset is already suitably aligned, so that there would be no
			 * pad bytes in any case: then the offset will be valid for either
			 * an aligned or unaligned value.
			 */
			if (!slow &&
				off == att_align_nominal(off, thisatt->attalign))
				thisatt->attcacheoff = off;
			else
			{
				off = att_align_pointer(off, thisatt->attalign, -1,
										tp + off);
				slow = true;
			}
		}
		else
		{
			/* not varlena, so safe to use att_align_nominal */
			off = att_align_nominal(off, thisatt->attalign);

			if (!slow)
				thisatt->attcacheoff = off;

		}
		if (!slow && thisatt->attlen < 0)
			slow = true;

		values[attnum] = fetchatt(thisatt, tp + off);

#ifdef USE_ASSERT_CHECKING
		/* Ignore attributes with dropped types */
		if (thisatt->attlen == -1 && !thisatt->attisdropped)
		{
			Assert(VARATT_IS_SHORT(DatumGetPointer(values[attnum])) ||
				   !VARATT_CAN_MAKE_SHORT(DatumGetPointer(values[attnum])) ||
				   thisatt->atttypid == OIDVECTOROID ||
				   thisatt->atttypid == INT2VECTOROID ||
				   thisatt->atttypid >= FirstNormalObjectId);	
		}
#endif

		off = att_addlength_pointer(off, thisatt->attlen, tp + off);
	}

	/*
	 * If tuple doesn't have all the atts indicated by tupleDesc, read the
	 * rest as null
	 */
	for (; attnum < tdesc_natts; attnum++)
	{
		values[attnum] = (Datum) 0;
		isnull[attnum] = true;
	}
}
Пример #11
0
Datum serialize_array(PG_FUNCTION_ARGS)
{
	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
	Oid		 element_type = ARR_ELEMTYPE(v);
	int16		typlen;
	bool		typbyval;
	char		typalign;
	char		typdelim;
	char	   *p, *value;
	char 		type_category;
	bool		needComma = false;

	bits8	  *bitmap;
	int		 bitmask;
	int		 nitems, i;
	int		 ndim, *dims;

	Oid			typioparam, typiofunc;
	HeapTuple 	type_tuple;
	FmgrInfo	proc, flinfo;

	StringInfoData buf;

//	FILE* log;

//	log = fopen("/var/lib/postgresql/serializer.log", "a");

//	fprintf(log, "Doing serialize_array\n");
//	fflush(log);

	/*
	 * Get info about element type, including its output conversion proc
	 */
	get_type_io_data(element_type, IOFunc_output,
					 &typlen, &typbyval,
					 &typalign, &typdelim,
					 &typioparam, &typiofunc);

	fmgr_info_cxt( typiofunc, &proc, fcinfo->flinfo->fn_mcxt );

	ndim = ARR_NDIM(v);
	dims = ARR_DIMS(v);
	nitems = ArrayGetNItems(ndim, dims);

	if( ndim > 1 )
		elog( ERROR, "multidimensional arrays doesn't supported" );

	p = ARR_DATA_PTR(v);
	bitmap = ARR_NULLBITMAP(v);
	bitmask = 1;

	/* obtain type information from pg_catalog */
	type_tuple = SearchSysCache1( TYPEOID, ObjectIdGetDatum(element_type) );
	if (!HeapTupleIsValid( type_tuple ))
		elog(ERROR, "cache lookup failed for relation %u", element_type);

	type_category = ((Form_pg_type) GETSTRUCT( type_tuple ))->typcategory;

	ReleaseSysCache( type_tuple );

	/* Build the result string */
	initStringInfo(&buf);

	appendStringInfoChar(&buf, '[');
	for (i = 0; i < nitems; i++)
	{
		if (needComma)
			appendStringInfoChar(&buf, ',');
		needComma = true;

		/* Get source element, checking for NULL */
		if (bitmap && (*bitmap & bitmask) == 0)
		{
			// append null
			appendStringInfoString(&buf, "null");
		}
		else
		{
			/* get item value and advance array data pointer */
			Datum itemvalue = fetch_att(p, typbyval, typlen);
			p = att_addlength_pointer(p, typlen, p);
			p = (char *) att_align_nominal(p, typalign);

			//------------------------
			switch( type_category )
			{
				// http://www.postgresql.org/docs/current/static/catalog-pg-type.html#CATALOG-TYPCATEGORY-TABLE

				case 'A': //array - impossible case
				break;

				case 'C': //composite
					//call to serialize_record( ... )
					MemSet( &flinfo, 0, sizeof( flinfo ) );
					flinfo.fn_addr = serialize_record;
					flinfo.fn_nargs = 1;
					flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt;

					value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, itemvalue ) );

					appendStringInfoString(&buf, value);
				break;

				case 'N': //numeric

					// get column text value
					value = OutputFunctionCall( &proc, itemvalue );

					appendStringInfoString(&buf, value);
				break;

				default: //another

					// get column text value
					value = OutputFunctionCall( &proc, itemvalue );

					appendStringInfoQuotedString(&buf, value);
			}
		}

		/* advance bitmap pointer if any */
		if (bitmap)
		{
			bitmask <<= 1;
			if (bitmask == 0x100)
			{
				bitmap++;
				bitmask = 1;
			}
		}
	}
	appendStringInfoChar(&buf, ']');

//	fclose(log);

	//PG_RETURN_CSTRING(retval);
	PG_RETURN_TEXT_P( PG_CSTR_GET_TEXT( buf.data ) );
}
Пример #12
0
static PyObject *
PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
						  char **dataptr_p, bits8 **bitmap_p, int *bitmask_p)
{
	int			i;
	PyObject   *list;

	list = PyList_New(dims[dim]);
	if (!list)
		return NULL;

	if (dim < ndim - 1)
	{
		/* Outer dimension. Recurse for each inner slice. */
		for (i = 0; i < dims[dim]; i++)
		{
			PyObject   *sublist;

			sublist = PLyList_FromArray_recurse(elm, dims, ndim, dim + 1,
												dataptr_p, bitmap_p, bitmask_p);
			PyList_SET_ITEM(list, i, sublist);
		}
	}
	else
	{
		/*
		 * Innermost dimension. Fill the list with the values from the array
		 * for this slice.
		 */
		char	   *dataptr = *dataptr_p;
		bits8	   *bitmap = *bitmap_p;
		int			bitmask = *bitmask_p;

		for (i = 0; i < dims[dim]; i++)
		{
			/* checking for NULL */
			if (bitmap && (*bitmap & bitmask) == 0)
			{
				Py_INCREF(Py_None);
				PyList_SET_ITEM(list, i, Py_None);
			}
			else
			{
				Datum		itemvalue;

				itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen);
				PyList_SET_ITEM(list, i, elm->func(elm, itemvalue));
				dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr);
				dataptr = (char *) att_align_nominal(dataptr, elm->typalign);
			}

			/* advance bitmap pointer if any */
			if (bitmap)
			{
				bitmask <<= 1;
				if (bitmask == 0x100 /* (1<<8) */ )
				{
					bitmap++;
					bitmask = 1;
				}
			}
		}

		*dataptr_p = dataptr;
		*bitmap_p = bitmap;
		*bitmask_p = bitmask;
	}

	return list;
}
Пример #13
0
Datum
count_distinct_elements_append(PG_FUNCTION_ARGS)
{
    int             i;
    element_set_t  *eset = NULL;

    /* info for anyarray */
    Oid input_type;
    Oid element_type;

    /* array data */
    ArrayType  *input;
    int         ndims;
    int        *dims;
    int         nitems;
    bits8      *null_bitmap;
    char       *arr_ptr;
    Datum       element;

    /* memory contexts */
    MemoryContext oldcontext;
    MemoryContext aggcontext;

    /*
     * If the new value is NULL, we simply return the current aggregate state
     * (it might be NULL, so check it). In this case we don't really care about
     * the types etc.
     *
     * We may still get NULL elements in the array, but to check that we would
     * have to walk the array, which does not qualify as cheap check. Also we
     * assume that there's at least one non-NULL element, and we'll walk the
     * array just once. It's possible we'll get empty set this way.
     */
    if (PG_ARGISNULL(1) && PG_ARGISNULL(0))
        PG_RETURN_NULL();
    else if (PG_ARGISNULL(1))
        PG_RETURN_DATUM(PG_GETARG_DATUM(0));

    /* from now on we know the new value is not NULL */

    /* get the type of array elements */
    input_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
    element_type = get_element_type(input_type);

    /*
     * parse the array contents (we know we got non-NULL value)
     */
    input = PG_GETARG_ARRAYTYPE_P(1);
    ndims = ARR_NDIM(input);
    dims = ARR_DIMS(input);
    nitems = ArrayGetNItems(ndims, dims);
    null_bitmap = ARR_NULLBITMAP(input);
    arr_ptr = ARR_DATA_PTR(input);

    /* make sure we're running as part of aggregate function */
    GET_AGG_CONTEXT("count_distinct_elements_append", fcinfo, aggcontext);

    oldcontext = MemoryContextSwitchTo(aggcontext);

    /* add all array elements to the set */
    for (i = 0; i < nitems; i++)
    {
        /* ignore nulls */
        if (null_bitmap && !(null_bitmap[i / 8] & (1 << (i % 8))))
            continue;

        /* init the hash table, if needed */
        if (eset == NULL)
        {
            if (PG_ARGISNULL(0))
            {
                int16       typlen;
                bool        typbyval;
                char        typalign;

                /* get type information for the second parameter (anyelement item) */
                get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

                /* we can't handle varlena types yet or values passed by reference */
                if ((typlen < 0) || (! typbyval))
                    elog(ERROR, "count_distinct_elements handles only arrays of fixed-length types passed by value");

                eset = init_set(typlen, typalign, aggcontext);
            }
            else
                eset = (element_set_t *)PG_GETARG_POINTER(0);
        }

        element = fetch_att(arr_ptr, true, eset->item_size);

        add_element(eset, (char*)&element);

        /* advance array pointer */
        arr_ptr = att_addlength_pointer(arr_ptr, eset->item_size, arr_ptr);
        arr_ptr = (char *) att_align_nominal(arr_ptr, eset->typalign);
    }

    MemoryContextSwitchTo(oldcontext);

    if (eset == NULL)
        PG_RETURN_NULL();
    else
        PG_RETURN_POINTER(eset);
}
Пример #14
0
void dump_mff1_inputs(ArrayType *price_quote_p, char *status_submitted_p,
		ArrayType *symbol_p, ArrayType *trade_qty, char *type_limit_buy_p,
		char *type_limit_sell_p, char *type_stop_loss_p) {
	int i;
	int nitems_pq;
	Datum *transdatums_pq;
	char *p_s;

	int16 typlen_s;
	bool typbyval_s;
	char typalign_s;

	int16 typlen_tq;
	bool typbyval_tq;
	char typalign_tq;

	int *p_tq;

	deconstruct_array(price_quote_p, NUMERICOID, -1, false, 'i',
			&transdatums_pq, NULL, &nitems_pq);

	get_typlenbyvalalign(ARR_ELEMTYPE(trade_qty), &typlen_tq, &typbyval_tq,
			&typalign_tq);
	p_tq = (int *) ARR_DATA_PTR(trade_qty);

	get_typlenbyvalalign(ARR_ELEMTYPE(symbol_p), &typlen_s, &typbyval_s,
			&typalign_s);
	p_s = ARR_DATA_PTR(symbol_p);

	elog(NOTICE, "MFF1: INPUTS START");
	for (i = 0; i < nitems_pq; i++) {
		elog(NOTICE, "MFF1: price_quote[%d] %s", i,
				DatumGetCString(DirectFunctionCall1(numeric_out,
						transdatums_pq[i])));
	}
	elog(NOTICE, "MFF1: status_submitted '%s'",
			DatumGetCString(DirectFunctionCall1(textout,
			PointerGetDatum(status_submitted_p))));
	for (i = 0; i < nitems_pq; i++) {
		elog(NOTICE, "MFF1: symbol[%d] '%s'", i,
				DatumGetCString(DirectFunctionCall1(textout,
						PointerGetDatum(p_s))));
	}
	for (i = 0; i < nitems_pq; i++) {
		elog(NOTICE, "MFF1: trade_qty[%d] %d", i, p_tq[i]);

		p_s = att_addlength_pointer(p_s, typlen_s, p_s);
		p_s = (char *) att_align_nominal(p_s, typalign_s);
	}

	elog(NOTICE, "MFF1: type_limit_buy '%s'",
			DatumGetCString(DirectFunctionCall1(textout,
			PointerGetDatum(type_limit_buy_p))));
	elog(NOTICE, "MFF1: type_limit_sell '%s'",
			DatumGetCString(DirectFunctionCall1(textout,
			PointerGetDatum(type_limit_sell_p))));
	elog(NOTICE, "MFF1: type_stop_loss '%s'",
			DatumGetCString(DirectFunctionCall1(textout,
			PointerGetDatum(type_stop_loss_p))));
	elog(NOTICE, "MFF1: INPUTS END");
}
Пример #15
0
/*
 * slot_deform_tuple
 *		Given a TupleTableSlot, extract data from the slot's physical tuple
 *		into its Datum/isnull arrays.  Data is extracted up through the
 *		natts'th column (caller must ensure this is a legal column number).
 *
 *		This is essentially an incremental version of heap_deform_tuple:
 *		on each call we extract attributes up to the one needed, without
 *		re-computing information about previously extracted attributes.
 *		slot->tts_nvalid is the number of attributes already extracted.
 */
static void
slot_deform_tuple(TupleTableSlot *slot, int natts)
{
	HeapTuple	tuple = TupGetHeapTuple(slot); 
	TupleDesc	tupleDesc = slot->tts_tupleDescriptor;
	Datum	   *values = slot->PRIVATE_tts_values;
	bool	   *isnull = slot->PRIVATE_tts_isnull;
	HeapTupleHeader tup = tuple->t_data;
	bool		hasnulls = HeapTupleHasNulls(tuple);
	Form_pg_attribute *att = tupleDesc->attrs;
	int			attnum;
	char	   *tp;				/* ptr to tuple data */
	long		off;			/* offset in tuple data */
	bits8	   *bp = tup->t_bits;		/* ptr to null bitmap in tuple */
	bool		slow;			/* can we use/set attcacheoff? */

	/*
	 * Check whether the first call for this tuple, and initialize or restore
	 * loop state.
	 */
	attnum = slot->PRIVATE_tts_nvalid;
	if (attnum == 0)
	{
		/* Start from the first attribute */
		off = 0;
		slow = false;
	}
	else
	{
		/* Restore state from previous execution */
		off = slot->PRIVATE_tts_off;
		slow = slot->PRIVATE_tts_slow;
	}

	tp = (char *) tup + tup->t_hoff;

	for (; attnum < natts; attnum++)
	{
		Form_pg_attribute thisatt = att[attnum];

		if (hasnulls && att_isnull(attnum, bp))
		{
			values[attnum] = (Datum) 0;
			isnull[attnum] = true;
			slow = true;		/* can't use attcacheoff anymore */
			continue;
		}

		isnull[attnum] = false;

		if (!slow && thisatt->attcacheoff >= 0)
			off = thisatt->attcacheoff;
		else if (thisatt->attlen == -1)
		{
			/*
			 * We can only cache the offset for a varlena attribute if the
			 * offset is already suitably aligned, so that there would be no
			 * pad bytes in any case: then the offset will be valid for either
			 * an aligned or unaligned value.
			 */
			if (!slow &&
				off == att_align_nominal(off, thisatt->attalign))
				thisatt->attcacheoff = off;
			else
			{
				off = att_align_pointer(off, thisatt->attalign, -1,
										tp + off);
				slow = true;
			}
		}
		else
		{
			/* not varlena, so safe to use att_align_nominal */
			off = att_align_nominal(off, thisatt->attalign);

			if (!slow)
				thisatt->attcacheoff = off;
		}
		if (!slow && thisatt->attlen < 0)
			slow = true;

		values[attnum] = fetchatt(thisatt, tp + off);

		off = att_addlength_pointer(off, thisatt->attlen, tp + off);

		if (thisatt->attlen <= 0)
			slow = true;		/* can't use attcacheoff anymore */
	}

	/*
	 * Save state for next execution
	 */
	slot->PRIVATE_tts_nvalid = attnum;
	slot->PRIVATE_tts_off = off;
	slot->PRIVATE_tts_slow = slow;
}
Пример #16
0
/* Clause 3.3.1.3 */
Datum MarketFeedFrame1(PG_FUNCTION_ARGS)
{
	FuncCallContext *funcctx;
	AttInMetadata *attinmeta;
	int call_cntr;
	int max_calls;

	int i, j, n;
	int num_updated = 0;
	int rows_sent;
	int send_len = 0;
	int count = 0;

	int nitems_pq;
	int *p_tq;
	char *p_s;

	char **values = NULL;

	/* Stuff done only on the first call of the function. */
	if (SRF_IS_FIRSTCALL()) {
		MemoryContext oldcontext;

		ArrayType *price_quote_p = PG_GETARG_ARRAYTYPE_P(0);
		char *status_submitted_p = (char *) PG_GETARG_TEXT_P(1);
		ArrayType *symbol_p = PG_GETARG_ARRAYTYPE_P(2);
		ArrayType *trade_qty = PG_GETARG_ARRAYTYPE_P(3);
		char *type_limit_buy_p = (char *) PG_GETARG_TEXT_P(4);
		char *type_limit_sell_p = (char *) PG_GETARG_TEXT_P(5);
		char *type_stop_loss_p = (char *) PG_GETARG_TEXT_P(6);

		enum mff1 {
				i_num_updated=0, i_send_len, i_symbol, i_trade_id,
				i_price_quote, i_trade_qty, i_trade_type
		};

		Datum *transdatums_pq;

		int16 typlen_s;
		bool typbyval_s;
		char typalign_s;

		int16 typlen_tq;
		bool typbyval_tq;
		char typalign_tq;

		int ret;
		TupleDesc tupdesc;
		SPITupleTable *tuptable = NULL;
		HeapTuple tuple = NULL;
#ifdef DEBUG
		char sql[2048];
#endif
		Datum args[7];
		char nulls[] = { ' ', ' ', ' ', ' ', ' ',
						' ', ' ' };
		char price_quote[S_PRICE_T_LEN + 1];
		char status_submitted[ST_ID_LEN + 1];
		char symbol[S_SYMB_LEN + 1];
		char type_limit_buy[TT_ID_LEN + 1];
		char type_limit_sell[TT_ID_LEN + 1];
		char type_stop_loss[TT_ID_LEN + 1];
		char *trade_id;
		char *req_price_quote;
		char *req_trade_type;
		char *req_trade_qty;

		/*
		 * Prepare a values array for building the returned tuple.
		 * This should be an array of C strings, which will
		 * be processed later by the type input functions.
		 */
		values = (char **) palloc(sizeof(char *) * 7);
		values[i_num_updated] =
				(char *) palloc((INTEGER_LEN + 1) * sizeof(char));
		values[i_send_len] = (char *) palloc((INTEGER_LEN + 1) * sizeof(char));
		/*
		 * FIXME: We don't know how many rows could be returned.  The average
		 * is supposed to be 4.  Let's be prepared for 100, just to be safe.
		 */
		values[i_symbol] = (char *) palloc(((S_SYMB_LEN + 3) * 100 + 3) *
				sizeof(char));
		values[i_trade_id] = (char *) palloc(((IDENT_T_LEN + 1) * 100 + 2) *
				sizeof(char));
		values[i_price_quote] = (char *) palloc(((S_PRICE_T_LEN + 1) * 100 +
				2) * sizeof(char));
		values[i_trade_qty] = (char *) palloc(((INTEGER_LEN + 1) * 100 + 2) *
				sizeof(char));
		values[i_trade_type] = (char *) palloc(((TT_ID_LEN + 3) * 100 + 3) *
				sizeof(char));

		/*
		 * This might be overkill since we always expect single dimensions
		 * arrays.
		 * Should probably check the count of all the arrays to make sure
		 * they are the same...
		 */
		get_typlenbyvalalign(ARR_ELEMTYPE(symbol_p), &typlen_s, &typbyval_s,
				&typalign_s);
		p_s = ARR_DATA_PTR(symbol_p);

		get_typlenbyvalalign(ARR_ELEMTYPE(trade_qty), &typlen_tq, &typbyval_tq,
				&typalign_tq);
		p_tq = (int *) ARR_DATA_PTR(trade_qty);

		deconstruct_array(price_quote_p, NUMERICOID, -1, false, 'i',
				&transdatums_pq, NULL, &nitems_pq);

		strcpy(status_submitted, DatumGetCString(DirectFunctionCall1(textout,
				PointerGetDatum(status_submitted_p))));
		strcpy(type_limit_buy, DatumGetCString(DirectFunctionCall1(textout,
				PointerGetDatum(type_limit_buy_p))));
        strcpy(type_limit_sell, DatumGetCString(DirectFunctionCall1(textout,
                PointerGetDatum(type_limit_sell_p))));
		strcpy(type_stop_loss, DatumGetCString(DirectFunctionCall1(textout,
                PointerGetDatum(type_stop_loss_p))));

#ifdef DEBUG
		dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p,
				trade_qty, type_limit_buy_p, type_limit_sell_p,
				type_stop_loss_p);
#endif

		/* create a function context for cross-call persistence */
		funcctx = SRF_FIRSTCALL_INIT();

		/* switch to memory context appropriate for multiple function calls */
		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

		SPI_connect();
		plan_queries(MFF1_statements);

		strcpy(values[i_symbol], "{");
		strcpy(values[i_trade_id], "{");
		strcpy(values[i_price_quote], "{");
		strcpy(values[i_trade_type], "{");
		strcpy(values[i_trade_qty], "{");
		for (i = 0; i < nitems_pq; i++) {
			rows_sent = 0;

			strcpy(price_quote,
					DatumGetCString(DirectFunctionCall1(numeric_out,
							transdatums_pq[i])));
			strcpy(symbol, DatumGetCString(DirectFunctionCall1(textout,
					PointerGetDatum(p_s))));
			/* FIXME: BEGIN/COMMIT statements not supported with SPI. */
/*
			ret = SPI_exec("BEGIN;", 0);
			if (ret == SPI_OK_SELECT) {
			} else {
				elog(NOTICE, "ERROR: BEGIN not ok = %d", ret);
			}
*/
#ifdef DEBUG
			sprintf(sql, SQLMFF1_1,
					DatumGetCString(DirectFunctionCall1(numeric_out,            
							transdatums_pq[i])),
					p_tq[i],
					symbol);
			elog(NOTICE, "SQL\n%s", sql);
#endif /* DEBUG */
			args[0] = Float8GetDatum(atof(price_quote));
			args[1] = Int64GetDatum(p_tq[i]);
			args[2] = CStringGetTextDatum(symbol);
			ret = SPI_execute_plan(MFF1_1, args, nulls, false, 0);
			if (ret != SPI_OK_UPDATE) {
				dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p,
						trade_qty, type_limit_buy_p, type_limit_sell_p,
						type_stop_loss_p);
				FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[0].sql);
			}
			num_updated += SPI_processed;
#ifdef DEBUG
			elog(NOTICE, "%d row(s) updated", num_updated);
			sprintf(sql, SQLMFF1_2, symbol, type_stop_loss, price_quote,
					type_limit_sell, price_quote, type_limit_buy, price_quote);
			elog(NOTICE, "SQL\n%s", sql);
#endif /* DEBUG */
			args[0] = CStringGetTextDatum(symbol);
			args[1] = CStringGetTextDatum(type_stop_loss);
			args[2] = Float8GetDatum(atof(price_quote));
			args[3] = CStringGetTextDatum(type_limit_sell);
			args[4] = args[2];
			args[5] = CStringGetTextDatum(type_limit_buy);
			args[6] = args[2];
			ret = SPI_execute_plan(MFF1_2, args, nulls, true, 0);
			if (ret != SPI_OK_SELECT) {
				dump_mff1_inputs(price_quote_p, status_submitted_p, symbol_p,
						trade_qty, type_limit_buy_p, type_limit_sell_p,
						type_stop_loss_p);
				FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[1].sql);
				continue;
			}
#ifdef DEBUG
			elog(NOTICE, "%d row(s) returned", SPI_processed);
#endif /* DEBUG */

			tupdesc = SPI_tuptable->tupdesc;
			tuptable = SPI_tuptable;
			n = SPI_processed;
			for (j = 0; j < n; j++) {
				tuple = tuptable->vals[j];
				trade_id = SPI_getvalue(tuple, tupdesc, 1);
				req_price_quote = SPI_getvalue(tuple, tupdesc, 2);
				req_trade_type = SPI_getvalue(tuple, tupdesc, 3);
				req_trade_qty = SPI_getvalue(tuple, tupdesc, 4);
#ifdef DEBUG
				elog(NOTICE, "trade_id = %s", trade_id);
				sprintf(sql, SQLMFF1_3, status_submitted, trade_id);
				elog(NOTICE, "SQL\n%s", sql);
#endif /* DEBUG */
				args[0] = CStringGetTextDatum(status_submitted);
				args[1] = Int64GetDatum(atoll(trade_id));
				ret = SPI_execute_plan(MFF1_3, args, nulls, false, 0);
				if (ret != SPI_OK_UPDATE) {
					dump_mff1_inputs(price_quote_p, status_submitted_p,
							symbol_p, trade_qty, type_limit_buy_p,
							type_limit_sell_p, type_stop_loss_p);
					FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[2].sql);
				}
#ifdef DEBUG
				sprintf(sql, SQLMFF1_4, trade_id);
				elog(NOTICE, "SQL\n%s", sql);
#endif /* DEBUG */
				args[0] = Int64GetDatum(atoll(trade_id));
				ret = SPI_execute_plan(MFF1_4, args, nulls, false, 0);
				if (ret != SPI_OK_DELETE) {
					dump_mff1_inputs(price_quote_p, status_submitted_p,
							symbol_p, trade_qty, type_limit_buy_p,
							type_limit_sell_p, type_stop_loss_p);
					FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[3].sql);
				}
#ifdef DEBUG
				sprintf(sql, SQLMFF1_5, trade_id, status_submitted);
				elog(NOTICE, "SQL\n%s", sql);
#endif /* DEBUG */
				args[1] = CStringGetTextDatum(status_submitted);
				ret = SPI_execute_plan(MFF1_5, args, nulls, false, 0);
				if (ret != SPI_OK_INSERT) {
					dump_mff1_inputs(price_quote_p, status_submitted_p,
							symbol_p, trade_qty, type_limit_buy_p,
							type_limit_sell_p, type_stop_loss_p);
					FAIL_FRAME_SET(&funcctx->max_calls, MFF1_statements[4].sql);
				}
				++rows_sent;
#ifdef DEBUG
				elog(NOTICE, "%d row(s) sent", rows_sent);
#endif /* DEBUG */

				if (count > 0) {
					strcat(values[i_symbol], ",");
					strcat(values[i_trade_id], ",");
					strcat(values[i_price_quote], ",");
					strcat(values[i_trade_type], ",");
					strcat(values[i_trade_qty], ",");
				}
				strcat(values[i_symbol], symbol);
				strcat(values[i_trade_id], trade_id);
				strcat(values[i_price_quote], req_price_quote);
				strcat(values[i_trade_type], req_trade_type);
				strcat(values[i_trade_qty], req_trade_qty);
				++count;
			}

			/* FIXME: BEGIN/COMMIT statements not supported with SPI. */
/*
			ret = SPI_exec("COMMIT;", 0);
			if (ret == SPI_OK_SELECT) {
			} else {
				elog(NOTICE, "ERROR: COMMIT not ok = %d", ret);
			}
*/

			send_len += rows_sent;

			p_s = att_addlength_pointer(p_s, typlen_s, p_s);
			p_s = (char *) att_align_nominal(p_s, typalign_s);
		}
		strcat(values[i_symbol], "}");
		strcat(values[i_trade_id], "}");
		strcat(values[i_price_quote], "}");
		strcat(values[i_trade_qty], "}");
		strcat(values[i_trade_type], "}");

		sprintf(values[i_num_updated], "%d", num_updated);
		sprintf(values[i_send_len], "%d", send_len);
		funcctx->max_calls = 1;

		/* Build a tuple descriptor for our result type */
		if (get_call_result_type(fcinfo, NULL, &tupdesc) !=
				TYPEFUNC_COMPOSITE) {
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					errmsg("function returning record called in context "
							"that cannot accept type record")));
		}

		/*
		 * generate attribute metadata needed later to produce tuples from raw
		 * C strings
		 */
		attinmeta = TupleDescGetAttInMetadata(tupdesc);
		funcctx->attinmeta = attinmeta;

		MemoryContextSwitchTo(oldcontext);
	}

	/* stuff done on every call of the function */
	funcctx = SRF_PERCALL_SETUP();

	call_cntr = funcctx->call_cntr;
	max_calls = funcctx->max_calls;
	attinmeta = funcctx->attinmeta;

	if (call_cntr < max_calls) {
		/* do when there is more left to send */
		HeapTuple tuple;
		Datum result;

#ifdef DEBUG                                                                    
		for (i = 0; i < 7; i++) {
			elog(NOTICE, "MFF1 OUT: %d %s", i, values[i]);
		}
#endif /* DEBUG */

		/* Build a tuple. */
		tuple = BuildTupleFromCStrings(attinmeta, values);

		/* Make the tuple into a datum. */
		result = HeapTupleGetDatum(tuple);

		SRF_RETURN_NEXT(funcctx, result);
	} else {
		/* Do when there is no more left. */
		SPI_finish();
		SRF_RETURN_DONE(funcctx);
	}
}
Пример #17
0
static text*
plvsubst_string(text *template_in, ArrayType *vals_in, text *c_subst, FunctionCallInfo fcinfo)
{
	ArrayType	   *v = vals_in;
	int				nitems,
				   *dims,
					ndims;
	char		   *p;
	int16			typlen;
	bool			typbyval;
	char			typalign;
	char			typdelim;
	Oid				typelem;
	Oid				typiofunc;
	FmgrInfo		proc;
	int				i = 0, items = 0;
	StringInfo		sinfo;
	const char	   *template_str;
	int				template_len;
	char		   *sizes;
	int			   *positions;
	int				subst_mb_len;
	int				subst_len;
	const bits8	   *bitmap;
	int				bitmask;

	if (v != NULL && (ndims = ARR_NDIM(v)) > 0)
	{
		if (ndims != 1)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid parameter"),
					 errdetail("Array of arguments has wrong dimension: %d", ndims)));

		p = ARR_DATA_PTR(v);
		dims = ARR_DIMS(v);
		nitems = ArrayGetNItems(ndims, dims);
		bitmap = ARR_NULLBITMAP(v);
		get_type_io_data(ARR_ELEMTYPE(v), IOFunc_output,
							&typlen, &typbyval,
							&typalign, &typdelim,
							&typelem, &typiofunc);
		fmgr_info_cxt(typiofunc, &proc, fcinfo->flinfo->fn_mcxt);
	}
	else
	{
		nitems = 0;
		p = NULL;
		bitmap = NULL;
	}

	template_str = VARDATA(template_in);
	template_len = ora_mb_strlen(template_in, &sizes, &positions);
	subst_mb_len = ora_mb_strlen1(c_subst);
	subst_len = VARSIZE_ANY_EXHDR(c_subst);
	sinfo = makeStringInfo();

	bitmask = 1;
	for (i = 0; i < template_len; i++)
	{
		if (strncmp(&template_str[positions[i]], VARDATA(c_subst), subst_len) == 0)
		{
			Datum    itemvalue;
			char     *value;

			if (items++ < nitems)
			{
				if (bitmap && (*bitmap & bitmask) == 0)
					value = pstrdup("NULL");
				else
				{
					itemvalue = fetch_att(p, typbyval, typlen);
					value = DatumGetCString(FunctionCall3(&proc,
								itemvalue,
								ObjectIdGetDatum(typelem),
								Int32GetDatum(-1)));

					p = att_addlength_pointer(p, typlen, p);
					p = (char *) att_align_nominal(p, typalign);
				}
				appendStringInfoString(sinfo, value);
				pfree(value);

				if (bitmap)
				{
					bitmask <<= 1;
					if (bitmask == 0x100)
					{
						bitmap++;
						bitmask = 1;
					}
				}
			}
			else
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("too few parameters specified for template string")));

			i += subst_mb_len - 1;
		}
		else
			appendBinaryStringInfo(sinfo, &template_str[positions[i]], sizes[i]);
	}

	return cstring_to_text(sinfo->data);
}