/* ----------------------------------------------------------------
 * testrtree.c --
 *	B-tree test code.
 * ----------------------------------------------------------------
 */

#include <stdio.h>
#include "postgres.h"

#include "fmgr.h"	/* for M_STATIC/M_DYNAMIC */

#include "skey.h"
#include "amutil.h"

#include "attnum.h"
#include "attval.h"
#include "bufmgr.h"
#include "catname.h"
#include "datum.h"
#include "log.h"
#include "genam.h"
#include "heapam.h"
#include "itemptr.h"
#include "istrat.h"
#include "itup.h"
#include "name.h"
/* #include "oid.h"  -- now in postgres.h */
#include "portal.h"
#include "sdir.h"
#include "tqual.h"
#include "valid.h"
#include "xcxt.h"

#include "rtrees.h"
#include "btree.h"

RcsId("$Header: /private/postgres/src/test/RCS/testrtree.c,v 1.12 1992/03/04 14:10:54 hong Exp $");

/* ----------------
 *	constants
 * ----------------
 */

#define UninitializedQualification	0
#define SingleQualification		1
#define DoubleQualification		2
#define MarkedQualification		3

#define MarkedTuplesToProcess		64

static int		QualificationState;
ScanKeyEntryData	QualificationKeyData[2];

AttributeNumber		indexAttrNumber;
ObjectId	 	indexAttrClass;

/* ----------------------------------------------------------------
 *			   misc functions
 * ----------------------------------------------------------------
 */

/* ----------------
 *	DoCreateIndex
 * ----------------
 */

void 
DoCreateIndex(heapName, indexName, attribute, cls)
   Name			heapName;
   Name			indexName;
   AttributeNumber	attribute;
   ObjectId		cls;
{
   AttributeNumber  attributeNumber[1];
   ObjectId	    attributeClass[1];

   elog(NOTICE, "DoCreateIndex... start");

   attributeNumber[0] = attribute;
   attributeClass[0] =  cls;


    RelationNameCreateIndexRelation(heapName,
    			   	    indexName,
    				    402,	/* rtree am number */
    				    1,
    				    attributeNumber,
    				    attributeClass,
    				    0,
    				    (Datum *) NULL);

   elog(NOTICE, "DoCreateIndex... end");

}

/* ----------------
 *	ShowResult
 * ----------------
 */

void 
ShowResult(result, heapRelation)
   GeneralRetrieveIndexResult	result;
   Relation			heapRelation;
{
   ItemPointer	pointer;

   Assert(GeneralRetrieveIndexResultIsValid(result));

   pointer = GeneralRetrieveIndexResultGetHeapItemPointer(result);

   printf("\t");

   if (!ItemPointerIsValid(pointer)) {
      printf("<invalid>\n");
   } else {
      HeapTuple	tuple;
      Buffer	buffer;

      printf("[b,p,o %d, %d, %3d] ",
	     ItemPointerGetBlockNumber(pointer),
	     ItemPointerSimpleGetPageNumber(pointer),
	     ItemPointerSimpleGetOffsetNumber(pointer));

      tuple =
	 RelationGetHeapTupleByItemPointer(heapRelation,
			NowTimeQual,
					   pointer,
					   &buffer);

      if (!HeapTupleIsValid(tuple)) {
	 printf("*NULL*\n");

      } else {
	 TupleDescriptor	descriptor;
	 AttributeValue		value;
	 Boolean		valueIsNull;

	 descriptor = RelationGetTupleDescriptor(heapRelation);

	 /* We might want to print more than just the index att, here */
	 value =
	    HeapTupleGetAttributeValue(tuple,
				       buffer,
				       indexAttrNumber,
				       descriptor,
				       &valueIsNull);

	 if (valueIsNull) {
	    printf("<NULL>\n");
	 } else {
	    printf("<0x%x(%d)>\n",
		   DatumGetObjectId(value), 
		   DatumGetObjectId(value));
	 }

	 ReleaseBuffer(buffer);
      }
   }
}

