Example #1
0
/*
 * master_drop_sequences attempts to drop a list of sequences on worker nodes.
 * The "IF EXISTS" clause is used to permit dropping sequences even if they may not
 * exist. If the commands fail on the workers, the operation is rolled back.
 * If ddl propagation (citus.enable_ddl_propagation) is set to off, then the function
 * returns without doing anything.
 */
Datum
master_drop_sequences(PG_FUNCTION_ARGS)
{
	ArrayType *sequenceNamesArray = PG_GETARG_ARRAYTYPE_P(0);
	ArrayIterator sequenceIterator = NULL;
	Datum sequenceText = 0;
	bool isNull = false;
	StringInfo dropSeqCommand = makeStringInfo();
	bool coordinator = IsCoordinator();

	CheckCitusVersion(ERROR);

	/* do nothing if DDL propagation is switched off or this is not the coordinator */
	if (!EnableDDLPropagation || !coordinator)
	{
		PG_RETURN_VOID();
	}

	/* iterate over sequence names to build single command to DROP them all */
	sequenceIterator = array_create_iterator(sequenceNamesArray, 0, NULL);
	while (array_iterate(sequenceIterator, &sequenceText, &isNull))
	{
		if (isNull)
		{
			ereport(ERROR, (errmsg("unexpected NULL sequence name"),
							errcode(ERRCODE_INVALID_PARAMETER_VALUE)));
		}

		/* append command portion if we haven't added any sequence names yet */
		if (dropSeqCommand->len == 0)
		{
			appendStringInfoString(dropSeqCommand, "DROP SEQUENCE IF EXISTS");
		}
		else
		{
			/* otherwise, add a comma to separate subsequent sequence names */
			appendStringInfoChar(dropSeqCommand, ',');
		}

		appendStringInfo(dropSeqCommand, " %s", TextDatumGetCString(sequenceText));
	}

	if (dropSeqCommand->len != 0)
	{
		appendStringInfoString(dropSeqCommand, " CASCADE");

		SendCommandToWorkers(WORKERS_WITH_METADATA, DISABLE_DDL_PROPAGATION);
		SendCommandToWorkers(WORKERS_WITH_METADATA, dropSeqCommand->data);
	}

	PG_RETURN_VOID();
}
Example #2
0
/*
 * CmsTopnUnion is a helper function for union operations. It first sums up two
 * sketchs and iterates through the top-n array of the second cms_topn to update
 * the top-n of union.
 */
