Polymorfní funkce assert equal

Z PostgreSQL
Skočit na navigaci Skočit na vyhledávání

Funkce plunit_assert_equals je ukázkou polymorfní funkce sloužící k porovnání dvou hodnot stejného typu. O přetypování se postará zajištění polymorfních typů. Funkce řeší pouze porovnání dvou hodnot. Jelikož dopředu nevíme o jaké typy se bude jednat, nelze použít statické řešení, tj. převod na C typy a klasické porovnání. Místo toho se dohledává funkce obsluhující operátor =. Jelikož tato funkce může být linkována k PostgreSQL dynamicky nelze ji volat staticky ale pouze použitím id (v případě zákaznických typů).

Funkce assert_equals_base je opakovaně použita. Použití parametru typu FunctionCallInfo jednoduše umožňuje předat externí (SQL) parametry. Pokud by se nepoužil tento přístup, pak by se parametry funkce, přístup do cache funkce zpracovávali pouze v PG_FUNCTION_INFO_V1 funkcích.

static bool 
assert_equals_base(FunctionCallInfo fcinfo) 
{ 
	Datum	value1 = PG_GETARG_DATUM(0); 
 	Datum	value2 = PG_GETARG_DATUM(1); 
 	Oid	*ptr; 
 
	ptr = (Oid *) fcinfo->flinfo->fn_extra; 
	if (ptr == NULL) 
	{ 
		Oid	valtype = get_fn_expr_argtype(fcinfo->flinfo, 0); 
  		Oid eqopfcid; 
 
		if (!OidIsValid(valtype)) 
			elog(ERROR, "could not determine data type of input"); 
 
		eqopfcid = equality_oper_funcid(valtype); 
  
		if (!OidIsValid(eqopfcid)) 
			ereport(ERROR,. 
    				(errcode(ERRCODE_INVALID_PARAMETER_VALUE), 
				 errmsg("unknown equal operand for datatype")));
 
    		/* First time calling for current query: allocate storage */ 
         	fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, 
									sizeof(Oid));
 
		ptr = (Oid *) fcinfo->flinfo->fn_extra; 
		*ptr = eqopfcid; 
	} 
	return DatumGetBool(OidFunctionCall2(*ptr, value1, value2));
}

Dohledání Oid funkce porovnání pro specifikovaný typ je relativně náročná operace, dohledaní Oid se ukládá do cache funkce. Funkce se aktivuje voláním OidFunctionCall2. Dvojka na konci určuje, že funkci porovnání se předávají dva Datum parametry.

Funkce assert_equals existuje ve dvou variantách - s explicitní chybovým výpisem a s defaultním chybovým výpisem. Proto také existují dvě C funkce: plunit_assert_equals a plunit_assert_equals_message. Teoreticky by stačila pouze jedna C funkce, která by sloužila ve dvou SQL funkcích. Prakticky sdílení C funkcí není zvykem, takže existují také dvě C funkce. plunit_assert_equals je pouze wrapper, který přesměrovává volání na funkci plunit_assert_equals_message. Parametry se předávají v default proměnné fcinfo. Tento parametr je definován makrem PG_FUNCTION_ARGS.

Datum.
plunit_assert_equals(PG_FUNCTION_ARGS)
{
	return plunit_assert_equals_message(fcinfo);
}

Test zda-li je chybová hláška zadaná je ve funkci assert_get_message. Tato funkce zároveň testuje, že pokud je chybová hláška zadaná explicitně, pak není NULL:

static char *
assert_get_message(FunctionCallInfo fcinfo, int nargs, char *message)
{
	char *result;

	if (PG_NARGS() == nargs)
	{
  		text	*msg;
  
		if (PG_ARGISNULL(nargs - 1))
			ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("message is NULL"),
				 errdetail("Message may not be NULL.")));

		msg = PG_GETARG_TEXT_P(nargs - 1);
		result = TextPGetCString(msg);
	}
	else
		result = message;  

	return result;
}

Vlastní funkce plunit_assert_equals_message pouze spojuje výše uvedené funkce:

Datum.
plunit_assert_equals_message(PG_FUNCTION_ARGS)
{
	char *message = assert_get_message(fcinfo, 3, "plunit.assert_equal excep

	/* skip all tests for NULL value */
	if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
		ereport(ERROR,
				(errcode(ERRCODE_CHECK_VIOLATION),
 				 errmsg(message),
				 errdetail("Plunit.assertation fails (assert_equals).")));

	if (!assert_equals_base(fcinfo))
		ereport(ERROR,
			(errcode(ERRCODE_CHECK_VIOLATION),
			 errmsg(message),
			 errdetail("Plunit.assertation fails (assert_equals).")));

	PG_RETURN_VOID();
}

Přetížené funkce jsou registrovány příkazy:

CREATE FUNCTION plunit.assert_equals(expected anyelement, actual anyelement)
RETURNS void
AS 'MODULE_PATHNAME','plunit_assert_equals'
LANGUAGE C IMMUTABLE;
COMMENT ON FUNCTION plunit.assert_equals(expected anyelement, actual anyelement) IS 'Asserts that expected and actual are equal';

CREATE FUNCTION plunit.assert_equals(expected anyelement, actual anyelement, message varchar)
RETURNS void
AS 'MODULE_PATHNAME','plunit_assert_equals_message'
LANGUAGE C IMMUTABLE;
COMMENT ON FUNCTION plunit.assert_equals(expected anyelement, actual anyelement, message varchar) IS 'Asserts that expected and actual are equal';