/* ----------------
 *	ShowScanKeyEntry
 * ----------------
 */

void 
ShowScanKeyEntry(entry)
   ScanKeyEntry	entry;
{
   printf("Qualification is procedure 0x%x(%d) for %d with 0x%x\n",
	  entry->procedure,
	  entry->procedure,
	  DatumGetObjectId(entry->argument),
	  entry->flags);
}

/* ----------------------------------------------------------------
 *			  scan functions
 * ----------------------------------------------------------------
 */

/* ----------------
 *	DoForwardScan
 * ----------------
 */

void 
DoForwardScan(indexRelation, heapRelation)
   Relation	indexRelation;
   Relation	heapRelation;
{
   IndexScanDesc		scan;
   GeneralRetrieveIndexResult	result;

   puts("A complete forward scan of the index reveals...");

   scan = RelationGetIndexScan(indexRelation, 0, 0, (ScanKey) NULL);

   for (;;) {
      result = AMgettuple(scan, 1);

      if (!GeneralRetrieveIndexResultIsValid(result))
	 break;

	 ShowResult(result, heapRelation);
   }

   AMendscan(scan);
}

/* ----------------
 *	DoBackwardScan
 * ----------------
 */

void 
DoBackwardScan(indexRelation, heapRelation)
   Relation	indexRelation;
   Relation	heapRelation;
{
   IndexScanDesc		scan;
   GeneralRetrieveIndexResult	result;

   puts("A complete reverse scan of the index reveals...");

   scan = RelationGetIndexScan(indexRelation, -1, 0, (ScanKey) NULL);

   for (;;) {
      result = AMgettuple(scan, -1);

      if (!GeneralRetrieveIndexResultIsValid(result))
	 break;

	 ShowResult(result, heapRelation);
   }

   AMendscan(scan);
}


/* ----------------
 *	DoSingleQualification
 * ----------------
 */

void 
DoSingleQualification(indexRelation, heapRelation, entry)
   Relation	indexRelation;
   Relation	heapRelation;
   ScanKeyEntry	entry;
{
   IndexScanDesc		scan;
   GeneralRetrieveIndexResult	result;

   QualificationKeyData[0] = *entry;

   ShowScanKeyEntry(&QualificationKeyData[0]);
   puts("A scan of the index reveals...");

   scan = RelationGetIndexScan(indexRelation, 0, 1,
			       (ScanKey)QualificationKeyData);

   for (;;) {
      result = AMgettuple(scan, 1);

      if (!GeneralRetrieveIndexResultIsValid(result))
	 break;

	 ShowResult(result, heapRelation);
   }

   AMendscan(scan);
}


/* ----------------
 *	DoDoubleQualification
 * ----------------
 */

void 
DoDoubleQualification(indexRelation, heapRelation, entry)
   Relation	indexRelation;
   Relation	heapRelation;
   ScanKeyEntry	entry;
{
   IndexScanDesc		scan;
   GeneralRetrieveIndexResult	result;

   QualificationKeyData[1] = *entry;

   ShowScanKeyEntry(&QualificationKeyData[0]);
   ShowScanKeyEntry(&QualificationKeyData[1]);
   puts("A scan of the index reveals...");

   scan = RelationGetIndexScan(indexRelation, 0, 2,
			       (ScanKey)QualificationKeyData);
   for (;;) {
      result = AMgettuple(scan, 1);

      if (!GeneralRetrieveIndexResultIsValid(result))
	 break;

	 ShowResult(result, heapRelation);
   }
   AMendscan(scan);
}

/* ----------------
 *	DoMarkedQualification
 * ----------------
 */

