/*--------------------------------------------------------------------------
*
* test_predtest . c
* Test correctness of optimizer ' s predicate proof logic .
*
* Copyright ( c ) 2018 - 2025 , PostgreSQL Global Development Group
*
* IDENTIFICATION
* src / test / modules / test_predtest / test_predtest . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "optimizer/optimizer.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
/*
* test_predtest ( query text ) returns record
*/
PG_FUNCTION_INFO_V1(test_predtest);
Datum
test_predtest(PG_FUNCTION_ARGS)
{
text *txt = PG_GETARG_TEXT_PP(0 );
char *query_string = text_to_cstring(txt);
SPIPlanPtr spiplan;
int spirc;
TupleDesc tupdesc;
bool s_i_holds,
w_i_holds,
s_r_holds,
w_r_holds;
CachedPlan *cplan;
PlannedStmt *stmt;
Plan *plan;
Expr *clause1;
Expr *clause2;
bool strong_implied_by,
weak_implied_by,
strong_refuted_by,
weak_refuted_by;
Datum values[8 ];
bool nulls[8 ] = {0 };
int i;
/* We use SPI to parse, plan, and execute the test query */
SPI_connect();
/*
* First , plan and execute the query , and inspect the results . To the
* extent that the query fully exercises the two expressions , this
* provides an experimental indication of whether implication or
* refutation holds .
*/
spiplan = SPI_prepare(query_string, 0 , NULL);
if (spiplan == NULL)
elog(ERROR, "SPI_prepare failed for \" %s\"" , query_string);
spirc = SPI_execute_plan(spiplan, NULL, NULL, true , 0 );
if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to execute \" %s\"" , query_string);
tupdesc = SPI_tuptable->tupdesc;
if (tupdesc->natts != 2 ||
TupleDescAttr(tupdesc, 0 )->atttypid != BOOLOID ||
TupleDescAttr(tupdesc, 1 )->atttypid != BOOLOID)
elog(ERROR, "test_predtest query must yield two boolean columns" );
s_i_holds = w_i_holds = s_r_holds = w_r_holds = true ;
for (i = 0 ; i < SPI_processed; i++)
{
HeapTuple tup = SPI_tuptable->vals[i];
Datum dat;
bool isnull;
char c1,
c2;
/* Extract column values in a 3-way representation */
dat = SPI_getbinval(tup, tupdesc, 1 , &isnull);
if (isnull)
c1 = 'n' ;
else if (DatumGetBool(dat))
c1 = 't' ;
else
c1 = 'f' ;
dat = SPI_getbinval(tup, tupdesc, 2 , &isnull);
if (isnull)
c2 = 'n' ;
else if (DatumGetBool(dat))
c2 = 't' ;
else
c2 = 'f' ;
/* Check for violations of various proof conditions */
/* strong implication: truth of c2 implies truth of c1 */
if (c2 == 't' && c1 != 't' )
s_i_holds = false ;
/* weak implication: non-falsity of c2 implies non-falsity of c1 */
if (c2 != 'f' && c1 == 'f' )
w_i_holds = false ;
/* strong refutation: truth of c2 implies falsity of c1 */
if (c2 == 't' && c1 != 'f' )
s_r_holds = false ;
/* weak refutation: truth of c2 implies non-truth of c1 */
if (c2 == 't' && c1 == 't' )
w_r_holds = false ;
}
/*
* Strong refutation implies weak refutation , so we should never observe
* s_r_holds = true with w_r_holds = false .
*
* We can ' t make a comparable assertion for implication since moving from
* strong to weak implication expands the allowed values of " A " from true
* to either true or NULL .
*
* If this fails it constitutes a bug not with the proofs but with either
* this test module or a more core part of expression evaluation since we
* are validating the logical correctness of the observed result rather
* than the proof .
*/
if (s_r_holds && !w_r_holds)
elog(WARNING, "s_r_holds was true; w_r_holds must not be false" );
/*
* Now , dig the clause querytrees out of the plan , and see what predtest . c
* does with them .
*/
cplan = SPI_plan_get_cached_plan(spiplan);
if (cplan == NULL || list_length(cplan->stmt_list) != 1 )
elog(ERROR, "test_predtest query string must contain exactly one query" );
stmt = linitial_node(PlannedStmt, cplan->stmt_list);
if (stmt->commandType != CMD_SELECT)
elog(ERROR, "test_predtest query must be a SELECT" );
plan = stmt->planTree;
Assert(list_length(plan->targetlist) >= 2 );
clause1 = linitial_node(TargetEntry, plan->targetlist)->expr;
clause2 = lsecond_node(TargetEntry, plan->targetlist)->expr;
/*
* Because the clauses are in the SELECT list , preprocess_expression did
* not pass them through canonicalize_qual nor make_ands_implicit .
*
* We can ' t do canonicalize_qual here , since it ' s unclear whether the
* expressions ought to be treated as WHERE or CHECK clauses . Fortunately ,
* useful test expressions wouldn ' t be affected by those transformations
* anyway . We should do make_ands_implicit , though .
*
* Another way in which this does not exactly duplicate the normal usage
* of the proof functions is that they are often given qual clauses
* containing RestrictInfo nodes . But since predtest . c just looks through
* those anyway , it seems OK to not worry about that point .
*/
clause1 = (Expr *) make_ands_implicit(clause1);
clause2 = (Expr *) make_ands_implicit(clause2);
strong_implied_by = predicate_implied_by((List *) clause1,
(List *) clause2,
false );
weak_implied_by = predicate_implied_by((List *) clause1,
(List *) clause2,
true );
strong_refuted_by = predicate_refuted_by((List *) clause1,
(List *) clause2,
false );
weak_refuted_by = predicate_refuted_by((List *) clause1,
(List *) clause2,
true );
/*
* Issue warning if any proof is demonstrably incorrect .
*/
if (strong_implied_by && !s_i_holds)
elog(WARNING, "strong_implied_by result is incorrect" );
if (weak_implied_by && !w_i_holds)
elog(WARNING, "weak_implied_by result is incorrect" );
if (strong_refuted_by && !s_r_holds)
elog(WARNING, "strong_refuted_by result is incorrect" );
if (weak_refuted_by && !w_r_holds)
elog(WARNING, "weak_refuted_by result is incorrect" );
/*
* As with our earlier check of the logical consistency of whether strong
* and weak refutation hold , we ought never prove strong refutation
* without also proving weak refutation .
*
* Also as earlier we cannot make the same guarantee about implication
* proofs .
*
* A warning here suggests a bug in the proof code .
*/
if (strong_refuted_by && !weak_refuted_by)
elog(WARNING, "strong_refuted_by was proven; weak_refuted_by should also be proven" );
/*
* Clean up and return a record of the results .
*/
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed" );
tupdesc = CreateTemplateTupleDesc(8 );
TupleDescInitEntry(tupdesc, (AttrNumber) 1 ,
"strong_implied_by" , BOOLOID, -1 , 0 );
TupleDescInitEntry(tupdesc, (AttrNumber) 2 ,
"weak_implied_by" , BOOLOID, -1 , 0 );
TupleDescInitEntry(tupdesc, (AttrNumber) 3 ,
"strong_refuted_by" , BOOLOID, -1 , 0 );
TupleDescInitEntry(tupdesc, (AttrNumber) 4 ,
"weak_refuted_by" , BOOLOID, -1 , 0 );
TupleDescInitEntry(tupdesc, (AttrNumber) 5 ,
"s_i_holds" , BOOLOID, -1 , 0 );
TupleDescInitEntry(tupdesc, (AttrNumber) 6 ,
"w_i_holds" , BOOLOID, -1 , 0 );
TupleDescInitEntry(tupdesc, (AttrNumber) 7 ,
"s_r_holds" , BOOLOID, -1 , 0 );
TupleDescInitEntry(tupdesc, (AttrNumber) 8 ,
"w_r_holds" , BOOLOID, -1 , 0 );
tupdesc = BlessTupleDesc(tupdesc);
values[0 ] = BoolGetDatum(strong_implied_by);
values[1 ] = BoolGetDatum(weak_implied_by);
values[2 ] = BoolGetDatum(strong_refuted_by);
values[3 ] = BoolGetDatum(weak_refuted_by);
values[4 ] = BoolGetDatum(s_i_holds);
values[5 ] = BoolGetDatum(w_i_holds);
values[6 ] = BoolGetDatum(s_r_holds);
values[7 ] = BoolGetDatum(w_r_holds);
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
}
Messung V0.5 in Prozent C=94 H=90 G=91
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland