/* ----------------------------------------------------------------
 *			   Rule Test Program
 *
 * $Header: /private/postgres/src/test/RCS/testrules.c,v 1.13 1992/06/28 03:48:44 mao Exp $
 * ----------------------------------------------------------------
 */

#include <stdio.h>
#include "catalog/catname.h"
#include "access/tupdesc.h"
#include "access/ftup.h"
#include "utils/log.h"
#include "tcop/tcop.h"
#include "rules/prs2.h"
#include "rules/prs2stub.h"
#include "access/heapam.h"
#include "utils/rel.h"
#include "executor/executor.h"

/*=======================================================================*/

char * RuleLockToString();
RuleLock StringToRuleLock();
EState CreateExecutorState();

/*---- testruleplans.c routines...   -----------*/
List makePlanFromQuery();
List makePlanFromFile();
List makePlanFromNLRule();

/*---- testtimer.c routines --------------------*/
double getTimer();

/*---- routines in this file... ----------------*/
void TestMain();
void testTimePlan();
void testCountPlan();

/*=======================================================================*/

#define MODE_COUNT	10	/* time query mode... */
#define MODE_TIME	20	/* count tuples mode... */

int TESTRULE_DEBUG_FLAG	= 0;

extern int Quiet;		/* GLOBAL POSTGRES FLAG */
extern int ShowStats;

/*=======================================================================
 *
 * run & time some queries....
 *
 * The program takes many options:
 *
 * -db <dbname>
 *	This specifies the database name and is proccessed by the routine
 *	that calls 'TestMain', so 'TestMain' ignores it
 * -v
 *	Run in verbose mode...
 * -Q
 *	Run postgres in Quiet mode.
 * -nov
 *	TurnOff verbose mode
 * -debug
 *	Print a LOT of debugging stuff!
 * -count
 *	count the number of tuples generated by the query
 * -time
 *	find the time ellapsed to run the query
 * -dummy
 *	before running a query, first run a dummy Xact. This will
 *	have the side effect of initilalizing the system cache,
 *	thus avoiding the significant overhead associated with that...
 * -nodummy
 *	Do not run dummy Xacts from now on
 * -f <filename>
 *	read the plan information from the given file
 *	(see comments in 'makePlanFromFile()' for the file format)
 * -q <query_string>
 *	run the given query
 *	(see also comments in 'makePlanFromString()')
 * -nl <string>
 *	make a plan for rule insertion/deletion simulation
 *	(see comments in 'makePlanFronNLRule()' for the string format)
 * -rp <string>
 *	create a rule plan and add it to pg_prs2plans
 *	see comments in 'addRulePlan' for the string format.
 *
 * NOTE:
 *	The program loops through all the options and executes them
 *	in order. So, it is possible to execute more than one
 *	queries ...
 */