static CmsTopn *
CmsTopnUnion(CmsTopn *firstCmsTopn, CmsTopn *secondCmsTopn,
             TypeCacheEntry *itemTypeCacheEntry)
{
	ArrayType *firstTopnArray = TopnArray(firstCmsTopn);
	ArrayType *secondTopnArray = TopnArray(secondCmsTopn);
	ArrayType *newTopnArray = NULL;
	Size firstTopnArrayLength = ARR_DIMS(firstTopnArray)[0];
	Size secondTopnArrayLength = ARR_DIMS(secondTopnArray)[0];
	Size sketchSize = 0;
	CmsTopn *newCmsTopn = NULL;
	uint32 topnItemIndex = 0;
	Datum topnItem = 0;
	ArrayIterator secondTopnArrayIterator = NULL;
	bool isNull = false;
	bool hasMoreItem = false;

	if (firstCmsTopn->sketchDepth != secondCmsTopn->sketchDepth ||
	    firstCmsTopn->sketchWidth != secondCmsTopn->sketchWidth ||
	    firstCmsTopn->topnItemCount != secondCmsTopn->topnItemCount)
	{
		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		                errmsg("cannot merge cms_topns with different parameters")));
	}

	if (firstTopnArrayLength == 0)
	{
		newCmsTopn = secondCmsTopn;
	}
	else if (secondTopnArrayLength == 0)
	{
		newCmsTopn = firstCmsTopn;
	}
	else if (ARR_ELEMTYPE(firstTopnArray) != ARR_ELEMTYPE(secondTopnArray))
	{
		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
		                errmsg("cannot merge cms_topns of different types")));
	}
	else
	{
		/* for performance reasons we use first cms_topn to merge two cms_topn's */
		newCmsTopn = firstCmsTopn;
		newTopnArray = firstTopnArray;

		sketchSize = newCmsTopn->sketchDepth * newCmsTopn->sketchWidth;
		for (topnItemIndex = 0; topnItemIndex < sketchSize; topnItemIndex++)
		{
			newCmsTopn->sketch[topnItemIndex] += secondCmsTopn->sketch[topnItemIndex];
		}

		secondTopnArrayIterator = array_create_iterator(secondTopnArray, 0);

		/*
		 * One by one we add top-n items in second top-n array to top-n array of
		 * first(new) top-n array. As a result only top-n elements of two item
		 * list are kept.
		 */
		hasMoreItem = array_iterate(secondTopnArrayIterator, &topnItem, &isNull);
		while (hasMoreItem)
		{
			Frequency newItemFrequency = 0;
			bool topnArrayUpdated = false;

			newItemFrequency = CmsTopnEstimateItemFrequency(newCmsTopn, topnItem,
			                                                itemTypeCacheEntry);
			newTopnArray = UpdateTopnArray(newCmsTopn, topnItem, itemTypeCacheEntry,
			                               newItemFrequency, &topnArrayUpdated);
			if (topnArrayUpdated)
			{
				newCmsTopn = FormCmsTopn(newCmsTopn, newTopnArray);
			}

			hasMoreItem = array_iterate(secondTopnArrayIterator, &topnItem, &isNull);
		}
	}

	return newCmsTopn;
}
Example #3
0
/*
 * UpdateTopnArray is a helper function for the unions and inserts. It takes
 * given item and its frequency. If the item is not in the top-n array, it tries
 * to insert new item. If there is place in the top-n array, it insert directly.
 * Otherwise, it compares its frequency with the minimum of current items in the
 * array and updates top-n array if new frequency is bigger.
 */
static ArrayType *
UpdateTopnArray(CmsTopn *cmsTopn, Datum candidateItem, TypeCacheEntry *itemTypeCacheEntry,
                Frequency itemFrequency, bool *topnArrayUpdated)
{
	ArrayType *currentTopnArray = TopnArray(cmsTopn);
	ArrayType *updatedTopnArray = NULL;
	int16 itemTypeLength = itemTypeCacheEntry->typlen;
	bool itemTypeByValue = itemTypeCacheEntry->typbyval;
	char itemTypeAlignment = itemTypeCacheEntry->typalign;
	Frequency minOfNewTopnItems = MAX_FREQUENCY;
	bool candidateAlreadyInArray = false;
	int candidateIndex = -1;

	int currentArrayLength = ARR_DIMS(currentTopnArray)[0];
	if (currentArrayLength == 0)
	{
		Oid itemType = itemTypeCacheEntry->type_id;
		if (itemTypeCacheEntry->typtype == TYPTYPE_COMPOSITE)
		{
			ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
			                errmsg("composite types are not supported")));
		}

		currentTopnArray = construct_empty_array(itemType);
	}

	/*
	 * If item frequency is smaller than min frequency of old top-n items,
	 * it cannot be in the top-n items and we insert it only if there is place.
	 * Otherwise, we need to check new minimum and whether it is in the top-n.
	 */
	if (itemFrequency <= cmsTopn->minFrequencyOfTopnItems)
	{
		if (currentArrayLength < cmsTopn->topnItemCount)
		{
			candidateIndex =  currentArrayLength + 1;
			minOfNewTopnItems = itemFrequency;
		}
	}
	else
	{
		ArrayIterator iterator = array_create_iterator(currentTopnArray, 0);
		Datum topnItem = 0;
		int topnItemIndex = 1;
		int minItemIndex = 1;
		bool hasMoreItem = false;
		bool isNull = false;

		/*
		 * Find the top-n item with minimum frequency to replace it with candidate
		 * item.
		 */
		hasMoreItem = array_iterate(iterator, &topnItem, &isNull);
		while (hasMoreItem)
		{
			Frequency topnItemFrequency = 0;

			/* check if we already have this item in top-n array */
			candidateAlreadyInArray = datumIsEqual(topnItem, candidateItem,
			                                       itemTypeByValue, itemTypeLength);
			if (candidateAlreadyInArray)
			{
				minItemIndex = -1;
				break;
			}

			/* keep track of minumum frequency item in the top-n array */
			topnItemFrequency = CmsTopnEstimateItemFrequency(cmsTopn, topnItem,
			                                                 itemTypeCacheEntry);
			if (topnItemFrequency < minOfNewTopnItems)
			{
				minOfNewTopnItems = topnItemFrequency;
				minItemIndex = topnItemIndex;
			}

			hasMoreItem = array_iterate(iterator, &topnItem, &isNull);
			topnItemIndex++;
		}

		/* if new item is not in the top-n and there is place, insert the item */
		if (!candidateAlreadyInArray && currentArrayLength < cmsTopn->topnItemCount)
		{
			minItemIndex = currentArrayLength + 1;
			minOfNewTopnItems = Min(minOfNewTopnItems, itemFrequency);
		}

		candidateIndex = minItemIndex;
	}

	/*
	 * If it is not in the top-n structure and its frequency bigger than minimum
	 * put into top-n instead of the item which has minimum frequency. If it is
	 * in top-n or not frequent items, do not change anything.
	 */
	if (!candidateAlreadyInArray && minOfNewTopnItems <= itemFrequency)
	{
		updatedTopnArray = array_set(currentTopnArray, 1, &candidateIndex,
		                             candidateItem, false, -1, itemTypeLength,
		                             itemTypeByValue, itemTypeAlignment);
		cmsTopn->minFrequencyOfTopnItems = minOfNewTopnItems;
		*topnArrayUpdated = true;
	}
	else
	{
		updatedTopnArray = currentTopnArray;
		*topnArrayUpdated = false;
	}

	return updatedTopnArray;
}
Example #4
0
/*
 * topn is a user-facing UDF which returns the top items and their frequencies.
 * It first gets the top-n structure and converts it into the ordered array of
 * FrequentTopnItem which keeps Datums and the frequencies in the first call.
 * Then, it returns an item and its frequency according to call counter. This
 * function requires a parameter for the type because PostgreSQL has strongly
 * typed system and the type of frequent items in returning rows has to be given.
 */