void
DoMarkedQualification(indexRelation, heapRelation, entry)
   Relation	indexRelation;
   Relation	heapRelation;
   ScanKeyEntry	entry;
{
   IndexScanDesc		scan;
   GeneralRetrieveIndexResult	result;
   int				tuplesLeft;
   bool				markIsSet;
   int				whichWay;

   tuplesLeft = MarkedTuplesToProcess;
   markIsSet = false;

   srandom((int)time(0));

   QualificationKeyData[0] = *entry;

   ShowScanKeyEntry(&QualificationKeyData[0]);
   puts("A scan of the index reveals...");

   scan = RelationGetIndexScan(indexRelation, (bool) (0x1 & random()), 1,
			       (ScanKey)QualificationKeyData);

   while (tuplesLeft > 0) {
      if (!(random() & 0xf)) {
	 printf("RESTARTING SCAN\n");
	 AMrescan(scan, (bool) (0x1 & random()), (ScanKey)QualificationKeyData);
	 markIsSet = false;
      }
      if (!(random() & 0x3)) {
	 if (markIsSet) {
	    printf("RESTORING MARK\n");
	    AMrestrpos(scan);
	    if (!(0x1 & random())) {
	       markIsSet = false;
	    }
	 } else {
	    printf("SET MARK\n");
	    AMmarkpos(scan);
	    markIsSet = true;
	 }
      }

      if ((random() & 0x1) == 0x1)
	 whichWay = -1;		/* backward */
      else
	 whichWay = 1;		/* forward */

      result = AMgettuple(scan, whichWay);

      if (!GeneralRetrieveIndexResultIsValid(result)) {
	 puts("\t*NULL*");
      } else {
	 ShowResult(result, heapRelation);
      }

      tuplesLeft -= 1;
   }
   AMendscan(scan);
}

/* ----------------------------------------------------------------
 *	DoQualifiedScan
 * ----------------------------------------------------------------
 */

void
DoQualifiedScan(indexRelation, heapRelation, operation, flags, returnType)
   Relation	indexRelation;
   Relation	heapRelation;
   char		*operation;
   uint16	flags;
   ObjectId	returnType;
{
   StrategyNumber	strategyNumber;
   IndexStrategy	indexStrategy;
   StrategyMap		strategyMap;
   ScanKeyEntry		scanKeyEntry;

   if (strcmp(operation, "<") == 0) {	
      strategyNumber = BTreeLessThanStrategyNumber;
   } else if (strcmp(operation, "<=") == 0) {
      strategyNumber = BTreeLessThanOrEqualStrategyNumber;
   } else if (strcmp(operation, "=") == 0) {
      strategyNumber = BTreeEqualStrategyNumber;
   } else if (strcmp(operation, ">=") == 0) {
      strategyNumber = BTreeGreaterThanOrEqualStrategyNumber;
   } else if (strcmp(operation, ">") == 0) {
      strategyNumber = BTreeGreaterThanStrategyNumber;
   } else {
      fprintf(stderr, "testrtree: unknown operation \"%s\"\n",
	      operation);
      return;
   }

   indexStrategy = RelationGetIndexStrategy(indexRelation);

   strategyMap = IndexStrategyGetStrategyMap(indexStrategy,
					     BTreeNumberOfStrategies, 1);

   scanKeyEntry = StrategyMapGetScanKeyEntry(strategyMap, strategyNumber);

   if (!RegProcedureIsValid(scanKeyEntry->procedure)) {
      fprintf(stderr, "testrtree: no procedure for strategy %d\n",
	      strategyNumber);

      return;
   }

   /*
    * Note: very dangerous to modify the reldesc, since it is cached.
    */
   scanKeyEntry->attributeNumber = 1;
   scanKeyEntry->flags = flags;
   scanKeyEntry->argument = ObjectIdGetDatum(returnType);

   switch (QualificationState) {
   case UninitializedQualification:
   case MarkedQualification:
      QualificationState = SingleQualification;
      DoSingleQualification(indexRelation, heapRelation,
			    scanKeyEntry);
      break;
   case SingleQualification:
      QualificationState = DoubleQualification;
      DoDoubleQualification(indexRelation, heapRelation,
			    scanKeyEntry);
      break;
   case DoubleQualification:
      QualificationState = MarkedQualification;
      DoMarkedQualification(indexRelation, heapRelation,
			    scanKeyEntry);
      break;
   default:
      fprintf(stderr, "testrtree: internal error!");
      exitpg(255);
   }
}