void
TestMain(argc, argv)
int argc;
char *argv[];
{
    int i;
    int verboseFlag, dummyFlag, mode;
    List planInfo;
    mode = MODE_TIME;
    verboseFlag = 0;
    dummyFlag = 0;

    for (i=1; i<argc; i++) {
	if (!strcmp(argv[i], "-debug")) {
	    TESTRULE_DEBUG_FLAG = 1;
	} else if (!strcmp(argv[i], "-Q")) {
	    Quiet = 1;
	} else if (!strcmp(argv[i], "-v")) {
	    verboseFlag = 1;
	} else if (!strcmp(argv[i], "-nov")) {
	    verboseFlag = 0;
	} else if (!strcmp(argv[i], "-dummy")) {
	    dummyFlag = 1;
	} else if (!strcmp(argv[i], "-nodummy")) {
	    dummyFlag = 0;
	} else if (!strcmp(argv[i], "-count")) {
	    mode = MODE_COUNT;
	} else if (!strcmp(argv[i], "-time")) {
	    mode = MODE_TIME;
	} else if (!strcmp(argv[i], "-f")) {
	    i++;
	    if (i >= argc) {
		fprintf(stderr,
		    "%s: option -f must be followed by a file name\n",
		    argv[0]);
		exitpg(1);
	    }
	    if (verboseFlag) {
		printf("+++ (FILE=) %s\n", argv[i]);
	    }
	    planInfo = makePlanFromFile(argv[i]);
	    runPlan(planInfo, mode, dummyFlag, verboseFlag);
	} else if (!strcmp(argv[i], "-q")) {
	    i++;
	    if (i >= argc) {
		fprintf(stderr,
		    "%s: option -q must be followed by a string\n",
		    argv[0]);
		exitpg(1);
	    }
	    if (verboseFlag) {
		printf("+++ (QUERY=) %s\n", argv[i]);
	    }
	    planInfo = makePlanFromQuery(argv[i]);
	    runPlan(planInfo, mode, dummyFlag, verboseFlag);
	} else if (!strcmp(argv[i], "-rp")) {
	    i++;
	    if (i >= argc) {
		fprintf(stderr,
		    "%s: option -rp must be followed by a string\n",
		    argv[0]);
		exitpg(1);
	    }
	    if (verboseFlag) {
		printf("+++ (ADDRPLAN=) %s\n", argv[i]);
	    }
	    addRulePlan(argv[i], verboseFlag);
	} else if (!strcmp(argv[i], "-nl")) {
	    i++;
	    if (i >= argc) {
		fprintf(stderr,
		    "%s: option -nl must be followed by a string\n",
		    argv[0]);
		exitpg(1);
	    }
	    if (verboseFlag) {
		printf("+++ (NLRULE=) %s\n", argv[i]);
	    }
	    planInfo = makePlanFromNLRule(argv[i]);
	    runPlan(planInfo, mode, dummyFlag, verboseFlag);
	} else if (!strcmp(argv[i], "-db")) {
	    /*
	     * ignore this option and the following database name...
	     */
	    i++;
	} else {
	    fprintf(stderr, "%s: Illegal option: %s\n", argv[0], argv[i]);
	    exitpg(1);
	}
    }/*for*/
}

/*---------------------------------------------
 *
 * run the plan
 */
runPlan(planInfo, mode, dummyFlag, verboseFlag)
List planInfo;
int mode;
int dummyFlag;
int verboseFlag;
{
    double time;
    int tupleCount;

    if (mode == MODE_TIME) {
	testTimePlan(planInfo, dummyFlag, verboseFlag, &time);
	printf("\n");
	printf("=== (time=) %.6f\n", time);
    } else if (mode == MODE_COUNT) {
	testCountPlan(planInfo, dummyFlag, verboseFlag, &time,
	&tupleCount);
	printf("\n");
	printf("=== (tuples=) %d\n", tupleCount);
	printf("=== (time=) %.6f\n", time);
    } else {
	fprintf(stderr,"Illegal mode = %d\n", mode);
	exitpg(1);
    }
}
   

/*---------------------------------------------
 * testTimePlan
 *
 * Given a query descriptor, execute the corresponding plan and return
 * the time needed for its completion.
 *
 * There are two flags:
 *    runDummyXact: it runs a dummy Xact to fill in the system cache
 *		to minimize the overhead of the second query.
 *    verbose: if true, then print quite a bit of stuff...
 */