Datum
topn(PG_FUNCTION_ARGS)
{
	FuncCallContext *functionCallContext = NULL;
	TupleDesc tupleDescriptor = NULL;
	Oid returningItemType = get_fn_expr_argtype(fcinfo->flinfo, 1);
	CmsTopn *cmsTopn = NULL;
	ArrayType *topnArray = NULL;
	int topnArrayLength = 0;
	Datum topnItem = 0;
	bool isNull = false;
	ArrayIterator topnIterator = NULL;
	bool hasMoreItem = false;
	int callCounter = 0;
	int maxCallCounter = 0;

	if (SRF_IS_FIRSTCALL())
	{
		MemoryContext oldcontext = NULL;
		Size topnArraySize = 0;
		TypeCacheEntry *itemTypeCacheEntry = NULL;
		int topnIndex = 0;
		Oid itemType = InvalidOid;
		FrequentTopnItem *sortedTopnArray = NULL;
		TupleDesc completeTupleDescriptor = NULL;

		functionCallContext = SRF_FIRSTCALL_INIT();
		if (PG_ARGISNULL(0))
		{
			SRF_RETURN_DONE(functionCallContext);
		}

		cmsTopn = (CmsTopn *)  PG_GETARG_VARLENA_P(0);
		topnArray = TopnArray(cmsTopn);
		topnArrayLength = ARR_DIMS(topnArray)[0];

		/* if there is not any element in the array just return */
		if (topnArrayLength == 0)
		{
			SRF_RETURN_DONE(functionCallContext);
		}

		itemType = ARR_ELEMTYPE(topnArray);
		if (itemType != returningItemType)
		{
			elog(ERROR, "not a proper cms_topn for the result type");
		}

		/* switch to consistent context for multiple calls */
		oldcontext = MemoryContextSwitchTo(functionCallContext->multi_call_memory_ctx);
		itemTypeCacheEntry = lookup_type_cache(itemType, 0);
		functionCallContext->max_calls = topnArrayLength;

		/* create an array to copy top-n items and sort them later */
		topnArraySize = sizeof(FrequentTopnItem) * topnArrayLength;
		sortedTopnArray = palloc0(topnArraySize);

		topnIterator = array_create_iterator(topnArray, 0);
		hasMoreItem = array_iterate(topnIterator, &topnItem, &isNull);
		while (hasMoreItem)
		{
			FrequentTopnItem frequentTopnItem;
			frequentTopnItem.topnItem = topnItem;
			frequentTopnItem.topnItemFrequency =
			        CmsTopnEstimateItemFrequency(cmsTopn, topnItem, itemTypeCacheEntry);
			sortedTopnArray[topnIndex] = frequentTopnItem;

			hasMoreItem = array_iterate(topnIterator, &topnItem, &isNull);
			topnIndex++;
		}

		SortTopnItems(sortedTopnArray, topnArrayLength);
		functionCallContext->user_fctx = sortedTopnArray;

		get_call_result_type(fcinfo, &returningItemType, &tupleDescriptor);
		completeTupleDescriptor = BlessTupleDesc(tupleDescriptor);
		functionCallContext->tuple_desc = completeTupleDescriptor;
		MemoryContextSwitchTo(oldcontext);
	}

	functionCallContext = SRF_PERCALL_SETUP();
	maxCallCounter = functionCallContext->max_calls;
	callCounter = functionCallContext->call_cntr;

	if (callCounter < maxCallCounter)
	{
		Datum *tupleValues = (Datum *) palloc(2 * sizeof(Datum));
		HeapTuple topnItemTuple;
		Datum topnItemDatum = 0;
		char *tupleNulls = (char *) palloc0(2 * sizeof(char));
		FrequentTopnItem *sortedTopnArray = NULL;
		TupleDesc completeTupleDescriptor = NULL;

		sortedTopnArray = (FrequentTopnItem *) functionCallContext->user_fctx;
		tupleValues[0] = sortedTopnArray[callCounter].topnItem;
		tupleValues[1] = sortedTopnArray[callCounter].topnItemFrequency;

		/* non-null attributes are indicated by a ' ' (space) */
		tupleNulls[0] = ' ';
		tupleNulls[1] = ' ';

		completeTupleDescriptor = functionCallContext->tuple_desc;
		topnItemTuple = heap_formtuple(completeTupleDescriptor, tupleValues, tupleNulls);

		topnItemDatum = HeapTupleGetDatum(topnItemTuple);
		SRF_RETURN_NEXT(functionCallContext, topnItemDatum);
	}
	else
	{
		SRF_RETURN_DONE(functionCallContext);
	}
}
Example #5
0
/*-----------------------------------------------------------------------------
 * array_positions :
 *			return an array of positions of a value in an array.
 *
 * IS NOT DISTINCT FROM semantics are used for comparisons.  Returns NULL when
 * the input array is NULL.  When the value is not found in the array, returns
 * an empty array.
 *
 * This is not strict so we have to test for null inputs explicitly.
 *-----------------------------------------------------------------------------
 */