/* ----------------------------------------------------------------
 *			     TestMain
 * ----------------------------------------------------------------
 */

void
TestMain()
{
   static NameData	heapNameData;
   static NameData 	indexNameData;
          Relation	indexRelation;
          Relation	heapRelation;
   static int	        beenHere = 0;
	  Portal	portal;


   Relation		AMopenr();	/* XXX */


   /*
    *  Create a dummy portal to get memory management.
    */

   portal = CreatePortal("<blank>");
   StartTransactionCommand(portal);

   if (beenHere == 0) {
      char *result;
      bool newIndex;
      bool done;

      /* discard any stuff on the input stream */
      fseek(stdin, 0, 2);

      fprintf(stdout, "Relation: ");
      fflush(stdout);

      if ((result = gets(&heapNameData.data[0])) == (char *) NULL) {
	 elog(NOTICE, "testrtree: no heap specified");
	 exitpg(0);
      }

      fprintf(stdout, "Index name: ");
      fflush(stdout);

      if ((result = gets(&indexNameData.data[0])) == (char *) NULL) {
	 elog(NOTICE, "testrtree: no index specified");
	 exitpg(0);
      }

      do {
	 char buf[20];

	 fprintf(stdout, "Is this a new index? (y/n) ");
	 fflush(stdout);

	 result = gets(&buf[0]);

	 if (result != (char *) NULL) {
	    if (strcmp(result, "y") == 0) {
	       newIndex = true;
	       done = true;
	    } else if (strcmp(result, "n") == 0) {
	       newIndex = false;
	       done = true;
	    } else {
	       fprintf(stdout, "*** y or n only ***\n");
	       done = false;
	    }
	 } else {
	    fprintf(stdout, "*** y or n only ***\n");
	    done = false;
	 }
      } while (!done);

      fprintf(stdout, "Attribute number for index: ");
      fflush(stdout);

      if (scanf("%hd", &indexAttrNumber) != 1) {
	 elog(NOTICE, "testrtree: attribute incorrectly specified");
	 exitpg(0);
      }

      if (newIndex) {
	 fprintf(stdout, "Assuming box attribute.\n");
	 indexAttrClass = 422;

	 fseek(stdin, 0, 2);

	 beenHere = 1;

	 DoCreateIndex(&heapNameData, &indexNameData,
			indexAttrNumber, indexAttrClass);
      }

      CommitTransactionCommand();
      StartTransactionCommand(portal);

      beenHere = 2;
   } else if (beenHere == 1) {
      elog(NOTICE, "testrtree: %s relation may exist--continuing!",
	   indexNameData.data);
      beenHere = 2;

   } else if (beenHere > 1) {
      elog(FATAL, "testrtree: giving up!");
   }

   indexRelation = AMopenr(indexNameData.data);	/* XXX */
   printf("And the relation is...\n");
   fflush(stdout);
   rtshowtree(indexRelation);

#ifdef NOTYET

   DoForwardScan(indexRelation, heapRelation);

   DoBackwardScan(indexRelation, heapRelation);

   for (;;) {
      int		status;
      NameData		operatorNameData;
      ObjectId		returnType;

      puts("\nPlease enter an operator and an integer value");

      fseek(stdin, 0, 2);	/* discard any stuff on the input stream */
      status = scanf("%s%d", &(operatorNameData), &returnType);

      if (status <= 0) {
	 break;
      } else if (status != 2) {
	 elog(NOTICE, "testrtree: improper qualification specified");
	 exitpg(0);
      }

      DoQualifiedScan(indexRelation, heapRelation, &operatorNameData,
		      0, returnType);
   }

   RelationCloseHeapRelation(heapRelation);
   RelationCloseIndexRelation(indexRelation);

   puts("\nDone!");
   CommitTransactionCommand();

#endif NOTYET

}
