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

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

#include <stdio.h>

#include "c.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"
#include "portal.h"
#include "sdir.h"
#include "tqual.h"
#include "valid.h"
#include "xcxt.h"

#include "btree.h"

RcsId("$Header: /private/postgres/src/test/RCS/testbtdel.c,v 1.10 1992/03/04 14:10:35 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;
   
   /*
   startmmgr(M_STATIC);		/* M_DYNAMIC is buggy */
   
   RelationNameCreateIndexRelation(heapName,
				   indexName,
				   400 /* B-tree AM */,
				   1,
				   attributeNumber,
				   attributeClass,
				   0,
				   (Datum *) NULL);
   /*
   endmmgr(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);
	 
	 /* may want to print more than just the index attribute, 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);
}

/* ----------------------------------------------------------------
 *			browse functions
 * ----------------------------------------------------------------
 */

/* ----------------
 *	DoBTreeBrowse
 * ----------------
 */

/*ARGSUSED*/
void 
   DoBTreeBrowse(indexRelation, heapRelation)
Relation	indexRelation;
Relation	heapRelation;
{
   BTreeNode   node;
   BlockNumber blockNumber;
   PageNumber  pageNumber;
   OffsetIndex off;
   int cnt;
   
   puts("\n--- BTree Browse ---");
   puts("at the prompt, enter a block number and a page number");
   puts("separated by a space or \".\" alone to end\n");
   
   forever {
	forever {
	   Puts("browse> ");
	   fflush(stdout);
	      
	   cnt = scanf("%d %d", &blockNumber, &pageNumber);
	   if (cnt != 2) {
		puts("\n--- ending BTree Browse ---");
		fseek(stdin, 0, 2);
		break;
	   }
		 
	   node = RelationFormBTreeNode(indexRelation, blockNumber, pageNumber);
	   DumpBTreeNode(node);
	   BTreeNodeFree(node);
	   putchar('\n');
	}

	Puts("Enter offset number: ");
	cnt = scanf("%hd", &off);
	if (cnt != 1) {
		puts("\n--- no more deletion ---");
		fseek(stdin, 0, 2);
		break;
	}

	/* do it */
	BTreeIndexTupleDelete(node, off);
   }
}

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

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

void 
   DoForwardScan(indexRelation, heapRelation)
Relation	indexRelation;
Relation	heapRelation;
{
   IndexScan			scan;
   GeneralRetrieveIndexResult	result;
   
   puts("A complete forward scan of the index reveals...");

   scan = RelationGetIndexScan(indexRelation, 0, 0, (ScanKey) NULL);
   while (result = IndexScanGetGeneralRetrieveIndexResult(scan, 0),
	  GeneralRetrieveIndexResultIsValid(result))
      {
	 ShowResult(result, heapRelation);
      }
   IndexScanEnd(scan);
}

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

void 
   DoBackwardScan(indexRelation, heapRelation)
Relation	indexRelation;
Relation	heapRelation;
{
   IndexScan			scan;
   GeneralRetrieveIndexResult	result;
   
   puts("A complete reverse scan of the index reveals...");

   scan = RelationGetIndexScan(indexRelation, -1, 0, (ScanKey) NULL);
   while (result = IndexScanGetGeneralRetrieveIndexResult(scan, 1),
	  GeneralRetrieveIndexResultIsValid(result))
      {
	 ShowResult(result, heapRelation);
      }
   IndexScanEnd(scan);
}


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

void 
   DoSingleQualification(indexRelation, heapRelation, entry)
Relation	indexRelation;
Relation	heapRelation;
ScanKeyEntry	entry;
{
   IndexScan			scan;
   GeneralRetrieveIndexResult	result;
   
   QualificationKeyData[0] = *entry;
   
   ShowScanKeyEntry(&QualificationKeyData[0]);
   puts("A SingleQual scan of the index reveals...");
   
   scan = RelationGetIndexScan(indexRelation, 0, 1,
			       (ScanKey)QualificationKeyData);
   while (result = IndexScanGetGeneralRetrieveIndexResult(scan, 0),
	  GeneralRetrieveIndexResultIsValid(result))
      {
	 ShowResult(result, heapRelation);
      }
   IndexScanEnd(scan);
}


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

void 
   DoDoubleQualification(indexRelation, heapRelation, entry)
Relation	indexRelation;
Relation	heapRelation;
ScanKeyEntry	entry;
{
   IndexScan			scan;
   GeneralRetrieveIndexResult	result;
   
   QualificationKeyData[1] = *entry;
   
   ShowScanKeyEntry(&QualificationKeyData[0]);
   ShowScanKeyEntry(&QualificationKeyData[1]);
   puts("A DoubleQual scan of the index reveals...");
   
   scan = RelationGetIndexScan(indexRelation, 0, 2,
			       (ScanKey)QualificationKeyData);
   while (result = IndexScanGetGeneralRetrieveIndexResult(scan, 0),
	  GeneralRetrieveIndexResultIsValid(result))
      {
	 ShowResult(result, heapRelation);
      }
   IndexScanEnd(scan);
}

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

void 
   DoMarkedQualification(indexRelation, heapRelation, entry)
Relation	indexRelation;
Relation	heapRelation;
ScanKeyEntry	entry;
{
   IndexScan			scan;
   GeneralRetrieveIndexResult	result;
   int				tuplesLeft;
   bool				markIsSet;
   
   tuplesLeft = MarkedTuplesToProcess;
   markIsSet = false;

   srandom((int)time(0));
   
   QualificationKeyData[0] = *entry;
   
   ShowScanKeyEntry(&QualificationKeyData[0]);
   puts("A MarkedQual scan of the index reveals...");
   
   scan = RelationGetIndexScan(indexRelation, 0x1 & random(), 1,
			       (ScanKey)QualificationKeyData);
   
   while (tuplesLeft > 0) {
      if (!(random() & 0xf)) {
	 printf("RESTARTING SCAN\n");
	 IndexScanRestart(scan,
			  0x1 & random(),
			  &QualificationKeyData[0]);
	 markIsSet = false;
      }
      if (!(random() & 0x3)) {
	 if (markIsSet) {
	    printf("RESTORING MARK\n");
	    IndexScanRestorePosition(scan);
	    if (!(0x1 & random())) {
	       markIsSet = false;
	    }
	 } else {
	    printf("SET MARK\n");
	    IndexScanMarkPosition(scan);
	    markIsSet = true;
	 }
      }
      
      result = IndexScanGetGeneralRetrieveIndexResult(scan, 0x1 & random());
      if (!GeneralRetrieveIndexResultIsValid(result)) {
	 puts("\t*NULL*");
      } else {
	 ShowResult(result, heapRelation);
      }
      
      tuplesLeft -= 1;
   }
   IndexScanEnd(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, "testbtdel: unknown operation \"%s\"\n",
	      operation);
      return;
   }
   
   indexStrategy = RelationGetIndexStrategy(indexRelation);
   
   strategyMap = IndexStrategyGetStrategyMap(indexStrategy,
					     BTreeNumberOfStrategies, 1);
   
   scanKeyEntry = StrategyMapGetScanKeyEntry(strategyMap, strategyNumber);
   
   if (!RegProcedureIsValid(scanKeyEntry->procedure)) {
      fprintf(stderr, "testbtdel: 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, "testbtdel: 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 */

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

   if (beenHere == 0) {

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

      puts("Please enter a heap relation name");
      if (scanf("%s", heapNameData.data) != 1) {
	 elog(NOTICE, "testbtdel: no heap specified");
	 exitpg(0);
      }
      puts("Please enter an index name");
      if (scanf("%s", indexNameData.data) != 1) {
	 elog(NOTICE, "testbtdel: no index specified");
	 exitpg(0);
      }
      
      puts("Please enter an attribute number to index");
      puts("If the relation is already indexed, enter");
      puts("the negative of the index attribute number");
      if (scanf("%hd", &indexAttrNumber) != 1) {
	 elog(NOTICE, "testbtdel: attribute incorrectly specified");
	 exitpg(0);
      }
      
      if (indexAttrNumber > 0) {
	 puts("Please enter the operator class of the attribute");
	 puts("--> hint: intops = 421 <--");
	 if (scanf("%d", &indexAttrClass) != 1) {
	    elog(NOTICE, "testbtdel: op class incorrectly specified");
	    exitpg(0);
	 }
	 
	 fseek(stdin, 0, 2);
	 
	 beenHere = 1;
	 
	 DoCreateIndex(&heapNameData, &indexNameData,
		       indexAttrNumber, indexAttrClass);

      } else {
         indexAttrNumber = -indexAttrNumber;
      }

      CommitTransactionCommand();
      StartTransactionCommand(portal);
 
      beenHere = 2;
      
   } else if (beenHere == 1) {
      elog(NOTICE, "testbtdel: %s relation may exist--continuing!",
	   indexNameData.data);
      beenHere = 2;
      
   } else if (beenHere > 1) {
      elog(FATAL, "testbtdel: giving up!");
   }

   QualificationState = UninitializedQualification;
   
   indexRelation = AMopenr(indexNameData.data);
   heapRelation = RelationNameOpenHeapRelation(heapNameData.data);

   DoBTreeBrowse(indexRelation, heapRelation);

   DoForwardScan(indexRelation, heapRelation);
   
   DoBackwardScan(indexRelation, heapRelation);

   for (;;) {
      int		status;
      NameData		operatorNameData;
      int		flags;
      ObjectId		returnType;
      
      puts("\nPlease enter an operator, flags, and an integer value");

      fseek(stdin, 0, 2);	/* discard any stuff on the input stream */
      status = scanf("%s%d%d", &(operatorNameData), &flags,
		     &returnType);
      
      if (status <= 0) {
	 break;
      } else if (status != 3) {
	 elog(NOTICE, "testbtdel: improper qualification specified");
	 exitpg(0);
      }
      
      DoQualifiedScan(indexRelation, heapRelation, &operatorNameData,
		      flags, returnType);
   }
   
   RelationCloseHeapRelation(heapRelation);
   RelationCloseIndexRelation(indexRelation);
   
   CommitTransactionCommand();

   puts("\nDone!");
}