Datum
array_positions(PG_FUNCTION_ARGS)
{
	ArrayType  *array;
	Oid			collation = PG_GET_COLLATION();
	Oid			element_type;
	Datum		searched_element,
				value;
	bool		isnull;
	int			position;
	TypeCacheEntry *typentry;
	ArrayMetaState *my_extra;
	bool		null_search;
	ArrayIterator array_iterator;
	ArrayBuildState *astate = NULL;

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	array = PG_GETARG_ARRAYTYPE_P(0);
	element_type = ARR_ELEMTYPE(array);

	position = (ARR_LBOUND(array))[0] - 1;

	/*
	 * We refuse to search for elements in multi-dimensional arrays, since we
	 * have no good way to report the element's location in the array.
	 */
	if (ARR_NDIM(array) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("searching for elements in multidimensional arrays is not supported")));

	astate = initArrayResult(INT4OID, CurrentMemoryContext, false);

	if (PG_ARGISNULL(1))
	{
		/* fast return when the array doesn't have nulls */
		if (!array_contains_nulls(array))
			PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
		searched_element = (Datum) 0;
		null_search = true;
	}
	else
	{
		searched_element = PG_GETARG_DATUM(1);
		null_search = false;
	}

	/*
	 * We arrange to look up type info for array_create_iterator 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_typlenbyvalalign(element_type,
							 &my_extra->typlen,
							 &my_extra->typbyval,
							 &my_extra->typalign);

		typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);

		if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
				errmsg("could not identify an equality operator for type %s",
					   format_type_be(element_type))));

		my_extra->element_type = element_type;
		fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
	}

	/*
	 * Accumulate each array position iff the element matches the given
	 * element.
	 */
	array_iterator = array_create_iterator(array, 0, my_extra);
	while (array_iterate(array_iterator, &value, &isnull))
	{
		position += 1;

		/*
		 * Can't look at the array element's value if it's null; but if we
		 * search for null, we have a hit.
		 */
		if (isnull || null_search)
		{
			if (isnull && null_search)
				astate =
					accumArrayResult(astate, Int32GetDatum(position), false,
									 INT4OID, CurrentMemoryContext);

			continue;
		}

		/* not nulls, so run the operator */
		if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
										   searched_element, value)))
			astate =
				accumArrayResult(astate, Int32GetDatum(position), false,
								 INT4OID, CurrentMemoryContext);
	}

	array_free_iterator(array_iterator);

	/* Avoid leaking memory when handed toasted input */
	PG_FREE_IF_COPY(array, 0);

	PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
}
Example #6
0
/*
 * array_position_common
 *		Common code for array_position and array_position_start
 *
 * These are separate wrappers for the sake of opr_sanity regression test.
 * They are not strict so we have to test for null inputs explicitly.
 */