void
testTimePlan(plans, runDummyXact, verbose, timeP)
List plans;
int runDummyXact;
int verbose;
double *timeP;
{
    Portal portal;
    List qDesc;
    LispValue res1, res2, res3;
    EState exState;
    double time;
    int feature;
    List t, planInfo;

    /*
     * run a dummy Xact (to fill in sys$$)
     */
    if (runDummyXact) {
	if (verbose) {
	    printf("Running dummy Xact...");
	}
        initTimer();
        StartTransactionCommand(portal);
        pg_eval("retrieve (pg_user.all) where pg_user.oid=\"1234\"::oid",
		(char *) NULL, (ObjectId *) NULL, 0);
        CommitTransactionCommand();
	if (verbose) {
	    printf("Dummy Xact took = %.6f secs.\n", getTimer());
	}
    }

    /*
     * OK now run the plan
     */
    StartTransactionCommand(portal);

    ResetUsage();
    initTimer();
    if (verbose) {
	printf("Starting XACT. timer = %.6f\n", getTimer());
    }

    foreach (t, plans) {
	planInfo = CAR(t);

	feature = CInteger(CAR(planInfo));
	qDesc = CAR(CDR(planInfo));
	exState = CreateExecutorState();
	res1 = ExecMain(qDesc, exState,
			    lispCons(lispInteger(EXEC_START), LispNil));
	res2 = ExecMain(qDesc, exState,
			    lispCons(lispInteger(feature), LispNil));
	res3 = ExecMain(qDesc, exState,
			    lispCons(lispInteger(EXEC_END), LispNil));
    }
    time = getTimer();
    ShowUsage();
    if (verbose) {
	printf("\n+++ (testTimePlan) %.6f (seconds)\n", time);
    }

    CommitTransactionCommand();

    /*
     * this dummy start + commit has to be there for no apparent reason...
     * (but it *has* to be there, trust me....)
     */
    StartTransactionCommand(portal);
    CommitTransactionCommand();

    *timeP = time;

}
/*----------------------------------------------
 * testCountPlan
 *
 * Read a plan, execute it & count the #of tuples returned
 * (NOTE, it also returns the time needed...)
 *
 * There are two flags:
 *    runDummyXact: it runs a dummy Xact to fill in the system cache
 *		to minimize the overhead of the second query.
 *    verbose: if true, then print quite a bit of stuff...
 */
void
testCountPlan(plans, runDummyXact, verbose, timeP, tupleCountP)
List plans;
int runDummyXact;
int verbose;
double *timeP;
int *tupleCountP;
{
    Portal portal;
    List qDesc;
    LispValue res1, res2, res3;
    EState exState;
    double time;
    int feature;
    int nTuples;
    HeapTuple resultTuple;
    TupleTableSlot slot;
    TupleDescriptor tupDesc;
    List t, planInfo;

    /*
     * run a dummy Xact (to fill in sys$$)
     */
    if (runDummyXact) {
	if (verbose) {
	    printf("Running dummy Xact...");
	}
        initTimer();
        StartTransactionCommand(portal);
        pg_eval("retrieve (pg_user.all)",
		(char *) NULL, (ObjectId *) NULL, 0);
        CommitTransactionCommand();
	if (verbose) {
	    printf("Dummy Xact took = %.6f secs.\n", getTimer());
	}
    }

    /*
     * OK now run the plan
     */
    StartTransactionCommand(portal);

    ResetUsage();
    initTimer();
    if (verbose) {
	printf("Starting XACT. timer = %.6f\n", getTimer());
    }
    nTuples = 0;

    foreach (t, plans) {
	planInfo = CAR(t);

	feature = EXEC_RETONE;
	qDesc = CAR(CDR(planInfo));
	exState = CreateExecutorState();
	res1 = ExecMain(qDesc, exState,
			    lispCons(lispInteger(EXEC_START), LispNil));
	tupDesc = (TupleDescriptor) CADR(res1);

	do {
	    res2 = ExecMain(qDesc, exState,
				lispCons(lispInteger(feature), LispNil));
	    slot = (TupleTableSlot) res2;
	    resultTuple = (HeapTuple) ExecFetchTuple(slot);
	    if (resultTuple != NULL) {
		nTuples++;
		if (TESTRULE_DEBUG_FLAG) {
		    printf("DEBUG: testCountPlan: tuple no %d\n",nTuples);
		    debugtup(resultTuple, tupDesc);
		}
	    }
	} while (resultTuple != NULL);

	res3 = ExecMain(qDesc, exState,
			    lispCons(lispInteger(EXEC_END), LispNil));
    }
    ShowUsage();
    time = getTimer();
    if (verbose) {
	printf("\n+++ (testCountPlan) %.6f (seconds)\n", time);
	printf("\n+++ (testCountPlan) %d (tuples found)\n", nTuples);
    }

    CommitTransactionCommand();

    /*
     * this dummy start + commit has to be there for no apparent reason...
     * (but it *has* to be there, trust me....)
     */
    StartTransactionCommand(portal);
    CommitTransactionCommand();

    *timeP = time;
    *tupleCountP = nTuples;

}