static Datum
array_position_common(FunctionCallInfo fcinfo)
{
	ArrayType  *array;
	Oid			collation = PG_GET_COLLATION();
	Oid			element_type;
	Datum		searched_element,
				value;
	bool		isnull;
	int			position,
				position_min;
	bool		found = false;
	TypeCacheEntry *typentry;
	ArrayMetaState *my_extra;
	bool		null_search;
	ArrayIterator array_iterator;

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	array = PG_GETARG_ARRAYTYPE_P(0);
	element_type = ARR_ELEMTYPE(array);

	/*
	 * We refuse to search for elements in multi-dimensional arrays, since we
	 * have no good way to report the element's location in the array.
	 */
	if (ARR_NDIM(array) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("searching for elements in multidimensional arrays is not supported")));

	if (PG_ARGISNULL(1))
	{
		/* fast return when the array doesn't have nulls */
		if (!array_contains_nulls(array))
			PG_RETURN_NULL();
		searched_element = (Datum) 0;
		null_search = true;
	}
	else
	{
		searched_element = PG_GETARG_DATUM(1);
		null_search = false;
	}

	position = (ARR_LBOUND(array))[0] - 1;

	/* figure out where to start */
	if (PG_NARGS() == 3)
	{
		if (PG_ARGISNULL(2))
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("initial position must not be null")));

		position_min = PG_GETARG_INT32(2);
	}
	else
		position_min = (ARR_LBOUND(array))[0];

	/*
	 * We arrange to look up type info for array_create_iterator 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_typlenbyvalalign(element_type,
							 &my_extra->typlen,
							 &my_extra->typbyval,
							 &my_extra->typalign);

		typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);

		if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
				errmsg("could not identify an equality operator for type %s",
					   format_type_be(element_type))));

		my_extra->element_type = element_type;
		fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
	}

	/* Examine each array element until we find a match. */
	array_iterator = array_create_iterator(array, 0, my_extra);
	while (array_iterate(array_iterator, &value, &isnull))
	{
		position++;

		/* skip initial elements if caller requested so */
		if (position < position_min)
			continue;

		/*
		 * Can't look at the array element's value if it's null; but if we
		 * search for null, we have a hit and are done.
		 */
		if (isnull || null_search)
		{
			if (isnull && null_search)
			{
				found = true;
				break;
			}
			else
				continue;
		}

		/* not nulls, so run the operator */
		if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
										   searched_element, value)))
		{
			found = true;
			break;
		}
	}

	array_free_iterator(array_iterator);

	/* Avoid leaking memory when handed toasted input */
	PG_FREE_IF_COPY(array, 0);

	if (!found)
		PG_RETURN_NULL();

	PG_RETURN_INT32(position);
}