Adding fulldisjunctions to the contrib

Started by Tzahi Fadidaover 19 years ago42 messages
#1Tzahi Fadida
Tzahi.ML@gmail.com
1 attachment(s)

Hi,
I wish to add the fulldisjunctions function to the contrib.
With the help of Jonah, we (or rather he :) created a patch with
regression tests. The function is finished programmatically but
still a little more code documentation touches and improved error messages
are needed. All the rest was extensively tested.

Attached is the patch.

Works great. Just compiled from a fresh cvs which i patched with the
attached diff. ran the fulldijsjunction.sql in the
share/contrib/fulldisjunction and let it run and it works great.
10x.

--
Regards,
        Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS:  see at
http://members.lycos.co.uk/my2nis/spamwarning.html

Attachments:

fulldisjunction.difftext/x-diff; charset=iso-8859-1; name=fulldisjunction.diffDownload
diff -crN pgsql/contrib/fulldisjunctions/algstructs.c pgsql-fd/contrib/fulldisjunctions/algstructs.c
*** pgsql/contrib/fulldisjunctions/algstructs.c	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/algstructs.c	2006-07-30 15:28:47.000000000 -0400
***************
*** 0 ****
--- 1,1071 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #include "postgres.h"
+ #include <string.h>
+ #include <time.h>
+ #include "algstructs.h"
+ #include "queues.h"
+ #include "queuesfuncs.h"
+ #include "algutils.h"
+ #include "catalog/pg_namespace.h"
+ #include "utils/memutils.h"
+ #include "parser/parse_coerce.h"
+ 
+ /* a helper function to convert time var to pretty text time */
+ static char *
+ str_time(time_t tnow)
+ {
+ 	static char buf[128];
+ 
+ 	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z", localtime(&tnow));
+ 	return buf;
+ }
+ 
+ /* here we parse the relations parameter the user gives 
+  * into a list of Oid of these tables */
+ Oid *
+ parseOIDs(text *relsStr, int *numOfRels)
+ {
+ 	if (relsStr == NULL)
+ 		elog(ERROR, "Something is wrong with the relations inputed");
+ 
+ 	int			relsStrSize = VARATT_SIZEP(relsStr) - VARHDRSZ + 1;
+ 	char		relsStrCp[relsStrSize];
+ 
+ 	memcpy(relsStrCp, VARDATA(relsStr), relsStrSize - 1);
+ 	relsStrCp[relsStrSize - 1] = (char) NULL;
+ 	return parseOIDs_(relsStrCp, numOfRels);
+ }
+ 
+ /* we just convert text to str */
+ char *
+ getRelsCharStr(text *relsStr)
+ {
+ 	if (relsStr == NULL)
+ 		elog(ERROR, "Something is wrong with the relations inputed");
+ 	int			relsStrSize = VARATT_SIZEP(relsStr) - VARHDRSZ + 1;
+ 	char	   *relsStrCp = (char *) palloc(relsStrSize * sizeof(char));
+ 
+ 	memcpy(relsStrCp, VARDATA(relsStr), relsStrSize - 1);
+ 	relsStrCp[relsStrSize - 1] = (char) NULL;
+ 	return relsStrCp;
+ }
+ 
+ /* here we parse the relations into a list of its Oids */
+ Oid *
+ parseOIDs_(char *relsStrCp, int *numOfRels)
+ {
+ 	Oid			oids[MaxHeapAttributeNumber];
+ 	const char	delimiters[] = ",";
+ 	const char	tokenDelimiters[] = ".";
+ 	char	   *token = NULL;
+ 	char	   *relsStrSvpt = NULL;
+ 	char	   *tokenSvpt = NULL;
+ 
+ 	token = strtok_r(relsStrCp, delimiters, &relsStrSvpt);
+     
+ 	char	   *namespace = NULL;
+ 	char	   *relname = NULL;
+ 	int			i = -1;
+ 	Oid			namespaceId = -1;
+ 
+ 	while (token != NULL)
+ 	{
+ 		namespace = strtok_r(token, tokenDelimiters, &tokenSvpt);
+ 		relname = strtok_r(NULL, tokenDelimiters, &tokenSvpt);
+ 		if (relname == NULL)
+ 		{
+ 			relname = namespace;
+ 			namespace = NULL;
+ 		}
+ 		if (relname == NULL)
+ 			elog(ERROR, "Something is wrong with the relations inputed, check delimiters , and .");
+ 		i++;
+ 		if (namespace != NULL)
+ 			namespaceId = GetSysCacheOid(NAMESPACENAME,
+ 										 CStringGetDatum(namespace), 0, 0, 0);
+ 		else
+ 			namespaceId = PG_PUBLIC_NAMESPACE;
+ 		if (!OidIsValid(namespaceId))
+ 			elog(ERROR, "Something is wrong with the relations inputed\
+ 		     ,possibly no such namespace ");
+ 		if (!OidIsValid(oids[i] = get_relname_relid(relname, namespaceId)))
+ 			elog(ERROR, "Something is wrong with the relations inputed, not such namespace or\
+ 			  no such relation ");
+ 		token = strtok_r(NULL, delimiters, &relsStrSvpt);
+ 	}
+ 	*numOfRels = i + 1;
+ 	Oid		   *relsOID = (Oid *) palloc(*numOfRels * sizeof(Oid));
+ 
+ 	for (i = 0; i < *numOfRels; i++)
+ 		relsOID[i] = oids[i];
+ 	return relsOID;
+ }
+ 
+ /* Here we pass the inputted mapping parameter into a Dictionary (see .h file */
+ Dictionary *
+ parseAttributeDictionary(char *mappingStrCp, Oid *relsOid, int numOfRels)
+ {
+ 	int			i,
+ 				j;
+ 	int			numAtts[numOfRels];
+ 	int			maxNumAtts = 0;
+ 	char	   *attName;
+ 	int			numPotentialAttNames = 0;
+ 	Mapping  ***relsAttsMappings = (Mapping ***) palloc(numOfRels * sizeof(Mapping **));
+ 	int		   *numMappings = (int *) palloc(numOfRels * sizeof(int));
+ 
+ 	for (i = 0; i < numOfRels; i++)
+ 	{
+ 		numAtts[i] = 0;
+ 		numMappings[i] = 0;
+ 		HeapTuple	attTuple;
+ 
+ 		for (j = 1;; j++)
+ 		{
+ 			attName = (char *) get_attname(relsOid[i], j);
+ 			if (attName != NULL)
+ 			{
+ 				attTuple = SearchSysCacheAttName(relsOid[i], attName);
+ 				if (attTuple != NULL)
+ 				{
+ 					ReleaseSysCache(attTuple);
+ 					numAtts[i]++;
+ 				}
+ 			}
+ 			else
+ 				break;
+ 		}
+ 		maxNumAtts += numAtts[i];
+ 		relsAttsMappings[i] = (Mapping **) palloc(numAtts[i] * sizeof(Mapping *));
+ 	}
+     
+ 	char	   *resultTuplePotentialAttNames[maxNumAtts];
+ 	const char	delimiters[] = ",";
+ 	const char	tokenDelimiters[] = "=.";
+ 	char	   *token = NULL;
+ 	char	   *mappingStrSvpt = NULL;
+ 	char	   *tokenSvpt = NULL;
+ 
+ 	token = strtok_r(mappingStrCp, delimiters, &mappingStrSvpt);
+ 	char	   *namespace = NULL;
+ 	char	   *relname = NULL;
+ 	char	   *resultTupleAttName;
+ 	char	   *relAttName;
+ 
+ 	i = -1;
+ 	Oid			namespaceId = -1;
+ 	Oid			relOid = -1;
+ 	int			relid;
+ 
+ 	while (token != NULL)
+ 	{
+ 		resultTupleAttName = strtok_r(token, tokenDelimiters, &tokenSvpt);
+ 		if (resultTupleAttName == NULL)
+ 			elog(ERROR, "problem parsing mapping string! check your format. e.g lastname=public.rel1.username");
+ 		namespace = strtok_r(NULL, tokenDelimiters, &tokenSvpt);
+ 		if (namespace == NULL)
+ 			elog(ERROR, "problem parsing mapping string! check your format. e.g lastname=public.rel1.username");
+ 		relname = strtok_r(NULL, tokenDelimiters, &tokenSvpt);
+ 		if (relname == NULL)
+ 			elog(ERROR, "problem parsing mapping string! check your format. e.g lastname=public.rel1.username");
+ 		relAttName = strtok_r(NULL, tokenDelimiters, &tokenSvpt);
+ 		if (relAttName == NULL)
+ 			elog(ERROR, "problem parsing mapping string! check your format. e.g lastname=public.rel1.username");
+ 		if (namespace != NULL)
+ 			namespaceId = GetSysCacheOid(NAMESPACENAME,
+ 										 CStringGetDatum(namespace), 0, 0, 0);
+ 		if (!OidIsValid(namespaceId))
+ 			elog(ERROR, "Something is wrong with the relations inputed\
+ 		     ,possibly no such namespace %s ", namespace);
+ 		if (!OidIsValid(relOid = get_relname_relid(relname, namespaceId)))
+ 			elog(ERROR, "Something is wrong with the relations inputed, not such namespace or\
+     no such relation:	  %s ", relname);
+ 		HeapTuple	attTuple = SearchSysCacheAttName(relOid, relAttName);
+ 
+ 		if (attTuple != NULL)
+ 			ReleaseSysCache(attTuple);
+ 		else
+ 			elog(ERROR, "Something is wrong with the relations inputed, not such namespace or\
+     no such relation or no such att:%s ", relAttName);
+ 	
+         relid = -1;
+ 		for (i = 0; i < numOfRels; i++)
+ 		{
+ 			if (relOid == relsOid[i])
+ 			{
+ 				relid = i;
+ 				break;
+ 			}
+ 		}
+ 		if (relid == -1)
+ 			elog(ERROR, "No such relation - %s.%s - in the inputted relations", namespace, relname);
+ 		int			nameLocation = -1;
+ 
+ 		for (i = 0; i < numPotentialAttNames; i++)
+ 			if (strcmp(resultTuplePotentialAttNames[i], resultTupleAttName) == 0)
+ 			{
+ 				nameLocation = i;
+ 				break;
+ 			}
+ 		if (nameLocation == -1)
+ 		{
+ 			resultTuplePotentialAttNames[numPotentialAttNames] = resultTupleAttName;
+ 			nameLocation = numPotentialAttNames;
+ 			numPotentialAttNames++;
+ 		}
+         
+ 		Mapping    *mapping = palloc(sizeof(Mapping));
+ 
+ 		mapping->resultTuplePotnetialAttNamesID = nameLocation;
+ 		mapping->relAttName = relAttName;
+ 		relsAttsMappings[relid][numMappings[relid]] = mapping;
+ 		numMappings[relid]++;
+ 		token = strtok_r(NULL, delimiters, &mappingStrSvpt);
+ 	}
+ 	Dictionary *dictionary = palloc(sizeof(Dictionary));
+ 
+ 	dictionary->resultTuplePotentialAttNames = palloc(numPotentialAttNames * sizeof(char *));
+     
+ 	for (i = 0; i < numPotentialAttNames; i++)
+ 		dictionary->resultTuplePotentialAttNames[i] = resultTuplePotentialAttNames[i];
+     
+ 	dictionary->numPotentialAttNames = numPotentialAttNames;
+ 	dictionary->relsAttsMappings = relsAttsMappings;
+ 	dictionary->numMappings = numMappings;
+ 	
+     int			k;
+ 	for (i = 0; i < numOfRels; i++)
+ 		for (j = 0; j < numMappings[i]; j++)
+ 		{
+ 			for (k = 0; k < numMappings[i]; k++)
+ 				if ((j != k) &&
+ 					(strcmp(resultTuplePotentialAttNames[relsAttsMappings[i][j]->resultTuplePotnetialAttNamesID],
+ 							relsAttsMappings[i][k]->relAttName) == 0))
+ 					elog(ERROR, "circular mappings:%s,%s",
+ 						 resultTuplePotentialAttNames[relsAttsMappings[i][j]->resultTuplePotnetialAttNamesID],
+ 						 relsAttsMappings[i][k]->relAttName);
+ 			if (strcmp(resultTuplePotentialAttNames[relsAttsMappings[i][j]->resultTuplePotnetialAttNamesID],
+ 					   relsAttsMappings[i][j]->relAttName) != 0)
+ 			{
+ 				HeapTuple	attTuple = SearchSysCacheAttName(relsOid[i],
+ 															 resultTuplePotentialAttNames[relsAttsMappings[i][j]->resultTuplePotnetialAttNamesID]);
+ 
+ 				if (attTuple != NULL)
+ 				{
+ 					ReleaseSysCache(attTuple);
+ 					elog(ERROR, "circular mappings:%s",
+ 						 resultTuplePotentialAttNames[relsAttsMappings[i][j]->resultTuplePotnetialAttNamesID]);
+ 				}
+ 			}
+ 		}
+ 	return dictionary;
+ }
+ 
+ /* we take a dictionary and map an attribute name from a relation 
+  * to another name using the dictionary */
+ char *
+ mapAttName(Dictionary * dictionary, int relid, char *attname)
+ {
+ 	if (dictionary == NULL)
+ 		return attname;
+ 	else
+ 	{
+ 		int			i;
+ 		int			numMappings = dictionary->numMappings[relid];
+ 		Mapping    *mapping = NULL;
+ 
+ 		if (numMappings == 0)
+ 			return attname;
+ 		for (i = 0; i < numMappings; i++)
+ 		{
+ 			mapping = dictionary->relsAttsMappings[relid][i];
+ 			if (strcmp(mapping->relAttName, attname) == 0)
+ 			{
+ 				return dictionary->resultTuplePotentialAttNames[mapping->resultTuplePotnetialAttNamesID];
+ 			}
+ 		}
+ 		return attname;
+ 	}
+ }
+ 
+ /* just a convinience call */
+ void
+ initialize_general_structures(PG_FUNCTION_ARGS, alg_fctx * fctx, FuncCallContext *funcctx)
+ {
+ 	initialize_general_structures_(fcinfo, fctx, true, funcctx);
+ }
+ 
+ /* initialized the fd general required structures */
+ void
+ initialize_general_structures_(PG_FUNCTION_ARGS, alg_fctx * fctx, bool parsing, FuncCallContext *funcctx)
+ {
+ 	unsigned int tmpTime = (unsigned) time(NULL);
+ 
+ 	srand(tmpTime);
+ 
+ #ifdef DEBUG
+ 	ereport(LOG, (errmsg(" ")));
+ 	ereport(LOG, (errmsg("FD-DEBUG: %s", str_time(time(NULL)))));
+ 	ereport(LOG, (errmsg("FD-DEBUG-SRAND:%d", tmpTime)));
+ 	ereport(LOG, (errmsg("FD-DEBUG-Relations:%s", getRelsCharStr(PG_GETARG_TEXT_P(0)))));
+ #endif
+     
+ 	fctx->per_query_ctx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"tuplestore2",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 	fctx->nargs = PG_NARGS();
+ 	char	   *mappingStr = NULL;
+ 
+ 	if (fctx->nargs >= 1)
+ 	{
+ 		char	   *policy = NULL;
+ 
+ 		switch (fctx->nargs)
+ 		{
+ 			case 9:
+ 			case 8:
+ 			case 7:
+ 			case 6:
+ 			case 5:
+ 				{
+ 					text	   *policyText = PG_GETARG_TEXT_P(4);
+ 					int			policyTextSize = VARATT_SIZEP(policyText) - VARHDRSZ;
+ 
+ 					if (policyTextSize == 0)
+ 						policy = NULL;
+ 					else
+ 					{
+ 						policy = (char *) palloc(policyTextSize * sizeof(char) + 1);
+ 						memcpy(policy, VARDATA(policyText), policyTextSize);
+ 						policy[policyTextSize] = '\0';
+ 					}
+ 				}
+ 			case 4:
+ 				fctx->noIndexScans = PG_GETARG_BOOL(3);
+ 			case 3:
+ 				if (fctx->nargs <= 3)
+ 					fctx->noIndexScans = false;
+ 				fctx->noDuplicates = PG_GETARG_BOOL(2);
+ 			case 2:
+ 				{
+ 					text	   *mappingText = PG_GETARG_TEXT_P(1);
+ 					int			mappingTextSize = VARATT_SIZEP(mappingText) - VARHDRSZ;
+ 
+ 					if (mappingTextSize > 0)
+ 					{
+ 						mappingStr = (char *) palloc(mappingTextSize * sizeof(char) + 1);
+ 						memcpy(mappingStr, VARDATA(mappingText), mappingTextSize);
+ 						mappingStr[mappingTextSize] = '\0';
+ 					}
+ 				}
+ 			case 1:
+ 				if (fctx->nargs <= 2)
+ 				{
+ 					fctx->noIndexScans = false;
+ 					fctx->noDuplicates = true;
+ 				}
+ 		}
+ #ifdef DEBUG
+ 		if (policy != NULL)
+ 			ereport(LOG, (errmsg("FD-DEBUG-Policy:%s", policy)));
+ #endif
+ 		fctx->query = NULL;
+ 		fctx->querySize = 0;
+ 		fctx->queryParams = NULL;
+ 		fctx->queryParamsTypes = NULL;
+ 		fctx->queryParamsSize = 0;
+         
+ 		fctx->policyWorkNode = NULL;
+ 		fctx->policySequence = 0;
+ 		fctx->extendingPolicy = -1;
+ 		if ((policy != NULL) && strcmp(policy, "majority") == 0)
+ 			fctx->extendingPolicy = POLICY_MAJORITY;
+ 		else if ((policy != NULL) && strcmp(policy, "mincost") == 0)
+ 			fctx->extendingPolicy = POLICY_MINCOST;
+ 		else if ((policy != NULL) && strcmp(policy, "random") == 0)
+ 			fctx->extendingPolicy = POLICY_RANDOM;
+ 		else if ((policy != NULL) && strcmp(policy, "naive") == 0)
+ 			fctx->extendingPolicy = POLICY_NAIVE;
+ 		else
+ 		{
+ 			if (policy != NULL)
+ 				elog(ERROR, "No such policy:%s", policy);
+ 			fctx->extendingPolicy = POLICY_MAJORITY;
+ 		}
+ 		if (policy != NULL)
+ 			pfree(policy);
+ 	}
+     
+ 	int			i = 0,
+ 				j = 0,
+ 				k = 0;
+ 	char	   *attName = NULL;
+ 
+ 	{
+ 		if (parsing)
+ 			fctx->relsOid = parseOIDs(PG_GETARG_TEXT_P(0), &fctx->numRels);
+ 		fctx->relsPages = (int *) palloc(fctx->numRels * sizeof(int));
+ 		fctx->relsHasIndex = (bool *) palloc(fctx->numRels * sizeof(bool));
+ 		fctx->sortedRelsByRelsPages = (int *) palloc(fctx->numRels * sizeof(int));
+         
+ 		fctx->relsMaxTupleSize = (int *) palloc(fctx->numRels * sizeof(int));
+ 		for (i = 0; i < fctx->numRels; i++)
+ 			fctx->relsMaxTupleSize[i] = 0;
+         
+ 		fctx->relsNAtt = (int *) palloc(fctx->numRels * sizeof(int));
+ 		fctx->relsAttNumsAtTupleSet
+ 			= (int **) palloc(fctx->numRels * sizeof(int *));
+ 		fctx->relsAttTypes = (Oid **) palloc(fctx->numRels * sizeof(Oid *));
+ 		fctx->resultTupleAttNames = NULL;
+ 		fctx->tupleSetAttTypes = NULL;
+         
+ 		fctx->resultTupleDesc = NULL;
+ 		fctx->tupleSetDesc = NULL;
+         
+ 		fctx->resultTupleNAtt = 0;
+ 		fctx->tupleSetMaxSize = RESULT_TUPLE_ASSUMED_MAX_SIZE_IN_BYTES;
+ 		fctx->assumingResultTupleMaxSize = true;
+ 		fctx->relsValidAttNumAtRelation = (int **) palloc(fctx->numRels * sizeof(int *));
+         
+ 		fctx->bits_scheme_graph = (RBITS *) palloc(fctx->numRels * sizeof(RBITS));
+         
+ 		fctx->dynamic_extend_cap = (adaptive_extend_upperbound *) palloc(sizeof(adaptive_extend_upperbound));
+ 		fctx->dynamic_extend_cap->previousScore = 0;
+ 		fctx->dynamic_extend_cap->nextUpperBound = 0;
+ 		fctx->dynamic_extend_cap->stage = 0;
+ 		fctx->dynamic_extend_cap->maxInputedExtendTuples = 0;
+         
+ 		fctx->printedTSet = NULL;
+ 		fctx->startedPrintingQueue = false;
+ 		fctx->printedTupleAddress = NULL;
+ 	}
+     
+     /* sort the array of relations, i.e. their oids */
+ 	sortOidArray(fctx->relsOid, fctx->numRels);
+     
+     /* Lets parse the dictionary. */
+ 	if (mappingStr == NULL)
+ 	{
+ 		fctx->dictionary = NULL;
+ 	}
+ 	else
+ 		fctx->dictionary = parseAttributeDictionary(mappingStr, fctx->relsOid, fctx->numRels);
+     
+ 	/* get each rel number of pages. */
+ 	{
+ 		Form_pg_class pgcform;
+ 		HeapTuple	ctup;
+ 		int			tmpRelsPages[fctx->numRels];
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			ctup = SearchSysCache(RELOID,
+ 								  ObjectIdGetDatum(fctx->relsOid[i]),
+ 								  0, 0, 0);
+ 			pgcform = (Form_pg_class) GETSTRUCT(ctup);
+ 			fctx->relsHasIndex[i] = pgcform->relhasindex;
+ 			fctx->relsPages[i] = pgcform->relpages;
+ 			ReleaseSysCache(ctup);
+ 			tmpRelsPages[i] = fctx->relsPages[i];
+ 			fctx->sortedRelsByRelsPages[i] = i;
+ 		}
+ 
+ 		/*
+ 		 * Here we create  a sorted array of the relations, sorted by the
+ 		 * number of pages they contain.
+ 		 */
+ 		int			swapint;
+ 
+ 		for (i = 0; i < (fctx->numRels - 1); i++)
+ 			for (j = (i + 1); j < fctx->numRels; j++)
+ 				if (tmpRelsPages[i] > tmpRelsPages[j])
+ 				{
+ 					swapint = tmpRelsPages[i];
+ 					tmpRelsPages[i] = tmpRelsPages[j];
+ 					tmpRelsPages[j] = swapint;
+ 					swapint = fctx->sortedRelsByRelsPages[i];
+ 					fctx->sortedRelsByRelsPages[i] = fctx->sortedRelsByRelsPages[j];
+ 					fctx->sortedRelsByRelsPages[j] = swapint;
+ 				}
+ 	}
+ 
+ 	/*
+ 	 * here we get the number of attributes for each relation And we also
+ 	 * creating the resultTuple Attribute names. we exploit the fact that
+ 	 * attributes of relations that are first in the order comes first in the
+ 	 * result tuple so do not change that!
+ 	 */
+ 	{
+ 		int			relsValidAttNumAtRelation[MaxHeapAttributeNumber];
+ 		bool		foundAtt = 0;
+ 		bool		skipped = false;
+ 		char	   *mappedAttName = NULL;
+ 
+ 		fctx->resultTupleAttNames = (char **) palloc(MaxHeapAttributeNumber * sizeof(char *));
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			fctx->relsNAtt[i] = 0;
+ 			for (j = 1;; j++)
+ 			{
+ 				attName = (char *) get_attname(fctx->relsOid[i], j);
+ 				skipped = false;
+ 				if (attName != NULL)
+ 				{
+ 					/*
+ 					 * here we check if an attribute was dropped by using the
+ 					 * special SearchSysCacheAttName
+ 					 */
+ 					HeapTuple	attTuple = SearchSysCacheAttName(fctx->relsOid[i], attName);
+ 
+ 					if (attTuple != NULL)
+ 						ReleaseSysCache(attTuple);
+ 					else
+ 					{
+ 						pfree(attName);
+ 						attName = NULL;
+ 						skipped = true;
+ 					}
+ 				}
+                 /* Ok so the attribute is valid. For example, an attribute of dropped
+                  * column is not valid.*/
+ 				if (attName != NULL)
+ 				{
+ 					relsValidAttNumAtRelation[fctx->relsNAtt[i]] = j;
+ 					fctx->relsNAtt[i]++;
+ 					foundAtt = 0;
+                     /* lets map the attribute of the relation using the mappings entered
+                      * by the user */
+ 					mappedAttName = mapAttName(fctx->dictionary, i, attName);
+                     /* let us see if we already added this attribute to the result tuple */
+ 					for (k = 0; k < fctx->resultTupleNAtt; k++)
+ 					{
+ 						if (strcmp(fctx->resultTupleAttNames[k], mappedAttName) == 0)
+ 						{
+ 							foundAtt = 1;
+ 							break;
+ 						}
+ 					}
+ 					if (!foundAtt)
+ 					{
+ 						fctx->resultTupleAttNames[fctx->resultTupleNAtt] = mappedAttName;
+ 						fctx->resultTupleNAtt++;
+ 					}
+ 				}
+ 				else if (!skipped)
+ 					break;
+ 			}
+ 			fctx->relsValidAttNumAtRelation[i] = (int *) palloc(fctx->relsNAtt[i] * sizeof(int));
+ 			for (j = 0; j < fctx->relsNAtt[i]; j++)
+ 				fctx->relsValidAttNumAtRelation[i][j] = relsValidAttNumAtRelation[j];
+ 		}
+ 		fctx->tupleSetNatt = fctx->resultTupleNAtt + 1;
+ 	}
+     
+ 	/* lets free the wasted resources. */
+ 	{
+ 		char	  **resultTupleAttNamesTemp
+ 		= (char **) palloc(fctx->resultTupleNAtt * sizeof(char *));
+ 
+ 		for (k = 0; k < fctx->resultTupleNAtt; k++)
+ 		{
+ 			resultTupleAttNamesTemp[k] = fctx->resultTupleAttNames[k];
+ 		}
+ 		pfree(fctx->resultTupleAttNames);
+ 		fctx->resultTupleAttNames = resultTupleAttNamesTemp;
+ 	}
+ 
+ 	/*
+ 	 * here we get the types of each attribute in each relation. And we also
+ 	 * assign the location of each attribute in each relation in the result
+ 	 * tuple, in their respective relation search table.
+ 	 */
+ 	{
+ 		char	   *mappedAttName = NULL;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			fctx->relsAttNumsAtTupleSet[i]
+ 				= (int *) palloc(fctx->relsNAtt[i] * sizeof(int));
+ 			fctx->relsAttTypes[i] = (Oid *) palloc(fctx->relsNAtt[i] * sizeof(Oid));
+ 			for (j = 0; j < fctx->relsNAtt[i]; j++)
+ 			{
+ 				fctx->relsAttTypes[i][j] = get_atttype(fctx->relsOid[i], fctx->relsValidAttNumAtRelation[i][j]);
+ 				attName = (char *) get_attname(fctx->relsOid[i], fctx->relsValidAttNumAtRelation[i][j]);
+ 				mappedAttName = mapAttName(fctx->dictionary, i, attName);
+ 				for (k = 0; k < fctx->resultTupleNAtt; k++)
+ 				{
+ 					if (strcmp(fctx->resultTupleAttNames[k], mappedAttName) == 0)
+ 					{
+ 						fctx->relsAttNumsAtTupleSet[i][j] = k + 1;
+ 						break;
+ 					}
+ 				}
+ 			}
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Here we create a sorted relsAttNumsAtTupleSet by a key of the order of
+ 	 * the tuple set attribute positions per relation
+ 	 */
+ 	int		 ***sortedRelsAttNumsAtTupleSet
+ 	= (int ***) palloc(fctx->numRels * sizeof(int **));
+ 
+ 	{
+ 		int			min,
+ 					minAttNum = -1,
+ 					curr;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			sortedRelsAttNumsAtTupleSet[i] = (int **) palloc(fctx->relsNAtt[i] * sizeof(int *));
+ 			curr = -1;
+ 			for (j = 0; j < fctx->relsNAtt[i]; j++)
+ 			{
+ 				min = MaxHeapAttributeNumber;
+ 				for (k = 0; k < fctx->relsNAtt[i]; k++)
+ 				{
+ 					if ((fctx->relsAttNumsAtTupleSet[i][k] > curr) &&
+ 						(fctx->relsAttNumsAtTupleSet[i][k] < min))
+ 					{
+ 						min = fctx->relsAttNumsAtTupleSet[i][k];
+ 						minAttNum = k;
+ 					}
+ 				}
+ 				curr = min;
+ 				sortedRelsAttNumsAtTupleSet[i][j] = (int *) palloc(2 * sizeof(int));
+ 				/* tuple set att num */
+ 				sortedRelsAttNumsAtTupleSet[i][j][0] = min;
+ 				/* matching rel att num */
+ 				sortedRelsAttNumsAtTupleSet[i][j][1] = minAttNum;
+ 			}
+ 		}
+ 		fctx->sortedRelsAttNumsAtTupleSet = sortedRelsAttNumsAtTupleSet;
+ 	}
+ 
+ 	/*
+ 	 * here we find the proper casting for each attribute in each relation so
+ 	 * that the equality function will be of same type. Also we setup the
+ 	 * attribute types for the tuple set and setup the equality functions we
+ 	 * are going to use.
+ 	 */
+ 	{
+ 		/* tuple set attribute types (oid types) */
+ 		fctx->tupleSetAttTypes = (Oid *) palloc(fctx->tupleSetNatt * sizeof(Oid));
+ 
+ 		/*
+ 		 * the first attribute is an internal fixed type (bytea, no need to
+ 		 * specify it)
+ 		 */
+ 		fctx->tupleSetAttTypes[LABELS] = (Oid) NULL;
+ 		/* tuple set per attribute proper equality functions */
+ 		fctx->tupleSetAttEQFunctions
+ 			= (TypeCacheEntry **) palloc(fctx->tupleSetNatt * sizeof(TypeCacheEntry *));
+ 		/* no need for an equality function for the lables slot */
+ 		fctx->tupleSetAttEQFunctions[LABELS] = NULL;
+ 
+ 		/*
+ 		 * a pointer to the attribute we are currently dealing with per
+ 		 * relation.
+ 		 */
+ 		int			relsAttPointer[fctx->numRels];
+ 
+ 		/*
+ 		 * a structure were each slot is a relation id for the set of
+ 		 * attribute with the same name we are currently dealing with. Not all
+ 		 * slots are filled, in any one time we deal with the number of slots
+ 		 * as the number of attributes with the same name shared among the
+ 		 * relations
+ 		 */
+ 		int			sharedAttRelIDs[fctx->numRels];
+ 
+ 		/*
+ 		 * same as last but contains the original attribute number in the
+ 		 * relsAttType structure
+ 		 */
+ 		int			sharedAttAttNum[fctx->numRels];
+ 
+ 		/*
+ 		 * num of attributes with the same name that can cast to the type of
+ 		 * the slot relID
+ 		 */
+ 		int			numCanCastToAttType[fctx->numRels];
+ 
+ 		/*
+ 		 * number of attribute from the above that must be cast, i.e., have a
+ 		 * different attribute type than the slot
+ 		 */
+ 		int			numMustCastToAttType[fctx->numRels];
+ 		Oid			stype;
+ 		Oid			ttype;
+ 		int			numSharedRelIDs = 0;
+ 		bool		simpleCast;
+ 		int			minMustCast;
+ 		int			minMustCastSharedRelID = -1;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 			relsAttPointer[i] = -1;
+ 		/* we do the casting checks per tuple set attribute */
+ 		for (i = FIRSTATT; i < fctx->tupleSetNatt; i++)
+ 		{
+ 			numSharedRelIDs = 0;
+ 
+ 			/*
+ 			 * here we locate the attribute in each relation with the same
+ 			 * name and put it in sharedAtt*
+ 			 */
+ 			for (j = 0; j < fctx->numRels; j++)
+ 			{
+ 				if ((relsAttPointer[j] + 1) >= fctx->relsNAtt[j])
+ 					relsAttPointer[j] = -2;
+ 				else if ((relsAttPointer[j] > -2) && (i == sortedRelsAttNumsAtTupleSet[j][relsAttPointer[j] + 1][0]))
+ 				{
+ 					relsAttPointer[j]++;
+ 					sharedAttAttNum[numSharedRelIDs] = sortedRelsAttNumsAtTupleSet[j][relsAttPointer[j]][1];
+ 					sharedAttRelIDs[numSharedRelIDs] = j;
+ 					numSharedRelIDs++;
+ 				}
+ 			}
+ 			/* here we find who can cast to whom */
+ 			for (j = 0; j < numSharedRelIDs; j++)
+ 			{
+ 				numCanCastToAttType[j] = 0;
+ 				numMustCastToAttType[j] = 0;
+ 				stype = fctx->relsAttTypes[sharedAttRelIDs[j]][sharedAttAttNum[j]];
+ 				for (k = 0; k < numSharedRelIDs; k++)
+ 				{
+ 					ttype = fctx->relsAttTypes[sharedAttRelIDs[k]][sharedAttAttNum[k]];
+ 					if ((j != k) && (can_coerce_type(1, &stype, &ttype, COERCION_IMPLICIT)))
+ 					{
+ 						numCanCastToAttType[j]++;
+ 						if (stype != ttype)
+ 							numMustCastToAttType[j]++;
+ 					}
+ 				}
+ 			}
+ 			simpleCast = false;
+ 			minMustCast = MaxHeapAttributeNumber;
+ 
+ 			/*
+ 			 * here we find the minimal number of casts absolutely neccessary
+ 			 * in the queries, however, a future policy might consider casting
+ 			 * smallest size in pages of relations or those with the most
+ 			 * edges in the scheme graph to other relations
+ 			 */
+ 			for (j = 0; j < numSharedRelIDs; j++)
+ 			{
+ 				if (numCanCastToAttType[j] == (numSharedRelIDs - 1))
+ 				{
+ 					simpleCast = true;
+ 					if (numMustCastToAttType[j] < minMustCast)
+ 						minMustCast = numMustCastToAttType[j];
+ 					minMustCastSharedRelID = j;
+ 				}
+ 			}
+ 
+ 			/*
+ 			 * if we can't cast all the attributes with the same name to the
+ 			 * same type we go out in error. However, sometimes there are
+ 			 * equality functions that can check for this but are difficult to
+ 			 * identify. Therefore we give the user the option to explicity
+ 			 * tell us that this attribute indeed can be casted. He does that
+ 			 * by first handing out to the FD function an already explicity
+ 			 * casted relations
+ 			 */
+ 			if (!simpleCast)
+ 				elog(ERROR, "Same names at different relations but with differnt types\
+ 			 that are difficult to cast.You can use CAST to explicitly change the attributes\
+ 		to the same type.The name of the last problematic attribute is:%s ",
+ 					 fctx->resultTupleAttNames[i - 1]);
+ 			else
+ 				fctx->tupleSetAttTypes[i]
+ 					= fctx->relsAttTypes[sharedAttRelIDs[minMustCastSharedRelID]][sharedAttAttNum[minMustCastSharedRelID]];
+ 		}
+         
+         /* here we get the equality function for same types for each attribute in the result tuple.*/
+ 		for (i = 0; i < fctx->resultTupleNAtt; i++)
+ 			fctx->tupleSetAttEQFunctions[i + 1]
+ 				= getEQFunction(fctx->tupleSetAttTypes[i + 1]);
+ 	}
+     
+ 	/* Here we create the scheme graph */
+ 	{
+ 		int			k,
+ 					z;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 			fctx->bits_scheme_graph[i] = 0;
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			for (j = i + 1; j < fctx->numRels; j++)
+ 			{
+ 				k = 0;
+ 				z = 0;
+ 				while ((k < fctx->relsNAtt[i]) && (z < fctx->relsNAtt[j]))
+ 				{
+ 					if (sortedRelsAttNumsAtTupleSet[i][k][0] ==
+ 						sortedRelsAttNumsAtTupleSet[j][z][0])
+ 					{
+ 						fctx->bits_scheme_graph[i] = (fctx->bits_scheme_graph[i] | nthBitFromLeft(j));
+ 						fctx->bits_scheme_graph[j] = (fctx->bits_scheme_graph[j] | nthBitFromLeft(i));
+ 						break;
+ 					}
+ 					else if (sortedRelsAttNumsAtTupleSet[i][k][0] <
+ 							 sortedRelsAttNumsAtTupleSet[j][z][0])
+ 						k++;
+ 					else
+ 						z++;
+ 				}
+ 			}
+ 		}
+ 	}
+     
+ 	/* Lets check that the scheme graph is a connected scheme graph.
+      * Because, the FD is only useful on connected scheme graphs.
+      * If it is not, we must exit.*/
+ 	{
+ 		RBITS		relsMask = (~0x0);
+ 
+ 		relsMask >>= fctx->numRels;
+ 		relsMask = ~relsMask;
+ 		RBITS		repository = nthBitFromLeft(0);
+ 
+ 		repository |= fctx->bits_scheme_graph[0];
+ 		RBITS		wereChecked = nthBitFromLeft(0);
+ 		bool		wasAddition = false;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			wasAddition = false;
+ 			for (j = 1; j < fctx->numRels; j++)
+ 			{
+ 				if ((!(varNthBitFromLeft(wereChecked, j))) && (varNthBitFromLeft(repository, j)))
+ 				{
+ 					repository |= fctx->bits_scheme_graph[j];
+ 					varSetNthBitFromLeft(repository, j);
+ 					varSetNthBitFromLeft(wereChecked, j);
+ 					wasAddition = true;
+ 					if (repository == relsMask)
+ 						break;
+ 				}
+ 			}
+ 			if ((!wasAddition) || (repository == relsMask))
+ 				break;
+ 		}
+ 		if (repository != relsMask)
+ 			elog(ERROR, "The relations must be connected to each other! for example A=(attribute1,attribute2), B=(attribute2,attribute3). A and B are connected because they share a common attribute2. Let C=(attribute4,attribute5). C is not connected to either A or B thus, the relations are not connected to each other. Make sure they do before you run the algorithm again.");
+ 	}
+     
+ 	/* lets generate the result tuple, tuple description. */
+ 	{
+ 		TupleDesc	desc = CreateTemplateTupleDesc(fctx->resultTupleNAtt, false);
+ 		TupleDesc	tupleSetDesc = CreateTemplateTupleDesc(NUMATT, false);
+ 
+         /* This is the added labels part of a tuple set. A tuple set is composed of a set
+          * of lables that represent the tuples from different relations in the set. And of
+          * course the attributes of these tuples. Since the attributes are the result of
+          * the join of these tuples, we do not need to repeat columns or actually keep
+          * the tuples in their original form. */
+ 		TupleDescInitEntry(tupleSetDesc, LABELS_ALIGNED, "participatingRelationsOIDs"
+ 						   ,BYTEAOID, -1, 0);
+ 		for (i = 0; i < fctx->resultTupleNAtt; i++)
+ 			if (!is_array_type(fctx->tupleSetAttTypes[i + 1]))
+ 			{
+ 				TupleDescInitEntry(desc, (i + 1), fctx->resultTupleAttNames[i]
+ 								   ,fctx->tupleSetAttTypes[i + 1], -1, 0);
+ 				TupleDescInitEntry(tupleSetDesc, (i + 2), fctx->resultTupleAttNames[i]
+ 								   ,fctx->tupleSetAttTypes[i + 1], -1, 0);
+ 			}
+ 			else
+ 			{
+ 				TupleDescInitEntry(desc, (i + 1), fctx->resultTupleAttNames[i]
+ 								   ,fctx->tupleSetAttTypes[i + 1], -1, 1);
+ 
+ 				/*
+ 				 * if its an array then lets give it 3 dim will occur in any
+ 				 * array. more than we will need to support at later
+ 				 */
+ 				TupleDescInitEntry(tupleSetDesc, (i + 2), fctx->resultTupleAttNames[i]
+ 								   ,fctx->tupleSetAttTypes[i + 1], -1, 1);
+ 			}
+ 		fctx->resultTupleDesc = desc;
+ 		fctx->tupleSetDesc = tupleSetDesc;
+ 		/* HERE WE REGISTER THE OUTPUT SCHEME!!! */
+ 	}
+ }
+ 
+ /* Here we initialize the SPI structures that will allow us to read the relations.
+  * Basically, we open them once here and iterate on them by moving the cursor to the
+  * start of the portal each time without reopening them. The use of cursors is important
+  * because of transactional issues where because the FD is a long operation we must always
+  * work with the same data and thus, we must use the same initial snapshot. Basically opening
+  * a read only cursor should do the trick */
+ void
+ initialize_SPI_structures(PG_FUNCTION_ARGS, alg_fctx * fctx, bool noDuplicatesTest
+ 						  ,FuncCallContext *funcctx)
+ {
+ 	SPI_connect();
+ 	fctx->portals = (Portal *) palloc(fctx->numRels * sizeof(Portal));
+ 	fctx->plans = (void **) palloc(fctx->numRels * sizeof(void *));
+ 	int			i = 0;
+ 
+ 	{
+ 		/* checking if there are duplicates in the relations. Only if the user requested. */
+ 		if (noDuplicatesTest)
+ 		{
+ 			bool		noDuplicates = true;
+ 
+ 			for (i = 0; i < fctx->numRels; i++)
+ 			{
+ 				char		query[255];
+ 				char	   *to = query;
+ 
+ 				to = (char *) stpcpy(to, "SELECT 'a' FROM pg_class WHERE relname='");
+ 				to = (char *) stpcpy(to, get_rel_name(fctx->relsOid[i]));
+ 				to = (char *) stpcpy(to, "' and relhaspkey='t' and relnamespace='");
+ 				char		charNameSpaceOID[20];
+ 
+ 				sprintf(charNameSpaceOID, "%d", get_rel_namespace(fctx->relsOid[i]));
+ 				to = (char *) stpcpy(to, charNameSpaceOID);
+ 				to = (char *) stpcpy(to, "'");
+ 				void	   *plan;
+ 				Portal		portal;
+ 
+ 				if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
+ 					elog(ERROR, "initialize_SPI_structures: SPI_prepare('%s') returns NULL",
+ 						 query);
+ 				if ((portal
+ 					 = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
+ 					elog(ERROR, "initialize_SPI_structures: SPI_cursor_open('%s') returns NULL",
+ 						 query);
+ 				SPI_cursor_fetch(portal, true, 1);
+ 				if (SPI_processed <= 0)
+ 					noDuplicates = false;
+ 				else
+ 					SPI_freetuptable(SPI_tuptable);
+ 				SPI_cursor_close(portal);
+ 				SPI_freeplan(plan);
+ 				if (!noDuplicates)
+ 				{
+ 					to = query;
+ 					to = (char *) stpcpy(to, "SELECT 'a' FROM ");
+ 					to = (char *) stpcpy(to, get_namespace_name(get_rel_namespace(fctx->relsOid[i])));
+ 					to = (char *) stpcpy(to, ".");
+ 					to = (char *) stpcpy(to, get_rel_name(fctx->relsOid[i]));
+ 					to = (char *) stpcpy(to, " HAVING count(*)>1");
+ 					if ((plan = SPI_prepare(query, 0, NULL)) == NULL)
+ 						elog(ERROR, "initialize_SPI_structures: SPI_prepare('%s') returns NULL",
+ 							 query);
+ 					if ((portal
+ 					= SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
+ 						elog(ERROR, "initialize_SPI_structures: SPI_cursor_open('%s') returns NULL",
+ 							 query);
+ 					SPI_cursor_fetch(portal, true, 1);
+ 					if (SPI_processed <= 0)
+ 						noDuplicates = false;
+ 					else
+ 						noDuplicates = true;
+ 					SPI_freetuptable(SPI_tuptable);
+ 					SPI_cursor_close(portal);
+ 					SPI_freeplan(plan);
+ 				}
+ 				if (!noDuplicates)
+ 					break;
+ 			}
+ 			fctx->noDuplicates = noDuplicates;
+ 		}
+ 	}
+ 
+     /* Here we actually setup the query and open the cursors. We also handle
+      * the proper castings and formulate a different query for the event there
+      * are duplicates. */
+ 	{
+ 		fctx->queries = (char **) palloc(fctx->numRels * sizeof(char *));
+ 		int			j,
+ 					k;
+ 		char	   *query;
+ 		char	   *to;
+ 		int			querySize;
+ 		bool		toFreeQuery;
+ 		bool		firstAtt = true;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			querySize = 1024;
+ 			fctx->queries[i] = (char *) palloc(querySize * sizeof(char));
+ 			query = fctx->queries[i];
+ 			to = query;
+ 			int			iterateNoDuplicates = 1;
+ 
+ 			if (fctx->noDuplicates)
+ 				query = addAndExpandStringBy(query, &querySize, "SELECT "
+ 											 ,querySize, &to, &toFreeQuery);
+ 			else
+ 			{
+ 				query = addAndExpandStringBy(query, &querySize, "SELECT DISTINCT ON ("
+ 											 ,querySize, &to, &toFreeQuery);
+ 				iterateNoDuplicates = 2;
+ 			}
+ 			bool		closed = false;
+ 
+ 			for (k = 0; k < iterateNoDuplicates; k++)
+ 			{
+ 				firstAtt = true;
+ 				for (j = 0; j < fctx->relsNAtt[i]; j++)
+ 				{
+ 					if (!firstAtt)
+ 					{
+ 						query = addAndExpandStringBy(query, &querySize, ","
+ 											  ,querySize, &to, &toFreeQuery);
+ 					}
+ 					firstAtt = false;
+ 					query = addAndExpandStringBy(query, &querySize,
+ 												 (char *) get_attname(fctx->relsOid[i], fctx->relsValidAttNumAtRelation[i][j])
+ 											  ,querySize, &to, &toFreeQuery);
+ 					if (fctx->relsAttTypes[i][j] != fctx->tupleSetAttTypes[fctx->relsAttNumsAtTupleSet[i][j]])
+ 					{
+ 						query = addAndExpandStringBy(query, &querySize, "::"
+ 											  ,querySize, &to, &toFreeQuery);
+ 						query = addAndExpandStringBy(query, &querySize,
+ 													 format_type_with_typemod(fctx->tupleSetAttTypes[fctx->relsAttNumsAtTupleSet[i][j]], -1)
+ 											  ,querySize, &to, &toFreeQuery);
+ 					}
+ 				}
+ 				if (!fctx->noDuplicates && !closed)
+ 				{
+ 					query = addAndExpandStringBy(query, &querySize, ") "
+ 											  ,querySize, &to, &toFreeQuery);
+ 					closed = true;
+ 				}
+ 			}
+             /* CTID is used as the label of a tuple (together with the table id) thus,
+              * we must have it */
+ 			query = addAndExpandStringBy(query, &querySize, ",CTID FROM "
+ 										 ,querySize, &to, &toFreeQuery);
+ 			query = addAndExpandStringBy(query, &querySize, get_namespace_name(get_rel_namespace(fctx->relsOid[i]))
+ 										 ,querySize, &to, &toFreeQuery);
+ 			query = addAndExpandStringBy(query, &querySize, "."
+ 										 ,querySize, &to, &toFreeQuery);
+ 			query = addAndExpandStringBy(query, &querySize, get_rel_name(fctx->relsOid[i])
+ 										 ,querySize, &to, &toFreeQuery);
+ 			if ((fctx->plans[i] = SPI_prepare(query, 0, NULL)) == NULL)
+ 				elog(ERROR, "initialize_SPI_structures: SPI_prepare('%s') returns NULL", query);
+ 			if ((fctx->portals[i]
+ 				 = SPI_cursor_open(NULL, fctx->plans[i], NULL, NULL, true)) == NULL)
+ 
+ 				/*
+ 				 * note that read_only in cursor_open must be true otherwise
+ 				 * ctid can change.
+ 				 */
+ 				elog(ERROR, "initialize_SPI_structures: SPI_cursor_open('%s') returns NULL",
+ 					 query);
+ 		}
+ 	}
+ }
diff -crN pgsql/contrib/fulldisjunctions/algstructs.h pgsql-fd/contrib/fulldisjunctions/algstructs.h
*** pgsql/contrib/fulldisjunctions/algstructs.h	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/algstructs.h	2006-07-30 13:30:11.000000000 -0400
***************
*** 0 ****
--- 1,214 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #ifndef ALGSTRUCTS_H
+ #define ALGSTRUCTS_H
+ 
+ #ifdef DEBUG
+ #else
+ #define NDEBUG 
+ #endif
+ 
+ #include <assert.h>
+ 
+ #include "postgres.h"
+ #include "utils/palloc.h"
+ #include "funcapi.h"
+ #include "fmgr.h"
+ #include "catalog/pg_type.h"
+ #include "executor/spi.h"
+ #include "executor/spi_priv.h"
+ #include "utils/lsyscache.h"
+ #include "utils/typcache.h"
+ #include "queues.h"
+ 
+ /* a safety measure if we do not calculate the max size of the result tuple */
+ #define RESULT_TUPLE_ASSUMED_MAX_SIZE_IN_PAGES 1
+ #define RESULT_TUPLE_ASSUMED_MAX_SIZE_IN_BYTES (RESULT_TUPLE_ASSUMED_MAX_SIZE_IN_PAGES*BLCKSZ)
+ 
+ /* Mapping from a relation attribute to an alias */
+ typedef struct
+ {
+ 	int			resultTuplePotnetialAttNamesID;
+ 	char	   *relAttName;
+ }	Mapping;
+ 
+ /* a dictionary of mappings */
+ typedef struct
+ {
+     /* list of unique aliases.*/
+ 	char	  **resultTuplePotentialAttNames;
+ 	int			numPotentialAttNames;
+     /* Holds a list of mappings for each relation. */
+ 	Mapping  ***relsAttsMappings;
+ 	int		   *numMappings;
+ }	Dictionary;
+ 
+ /* a persistant state strucutre between calls to the OnDemand Procedure. */
+ typedef struct
+ {
+ 	/* index in Q */
+ 	int			q;
+ 	/* index in P */
+ 	int			p;
+ 	memQueue   *Q;
+     /* pointer in Q */
+ 	memQueueNode *qPtr;
+     /* current relation tuple we work with */
+ 	DTuple		P;
+ 	TupleDesc	Pdesc;
+ 	int			PrelID;
+     /* we keep the last SPI (P) tuptable so we can free it properly. */
+ 	SPITupleTable *lastTuptable;
+     /* current relation we are scanning */
+ 	int			cur_rel;
+     /* The portals of all the relations. They are the same as in the SPI structures in algstructs
+      * but we need to scan them seperately here with a seperate cursor. */
+ 	Portal	   *portals;
+ }	OnDemandState;
+ 
+ /* This is the structure of the adaptive upper bound on how many tuple sets we are
+  * going to extend at once. */
+ typedef struct
+ {
+ 	double		previousScore;
+ 	int			nextUpperBound;
+ 	int			stage;
+ 	int			maxInputedExtendTuples;
+ 	int			actualInputedExtendTuples;
+ }	adaptive_extend_upperbound;
+ 
+ /* This is the general context of the algorithm. 
+  * Most of the algorithms structures are here. */
+ typedef struct
+ {
+     /* number of arguments in the input */
+ 	int			nargs;
+     /* number of inputted relations */
+ 	int			numRels;
+     /* a portal for each relation */
+ 	Portal	   *portals;
+     /* a query for each relation */
+ 	char	  **queries;
+     /* a plan for each relation */
+ 	void	  **plans;
+ 	/* relations oid's */
+ 	Oid		   *relsOid;
+ 	/* the size in pages of each relation. */
+ 	int		   *relsPages;
+ 	/* true if relation has an index, false otherwise. */
+ 	bool	   *relsHasIndex;
+ 
+ 	/*
+ 	 * the array will contain RELIDS where cell 0 will contain the smallest
+ 	 * paged size relation ID.
+ 	 */
+ 	int		   *sortedRelsByRelsPages;
+     /* the estimated or real maximum tuple size for each relation */
+ 	int		   *relsMaxTupleSize;
+ 	/* number of attributes of each relation. */
+ 	int		   *relsNAtt;
+ 
+ 	/*
+ 	 * for each relation and each of its attributes, specifies the attribute
+ 	 * number at the result tuple.
+ 	 */
+ 	int		  **relsAttNumsAtTupleSet;
+ 	/* same as last but sorted by tuple set att num key. */
+ 	int		 ***sortedRelsAttNumsAtTupleSet;
+     /* does not include dropped columns of relations */
+ 	int		  **relsValidAttNumAtRelation;
+ 	/* for each relation lets save its attributes types; */
+ 	Oid		  **relsAttTypes;
+ 	/* The result tuple attribute names. */
+ 	char	  **resultTupleAttNames;
+ 	/* The result tupl attribute types. */
+ 	Oid		   *tupleSetAttTypes;
+     /* equality functions for same types of the respected tuple set type */
+ 	TypeCacheEntry **tupleSetAttEQFunctions;
+     /* tuple description of the result tuple */
+ 	TupleDesc	resultTupleDesc;
+ 	/* The result tuple number of attributes. */
+ 	int			resultTupleNAtt;
+ 	/* in bytes. how much its HeapTuple occuppies in memory. */
+ 	int			tupleSetMaxSize;
+ 	/* how many result tuples can fit in one page. */
+ 	int			m_NumResultTuplesInOnePage;
+     
+     /* indicate if we use the default value */
+ 	bool		assumingResultTupleMaxSize;
+     /* The tuple set description (used to dumb tuple set to storage) */
+ 	TupleDesc	tupleSetDesc;
+     /* tuple set number of attributes */
+ 	int			tupleSetNatt;
+     /* dictionary of inputted mappings */
+ 	Dictionary *dictionary;
+ 	/* 0 not connected, 1 connected. */
+ 	RBITS	   *bits_scheme_graph;
+     /* tuple store of outputted result tuples */
+ 	storageQueue complete;
+     /* tuple store of not yet outputted result tuples */
+ 	storageQueue precomplete;
+     /* indicates if there is a need to check for duplicates and later after testing
+      * if there are in fact duplicates. */
+ 	bool		noDuplicates;
+     /* The algorithm state in the state machine. */
+ 	int			algorithm_state;
+     /* a queue for reading from virtual incomplete (precomplete with the relations) */
+ 	memQueue	Q;
+     /* a queue for extending */
+ 	memQueue	E;
+ 	/* a pointer to the last sent tset for printing or pipelining. */
+ 	memQueueNode *printedTSet;
+ 
+ 	/*
+ 	 * if you are using queue to print then this signifies that we started
+ 	 * printing when we come back from backend.
+ 	 */
+ 	bool		startedPrintingQueue;
+     /* pointer to the printed tuple */
+ 	HeapTuple	printedTupleAddress;
+     /* indicates if we want to use indices in the relations for extending */
+ 	bool		noIndexScans;
+ 	double		startTime;
+ 	double		endTime;
+     /* the persistent state for the on demand procedure */
+ 	OnDemandState *onDemandState;
+     /* the MB input parameter */
+ 	int			MB;
+     /* the policy used for extending - inputted */
+ 	int			extendingPolicy;
+     /* the last relation we have scanned in extending */
+ 	int			extendingLastRelScanned;
+     /* statistical variable */
+ 	int			extendingNaivePolicyNumCompleteScans;
+     /* used for extending with complex policies */
+ 	memQueueNode *policyWorkNode;
+ 	RBITS		policySequence;
+ 	RBITS		relationsToScan;
+     /* query for extending when using indices */
+ 	char	   *query;
+ 	int			querySize;
+ 	Datum	   *queryParams;
+ 	Oid		   *queryParamsTypes;
+ 	int			queryParamsSize;
+     /* future indicator, not really used currently. */
+ 	bool		trueIndexFalseNonIndexBasedAlgorithm;
+     /* adaptive upper bound for extending structures */
+ 	adaptive_extend_upperbound *dynamic_extend_cap;
+     /* an additional memory context to handle accesses to the tuple store.
+      * Needed to prevent leaking and all kinds of weird issues */
+ 	MemoryContext per_query_ctx;
+ }	alg_fctx;
+ 
+ extern Oid *parseOIDs(text *relsStr, int *numOfRels);
+ extern Oid *parseOIDs_(char *relsStrCp, int *numOfRels);
+ extern void initialize_general_structures(PG_FUNCTION_ARGS, alg_fctx * fctx, FuncCallContext *funcctx);
+ extern void initialize_general_structures_(PG_FUNCTION_ARGS, alg_fctx * fctx, bool parsing, FuncCallContext *funcctx);
+ extern void initialize_SPI_structures(PG_FUNCTION_ARGS, alg_fctx * fctx, bool noDuplicatesTest
+ 						  ,FuncCallContext *funcctx);
+ 
+ #endif   /* ALGSTRUCTS_H */
diff -crN pgsql/contrib/fulldisjunctions/algutils.c pgsql-fd/contrib/fulldisjunctions/algutils.c
*** pgsql/contrib/fulldisjunctions/algutils.c	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/algutils.c	2006-07-30 13:30:12.000000000 -0400
***************
*** 0 ****
--- 1,1731 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #include "postgres.h"
+ #include "algutils.h"
+ #include "catalog/heap.h"
+ #include "tset.h"
+ #include "tsetfuncs.h"
+ #include "queues.h"
+ #include "queuesfuncs.h"
+ #include "utils/datum.h"
+ #include <stdarg.h>
+ 
+ /* Just copying a datum */
+ Datum
+ myDatumCopy(Datum value, bool typByVal, int typLen)
+ {
+ 	Datum		res;
+ 
+ 	if (typByVal)
+ 		res = value;
+ 	else
+ 	{
+ 		Size		realSize;
+ 		char	   *s;
+ 
+ 		if (DatumGetPointer(value) == NULL)
+ 			return PointerGetDatum(NULL);
+ 		realSize = datumGetSize(value, typByVal, typLen);
+ 		s = (char *) palloc(realSize);
+ 		memcpy(s, DatumGetPointer(value), realSize);
+ 		res = PointerGetDatum(s);
+ 	}
+ 	return res;
+ }
+ 
+ /* Freeing a datum */
+ void
+ myDatumFree(Datum value, bool typByVal, int typLen)
+ {
+ 	if (!typByVal)
+ 	{
+ 		Pointer		s = DatumGetPointer(value);
+ 
+ 		pfree(s);
+ 	}
+ }
+ 
+ /* sorting an array of Oids */
+ void
+ sortOidArray(Oid *array, int numElements)
+ {
+ 	int			swapint,
+ 				i,
+ 				j;
+ 
+ 	for (i = 0; i < (numElements - 1); i++)
+ 		for (j = (i + 1); j < numElements; j++)
+ 			if (array[i] > array[j])
+ 			{
+ 				swapint = array[i];
+ 				array[i] = array[j];
+ 				array[j] = swapint;
+ 			}
+ }
+ 
+ /* updating the max tuple size */
+ void
+ updateMaxTupleSize(alg_fctx * fctx, int relID, HeapTuple tuple)
+ {
+ 	int			size = tupleSize(tuple);
+ 
+ 	if (fctx->relsMaxTupleSize[relID] < size)
+ 		fctx->relsMaxTupleSize[relID] = size;
+ }
+ 
+ /* updating the max result tuple size*/
+ void
+ updateResultTupleMaxSize(alg_fctx * fctx)
+ {
+ 	int			tmpSize = 0;
+ 	int			i = 0;
+ 
+ 	for (i = 0; i < fctx->numRels; i++)
+ 		tmpSize += fctx->relsMaxTupleSize[i];
+ 	fctx->tupleSetMaxSize = tmpSize;
+ 	double		blcksz = BLCKSZ;
+ 
+ 	if (tmpSize > 0)
+ 		fctx->m_NumResultTuplesInOnePage = (int) (blcksz / (double) tmpSize);
+ 	else
+ 		fctx->m_NumResultTuplesInOnePage = 1;
+ 	if (fctx->m_NumResultTuplesInOnePage < 1)
+ 		fctx->m_NumResultTuplesInOnePage = 1;
+ }
+ 
+ /* getting an equality function for same type */
+ TypeCacheEntry *
+ getEQFunction(Oid typeid)
+ {
+ 	TypeCacheEntry *typentry;
+ 
+ 	/*
+ 	 * Find the data type in the typcache, and ask for eq_opr info.
+ 	 */
+ 	typentry = lookup_type_cache(typeid, TYPECACHE_EQ_OPR_FINFO);
+ 	if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ 		ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 				errmsg("could not identify an equality operator for type %s",
+ 					   format_type_be(typeid))));
+ 	return typentry;
+ }
+ 
+ /* auto expanding a datum array in memory */
+ Datum *
+ getAndExpandDatumArray(Datum *oldDatumArr, int oldDatumArraySize, int newDatumArraySize)
+ {
+ 	if (oldDatumArr == NULL)
+ 		return (Datum *) palloc(newDatumArraySize * sizeof(Datum));
+ 	else
+ 	{
+ 		if (oldDatumArraySize < newDatumArraySize)
+ 		{
+ 			Datum	   *newArr = (Datum *) palloc(newDatumArraySize * sizeof(Datum));
+ 
+ 			memcpy(newArr, oldDatumArr, oldDatumArraySize * sizeof(Datum));
+ 			return newArr;
+ 		}
+ 	}
+ 	return oldDatumArr;
+ }
+ 
+ /* auto expanding an Oid array in memory */
+ Oid *
+ getAndExpandOidArray(Oid *oldOidArr, int oldOidArraySize, int newOidArraySize)
+ {
+ 	if (oldOidArr == NULL)
+ 		return (Oid *) palloc(newOidArraySize * sizeof(Oid));
+ 	else
+ 	{
+ 		if (oldOidArraySize < newOidArraySize)
+ 		{
+ 			Oid		   *newArr = (Oid *) palloc(newOidArraySize * sizeof(Oid));
+ 
+ 			memcpy(newArr, oldOidArr, oldOidArraySize * sizeof(Oid));
+ 			return newArr;
+ 		}
+ 	}
+ 	return oldOidArr;
+ }
+ 
+ /* auto expanding a string array in memory */
+ char *
+ addAndExpandStringBy(char *oldStr, int *oldStrAllocSize, char *addStr, int sizeToAdd
+ 					 ,char **relative_ptr, bool *free_old_str)
+ {
+ 	int			oldStrSize = (int) *relative_ptr - (int) oldStr;
+ 	int			addStrSize = strlen(addStr);
+ 
+ 	/* +1 for null at the end */
+ 	if (*oldStrAllocSize >= (oldStrSize + addStrSize + 1))
+ 	{
+ 		*relative_ptr = (char *) stpcpy(*relative_ptr, addStr);
+ 		return oldStr;
+ 	}
+ 	*oldStrAllocSize = *oldStrAllocSize + addStrSize + sizeToAdd * sizeof(char);
+ 	char	   *newStr = (char *) palloc(*oldStrAllocSize);
+ 	char	   *to = newStr;
+ 
+ 	to = (char *) stpcpy(to, oldStr);
+ 	to = (char *) stpcpy(to, addStr);
+ 	if (relative_ptr != NULL)
+ 		*relative_ptr = to;
+ 	else
+ 		elog(ERROR, "Error in addAndExpandStringBy");
+ 	if (*free_old_str)
+ 		pfree(oldStr);
+ 	*free_old_str = true;
+ 	return newStr;
+ }
+ 
+ /* checks if an attribute in the result tuple also appear in the relation specified
+  * in the relID */
+ bool
+ isResultTupleAttOccursInRelID(alg_fctx * fctx, int resultTupleAttID, int relID)
+ {
+ 	int			i = 0;
+ 
+ 	for (i = 0; i < fctx->relsNAtt[relID]; i++)
+ 	{
+ 		if (fctx->relsAttNumsAtTupleSet[relID][i] == resultTupleAttID)
+ 			return true;
+ 	}
+ 	return false;
+ }
+ 
+ /*
+  *	in some RelID of a tuple set of course. Where the tuple set is represented
+  *	by the relsID.
+  */
+ bool
+ isResultTupleAttOccursInSomeRelID(alg_fctx * fctx, int resultTupleAttID, bytea *relsID)
+ {
+ 	int			numIDs = numOfTuplesInTSet(fctx, relsID);
+ 	int			j = 0;
+ 
+ 	for (j = 0; j < numIDs; j++)
+ 		if (isResultTupleAttOccursInRelID(fctx, resultTupleAttID
+ 								,getTableIdOfRelsOidInTSet(fctx, relsID, j)))
+ 			return true;
+ 	return false;
+ }
+ 
+ /* Same as isResultTupleAttOccursInSomeRelID but using a BITS array instead of a bytea set
+  * of lables. */
+ bool
+ isResultTupleAttOccursInSomeRelIDWithSpecifiedRelations(alg_fctx * fctx, int resultTupleAttID,
+ 													  RBITS relationsToCheck)
+ {
+ 	int			i = 0,
+ 				j = 0;
+ 	RBITS		localRelationsToCheck = relationsToCheck;
+ 
+ 	while (localRelationsToCheck)
+ 	{
+ 		if (varNthBitFromLeft(localRelationsToCheck, 0))
+ 			for (j = 0; j < fctx->relsNAtt[i]; j++)
+ 			{
+ 				if (fctx->relsAttNumsAtTupleSet[i][j] == resultTupleAttID)
+ 					return true;
+ 			}
+ 		i++;
+ 		localRelationsToCheck <<= 1;
+ 	}
+ 	return false;
+ }
+ 
+ /*
+  * A policy to choose the next relation to extend to.
+  * majority is to extend to the relation most tuple set currently needs
+  * to extend to.
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_majority_Q_one_tuple_set_at_a_time(alg_fctx * fctx, memQueue * Q, int tsetsNum)
+ {
+ 	int			j = 0,
+ 				maxSum = 0,
+ 				i = 0;
+ 	RBITS		tmp = 0,
+ 				tmpRels = 0;
+ 	int			relsSum[fctx->numRels];
+ 
+ 	for (j = 0; j < fctx->numRels; j++)
+ 		relsSum[j] = 0;
+ 	memQueueNode *ptr = NULL;
+ 
+ 	if (fctx->policyWorkNode)
+ 		ptr = fctx->policyWorkNode;
+ 	else
+ 	{
+ 		ptr = getMemQueueFirstElementPtr(Q);
+ 		if (!(ptr))
+ 			return -1;
+ 	}
+ 	tmp = ptr->toVisit;
+ 	if (!(tmp))
+ 		elog(ERROR, "Unexpecteed problem1");
+ 	fctx->policyWorkNode = ptr;
+ 	ptr = getMemQueueFirstElementPtr(Q);
+ 	while (ptr != NULL)
+ 	{
+ 		tmpRels = ((ptr->toVisit) & tmp);
+ 		j = 0;
+ 		while (tmpRels)
+ 		{
+ 			varNthBitFromLeft(tmpRels, 0) && relsSum[j]++;
+ 			tmpRels <<= 1;
+ 			j++;
+ 		}
+ 		ptr = getMemQueueNextElementPtr(ptr);
+ 	}
+ 	for (j = 0; j < fctx->numRels; j++)
+ 		if (relsSum[j] > maxSum)
+ 		{
+ 			i = j;
+ 			maxSum = relsSum[j];
+ 		}
+ 	return i;
+ }
+ 
+ /*
+  * same as with policy_majority_Q_one_tuple_set_at_a_time but with sequencing.
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_majority_Q_one_tuple_set_at_a_time_with_sequencing(alg_fctx * fctx, memQueue * Q,
+ 														  int tsetsNum)
+ {
+ 	int			j = 0,
+ 				maxSum = 0,
+ 				i = 0;
+ 	int			relsSum[fctx->numRels];
+ 
+ 	for (j = 0; j < fctx->numRels; j++)
+ 		relsSum[j] = 0;
+ 	RBITS		tmp = 0;
+ 	RBITS		tmpRels = 0;
+ 	RBITS		relsMask = (~0x0);
+ 
+ 	relsMask >>= fctx->numRels;
+ 	relsMask = ~relsMask;
+ 	bool		wasAddToSum = false;
+ 	memQueueNode *ptr = NULL;
+ 
+ 	if (fctx->policyWorkNode)
+ 	{
+ 		ptr = fctx->policyWorkNode;
+ 		tmp = ptr->toVisit;
+ 	}
+ 	else
+ 	{
+ 		ptr = getMemQueueFirstElementPtr(Q);
+ 		if (!(ptr))
+ 			return -1;
+ 		if (fctx->policySequence)
+ 		{
+ 			tmp = fctx->policySequence;
+ 		}
+ 		else
+ 		{
+ 			fctx->policySequence = relsMask;
+ 			tmp = ptr->toVisit;
+ 			if (!(tmp))
+ 				elog(ERROR, "Unexpecteed problem21");
+ 		}
+ 	}
+ 	if (!(tmp))
+ 		elog(ERROR, "Unexpecteed problem2");
+ 	fctx->policyWorkNode = ptr;
+ 	ptr = getMemQueueFirstElementPtr(Q);
+ 	while (ptr != NULL)
+ 	{
+ 		tmpRels = ((ptr->toVisit) & tmp);
+ 		j = 0;
+ 		while (tmpRels)
+ 		{
+ 			varNthBitFromLeft(tmpRels, 0) && relsSum[j]++;
+ 			varNthBitFromLeft(tmpRels, 0) && (wasAddToSum = true);
+ 			tmpRels <<= 1;
+ 			j++;
+ 		}
+ 		ptr = getMemQueueNextElementPtr(ptr);
+ 	}
+ 	if (wasAddToSum)
+ 	{
+ 		for (j = 0; j < fctx->numRels; j++)
+ 			if (relsSum[j] > maxSum)
+ 			{
+ 				i = j;
+ 				maxSum = relsSum[j];
+ 			}
+ 		varUnSetNthBitFromLeft(fctx->policySequence, i);
+ 		return i;
+ 	}
+ 	else
+ 	{
+ 		fctx->policySequence = 0;
+ 		fctx->policyWorkNode = NULL;
+ 		return policy_majority_Q_one_tuple_set_at_a_time_with_sequencing(fctx, Q, tsetsNum);
+ 	}
+ }
+ 
+ /*
+  * mincost policy choose the relations the tuple sets needs to extend to but that has
+  * the least pages in it.
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_mincost_Q_one_tuple_set_at_a_time(alg_fctx * fctx, memQueue * Q, int tsetsNum)
+ {
+ 	int			j = 0;
+ 	RBITS		tmp = 0;
+ 	memQueueNode *ptr = NULL;
+ 
+ 	if (fctx->policyWorkNode)
+ 		ptr = fctx->policyWorkNode;
+ 	else
+ 	{
+ 		ptr = getMemQueueFirstElementPtr(Q);
+ 		if (!(ptr))
+ 			return -1;
+ 	}
+ 	tmp = ptr->toVisit;
+ 	if (!(tmp))
+ 		elog(ERROR, "Unexpecteed problem3");
+ 	fctx->policyWorkNode = ptr;
+ 	for (j = 0; j < fctx->numRels; j++)
+ 		if (varNthBitFromLeft(tmp, fctx->sortedRelsByRelsPages[j]))
+ 			return fctx->sortedRelsByRelsPages[j];
+ 	return -1;
+ }
+ 
+ /*
+  * same as one at a time, but with sequencing.
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_mincost_Q_one_tuple_set_at_a_time_with_sequencing(alg_fctx * fctx, memQueue * Q,
+ 														 int tsetsNum)
+ {
+ 	int			j = 0,
+ 				relID = 0;
+ 	RBITS		tmp = 0;
+ 	RBITS		tmpRels = 0;
+ 	RBITS		relsMask = (~0x0);
+ 
+ 	relsMask >>= fctx->numRels;
+ 	relsMask = ~relsMask;
+ 	memQueueNode *ptr = NULL;
+ 	RBITS		sequence = fctx->policySequence;
+ 
+ 	if (fctx->policyWorkNode)
+ 	{
+ 		ptr = fctx->policyWorkNode;
+ 		tmp = ptr->toVisit;
+ 	}
+ 	else
+ 	{
+ 		ptr = getMemQueueFirstElementPtr(Q);
+ 		if (!(ptr))
+ 			return -1;
+ 		if (sequence)
+ 		{
+ 			for (j = 0; j < fctx->numRels; j++)
+ 			{
+ 				relID = fctx->sortedRelsByRelsPages[j];
+ 				if (varNthBitFromLeft(sequence, relID))
+ 				{
+ 					tmp = nthBitFromLeft(relID);
+ 					break;
+ 				}
+ 			}
+ 			while (ptr != NULL)
+ 			{
+ 				tmpRels |= (ptr->toVisit);
+ 				if (tmpRels & tmp)
+ 				{
+ 					varUnSetNthBitFromLeft(fctx->policySequence, relID);
+ 					return relID;
+ 				}
+ 				if ((tmpRels & sequence) == sequence)
+ 					break;
+ 				ptr = getMemQueueNextElementPtr(ptr);
+ 			}
+ 			ptr = NULL;
+ 			tmp = fctx->policySequence & tmpRels;
+ 			if (!(tmp))
+ 			{
+ 				ptr = getMemQueueFirstElementPtr(Q);
+ 				fctx->policySequence = relsMask;
+ 				tmp = ptr->toVisit;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			fctx->policySequence = relsMask;
+ 			tmp = ptr->toVisit;
+ 		}
+ 	}
+ 	if (!(tmp))
+ 		elog(ERROR, "Unexpecteed problem4");
+ 	fctx->policyWorkNode = ptr;
+ 	for (j = 0; j < fctx->numRels; j++)
+ 		if (varNthBitFromLeft(tmp, fctx->sortedRelsByRelsPages[j]))
+ 		{
+ 			varUnSetNthBitFromLeft(fctx->policySequence, fctx->sortedRelsByRelsPages[j]);
+ 			return fctx->sortedRelsByRelsPages[j];
+ 		}
+ 	return -1;
+ }
+ 
+ /*
+  * random relation choice but only from those the tuple sets needs to extend to.
+  * (the tuple sets in Q)
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_random_Q(alg_fctx * fctx, memQueue * Q, int tsetsNum)
+ {
+ 	int			i = 0;
+ 	int			j = 0;
+ 	int			numRelsToExtend = 0;
+ 	RBITS		tmpRels = 0;
+ 	RBITS		tmp;
+ 	RBITS		relsMask = (~0x0);
+ 
+ 	relsMask >>= fctx->numRels;
+ 	relsMask = ~relsMask;
+ 	memQueueNode *ptr = getMemQueueFirstElementPtr(Q);
+ 
+ 	while (ptr != NULL)
+ 	{
+ 		tmpRels |= (ptr->toVisit);
+ 		if (tmpRels == relsMask)
+ 			break;
+ 		ptr = getMemQueueNextElementPtr(ptr);
+ 	}
+ 	tmp = tmpRels;
+ 	while (tmp)
+ 	{
+ 		if (varNthBitFromLeft(tmp, 0))
+ 			numRelsToExtend++;
+ 		tmp <<= 1;
+ 	}
+ 	if (numRelsToExtend > 0)
+ 	{
+ 		int			k = 0;
+ 
+ 		i = ((int) (numRelsToExtend * (rand() / (RAND_MAX + 1.0)))) + 1;
+ 		if (i > numRelsToExtend)
+ 			elog(ERROR, "policy_random_Q (i>numRelsToExtend)");
+ 		j = 0;
+ 		while ((j < i) && (j < numRelsToExtend))
+ 		{
+ 			if (varNthBitFromLeft(tmpRels, k))
+ 			{
+ 				j++;
+ 			}
+ 			k++;
+ 		}
+ 		k--;
+ 		return k;
+ 	}
+ 	return -1;
+ }
+ 
+ /*
+  * naive policy. mostly useless.
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_naive_Q(alg_fctx * fctx, memQueue * Q, int tsetsNum)
+ {
+ 	if (fctx->extendingLastRelScanned < 0)
+ 		return 0;
+ 	if ((fctx->extendingLastRelScanned + 1) < fctx->numRels)
+ 		return (fctx->extendingLastRelScanned + 1);
+ 	fctx->extendingNaivePolicyNumCompleteScans++;
+ 	if (fctx->extendingNaivePolicyNumCompleteScans == fctx->numRels)
+ 		return -1;
+ 	return 0;
+ }
+ 
+ /* naive policy, chooses the relation in a deterministic manner with no regard to the
+  * tuple sets.*/
+ int
+ policy_naive(alg_fctx * fctx, int **checkTable, int tsetsNum)
+ {
+ 	if (fctx->extendingLastRelScanned < 0)
+ 		return 0;
+ 	if ((fctx->extendingLastRelScanned + 1) < fctx->numRels)
+ 		return (fctx->extendingLastRelScanned + 1);
+ 	fctx->extendingNaivePolicyNumCompleteScans++;
+ 	if (fctx->extendingNaivePolicyNumCompleteScans == fctx->numRels)
+ 		return -1;
+ 	return 0;
+ }
+ 
+ /* checks using a tree search the plantree if it contains a sequence scan. and return
+  * true if it does.*/
+ bool
+ isPlanUsingSeqScan(Plan *planTree)
+ {
+ 	if (nodeTag(planTree) == T_SeqScan)
+ 		return true;
+ 	if (nodeTag(planTree) == T_Append)
+ 	{
+ 		Append	   *appendplan = (Append *) planTree;
+ 		ListCell   *lst;
+ 
+ 		foreach(lst, appendplan->appendplans)
+ 		{
+ 			Plan	   *subnode = (Plan *) lfirst(lst);
+ 
+ 			return (isPlanUsingSeqScan(subnode));
+ 		}
+ 	}
+ 	if (nodeTag(planTree) == T_SubqueryScan)
+ 	{
+ 		SubqueryScan *subqueryscan = (SubqueryScan *) planTree;
+ 		Plan	   *subnode = subqueryscan->subplan;
+ 
+ 		return (isPlanUsingSeqScan(subnode));
+ 	}
+ 	if (innerPlan(planTree))
+ 		if (isPlanUsingSeqScan(innerPlan(planTree)) == true)
+ 			return true;
+ 	if (outerPlan(planTree))
+ 		if (isPlanUsingSeqScan(outerPlan(planTree)) == true)
+ 			return true;
+ 	return false;
+ }
+ 
+ /*simply recombines queues using their head and tail pointers.*/
+ void
+ recombineQueues(memQueue * destQ, int numQueues,...)
+ {
+ 	memQueue   *sourceQ = NULL;
+ 	memQueue	destQtmp;
+ 
+ 	_initializeMemQueue(&destQtmp, destQ->allocatedBytes);
+ 	int			i;
+ 	va_list		ap;
+ 
+ 	va_start(ap, numQueues);
+ 	for (i = 0; i < numQueues; i++)
+ 	{
+ 		sourceQ = va_arg(ap, memQueue *);
+ 		if (sourceQ->head != NULL)
+ 		{
+ 			if (destQtmp.head == NULL)
+ 				destQtmp.head = sourceQ->head;
+ 			else
+ 				destQtmp.tail->next = (struct memQueueNode *) sourceQ->head;
+ 			destQtmp.numElements += sourceQ->numElements;
+ 			destQtmp.sizeInBytes += sourceQ->sizeInBytes;
+ 			destQtmp.tail = sourceQ->tail;
+ 		}
+ 	}
+ 	_initializeMemQueue(destQ, destQ->allocatedBytes);
+ 	destQ->head = destQtmp.head;
+ 	destQ->tail = destQtmp.tail;
+ 	destQ->numElements = destQtmp.numElements;
+ 	destQ->sizeInBytes = destQtmp.sizeInBytes;
+ 	va_end(ap);
+ }
+ 
+ /*
+  * a random policy to choose a relation. However, only those that the tuple sets in Q needs
+  * to extend to.
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_random_Q_one_tuple_set_at_a_time(alg_fctx * fctx, memQueue * Q, int tsetsNum)
+ {
+ 	int			i = 0;
+ 	int			j = 0;
+ 	int			numRelsToExtend = 0;
+ 	RBITS		tmp;
+ 	RBITS		relsMask = (~0x0);
+ 
+ 	relsMask >>= fctx->numRels;
+ 	relsMask = ~relsMask;
+ 	memQueueNode *ptr = NULL;
+ 
+ 	if (fctx->policyWorkNode)
+ 		ptr = fctx->policyWorkNode;
+ 	else
+ 		ptr = getMemQueueFirstElementPtr(Q);
+ 	if (!(ptr))
+ 	{
+ 		fctx->policyWorkNode = NULL;
+ 		return -1;
+ 	}
+ 	tmp = ptr->toVisit;
+ 	if (!(tmp))
+ 		elog(ERROR, "Unexpecteed problem5");
+ 	fctx->policyWorkNode = ptr;
+ 	while (tmp)
+ 	{
+ 		if (varNthBitFromLeft(tmp, 0))
+ 			numRelsToExtend++;
+ 		tmp <<= 1;
+ 	}
+ 	tmp = ptr->toVisit;
+ 	if (numRelsToExtend > 0)
+ 	{
+ 		int			k = 0;
+ 
+ 		i = ((int) (numRelsToExtend * (rand() / (RAND_MAX + 1.0)))) + 1;
+ 		if (i > numRelsToExtend)
+ 			elog(ERROR, "policy_random_Q_one_tuple_set_at_a_time (i>numRelsToExtend)");
+ 		j = 0;
+ 		while ((j < i) && (j < numRelsToExtend))
+ 		{
+ 			if (varNthBitFromLeft(tmp, k))
+ 			{
+ 				j++;
+ 			}
+ 			k++;
+ 		}
+ 		k--;
+ 		return k;
+ 	}
+ 	return -1;
+ }
+ 
+ /*
+  * same as one at a time but with sequencing support.
+  * -1 means, no relation was chosen
+  */
+ int
+ policy_random_Q_one_tuple_set_at_a_time_with_sequencing(alg_fctx * fctx, memQueue * Q,
+ 														int tsetsNum)
+ {
+ 	int			i = 0;
+ 	int			j = 0;
+ 	int			numRelsToExtend = 0;
+ 	RBITS		tmp = 0,
+ 				tmp2 = 0;
+ 	RBITS		tmpRels = 0;
+ 	RBITS		relsMask = (~0x0);
+ 
+ 	relsMask >>= fctx->numRels;
+ 	relsMask = ~relsMask;
+ 	memQueueNode *ptr = NULL;
+ 
+ 	if (fctx->policyWorkNode)
+ 	{
+ 		ptr = fctx->policyWorkNode;
+ 		tmp = ptr->toVisit;
+ 	}
+ 	else
+ 	{
+ 		ptr = getMemQueueFirstElementPtr(Q);
+ 		if (!(ptr))
+ 			return -1;
+ 		if (fctx->policySequence)
+ 		{
+ 			while (ptr != NULL)
+ 			{
+ 				tmpRels |= (ptr->toVisit);
+ 				if (tmpRels == relsMask)
+ 					break;
+ 				ptr = getMemQueueNextElementPtr(ptr);
+ 			}
+ 			ptr = NULL;
+ 			tmp = fctx->policySequence & tmpRels;
+ 			if (!(tmp))
+ 			{
+ 				ptr = getMemQueueFirstElementPtr(Q);
+ 				fctx->policySequence = relsMask;
+ 				tmp = ptr->toVisit;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			fctx->policySequence = relsMask;
+ 			tmp = ptr->toVisit;
+ 		}
+ 	}
+ 	Assert(tmp);
+ 	if (!(tmp))
+ 		elog(ERROR, "Unexpecteed problem6");
+ 	fctx->policyWorkNode = ptr;
+ 	tmp2 = tmp;
+ 	while (tmp)
+ 	{
+ 		if (varNthBitFromLeft(tmp, 0))
+ 			numRelsToExtend++;
+ 		tmp <<= 1;
+ 	}
+ 	if (numRelsToExtend > 0)
+ 	{
+ 		int			k = 0;
+ 
+ 		i = ((int) (numRelsToExtend * (rand() / (RAND_MAX + 1.0)))) + 1;
+ 		if (i > numRelsToExtend)
+ 			elog(ERROR, "policy_random_Q_one_tuple_set_at_a_time_with_sequencing (i>numRelsToExtend)");
+ 		j = 0;
+ 		while ((j < i) && (j < numRelsToExtend))
+ 		{
+ 			if (varNthBitFromLeft(tmp2, k))
+ 			{
+ 				j++;
+ 			}
+ 			k++;
+ 		}
+ 		k--;
+ 		varUnSetNthBitFromLeft(fctx->policySequence, k);
+ 		return k;
+ 	}
+ 	return -1;
+ }
+ 
+ /* helper macro for the policies functions */
+ #define update_policy_status(fctx, policy, updateCode, nodePtr)\
+ { if (fctx->policyWorkNode==nodePtr)\
+ 		fctx->policyWorkNode=NULL;}
+ 
+ /* Simply get a string from a datum directly.
+  * must be non null value. check in advance. also check num of attr=>fnumber*/
+ char *
+ getCStrvalueFromDatum(Datum origval, TupleDesc tupdesc, int fnumber)
+ {
+ 	Datum		val,
+ 				result;
+ 	Oid			typoid,
+ 				foutoid;
+ 	bool		typisvarlena;
+ 
+ 	if (fnumber > 0)
+ 		typoid = tupdesc->attrs[fnumber - 1]->atttypid;
+ 	else
+ 		typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
+ 	getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
+ 
+ 	/*
+ 	 * If we have a toasted datum, forcibly detoast it here to avoid memory
+ 	 * leakage inside the type's output routine.
+ 	 */
+ 	if (typisvarlena)
+ 		val = PointerGetDatum(PG_DETOAST_DATUM(origval));
+ 	else
+ 		val = origval;
+ 	result = OidFunctionCall1(foutoid,
+ 							  val);
+ 	/* Clean up detoasted copy, if any */
+ 	if (val != origval)
+ 		pfree(DatumGetPointer(val));
+ 	return DatumGetCString(result);
+ }
+ 
+ /* algorithm's extend procedure.
+  * The extending operation is to take tuple sets and add to them tuples from the inputted
+  * relations that the resulting set is join consistend and connected (JCC).
+  * Finally stop when all the tuple sets are maximal in that respect.*/
+ void
+ Extend(alg_fctx * fctx, memQueue * Q)
+ {
+ 	int			QNumElements = getMemQueueNumElements(Q);
+ 
+ 	if (!(QNumElements))
+ 		return;
+     
+     /* an estimate for the average size of a tuple set in memory. */
+ 	int			avgTSetSize = (int) (getMemQueueSize(Q) / (QNumElements + 1)) + QNumElements;
+ 	int			i = 0;
+     
+     /* The first element in Q */
+ 	memQueueNode *nodePtr = getMemQueueFirstElementPtr(Q);
+     
+     /* a pointer to a queue which we need to set visited a relation to each of its tuple sets */
+     memQueue   *setVisitedQ = NULL;
+     
+ 	TSet		retTSet = NULL;
+ 	DTuple		tuple = newDTuple(fctx, fctx->resultTupleNAtt);
+ 	
+     /* a structure that deforms a tuple one attribute at a time until it is deformed completly.
+      * However, we can abort in the middle */
+     deformTupleIterativeState *dsTuple
+ 	= (deformTupleIterativeState *) palloc(sizeof(deformTupleIterativeState));
+ 	bool		endScan = false;
+ 	bool		firstPass = true;
+ 	bool		isNull = false;
+ 	memQueueNode *prevNodePtr = NULL;
+ 	TSet		tset = NULL;
+ 
+ 	i = 0;
+ 	bool		scanNotEmpty = false;
+ 	int			relToCheck = -1;
+ 	char	   *query = fctx->query;
+ 	int			querySize = fctx->querySize;
+ 	bool		toFreeQuery = true;
+ 	POLICY_FUNC policy = NULL;
+ 
+     /* Lets choose the inputted policy function to use, 
+      * these functions all have the same interface */
+ 	switch (fctx->extendingPolicy)
+ 	{
+ 		case POLICY_MAJORITY:
+ 			if (fctx->trueIndexFalseNonIndexBasedAlgorithm)
+ 				policy = &policy_majority_Q_one_tuple_set_at_a_time_with_sequencing;
+ 			else
+ 				policy = &policy_majority_Q_one_tuple_set_at_a_time;
+ 			break;
+ 		case POLICY_MINCOST:
+ 			if (fctx->trueIndexFalseNonIndexBasedAlgorithm)
+ 				policy = &policy_mincost_Q_one_tuple_set_at_a_time_with_sequencing;
+ 			else
+ 				policy = &policy_mincost_Q_one_tuple_set_at_a_time;
+ 			break;
+ 		case POLICY_RANDOM:
+ 			if (fctx->trueIndexFalseNonIndexBasedAlgorithm)
+ 				policy = &policy_random_Q_one_tuple_set_at_a_time_with_sequencing;
+ 			else
+ 				policy = &policy_random_Q_one_tuple_set_at_a_time;
+ 			break;
+ 		case POLICY_NAIVE:
+ 			policy = &policy_naive_Q;
+ 			fctx->extendingNaivePolicyNumCompleteScans = 0;
+ 			break;
+ 		default:
+ 			elog(ERROR, "POLICY FUNCTION ASSIGNENT ERRORED!");
+ 	}
+ 
+     /* the queues for the extending process */
+ 	memQueue	needsChanging;
+ 	memQueue	notInvolved;
+ 	memQueue	maximalyExtended;
+ 
+ 	_initializeMemQueue(&needsChanging, INFINITY);
+ 	_initializeMemQueue(&notInvolved, INFINITY);
+ 	_initializeMemQueue(&maximalyExtended, INFINITY);
+     
+ 	/* Here we work under the assumption that Q is ALREADY MERGED!!!!!!!!!!1 */
+ 	nodePtr = getMemQueueFirstElementPtr(Q);
+     /* here we find all the already maximally extended tuple sets and move them to the result */
+ 	prevNodePtr = nodePtr;	
+     while (nodePtr != NULL)
+ 	{
+ 		if (!(nodePtr->toVisit))
+ 		{
+ 			detachNodeFromMemQueueNode(prevNodePtr, nodePtr, Q);
+ 			update_policy_status(fctx, policy, POLICY_CODE_MAXIMALLY_EXTENDED_6, nodePtr);
+ 			addNodeToMemQueue(nodePtr, &maximalyExtended);
+ 			nodePtr = prevNodePtr;
+ 		}
+ 		getMemQueueNextElementAfterRemovalPtr(prevNodePtr, nodePtr, Q);
+ 	}
+ 	QNumElements = getMemQueueNumElements(Q);
+ 	fctx->extendingLastRelScanned = -1;
+     
+     /* this is the main iteration loop where we choose  the next relation to extend to and
+      * indeed extend to it all the tuple sets that needs to extend to it */
+ 	while ((relToCheck = policy(fctx, Q, QNumElements)) > -1)
+ 	{
+ 		_initializeMemQueue(&needsChanging, INFINITY);
+ 		_initializeMemQueue(&notInvolved, INFINITY);
+         
+         /* The last relation we checked. */
+ 		fctx->extendingLastRelScanned = relToCheck;
+ 		bool		attemptIndexScan = true;
+ 
+         /* before attempting to scan a relation using indices, we check it it has indices at all */
+ 		if (fctx->noIndexScans || (!fctx->relsHasIndex[relToCheck]))
+ 			attemptIndexScan = false;
+         
+ 		bool		firstTSet = true;
+ 		bool		firstAtt = true;
+ 		int			paramNum = -1;
+ 
+ 		nodePtr = getMemQueueFirstElementPtr(Q);
+         /* Here we check if we SHOULD scan using an index and formulate a proper
+          * query to retrieve the needed tuples */
+ 		if (attemptIndexScan)
+ 		{
+ 			int			numTSetsInQuery = 0;
+ 
+ 			while (nodePtr)
+ 			{
+ 				tset = getMemQueueElement(nodePtr);
+ 				if (varNthBitFromLeft(nodePtr->toVisit, relToCheck))
+ 				{
+ 					numTSetsInQuery++;
+ 				}
+ 				nodePtr = getMemQueueNextElementPtr(nodePtr);
+ 			}
+             
+ 			if (!(numTSetsInQuery > 0))
+ 				attemptIndexScan = false;
+ 			else
+ 			{
+ 				if (fctx->relsPages[relToCheck] >
+ 					(INDEX_AND_PLAN_BASE_FACTOR
+ 					 + ((INDEX_AND_PLAN_PER_TUPLE_FACTOR_CPU_COST + INDEX_AND_PLAN_PER_TUPLE_FACTOR) * numTSetsInQuery)))
+ 					attemptIndexScan = true;
+ 				else
+ 					attemptIndexScan = false;
+ 			}
+             
+ 			if (query == NULL && attemptIndexScan)
+ 			{
+ 				if (fctx->querySize < 1)
+ 				{
+ 					int			resultAttNamesSize = 0;
+ 
+ 					for (i = 0; i < fctx->resultTupleNAtt; i++)
+ 						resultAttNamesSize += strlen(fctx->resultTupleAttNames[i]);
+ 					querySize = 256 + (avgTSetSize * numTSetsInQuery)
+ 						+ (10 + resultAttNamesSize) * fctx->relsNAtt[relToCheck];
+ 					fctx->querySize = querySize;
+ 				}
+ 				query = (char *) palloc(querySize);
+ 			}
+             
+             /* so we confirmed we think we should use an index. Here we formulate a proper query*/
+ 			if (attemptIndexScan)
+ 			{
+ 				fctx->queryParams = getAndExpandDatumArray(fctx->queryParams
+ 														   ,fctx->queryParamsSize, numTSetsInQuery * fctx->resultTupleNAtt);
+ 				fctx->queryParamsTypes = getAndExpandOidArray(fctx->queryParamsTypes
+ 															  ,fctx->queryParamsSize, numTSetsInQuery * fctx->resultTupleNAtt);
+ 				if (fctx->queryParamsSize < numTSetsInQuery * fctx->resultTupleNAtt)
+ 					fctx->queryParamsSize = numTSetsInQuery * fctx->resultTupleNAtt;
+ 				Datum	   *params = fctx->queryParams;
+ 				Oid		   *types = fctx->queryParamsTypes;
+ 				char		paramStr[12];
+ 
+ 				paramNum = 0;
+ 				char	   *to = (char *) query;
+ 
+ 				nodePtr = getMemQueueFirstElementPtr(Q);
+ 				while (nodePtr)
+ 				{
+ 					tset = getMemQueueElement(nodePtr);
+ 					if (varNthBitFromLeft(nodePtr->toVisit, relToCheck))
+ 					{
+ 						if (!firstTSet)
+ 							query = addAndExpandStringBy(query, &querySize, " UNION ALL", BLCKSZ, &to
+ 														 ,&toFreeQuery);
+ 						firstTSet = false;
+ 						query = addAndExpandStringBy(query, &querySize, "("
+ 												 ,BLCKSZ, &to, &toFreeQuery);
+ 						query = addAndExpandStringBy(query, &querySize, fctx->queries[relToCheck]
+ 												 ,BLCKSZ, &to, &toFreeQuery);
+ 						firstAtt = true;
+ 						for (i = 0; i < fctx->relsNAtt[relToCheck]; i++)
+ 						{
+ 							if (tset->n[fctx->relsAttNumsAtTupleSet[relToCheck][i]] != 'n')
+ 							{
+ 								if (firstAtt)
+ 								{
+ 									query = addAndExpandStringBy(query, &querySize, " WHERE "
+ 												 ,BLCKSZ, &to, &toFreeQuery);
+ 									firstAtt = false;
+ 								}
+ 								else
+ 								{
+ 									query = addAndExpandStringBy(query, &querySize, " AND "
+ 												 ,BLCKSZ, &to, &toFreeQuery);
+ 								}
+ 								query = addAndExpandStringBy(query, &querySize,
+ 															 (char *) get_attname(fctx->relsOid[relToCheck],
+ 																				  fctx->relsValidAttNumAtRelation[relToCheck][i])
+ 												 ,BLCKSZ, &to, &toFreeQuery);
+ 								params[paramNum] = tset->v[fctx->relsAttNumsAtTupleSet[relToCheck][i]];
+ 								types[paramNum] = fctx->relsAttTypes[relToCheck][i];
+ 								paramNum++;
+ 								sprintf(paramStr, "%d", paramNum);
+ 								query = addAndExpandStringBy(query, &querySize, "=$"
+ 												 ,BLCKSZ, &to, &toFreeQuery);
+ 								query = addAndExpandStringBy(query, &querySize, paramStr
+ 												 ,BLCKSZ, &to, &toFreeQuery);
+ 							}
+ 						}
+ 						query = addAndExpandStringBy(query, &querySize, " LIMIT 1)", BLCKSZ, &to
+ 													 ,&toFreeQuery);
+ 					}
+ 					nodePtr = getMemQueueNextElementPtr(nodePtr);
+ 				}
+ 			}
+ 			fctx->querySize = querySize;
+ 			fctx->query = query;
+ 		}
+         
+ 		_SPI_plan  *plan = NULL;
+ 		Portal		portal = NULL;
+ 		bool		usingSeqScan = true;
+         /* here we prepare the index based query. and check if the plan includes a sequential
+          * scan, in which case we should just use our already opened cursor to the relation.
+          * This is done because of deficiency in the capability of the planner to accommodate
+          * our complex index based query */
+ 		if (attemptIndexScan)
+ 		{
+ 			if ((plan = SPI_prepare(query, paramNum, fctx->queryParamsTypes)) == NULL)
+ 				elog(ERROR, "PSFD: SPI_prepare('%s') returns NULL", query);
+ 			Plan	   *planTree;
+ 			ListCell   *plan_list_item = list_head(plan->ptlist);
+ 
+ 			planTree = lfirst(plan_list_item);
+ 			usingSeqScan = isPlanUsingSeqScan(planTree);
+ 		}
+ 		else
+ 		{
+ 			usingSeqScan = true;
+ 		}
+ 
+         
+ 		if (usingSeqScan)
+ 		{
+ 			if (attemptIndexScan)
+ 			{
+ 				SPI_freeplan(plan);
+ 			}
+ 			plan = fctx->plans[relToCheck];
+ 			portal = fctx->portals[relToCheck];
+ 			SPI_cursor_move(portal, false, FETCH_ALL);
+ 			SPI_freetuptable(SPI_tuptable);
+ 		}
+ 		else
+ 		{
+ 			if ((portal
+ 				 = SPI_cursor_open(NULL, plan, fctx->queryParams, NULL, true)) == NULL)
+ 				elog(ERROR, "PSFD: SPI_cursor_open('%s') returns NULL", query);
+ 		}
+ 
+         /* Lets remove a tuple from either our usual portal or specially opened index
+          * based portal */
+ 		SPI_cursor_fetch(portal, true, 1);
+         
+ 		retTSet = NULL;
+ 		endScan = false;
+ 		firstPass = true;
+ 		prevNodePtr = NULL;
+ 		scanNotEmpty = false;
+ 
+         /* This loop handles extending of all the tuple sets to the relation we are currently
+          * attempting to extend to. */
+ 		while ((SPI_processed > 0) && (!endScan))
+ 		{
+ 			tuple->t = SPI_tuptable->vals[0];
+ 			tuple->deformed = false;
+ 			dsTuple->firstIteration = true;
+ 			scanNotEmpty = true;
+ 			isNull = false;
+ 			if (fctx->extendingPolicy == POLICY_NAIVE)
+ 				endScan = false;
+ 			else
+ 				endScan = true;
+             /* basically we move tuple sets based on their charecteristics of their potential
+              * to be extended (need extend, already extend, cannot extend...) 
+              * The first pass does the initial redirections and attempt to extend to the first tuple */
+ 			if (firstPass)
+ 			{
+ 				detachHeadNodeFromMemQueue(nodePtr, Q);
+ 				while (nodePtr)
+ 				{
+ 					if (varNthBitFromLeft(nodePtr->toVisit, relToCheck))
+ 					{
+ 						tset = getMemQueueElement(nodePtr);
+ 						endScan = false;
+ 						retTSet = JCCTSetAndTupleAndReturnTSet(fctx, tset, tuple
+ 								,SPI_tuptable->tupdesc, relToCheck, dsTuple);
+ 						if (retTSet != NULL)
+ 						{
+ 							varSetNthBitFromLeft(nodePtr->relsOfTuples, relToCheck);
+ 							varUnSetNthBitFromLeft(nodePtr->toVisit, relToCheck);
+ 							nodePtr->toVisit |= ((fctx->bits_scheme_graph[relToCheck])
+ 												 & (~(nodePtr->relsOfTuples)) & (~(nodePtr->visited)));
+ 							if (nodePtr->toVisit)
+ 							{
+ 								mergeOutOfMemQueueIntoNode(fctx, &needsChanging, nodePtr, false, true);
+ 								mergeOutOfMemQueueIntoNode(fctx, Q, nodePtr, false, true);
+ 								if (nodePtr->toVisit)
+ 								{
+ 									update_policy_status(fctx, policy, POLICY_CODE_NOT_INVOLVED_1, nodePtr);
+ 									addNodeToMemQueue(nodePtr, &notInvolved);
+ 								}
+ 								else
+ 								{
+ 									update_policy_status(fctx, policy, POLICY_CODE_MAXIMALLY_EXTENDED_0,
+ 														 nodePtr);
+ 									addNodeToMemQueue(nodePtr, &maximalyExtended);
+ 								}
+ 							}
+ 							else
+ 							{
+ 								update_policy_status(fctx, policy, POLICY_CODE_MAXIMALLY_EXTENDED_1,
+ 													 nodePtr);
+ 								addNodeToMemQueue(nodePtr, &maximalyExtended);
+ 							}
+ 						}
+ 						else
+ 							addNodeToMemQueue(nodePtr, &needsChanging);
+ 					}
+ 					else if (!(nodePtr->toVisit))
+ 					{
+ 						update_policy_status(fctx, policy, POLICY_CODE_MAXIMALLY_EXTENDED_2, nodePtr);
+ 						addNodeToMemQueue(nodePtr, &maximalyExtended);
+ 					}
+ 					else
+ 					{
+ 						update_policy_status(fctx, policy, POLICY_CODE_NOT_INVOLVED_2, nodePtr);
+ 						addNodeToMemQueue(nodePtr, &notInvolved);
+ 					}
+ 					detachHeadNodeFromMemQueue(nodePtr, Q);
+ 				}
+ 				firstPass = false;
+ 			}
+ 			else
+ 			{
+ 				nodePtr = getMemQueueFirstElementPtr(&needsChanging);
+ 				prevNodePtr = nodePtr;
+ 				while (nodePtr)
+ 				{
+ 					tset = getMemQueueElement(nodePtr);
+ 					endScan = false;
+ 					retTSet = JCCTSetAndTupleAndReturnTSet(fctx, tset, tuple
+ 								,SPI_tuptable->tupdesc, relToCheck, dsTuple);
+ 					if (retTSet != NULL)
+ 					{
+ 						varSetNthBitFromLeft(nodePtr->relsOfTuples, relToCheck);
+ 						varUnSetNthBitFromLeft(nodePtr->toVisit, relToCheck);
+ 						nodePtr->toVisit |= ((fctx->bits_scheme_graph[relToCheck])
+ 						& (~(nodePtr->relsOfTuples)) & (~(nodePtr->visited)));
+ 						detachNodeFromMemQueueNode(prevNodePtr, nodePtr, &needsChanging);
+ 						if (nodePtr->toVisit)
+ 						{
+ 							mergeOutOfMemQueueIntoNode(fctx, &needsChanging, nodePtr, false, true);
+ 							if (nodePtr->toVisit)
+ 							{
+ 								update_policy_status(fctx, policy, POLICY_CODE_NOT_INVOLVED_3, nodePtr);
+ 								addNodeToMemQueue(nodePtr, &notInvolved);
+ 							}
+ 							else
+ 							{
+ 								update_policy_status(fctx, policy, POLICY_CODE_MAXIMALLY_EXTENDED_3,
+ 													 nodePtr);
+ 								addNodeToMemQueue(nodePtr, &maximalyExtended);
+ 							}
+ 						}
+ 						else
+ 						{
+ 							update_policy_status(fctx, policy, POLICY_CODE_MAXIMALLY_EXTENDED_4,
+ 												 nodePtr);
+ 							addNodeToMemQueue(nodePtr, &maximalyExtended);
+ 						}
+ 						nodePtr = prevNodePtr;
+ 					}
+ 					getMemQueueNextElementAfterRemovalPtr(prevNodePtr, nodePtr, &needsChanging);
+ 				}
+ 			}
+ 			SPI_freetuptable(SPI_tuptable);
+ 			SPI_cursor_fetch(portal, true, 1);
+ 		}
+ 		SPI_freetuptable(SPI_tuptable);
+ 
+         /* we finished here extending to the specific relation. 
+          * we now set all the non extended tuple sets to being visited. */
+ 		if (!scanNotEmpty)
+ 			setVisitedQ = Q;
+ 		else
+ 			setVisitedQ = &needsChanging;
+ 		nodePtr = getMemQueueFirstElementPtr(setVisitedQ);
+ 		prevNodePtr = nodePtr;
+ 		while (nodePtr != NULL)
+ 		{
+ 			if (varNthBitFromLeft(nodePtr->toVisit, relToCheck))
+ 			{
+ 				varUnSetNthBitFromLeft(nodePtr->toVisit, relToCheck);
+ 				varSetNthBitFromLeft(nodePtr->visited, relToCheck);
+ 			}
+ 			if (!(nodePtr->toVisit))
+ 			{
+ 				detachNodeFromMemQueueNode(prevNodePtr, nodePtr, setVisitedQ);
+ 				update_policy_status(fctx, policy, POLICY_CODE_MAXIMALLY_EXTENDED_5, nodePtr);
+ 				addNodeToMemQueue(nodePtr, &maximalyExtended);
+ 				nodePtr = prevNodePtr;
+ 			}
+ 			getMemQueueNextElementAfterRemovalPtr(prevNodePtr, nodePtr, setVisitedQ);
+ 		}
+ 
+         /* finally lets return all the tuple sets we redirected based on their extending potential
+          * to other queues, to Q to start the whole process again. */
+ 		if (scanNotEmpty)
+ 			recombineQueues(Q, 2, &needsChanging, &notInvolved);
+ 		QNumElements = getMemQueueNumElements(Q);
+ 		if (!usingSeqScan)
+ 		{
+ 			SPI_cursor_close(portal);
+ 			SPI_freeplan(plan);
+ 		}
+ 	}
+ 	pfree(tuple);
+ 	pfree(dsTuple);
+ 	if (!isMemQueueEmpty(Q))
+ 		elog(ERROR, "Q not EMPTY before recombine!");
+ 	if (!isMemQueueEmpty(&notInvolved))
+ 		elog(INFO, "NotInvolved NOT EMPTY!");
+ 	if (!isMemQueueEmpty(&needsChanging))
+ 		elog(INFO, "needsChanging NOT EMPTY!");
+ 
+     /* finally we move all the maximally extended tuple sets back into Q to return from the function */
+ 	recombineQueues(Q, 1, &maximalyExtended);
+ }
+ 
+ /* 
+  * This procedure updates the relations we need to scan for generating 
+  * alternative tuple sets (algorithm FD terminology) which greatly reduces
+  * the time of the algorithm.
+  * Note, that here we changed the meaning of ptr->toVisit to mean that
+  * we have to generate alternative tuple set with a relation in toVisit.*/
+ void
+ updateOnDemandRelationsToScan(alg_fctx * fctx, memQueue * Q)
+ {
+ 	RBITS		relationsToScan = 0;
+ 	int			i = 0,
+ 				j = 0,
+ 				k = 0;
+ 	memQueueNode *ptr = getMemQueueFirstElementPtr(Q);
+ 	int			numAtt = NUMATT;
+ 	int			numOfTuples;
+ 	int			numOfTuplesInIntersection = 0;
+ 	int			tsetAtt;
+ 	int			tsetAttributes[numAtt];
+ 	RBITS		RNeighboursIntersectTSetRelations = 0;
+ 	RBITS		relsOfTuples;
+ 	RBITS		relsMask = (~0x0);
+ 
+ 	relsMask >>= fctx->numRels;
+ 	relsMask = ~relsMask;
+ 	TSet		tset = NULL;
+ 	bool		addRelation = false;
+ 
+     /* we pass over all the tuple sets we wish to generate alternative tuple sets
+      * for and update the list of relations we will need to scan if we suspect it
+      * is possible we will need to scan it for some tuple sets.
+      * The algorithm itself is in the FD algorithm docs. */
+ 	while (ptr)
+ 	{
+ 		tset = ptr->tset;
+ 		ptr->toVisit = 0;
+ 		relsOfTuples = ptr->relsOfTuples;
+ 		numOfTuples = numOfTuplesInTSet(fctx, tset->tids);
+ 		if (numOfTuples > 1)
+ 		{
+ 			relationsToScan |= relsOfTuples;
+ 			ptr->toVisit |= relsOfTuples;
+ 			for (i = 0; i < fctx->numRels; i++)
+ 			{
+ 				for (k = 0; k < numAtt; k++)
+ 					tsetAttributes[k] = 0;
+ 				RNeighboursIntersectTSetRelations = (fctx->bits_scheme_graph[i] & relsOfTuples);
+ 				if (!(RNeighboursIntersectTSetRelations))
+ 					continue;
+ 				j = 0;
+ 				numOfTuplesInIntersection = 0;
+ 				while (RNeighboursIntersectTSetRelations)
+ 				{
+ 					if (varNthBitFromLeft(RNeighboursIntersectTSetRelations, 0))
+ 					{
+ 						numOfTuplesInIntersection++;
+ 						for (k = 0; k < fctx->relsNAtt[j]; k++)
+ 						{
+ 							tsetAttributes[fctx->relsAttNumsAtTupleSet[j][k]]++;
+ 						}
+ 					}
+ 					j++;
+ 					RNeighboursIntersectTSetRelations <<= 1;
+ 				}
+ 				addRelation = false;
+ 				for (k = 0; k < fctx->relsNAtt[i]; k++)
+ 				{
+ 					tsetAtt = fctx->relsAttNumsAtTupleSet[i][k];
+ 					if (tsetAttributes[tsetAtt] == numOfTuplesInIntersection)
+ 					{
+ 						if (tsetAttIsNull(tset, tsetAtt))
+ 						{
+ 							addRelation = false;
+ 							break;
+ 						}
+ 					}
+ 					else if (tsetAttributes[tsetAtt] > 0)
+ 					{
+ 						addRelation = true;
+ 					}
+ 				}
+ 				if (addRelation)
+ 				{
+ 					varSetNthBitFromLeft(ptr->toVisit, i);
+ 					varSetNthBitFromLeft(relationsToScan, i);
+ 				}
+ 			}
+ 		}
+ 		else if (!(relsOfTuples))
+ 		{
+ 			ptr->toVisit = relsMask;
+ 			relationsToScan = relsMask;
+ 		}
+ 		ptr = getMemQueueNextElementPtr(ptr);
+ 	}
+ 	fctx->relationsToScan = relationsToScan;
+ }
+ 
+ 
+ /* here we remove one page from preComplete into Q */
+ void
+ onDemandPreCompleteRemovePage(alg_fctx * fctx, FuncCallContext *funcctx, memQueue * Q)
+ {
+ 	clearMemQueue(fctx, Q, true);
+ 	bool		should_free;
+ 	HeapTuple	tuptset;
+ 	int			readSize = 0;
+ 
+ 	while (SQNotEmpty(&fctx->precomplete))
+ 	{
+ 		tuptset = SQGetNextElement(fctx, funcctx, &fctx->precomplete, &should_free, true);
+ 		readSize += tupleSize(tuptset);
+ 		if (readSize > (BLCKSZ))
+ 		{
+ 			SQSetMemOverflowTuple(&fctx->precomplete, tuptset);
+ 			break;
+ 		}
+ 		else
+ 		{
+ 			addTupleSetToMemQueue(fctx, Q, newTSet___(fctx, tuptset));
+ 		}
+ 	}
+ 	updateOnDemandRelationsToScan(fctx, Q);
+ }
+ 
+ 
+ /* Here we take a relation to scan from the list of relations to scan for
+  * the on demand procedure */
+ int
+ getRandomNextRelationToScan(alg_fctx * fctx, int lastScannedRelation)
+ {
+ 	RBITS		relationsToScan = fctx->relationsToScan;
+ 	RBITS		tmp = fctx->relationsToScan;
+ 	int			numRelsToScan = 0;
+ 
+ 	while (tmp)
+ 	{
+ 		if (varNthBitFromLeft(tmp, 0))
+ 			numRelsToScan++;
+ 		tmp <<= 1;
+ 	}
+ 	if (numRelsToScan > 0)
+ 	{
+ 		int			j = 0;
+ 		int			k = 0;
+ 		int			i = ((int) (numRelsToScan * (rand() / (RAND_MAX + 1.0)))) + 1;
+ 
+ 		if (i > numRelsToScan)
+ 			elog(ERROR, "getRandomNextRelationToScan (i>numRelsToScan)");
+ 		while ((j < i) && (j < numRelsToScan))
+ 		{
+ 			if (varNthBitFromLeft(relationsToScan, k))
+ 			{
+ 				j++;
+ 			}
+ 			k++;
+ 		}
+ 		k--;
+ 		varUnSetNthBitFromLeft(fctx->relationsToScan, k);
+ 		return k;
+ 	}
+ 	return -1;
+ }
+ 
+ /* Here we retrieve one tuple at a time from the relation we are currently scanning
+  * and if we reach the end of the relation we cycle to the next relation in the list
+  * automatically. The name "..NextPage.." is left to accommodate the original terminology
+  * of the FD algorithm, however, the page is indeed read in the backend and there is no
+  * actual need to load a whole page again here. */
+ void
+ onDemandRetrieveNextPageFromR(alg_fctx * fctx, FuncCallContext *funcctx)
+ {
+ 	OnDemandState *S = fctx->onDemandState;
+ 
+ 	if (S->lastTuptable)
+ 	{
+ 		SPI_freetuptable(S->lastTuptable);
+ 		S->lastTuptable = NULL;
+ 	}
+ 	if (S->P->t == NULL)
+ 	{
+ 		S->cur_rel = getRandomNextRelationToScan(fctx, -1);
+ 		if (S->cur_rel >= 0)
+ 		{
+ 			SPI_cursor_move(S->portals[S->cur_rel], false, FETCH_ALL);
+ 			SPI_freetuptable(SPI_tuptable);
+ 		}
+ 	}
+ 	while (S->cur_rel >= 0)
+ 	{
+ 		SPI_cursor_fetch(S->portals[S->cur_rel], true, 1);
+ 		if (SPI_processed > 0)
+ 		{
+ 			S->P->t = SPI_tuptable->vals[0];
+ 			S->P->deformed = false;
+ 			S->Pdesc = SPI_tuptable->tupdesc;
+ 			S->PrelID = S->cur_rel;
+ 			S->lastTuptable = SPI_tuptable;
+ 			return;
+ 		}
+ 		SPI_freetuptable(SPI_tuptable);
+ 		if ((S->cur_rel = getRandomNextRelationToScan(fctx, S->cur_rel)) < 0)
+ 			break;
+ 		SPI_cursor_move(S->portals[S->cur_rel], false, FETCH_ALL);
+ 		SPI_freetuptable(SPI_tuptable);
+ 	}
+ 	S->P->t = NULL;
+ 	return;
+ }
+ 
+ /* This procedure is the On demand procedure which is a FD algorithm specific
+  * terminology for generating alternative tuple sets on demand, i.e., retrieving
+  * enough alternative tuple sets to fit into allocated memory. The implementation
+  * here is very close to the implementation suggested in the FD algorithm docs.
+  * The inputs are also documented in the FD algorithm docs.*/
+ void
+ OnDemandGetAlternativeTupleSets(alg_fctx * fctx, FuncCallContext *funcctx,
+ 		  memQueue * out, bool trueUseMemorySizeFalseUseNumTupleSets, int SZ,
+ 								int numTupleSetsToReturn, bool addToComplete)
+ {
+ 	OnDemandState *S = fctx->onDemandState;
+ 	TSet		memQTSet = NULL;
+ 	TSet		alternativeTSet = NULL;
+ 
+ 	if (isMemQueueEmpty(S->Q))
+ 	{
+ 		fctx->relationsToScan = 0;
+         /* here we read a page from precomplete and a tuple from the relations to scan */
+ 		while (!(fctx->relationsToScan))
+ 		{
+ 			onDemandPreCompleteRemovePage(fctx, funcctx, S->Q);
+ 			if (addToComplete)
+ 			{
+ 				memQueueNode *ptr = getMemQueueFirstElementPtr(S->Q);
+ 				TSet		tset = NULL;
+ 				int			i;
+ 				char		nulls[NUMATT];
+ 
+ 				for (i = FIRSTATT; i < NUMATT; i++)
+ 					nulls[i] = 'n';
+ 				nulls[LABELS] = ' ';
+ 				HeapTuple	tempTuple;
+ 
+ 				while (ptr != NULL)
+ 				{
+ 					tset = getMemQueueElement(ptr);
+ 					if (numOfTuplesInTSet(fctx, tset->tids) > 0)
+ 					{
+ 						tempTuple = clearValuesFromTSet(fctx, tset, nulls);
+ 						SQAddElement(fctx, funcctx, &fctx->complete, tempTuple);
+ 						heap_freetuple(tempTuple);
+ 					}
+ 					ptr = getMemQueueNextElementPtr(ptr);
+ 				}
+ 			}
+ 			if (isMemQueueEmpty(S->Q))
+ 				return;
+ 		}
+ 		S->P->t = NULL;
+ 		S->p = 1;
+ 		S->q = 0;
+ 		S->qPtr = getMemQueueFirstElementPtr(S->Q);
+ 		onDemandRetrieveNextPageFromR(fctx, funcctx);
+ 	}
+ 	while (1)
+ 	{
+ 		if (trueUseMemorySizeFalseUseNumTupleSets && numPagesInMemQueue(out) >= SZ)
+ 			return;
+ 		else if (!trueUseMemorySizeFalseUseNumTupleSets
+ 				 && numTupleSetsToReturn <= getMemQueueNumElements(out))
+ 			return;
+ 		if (S->q > 0)
+ 			S->qPtr = getMemQueueNextElementPtr(S->qPtr);
+ 		S->q++;
+ 		if (S->q > getMemQueueNumElements(S->Q))
+ 		{
+ 			S->p++;
+ 			S->q = 1;
+ 			S->qPtr = getMemQueueFirstElementPtr(S->Q);
+ 		}
+ 		if (S->p > 1)
+ 		{
+ 			S->p = 1;
+ 			onDemandRetrieveNextPageFromR(fctx, funcctx);
+ 		}
+ 		if (S->P->t == NULL)
+ 		{
+ 			fctx->relationsToScan = 0;
+ 			while (!(fctx->relationsToScan))
+ 			{
+ 				onDemandPreCompleteRemovePage(fctx, funcctx, S->Q);
+ 				if (addToComplete)
+ 				{
+ 					memQueueNode *ptr = getMemQueueFirstElementPtr(S->Q);
+ 					TSet		tset = NULL;
+ 					int			i;
+ 					char		nulls[NUMATT];
+ 
+ 					for (i = FIRSTATT; i < NUMATT; i++)
+ 						nulls[i] = 'n';
+ 					nulls[LABELS] = ' ';
+ 					HeapTuple	tempTuple;
+ 
+ 					while (ptr != NULL)
+ 					{
+ 						tset = getMemQueueElement(ptr);
+ 						if (numOfTuplesInTSet(fctx, tset->tids) > 0)
+ 						{
+ 							tempTuple = clearValuesFromTSet(fctx, tset, nulls);
+ 							SQAddElement(fctx, funcctx, &fctx->complete, tempTuple);
+ 							heap_freetuple(tempTuple);
+ 						}
+ 						ptr = getMemQueueNextElementPtr(ptr);
+ 					}
+ 				}
+ 				if (isMemQueueEmpty(S->Q))
+ 					return;
+ 			}
+ 			S->p = 1;
+ 			S->q = 1;
+ 			S->qPtr = getMemQueueFirstElementPtr(S->Q);
+ 			onDemandRetrieveNextPageFromR(fctx, funcctx);
+ 		}
+         /* here we generate the alternative tuple set with a tuple set from precomplete
+          * and a tuple from the relation */
+ 		memQTSet = getMemQueueElement(S->qPtr);
+ 		if ((S->qPtr->relsOfTuples) && (varNthBitFromLeft(S->qPtr->toVisit, S->PrelID)))
+ 		{
+ 			alternativeTSet = generateAlternativeTupleSet(fctx, memQTSet, S->qPtr->relsOfTuples,
+ 												  S->P, S->Pdesc, S->PrelID);
+ 		}
+         /* this handles the singleton tuple sets that are generated by taking the empty tuple
+          * set and generating alternative tuple sets to it with tuples from the relations which
+          * trivially results in the singleton tuple set that holds the tuple from the relation */
+ 		else if (!(S->qPtr->relsOfTuples))
+ 		{
+ 			makeSureDeformedTuple(fctx, S->P, S->Pdesc, S->cur_rel);
+ 			alternativeTSet = create_tuple_set(fctx, S->P, S->Pdesc, S->PrelID);
+ 		}
+ 		else
+ 			alternativeTSet = NULL;
+         
+         /* finally if we indeed generated the alternative tuple set we output it */
+ 		if (alternativeTSet != NULL)
+ 		{
+ 			addTupleSetToMemQueue(fctx, out, alternativeTSet);
+ 		}
+ 	}
+ }
+ 
+ /* simple floor function */
+ int
+ algFloor(float value)
+ {
+ 	return (int) value;
+ }
+ 
+ /* simple ceiling function */
+ int
+ algCeiling(float value)
+ {
+ 	if (value > (int) value)
+ 		return (((int) value) + 1);
+ 	else
+ 		return (int) value;
+ }
+ 
+ /* simple max function */
+ double
+ max(double a, double b)
+ {
+ 	if (a > b)
+ 		return a;
+ 	else
+ 		return b;
+ }
+ 
+ /*simple min function */
+ double
+ min(double a, double b)
+ {
+ 	if (a < b)
+ 		return a;
+ 	else
+ 		return b;
+ }
+ 
+ /* getting the upper bound calculated by the adaptive algorithm */
+ int
+ getAdaptiveUpperBound(alg_fctx * fctx)
+ {
+ 	if (fctx->dynamic_extend_cap->stage > 0)
+ 		return fctx->dynamic_extend_cap->nextUpperBound;
+ 	else
+ 		return ADAPTIVE_EXTEND_UPPER_BOUND_MAX;
+ }
+ 
+ /* the core of the adaptive algorithm of an upper bound for the number of
+  * tuple sets to be extended in the extending process at once.
+  * This is not documented in the FD algorithm docs but it is an adhoc
+  * algorithm created for efficiency reasons by using trial and errors.
+  * It is a mathematical formulation which is straight forward from the code. */
+ void
+ updateAdaptiveScore(alg_fctx * fctx, double time)
+ {
+ 	adaptive_extend_upperbound *ad = fctx->dynamic_extend_cap;
+ 	double		currentScore = ((ad->actualInputedExtendTuples ^ 2) / time);
+ 
+ 	if (ad->stage == 0)
+ 	{
+ 		ad->previousScore = currentScore;
+ 		if ((2 * ad->maxInputedExtendTuples) <= ADAPTIVE_EXTEND_UPPER_BOUND_MAX)
+ 			ad->nextUpperBound = 2 * ad->maxInputedExtendTuples;
+ 		else
+ 			ad->nextUpperBound = ADAPTIVE_EXTEND_UPPER_BOUND_MAX;
+ 		ad->stage = 1;
+ 		return;
+ 	}
+ 	if (ad->stage == 1)
+ 	{
+ 		if (currentScore > ad->previousScore)
+ 		{
+ 			if (ad->actualInputedExtendTuples > ad->nextUpperBound)
+ 			{
+ 				ad->nextUpperBound = ad->actualInputedExtendTuples;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			ad->stage = 2;
+ 		}
+ 	}
+ 	if (ad->stage == 2)
+ 	{
+ 		int			currentUpperBound = ad->nextUpperBound;
+ 		double		halfNormalizedDiffScore
+ 		= (max(currentScore, ad->previousScore)
+ 		   - min(currentScore, ad->previousScore)) / (2 * max(currentScore, ad->previousScore));
+ 
+ 		if (currentScore > ad->previousScore)
+ 			ad->nextUpperBound = ad->nextUpperBound + ADAPTIVE_EXTEND_UPPER_BOUND_FIXED_PENALTY_INC
+ 				+ (int) (halfNormalizedDiffScore * ad->nextUpperBound);
+ 		else
+ 			ad->nextUpperBound = ad->nextUpperBound -
+ 				(ADAPTIVE_EXTEND_UPPER_BOUND_FIXED_PENALTY_DEC + (int) (halfNormalizedDiffScore * ad->nextUpperBound));
+ 		if (ad->nextUpperBound < 100)
+ 			ad->nextUpperBound = currentUpperBound;
+ 	}
+ 	ad->previousScore = currentScore;
+ }
diff -crN pgsql/contrib/fulldisjunctions/algutils.h pgsql-fd/contrib/fulldisjunctions/algutils.h
*** pgsql/contrib/fulldisjunctions/algutils.h	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/algutils.h	2006-07-30 13:30:12.000000000 -0400
***************
*** 0 ****
--- 1,111 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #ifndef ALGUTILS_H
+ #define ALGUTILS_H
+ 
+ #ifdef DEBUG
+ #else
+ #define NDEBUG 
+ #endif
+ 
+ #include <assert.h>
+ 
+ #include "postgres.h"
+ #include "utils/palloc.h"
+ #include "algstructs.h"
+ 
+ /* constants to help in the decision if to use an index scan when extending */
+ #define INDEX_AND_PLAN_PER_TUPLE_FACTOR 0.1
+ #define INDEX_AND_PLAN_PER_TUPLE_FACTOR_CPU_COST (0.001)
+ #define INDEX_AND_PLAN_BASE_FACTOR 0.1
+ 
+ /* 
+  * adaptive upper bound algorithm constants. These are estimates for a pentium 1.6 machine
+  * which is assumed to be a starting point. Perhaps in a few years these could be increased
+  * a bit. but these are just a starting point, the algorithm increase them in mid runs anyway.
+  * */
+ #define ADAPTIVE_EXTEND_UPPER_BOUND_MAX 500
+ #define ADAPTIVE_EXTEND_UPPER_BOUND_FIXED_PENALTY_INC 25
+ #define ADAPTIVE_EXTEND_UPPER_BOUND_FIXED_PENALTY_DEC 15
+ 
+  /* note! the numbers matters! only add codes after last code! */
+ enum policies_update_codes
+ {
+ 	POLICY_CODE_DELETED_NODE_0 = 0,
+ 	POLICY_CODE_DELETED_NODE_1 = 1,
+ 	POLICY_CODE_DELETED_NODE_2 = 2,
+ 	POLICY_CODE_DELETED_NODE_3 = 3,
+ 	POLICY_CODE_DELETED_NODE_4 = 4,
+ 	POLICY_CODE_DELETED_NODE_5 = 5,
+ 	POLICY_CODE_MAXIMALLY_EXTENDED_0 = 6,
+ 	POLICY_CODE_MAXIMALLY_EXTENDED_1 = 7,
+ 	POLICY_CODE_MAXIMALLY_EXTENDED_2 = 8,
+ 	POLICY_CODE_MAXIMALLY_EXTENDED_3 = 9,
+ 	POLICY_CODE_MAXIMALLY_EXTENDED_4 = 10,
+ 	POLICY_CODE_MAXIMALLY_EXTENDED_5 = 11,
+ 	POLICY_CODE_MAXIMALLY_EXTENDED_6 = 12,
+ 	POLICY_CODE_NOT_INVOLVED_1 = 13,
+ 	POLICY_CODE_NOT_INVOLVED_2 = 14,
+ 	POLICY_CODE_NOT_INVOLVED_3 = 15
+ };
+ 
+ /* policy functions interface definition. Use this interface for new policy
+  * functions.*/
+ typedef int (*POLICY_FUNC) (alg_fctx * fctx, memQueue * Q, int tsetsNum);
+ 
+ /* code that symbolises each policy */
+ #define POLICY_MAJORITY 0
+ #define POLICY_MINCOST 1
+ #define POLICY_RANDOM 2
+ #define POLICY_NAIVE 3
+ 
+ /* calculates a heaptuple size */
+ #define tupleSize(tuple)\
+ (HEAPTUPLESIZE + tuple->t_len)
+ 
+ /*
+  * Call the type specific '=' function\
+   */
+ #define areAttributesEqual_RT(fctx, attID, lvalue, rvalue)\
+ DatumGetBool(\
+ 	   FunctionCall2(&(fctx->tupleSetAttEQFunctions[attID]->eq_opr_finfo) \
+ ,lvalue, rvalue))
+ 
+  /*
+   * stpcpy is not mine but its difficult to get its header working all the
+   * time. Thus i am redeclaring it here for safety. No harm anycase.
+   */
+ extern char *stpcpy(char *__dest, const char *__src);
+ 
+ extern Datum myDatumCopy(Datum value, bool typByVal, int typLen);
+ extern void myDatumFree(Datum value, bool typByVal, int typLen);
+ extern void sortOidArray(Oid *array, int numElements);
+ extern void updateMaxTupleSize(alg_fctx * fctx, int relID, HeapTuple tuple);
+ extern void updateResultTupleMaxSize(alg_fctx * fctx);
+ extern TypeCacheEntry *getEQFunction(Oid typeid);
+ extern char *addAndExpandStringBy(char *oldStr, int *oldStrAllocSize, char *addStr
+ 					 ,int sizeToAdd, char **relative_ptr, bool *free_old_str);
+ extern bool isResultTupleAttOccursInRelID(alg_fctx * fctx, int resultTupleAttID, int relID);
+ extern bool isResultTupleAttOccursInSomeRelID(alg_fctx * fctx, int resultTupleAttID
+ 								  ,bytea *relsID);
+ extern bool isResultTupleAttOccursInSomeRelIDWithSpecifiedRelations(alg_fctx * fctx,
+ 							   int resultTupleAttID, RBITS relationsToCheck);
+ extern bool isPlanUsingSeqScan(Plan *planTree);
+ extern void Extend(alg_fctx * fctx, memQueue * Q);
+ extern void updateOnDemandRelationsToScan(alg_fctx * fctx, memQueue * Q);
+ extern void onDemandPreCompleteRemovePage(alg_fctx * fctx, FuncCallContext *funcctx, memQueue * Q);
+ extern int	getRandomNextRelationToScan(alg_fctx * fctx, int lastScannedRelation);
+ extern void onDemandRetrieveNextPageFromR(alg_fctx * fctx, FuncCallContext *funcctx);
+ extern void OnDemandGetAlternativeTupleSets(alg_fctx * fctx, FuncCallContext *funcctx,
+ 		  memQueue * out, bool trueUseMemorySizeFalseUseNumTupleSets, int SZ,
+ 								int numTupleSetsToReturn, bool addToComplete);
+ extern int	algFloor(float value);
+ extern int	algCeiling(float value);
+ extern int	getAdaptiveUpperBound(alg_fctx * fctx);
+ extern void updateAdaptiveScore(alg_fctx * fctx, double time);
+ 
+ #endif   /* ALGUTILS_H */
diff -crN pgsql/contrib/fulldisjunctions/createTable.py pgsql-fd/contrib/fulldisjunctions/createTable.py
*** pgsql/contrib/fulldisjunctions/createTable.py	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/createTable.py	2006-07-30 15:55:58.000000000 -0400
***************
*** 0 ****
--- 1,51 ----
+ import random
+ import time
+ import datetime
+ import sys
+ import os
+ import csv
+ import popen2
+ import ConfigParser
+ from sets import Set as set
+ PSQLDIR=""
+ TMPDIR=os.getcwd()+"/"
+ SAMPLE=int(sys.argv[1])
+ POPULATION=int(sys.argv[2])
+ DBNAME=sys.argv[3]
+ TABLENAME=sys.argv[4]
+ ATT1=sys.argv[5]
+ ATT2=sys.argv[6]
+ ERRORSLOGFILE=os.getcwd()+"/.randomtest_errors"
+ def logerrors(ferr):
+   errfile=open(ERRORSLOGFILE,"a")
+   errfile.write(ferr.read())
+   errfile.close()
+ 
+ def createTable(db, sampleSize, population, tablename,att1,att2):
+     rand=[]
+     rand.append(set([]))
+     while len(rand[0])<sampleSize:
+         rand[0].add((int(round(random.random()*population))
+           ,int(round(random.random()*population))))
+     csvfile=open(TMPDIR + ".randtest"+tablename+".csv", "wb")
+     writer = csv.writer(csvfile)
+     for row in rand[0]:
+         writer.writerow(row)
+     csvfile.close()
+     fin, fout, ferr = popen2.popen3(PSQLDIR + "psql " + db + 
+         " -t -c 'CREATE TABLE " + tablename + "(" +
+         att1 +" int, "+ att2 +" int)'")
+     logerrors(ferr)
+     fin, fout, ferr = popen2.popen3(PSQLDIR + "psql " + db + 
+         " -t -c 'TRUNCATE " + tablename + "'")
+     logerrors(ferr)
+     fin, fout, ferr = popen2.popen3(PSQLDIR + "psql " + db + " -t -c \"COPY "
+          + tablename + " FROM " + "'" + TMPDIR + 
+          ".randtest"+ tablename + ".csv" + "'" + " CSV\"")
+     logerrors(ferr)
+     return
+ 
+ #clear errorfile
+ errfile=open(ERRORSLOGFILE,"w")
+ errfile.close()
+ createTable(DBNAME, SAMPLE, POPULATION, TABLENAME,ATT1,ATT2)
diff -crN pgsql/contrib/fulldisjunctions/expected/fulldisjunctions.out pgsql-fd/contrib/fulldisjunctions/expected/fulldisjunctions.out
*** pgsql/contrib/fulldisjunctions/expected/fulldisjunctions.out	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/expected/fulldisjunctions.out	2006-07-30 16:21:53.000000000 -0400
***************
*** 0 ****
--- 1,2620 ----
+ --
+ --  Test Full Disjunctions
+ --
+ SET client_min_messages = warning;
+ \set ECHO none
+ RESET client_min_messages;
+ --
+ --  Test getfdr
+ --
+ SELECT getfdr('t1,t2,t3');
+           getfdr           
+ ---------------------------
+  (a0 int4,a1 int4,a2 int4)
+ (1 row)
+ 
+ --
+ --  Test fulldisjunction
+ --
+ SELECT * FROM fulldisjunction('t1,t2,t3') AS RECORD (a0 int4,a1 int4,a2 int4) ORDER BY a0, a1, a2;
+   a0  |  a1  |  a2  
+ ------+------+------
+     3 |  551 | 3171
+     3 |  551 | 3217
+     5 |      | 2525
+     6 | 2670 | 2260
+     6 |      | 1310
+    11 |      | 2231
+    14 |      | 3254
+    17 | 1320 |     
+    19 | 1227 |     
+    19 | 4541 |     
+    21 |      | 2205
+    23 | 4478 | 1688
+    23 | 4628 | 1688
+    23 |      | 1344
+    26 |  177 |     
+    26 | 1055 |     
+    27 |      | 2422
+    29 |  991 | 1355
+    30 |  529 |     
+    38 |      | 1801
+    41 | 2135 |     
+    42 | 3971 |     
+    45 | 2722 | 4845
+    46 | 2029 | 3879
+    50 | 2723 | 1954
+    51 | 4701 |     
+    53 |  488 |     
+    53 | 1550 |     
+    59 |  207 |     
+    63 | 1548 |     
+    64 | 3735 |     
+    65 | 1899 | 4482
+    67 | 4855 |     
+    72 | 4327 | 2728
+    74 | 3113 |     
+    80 |      |   54
+    81 |      | 3619
+    82 |      | 3494
+    83 |      |  504
+    87 |      | 3261
+    88 |      | 2041
+    89 |  449 |     
+    90 |  421 | 1666
+    90 |      | 2768
+    92 |  443 |     
+    99 | 1878 |     
+   101 | 4823 | 1768
+   102 | 1378 | 2648
+   102 | 1906 | 2648
+   106 | 1950 |     
+   110 |      | 2973
+   114 | 1799 |     
+   115 |      |  922
+   120 | 4201 |     
+   122 | 1286 |     
+   123 |      |   88
+   126 | 1432 |     
+   131 | 3631 |     
+   133 |      | 1182
+   135 | 4563 | 2033
+   137 |      |  397
+   140 |      | 3765
+   143 | 3793 |     
+   144 | 1181 | 2898
+   148 |  814 |     
+   151 |      | 4214
+   152 |  697 | 4150
+   154 | 3700 |     
+   158 | 3687 | 1849
+   160 | 4257 |     
+   165 |  492 | 3013
+   167 |      | 1420
+   167 |      | 3967
+   168 | 3920 | 2932
+   170 | 1757 |     
+   180 |      | 4994
+   195 |      | 2772
+   196 | 1248 |     
+   202 | 1459 | 1469
+   204 |      | 1520
+   206 |      | 3878
+   208 | 1457 | 1712
+   208 | 2159 | 1712
+   217 | 1160 |     
+   220 | 4184 |     
+   229 |      | 2978
+   237 | 3241 |     
+   239 |      | 4097
+   240 |      | 3807
+   242 | 4378 | 4229
+   243 | 2338 |     
+   244 |  632 |     
+   250 | 2407 | 4162
+   254 | 3450 |     
+   257 |  404 |     
+   260 |      | 2144
+   260 |      | 3112
+   261 |      | 3031
+   264 |  728 |     
+   270 |      | 1563
+   272 |      | 3196
+   273 | 4489 |     
+   274 | 2207 |     
+   277 |      |  188
+   279 | 4383 |     
+   287 |  317 |     
+   291 |      | 1305
+   292 |      |  614
+   294 |  923 |  869
+   294 |  923 | 1341
+   294 | 3263 | 4641
+   304 |  847 |     
+   305 |      |  359
+   307 | 3947 |     
+   310 | 2741 |     
+   311 |      | 3464
+   312 |      | 1433
+   313 | 2036 | 1394
+   315 |      | 3936
+   316 |      | 3911
+   319 |      | 1580
+   320 |      | 4101
+   325 | 1762 |  494
+   325 | 1762 |  807
+   329 |  395 | 3583
+   330 | 3073 |     
+   332 |  747 |     
+   337 | 4313 |     
+   339 |      | 1650
+   341 |      | 3490
+   343 |      | 3997
+   345 | 2263 |     
+   347 |      |  662
+   350 |  649 |     
+   354 | 2146 |     
+   355 | 3937 | 2011
+   357 | 2221 |     
+   360 | 4852 | 2805
+   361 |   31 |     
+   361 |  171 |     
+   363 |      |  364
+   363 |      | 2404
+   366 |  296 | 3981
+   367 |      | 3322
+   369 | 1010 |  154
+   370 | 4821 | 4289
+   371 |      | 1867
+   373 |  383 |     
+   373 | 3362 |     
+   375 | 4502 | 2239
+   380 |      | 3278
+   388 |      | 4381
+   389 |  279 |     
+   390 |      | 2190
+   391 |      |   24
+   392 | 4884 |     
+   395 |      | 3690
+   396 |  732 |     
+   397 | 2544 |     
+   398 |  915 |  231
+   398 |  915 | 2787
+   402 |      | 1914
+   404 |      | 4395
+   405 |      | 4829
+   413 |      | 3364
+   414 | 3350 |     
+   416 | 3895 |     
+   418 | 1442 | 2812
+   419 |      |  598
+   423 |      | 4863
+   424 |      | 3958
+   427 |   32 |     
+   431 |      | 1351
+   435 |      | 1385
+   438 |      | 2981
+   441 | 1042 | 3956
+   444 |      | 4143
+   450 |      |  348
+   460 |      | 1181
+   465 | 2802 | 4517
+   467 |      | 2527
+   468 |  712 | 1662
+   469 | 1044 |     
+   472 |  431 | 3897
+   472 | 4178 |     
+   474 | 1612 | 1747
+   478 |      | 2212
+   480 |      | 3862
+   484 |      |   79
+   486 |      | 3376
+   488 | 4710 |  275
+   492 | 2267 |     
+   496 | 1418 |  876
+   496 | 1418 | 4067
+   500 |      |  428
+   500 |      |  861
+   502 |      | 3126
+   504 | 2952 |     
+   512 | 2853 | 2338
+   518 | 2316 |     
+   519 |      |  881
+   521 |      |   40
+   526 | 1882 |     
+   526 | 2459 | 2130
+   529 |      | 4747
+   532 |  938 |     
+   536 | 3120 |     
+   540 |  332 |     
+   543 |      | 3388
+   547 |  128 | 2331
+   548 |      | 1137
+   548 |      | 4548
+   551 |      |  784
+   558 |  759 |     
+   560 |  923 |  869
+   563 | 3110 |     
+   565 | 3021 | 4141
+   573 | 1085 |     
+   574 | 3389 |     
+   580 | 4574 |     
+   581 | 1408 | 1515
+   581 | 3022 | 1515
+   590 |      | 4416
+   593 |      | 2860
+   597 |      | 1494
+   597 |      | 2899
+   599 | 1994 |     
+   601 | 4435 |     
+   603 | 1461 | 4162
+   604 | 4408 | 4439
+   605 | 2403 |     
+   607 | 2385 |     
+   610 |      |  792
+   612 | 1688 |     
+   614 | 3323 |     
+   615 |      | 2756
+   619 |      |  413
+   625 |  603 |     
+   626 |      |  460
+   627 |      | 4142
+   632 | 3470 |     
+   633 | 1899 | 4482
+   635 | 2725 |     
+   637 | 1828 | 4279
+   639 | 3825 |     
+   644 |      |  900
+   645 |      | 1966
+   646 |  114 |     
+   648 |      | 1575
+   653 | 2798 |     
+   655 | 3955 |     
+   656 | 3874 |     
+   660 |      | 1680
+   661 | 1497 |     
+   664 |      | 3654
+   668 | 4406 |  955
+   668 | 4406 | 1363
+   670 |      | 1582
+   672 | 1775 |     
+   675 |      | 3510
+   678 | 4399 |     
+   686 | 2802 | 4019
+   686 | 3802 | 4019
+   687 |      |  897
+   688 | 3371 |     
+   690 |  304 | 1778
+   692 |      | 2366
+   694 |  863 | 4372
+   701 |  649 |     
+   703 |      | 3436
+   703 |      | 4339
+   707 | 4185 | 3698
+   708 | 3072 | 4615
+   710 |      | 1513
+   715 |      |  110
+   715 |      | 2951
+   716 | 4716 |     
+   721 | 3098 |     
+   722 | 1965 |     
+   722 | 3268 |     
+   724 |      | 1602
+   724 |      | 4282
+   727 | 2680 |   31
+   729 |      | 2158
+   730 | 4886 | 4265
+   732 | 2568 |  198
+   732 | 3350 |  198
+   740 |      | 1921
+   742 |      | 3479
+   748 |      |  580
+   755 |      | 3872
+   762 | 1884 |     
+   764 | 4422 |     
+   766 |      | 3703
+   770 |      | 2434
+   771 | 1917 |     
+   771 | 3564 | 4541
+   772 | 1498 |  587
+   774 | 1064 |     
+   775 | 1331 |  486
+   776 |  451 |     
+   779 | 1582 |     
+   779 | 3182 |     
+   781 | 1190 |  920
+   782 | 1166 |     
+   783 | 2905 |     
+   784 |      | 2583
+   788 |      | 3086
+   790 | 4747 |     
+   795 | 2647 | 1903
+   799 |      | 1835
+   800 |  890 | 3052
+   805 |  864 | 2226
+   807 | 2647 | 1903
+   808 | 4483 |  503
+   808 | 4483 | 3135
+   809 |      | 4094
+   813 | 1677 | 3644
+   822 |  517 |     
+   824 |  780 |     
+   825 | 3118 | 2029
+   825 | 3539 | 3368
+   829 | 2743 | 1149
+   830 |      | 2380
+   833 |      | 4527
+   835 |  431 | 3897
+   842 | 4564 |     
+   843 |      |  809
+   847 | 1425 |     
+   848 | 2229 | 3895
+   849 | 3560 | 3464
+   851 |      | 3069
+   853 |      | 4861
+   854 |      |  207
+   855 |      | 4683
+   856 |      | 4206
+   867 |      | 1402
+   870 | 3690 |     
+   879 | 1070 |     
+   880 |      | 3075
+   883 |      | 3936
+   885 | 1224 |     
+   888 |      | 3901
+   891 |  518 |     
+   893 |      | 4872
+   898 | 3750 | 4693
+   907 | 4406 |     
+   911 | 2237 | 1911
+   912 | 1798 |     
+   916 | 3908 |     
+   918 |      |  734
+   919 |      | 4341
+   920 | 4445 | 4710
+   922 |      | 3616
+   924 | 2724 | 3859
+   927 | 3088 | 4103
+   928 |      | 4107
+   929 |  376 | 2903
+   929 | 3706 | 2903
+   930 | 4837 |     
+   937 |      | 4113
+   941 | 1028 |     
+   942 | 1941 |     
+   954 | 2032 |  625
+   956 |      | 1974
+   961 | 2619 |     
+   962 | 3666 |     
+   964 | 3759 |     
+   965 |      | 4700
+   966 |      | 4278
+   968 |      | 3016
+   969 | 2008 | 3394
+   971 | 2972 | 2705
+   973 | 4111 |     
+   974 |      | 3552
+   976 |      | 2102
+   978 |      | 4990
+   981 | 1275 |     
+   982 |   29 | 1374
+   982 | 3698 | 1917
+   984 |      | 1771
+   986 | 2016 | 3068
+   987 |  949 |  527
+   990 | 1649 | 4995
+   995 |  485 | 4089
+   996 |  689 |     
+   999 | 3822 |     
+  1001 | 4684 | 3266
+  1002 | 1514 | 2459
+  1003 |  115 | 1614
+  1005 | 4706 | 4260
+  1008 | 1890 | 4975
+  1008 | 2312 | 4975
+  1010 | 2322 |     
+  1011 | 4211 |     
+  1012 | 1998 | 3148
+  1014 |      | 2743
+  1015 | 3855 | 1681
+  1019 |  116 | 2777
+  1022 |      | 2301
+  1024 |      | 3919
+  1027 | 1524 |     
+  1028 |      | 1229
+  1031 |      | 3966
+  1039 |  934 | 1102
+  1039 |  934 | 1717
+  1041 | 4491 |     
+  1044 |      | 1561
+  1046 |  127 |     
+  1046 |  450 |     
+  1046 | 3279 |     
+  1047 | 1178 | 2728
+  1047 | 4327 | 2728
+  1051 | 1909 | 2158
+  1056 |   22 |     
+  1059 | 4255 |     
+  1060 | 2246 | 2097
+  1065 |      | 4663
+  1071 | 1739 |     
+  1072 | 4954 |     
+  1073 |      | 1132
+  1074 |      |  455
+  1075 |  108 |     
+  1077 |  716 |     
+  1078 |      | 2291
+  1079 | 4447 | 2155
+  1079 |      | 4347
+  1084 | 4706 | 1766
+  1084 | 4706 | 3578
+  1084 | 4706 | 4260
+  1085 | 1651 | 3313
+  1096 |  307 | 4225
+  1097 |  567 | 3841
+  1101 |      | 1588
+  1102 | 2797 |     
+  1103 | 1452 | 1669
+  1103 | 4869 | 1669
+  1107 | 3794 | 1838
+  1107 | 3794 | 3186
+  1117 | 1908 |     
+  1124 | 3663 |     
+  1127 | 3559 | 1404
+  1134 |      | 2325
+  1141 | 2911 | 4298
+  1144 |  498 |     
+  1146 | 4620 |     
+  1147 | 3605 |     
+  1151 | 1811 |  823
+  1157 | 4389 | 4283
+  1162 |  142 |     
+  1163 | 4554 |   80
+  1164 |  172 |     
+  1173 |      |  266
+  1176 | 1564 | 3829
+  1179 |      | 4082
+  1180 |      | 3187
+  1189 | 2279 | 1041
+  1194 |      | 3900
+  1195 | 1800 | 2920
+  1199 | 2794 |     
+  1200 | 1174 |     
+  1207 |      | 1578
+  1213 | 4554 |   80
+  1213 | 4554 | 4149
+  1214 |  685 |  360
+  1214 |  685 | 4656
+  1214 |  685 | 4670
+  1220 |      | 1350
+  1223 |      |  464
+  1223 |      | 3051
+  1223 |      | 4888
+  1225 |      | 3820
+  1227 |      | 4073
+  1231 | 4919 |     
+  1236 | 4172 |     
+  1237 | 2395 |     
+  1238 |  879 |     
+  1239 |  724 |     
+  1242 |      | 3816
+  1246 | 1302 | 2093
+  1248 | 3696 |     
+  1253 | 2044 | 1670
+  1253 | 3187 | 1670
+  1255 | 2557 |  506
+  1258 | 2846 |     
+  1260 |      | 3804
+  1263 | 3994 |     
+  1264 |      |  158
+  1272 |   63 |     
+  1273 |      | 1115
+  1274 | 3209 |     
+  1278 | 4885 |     
+  1279 |      | 4128
+  1288 |      | 3313
+  1289 | 2029 | 3879
+  1291 | 4883 |     
+  1298 |      |  802
+  1298 |      | 2240
+  1299 |      |  203
+  1303 |      |  899
+  1307 |      | 1653
+  1311 |      |  128
+  1313 |      |  546
+  1315 | 2584 | 3570
+  1323 | 3592 | 2150
+  1330 | 2512 |     
+  1334 |      |  786
+  1336 |      |  881
+  1336 |      | 1466
+  1342 | 3346 |     
+  1350 | 2620 |     
+  1351 |  472 |  992
+  1351 | 2898 |  992
+  1354 |  306 |     
+  1357 |      | 1819
+  1359 | 1755 |  377
+  1365 |      |   49
+  1366 |      | 3545
+  1368 | 4976 | 2649
+  1370 | 2338 |     
+  1371 | 2890 |     
+  1371 | 3457 |     
+  1373 | 1409 |     
+  1375 | 4921 |     
+  1380 |   72 |     
+  1383 | 3395 | 4297
+  1383 | 4920 |     
+  1386 | 4841 | 4296
+  1388 |  557 |     
+  1392 | 2200 | 3110
+  1394 | 1956 |     
+  1395 |  726 |     
+  1399 | 3663 |     
+  1400 |      | 2157
+  1402 |      | 4746
+  1405 |      | 1588
+  1406 |  533 |     
+  1413 |      | 1676
+  1415 |  368 |     
+  1416 |  923 |  869
+  1416 | 2306 |  869
+  1416 | 2306 | 3618
+  1419 | 3931 |     
+  1421 |      | 4902
+  1424 | 1008 |     
+  1425 |      | 4743
+  1426 | 3377 |     
+  1428 | 4254 | 4654
+  1430 | 2343 | 1061
+  1434 | 1126 |     
+  1435 | 3688 | 1567
+  1436 |  297 |     
+  1438 |      |  550
+  1438 |      | 3355
+  1445 |  996 | 3346
+  1445 | 2944 | 3346
+  1446 | 2685 | 2961
+  1446 | 3135 |  445
+  1446 | 3135 | 2961
+  1448 |      | 1352
+  1449 |      |  193
+  1449 |      |  802
+  1453 |      | 4964
+  1456 | 4205 | 4735
+  1458 |      | 2899
+  1459 |      | 1081
+  1460 |      | 1316
+  1463 | 3801 | 4816
+  1466 |      | 1689
+  1473 |      | 1686
+  1483 |      | 2726
+  1486 |  155 |     
+  1488 | 4926 |     
+  1489 | 4832 |     
+  1490 | 4708 |     
+  1492 |      | 2232
+  1494 |      | 4399
+  1495 | 4851 |     
+  1496 |      | 2605
+  1498 | 3975 |     
+  1498 | 4559 |     
+  1505 | 1450 | 1182
+  1505 | 1450 | 2245
+  1506 | 1803 |     
+  1507 |   51 | 1525
+  1508 |  207 |     
+  1513 | 2969 |  311
+  1514 | 1072 | 1163
+  1518 |      | 2223
+  1520 |      | 3244
+  1524 | 1662 |     
+  1525 | 3420 |     
+  1528 |      | 4215
+  1532 | 1129 | 2307
+  1534 |      | 2016
+  1534 |      | 2525
+  1534 |      | 4759
+  1536 | 1053 | 1485
+  1538 | 3942 | 1775
+  1538 | 3942 | 3733
+  1539 |  939 | 3326
+  1539 |  939 | 3933
+  1545 |      |  635
+  1546 |      | 2121
+  1547 | 4207 |     
+  1548 |  569 |  944
+  1548 |  569 | 1097
+  1552 |      | 2294
+  1554 |      |  744
+  1562 |      | 3521
+  1563 |      | 3859
+  1567 |      |  916
+  1568 |      |  156
+  1573 | 4758 | 4309
+  1579 | 4454 | 4727
+  1580 | 4742 |     
+  1581 |      |  763
+  1582 |      | 1940
+  1583 | 3093 |     
+  1584 | 1072 | 1163
+  1588 |      | 3726
+  1590 | 1858 |     
+  1591 | 2864 | 1536
+  1592 |      | 2447
+  1594 |      | 4372
+  1595 |      | 2717
+  1596 |  795 |     
+  1605 | 4214 |     
+  1607 |      | 2573
+  1608 |      |  371
+  1609 | 2516 | 1507
+  1613 |      | 2210
+  1615 |      | 3391
+  1618 |  285 | 4638
+  1622 |  157 |  677
+  1622 |  599 |  677
+  1624 | 2684 |     
+  1626 | 4436 |     
+  1630 |  858 | 4492
+  1630 | 4674 | 4492
+  1637 |      | 3445
+  1638 | 2228 |     
+  1638 | 3317 |     
+  1640 |      | 4802
+  1644 | 4136 |     
+  1646 |      |  960
+  1647 |  237 |     
+  1648 |      | 4793
+  1649 |      | 1277
+  1652 | 4193 |     
+  1655 | 4910 |  353
+  1657 |      | 3510
+  1664 | 3515 |     
+  1667 | 2545 |     
+  1670 | 1219 |     
+  1672 | 1057 |  305
+  1678 |  247 |     
+  1679 | 2972 | 2705
+  1685 | 1146 | 4823
+  1687 |      |  196
+  1688 |  297 | 3263
+  1689 |  770 |     
+  1692 | 2695 | 4133
+  1695 |      | 3854
+  1696 | 2525 | 2037
+  1696 |      |   55
+  1696 |      | 2100
+  1700 | 4725 | 1870
+  1701 |  561 |     
+  1701 | 1698 |     
+  1703 | 4294 |     
+  1707 | 3100 | 2645
+  1709 |  416 |     
+  1711 | 4979 | 1477
+  1711 | 4979 | 2163
+  1712 |  242 |     
+  1713 | 4021 | 3793
+  1715 | 1205 |     
+  1716 |      | 3146
+  1720 | 2947 |  122
+  1724 | 2219 |     
+  1725 | 4653 |     
+  1727 |      | 3261
+  1730 |      | 4700
+  1731 | 1797 |     
+  1732 |  795 |     
+  1735 |      |  745
+  1736 |  470 |     
+  1737 | 2371 | 2982
+  1742 | 1915 | 2173
+  1744 | 4008 |     
+  1752 | 1538 | 1086
+  1753 | 4490 |     
+  1754 |      | 3750
+  1764 |  814 |     
+  1765 | 2991 |     
+  1766 | 2813 |     
+  1773 |      | 1505
+  1777 | 3357 |     
+  1784 |      | 2184
+  1790 |      |  682
+  1791 |  872 |     
+  1792 |      | 4797
+  1794 |      | 3228
+  1797 | 2559 | 3990
+  1798 | 4406 |     
+  1799 |      | 2380
+  1800 | 3344 | 1544
+  1806 | 1114 |  907
+  1808 | 2962 |  688
+  1815 |      | 3736
+  1816 | 1721 |     
+  1819 |  714 | 2594
+  1819 | 3833 | 2594
+  1819 |      |  424
+  1820 |  594 |     
+  1821 |      | 3180
+  1823 |   77 | 2279
+  1824 |      | 1342
+  1826 | 4456 |     
+  1829 |  801 |     
+  1833 | 1340 | 3754
+  1834 |      | 1723
+  1835 |      | 2297
+  1841 | 1570 |     
+  1845 |      | 1973
+  1849 | 1948 |     
+  1856 |      | 2232
+  1861 | 1282 |     
+  1863 |      | 1655
+  1867 |      |  918
+  1868 |      | 2598
+  1875 |      | 3708
+  1877 | 3650 |     
+  1881 | 2124 |     
+  1885 | 2601 |     
+  1889 | 1430 |     
+  1889 | 4089 |     
+  1891 |      | 1699
+  1895 |      | 1088
+  1899 | 2384 |     
+  1910 | 4951 |     
+  1912 | 4701 |     
+  1913 | 1398 |     
+  1916 | 3981 |     
+  1928 | 3307 |     
+  1930 |  409 |     
+  1931 |  351 |     
+  1932 |      |  617
+  1935 |      |  791
+  1935 |      | 3400
+  1940 | 1188 |     
+  1941 | 3968 | 1372
+  1947 | 4742 |     
+  1949 | 4950 |     
+  1951 | 4328 | 2792
+  1954 |      |  885
+  1959 | 4943 | 3111
+  1960 |      | 1750
+  1966 | 3718 |     
+  1968 |  373 | 3603
+  1969 | 2701 | 4970
+  1976 |  429 | 1296
+  1977 | 1917 |     
+  1995 | 2772 |     
+  1996 | 3414 | 2861
+  1998 | 1415 |     
+  1999 |      | 2499
+  2001 |  141 | 3467
+  2001 | 3606 | 3467
+  2004 |      |  394
+  2004 |      | 3977
+  2010 |  262 | 2848
+  2010 |      | 2192
+  2013 |      | 3596
+  2019 | 4363 |     
+  2020 | 1778 |     
+  2024 | 2123 |     
+  2026 |      | 1246
+  2026 |      | 3098
+  2029 | 4275 | 4708
+  2033 | 4169 |     
+  2034 | 1427 |     
+  2038 | 2732 |     
+  2044 |      |  438
+  2044 |      | 2214
+  2044 |      | 3239
+  2048 |      | 3027
+  2049 | 3447 |     
+  2061 | 4803 |     
+  2062 | 4578 |     
+  2064 |      | 3674
+  2067 | 1623 |     
+  2072 | 4587 | 2799
+  2074 |      |  614
+  2074 |      | 4026
+  2084 | 3050 |     
+  2097 | 1707 | 3629
+  2102 | 4840 |     
+  2105 |      | 1421
+  2116 | 1086 | 2289
+  2116 | 2410 | 2289
+  2116 | 4513 | 2289
+  2117 |      | 1654
+  2122 |  489 |     
+  2126 |      | 3899
+  2130 |      | 2494
+  2131 |      | 1387
+  2132 |      | 4895
+  2133 |      | 4383
+  2134 |      | 2415
+  2138 |      | 1815
+  2147 | 1984 | 4798
+  2149 | 4841 | 4296
+  2149 |      | 2004
+  2155 | 1653 |     
+  2162 | 4510 |     
+  2165 |   17 |     
+  2166 | 1761 |     
+  2168 |  149 |     
+  2168 |  607 |     
+  2180 |      |  626
+  2181 | 3405 |     
+  2184 | 1461 | 4162
+  2191 |      | 3017
+  2192 | 4588 |  781
+  2193 |      | 2746
+  2200 | 2908 |     
+  2202 | 2918 |     
+  2203 | 3034 |     
+  2208 |      |  631
+  2209 |      | 4539
+  2213 |      | 2564
+  2214 |      | 3359
+  2215 | 2874 | 1649
+  2215 |      | 2989
+  2220 |      | 4652
+  2222 | 2545 |  126
+  2226 | 3561 |  289
+  2231 | 3446 | 4572
+  2231 | 4282 | 4572
+  2234 | 1539 |     
+  2235 |      | 1025
+  2236 |      | 4223
+  2237 |      | 3058
+  2238 | 4761 |     
+  2240 | 2375 |     
+  2241 | 2637 | 4247
+  2241 | 3167 | 4247
+  2242 |      | 3593
+  2249 | 1206 |     
+  2251 |      | 4660
+  2256 |      | 4049
+  2263 | 1742 |     
+  2264 |      |  944
+  2268 |  617 | 4498
+  2270 | 1564 |  599
+  2270 | 1564 | 3829
+  2273 | 1289 |  706
+  2277 | 3185 | 4258
+  2278 |      | 2621
+  2279 |      | 2832
+  2281 |  190 |     
+  2281 | 4339 |     
+  2283 |      | 2976
+  2287 |      | 3977
+  2290 |      | 2047
+  2293 | 1214 |  440
+  2295 | 3008 | 2646
+  2302 | 3717 |     
+  2308 | 1360 |     
+  2315 |      | 3978
+  2316 | 3124 |     
+  2316 | 4073 |     
+  2318 |  117 |     
+  2320 |      | 2751
+  2321 |   78 | 4521
+  2322 |      |  952
+  2323 | 3056 |     
+  2324 |      | 1858
+  2326 |      | 2416
+  2328 |  103 |     
+  2332 |      | 1175
+  2333 |      | 1892
+  2334 | 4186 |     
+  2335 | 3711 |     
+  2335 | 4035 |     
+  2337 |      | 1466
+  2339 | 3634 |     
+  2345 |  873 |     
+  2350 |      |  861
+  2351 | 4880 | 4972
+  2352 |      | 1247
+  2352 |      | 2449
+  2354 |      | 1468
+  2360 |  276 |     
+  2362 | 2147 | 3846
+  2363 | 4867 |  692
+  2363 | 4867 | 1612
+  2364 |   14 | 4076
+  2364 | 2021 | 4076
+  2364 | 3156 | 4076
+  2365 |  235 | 1791
+  2367 | 2833 | 4013
+  2375 |      | 2824
+  2378 | 4094 |     
+  2381 |      | 1155
+  2382 |      | 1174
+  2388 |      | 1769
+  2391 | 2741 |     
+  2395 | 1164 |     
+  2396 | 2005 | 4486
+  2397 | 4362 |     
+  2398 |      | 3409
+  2399 |  386 | 4251
+  2405 | 4755 | 2113
+  2406 | 4983 | 4268
+  2406 |      | 4584
+  2408 | 3913 |     
+  2409 |      | 1299
+  2412 |      | 2015
+  2414 |      |  820
+  2415 |      | 1080
+  2416 |  376 | 2903
+  2416 | 3706 | 2903
+  2417 | 2687 |  471
+  2418 |      |  472
+  2420 | 4590 |     
+  2423 |      | 3508
+  2425 |  336 |  491
+  2425 |  336 | 4016
+  2425 |  681 |  491
+  2426 |      |  301
+  2431 | 1966 | 1826
+  2437 |      |  143
+  2439 | 3189 |     
+  2441 |      | 4451
+  2442 | 2195 |     
+  2443 |      | 2535
+  2449 | 1372 |     
+  2451 |      | 2730
+  2452 |      |  720
+  2454 | 4828 |     
+  2462 | 1728 |     
+  2463 |      | 3337
+  2465 |      | 4658
+  2468 |      |  596
+  2470 |      | 2618
+  2471 |      | 1383
+  2473 |  164 | 2059
+  2473 | 2767 | 2059
+  2473 | 3500 | 2059
+  2482 |  304 |     
+  2482 | 3706 | 2903
+  2483 |      | 1473
+  2486 |      |  122
+  2488 | 4001 |     
+  2490 |      | 4590
+  2495 | 1676 |  499
+  2497 | 2242 |     
+  2501 |   47 | 2445
+  2501 |   47 | 3869
+  2502 |  627 |     
+  2504 |  357 |     
+  2508 |      |  963
+  2513 | 1698 |     
+  2514 |  857 | 3846
+  2514 | 2147 | 3846
+  2515 | 1841 |     
+  2516 |  145 | 3472
+  2516 |  870 | 3472
+  2519 | 1013 |     
+  2521 |      | 1415
+  2526 | 2763 |     
+  2530 |      | 3764
+  2532 |      | 1764
+  2537 |      |  562
+  2539 | 4549 |  673
+  2540 |      | 1802
+  2548 |      | 4696
+  2560 |      | 2872
+  2560 |      | 3342
+  2562 |      |  618
+  2567 |  144 |     
+  2576 |  961 | 2063
+  2577 |  501 |     
+  2577 | 4400 | 2690
+  2577 | 4400 | 2804
+  2580 | 3891 |     
+  2581 |  651 | 4254
+  2582 | 1811 |  823
+  2582 | 2035 |  823
+  2584 |      |  935
+  2588 | 2265 |     
+  2593 | 2472 | 3074
+  2595 | 3343 |     
+  2598 |  504 |     
+  2601 | 3255 |     
+  2601 | 4421 |     
+  2608 |      | 3924
+  2611 | 1232 |     
+  2612 |      | 2836
+  2617 | 3550 |     
+  2623 | 3627 |  160
+  2623 |      | 3223
+  2625 | 3833 | 2594
+  2627 | 4623 |     
+  2631 |      |  170
+  2633 |   30 | 1548
+  2636 |  558 | 3460
+  2637 | 2635 |     
+  2639 |      | 4116
+  2640 | 1792 | 2221
+  2641 | 1066 |     
+  2642 | 1551 | 3248
+  2655 | 2713 |     
+  2656 |      | 4069
+  2663 | 1604 | 2373
+  2666 | 3644 | 2880
+  2666 |      | 3474
+  2671 | 3592 | 3567
+  2674 | 4285 | 3401
+  2676 |  918 |  795
+  2676 | 3612 |  795
+  2678 |      | 2073
+  2681 | 2548 | 4198
+  2682 |      | 3000
+  2683 | 4527 | 1481
+  2685 |      | 2396
+  2686 | 2884 |     
+  2689 |      | 4514
+  2691 |      | 3570
+  2693 | 2733 |     
+  2694 |      | 3988
+  2695 | 1828 | 4279
+  2699 | 4279 |     
+  2700 |      | 4034
+  2701 |      | 2978
+  2708 |  605 | 2300
+  2708 |  605 | 2924
+  2708 |  605 | 3088
+  2712 | 3278 | 4095
+  2713 | 4435 |     
+  2714 | 1028 |     
+  2714 | 2451 |     
+  2719 |      | 2985
+  2722 |  294 | 4071
+  2727 |  172 |     
+  2741 |      |  720
+  2742 | 1857 |     
+  2742 | 2578 |     
+  2745 |  309 |     
+  2747 |      | 1427
+  2748 | 3972 | 2019
+  2751 | 2617 | 4395
+  2752 |      | 4747
+  2758 |  366 |     
+  2771 |      | 2125
+  2776 | 1501 |     
+  2789 |  194 | 4189
+  2790 |  859 |     
+  2791 | 2325 | 4455
+  2804 |      | 3467
+  2804 |      | 4997
+  2807 | 3765 |     
+  2808 | 3277 |     
+  2810 |      | 4321
+  2811 |      |  528
+  2811 |      | 4668
+  2815 | 4764 |     
+  2816 | 4256 | 4580
+  2822 | 4748 |     
+  2825 |      | 1004
+  2829 |      | 2403
+  2831 | 3830 |     
+  2841 | 4284 | 3004
+  2844 |      |  301
+  2844 |      | 3033
+  2845 | 2441 |     
+  2848 |      | 2126
+  2850 |  227 |     
+  2852 | 3994 |     
+  2855 | 1378 |     
+  2859 | 3020 | 2941
+  2860 | 1539 |     
+  2860 | 2893 |     
+  2861 |      | 1309
+  2862 | 2691 | 4953
+  2863 | 1667 | 4213
+  2869 | 1309 |  739
+  2870 | 1016 |     
+  2876 | 1764 |     
+  2888 |      | 4652
+  2889 | 2479 |     
+  2899 | 1159 | 3299
+  2902 | 1178 |     
+  2905 | 2749 | 1132
+  2908 |      |  794
+  2909 |      | 2474
+  2911 |      |  793
+  2913 |      | 3448
+  2913 |      | 4974
+  2916 | 3403 |     
+  2917 | 1586 | 3699
+  2917 | 1586 | 4463
+  2919 |      |    7
+  2920 |      |  914
+  2922 | 3046 |     
+  2923 |  233 | 2950
+  2924 | 3008 | 2646
+  2926 | 4584 | 1020
+  2926 | 4584 | 3701
+  2927 | 4965 | 3451
+  2936 |  232 |     
+  2937 |      |  717
+  2938 |      | 4602
+  2940 |      | 4908
+  2944 |  258 |     
+  2951 | 4148 | 1479
+  2953 | 3703 | 4153
+  2953 | 3712 | 4153
+  2961 | 3076 | 2200
+  2961 |      | 1537
+  2962 | 3028 |     
+  2965 |      | 1727
+  2967 | 2661 |   74
+  2970 |      | 4386
+  2973 |  189 | 4880
+  2975 |  949 |  527
+  2978 | 4638 | 1119
+  2978 | 4638 | 1519
+  2979 | 2200 | 3110
+  2979 | 4200 |     
+  2980 | 3677 |     
+  2982 | 1975 |     
+  2985 |   51 | 1525
+  2985 | 4705 |     
+  2991 |  417 |     
+  2992 |      | 2243
+  2993 | 3403 |     
+  2994 | 4980 |     
+  2996 | 2179 |     
+  2998 | 2832 | 2170
+  2998 | 2832 | 4126
+  2999 | 3264 |     
+  3003 | 2904 |     
+  3006 |      | 3553
+  3008 | 4604 |     
+  3009 |      |  806
+  3010 | 1042 | 3956
+  3013 | 2615 | 3563
+  3017 |  567 | 3841
+  3017 |      | 3264
+  3017 |      | 3329
+  3018 | 2883 |     
+  3026 |      | 1494
+  3027 | 1854 |     
+  3028 | 1584 | 3337
+  3029 |      | 2535
+  3034 | 1809 |     
+  3036 | 4790 | 2603
+  3041 | 2977 |     
+  3045 |   77 | 2279
+  3045 |   77 | 4749
+  3048 |      | 4233
+  3049 |  312 |     
+  3050 | 3996 | 2921
+  3052 | 3055 |     
+  3053 | 2714 |     
+  3056 | 4276 |     
+  3059 |  711 |     
+  3064 |      | 1020
+  3074 |  733 |     
+  3077 | 2887 | 1764
+  3079 | 3219 |     
+  3083 |      | 2206
+  3087 |      | 1865
+  3088 | 3467 |     
+  3092 | 2323 |     
+  3093 |      | 2035
+  3094 | 3885 | 4740
+  3094 | 4373 | 4740
+  3097 | 4103 | 1215
+  3097 | 4429 | 1215
+  3100 |      | 2988
+  3100 |      | 3021
+  3105 | 1474 | 2261
+  3105 |      |   28
+  3106 |  628 | 1140
+  3109 |      | 2053
+  3111 |      |  834
+  3116 |      | 1253
+  3120 |   95 |     
+  3123 |      | 1819
+  3124 |  429 | 1296
+  3125 |      | 4094
+  3126 | 2204 |     
+  3128 |  238 | 4533
+  3128 | 2636 | 4533
+  3131 |      | 4695
+  3132 |      | 3146
+  3133 |  135 |  930
+  3134 |      | 1587
+  3149 | 1708 |  481
+  3150 |      | 3545
+  3153 | 4966 |     
+  3154 | 1445 |     
+  3155 | 4340 |  991
+  3159 | 1993 |  639
+  3161 | 4571 | 4695
+  3162 | 3205 |  827
+  3170 |      | 3985
+  3174 | 1743 |   22
+  3177 |      |  461
+  3183 |      | 1671
+  3194 |      | 2008
+  3195 |      | 2993
+  3196 | 2826 |  398
+  3199 | 3813 | 4118
+  3202 | 2731 | 3580
+  3202 | 4104 |     
+  3206 | 2691 | 2293
+  3210 | 2631 |  180
+  3211 |      | 4671
+  3214 |      | 2210
+  3217 |      | 1140
+  3222 |  713 | 4925
+  3225 |  704 |     
+  3230 | 2549 |     
+  3232 | 2735 | 4363
+  3235 | 1280 |     
+  3236 |      | 3786
+  3237 | 3988 |     
+  3240 | 2045 | 1814
+  3243 |      | 1887
+  3244 |  981 | 4345
+  3244 | 4252 | 4345
+  3250 | 3153 | 2606
+  3254 | 4698 |     
+  3256 |      | 2822
+  3259 | 4722 |     
+  3267 | 1191 |     
+  3268 |      | 4695
+  3269 | 4665 |     
+  3274 |      | 4594
+  3275 | 2594 |     
+  3281 | 3104 | 2255
+  3282 | 2161 |     
+  3286 |      | 1959
+  3292 | 3907 | 3208
+  3292 |      | 4990
+  3294 |      | 1398
+  3295 | 2046 | 4250
+  3296 | 1957 |     
+  3296 | 4021 | 3793
+  3297 | 3188 |     
+  3298 | 3072 | 4615
+  3298 | 4094 | 4615
+  3299 |      | 3602
+  3300 |  932 |     
+  3303 |      |  186
+  3309 |  536 | 4162
+  3309 | 1461 | 4162
+  3309 | 2407 | 4162
+  3311 | 1612 | 1747
+  3315 |  151 | 4195
+  3320 | 3474 |     
+  3321 | 4583 |     
+  3323 | 3959 |     
+  3325 | 2185 |  453
+  3327 | 4474 |     
+  3328 |  701 |     
+  3331 |      | 3807
+  3337 | 2240 |     
+  3339 | 1412 |  832
+  3339 | 2992 |  832
+  3339 | 2992 | 2063
+  3346 |  297 |     
+  3359 |      | 1644
+  3361 | 3757 |     
+  3363 |      | 2582
+  3363 |      | 4114
+  3365 |      | 3807
+  3365 |      | 4308
+  3367 |      |  685
+  3368 | 2656 |     
+  3370 | 1021 |     
+  3374 |      | 4428
+  3376 |      | 4429
+  3381 | 1729 |     
+  3383 | 3523 |     
+  3385 | 1775 |     
+  3387 | 4706 | 3578
+  3393 | 1104 |  553
+  3393 |      |  851
+  3394 | 1152 |     
+  3399 | 1042 | 3956
+  3408 | 1564 | 3829
+  3415 | 3925 | 2948
+  3417 |      | 3731
+  3419 | 1788 | 3845
+  3421 | 3965 |     
+  3422 |      |  716
+  3422 |      | 3023
+  3426 |      | 3816
+  3428 | 4719 |  205
+  3433 |  303 |     
+  3435 |      | 3306
+  3437 |      | 1494
+  3438 |  272 |  888
+  3440 | 2370 | 3705
+  3441 | 4663 | 2754
+  3442 |      |  894
+  3443 |      | 4634
+  3446 | 2543 | 1301
+  3447 | 4461 |     
+  3447 | 4494 |     
+  3448 |  511 | 1673
+  3448 |  511 | 2275
+  3449 |      | 2013
+  3454 |      | 2192
+  3456 | 2511 |     
+  3458 | 2866 |     
+  3459 |  934 | 1717
+  3459 |      |  971
+  3461 | 1586 | 4463
+  3465 |      |  152
+  3469 |      | 1730
+  3470 | 2498 | 2608
+  3470 |      |  400
+  3471 | 2855 | 1591
+  3472 |      | 2478
+  3475 | 1279 |     
+  3479 |  950 |     
+  3480 | 1067 | 4935
+  3494 |  568 | 1592
+  3501 |      | 3661
+  3504 | 4358 | 3721
+  3504 |      | 3628
+  3507 |      | 4197
+  3510 | 1437 |     
+  3513 | 2684 |     
+  3515 | 2168 |     
+  3520 |  410 | 2925
+  3522 |      | 3584
+  3530 |      | 4415
+  3536 |  974 | 1280
+  3537 | 3901 |     
+  3538 | 1858 |     
+  3540 |  893 |     
+  3540 | 1361 |     
+  3541 | 1241 |  261
+  3542 |  444 |     
+  3542 | 2435 | 2096
+  3545 | 2983 |     
+  3547 |  102 | 2828
+  3547 | 2745 |     
+  3547 | 3489 |     
+  3550 | 3908 |     
+  3554 |      |  873
+  3564 | 3554 | 3288
+  3564 | 3849 | 3288
+  3569 | 3116 |     
+  3571 |      | 1012
+  3581 | 3586 | 1538
+  3582 |      |  100
+  3585 | 1568 |  588
+  3586 |  366 |     
+  3587 |      | 4607
+  3593 |  173 |     
+  3594 | 3027 |     
+  3600 |      | 3382
+  3602 | 2892 |     
+  3603 |   78 |     
+  3603 | 4620 |     
+  3604 |      | 3834
+  3606 | 4707 |     
+  3607 | 4856 |     
+  3608 | 1377 | 2885
+  3616 |      |  493
+  3617 | 3206 |     
+  3619 |  961 | 2063
+  3619 | 2992 | 2063
+  3619 | 4808 | 2063
+  3619 |      | 3510
+  3620 |  540 |     
+  3622 |      |   68
+  3623 |      | 2221
+  3624 |      | 2907
+  3629 | 1006 |     
+  3632 |  125 | 3990
+  3632 | 1452 | 1669
+  3632 | 1452 | 3874
+  3635 |      | 2671
+  3638 | 2068 |  924
+  3639 | 1412 |  832
+  3642 |  138 |  684
+  3645 | 4913 |     
+  3650 |      | 2166
+  3661 |      | 2038
+  3664 |      |  393
+  3665 | 2485 |     
+  3674 | 2290 |     
+  3681 | 1934 |     
+  3682 |      | 1081
+  3683 |      | 1622
+  3687 | 1823 |  825
+  3687 | 2222 |  825
+  3691 |   21 |     
+  3693 | 2817 | 3342
+  3693 | 3227 | 3342
+  3693 | 3227 | 4472
+  3701 |  617 | 1111
+  3702 | 4387 |     
+  3707 |  716 |     
+  3713 |      | 1148
+  3719 | 2983 |     
+  3721 | 4973 |     
+  3728 | 3299 |     
+  3732 | 2288 | 2514
+  3732 |      | 4578
+  3733 | 4184 | 3410
+  3735 | 1794 |     
+  3737 |      | 2585
+  3741 |      | 1577
+  3743 | 1970 | 4307
+  3745 | 1329 | 2995
+  3746 |   89 |     
+  3748 |  237 |     
+  3749 | 1494 |     
+  3750 | 3576 |  993
+  3751 |      | 4172
+  3756 | 3477 |     
+  3761 |  716 |     
+  3763 | 2577 |     
+  3767 |      |  550
+  3768 |      | 3768
+  3769 | 3972 |     
+  3772 | 2470 |  864
+  3772 | 3904 |  864
+  3773 | 1610 |     
+  3777 | 4897 |     
+  3779 |      | 2127
+  3782 |  109 |  663
+  3783 | 1526 | 4220
+  3785 |      | 1835
+  3788 |      | 4155
+  3790 | 4178 |     
+  3793 | 1246 |     
+  3796 | 1565 | 1147
+  3796 | 1565 | 1974
+  3799 | 2052 |     
+  3800 | 3776 |     
+  3804 |  202 |  235
+  3806 |      | 3804
+  3807 |      | 1870
+  3808 |      | 1580
+  3813 | 3030 |     
+  3822 | 2299 |     
+  3823 |  988 | 2905
+  3824 |      | 4246
+  3826 | 3144 |     
+  3834 |      | 2756
+  3838 | 4549 |  673
+  3838 |      | 2839
+  3840 |      |  372
+  3843 | 2328 | 3043
+  3844 | 1128 |     
+  3846 | 3073 |     
+  3849 |      |  528
+  3850 |      | 3935
+  3854 |      | 3787
+  3857 | 2947 |     
+  3858 |      | 1844
+  3860 |      | 1910
+  3867 | 4588 |  781
+  3868 | 1862 |     
+  3870 |      | 4357
+  3871 |      | 3289
+  3872 |  345 | 4985
+  3872 | 2288 | 2514
+  3872 | 2288 | 4985
+  3872 | 2689 | 4985
+  3877 | 1933 |     
+  3879 | 2015 |     
+  3880 |  240 |     
+  3883 | 3016 |     
+  3886 | 1749 | 1201
+  3887 | 2818 |     
+  3890 |      | 3687
+  3894 | 4187 |     
+  3895 |  759 |     
+  3895 | 4923 |     
+  3903 | 1599 | 3189
+  3903 | 1893 | 3189
+  3916 | 3309 |     
+  3917 |      | 1060
+  3921 | 1097 | 2926
+  3924 | 1170 |     
+  3924 | 2196 |     
+  3929 | 4708 |     
+  3930 | 1354 | 4702
+  3933 | 2511 |     
+  3934 |      | 4301
+  3935 |      | 2011
+  3936 | 2562 |     
+  3939 |      | 4601
+  3941 | 1952 |     
+  3942 | 1757 |     
+  3942 | 1935 |     
+  3943 |      | 3575
+  3944 | 3404 |     
+  3944 | 3481 |     
+  3946 | 2864 | 1536
+  3948 | 3126 |     
+  3950 |  587 |     
+  3954 | 1237 |     
+  3955 |      | 1870
+  3957 |      |  751
+  3965 |   40 | 3581
+  3967 |  626 | 2466
+  3967 | 1094 | 2466
+  3967 | 3821 | 2466
+  3968 |      | 3239
+  3971 | 1061 |     
+  3974 |  685 |  360
+  3974 | 1698 |  360
+  3974 | 1698 | 3418
+  3976 | 1005 |     
+  3983 | 1036 |     
+  3984 | 4979 | 1477
+  3985 | 2819 | 3081
+  3988 | 1745 |     
+  3991 | 3079 | 1567
+  3992 |   71 | 2143
+  3993 |  539 |     
+  3993 | 3775 |     
+  3993 | 4574 |     
+  3994 |      | 2385
+  3994 |      | 3018
+  3997 | 3794 | 1838
+  3997 | 3794 | 3186
+  3998 | 2534 | 3511
+  4006 | 2450 |     
+  4006 | 4858 |     
+  4009 | 4127 |     
+  4011 |      |  273
+  4013 |      |  191
+  4016 |  287 |     
+  4019 | 2877 |  880
+  4023 |      | 2053
+  4026 |      | 1422
+  4030 | 4079 | 1126
+  4033 |  610 |  111
+  4033 |  610 | 1429
+  4036 |      |  719
+  4040 | 1500 |     
+  4041 |      |  197
+  4049 | 3979 | 2631
+  4049 | 3979 | 3780
+  4050 | 3677 |     
+  4051 | 2513 |     
+  4052 | 1783 |     
+  4058 | 4809 | 2427
+  4064 | 2805 | 1070
+  4064 | 3862 | 1070
+  4066 |      | 3161
+  4068 |  739 |     
+  4075 | 3105 |     
+  4076 |  451 |     
+  4077 | 3091 |     
+  4081 | 4510 |     
+  4082 |  506 |     
+  4083 | 1404 |     
+  4083 | 4709 |     
+  4083 | 4747 |     
+  4084 | 4607 |     
+  4086 | 3500 |     
+  4087 | 1707 | 3629
+  4088 |      | 4822
+  4093 |  896 |     
+  4093 | 4831 |     
+  4095 |      |  161
+  4095 |      | 1428
+  4100 | 1822 |  956
+  4103 | 2398 |     
+  4104 | 1047 |     
+  4107 |      | 2090
+  4112 | 3418 |     
+  4113 |      |  274
+  4114 | 1890 | 4975
+  4117 |      | 4429
+  4119 | 1030 |     
+  4120 | 2154 |     
+  4120 | 3188 |     
+  4126 |      |  170
+  4128 | 1405 |     
+  4131 |      | 3941
+  4132 |  542 |     
+  4133 | 2380 |     
+  4133 | 2542 |     
+  4136 |      | 3946
+  4137 | 3645 |     
+  4138 | 1157 |     
+  4138 | 3475 | 2858
+  4139 | 4868 |     
+  4140 |      | 4318
+  4142 |  423 |     
+  4146 |      |  204
+  4147 | 3578 |     
+  4149 |      | 3607
+  4149 |      | 4863
+  4150 |  374 |     
+  4153 |      | 3859
+  4158 | 1423 |   76
+  4162 |      |  334
+  4166 | 2177 | 1697
+  4167 | 4103 | 1215
+  4167 | 4588 |  781
+  4167 | 4588 | 1215
+  4168 | 3045 |     
+  4170 |  153 |     
+  4172 |  123 |     
+  4173 | 2038 |     
+  4173 | 2206 |     
+  4175 | 4789 |     
+  4178 |      |  876
+  4179 |      | 1937
+  4180 |      | 4090
+  4182 |  514 |     
+  4183 | 4250 | 4605
+  4184 | 2057 |  797
+  4185 | 2501 | 4549
+  4187 | 4194 |     
+  4188 | 4754 |  102
+  4196 |  295 |     
+  4197 |  293 |     
+  4198 | 3670 |     
+  4199 | 3217 |     
+  4200 | 2970 |     
+  4202 | 2178 |     
+  4203 | 1157 |     
+  4204 | 2917 |     
+  4207 |      | 4437
+  4214 |  559 |     
+  4215 | 3450 |     
+  4219 |  326 | 4433
+  4219 | 1763 | 4433
+  4220 |      | 1690
+  4224 | 1514 | 2459
+  4232 |      | 1387
+  4243 |      | 2635
+  4245 |      | 4547
+  4247 |  617 |  530
+  4249 |      | 1416
+  4253 |  732 |     
+  4257 |   58 |     
+  4262 |      | 1825
+  4266 | 2860 |     
+  4270 | 4675 | 4707
+  4274 | 4560 |     
+  4280 | 2961 | 2497
+  4283 | 1216 |     
+  4283 | 1689 | 4070
+  4286 | 1711 | 4069
+  4290 |  147 | 2174
+  4294 | 4934 |     
+  4296 |      |  291
+  4300 |  915 |     
+  4301 |      | 3953
+  4302 | 4365 |     
+  4305 |      | 2983
+  4306 |  431 | 3897
+  4308 |  375 |     
+  4310 | 3078 | 3217
+  4311 |      | 3024
+  4312 |  584 |     
+  4315 |      | 1131
+  4321 |      |  943
+  4321 |      | 2874
+  4322 | 4572 |     
+  4324 |      | 2967
+  4324 |      | 3909
+  4329 | 3676 |     
+  4332 | 2756 | 1107
+  4335 |      | 3422
+  4336 |   92 |     
+  4338 | 1870 |     
+  4339 |      | 3542
+  4341 | 3919 |     
+  4342 |  697 | 4150
+  4342 |      | 2457
+  4348 |      | 1068
+  4349 |      | 2536
+  4350 | 1683 |     
+  4351 | 1117 |     
+  4352 | 2926 | 4379
+  4354 | 4513 | 2289
+  4356 | 3451 |     
+  4359 |      | 2326
+  4364 |  330 |     
+  4364 |  623 |     
+  4364 | 4900 |  220
+  4365 |      | 2286
+  4367 |      | 3237
+  4372 |      | 1115
+  4374 |      |  958
+  4376 | 4608 | 2945
+  4382 | 1341 |     
+  4384 |  382 |     
+  4387 |  105 | 4776
+  4389 |      |  683
+  4394 | 2597 |     
+  4397 | 3735 |     
+  4401 | 1077 |     
+  4402 |      | 4582
+  4404 | 3546 | 2005
+  4405 | 1135 |  565
+  4407 | 3802 |     
+  4409 |      |  753
+  4411 |   21 |     
+  4413 |  688 | 1310
+  4413 |  688 | 2546
+  4413 |  688 | 4109
+  4413 | 2190 | 1310
+  4413 | 2190 | 2546
+  4415 |      | 3002
+  4416 | 2797 |     
+  4421 |  655 | 4007
+  4423 |  669 | 1665
+  4423 |  669 | 4716
+  4425 | 4527 | 1481
+  4427 |      | 1654
+  4433 | 3300 | 2783
+  4434 | 2812 | 4070
+  4435 | 1958 | 4856
+  4436 | 4824 |     
+  4442 |  368 |     
+  4442 | 1285 |     
+  4443 | 1673 |     
+  4446 | 4573 |     
+  4447 |      | 2931
+  4449 |      | 3264
+  4450 |      | 2035
+  4453 |  105 | 4776
+  4453 | 1646 | 2703
+  4455 |      | 2768
+  4456 |      | 2041
+  4461 | 3294 | 1123
+  4465 |   89 |     
+  4472 | 3060 | 3953
+  4473 | 1223 | 3726
+  4473 | 1223 | 4122
+  4473 | 2986 | 4122
+  4477 | 2051 |     
+  4479 | 3383 |     
+  4480 |      | 1342
+  4484 | 3283 | 4629
+  4486 |   98 |     
+  4500 | 3703 | 4153
+  4500 | 3712 | 4153
+  4503 |      | 4445
+  4505 |      | 4916
+  4507 | 3051 |  740
+  4509 | 1914 | 2374
+  4512 |      | 3596
+  4515 | 3220 |     
+  4518 |      | 4930
+  4523 | 3382 |     
+  4525 |      | 3962
+  4526 |  155 |     
+  4526 | 1090 |     
+  4540 | 4165 |  878
+  4540 | 4165 | 3892
+  4540 | 4929 |     
+  4542 | 3010 |     
+  4544 |      |  604
+  4545 |  267 |     
+  4553 |      | 1830
+  4556 | 4511 |     
+  4559 | 4968 |     
+  4560 | 2829 | 3637
+  4561 |      |  923
+  4563 |      | 4017
+  4569 |      | 2237
+  4571 | 2449 |     
+  4575 |      | 3886
+  4580 | 4747 |     
+  4581 |      | 4141
+  4586 |      | 1116
+  4586 |      | 3431
+  4587 |      | 3551
+  4591 | 4103 | 1215
+  4591 | 4103 | 4592
+  4592 |      | 3065
+  4594 | 3749 |     
+  4595 | 3848 | 2671
+  4598 | 1682 |     
+  4601 | 4721 |     
+  4602 |      |  319
+  4606 |      |  140
+  4606 |      |  787
+  4607 |      | 2590
+  4607 |      | 3423
+  4609 |      |  478
+  4609 |      | 1352
+  4610 | 2839 | 3609
+  4613 |      | 1365
+  4616 |      | 1956
+  4617 |      | 4435
+  4618 | 4187 |     
+  4619 |      | 1264
+  4623 | 4124 | 1645
+  4632 |    9 |     
+  4632 |  703 |  507
+  4637 |      |   99
+  4638 |   45 | 2975
+  4638 | 2062 | 2975
+  4643 |      | 3162
+  4647 |  332 |     
+  4648 |  454 |     
+  4650 |      | 3906
+  4651 | 2912 | 3665
+  4655 | 2319 |  926
+  4657 |      | 2430
+  4658 | 2696 |     
+  4659 |   19 |     
+  4663 |      | 3750
+  4663 |      | 4311
+  4664 | 1260 | 4072
+  4664 | 1382 | 4072
+  4664 | 1780 | 4072
+  4665 | 2949 |     
+  4676 | 1722 | 2165
+  4678 |      |  148
+  4680 | 1850 |     
+  4680 | 4966 |     
+  4685 | 2499 | 2511
+  4689 | 1423 |   76
+  4689 | 1423 | 4242
+  4694 |  744 |     
+  4698 | 2189 |  845
+  4698 | 2189 | 2054
+  4699 | 4826 |  575
+  4701 |  980 |     
+  4705 | 1445 |     
+  4712 |  413 |     
+  4713 | 4524 |     
+  4716 |  704 | 4266
+  4720 |      | 4552
+  4722 |      | 2761
+  4728 |      | 1088
+  4729 |      | 1358
+  4733 |      | 2195
+  4743 | 2382 | 3872
+  4748 |      | 2052
+  4749 | 3092 | 1414
+  4749 | 3092 | 2053
+  4749 | 3092 | 3558
+  4754 |      |  833
+  4758 | 3490 |     
+  4765 | 4771 | 3176
+  4768 | 4173 |     
+  4769 |      | 2612
+  4770 |      |  724
+  4776 |      | 1147
+  4779 | 1489 | 1581
+  4779 | 2051 |     
+  4779 | 4334 |     
+  4780 | 4358 | 3721
+  4781 | 1426 |     
+  4792 | 4988 |     
+  4794 |      | 4974
+  4795 | 1643 |     
+  4797 |      | 1906
+  4800 | 2805 | 1070
+  4801 | 3549 | 3101
+  4803 | 3467 |     
+  4806 | 1344 |     
+  4815 | 3171 |     
+  4817 | 4928 |     
+  4819 | 2204 |     
+  4822 |      | 3276
+  4826 |      |  294
+  4836 | 2512 | 1223
+  4837 | 1646 | 2703
+  4837 | 2760 | 2703
+  4843 | 3306 | 3714
+  4846 | 4886 |     
+  4847 | 4632 |  519
+  4857 | 3642 |     
+  4862 |      | 2832
+  4864 | 4771 | 3176
+  4870 |      |  247
+  4871 |      |  910
+  4872 |  370 |     
+  4873 |      | 4487
+  4886 | 2520 |     
+  4886 | 4916 | 2868
+  4895 |      | 2662
+  4897 |      | 2186
+  4899 |      | 4960
+  4901 |      | 4537
+  4901 |      | 4952
+  4907 | 4614 |  525
+  4909 |  277 |     
+  4911 | 1948 |     
+  4912 |      |  459
+  4914 |      |  689
+  4914 |      | 4402
+  4916 |      | 2911
+  4919 |      | 2558
+  4927 | 2785 |     
+  4930 | 2739 | 2118
+  4930 | 3949 | 2118
+  4930 | 4632 |  519
+  4930 | 4632 | 2118
+  4931 |      | 1665
+  4936 |  184 |     
+  4939 |      |   87
+  4941 | 3328 |   47
+  4947 |  870 | 4653
+  4947 | 4703 | 4653
+  4950 |  713 | 4925
+  4951 |  977 |     
+  4956 | 3980 |     
+  4957 | 2380 |     
+  4960 |  428 |  587
+  4960 | 1498 |  587
+  4960 | 3486 |  587
+  4967 |      | 4969
+  4970 | 2956 |     
+  4975 | 4606 |     
+  4985 |  312 | 2675
+  4985 | 1787 | 2675
+  4987 | 2772 |     
+  4988 |      |    2
+  4994 | 2502 |     
+  4996 | 2503 | 3901
+  4999 |  263 |     
+  5000 | 4014 |     
+       |   12 |  511
+       |   18 | 1109
+       |   18 | 2823
+       |   20 | 3887
+       |   26 |  571
+       |   34 | 4635
+       |   57 | 3008
+       |   66 | 4664
+       |   69 | 4536
+       |   87 | 1516
+       |   91 | 1486
+       |   96 | 2337
+       |  110 | 4799
+       |  112 | 3640
+       |  119 | 1124
+       |  129 | 4121
+       |  139 | 4253
+       |  150 | 3725
+       |  159 | 3942
+       |  161 | 4609
+       |  167 | 1096
+       |  174 | 3689
+       |  175 | 4285
+       |  176 |  467
+       |  181 | 2719
+       |  185 | 3475
+       |  221 |   25
+       |  245 | 4288
+       |  246 | 3512
+       |  249 |   26
+       |  253 | 3710
+       |  256 | 4234
+       |  265 | 1073
+       |  270 | 1710
+       |  271 | 2850
+       |  280 |  728
+       |  281 |  125
+       |  288 | 2123
+       |  316 |  782
+       |  324 | 2045
+       |  338 | 2170
+       |  340 | 4356
+       |  348 | 1259
+       |  355 | 2054
+       |  359 | 1136
+       |  371 | 2982
+       |  392 |  177
+       |  394 |  141
+       |  399 |  130
+       |  408 | 1237
+       |  412 | 3063
+       |  421 | 4036
+       |  422 | 2653
+       |  426 |  831
+       |  447 |  261
+       |  484 | 1292
+       |  499 | 4626
+       |  500 | 4987
+       |  505 | 2316
+       |  513 | 4558
+       |  516 | 2445
+       |  523 | 2469
+       |  523 | 3367
+       |  526 | 3887
+       |  528 | 4848
+       |  531 | 3606
+       |  541 | 4089
+       |  548 | 2320
+       |  549 | 1742
+       |  549 | 2563
+       |  553 |  325
+       |  570 | 2712
+       |  576 | 1282
+       |  577 | 2837
+       |  593 | 1783
+       |  600 | 2845
+       |  613 |  125
+       |  616 | 2363
+       |  619 | 4340
+       |  647 | 4751
+       |  650 | 2863
+       |  661 | 2625
+       |  676 |  223
+       |  684 | 1953
+       |  696 | 2333
+       |  709 | 3465
+       |  710 | 2664
+       |  712 | 2201
+       |  719 |  957
+       |  736 | 3737
+       |  737 | 3861
+       |  741 |  771
+       |  749 | 2028
+       |  749 | 2729
+       |  766 |  541
+       |  767 | 4618
+       |  769 | 2251
+       |  782 |  933
+       |  790 | 4144
+       |  791 |  369
+       |  792 | 4110
+       |  809 |  407
+       |  819 | 3265
+       |  851 | 2412
+       |  865 |  199
+       |  866 | 3045
+       |  867 | 2842
+       |  886 |   80
+       |  898 | 3885
+       |  902 | 1162
+       |  910 | 2000
+       |  910 | 2814
+       |  924 | 3722
+       |  930 | 4441
+       |  933 | 3057
+       |  935 | 3009
+       |  942 | 2965
+       |  943 | 3717
+       |  948 |  725
+       |  948 |  846
+       |  954 | 1324
+       |  958 | 3871
+       |  972 | 4587
+       |  973 |  871
+       |  985 |  668
+       |  998 |  718
+       |  998 | 2610
+       | 1000 |  987
+       | 1025 | 2678
+       | 1040 | 3013
+       | 1051 |   44
+       | 1051 | 4237
+       | 1054 | 3572
+       | 1068 |  421
+       | 1069 |  853
+       | 1071 | 4226
+       | 1076 | 4951
+       | 1079 | 1289
+       | 1083 |  403
+       | 1099 | 1403
+       | 1099 | 2057
+       | 1099 | 3055
+       | 1102 | 4698
+       | 1105 | 2197
+       | 1112 | 3797
+       | 1119 | 1735
+       | 1125 | 1589
+       | 1125 | 3272
+       | 1155 |  953
+       | 1189 | 1485
+       | 1193 | 3727
+       | 1200 | 1970
+       | 1207 | 1762
+       | 1209 |  843
+       | 1210 | 2868
+       | 1222 | 1626
+       | 1249 | 4978
+       | 1250 | 4817
+       | 1257 | 2725
+       | 1268 | 1856
+       | 1294 |  606
+       | 1295 |   85
+       | 1296 | 1027
+       | 1300 | 4310
+       | 1308 | 1739
+       | 1308 | 2890
+       | 1315 | 3489
+       | 1338 |  593
+       | 1345 | 3331
+       | 1369 | 4259
+       | 1375 | 4870
+       | 1386 |  138
+       | 1394 | 4586
+       | 1420 | 3868
+       | 1424 |  257
+       | 1429 | 1827
+       | 1443 | 2569
+       | 1444 | 3751
+       | 1453 |  149
+       | 1471 | 3231
+       | 1472 | 3052
+       | 1473 | 1628
+       | 1481 | 4824
+       | 1490 |  860
+       | 1493 | 3695
+       | 1508 | 1251
+       | 1511 | 1615
+       | 1546 |  522
+       | 1546 | 3035
+       | 1553 | 4621
+       | 1563 | 4736
+       | 1573 | 2408
+       | 1583 | 3176
+       | 1585 | 3073
+       | 1590 | 3791
+       | 1605 | 1696
+       | 1607 | 3908
+       | 1609 | 4129
+       | 1628 | 3519
+       | 1631 | 2773
+       | 1633 | 1754
+       | 1638 | 4444
+       | 1645 | 2548
+       | 1654 | 3495
+       | 1668 | 3383
+       | 1685 | 1651
+       | 1722 | 2804
+       | 1724 | 2089
+       | 1740 | 3884
+       | 1763 | 3292
+       | 1768 | 1907
+       | 1773 | 3111
+       | 1782 |  733
+       | 1789 |   29
+       | 1791 | 2611
+       | 1807 | 4853
+       | 1812 |  895
+       | 1825 |  227
+       | 1830 |  324
+       | 1834 |   46
+       | 1834 |  664
+       | 1836 | 4551
+       | 1839 | 4697
+       | 1860 |  212
+       | 1872 | 2453
+       | 1881 | 1856
+       | 1897 | 3155
+       | 1912 | 3459
+       | 1923 | 2946
+       | 1953 | 4441
+       | 1962 | 4749
+       | 1967 |  237
+       | 1979 | 3548
+       | 1980 | 2448
+       | 1981 | 2575
+       | 1989 |  848
+       | 2001 | 2145
+       | 2002 | 2399
+       | 2007 |  395
+       | 2007 | 3063
+       | 2009 |  315
+       | 2018 | 1328
+       | 2020 | 4287
+       | 2025 |  770
+       | 2037 | 1493
+       | 2043 | 3260
+       | 2055 |  854
+       | 2058 | 1396
+       | 2063 | 1119
+       | 2066 | 1971
+       | 2073 |  783
+       | 2076 | 3897
+       | 2085 | 2930
+       | 2094 | 1507
+       | 2097 | 4801
+       | 2101 | 1486
+       | 2104 |  584
+       | 2105 | 3235
+       | 2111 | 3524
+       | 2112 | 3903
+       | 2114 | 4765
+       | 2119 | 1384
+       | 2120 | 3504
+       | 2136 | 1510
+       | 2151 |  597
+       | 2152 | 1328
+       | 2153 |  173
+       | 2158 | 4936
+       | 2173 |   35
+       | 2173 | 1090
+       | 2180 | 4219
+       | 2183 | 4661
+       | 2202 | 3905
+       | 2220 | 2456
+       | 2224 | 3138
+       | 2257 | 2148
+       | 2262 | 3563
+       | 2266 | 1970
+       | 2270 | 4133
+       | 2271 | 2341
+       | 2272 | 3629
+       | 2276 | 2747
+       | 2283 | 4009
+       | 2291 | 2517
+       | 2292 | 4264
+       | 2302 | 4967
+       | 2305 | 4564
+       | 2311 | 1744
+       | 2317 |  783
+       | 2326 | 1178
+       | 2345 | 4929
+       | 2356 | 1589
+       | 2373 | 4765
+       | 2374 | 4921
+       | 2378 |  518
+       | 2408 | 4851
+       | 2414 | 2159
+       | 2415 | 1923
+       | 2433 | 3432
+       | 2446 | 4673
+       | 2456 | 2946
+       | 2458 | 2640
+       | 2463 | 3285
+       | 2472 | 1000
+       | 2474 | 3757
+       | 2481 | 1965
+       | 2481 | 3325
+       | 2484 |  127
+       | 2487 | 3685
+       | 2490 | 2833
+       | 2492 | 1496
+       | 2492 | 4877
+       | 2497 | 3727
+       | 2498 |  852
+       | 2506 | 3763
+       | 2509 | 3569
+       | 2510 | 3025
+       | 2526 | 3597
+       | 2529 | 4834
+       | 2533 | 4929
+       | 2550 |  465
+       | 2550 | 2264
+       | 2551 | 3483
+       | 2552 | 3623
+       | 2555 | 2007
+       | 2556 | 1029
+       | 2570 | 2550
+       | 2582 | 2216
+       | 2582 | 2272
+       | 2590 | 4966
+       | 2600 | 3124
+       | 2602 | 1186
+       | 2606 | 2801
+       | 2609 |  370
+       | 2610 | 3627
+       | 2613 | 2492
+       | 2614 | 3801
+       | 2623 |  154
+       | 2625 |  423
+       | 2637 | 2955
+       | 2653 | 4667
+       | 2658 | 1931
+       | 2663 | 2680
+       | 2666 | 4789
+       | 2670 |  524
+       | 2673 | 3157
+       | 2683 | 2779
+       | 2688 |  458
+       | 2691 |  150
+       | 2692 |  390
+       | 2692 | 2663
+       | 2704 | 3836
+       | 2709 | 4248
+       | 2712 | 3856
+       | 2718 | 4480
+       | 2726 | 4574
+       | 2730 | 4012
+       | 2739 | 2425
+       | 2764 | 4191
+       | 2767 | 2611
+       | 2779 | 2863
+       | 2784 |  220
+       | 2821 | 2443
+       | 2834 | 3284
+       | 2836 | 5000
+       | 2837 | 3119
+       | 2845 |  479
+       | 2850 | 4560
+       | 2859 | 4042
+       | 2863 | 4680
+       | 2876 | 3333
+       | 2901 | 4374
+       | 2907 | 1568
+       | 2916 |  863
+       | 2920 | 4065
+       | 2933 | 4145
+       | 2936 |  468
+       | 2943 | 2589
+       | 2958 | 1048
+       | 2979 | 2706
+       | 2982 | 1601
+       | 2985 |  378
+       | 2987 |  842
+       | 3006 | 1251
+       | 3013 | 1055
+       | 3014 | 2982
+       | 3015 | 2360
+       | 3019 |  718
+       | 3031 | 4546
+       | 3033 | 4088
+       | 3039 | 2377
+       | 3043 |   52
+       | 3048 | 2878
+       | 3058 | 2854
+       | 3061 | 4603
+       | 3068 | 2238
+       | 3075 | 3713
+       | 3086 |  280
+       | 3102 | 2552
+       | 3131 | 4230
+       | 3138 |  659
+       | 3139 |  636
+       | 3142 | 4715
+       | 3155 |  766
+       | 3157 | 4609
+       | 3159 | 3742
+       | 3161 | 4796
+       | 3163 | 3922
+       | 3164 | 2929
+       | 3164 | 3052
+       | 3168 | 2099
+       | 3170 | 2902
+       | 3173 |  847
+       | 3176 | 3275
+       | 3191 | 3107
+       | 3203 | 2108
+       | 3214 | 3541
+       | 3233 | 1969
+       | 3237 |  167
+       | 3239 | 2841
+       | 3244 | 3503
+       | 3245 | 2827
+       | 3245 | 4168
+       | 3256 | 2902
+       | 3266 | 3822
+       | 3273 | 3071
+       | 3275 | 2992
+       | 3310 |  383
+       | 3315 | 4324
+       | 3325 | 1758
+       | 3338 |  640
+       | 3342 | 1535
+       | 3344 | 2643
+       | 3347 |  699
+       | 3356 | 3336
+       | 3364 | 4328
+       | 3372 | 4674
+       | 3385 | 1773
+       | 3393 |  556
+       | 3394 | 1084
+       | 3394 | 2327
+       | 3399 | 1388
+       | 3407 |  974
+       | 3407 | 2683
+       | 3409 | 3701
+       | 3413 | 2410
+       | 3421 |   56
+       | 3423 | 2187
+       | 3426 |  429
+       | 3429 | 3029
+       | 3439 | 2390
+       | 3487 |  700
+       | 3498 | 2532
+       | 3503 |  776
+       | 3503 |  997
+       | 3519 | 3817
+       | 3531 | 1995
+       | 3548 | 1348
+       | 3554 | 2820
+       | 3557 |  570
+       | 3561 | 1809
+       | 3563 |  606
+       | 3563 | 1977
+       | 3563 | 4875
+       | 3569 | 4306
+       | 3587 | 3535
+       | 3598 | 4541
+       | 3600 | 3887
+       | 3608 | 1120
+       | 3609 | 2296
+       | 3613 | 2295
+       | 3617 | 1244
+       | 3618 | 2520
+       | 3624 |  320
+       | 3626 | 2461
+       | 3630 | 4010
+       | 3636 | 1278
+       | 3643 | 2615
+       | 3644 |  442
+       | 3644 | 3759
+       | 3655 | 1934
+       | 3672 | 1443
+       | 3683 | 1129
+       | 3689 | 3507
+       | 3692 | 4039
+       | 3695 |  490
+       | 3697 | 2552
+       | 3698 | 2384
+       | 3698 | 4015
+       | 3698 | 4763
+       | 3703 | 2348
+       | 3714 | 3334
+       | 3739 | 1036
+       | 3755 | 4750
+       | 3756 | 4192
+       | 3780 |  555
+       | 3781 | 4546
+       | 3783 | 2930
+       | 3783 | 3104
+       | 3787 | 3498
+       | 3806 | 1281
+       | 3818 | 1752
+       | 3824 | 1998
+       | 3844 | 3451
+       | 3844 | 4506
+       | 3853 | 4591
+       | 3858 |  936
+       | 3863 |  933
+       | 3887 | 2351
+       | 3898 | 2503
+       | 3900 | 3096
+       | 3907 |  498
+       | 3911 |  283
+       | 3915 | 3518
+       | 3926 | 1526
+       | 3932 | 1542
+       | 3938 | 4993
+       | 3952 | 1136
+       | 3962 |  416
+       | 3973 |  571
+       | 3977 | 4739
+       | 3984 | 1333
+       | 3985 | 3881
+       | 3991 | 4470
+       | 3993 | 3026
+       | 3999 | 2531
+       | 4005 | 3890
+       | 4022 | 3487
+       | 4030 |   53
+       | 4030 | 3441
+       | 4032 | 2088
+       | 4036 |  421
+       | 4048 | 3696
+       | 4050 |  933
+       | 4063 | 2267
+       | 4077 | 3349
+       | 4109 | 1184
+       | 4117 | 2547
+       | 4135 | 2615
+       | 4141 | 1920
+       | 4141 | 2963
+       | 4151 | 1128
+       | 4155 | 4505
+       | 4155 | 4704
+       | 4158 |  508
+       | 4179 | 1822
+       | 4188 | 4977
+       | 4190 | 2381
+       | 4222 | 3959
+       | 4228 | 1935
+       | 4236 | 2056
+       | 4238 | 4477
+       | 4247 | 3401
+       | 4259 | 1929
+       | 4261 | 3774
+       | 4263 | 3980
+       | 4268 |  174
+       | 4271 | 1781
+       | 4278 |  382
+       | 4280 | 1226
+       | 4287 |  839
+       | 4292 | 1452
+       | 4299 | 3194
+       | 4301 | 3781
+       | 4307 | 4595
+       | 4312 | 3298
+       | 4316 |  733
+       | 4317 | 2300
+       | 4319 | 1922
+       | 4332 | 3735
+       | 4340 | 1632
+       | 4366 | 4196
+       | 4368 | 1660
+       | 4376 | 4204
+       | 4388 | 1852
+       | 4394 | 3078
+       | 4397 | 1225
+       | 4420 |  577
+       | 4426 | 1904
+       | 4433 | 3262
+       | 4457 | 1170
+       | 4464 | 1432
+       | 4469 | 4827
+       | 4473 | 3025
+       | 4475 | 3888
+       | 4499 | 4614
+       | 4501 | 3709
+       | 4502 | 4763
+       | 4503 |  245
+       | 4505 | 1637
+       | 4508 | 3600
+       | 4509 | 1604
+       | 4527 | 4723
+       | 4528 | 2581
+       | 4533 | 3555
+       | 4535 | 2963
+       | 4537 | 2852
+       | 4547 | 3515
+       | 4549 | 1133
+       | 4567 |  853
+       | 4570 | 1982
+       | 4576 |  636
+       | 4576 | 3396
+       | 4579 | 4515
+       | 4589 | 4488
+       | 4593 |  505
+       | 4601 | 3819
+       | 4612 |  783
+       | 4616 | 3569
+       | 4618 |  264
+       | 4639 |  949
+       | 4650 | 2895
+       | 4651 | 2897
+       | 4659 | 2419
+       | 4661 | 2296
+       | 4664 | 2825
+       | 4666 | 2716
+       | 4671 | 1035
+       | 4672 | 4291
+       | 4685 | 3154
+       | 4685 | 3773
+       | 4687 |  676
+       | 4726 | 2672
+       | 4734 | 4329
+       | 4735 |  549
+       | 4735 | 2007
+       | 4746 |  323
+       | 4746 | 2866
+       | 4774 | 2390
+       | 4783 | 3610
+       | 4784 | 1298
+       | 4799 |  862
+       | 4802 | 1615
+       | 4811 | 1931
+       | 4818 |  921
+       | 4829 | 4713
+       | 4861 |  722
+       | 4875 | 1842
+       | 4881 |  666
+       | 4881 | 1179
+       | 4892 | 3735
+       | 4895 | 1845
+       | 4905 | 3920
+       | 4918 | 4471
+       | 4924 | 1896
+       | 4930 | 3047
+       | 4936 | 1555
+       | 4939 |   67
+       | 4945 | 1486
+       | 4945 | 3921
+       | 4957 |  777
+       | 4961 | 4424
+       | 4970 | 3592
+       | 4985 | 3947
+       | 4986 | 2660
+       | 4995 | 4105
+ (2597 rows)
+ 
diff -crN pgsql/contrib/fulldisjunctions/fulldisjunctions.sql.in pgsql-fd/contrib/fulldisjunctions/fulldisjunctions.sql.in
*** pgsql/contrib/fulldisjunctions/fulldisjunctions.sql.in	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/fulldisjunctions.sql.in	2006-07-30 15:51:39.000000000 -0400
***************
*** 0 ****
--- 1,39 ----
+ -- Adjust this setting to control where the objects get created.
+ SET search_path = public;
+ 
+ CREATE OR REPLACE FUNCTION fulldisjunction(text) RETURNS SETOF RECORD
+ AS 'MODULE_PATHNAME','odmbfd'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION fulldisjunction(text,text) RETURNS SETOF RECORD
+ AS 'MODULE_PATHNAME','odmbfd'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION fulldisjunction(text,text,boolean) RETURNS SETOF RECORD
+ AS 'MODULE_PATHNAME','odmbfd'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION fulldisjunction(text,text,boolean,boolean) RETURNS SETOF RECORD
+ AS 'MODULE_PATHNAME','odmbfd'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION fulldisjunction(text,text,boolean,boolean,text) RETURNS SETOF RECORD
+ AS 'MODULE_PATHNAME','odmbfd'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION fulldisjunction(text,text,boolean,boolean,text,int) RETURNS SETOF RECORD
+ AS 'MODULE_PATHNAME','odmbfd'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION odmbfd(text,text,boolean,boolean,text,int) RETURNS SETOF RECORD
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION getfdr(text) RETURNS text
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C STABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION getfdr(text,text) RETURNS text
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C STABLE STRICT;
+ 
diff -crN pgsql/contrib/fulldisjunctions/getfdr.c pgsql-fd/contrib/fulldisjunctions/getfdr.c
*** pgsql/contrib/fulldisjunctions/getfdr.c	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/getfdr.c	2006-07-30 15:25:22.000000000 -0400
***************
*** 0 ****
--- 1,57 ----
+  /* Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. */
+ #include "postgres.h"
+ #include "utils/palloc.h"
+ #include "algstructs.h"
+ #include "algutils.h"
+ PG_FUNCTION_INFO_V1(getfdr);
+ 
+ 
+ Datum
+ getfdr(PG_FUNCTION_ARGS)
+ {
+ 
+ 		FuncCallContext *funcctx = NULL;
+ 
+ 
+ 		alg_fctx * fctx = (alg_fctx *) palloc(sizeof(alg_fctx));
+ 
+ 		initialize_general_structures(fcinfo, fctx, funcctx);
+ 
+ 		int			l = 0;
+ 
+ 
+ 		int			sizeOfHeader = (20 + MaxHeapAttributeNumber * 50) * sizeof(char);
+ 
+ 
+ 		text	   *resultTupleRecord = (text *) palloc(sizeOfHeader + VARHDRSZ);
+ 
+ 
+ 		char	   *header = VARDATA(resultTupleRecord);
+ 
+ 
+ 		char	   *toh = header;
+ 
+ 
+ 		toh = (char *) stpcpy(toh, "(");
+ 
+ 		for (l = 0; l < fctx->resultTupleNAtt; l++)
+ 	{
+ 
+ 			toh = (char *) stpcpy(toh, fctx->resultTupleAttNames[l]);
+ 
+ 			toh = (char *) stpcpy(toh, " ");
+ 
+ 			toh = (char *) stpcpy(toh, SPI_gettype(fctx->resultTupleDesc, l + 1));
+ 
+ 			if (l != (fctx->resultTupleNAtt - 1))
+ 
+ 				toh = (char *) stpcpy(toh, ",");
+ 
+ 	}
+ 		toh = (char *) stpcpy(toh, ")");
+ 
+ 		VARATT_SIZEP(resultTupleRecord) = strlen(header) + VARHDRSZ;
+ 
+ 		PG_RETURN_TEXT_P(resultTupleRecord);
+ 
+ }
diff -crN pgsql/contrib/fulldisjunctions/Makefile pgsql-fd/contrib/fulldisjunctions/Makefile
*** pgsql/contrib/fulldisjunctions/Makefile	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/Makefile	2006-07-30 16:16:02.000000000 -0400
***************
*** 0 ****
--- 1,19 ----
+ # $PostgreSQL$
+ 
+ MODULE_big = fulldisjunctions
+ SRCS += algstructs.c algutils.c getfdr.c odmbfd.c queues.c tset.c
+ OBJS = $(SRCS:.c=.o)
+ DATA_built = fulldisjunctions.sql
+ DATA = uninstall_fulldisjunctions.sql
+ DOCS = README.fulldisjunctions
+ REGRESS = fulldisjunctions
+ 
+ ifdef USE_PGXS
+ PGXS := $(shell pg_config --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/fulldisjunctions
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
diff -crN pgsql/contrib/fulldisjunctions/odmbfd.c pgsql-fd/contrib/fulldisjunctions/odmbfd.c
*** pgsql/contrib/fulldisjunctions/odmbfd.c	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/odmbfd.c	2006-07-29 20:54:13.000000000 -0400
***************
*** 0 ****
--- 1,356 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #include "postgres.h"
+ #include "funcapi.h"
+ #include "catalog/pg_type.h"
+ #include "executor/spi.h"
+ #include "executor/spi_priv.h"
+ #include  <sys/time.h>
+ #include "utils/lsyscache.h"
+ #include "utils/typcache.h"
+ #include "utils/elog.h"
+ #include "utils/memutils.h"
+ 
+ #ifdef PG_MODULE_MAGIC
+ PG_MODULE_MAGIC;
+ #endif   /* */
+ 
+ #include "algstructs.h"
+ #include "tset.h"
+ #include "tsetfuncs.h"
+ #include "queues.h"
+ #include "queuesfuncs.h"
+ #include "algutils.h"
+ 
+ #define DEFAULT_MB 100
+ #define PRINT 1
+ 
+ enum odmbfd_algorithm_state
+ {
+ 	ALGORITHM_START = 0,
+     /* Load all the tuples from the relations into the virtual PreComplete */
+ 	MAIN_PRELOADING,
+ 	MAIN_WHILE_PRECOMPLETE_NOTEMPTY,
+ 	MAIN_WHILE_Q_NOTEMPTY,
+     /* Output the result tuple sets */
+ 	MAIN_PRINT,
+ 	ALGORITHM_END
+ };
+ 
+ void
+ initialize_odmbfd_structures(PG_FUNCTION_ARGS, alg_fctx * fctx, FuncCallContext *funcctx)
+ {
+ 	/* initialize tuple stores. */
+ 	SQInitialize(fctx, funcctx, &fctx->complete);
+ 	SQInitialize(fctx, funcctx, &fctx->precomplete);
+     
+ 	/* additional, per algorithm arguments (for future algorithms) */
+ 	if (fctx->nargs >= 6)
+ 	{
+ 		/* MEMORY MUST BE ALLOCATED IN PAGES! */
+ 		fctx->MB = PG_GETARG_INT32(5);
+ 		if (fctx->MB < 2)
+ 			elog(ERROR, "The memory allocation must be greater than 1, prefferably at least as the number of relations inputted and bigger is faster (commonly at least 100)!");
+ 	}
+ 	else
+ 		fctx->MB = DEFAULT_MB;
+ 
+ 	_initializeMemQueue(&fctx->Q, (fctx->MB * BLCKSZ));
+ 
+ 	/*
+ 	 * potentially 1 page (= BLCKSZ) and MB pages that could be free (however,
+ 	 * the algorithm will determine how much to use)
+ 	 */
+ 	_initializeMemQueue(&fctx->E, ((1 + fctx->MB) * BLCKSZ));
+     
+ 	/* persistant state for the On Demand procedure. */
+ 	fctx->onDemandState = (OnDemandState *) palloc(sizeof(OnDemandState));
+ 	OnDemandState *S = fctx->onDemandState;
+ 
+ 	S->Q = palloc(sizeof(memQueue));
+ 	S->lastTuptable = NULL;
+ 	S->portals = (Portal *) palloc(fctx->numRels * sizeof(Portal));
+ 	S->portals[0] = NULL;
+ 	{
+ 		int			i;
+ 		int			maxRelNatt = 0;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 			if (maxRelNatt < fctx->relsNAtt[i])
+ 				maxRelNatt = fctx->relsNAtt[i];
+ 		S->P = newDTuple(fctx, maxRelNatt);
+ 	}
+ 	int			i = 0;
+ 	for (i = 0; i < fctx->numRels; i++)
+ 	{
+ 		if ((S->portals[i]
+ 		  = SPI_cursor_open(NULL, fctx->plans[i], NULL, NULL, true)) == NULL)
+ 
+ 			/*
+ 			 * note that read_only in cursor_open must be true otherwise ctid
+ 			 * can change.
+ 			 */
+ 			elog(ERROR, "ODMBFD: SPI_cursor_open('%d') returns NULL", i);
+ 	}
+ 	_initializeMemQueue(S->Q, BLCKSZ);
+ 	
+ 	/* Here we calculate the maximal size of a tuple for each relation. */
+     {
+ 		int			i = 0;
+ 
+ 		for (i = 0; i < fctx->numRels; i++)
+ 		{
+ 			SPI_cursor_move(fctx->portals[i], false, FETCH_ALL);
+ 			SPI_cursor_fetch(fctx->portals[i], true, 1);
+ 			while (SPI_processed > 0)
+ 			{
+ 				updateMaxTupleSize(fctx, i, SPI_tuptable->vals[0]);
+ 				SPI_freetuptable(SPI_tuptable);
+ 				SPI_cursor_fetch(fctx->portals[i], true, 1);
+ 			}
+ 			SPI_freetuptable(SPI_tuptable);
+ 			SPI_cursor_move(fctx->portals[i], false, FETCH_ALL);
+ 		}
+ 		updateResultTupleMaxSize(fctx);
+ 		fctx->assumingResultTupleMaxSize = false;
+ 	}
+ }
+ 
+ /*
+  * Here we free the most important to free resources.
+  */
+ void
+ clean_odmbfd_structures_on_exit(PG_FUNCTION_ARGS, alg_fctx * fctx
+ 								,FuncCallContext *funcctx)
+ {
+ 	SQFinish(fctx, funcctx, &fctx->precomplete);
+ 	SPI_finish();
+ 	MemoryContextDelete(fctx->per_query_ctx);
+ }
+ 
+ PG_FUNCTION_INFO_V1(odmbfd);
+ Datum
+ odmbfd(PG_FUNCTION_ARGS)
+ {
+ 	FuncCallContext *funcctx;
+ 	alg_fctx   *fctx = NULL;
+ 
+ 	/* stuff done only on the first call of the function */
+ 	if (SRF_IS_FIRSTCALL())
+ 	{
+ 		MemoryContext oldcontext;
+ 
+ 		/* create a function context for cross-call persistence */
+ 		funcctx = SRF_FIRSTCALL_INIT();
+         
+ 		/* switch to memory context appropriate for multiple function calls */
+ 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+         
+ 		fctx = (alg_fctx *) palloc(sizeof(alg_fctx));
+ 		
+         fctx->trueIndexFalseNonIndexBasedAlgorithm = false;
+         
+         /* Initialize general to all future algorithms structures */
+ 		initialize_general_structures(fcinfo, fctx, funcctx);
+         assert(fctx != NULL);
+ 		
+         funcctx->tuple_desc = BlessTupleDesc(fctx->resultTupleDesc);
+         
+         /* initialize general SPI structures */
+ 		initialize_SPI_structures(fcinfo, fctx, !fctx->noDuplicates, funcctx);
+ 		
+         /* initialize this algorithm specific structures */
+         initialize_odmbfd_structures(fcinfo, fctx, funcctx);
+ 		
+         fctx->algorithm_state = ALGORITHM_START;
+ 		
+         funcctx->user_fctx = fctx;
+ 		
+         MemoryContextSwitchTo(oldcontext);
+ 	}
+     
+     
+ 	/* stuff done on every call of the function */
+ 	funcctx = SRF_PERCALL_SETUP();
+ 	fctx = funcctx->user_fctx;
+ 	/* Seems that if i take this off, all kind of leaks. Better to leave this. */
+ 	MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ 
+     
+ 	/* Algorithm starts here. */
+ 	while (fctx->algorithm_state != ALGORITHM_END)
+ 	{
+ 		switch (fctx->algorithm_state)
+ 		{
+ 			case ALGORITHM_START:
+ 				clearMemQueue(fctx, &fctx->Q, true);
+ 				fctx->algorithm_state = MAIN_PRELOADING;
+ 				break;
+ 			case MAIN_PRELOADING:
+ 				{
+                   /* here we load an empty set that its union with each tuple of the relation is
+                    * the singleton tuple of the relation itself */
+ 					TSet		emptyTSet = newEmptyTSet(fctx);
+ 
+ 					SQAddElement(fctx, funcctx, &fctx->precomplete, emptyTSet->t);
+ 				}
+ 				fctx->algorithm_state = MAIN_WHILE_PRECOMPLETE_NOTEMPTY;
+ 				break;
+ 			case MAIN_WHILE_PRECOMPLETE_NOTEMPTY:
+ 				/*
+ 				 * the ondemand->Q check is instead of the uncommited read in
+ 				 * ondemand function in the thesis. more efficient,
+ 				 * programming wise.
+ 				 */
+ 				if (SQNotEmpty(&fctx->precomplete) || !isMemQueueEmpty(fctx->onDemandState->Q))
+ 				{
+ 					OnDemandGetAlternativeTupleSets(fctx, funcctx, &fctx->Q, true, fctx->MB, -1, true);
+ 					if (!isMemQueueEmpty(&fctx->Q))
+ 					{
+ 						HeapTuple	tuptset = NULL;
+ 						bool		should_free = false;
+ 						bool		isnull;
+ 						TSet		newtset = newTSet____(fctx);
+ 
+ 						/* Q.RemoveSumsumptionsAgainstComplete */
+ 						SQRewindToFirstElement(fctx, funcctx, &fctx->complete);
+ 						while (!SQEof(&fctx->complete))
+ 						{
+ 							if (isMemQueueEmpty(&fctx->Q))
+ 								break;
+ 							tuptset = SQGetNextElement(fctx, funcctx, &fctx->complete, &should_free, false);
+ 							setTSet(fctx, newtset, tuptset, isnull);
+ 							removeLeftMemQueueNodesSubsumedInRightTSet(fctx, &fctx->Q, newtset);
+ 							if (should_free)
+ 								heap_freetuple(tuptset);
+ 						}
+ 						/* Q.RemoveSumsumptionsAgainstPreComplete */
+ 						SQMarkPosition(fctx, funcctx, &fctx->precomplete);
+ 						while (!SQEof(&fctx->precomplete))
+ 						{
+ 							if (isMemQueueEmpty(&fctx->Q))
+ 								break;
+ 							tuptset = SQGetNextElement(fctx, funcctx, &fctx->precomplete, &should_free, false);
+ 							setTSet(fctx, newtset, tuptset, isnull);
+ 							removeLeftMemQueueNodesSubsumedInRightTSet(fctx, &fctx->Q, newtset);
+ 							if (should_free)
+ 								heap_freetuple(tuptset);
+ 						}
+ 						SQRestorePosition(fctx, funcctx, &fctx->precomplete);
+ 						pfree(newtset);
+ 					}
+ 					fctx->algorithm_state = MAIN_WHILE_Q_NOTEMPTY;
+ 				}
+ 				else
+ 					fctx->algorithm_state = ALGORITHM_END;
+ 				break;
+ 			case MAIN_WHILE_Q_NOTEMPTY:
+ 				clearMemQueue(fctx, &fctx->E, true);
+ 				if (!isMemQueueEmpty(&fctx->Q))
+ 				{
+ 					/*
+ 					 * New addition: adaptive upper bound on the number of
+ 					 * tuples being extended at one time. Because of CPU
+ 					 * constraits and transactional constraints, changing this
+ 					 * value might be worth it.
+ 					 */
+ 					struct timeval startTime;
+ 
+ 					gettimeofday(&startTime, NULL);
+ 					int			freePagesInQ = fctx->MB - floorNumPagesInMemQueue(&fctx->Q);
+ 
+ 					if (freePagesInQ < 0)
+ 						freePagesInQ = 0;
+ 					int			numTupleSetsToGet
+ 					= fctx->m_NumResultTuplesInOnePage * (freePagesInQ + 1);
+ 
+ 					if (fctx->dynamic_extend_cap->maxInputedExtendTuples < numTupleSetsToGet)
+ 						fctx->dynamic_extend_cap->maxInputedExtendTuples = numTupleSetsToGet;
+ 					if (getAdaptiveUpperBound(fctx) < numTupleSetsToGet)
+ 						numTupleSetsToGet = getAdaptiveUpperBound(fctx);
+ 					fctx->dynamic_extend_cap->actualInputedExtendTuples = numTupleSetsToGet;
+ 					memQueueNode *nodePtr;
+ 
+ 					while (!isMemQueueEmpty(&fctx->Q) && (getMemQueueNumElements(&fctx->E) < numTupleSetsToGet))
+ 					{
+ 						detachHeadNodeFromMemQueue(nodePtr, &fctx->Q);
+ 						mergeOutOfMemQueueIntoNode(fctx, &fctx->E, nodePtr, true, true);
+ 						addNodeToMemQueue(nodePtr, &fctx->E);
+ 					}
+                     
+ 					Extend(fctx, &fctx->E);
+                     
+ 					removeLeftMemQueueNodesSubsumedInRightMemQueueNodes(fctx, &fctx->Q, &fctx->E);
+ 					/* add E to PRECOMPLETE */
+ 					{
+ 						memQueueNode *ptr = getMemQueueFirstElementPtr(&fctx->E);
+ 						TSet		tset = NULL;
+ 
+ 						while (ptr != NULL)
+ 						{
+ 							tset = getMemQueueElement(ptr);
+ 							tset->t = heap_formtuple(fctx->tupleSetDesc, tset->v, tset->n);
+ 							SQAddElement(fctx, funcctx, &fctx->precomplete, tset->t);
+ 							ptr = getMemQueueNextElementPtr(ptr);
+ 						}
+ 					}
+ 					{
+ 						struct timeval tim;
+ 
+ 						gettimeofday(&tim, NULL);
+ 						double		length = tim.tv_usec - startTime.tv_usec;
+ 
+ 						if (length > 0)
+ 							updateAdaptiveScore(fctx, length);
+ 						else
+ 							updateAdaptiveScore(fctx, 10000);
+ 					}
+ 					if (PRINT)
+ 						fctx->algorithm_state = MAIN_PRINT;
+ 				}
+ 				else
+ 				{
+ 					fctx->algorithm_state = MAIN_WHILE_PRECOMPLETE_NOTEMPTY;
+ 				}
+ 				break;
+ 			case MAIN_PRINT:
+ 				if (fctx->printedTupleAddress != NULL)
+ 				{
+ 					pfree(fctx->printedTupleAddress);
+ 					fctx->printedTupleAddress = NULL;
+ 				}
+ 				if (fctx->startedPrintingQueue == false)
+ 				{
+ 					fctx->printedTSet = fctx->E.head;
+ 					fctx->startedPrintingQueue = true;
+ 				}
+ 				else
+ 					fctx->printedTSet = (memQueueNode *) fctx->printedTSet->next;
+ 				if (fctx->printedTSet != NULL)
+ 				{
+ 					fctx->printedTupleAddress
+ 						= heap_formtuple(fctx->resultTupleDesc, &fctx->printedTSet->tset->v[FIRSTATT],
+ 									  &fctx->printedTSet->tset->n[FIRSTATT]);
+ 					SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(fctx->printedTupleAddress));
+ 				}
+ 				else
+ 				{
+ 					fctx->startedPrintingQueue = false;
+ 					fctx->algorithm_state = MAIN_WHILE_Q_NOTEMPTY;
+ 				}
+ 				break;
+ 			case ALGORITHM_END:
+ 				break;
+ 			default:
+ 				elog(ERROR, "FUNCTION HAVE UNEXPECTED ERROR, Please contact developers.");
+ 		}
+ 	}
+ 
+ 	/* Algorithm ends here. */
+ 	clean_odmbfd_structures_on_exit(fcinfo, fctx, funcctx);
+ 	SRF_RETURN_DONE(funcctx);
+ }
diff -crN pgsql/contrib/fulldisjunctions/queues.c pgsql-fd/contrib/fulldisjunctions/queues.c
*** pgsql/contrib/fulldisjunctions/queues.c	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/queues.c	2006-07-30 13:30:12.000000000 -0400
***************
*** 0 ****
--- 1,628 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #include "queues.h"
+ #include "queuesfuncs.h"
+ #include "tset.h"
+ #include "tsetfuncs.h"
+ #include "algutils.h"
+ #include "utils/tuplestore.h"
+ #include "utils/memutils.h"
+ 
+ void
+ _initializeMemQueue(memQueue * Q, int allocatedBytes)
+ {
+ 	Q->head = NULL;
+ 	Q->tail = NULL;
+ 	Q->numElements = 0;
+ 	Q->sizeInBytes = 0;
+ 	Q->allocatedBytes = allocatedBytes;
+ }
+ void
+ removeDuplicatesMemQueueWithSelf(alg_fctx * fctx, memQueue * Q)
+ {
+ 	if (Q->numElements < 2)
+ 		return;
+ 	memQueueNode *lPtr = Q->head;
+ 	memQueueNode *rPtr = Q->head;
+ 	memQueueNode *prevRPtr = Q->head;
+ 	RBITS		lrels;
+ 
+ 	while (lPtr != NULL)
+ 	{
+ 		if (Q->numElements < 2)
+ 			return;
+ 		lrels = lPtr->relsOfTuples;
+ 		rPtr = (memQueueNode *) lPtr->next;
+ 		prevRPtr = lPtr;
+ 		while (rPtr != NULL)
+ 		{
+ 			if (lrels == rPtr->relsOfTuples)
+ 				if (areTSetsEqual(fctx, lPtr->tset, rPtr->tset))
+ 				{
+ 					detachNodeFromMemQueueNode(prevRPtr, rPtr, Q);
+ 					freeTSet(fctx, rPtr->tset);
+ 					pfree(rPtr);
+ 					rPtr = prevRPtr;
+ 				}
+ 			getMemQueueNextElementAfterRemovalPtr(prevRPtr, rPtr, Q);
+ 		}
+ 		lPtr = (memQueueNode *) lPtr->next;
+ 	}
+ }
+ void
+ removeSubsumptionsMemQueueWithSelf(alg_fctx * fctx, memQueue * Q)
+ {
+ 	if (Q->numElements < 2)
+ 		return;
+ 	memQueueNode *lPtr = Q->head;
+ 	memQueueNode *rPtr = Q->head;
+ 	memQueueNode *prevRPtr = Q->head;
+ 
+ 	while (lPtr != NULL)
+ 	{
+ 		if (Q->numElements < 2)
+ 			return;
+ 		rPtr = Q->head;
+ 		prevRPtr = Q->head;
+ 		while (rPtr != NULL)
+ 		{
+ 			if (lPtr != rPtr)
+ 			{
+ 				if (isLeftSubsumedRightTset(fctx, lPtr->tset, rPtr->tset))
+ 				{
+ 					Q->sizeInBytes -= tupleSize(rPtr->tset->t);
+ 					if ((memQueueNode *) lPtr->next == (memQueueNode *) rPtr)
+ 						lPtr->next = rPtr->next;
+ 					if (Q->head == rPtr)
+ 						Q->head = (memQueueNode *) rPtr->next;
+ 					if (rPtr == Q->tail)
+ 						Q->tail = prevRPtr;
+ 					prevRPtr->next = rPtr->next;
+ 					freeTSet(fctx, rPtr->tset);
+ 					pfree(rPtr);
+ 					Q->numElements--;
+ 					break;
+ 				}
+ 			}
+ 			prevRPtr = rPtr;
+ 			rPtr = (memQueueNode *) rPtr->next;
+ 		}
+ 		lPtr = (memQueueNode *) lPtr->next;
+ 	}
+ }
+ void
+ initializeMemQueue(memQueue * Q)
+ {
+ 	_initializeMemQueue(Q, MINIMUM_ALLOCATED_NUM_BYTES);
+ }
+ float
+ numPagesInMemQueue(memQueue * Q)
+ {
+ 	return ((float) Q->sizeInBytes / BLCKSZ);
+ }
+ int
+ ceilNumPagesInMemQueue(memQueue * Q)
+ {
+ 	int			floor = ((int) (Q->sizeInBytes / BLCKSZ));
+ 
+ 	if (floor > (int) floor)
+ 		return ((int) floor + 1);
+ 	else
+ 		return (int) floor;
+ }
+ int
+ floorNumPagesInMemQueue(memQueue * Q)
+ {
+ 	return ((int) (Q->sizeInBytes / BLCKSZ));
+ }
+ 
+ /*
+  * #define NO_MERGE				   0
+  * #define LEFT_CONTAINED_RIGHT    1
+  * #define RIGHT_CONTAINED_LEFT    2
+  * #define MERGED_INTO_NEW_TSET    3
+  * in all cases of contained or merged. update the status vars
+  * visited, toVisit, relsOfTuples to the
+   */
+ int
+ mergeTwoTSets(alg_fctx * fctx, TSet ltset, Datum *ltsetVals, char *ltsetNulls,
+ 			  bool *ltsetDeformed, RBITS lrelsOfTuples, RBITS ltoVisit, RBITS lvisited, TSet rtset,
+ 			  RBITS rrelsOfTuples, RBITS rtoVisit, RBITS rvisited,
+ 	 TSet * mtset, RBITS * mrelsOfTuples, RBITS * mtoVisit, RBITS * mvisited)
+ {
+ 	bytea	   *ltids = ltset->tids;
+ 	bytea	   *rtids = rtset->tids;
+ 	RBITS		intersection,
+ 				tmp,
+ 				tmp2;
+ 
+ 	*ltsetDeformed = true;
+ 	bool		attemptJCCUnify = false;
+ 	TSet		unifyJCCResult;
+ 	int			j = 0;
+ 	RBITS		ltsetBlocked = ((~lrelsOfTuples) & lvisited);
+ 	RBITS		rtsetBlocked = ((~rrelsOfTuples) & rvisited);
+ 
+ 	/*
+ 	 * if there are tuples in one tuple set that were blocked(visited but no
+ 	 * tuple) then surely they CANNOT merge. Note, its first because it must
+ 	 * be done in order. i used a logic that depends on the ORDER ORDER ORDER
+ 	 * ORDER!
+ 	 */
+ 	if ((lrelsOfTuples & rtsetBlocked) || (rrelsOfTuples & ltsetBlocked));
+ 
+ 	/*
+ 	 * Lets check if there is an intersection of relations between the tuple
+ 	 * sets. If there is an intersection, lets compare the rowids.
+ 	 */
+ 	else if ((intersection = (lrelsOfTuples & rrelsOfTuples)))
+ 	{
+ 		int			leftPos = 0;
+ 		int			leftRelOid = 0;
+ 		int			rightRelOid = 0;
+ 		int			numRightTuples = numOfTuplesInTSet(fctx, rtids);
+ 
+ 		leftRelOid = getTableIdOfRelsOidInTSet(fctx, ltids, leftPos);
+ 		for (j = 0; j < numRightTuples; j++)
+ 		{
+ 			rightRelOid = getTableIdOfRelsOidInTSet(fctx, rtids, j);
+ 			if (varNthBitFromLeft(intersection, rightRelOid))
+ 			{
+ 				while (leftRelOid != rightRelOid)
+ 				{
+ 					leftPos++;
+ 					leftRelOid = getTableIdOfRelsOidInTSet(fctx, ltids, leftPos);
+ 				}
+ 				if (memcmp(VARDATA(ltids) + leftPos * (1 + sizeof(ItemPointerData))
+ 						 ,VARDATA(rtids) + j * (1 + sizeof(ItemPointerData)),
+ 						   1 + (sizeof(ItemPointerData))))
+ 				{
+ 					break;
+ 				}
+ 			}
+ 			tmp = (~(0x0));
+ 			if (!(intersection & (tmp >> (rightRelOid + 1))))
+ 			{
+ 				j = numRightTuples;
+ 				break;
+ 			}
+ 		}
+ 		if (j >= numRightTuples)
+ 		{
+ 			/*
+ 			 * if the left side is contained in right side then don't do
+ 			 * anything but consider it merged. So lets check if one contains
+ 			 * the other:
+ 			 */
+ 			if ((rrelsOfTuples | lrelsOfTuples) == rrelsOfTuples)
+ 			{
+ 				*mrelsOfTuples = rrelsOfTuples;
+ 				*mvisited = (~(rrelsOfTuples)) & (rvisited | lvisited);
+ 				*mtoVisit = (~(rrelsOfTuples)) & (rtoVisit | ltoVisit)
+ 					& (~(rvisited | lvisited));
+ 				return RIGHT_CONTAINED_LEFT;
+ 			}
+ 			else if ((rrelsOfTuples | lrelsOfTuples) == lrelsOfTuples)
+ 			{
+ 				/* replace ltset with the rtset. */
+ 				*mrelsOfTuples = lrelsOfTuples;
+ 				*mvisited = (~(lrelsOfTuples)) & (rvisited | lvisited);
+ 				*mtoVisit = (~(lrelsOfTuples)) & (rtoVisit | ltoVisit)
+ 					& (~(rvisited | lvisited));
+ 				return LEFT_CONTAINED_RIGHT;
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * Here we want to check if relations of tuples of one of the
+ 				 * tuple sets that are not in the intesection, are connected
+ 				 * to tuples of the other tuple set that are not in the
+ 				 * intersection. Because if so, we cannot use dumb unify and
+ 				 * we have to check also for JCC.
+ 				 */
+ 				j = 0;
+ 				tmp2 = 0;
+ 				/* tuples of rtset not in intersection */
+ 				tmp = (~intersection) & rrelsOfTuples;
+ 
+ 				/*
+ 				 * tmp2 contains relations that relations of tuples not in
+ 				 * intersection, are connected to.
+ 				 */
+ 				while (tmp)
+ 				{
+ 					if (varNthBitFromLeft(tmp, 0))
+ 						tmp2 |= (fctx->bits_scheme_graph[j]);
+ 					j++;
+ 					tmp <<= 1;
+ 				}
+ 
+ 				/*
+ 				 * we check if the tuples of rtset not in the intersections
+ 				 * are connected to tuples of ltset not in the intersection. f
+ 				 * not we continue, otherwise we will later attempt Unifying
+ 				 * with JCC(ELSE CLAUSE).
+ 				 */
+ 				if (!((~intersection) & lrelsOfTuples & tmp2))
+ 				{
+ 					j = 0;
+ 					tmp2 = 0;
+ 					/* tuples of ltset not in intersection. */
+ 					tmp = (~intersection) & lrelsOfTuples;
+ 					while (tmp)
+ 					{
+ 						if (varNthBitFromLeft(tmp, 0))
+ 							tmp2 |= (fctx->bits_scheme_graph[j]);
+ 						j++;
+ 						tmp <<= 1;
+ 					}
+ 
+ 					/*
+ 					 * we check if the tuples of ltset not in the
+ 					 * intersections are connected to tuples of rtset not in
+ 					 * the intersection. if not we will unify the tuple sets
+ 					 * without checks. otherwise we will proceed to unifying
+ 					 * with checks (ELSE CLAUSE).
+ 					 */
+ 					if (!((~intersection) & rrelsOfTuples & tmp2))
+ 					{
+ 						unifyJCCResult = simpleUnify(fctx, ltset, ltsetVals, ltsetNulls, rtset);
+ 						*mtset = unifyJCCResult;
+ 						*mrelsOfTuples = lrelsOfTuples | rrelsOfTuples;
+ 						*mvisited = (~(rrelsOfTuples)) & (rvisited | lvisited);
+ 						*mtoVisit = (~(rrelsOfTuples)) & (rtoVisit | ltoVisit)
+ 							& (~(rvisited | lvisited));
+ 						return MERGED_INTO_NEW_TSET;
+ 					}
+ 					else
+ 						attemptJCCUnify = true;
+ 				}
+ 				else
+ 					attemptJCCUnify = true;
+ 			}
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Otherwise, there was no intersection of relations:
+ 	 * ------------------------------ Here we CANNOT merge if all the
+ 	 * relations, either tuple set wants to visit, were blocked in the other
+ 	 * tuple set. Note there can be lack of information so blocked only
+ 	 * reflect the knowledge of visiting if we did try to visit.
+ 	 */
+ 	else if ((!(ltoVisit & (~rtsetBlocked))) && (!(rtoVisit & (~ltsetBlocked))));
+ 
+ 	/*
+ 	 * We will only try to unify if both tuple sets needs to visit a tuple in
+ 	 * the other tuple set.
+ 	 */
+ 	else if ((ltoVisit & rrelsOfTuples) || (rtoVisit & lrelsOfTuples))
+ 		attemptJCCUnify = true;
+ 	if (attemptJCCUnify)
+ 	{
+ 		if ((unifyJCCResult =
+ 			 unifyAndJCC(fctx, ltset, ltsetVals, ltsetNulls, rtset, ((~intersection)
+ 				& ((lrelsOfTuples & rtoVisit) | (rrelsOfTuples & ltoVisit))),
+ 						 lrelsOfTuples, rrelsOfTuples)) != NULL)
+ 		{
+ 			*mtset = unifyJCCResult;
+ 			*mrelsOfTuples = lrelsOfTuples | rrelsOfTuples;
+ 			*mvisited = (~(rrelsOfTuples)) & (rvisited | lvisited);
+ 			*mtoVisit = (~(rrelsOfTuples)) & (rtoVisit | ltoVisit)
+ 				& (~(rvisited | lvisited));
+ 			return MERGED_INTO_NEW_TSET;
+ 		}
+ 	}
+ 	return NO_MERGE;
+ }
+ bool
+ mergeOutOfMemQueueIntoNode(alg_fctx * fctx, memQueue * Q, memQueueNode * node,
+ 						   bool exitAtFirstMerge, bool assumeQueueMerged)
+ {
+ 	RBITS		relsOfTuples = node->relsOfTuples;
+ 	RBITS		toVisit = node->toVisit;
+ 	RBITS		visited = node->visited;
+ 	Datum	   *tsetVals = node->tset->v;
+ 	char	   *tsetNulls = node->tset->n;
+ 	bool		deformedTSet = false;
+ 	int			result;
+ 	RBITS		mrelsOfTuples,
+ 				mtoVisit,
+ 				mvisited;
+ 	TSet		mtset = NULL;
+ 	TSet		tset = node->tset;
+ 	memQueueNode *ptr = Q->head;
+ 	memQueueNode *prevPtr = ptr;
+ 	bool		wasMerging = false;
+ 
+ 	while (ptr != NULL)
+ 	{
+ 		if ((result = mergeTwoTSets(fctx, tset, tsetVals, tsetNulls,
+ 					&deformedTSet, relsOfTuples, toVisit, visited, ptr->tset,
+ 							   ptr->relsOfTuples, ptr->toVisit, ptr->visited,
+ 							  &mtset, &mrelsOfTuples, &mtoVisit, &mvisited)))
+ 		{
+ 			switch (result)
+ 			{
+ 				case LEFT_CONTAINED_RIGHT:
+ 					removeMemQueueNode(Q, prevPtr, ptr);
+ 					wasMerging = true;
+ 					relsOfTuples = mrelsOfTuples;
+ 					toVisit = mtoVisit;
+ 					visited = mvisited;
+ 					break;
+ 				case RIGHT_CONTAINED_LEFT:
+ 					detachNodeFromMemQueueNode(prevPtr, ptr, (Q));
+ 					freeTSet(fctx, tset);
+ 					node->tset = ptr->tset;
+ 					tset = ptr->tset;
+ 					deformedTSet = false;
+ 					wasMerging = true;
+ 					relsOfTuples = ptr->relsOfTuples;
+ 					toVisit = ptr->toVisit;
+ 					visited = ptr->visited;
+ 					pfree(ptr);
+ 					ptr = prevPtr;
+ 					break;
+ 				case MERGED_INTO_NEW_TSET:
+ 					removeMemQueueNode(Q, prevPtr, ptr);
+ 					node->tset = mtset;
+ 					tset = mtset;
+ 					deformedTSet = false;
+ 					wasMerging = true;
+ 					relsOfTuples = mrelsOfTuples;
+ 					toVisit = mtoVisit;
+ 					visited = mvisited;
+ 			}
+ 		}
+ 		if ((exitAtFirstMerge && wasMerging) || (assumeQueueMerged && (result == RIGHT_CONTAINED_LEFT)))
+ 			break;
+ 		getMemQueueNextElementAfterRemovalPtr(prevPtr, ptr, Q);
+ 	}
+ 	node->relsOfTuples = relsOfTuples;
+ 	node->toVisit = toVisit;
+ 	node->visited = visited;
+ 	return wasMerging;
+ }
+ void
+ addTupleSetToMemQueue(alg_fctx * fctx, memQueue * Q, TSet tset)
+ {
+ 	RBITS		relsOfTuples = 0;
+ 	RBITS		toVisit = 0;
+ 	bytea	   *b = tset->tids;
+ 	int			j,
+ 				relID;
+ 	int			numTuples = numOfTuplesInTSet(fctx, b);
+ 
+ 	for (j = 0; j < numTuples; j++)
+ 	{
+ 		relID = getTableIdOfRelsOidInTSet(fctx, b, j);
+ 		varSetNthBitFromLeft(relsOfTuples, relID);
+ 	}
+ 	for (j = 0; j < fctx->numRels; j++)
+ 		if ((varNthBitFromLeft(relsOfTuples, j)))
+ 			toVisit |= ((~relsOfTuples) & (fctx->bits_scheme_graph[j]));
+ 	memQueueNode *newNode = palloc(sizeof(memQueueNode));
+ 
+ 	newNode->tset = tset;
+ 	newNode->relsOfTuples = relsOfTuples;
+ 	newNode->toVisit = toVisit;
+ 	newNode->visited = 0;
+ 	newNode->next = NULL;
+ 	if (Q->head == NULL)
+ 		Q->head = newNode;
+ 	if (Q->tail != NULL)
+ 		Q->tail->next = (struct memQueueNode *) newNode;
+ 	Q->tail = newNode;
+ 	Q->numElements++;
+ 	Q->sizeInBytes += tset->size;
+ }
+ bool
+ removeLeftMemQueueNodesSubsumedInRightTSet(alg_fctx * fctx, memQueue * Q, TSet tset)
+ {
+ 	memQueueNode *ptr = Q->head;
+ 	memQueueNode *prev = ptr;
+ 	bool		wasRemoval = false;
+ 
+ 	while (ptr != NULL)
+ 	{
+ 		if (isLeftSubsumedRightTset(fctx, ptr->tset, tset))
+ 		{
+ 			wasRemoval = true;
+ 			removeMemQueueNode(Q, prev, ptr);
+ 		}
+ 		getMemQueueNextElementAfterRemovalPtr(prev, ptr, Q);
+ 	}
+ 	return wasRemoval;
+ }
+ void
+ removeLeftMemQueueNodesSubsumedInRightMemQueueNodes(alg_fctx * fctx, memQueue * QLeft,
+ 													memQueue * QRight)
+ {
+ 	if (QLeft == QRight)
+ 		elog(ERROR, "Not protected against self, use mergeMemQueueWithSelf instead");
+ 	memQueueNode *ptr = QRight->head;
+ 
+ 	while (ptr != NULL)
+ 	{
+ 		removeLeftMemQueueNodesSubsumedInRightTSet(fctx, QLeft, ptr->tset);
+ 		ptr = (memQueueNode *) ptr->next;
+ 	}
+ }
+ void
+ clearMemQueue(alg_fctx * fctx, memQueue * Q, bool free_tsets)
+ {
+ 	memQueueNode *ptr = Q->head;
+ 	memQueueNode *prevPtr = ptr;
+ 
+ 	while (ptr != NULL)
+ 	{
+ 		if (free_tsets)
+ 			freeTSet(fctx, ptr->tset);
+ 		prevPtr = ptr;
+ 		ptr = (memQueueNode *) ptr->next;
+ 		pfree(prevPtr);
+ 	}
+ 	Q->tail = NULL;
+ 	Q->head = NULL;
+ 	Q->numElements = 0;
+ 	Q->sizeInBytes = 0;
+ }
+ void
+ SQInitialize(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q)
+ {
+ 	MemoryContext oldcontext = MemoryContextSwitchTo(fctx->per_query_ctx);
+ 
+ 	Q->tuplestorage = tuplestore_begin_heap(false, false, (int) (BLCKSZ / 1024));
+ #ifdef PG_MODULE_MAGIC
+ 	Q->slot = MakeSingleTupleTableSlot(fctx->tupleSetDesc);
+ #endif   /* */
+ 	MemoryContextSwitchTo(oldcontext);
+ 	Q->numElements = 0;
+ 	Q->sizeInBytes = 0;
+ 	Q->lastReadPosition = 0;
+ 	Q->markedLastReadPosition = -1;
+ 	Q->markedNumElements = -1;
+ 	Q->markedSizeInBytes = -1;
+ 	Q->markedMemOverflowTuple = NULL;
+ 	Q->memOverflowTuple = NULL;
+ }
+ void
+ SQFinish(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q)
+ {
+ 	MemoryContext oldcontext = MemoryContextSwitchTo(fctx->per_query_ctx);
+ 
+ 	if (Q->tuplestorage != NULL)
+ 		tuplestore_end(Q->tuplestorage);
+ 	MemoryContextSwitchTo(oldcontext);
+ 	Q->tuplestorage = NULL;
+ 	Q->numElements = 0;
+ 	Q->sizeInBytes = 0;
+ 	Q->markedLastReadPosition = -1;
+ 	Q->markedNumElements = -1;
+ 	Q->markedSizeInBytes = -1;
+ 	Q->markedMemOverflowTuple = NULL;
+ 	Q->lastReadPosition = 0;
+ 	if (Q->memOverflowTuple != NULL)
+ 		heap_freetuple(Q->memOverflowTuple);
+ 	if (Q->markedMemOverflowTuple != NULL)
+ 		heap_freetuple(Q->markedMemOverflowTuple);
+ }
+ 
+ /*
+  * the caller must return the tuple he took out.
+   */
+ void
+ SQSetMemOverflowTuple(storageQueue * Q, HeapTuple tuple)
+ {
+ 	Q->memOverflowTuple = tuple;
+ 	Q->lastReadPosition--;
+ 	Q->sizeInBytes += tupleSize(tuple);
+ }
+ void
+ SQAddElement(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q, HeapTuple tuple)
+ {
+ 	MemoryContext oldcontext = MemoryContextSwitchTo(fctx->per_query_ctx);
+ 
+ 	tuplestore_puttuple(Q->tuplestorage, tuple);
+ 	MemoryContextSwitchTo(oldcontext);
+ 	Q->numElements++;
+ 	Q->sizeInBytes += tupleSize(tuple);
+ }
+ HeapTuple
+ SQGetNextElement(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q, bool *should_free,
+ 				 bool copytuple)
+ {
+ 	Q->lastReadPosition++;
+ 	HeapTuple	tmpTuple = NULL;
+ 
+ 	if (Q->memOverflowTuple != NULL)
+ 	{
+ 		*should_free = true;
+ 		tmpTuple = Q->memOverflowTuple;
+ 		Q->memOverflowTuple = NULL;
+ 		Q->sizeInBytes -= tupleSize(tmpTuple);
+ 		return tmpTuple;
+ 	}
+ 	MemoryContext oldcontext = MemoryContextSwitchTo(fctx->per_query_ctx);
+ 	HeapTuple	tuple = NULL;
+ 
+ #ifdef PG_MODULE_MAGIC
+ 	ExecClearTuple(Q->slot);
+ 	if (tuplestore_gettupleslot(Q->tuplestorage, true, Q->slot))
+ 	{
+ 		tuple = ExecFetchSlotTuple(Q->slot);
+ 	}
+ 	else
+ 		elog(ERROR, "In SQGetNextElement unexpected no tuple returned. we do not check here for end of queue");
+ #else							/* */
+ 	tuple = tuplestore_gettuple(Q->tuplestorage, true, should_free);
+ #endif   /* */
+ 	MemoryContextSwitchTo(oldcontext);
+ 	if (copytuple)
+ 	{
+ 		tmpTuple = (HeapTuple) heap_copytuple(tuple);
+ #ifndef PG_MODULE_MAGIC
+ 		if (*should_free)
+ 			heap_freetuple(tuple);
+ #endif   /* */
+ 		*should_free = true;
+ 		Q->sizeInBytes -= tupleSize(tmpTuple);
+ 		return tmpTuple;
+ 	}
+ 	else
+ 	{
+ 		Q->sizeInBytes -= tupleSize(tuple);
+ 		return tuple;
+ 	}
+ }
+ void
+ SQRewindToFirstElement(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q)
+ {
+ 	MemoryContext oldcontext = MemoryContextSwitchTo(fctx->per_query_ctx);
+ 
+ 	tuplestore_rescan(Q->tuplestorage);
+ 	MemoryContextSwitchTo(oldcontext);
+ 	Q->lastReadPosition = 0;
+ }
+ void
+ SQMarkPosition(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q)
+ {
+ 	MemoryContext oldcontext = MemoryContextSwitchTo(fctx->per_query_ctx);
+ 
+ 	tuplestore_markpos(Q->tuplestorage);
+ 	MemoryContextSwitchTo(oldcontext);
+ 	Q->markedLastReadPosition = Q->lastReadPosition;
+ 	Q->markedNumElements = Q->numElements;
+ 	Q->markedSizeInBytes = Q->sizeInBytes;
+ 	if (Q->memOverflowTuple != NULL)
+ 		Q->markedMemOverflowTuple = heap_copytuple(Q->memOverflowTuple);
+ 	else
+ 		Q->markedMemOverflowTuple = NULL;
+ }
+ void
+ SQRestorePosition(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q)
+ {
+ 	MemoryContext oldcontext = MemoryContextSwitchTo(fctx->per_query_ctx);
+ 
+ 	tuplestore_restorepos(Q->tuplestorage);
+ 	MemoryContextSwitchTo(oldcontext);
+ 	Q->lastReadPosition = Q->markedLastReadPosition;
+ 	Q->numElements = Q->markedNumElements;
+ 	Q->sizeInBytes = Q->markedSizeInBytes;
+ 	if (Q->memOverflowTuple)
+ 	{
+ 		heap_freetuple(Q->memOverflowTuple);
+ 		Q->memOverflowTuple = NULL;
+ 	}
+ 	if (Q->markedMemOverflowTuple != NULL)
+ 		Q->memOverflowTuple = Q->markedMemOverflowTuple;
+ 	Q->markedLastReadPosition = -1;
+ 	Q->markedNumElements = -1;
+ 	Q->markedSizeInBytes = -1;
+ 	Q->markedMemOverflowTuple = NULL;
+ }
diff -crN pgsql/contrib/fulldisjunctions/queuesfuncs.h pgsql-fd/contrib/fulldisjunctions/queuesfuncs.h
*** pgsql/contrib/fulldisjunctions/queuesfuncs.h	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/queuesfuncs.h	2006-07-29 20:54:13.000000000 -0400
***************
*** 0 ****
--- 1,177 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #ifndef QUEUESFUNCS_H
+ #define QUEUESFUNCS_H
+ 
+ #ifdef DEBUG
+ #else
+ #define NDEBUG 
+ #endif
+ 
+ #include <assert.h>
+ 
+ #define MINIMUM_ALLOCATED_NUM_PAGES 2
+ #define MINIMUM_ALLOCATED_NUM_BYTES (MINIMUM_ALLOCATED_NUM_PAGES*BLCKSZ)
+ #define bitsInVariable 32
+ #define firstBitFromLeftInHex 0x80000000
+ #define firstBitFromLeftInHex 0x80000000
+ #define NO_MERGE				0
+ #define LEFT_CONTAINED_RIGHT	1
+ #define RIGHT_CONTAINED_LEFT	2
+ #define MERGED_INTO_NEW_TSET	3
+ #include "utils/palloc.h"
+ #include "queues.h"
+ #include "algstructs.h"
+  /* n is starting from zero! */
+ #define nthBitFromLeft(n)\
+ (firstBitFromLeftInHex >> n)
+ #define varNthBitFromLeft(var,n)\
+ ((var) & (nthBitFromLeft(n)))
+ #define varSetNthBitFromLeft(var,n)\
+ (var = (var) | (nthBitFromLeft(n)))
+ #define varUnSetNthBitFromLeft(var,n)\
+ (var = (var) & (~(nthBitFromLeft(n))))
+ #define replaceNodeTSet(fctx,node,retset)\
+ do\
+ {\
+ 	freeTSet(fctx, (node)->tset);\
+ 		(node)->tset = retset;\
+ } while (0)
+ 
+ #define freeMemQueueNode(node)\
+ do\
+ {\
+ 	freeTSet(fctx, node->tset);	\
+ 		pfree(node);\
+ 		node = NULL;\
+ } while (0)
+ 
+ 	/* no queue size modifications */
+ #define addNodeToMemQueue(node,Q) \
+ do\
+ {\
+ 	if ((Q)->head == NULL)\
+ 		(Q)->head = (node); \
+ 		else\
+ 		(Q)->tail->next = (struct memQueueNode *) (node);\
+ 		(node)->next = NULL;\
+ 		(Q)->numElements++;\
+ 		(Q)->sizeInBytes += (node)->tset->size;\
+ 		(Q)->tail = (node);\
+ 	} while (0)
+ 
+ /*node is modified to detached node.
+    no queue size modifications*/
+ #define detachHeadNodeFromMemQueue(node,Q)\
+ do\
+ 	{\
+ 		if ((Q)->head != NULL)\
+ 		{\
+ 			node = (Q)->head;\
+ 				(Q)->head = (memQueueNode *) node->next;\
+ 				(Q)->numElements--;	\
+ 				(Q)->sizeInBytes -= (node)->tset->size;	\
+ 				if ((Q)->head == NULL)\
+ 				(Q)->tail = NULL;\
+ 		} \
+ 			else\
+ 			node = NULL;\
+ 	} while (0)
+ 
+ 	/* no queue size modifications */
+ #define detachNodeFromMemQueueNode(prevNode,node,Q)\
+ do\
+ {\
+ 	if ((node) == (prevNode))\
+ 	{\
+ 			if ((prevNode) == (Q)->head)\
+ 				(Q)->head = (memQueueNode *) (prevNode)->next;\
+ 			if ((prevNode) == (Q)->tail)\
+ 				(Q)->tail = NULL;\
+ 			(prevNode) = NULL;\
+ 	}\
+ 	else\
+ 	{\
+ 			(prevNode)->next = (node)->next;\
+ 			if ((node) == (Q)->tail)\
+ 				(Q)->tail = (prevNode);\
+ 	} \
+ 		(Q)->numElements--;\
+ 		(Q)->sizeInBytes -= (node)->tset->size;	\
+ } while (0)
+ 
+ #define removeMemQueueNode(Q, prevPtr, ptr)\
+ do\
+ {\
+ 	detachNodeFromMemQueueNode((prevPtr), (ptr), (Q));\
+ 		freeMemQueueNode(ptr);\
+ 		(ptr) = (prevPtr);\
+ } while (0)
+ 
+ #define getMemQueueNextElementAfterRemovalPtr(prevNodePtr,nodePtr,Q)\
+ do\
+ {\
+ 	if (!prevNodePtr)\
+ 	{\
+ 			nodePtr = getMemQueueFirstElementPtr((Q));\
+ 			prevNodePtr = nodePtr;\
+ 	}\
+ 	else if (nodePtr)\
+ 	{\
+ 			prevNodePtr = nodePtr;\
+ 			nodePtr = getMemQueueNextElementPtr(nodePtr);\
+ 	}\
+ } while (0)
+ 
+ #define getMemQueueElement(ptr)\
+ (ptr->tset)
+ #define getMemQueueNextElementPtr(ptr)\
+ ((memQueueNode *) (ptr->next))
+ #define getMemQueueFirstElementPtr(Q)\
+ ((Q)->head)
+ #define getMemQueueLastElementPtr(Q)\
+ ((Q)->tail)
+ #define getMemQueueNumElements(Q)\
+ ((Q)->numElements)
+ #define getMemQueueSize(Q)\
+ ((Q)->sizeInBytes)
+ #define isMemQueueEmpty(Q)\
+ (((Q)->numElements) <= 0)
+ #define SQGetNumElements(Q)\
+ ((Q)->numElements)
+ #define numPagesInSQ(Q)\
+ ((Q)->sizeInBytes / BLCKSZ)
+ #define SQEof(Q)\
+ ((Q)->numElements <= (Q)->lastReadPosition)
+ #define SQNotEmpty(Q)\
+ ((Q)->numElements > (Q)->lastReadPosition)
+ 	extern void _initializeMemQueue(memQueue * Q, int allocatedBytes);
+ extern void removeDuplicatesMemQueueWithSelf(alg_fctx * fctx, memQueue * Q);
+ extern void removeSubsumptionsMemQueueWithSelf(alg_fctx * fctx, memQueue * Q);
+ extern void initializeMemQueue(memQueue * Q);
+ extern float numPagesInMemQueue(memQueue * Q);
+ extern int	floorNumPagesInMemQueue(memQueue * Q);
+ extern int	ceilNumPagesInMemQueue(memQueue * Q);
+ extern bool mergeOutOfMemQueueIntoNode(alg_fctx * fctx, memQueue * Q, memQueueNode * node,
+ 						   bool exitAtFirstMerge, bool assumeQueueMerged);
+ extern void addTupleSetToMemQueue(alg_fctx * fctx, memQueue * Q, TSet tset);
+ extern bool removeLeftMemQueueNodesSubsumedInRightTSet(alg_fctx * fctx
+ 										   ,memQueue * Q, TSet tset);
+ extern void removeLeftMemQueueNodesSubsumedInRightMemQueueNodes(alg_fctx * fctx
+ 									   ,memQueue * QLeft, memQueue * QRight);
+ extern void clearMemQueue(alg_fctx * fctx, memQueue * Q, bool free_tsets);
+ extern void SQInitialize(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q);
+ extern void SQFinish(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q);
+ extern void SQSetMemOverflowTuple(storageQueue * Q, HeapTuple tuple);
+ extern void SQAddElement(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q, HeapTuple tuple);
+ extern HeapTuple SQGetNextElement(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q, bool *should_free,
+ 				 bool copytuple);
+ extern void SQRewindToFirstElement(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q);
+ extern void SQMarkPosition(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q);
+ extern void SQRestorePosition(alg_fctx * fctx, FuncCallContext *funcctx, storageQueue * Q);
+ 
+ #endif   /* QUEUESFUNCS_H */
diff -crN pgsql/contrib/fulldisjunctions/queues.h pgsql-fd/contrib/fulldisjunctions/queues.h
*** pgsql/contrib/fulldisjunctions/queues.h	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/queues.h	2006-07-30 15:27:12.000000000 -0400
***************
*** 0 ****
--- 1,66 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #ifndef QUEUES_H
+ #define QUEUES_H
+ 
+ #ifdef DEBUG
+ #else
+ #define NDEBUG 
+ #endif
+ 
+ #include <assert.h>
+ 
+ #include "postgres.h"
+ #include "tset.h"
+ 
+ #ifndef INFINITY
+ #define INFINITY INT_MAX
+ #endif
+ 
+ typedef unsigned int RBITS;
+ typedef struct
+ {
+ 	TSet		tset;
+ 	unsigned long visited;
+ 	unsigned long relsOfTuples;
+ 	unsigned long toVisit;
+ 	struct memQueueNode *next;
+ }	memQueueNode;
+ typedef struct
+ {
+ 	memQueueNode *head;
+ 	memQueueNode *tail;
+ 	int			numElements;
+ 	int			sizeInBytes;
+ 	int			allocatedBytes;
+ }	memQueue;
+ typedef struct
+ {
+ 	Tuplestorestate *tuplestorage;
+ 	int			numElements;
+ 	int			lastReadPosition;
+ 	int			sizeInBytes;
+ 	int			markedLastReadPosition;
+ 	int			markedNumElements;
+ 	int			markedSizeInBytes;
+ 	HeapTuple	markedMemOverflowTuple;
+ 
+ 	/*
+ 	 * we pulled it out of storage but couldn't hold it memory. thus we leave
+ 	 * it here. potentially i can read half tuples and thus never overflow,
+ 	 * however, since i don't want to implement the tuplestore again, i
+ 	 * simulate this by leaving this tuple in the storage structure. the next
+ 	 * ead will be this tuple. in case of an overflow we do not increment the
+ 	 * lastReadPosition.
+ 	 */
+ 	HeapTuple	memOverflowTuple;
+ #ifdef PG_MODULE_MAGIC
+ 	TupleTableSlot *slot;
+ #endif   /* */
+ }	storageQueue;
+ 
+ #endif   /* QUEUES_H */
diff -crN pgsql/contrib/fulldisjunctions/README.fulldisjunctions pgsql-fd/contrib/fulldisjunctions/README.fulldisjunctions
*** pgsql/contrib/fulldisjunctions/README.fulldisjunctions	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/README.fulldisjunctions	2006-07-30 16:25:31.000000000 -0400
***************
*** 0 ****
--- 1,220 ----
+ ***************************************************************************
+ Copyright (c) 2006 Itzhak Fadida
+ All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ 
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with the
+    distribution.
+  * Neither the name Itzhak Fadida nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ***************************************************************************
+ 
+ Full Disjunctions
+ 
+ License
+ 
+     BSD
+ 
+ Short Description
+     The full-disjunction operation is a variation of the join operator that
+     maximally combines tuples from connected relations, while preserving all
+     information in the relations. Full disjunctions can be very useful for
+     integrating heterogeneous data sources (e.g., web sources) since such data
+     sources are often incomplete. Under certain conditions full disjunctions
+     can be expressed using a series of natural outer joins. However, the
+     general case full disjunctions cannot be expressed using standard
+     relational operators, and thus, special algorithms are needed.
+     
+ Additional Details
+     Full Disjunctions (FD) is an SQL operator that has a top-down approach to
+     extracting the maximum information without duplication of information or
+     subsumptions (information of one tuple is contained in another tuple).
+     The FD operator generates information that cannot be expressed using
+     standard SQL statements (including natural full outer joins). 
+     
+     Example of an input and output of a full disjunctions:
+     INPUT:
+         --A---|---B---|---C--
+         X---Y-|-Y---Z-|-X---Z
+         a-|-b-|-b-|-c-|-a-|-d
+ 
+     A,B and C are relations. X,Y and Z are attributes. a,b,c and d are values.
+     Note that A,B and C are connected in a cycle. That is:
+     A is connected to B on attribute Y,
+     B is connected to C on attribute Z,
+     C is connected to A on attribute X.
+ 
+     The output of the full disjunctions FD(A,B,C):
+            FD
+         X---Y---Z
+         a-|-b-|-c
+         a-|-b-|-d
+ 
+     This output cannot be generated using standard SQL statements.
+     The closest operator is a natural full join and even it cannot
+     generate this output. Following is all the possible ways to use a
+     natural outer join (noj):
+     
+     (A noj B) noj C = C noj (A noj B):
+         X----Y-----Z
+         a-|-b----|-c
+         a-|-Null-|-d
+ 
+     (A noj C) noj B = B noj (A noj C):
+         X------Y---Z
+         a----|-b-|-d
+         Null-|-b-|-c
+ 
+     (B noj C) noj A = A noj (B noj C):
+         X------Y------Z
+         Null-|-b----|-c
+         a----|-Null-|-d
+         a----|-b----|-Null
+ 
+     As can be clearly seen, we get incomplete information.
+ 
+ 
+ Installation
+ 
+     Execute the following to use Full Disjunctions:
+ 
+     make
+     make install
+     make installcheck
+ 
+     After the Full Disjunction libraries are installed, you then need
+     to execute the following SQL script as a superuser: 
+ 
+     psql databasename -f fulldisjunctions.sql
+ 
+ Usage
+     
+     An example of a run:
+ 
+     The function getfdr will return a string containing the RECORD
+     which is the schema that will be returned.
+     "SELECT getfdr('relation1,relation2,relation3')"
+ 
+     Let STR be that string.
+ 
+     Replace <STR> with the actual string.
+     "SELECT * FROM fulldisjunction('relation1,relation2,relation3') AS RECORD <STR>"
+ 
+     You can run the following functions:
+     getfdr(<relations>) -   returns the schema that will be returned by the
+                             full disjunctions of the <relations>.
+                             NOTE that <relations> can be any number of connected 
+                             relations (currently limited to 32)
+                         
+     getfdr(<relations>[,mappings]) - returns the schema that will be returned by the
+                             full disjunctions of the <relations>. However, it is the common case
+                             that relations will not contain the same names of attribute that you know
+                             needs to be joined. For example, id and identification are the same attributes
+                             in different relations but the function cannot know that. To solve that, we
+                             provide a was, akeen to AS clause, to provide aliases for relations attributes.
+                             Mappings are used as follows: 
+                             "result_attribute_name=schema.relation.attribute_name, ..."
+                             Each pair is one alias. If you do not know what a schema is, use public,
+                             e.g. "id=public.people.identification,name=public.people.first_name".
+                             
+     fulldisjunction(<relations>) - returns a set of tuples that are the full disjunctions of the relations.
+ 
+     fulldisjunction(<relations>[,mappings[,noduplicates[,noindices[,policy[,numPages]]]]]) - same as fd above but 
+         with more control over the parameters used.
+         text    mappings     -  The same as described for getfdr. However, note that if you want
+                                 to use mappings and you want to use getfdr, you have to also use
+                                 the same mappings string in getfdr.
+         boolean noduplicates -  true will not remove duplicates from the source <relations> when
+                                 selecting them. false will remove. If you have a table with duplicates
+                                 (whole tuples that are repeated), not removing these tuples will mean
+                                 there will also be duplicates in the result of the full disjunction.
+                                 This is not an error since it complies with the definition of the 
+                                 full disjunction when working with tuple sets. If you will allow to
+                                 remove them (not physically, just selecting with no duplicates) than
+                                 there will not be duplicates in the results of the full disjunctions.
+         boolean noindices -     true will not use indices in the source relations. false will try
+                                 to use indices in the source relations where possible.
+         text    policy  -       three policies can be used when extending tuple sets which depends
+                                 on the schemes of the relations and their content, might improve
+                                 the overall runtime. policies that can be used: majority,mincost,random,
+                                 naive.
+         int     numPages -      Number of pages allocated for the algorithm. Theoretically, the more
+                                 the better, however there is a CPU dependense thus, there is a balance
+                                 depending on your hardware. Recommended to start with 5-10 pages before
+                                 experimenting with higher numbers.
+ 
+     Note that if you only want, for example, noindices, you have to include all those that came
+     before in the order. In this case, noduplicates.
+     
+     Example of using the detailed fd function:
+     SELECT * FROM fulldisjunction('relation1,relation2,relation3','attnew=public.relation1.oldatt',true,false,'majority',100)
+  
+     NOTE: If you do not want to supply a value in a text field you must 
+           NOT! use NULL, use the empty string ''.
+ 
+ Test Run:
+ 
+    Using the helper python script createTable.py, you can create tables to test the FD operator.
+    createTable.py <number of tuple> <interval from 0 to specified number to randomly draw numbers>
+                   <name of database> <name of table> <name of attribute 1> <name of attribute 2>
+                   
+    FD is only useful when there is a cycle in the scheme graph of the relations we are trying to compute
+    FD on. Therefore, we will create a cycle of (a0,a1) (a1,a2) (a2,a0).
+    Note that this cannot be computed in SQL other than using FD.
+     
+    python createTable.py 1000 5000 test t1 a0 a1
+    python createTable.py 1000 5000 test t2 a1 a2
+    python createTable.py 1000 5000 test t3 a2 a0
+ 
+    You can output the record by simply calling:
+         psql test -c "SELECT getfdr('t1,t2,t3')"
+     
+    Which will output:
+         (a0 int4,a1 int4,a2 int4)
+    
+    Now lets run the fd:
+         psql test -c "SELECT * FROM fulldisjunction('t1,t2,t3') AS RECORD (a0 int4,a1 int4,a2 int4)"
+ 
+    Expect a long run of about 15 seconds depending on your hardware and available cpu time.
+    As you can already see, it is recommended to use full disjunctions sparingly or on very small
+    tables. Usually small tables are derived from XML files or other web sources.
+    However, sometimes you have to do it on your own internal data, in which case design your applciation
+    to rarely compute full disjunctions or to materialize it, i.e., store the results in a table and refresh
+    the table periodically as required. You can use FD on large tables but expect it to run from hours to 
+    days (it was extensively tested for long runs) so it is ill advised to actually use FD in interactive
+    applications such as web over very large tables.
+ 
+ Limitation:
+ 
+    This implementation of the function is limited to 32 relations at the most because of efficiency reasons.
+    The number of relations supported can be easily extended by redefining RBITS and firstBitFromLeftInHex to
+    support 64 bits but have not been tested.
+ 
+ 
+ Developers Corner:
+ 
+  * For optimization purposes, merging was disabled in On Demand procedure as
+    opposed to textbook algorithm and instead i also remove duplicates after
+    the extending process. The reason is because much of the merging operations
+    is done in Extend anyway. This change does not break the issue of doing more
+    work than tuple-based algorithm because we are still merging in Extend.
diff -crN pgsql/contrib/fulldisjunctions/sql/fulldisjunctions.sql pgsql-fd/contrib/fulldisjunctions/sql/fulldisjunctions.sql
*** pgsql/contrib/fulldisjunctions/sql/fulldisjunctions.sql	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/sql/fulldisjunctions.sql	2006-07-30 16:20:37.000000000 -0400
***************
*** 0 ****
--- 1,3053 ----
+ --
+ --  Test Full Disjunctions
+ --
+ 
+ SET client_min_messages = warning;
+ \set ECHO none
+ \i fulldisjunctions.sql
+ SET client_encoding = 'UTF8';
+ SET standard_conforming_strings = off;
+ SET check_function_bodies = false;
+ SET client_min_messages = warning;
+ SET escape_string_warning = off;
+ SET search_path = public;
+ SET default_tablespace = '';
+ SET default_with_oids = false;
+ 
+ CREATE TABLE t1 (
+     a0 integer,
+     a1 integer
+ );
+ 
+ CREATE TABLE t2 (
+     a1 integer,
+     a2 integer
+ );
+ 
+ CREATE TABLE t3 (
+     a2 integer,
+     a0 integer
+ );
+ 
+ COPY t1 (a0, a1) FROM stdin;
+ 2951	4148
+ 3323	3959
+ 355	3937
+ 4819	2204
+ 1959	4943
+ 1446	3135
+ 1703	4294
+ 3763	2577
+ 3707	716
+ 574	3389
+ 3237	3988
+ 2742	2578
+ 4712	413
+ 4300	915
+ 2473	164
+ 605	2403
+ 2335	4035
+ 2420	4590
+ 416	3895
+ 995	485
+ 53	1550
+ 1107	3794
+ 1236	4172
+ 472	4178
+ 1624	2684
+ 4306	431
+ 67	4855
+ 2515	1841
+ 3632	125
+ 3282	2161
+ 3936	2562
+ 3748	237
+ 26	1055
+ 2339	3634
+ 4486	98
+ 512	2853
+ 3385	1775
+ 782	1166
+ 488	4710
+ 1434	1126
+ 4680	1850
+ 1315	2584
+ 2378	4094
+ 4847	4632
+ 1609	2516
+ 3515	2168
+ 1489	4832
+ 3368	2656
+ 4416	2797
+ 4302	4365
+ 807	2647
+ 2240	2375
+ 2165	17
+ 1692	2695
+ 3480	1067
+ 1678	247
+ 3513	2684
+ 1056	22
+ 264	728
+ 1791	872
+ 273	4489
+ 2577	501
+ 1151	1811
+ 4701	980
+ 1532	1129
+ 4836	2512
+ 2526	2763
+ 2674	4285
+ 361	171
+ 4618	4187
+ 3930	1354
+ 3049	312
+ 678	4399
+ 148	814
+ 4795	1643
+ 1359	1755
+ 688	3371
+ 1590	1858
+ 771	3564
+ 962	3666
+ 2860	2893
+ 1124	3663
+ 4175	4789
+ 1877	3650
+ 1394	1956
+ 2482	3706
+ 4137	3645
+ 4332	2756
+ 3687	1823
+ 2302	3717
+ 954	2032
+ 389	279
+ 2335	3711
+ 847	1425
+ 4184	2057
+ 4286	1711
+ 4411	21
+ 3537	3901
+ 4142	423
+ 1274	3209
+ 1084	4706
+ 2633	30
+ 4077	3091
+ 4147	3578
+ 4713	4524
+ 1977	1917
+ 4204	2917
+ 254	3450
+ 1889	1430
+ 774	1064
+ 563	3110
+ 2642	1551
+ 3202	2731
+ 3728	3299
+ 1368	4976
+ 3773	1610
+ 1638	3317
+ 1580	4742
+ 1816	1721
+ 4985	1787
+ 4183	4250
+ 4364	330
+ 518	2316
+ 3174	1743
+ 19	1227
+ 4601	4721
+ 131	3631
+ 3456	2511
+ 4647	332
+ 2863	1667
+ 4685	2499
+ 2601	3255
+ 4623	4124
+ 3629	1006
+ 526	1882
+ 3603	4620
+ 1419	3931
+ 4815	3171
+ 783	2905
+ 4442	368
+ 3494	568
+ 1495	4851
+ 4138	1157
+ 4312	584
+ 496	1418
+ 912	1798
+ 1371	3457
+ 2917	1586
+ 4199	3217
+ 1330	2512
+ 2351	4880
+ 2295	3008
+ 4112	3418
+ 330	3073
+ 3550	3908
+ 4779	1489
+ 4443	1673
+ 1291	4883
+ 1272	63
+ 2595	3343
+ 3967	1094
+ 540	332
+ 1910	4951
+ 1446	2685
+ 3844	1128
+ 986	2016
+ 165	492
+ 4442	1285
+ 1370	2338
+ 2408	3913
+ 3097	4429
+ 274	2207
+ 2822	4748
+ 3585	1568
+ 4664	1780
+ 1712	242
+ 3879	2015
+ 2627	4623
+ 4253	732
+ 19	4541
+ 3339	2992
+ 3745	1329
+ 1200	1174
+ 1039	934
+ 1947	4742
+ 4960	3486
+ 1547	4207
+ 4956	3980
+ 4120	2154
+ 4595	3848
+ 4947	4703
+ 722	1965
+ 639	3825
+ 1488	4926
+ 4817	4928
+ 3199	3813
+ 1238	879
+ 668	4406
+ 2991	417
+ 3822	2299
+ 354	2146
+ 250	2407
+ 4864	4771
+ 1913	1398
+ 2024	2123
+ 4694	744
+ 4033	610
+ 808	4483
+ 4322	4572
+ 5000	4014
+ 1548	569
+ 3281	3104
+ 4214	559
+ 397	2544
+ 1071	1739
+ 4188	4754
+ 891	518
+ 304	847
+ 2745	309
+ 2001	141
+ 824	780
+ 3419	1788
+ 1146	4620
+ 1463	3801
+ 3983	1036
+ 1685	1146
+ 4172	123
+ 3003	2904
+ 3328	701
+ 3929	4708
+ 1524	1662
+ 772	1498
+ 1075	108
+ 1395	726
+ 1505	1450
+ 3267	1191
+ 4749	3092
+ 1701	1698
+ 1753	4490
+ 730	4886
+ 4081	4510
+ 4542	3010
+ 4515	3220
+ 4780	4358
+ 1351	2898
+ 114	1799
+ 1498	3975
+ 2263	1742
+ 4779	4334
+ 4198	3670
+ 790	4747
+ 3126	2204
+ 2577	4400
+ 1214	685
+ 708	3072
+ 3756	3477
+ 3541	1241
+ 849	3560
+ 924	2724
+ 942	1941
+ 2049	3447
+ 3325	2185
+ 3846	3073
+ 3298	4094
+ 1102	2797
+ 2979	4200
+ 4075	3105
+ 1995	2772
+ 1713	4021
+ 332	747
+ 3056	4276
+ 1700	4725
+ 2454	4828
+ 2166	1761
+ 2162	4510
+ 2714	2451
+ 1012	1998
+ 196	1248
+ 3702	4387
+ 2625	3833
+ 2922	3046
+ 1354	306
+ 3052	3055
+ 4397	3735
+ 2567	144
+ 1015	3855
+ 26	177
+ 3942	1935
+ 1003	115
+ 143	3793
+ 3843	2328
+ 661	1497
+ 3520	410
+ 672	1775
+ 1237	2395
+ 4182	514
+ 1041	4491
+ 1428	4254
+ 4356	3451
+ 565	3021
+ 2686	2884
+ 1667	2545
+ 1386	4841
+ 1644	4136
+ 2979	2200
+ 3475	1279
+ 2334	4186
+ 154	3700
+ 981	1275
+ 3916	3309
+ 1652	4193
+ 4806	1344
+ 1940	1188
+ 1246	1302
+ 427	32
+ 4556	4511
+ 4006	2450
+ 3761	716
+ 1010	2322
+ 3997	3794
+ 414	3350
+ 2462	1728
+ 3133	135
+ 1766	2813
+ 3993	3775
+ 3210	2631
+ 2899	1159
+ 4930	3949
+ 4083	4709
+ 3993	539
+ 2712	3278
+ 3985	2819
+ 1373	1409
+ 3569	3116
+ 122	1286
+ 4257	58
+ 2944	258
+ 2617	3550
+ 2870	1016
+ 4016	287
+ 2200	2908
+ 3202	4104
+ 4133	2380
+ 3642	138
+ 1655	4910
+ 3346	297
+ 4765	4771
+ 4698	2189
+ 1077	716
+ 74	3113
+ 1231	4919
+ 3606	4707
+ 2425	336
+ 2996	2179
+ 603	1461
+ 3235	1280
+ 4594	3749
+ 3300	932
+ 3250	3153
+ 1949	4950
+ 373	383
+ 3059	711
+ 3632	1452
+ 2504	357
+ 4837	2760
+ 1715	1205
+ 3088	3467
+ 4083	1404
+ 4792	4988
+ 2442	2195
+ 4051	2513
+ 4803	3467
+ 2328	103
+ 4082	506
+ 63	1548
+ 990	1649
+ 1806	1114
+ 1157	4389
+ 4872	370
+ 3	551
+ 4680	4966
+ 3933	2511
+ 3297	3188
+ 1626	4436
+ 504	2952
+ 4139	4868
+ 4484	3283
+ 3275	2594
+ 337	4313
+ 1930	409
+ 1966	3718
+ 781	1190
+ 2482	304
+ 2501	47
+ 1885	2601
+ 2488	4001
+ 2850	227
+ 396	732
+ 2916	3403
+ 2360	276
+ 3950	587
+ 4699	4826
+ 3077	2887
+ 3944	3404
+ 1195	1800
+ 3867	4588
+ 3321	4583
+ 243	2338
+ 4936	184
+ 612	1688
+ 4350	1683
+ 4648	454
+ 4132	542
+ 4632	9
+ 4659	19
+ 4665	2949
+ 3479	950
+ 601	4435
+ 2397	4362
+ 3036	4790
+ 307	3947
+ 4957	2380
+ 4465	89
+ 721	3098
+ 3370	1021
+ 42	3971
+ 3050	3996
+ 2168	607
+ 4434	2812
+ 3799	2052
+ 1583	3093
+ 2973	189
+ 1278	4885
+ 2611	1232
+ 971	2972
+ 1127	3559
+ 4058	4809
+ 607	2385
+ 4571	2449
+ 1725	4653
+ 1736	470
+ 1829	801
+ 4423	669
+ 3027	1854
+ 120	4201
+ 17	1320
+ 4274	4560
+ 1931	351
+ 2431	1966
+ 4114	1890
+ 4138	3475
+ 2019	4363
+ 3603	78
+ 2405	4755
+ 4133	2542
+ 2270	1564
+ 3733	4184
+ 2321	78
+ 2962	3028
+ 4120	3188
+ 2815	4764
+ 2876	1764
+ 2318	117
+ 4104	1047
+ 4294	4934
+ 1436	297
+ 2293	1214
+ 1647	237
+ 4215	3450
+ 4477	2051
+ 294	923
+ 3800	3776
+ 2640	1792
+ 2497	2242
+ 581	3022
+ 3944	3481
+ 287	317
+ 4435	1958
+ 4170	153
+ 1514	1072
+ 1383	4920
+ 3991	3079
+ 3162	3205
+ 848	2229
+ 1072	4954
+ 779	1582
+ 4461	3294
+ 2067	1623
+ 92	443
+ 4472	3060
+ 2926	4584
+ 2841	4284
+ 1060	2246
+ 3120	95
+ 1383	3395
+ 536	3120
+ 3381	1729
+ 3327	4474
+ 4526	155
+ 1059	4255
+ 217	1160
+ 1255	2557
+ 3988	1745
+ 764	4422
+ 3796	1565
+ 469	1044
+ 646	114
+ 800	890
+ 1046	127
+ 1490	4708
+ 3545	2983
+ 3034	1809
+ 2751	2617
+ 4187	4194
+ 2316	4073
+ 1709	416
+ 1688	297
+ 4052	1783
+ 1622	599
+ 4168	3045
+ 3593	173
+ 2845	2441
+ 3399	1042
+ 3159	1993
+ 3813	3030
+ 3857	2947
+ 3540	1361
+ 3320	3474
+ 160	4257
+ 776	451
+ 4405	1135
+ 4197	293
+ 3079	3219
+ 4280	2961
+ 4086	3500
+ 3903	1893
+ 1351	472
+ 3447	4494
+ 360	4852
+ 3540	893
+ 2364	3156
+ 2122	489
+ 2681	2548
+ 614	3323
+ 3013	2615
+ 1861	1282
+ 4196	295
+ 2102	4840
+ 2502	627
+ 3721	4973
+ 1744	4008
+ 805	864
+ 3769	3972
+ 1046	3279
+ 2902	1178
+ 885	1224
+ 1399	3663
+ 822	517
+ 2831	3830
+ 1538	3942
+ 3993	4574
+ 1144	498
+ 4128	1405
+ 350	649
+ 3433	303
+ 2516	870
+ 1539	939
+ 472	431
+ 1670	1219
+ 3974	1698
+ 4559	4968
+ 4941	3328
+ 2655	2713
+ 3924	1170
+ 89	449
+ 3976	1005
+ 1258	2846
+ 99	1878
+ 51	4701
+ 2249	1206
+ 2993	3403
+ 4970	2956
+ 1881	2124
+ 775	1331
+ 4781	1426
+ 4479	3383
+ 244	632
+ 2363	4867
+ 3254	4698
+ 1899	2384
+ 2439	3189
+ 599	1994
+ 1638	2228
+ 3196	2826
+ 927	3088
+ 2727	172
+ 1371	2890
+ 2855	1378
+ 732	3350
+ 4119	1030
+ 2975	949
+ 558	759
+ 4909	277
+ 310	2741
+ 2637	2635
+ 4886	4916
+ 2978	4638
+ 3421	3965
+ 4064	3862
+ 3886	1749
+ 3924	2196
+ 4446	4573
+ 732	2568
+ 1248	3696
+ 2714	1028
+ 2155	1653
+ 4270	4675
+ 4960	428
+ 4202	2178
+ 2241	3167
+ 1664	3515
+ 1046	450
+ 3232	2735
+ 59	207
+ 4975	4606
+ 870	3690
+ 3547	102
+ 366	296
+ 2742	1857
+ 329	395
+ 2580	3891
+ 2192	4588
+ 1513	2969
+ 3008	4604
+ 4994	2502
+ 279	4383
+ 1085	1651
+ 1826	4456
+ 961	2619
+ 4999	263
+ 3440	2370
+ 3608	1377
+ 2982	1975
+ 3971	1061
+ 1605	4214
+ 3542	444
+ 126	1432
+ 4413	688
+ 4927	2785
+ 3639	1412
+ 2116	2410
+ 1797	2559
+ 4150	374
+ 2038	2732
+ 762	1884
+ 4523	3382
+ 392	4884
+ 4352	2926
+ 2808	3277
+ 1798	4406
+ 4658	2696
+ 4526	1090
+ 2588	2265
+ 3674	2290
+ 3311	1612
+ 1820	594
+ 4996	2503
+ 3942	1757
+ 2020	1778
+ 1117	1908
+ 1213	4554
+ 1764	814
+ 907	4406
+ 4351	1117
+ 2999	3264
+ 3092	2323
+ 170	1757
+ 4006	4858
+ 4509	1914
+ 1996	3414
+ 3448	511
+ 4911	1948
+ 1342	3346
+ 3602	2892
+ 2449	1372
+ 2061	4803
+ 3259	4722
+ 2693	2733
+ 4093	896
+ 3161	4571
+ 2034	1427
+ 1841	1570
+ 4009	4127
+ 3586	366
+ 4632	703
+ 3542	2435
+ 2980	3677
+ 4308	375
+ 2722	294
+ 3153	4966
+ 3638	2068
+ 4779	2051
+ 3823	988
+ 2790	859
+ 1720	2947
+ 4758	3490
+ 53	488
+ 771	1917
+ 1435	3688
+ 4473	1223
+ 3018	2883
+ 64	3735
+ 653	2798
+ 168	3920
+ 1724	2219
+ 1525	3420
+ 1176	1564
+ 4266	2860
+ 2576	961
+ 4103	2398
+ 2097	1707
+ 3581	3586
+ 4507	3051
+ 3826	3144
+ 2859	3020
+ 842	4564
+ 2084	3050
+ 4987	2772
+ 779	3182
+ 3154	1445
+ 3547	2745
+ 4768	4173
+ 2365	235
+ 2708	605
+ 4598	1682
+ 1430	2343
+ 1199	2794
+ 4310	3078
+ 4173	2206
+ 4545	267
+ 325	1762
+ 4655	2319
+ 4689	1423
+ 1163	4554
+ 3547	3489
+ 1147	3605
+ 2641	1066
+ 2713	4435
+ 532	938
+ 1350	2620
+ 1689	770
+ 2238	4761
+ 2062	4578
+ 694	863
+ 3645	4913
+ 707	4185
+ 4886	2520
+ 3230	2549
+ 3894	4187
+ 3361	3757
+ 964	3759
+ 973	4111
+ 3594	3027
+ 2222	2545
+ 2273	1289
+ 2391	2741
+ 2598	504
+ 4040	1500
+ 4930	4632
+ 4907	4614
+ 1011	4211
+ 835	431
+ 3965	40
+ 1380	72
+ 2516	145
+ 1928	3307
+ 2889	2479
+ 2395	1164
+ 3045	77
+ 4336	92
+ 526	2459
+ 4705	1445
+ 361	31
+ 969	2008
+ 2202	2918
+ 4200	2970
+ 4580	4747
+ 2323	3056
+ 3693	2817
+ 2936	232
+ 1591	2864
+ 4068	739
+ 4173	2038
+ 3510	1437
+ 4283	1689
+ 373	3362
+ 2519	1013
+ 1375	4921
+ 4384	382
+ 879	1070
+ 2168	149
+ 3877	1933
+ 2308	1360
+ 2905	2749
+ 2776	1501
+ 1164	172
+ 3458	2866
+ 4087	1707
+ 996	689
+ 242	4378
+ 3383	3523
+ 3225	704
+ 2807	3765
+ 3240	2045
+ 1777	3357
+ 2791	2325
+ 2985	51
+ 4540	4165
+ 3149	1708
+ 106	1950
+ 4800	2805
+ 2033	4169
+ 4716	704
+ 4436	4824
+ 1508	207
+ 357	2221
+ 656	3874
+ 4413	2190
+ 4364	4900
+ 4985	312
+ 208	2159
+ 3536	974
+ 369	1010
+ 2789	194
+ 1416	2306
+ 3244	981
+ 2852	3994
+ 1941	3968
+ 1051	1909
+ 716	4716
+ 1998	1415
+ 4382	1341
+ 4857	3642
+ 1162	142
+ 1415	368
+ 4591	4103
+ 2998	2832
+ 2203	3034
+ 1737	2371
+ 2985	4705
+ 398	915
+ 4084	4607
+ 1027	1524
+ 2181	3405
+ 1097	567
+ 1596	795
+ 3128	2636
+ 3693	3227
+ 4076	451
+ 3028	1584
+ 2495	1676
+ 3447	4461
+ 1889	4089
+ 2514	857
+ 3269	4665
+ 4167	4588
+ 4049	3979
+ 1765	2991
+ 30	529
+ 4407	3802
+ 3895	4923
+ 3295	2046
+ 625	603
+ 4100	1822
+ 2758	366
+ 4743	2382
+ 4341	3919
+ 1912	4701
+ 2345	873
+ 3887	2818
+ 1701	561
+ 4338	1870
+ 3681	1934
+ 722	3268
+ 3872	2288
+ 1426	3377
+ 2699	4279
+ 1916	3981
+ 1711	4979
+ 930	4837
+ 3948	3126
+ 345	2263
+ 3296	1957
+ 1506	1803
+ 1424	1008
+ 813	1677
+ 492	2267
+ 3941	1952
+ 3428	4719
+ 3790	4178
+ 3895	759
+ 102	1378
+ 3094	4373
+ 4401	1077
+ 1618	285
+ 635	2725
+ 2281	190
+ 3041	2977
+ 4283	1216
+ 3074	733
+ 916	3908
+ 3883	3016
+ 3337	2240
+ 4329	3676
+ 41	2135
+ 1239	724
+ 3620	540
+ 3746	89
+ 655	3955
+ 1573	4758
+ 2601	4421
+ 3777	4897
+ 2860	1539
+ 220	4184
+ 4203	1157
+ 4290	147
+ 294	3263
+ 3665	2485
+ 1388	557
+ 1189	2279
+ 3793	1246
+ 2994	4980
+ 2396	2005
+ 135	4563
+ 4050	3677
+ 686	3802
+ 2072	4587
+ 4083	4747
+ 1849	1948
+ 999	3822
+ 1047	1178
+ 1289	2029
+ 2184	1461
+ 2029	4275
+ 4364	623
+ 3719	2983
+ 3607	4856
+ 2001	3606
+ 3749	1494
+ 2367	2833
+ 1731	1797
+ 2748	3972
+ 3296	4021
+ 573	1085
+ 3992	71
+ 547	128
+ 701	649
+ 941	1028
+ 3735	1794
+ 2116	1086
+ 3394	1152
+ 3415	3925
+ 3617	3206
+ 257	404
+ 4846	4886
+ 3691	21
+ 237	3241
+ 1536	1053
+ 2281	4339
+ 1498	4559
+ 2316	3124
+ 1406	533
+ 3538	1858
+ 4664	1382
+ 2231	3446
+ 4093	4831
+ 2234	1539
+ 3446	2543
+ 1732	795
+ 3106	628
+ 2513	1698
+ 4387	105
+ 2927	4965
+ 4394	2597
+ 4947	870
+ 1002	1514
+ 4951	977
+ 3053	2714
+ 3868	1862
+ 1486	155
+ 690	304
+ 632	3470
+ 4540	4929
+ 2473	3500
+ 72	4327
+ 4664	1260
+ 3954	1237
+ 3880	240
+ 580	4574
+ 1263	3994
+ \.
+ 
+ COPY t2 (a1, a2) FROM stdin;
+ 119	1124
+ 949	527
+ 3979	3780
+ 3394	1084
+ 791	369
+ 3698	4763
+ 3118	2029
+ 3687	1849
+ 125	3990
+ 1825	227
+ 2306	3618
+ 4222	3959
+ 373	3603
+ 2637	4247
+ 3187	1670
+ 3844	3451
+ 1667	4213
+ 4079	1126
+ 4292	1452
+ 2687	471
+ 1345	3331
+ 2173	35
+ 4576	636
+ 4985	3947
+ 576	1282
+ 1707	3629
+ 426	831
+ 4469	4827
+ 924	3722
+ 4965	3451
+ 2658	1931
+ 2498	852
+ 115	1614
+ 4638	1119
+ 3072	4615
+ 3794	3186
+ 1553	4621
+ 1457	1712
+ 1125	3272
+ 4547	3515
+ 2911	4298
+ 1915	2173
+ 4261	3774
+ 3159	3742
+ 2183	4661
+ 996	3346
+ 1899	4482
+ 2272	3629
+ 1612	1747
+ 548	2320
+ 3203	2108
+ 4943	3111
+ 2691	150
+ 340	4356
+ 4327	2728
+ 4945	3921
+ 4758	4309
+ 2637	2955
+ 499	4626
+ 3135	445
+ 1654	3495
+ 2602	1186
+ 4426	1904
+ 1072	1163
+ 2590	4966
+ 998	2610
+ 2112	3903
+ 270	1710
+ 3813	4118
+ 4188	4977
+ 3344	2643
+ 1966	1826
+ 1979	3548
+ 2177	1697
+ 4268	174
+ 2105	3235
+ 2062	2975
+ 3068	2238
+ 4030	53
+ 935	3009
+ 1633	1754
+ 3885	4740
+ 1315	3489
+ 3644	3759
+ 4570	1982
+ 4389	4283
+ 87	1516
+ 2533	4929
+ 2916	863
+ 1914	2374
+ 1453	149
+ 2653	4667
+ 2614	3801
+ 4918	4471
+ 626	2466
+ 3996	2921
+ 2220	2456
+ 2094	1507
+ 2666	4789
+ 2487	3685
+ 4593	505
+ 685	360
+ 4527	1481
+ 4983	4268
+ 4397	1225
+ 2526	3597
+ 2529	4834
+ 549	1742
+ 1981	2575
+ 3563	4875
+ 245	4288
+ 4483	503
+ 3991	4470
+ 2709	4248
+ 1189	1485
+ 1010	154
+ 1607	3908
+ 3844	4506
+ 2739	2425
+ 4400	2804
+ 4802	1615
+ 1294	606
+ 2837	3119
+ 3006	1251
+ 3794	1838
+ 4663	2754
+ 617	1111
+ 3487	700
+ 3925	2948
+ 280	728
+ 2414	2159
+ 355	2054
+ 1025	2678
+ 3626	2461
+ 2726	4574
+ 4117	2547
+ 2463	3285
+ 4618	264
+ 1993	639
+ 3014	2982
+ 3712	4153
+ 1638	4444
+ 1812	895
+ 151	4195
+ 2730	4012
+ 3031	4546
+ 2600	3124
+ 253	3710
+ 3698	1917
+ 3170	2902
+ 4312	3298
+ 294	4071
+ 176	467
+ 386	4251
+ 3689	3507
+ 4930	3047
+ 4916	2868
+ 3214	3541
+ 933	3057
+ 105	4776
+ 790	4144
+ 528	4848
+ 1605	1696
+ 3205	827
+ 4687	676
+ 4021	3793
+ 4332	3735
+ 3306	3714
+ 2609	370
+ 3266	3822
+ 1119	1735
+ 4537	2852
+ 2673	3157
+ 3227	4472
+ 4554	80
+ 4734	4329
+ 3173	847
+ 3942	3733
+ 484	1292
+ 3984	1333
+ 2615	3563
+ 3576	993
+ 3256	2902
+ 2784	220
+ 2557	506
+ 923	1341
+ 3806	1281
+ 2845	479
+ 2661	74
+ 3421	56
+ 422	2653
+ 2901	4374
+ 2663	2680
+ 1755	377
+ 2007	3063
+ 77	2279
+ 4939	67
+ 1068	421
+ 741	771
+ 3672	1443
+ 851	2412
+ 4408	4439
+ 4376	4204
+ 2020	4287
+ 1338	593
+ 4280	1226
+ 1452	3874
+ 1897	3155
+ 202	235
+ 647	4751
+ 2516	1507
+ 1459	1469
+ 4666	2716
+ 2158	4936
+ 2036	1394
+ 2009	315
+ 1890	4975
+ 2066	1971
+ 943	3717
+ 2610	3627
+ 948	846
+ 4278	382
+ 991	1355
+ 2114	4765
+ 3952	1136
+ 4659	2419
+ 1193	3727
+ 2025	770
+ 4151	1128
+ 1268	1856
+ 3395	4297
+ 3904	864
+ 2371	2982
+ 1860	212
+ 2305	4564
+ 3549	3101
+ 4818	921
+ 1989	848
+ 4628	1688
+ 1053	1485
+ 910	2814
+ 1585	3073
+ 1071	4226
+ 4650	2895
+ 485	4089
+ 12	511
+ 2819	3081
+ 3039	2377
+ 1984	4798
+ 617	4498
+ 410	2925
+ 3781	4546
+ 2373	4765
+ 265	1073
+ 129	4121
+ 2723	1954
+ 3548	1348
+ 3531	1995
+ 3393	556
+ 2743	1149
+ 2173	1090
+ 1564	3829
+ 2802	4019
+ 4685	3773
+ 2836	5000
+ 2481	1965
+ 18	1109
+ 2044	1670
+ 1394	4586
+ 3907	3208
+ 1631	2773
+ 3061	4603
+ 4502	4763
+ 4366	4196
+ 1836	4551
+ 3554	2820
+ 1249	4978
+ 181	2719
+ 2472	1000
+ 1763	3292
+ 4735	2007
+ 2982	1601
+ 4549	673
+ 1412	832
+ 4502	2239
+ 2829	3637
+ 3598	4541
+ 2805	1070
+ 1375	4870
+ 1099	2057
+ 26	571
+ 2189	845
+ 651	4254
+ 3962	416
+ 3191	3107
+ 4936	1555
+ 2104	584
+ 2292	4264
+ 3824	1998
+ 1599	3189
+ 1563	4736
+ 3407	2683
+ 3244	3503
+ 1420	3868
+ 3409	3701
+ 2701	4970
+ 1135	565
+ 4005	3890
+ 139	4253
+ 1677	3644
+ 4685	3154
+ 3429	3029
+ 4285	3401
+ 2262	3563
+ 238	4533
+ 3168	2099
+ 1250	4817
+ 3643	2615
+ 4358	3721
+ 1079	1289
+ 934	1717
+ 1953	4441
+ 371	2982
+ 650	2863
+ 1791	2611
+ 3315	4324
+ 1369	4259
+ 1768	1907
+ 3557	570
+ 3561	1809
+ 4774	2390
+ 511	2275
+ 174	3689
+ 112	3640
+ 1789	29
+ 4900	220
+ 110	4799
+ 4499	4614
+ 91	1486
+ 4063	2267
+ 1146	4823
+ 1129	2307
+ 2085	2930
+ 4287	839
+ 516	2445
+ 4905	3920
+ 1649	4995
+ 3155	766
+ 2986	4122
+ 867	2842
+ 4895	1845
+ 2302	4967
+ 2582	2272
+ 1067	4935
+ 3013	1055
+ 233	2950
+ 3561	289
+ 1604	2373
+ 4584	3701
+ 2378	518
+ 1051	44
+ 3233	1969
+ 2688	458
+ 1749	1201
+ 898	3885
+ 1042	3956
+ 1423	76
+ 526	3887
+ 1546	522
+ 4036	421
+ 2007	395
+ 3075	3713
+ 513	4558
+ 954	1324
+ 3818	1752
+ 1546	3035
+ 235	1791
+ 1685	1651
+ 2812	4070
+ 3780	555
+ 2456	2946
+ 3655	1934
+ 551	3217
+ 3993	3026
+ 2326	1178
+ 1689	4070
+ 2859	4042
+ 2433	3432
+ 1583	3176
+ 3600	3887
+ 685	4670
+ 4527	4723
+ 3503	997
+ 421	1666
+ 985	668
+ 1830	324
+ 2151	597
+ 3618	2520
+ 2718	4480
+ 2739	2118
+ 2459	2130
+ 3519	3817
+ 3131	4230
+ 492	3013
+ 1105	2197
+ 2722	4845
+ 1646	2703
+ 272	888
+ 2257	2148
+ 3008	2646
+ 4501	3709
+ 2691	4953
+ 2689	4985
+ 1241	261
+ 3033	4088
+ 2317	783
+ 3086	280
+ 429	1296
+ 1051	4237
+ 3058	2854
+ 974	1280
+ 2270	4133
+ 923	869
+ 2458	2640
+ 4924	1896
+ 3102	2552
+ 2472	3074
+ 3157	4609
+ 2979	2706
+ 69	4536
+ 713	4925
+ 1102	4698
+ 4783	3610
+ 3245	4168
+ 567	3841
+ 392	177
+ 2670	2260
+ 2855	1591
+ 4535	2963
+ 2606	2801
+ 4601	3819
+ 1331	486
+ 948	725
+ 3911	283
+ 2484	127
+ 3079	1567
+ 4513	2289
+ 4282	4572
+ 4368	1660
+ 4503	245
+ 256	4234
+ 2683	2779
+ 2055	854
+ 749	2028
+ 2943	2589
+ 296	3981
+ 703	507
+ 736	3737
+ 3853	4591
+ 2097	4801
+ 4533	3555
+ 3185	4258
+ 958	3871
+ 2312	4975
+ 1473	1628
+ 3076	2200
+ 1526	4220
+ 3300	2783
+ 1493	3695
+ 394	141
+ 500	4987
+ 3887	2351
+ 577	2837
+ 3977	4739
+ 3985	3881
+ 271	2850
+ 2735	4363
+ 4995	4105
+ 1490	860
+ 3078	3217
+ 2279	1041
+ 600	2845
+ 819	3265
+ 4746	323
+ 4706	3578
+ 2032	625
+ 3627	160
+ 2958	1048
+ 3138	659
+ 3563	606
+ 511	1673
+ 972	4587
+ 988	2905
+ 1207	1762
+ 4473	3025
+ 2767	2059
+ 1828	4279
+ 4754	102
+ 3161	4796
+ 2266	1970
+ 541	4089
+ 4205	4735
+ 4706	4260
+ 4236	2056
+ 2556	1029
+ 523	3367
+ 194	4189
+ 3104	2255
+ 3698	4015
+ 4823	1768
+ 3347	699
+ 3019	718
+ 2692	390
+ 2435	2096
+ 1811	823
+ 45	2975
+ 4190	2381
+ 3498	2532
+ 570	2712
+ 47	2445
+ 3706	2903
+ 4841	4296
+ 1590	3791
+ 4247	3401
+ 4910	353
+ 3503	776
+ 3755	4750
+ 1645	2548
+ 714	2594
+ 2018	1328
+ 1444	3751
+ 157	677
+ 886	80
+ 2189	2054
+ 1083	403
+ 2311	1744
+ 376	2903
+ 3153	2606
+ 2291	2517
+ 1762	807
+ 737	3861
+ 1210	2868
+ 2057	797
+ 3344	1544
+ 4301	3781
+ 18	2823
+ 712	1662
+ 4328	2792
+ 2864	1536
+ 2863	4680
+ 2016	3068
+ 617	530
+ 2492	1496
+ 4612	783
+ 549	2563
+ 3569	4306
+ 3338	640
+ 3787	3498
+ 684	1953
+ 246	3512
+ 3364	4328
+ 1076	4951
+ 3695	490
+ 809	407
+ 14	4076
+ 1668	3383
+ 4986	2660
+ 4048	3696
+ 2625	423
+ 3342	1535
+ 4672	4291
+ 2570	2550
+ 1471	3231
+ 3088	4103
+ 1209	843
+ 4867	1612
+ 4307	4595
+ 4852	2805
+ 2802	4517
+ 2506	3763
+ 262	2848
+ 3783	2930
+ 2704	3836
+ 4158	508
+ 2874	1649
+ 1257	2725
+ 616	2363
+ 3563	1977
+ 324	2045
+ 1722	2165
+ 3714	3334
+ 4861	722
+ 710	2664
+ 605	2924
+ 4141	2963
+ 3015	2360
+ 1429	1827
+ 2374	4921
+ 2490	2833
+ 3644	2880
+ 2551	3483
+ 3475	2858
+ 918	795
+ 2076	3897
+ 2498	2608
+ 1181	2898
+ 1054	3572
+ 3692	4039
+ 505	2316
+ 338	2170
+ 3394	2327
+ 4263	3980
+ 1443	2569
+ 316	782
+ 2779	2863
+ 3999	2531
+ 3849	3288
+ 2936	468
+ 3863	933
+ 696	2333
+ 2613	2492
+ 150	3725
+ 431	3897
+ 1586	4463
+ 4433	3262
+ 2276	2747
+ 4032	2088
+ 3356	3336
+ 175	4285
+ 1514	2459
+ 2582	2216
+ 3898	2503
+ 688	4109
+ 2136	1510
+ 2153	173
+ 2756	1107
+ 2920	4065
+ 2415	1923
+ 1296	1027
+ 4735	549
+ 4400	2690
+ 4340	1632
+ 3100	2645
+ 3613	2295
+ 4979	1477
+ 2073	783
+ 1481	4824
+ 3608	1120
+ 4420	577
+ 2839	3609
+ 2876	3333
+ 2101	1486
+ 1923	2946
+ 523	2469
+ 285	4638
+ 3385	1773
+ 2534	3511
+ 1125	1589
+ 2992	2063
+ 3973	571
+ 3644	442
+ 866	3045
+ 2283	4009
+ 1970	4307
+ 4317	2300
+ 1442	2812
+ 3399	1388
+ 1906	2648
+ 1474	2261
+ 1773	3111
+ 1461	4162
+ 3750	4693
+ 2446	4673
+ 2029	3879
+ 2407	4162
+ 447	261
+ 1508	1251
+ 66	4664
+ 4464	1432
+ 1200	1970
+ 159	3942
+ 3372	4674
+ 2147	3846
+ 4706	1766
+ 1839	4697
+ 3926	1526
+ 4588	781
+ 4549	1133
+ 2559	3990
+ 109	663
+ 1498	587
+ 942	2965
+ 1000	987
+ 1104	553
+ 4109	1184
+ 4811	1931
+ 2850	4560
+ 4579	4515
+ 3915	3518
+ 1834	664
+ 2111	3524
+ 4784	1298
+ 2691	2293
+ 4475	3888
+ 2550	2264
+ 719	957
+ 4103	1215
+ 2933	4145
+ 1763	4433
+ 4022	3487
+ 998	718
+ 593	1783
+ 348	1259
+ 4684	3266
+ 2474	3757
+ 185	3475
+ 2731	3580
+ 1057	305
+ 2497	3727
+ 4881	666
+ 4719	205
+ 1099	1403
+ 1309	739
+ 4632	519
+ 4509	1604
+ 1722	2804
+ 3539	3368
+ 4671	1035
+ 2345	4929
+ 2670	524
+ 661	2625
+ 2550	465
+ 2712	3856
+ 1724	2089
+ 858	4492
+ 3697	2552
+ 1800	2920
+ 3139	636
+ 4589	4488
+ 4457	1170
+ 3554	3288
+ 890	3052
+ 4808	2063
+ 2058	1396
+ 4394	3078
+ 29	1374
+ 2944	3346
+ 2525	2037
+ 2045	1814
+ 2962	688
+ 288	2123
+ 1967	237
+ 3413	2410
+ 4638	1519
+ 3239	2841
+ 4252	4345
+ 3592	3567
+ 3624	320
+ 2692	2663
+ 1040	3013
+ 910	2000
+ 2202	3905
+ 4821	4289
+ 2222	825
+ 2623	154
+ 4316	733
+ 1881	1856
+ 2695	4133
+ 4256	4580
+ 102	2828
+ 2370	3705
+ 767	4618
+ 4445	4710
+ 3275	2992
+ 3051	740
+ 77	4749
+ 4508	3600
+ 2832	4126
+ 1998	3148
+ 4746	2866
+ 3698	2384
+ 3907	498
+ 973	871
+ 1872	2453
+ 4674	4492
+ 2035	823
+ 3310	383
+ 4881	1179
+ 2288	2514
+ 96	2337
+ 3756	4192
+ 1628	3519
+ 2972	2705
+ 3163	3922
+ 865	199
+ 2764	4191
+ 1424	257
+ 4148	1479
+ 2237	1911
+ 4661	2296
+ 4454	4727
+ 4135	2615
+ 189	4880
+ 51	1525
+ 2907	1568
+ 3423	2187
+ 2510	3025
+ 4809	2427
+ 782	933
+ 395	3583
+ 1807	4853
+ 57	3008
+ 1958	4856
+ 3617	1244
+ 221	25
+ 1099	3055
+ 307	4225
+ 1573	2408
+ 1511	1615
+ 1740	3884
+ 4869	1669
+ 4892	3735
+ 3237	167
+ 345	4985
+ 2509	3569
+ 4319	1922
+ 3586	1538
+ 4945	1486
+ 610	1429
+ 3858	936
+ 3546	2005
+ 3164	2929
+ 1386	138
+ 4155	4505
+ 2063	1119
+ 4165	878
+ 1408	1515
+ 359	1136
+ 2647	1903
+ 2821	2443
+ 2119	1384
+ 161	4609
+ 2877	880
+ 3263	4641
+ 4155	4704
+ 605	2300
+ 4505	1637
+ 1980	2448
+ 4829	4713
+ 4271	1781
+ 167	1096
+ 2224	3138
+ 4077	3349
+ 4771	3176
+ 4299	3194
+ 1834	46
+ 619	4340
+ 3092	1414
+ 3683	1129
+ 1418	4067
+ 1069	853
+ 4238	4477
+ 3900	3096
+ 712	2201
+ 1743	22
+ 902	1162
+ 3821	2466
+ 4675	4707
+ 4970	3592
+ 3609	2296
+ 536	4162
+ 2987	842
+ 2001	2145
+ 249	26
+ 3048	2878
+ 568	1592
+ 531	3606
+ 3592	2150
+ 4478	1688
+ 4141	1920
+ 2043	3260
+ 3979	2631
+ 116	2777
+ 2501	4549
+ 792	4110
+ 2356	1589
+ 2200	3110
+ 3043	52
+ 766	541
+ 1472	3052
+ 1308	2890
+ 4567	853
+ 4528	2581
+ 655	4007
+ 2481	3325
+ 2002	2399
+ 2767	2611
+ 709	3465
+ 4639	949
+ 4179	1822
+ 4664	2825
+ 4651	2897
+ 1708	481
+ 4165	3892
+ 4050	933
+ 2271	2341
+ 864	2226
+ 4228	1935
+ 961	2063
+ 2912	3665
+ 1912	3459
+ 1155	953
+ 930	4441
+ 3703	2348
+ 553	325
+ 4875	1842
+ 1222	1626
+ 34	4635
+ 2246	2097
+ 3245	2827
+ 281	125
+ 1609	4129
+ 2552	3623
+ 3325	1758
+ 1962	4749
+ 4961	4424
+ 3938	4993
+ 4259	1929
+ 2832	2170
+ 1782	733
+ 3273	3071
+ 1489	1581
+ 3164	3052
+ 2985	378
+ 551	3171
+ 4388	1852
+ 3783	3104
+ 1300	4310
+ 1340	3754
+ 1538	1086
+ 2408	4851
+ 3630	4010
+ 613	125
+ 3564	4541
+ 20	3887
+ 2120	3504
+ 326	4433
+ 4447	2155
+ 421	4036
+ 336	4016
+ 2037	1493
+ 1308	1739
+ 2325	4455
+ 558	3460
+ 1295	85
+ 4726	2672
+ 2180	4219
+ 1568	588
+ 1452	1669
+ 3426	429
+ 681	491
+ 1190	920
+ 3612	795
+ 4616	3569
+ 749	2729
+ 408	1237
+ 4340	991
+ 3176	3275
+ 3587	3535
+ 4957	777
+ 412	3063
+ 2470	864
+ 3688	1567
+ 4576	3396
+ 2492	4877
+ 3407	974
+ 769	2251
+ 4799	862
+ 4608	2945
+ 2555	2007
+ 3439	2390
+ 2152	1328
+ 1097	2926
+ 399	130
+ 1112	3797
+ 2021	4076
+ 3739	1036
+ 3636	1278
+ 3932	1542
+ 3142	4715
+ 4030	3441
+ 2826	398
+ 40	3581
+ 3833	2594
+ 4587	2799
+ 676	223
+ 3703	4153
+ 697	4150
+ 2834	3284
+ 2680	31
+ \.
+ 
+ COPY t3 (a2, a0) FROM stdin;
+ 3834	3604
+ 2772	195
+ 2301	1022
+ 3474	2666
+ 99	4637
+ 4162	3309
+ 3570	1315
+ 4797	1792
+ 745	1735
+ 74	2967
+ 4251	2399
+ 1296	3124
+ 4383	2133
+ 2705	1679
+ 1372	1941
+ 1012	3571
+ 1070	4064
+ 2385	3994
+ 2035	3093
+ 3829	3408
+ 4487	4873
+ 4386	2970
+ 3510	3619
+ 4347	1079
+ 930	3133
+ 3570	2691
+ 1921	740
+ 3807	240
+ 274	4113
+ 1126	4030
+ 1771	984
+ 47	4941
+ 2746	2193
+ 1215	3097
+ 1061	1430
+ 3966	1031
+ 2190	390
+ 2967	4324
+ 2121	1546
+ 3313	1288
+ 3854	1695
+ 3578	3387
+ 1394	313
+ 550	1438
+ 1181	460
+ 2777	1019
+ 3112	260
+ 3754	1833
+ 2717	1595
+ 2293	3206
+ 956	4100
+ 1727	2965
+ 3567	2671
+ 2768	4455
+ 3841	3017
+ 4759	1534
+ 3661	3501
+ 1363	668
+ 2260	6
+ 1466	1336
+ 1086	1752
+ 4402	4914
+ 1747	474
+ 1247	2352
+ 4429	3376
+ 2973	110
+ 3846	2362
+ 4974	4794
+ 1102	1039
+ 2144	260
+ 3807	3331
+ 1404	1127
+ 3342	2560
+ 2404	363
+ 4357	3870
+ 4307	3743
+ 4399	1494
+ 3023	3422
+ 4341	919
+ 1819	3123
+ 3872	4743
+ 311	1513
+ 4964	1453
+ 4142	627
+ 2976	2283
+ 3239	2044
+ 914	2920
+ 1385	435
+ 3545	1366
+ 3862	480
+ 493	3616
+ 4696	2548
+ 1416	4249
+ 3511	3998
+ 3422	4335
+ 4128	1279
+ 806	3009
+ 885	1954
+ 4660	2251
+ 734	918
+ 2163	1711
+ 688	1808
+ 3545	3150
+ 3558	4749
+ 3911	316
+ 2671	4595
+ 4863	4149
+ 2073	2678
+ 3423	4607
+ 4379	4352
+ 1887	3243
+ 2415	2134
+ 553	3393
+ 4254	2581
+ 4026	2074
+ 1175	2332
+ 684	3642
+ 2494	2130
+ 2726	1483
+ 3721	3504
+ 923	4561
+ 28	3105
+ 193	1449
+ 2985	2719
+ 1654	4427
+ 2245	1505
+ 1140	3217
+ 1937	4179
+ 3602	3299
+ 4602	2938
+ 2380	1799
+ 2174	4290
+ 4246	3824
+ 319	4602
+ 4197	3507
+ 2158	1051
+ 4296	2149
+ 2860	593
+ 2016	1534
+ 4381	388
+ 958	4374
+ 1575	648
+ 1344	23
+ 472	2418
+ 530	4247
+ 599	2270
+ 4017	4563
+ 1155	2381
+ 4549	4185
+ 4451	2441
+ 720	2741
+ 2126	2848
+ 160	2623
+ 1665	4423
+ 186	3303
+ 1959	3286
+ 100	3582
+ 3708	1875
+ 3901	888
+ 2157	1400
+ 4082	1179
+ 916	1567
+ 3807	3365
+ 1469	202
+ 794	2908
+ 4547	4245
+ 4289	370
+ 575	4699
+ 3337	2463
+ 3962	4525
+ 618	2562
+ 4265	730
+ 1182	1505
+ 2703	4837
+ 1602	724
+ 2950	2923
+ 1111	3701
+ 3409	2398
+ 4266	4716
+ 706	2273
+ 1525	1507
+ 2898	144
+ 944	2264
+ 3237	4367
+ 1697	4166
+ 4710	920
+ 4101	320
+ 3609	4610
+ 4654	1428
+ 3956	3010
+ 673	3838
+ 3872	755
+ 944	1548
+ 68	3622
+ 3266	1001
+ 2422	27
+ 1132	2905
+ 1115	1273
+ 1802	2540
+ 2885	3608
+ 1676	1413
+ 4683	855
+ 528	2811
+ 4395	404
+ 1088	1895
+ 4153	4500
+ 4247	2241
+ 2907	3624
+ 2975	4638
+ 864	3772
+ 598	419
+ 3750	4663
+ 3786	3236
+ 3394	969
+ 1140	3106
+ 1835	3785
+ 4141	4581
+ 3342	3693
+ 1650	339
+ 4607	3587
+ 2703	4453
+ 1867	371
+ 1494	597
+ 2192	3454
+ 1163	1584
+ 4845	45
+ 1954	50
+ 899	1303
+ 1025	2235
+ 3162	4643
+ 4925	3222
+ 4629	4484
+ 1131	4315
+ 156	1568
+ 2184	1784
+ 2988	3100
+ 1548	2633
+ 4695	3161
+ 459	4912
+ 1830	4553
+ 682	1790
+ 1494	3437
+ 873	3554
+ 2649	1368
+ 2730	2451
+ 2206	3083
+ 3859	4153
+ 158	1264
+ 2063	3619
+ 786	1334
+ 1801	38
+ 4521	2321
+ 2839	3838
+ 4539	2209
+ 2961	1446
+ 4282	724
+ 3977	2004
+ 2200	2961
+ 4279	637
+ 791	1935
+ 4345	3244
+ 4578	3732
+ 1578	1207
+ 393	3664
+ 4590	2490
+ 2239	375
+ 869	560
+ 400	3470
+ 2100	1696
+ 3004	2841
+ 3575	3943
+ 3002	4415
+ 1264	4619
+ 3895	848
+ 3223	2623
+ 2038	3661
+ 4094	809
+ 971	3459
+ 111	4033
+ 685	3367
+ 2536	4349
+ 3736	1815
+ 2366	692
+ 3901	4996
+ 7	2919
+ 148	4678
+ 3368	825
+ 2447	1592
+ 4746	1402
+ 494	325
+ 2231	11
+ 3845	3419
+ 504	83
+ 1415	2521
+ 291	4296
+ 2756	3834
+ 2645	1707
+ 3018	3994
+ 4372	694
+ 3322	367
+ 3388	543
+ 4072	4664
+ 1115	4372
+ 881	519
+ 3326	1539
+ 4861	853
+ 888	3438
+ 2546	4413
+ 604	4544
+ 1147	4776
+ 2210	3214
+ 4716	4423
+ 4533	3128
+ 1910	3860
+ 3616	922
+ 2195	4733
+ 3346	1445
+ 1088	4728
+ 54	80
+ 943	4321
+ 861	2350
+ 550	3767
+ 360	3974
+ 1174	2382
+ 1671	3183
+ 2261	3105
+ 4888	1223
+ 1956	4616
+ 4395	2751
+ 823	2582
+ 1645	4623
+ 2558	4919
+ 301	2844
+ 3248	2642
+ 4250	3295
+ 348	450
+ 4225	1096
+ 3261	87
+ 2326	4359
+ 2279	1823
+ 724	4770
+ 2013	3449
+ 3750	1754
+ 3820	1225
+ 2754	3441
+ 2822	3256
+ 527	987
+ 207	854
+ 2848	2010
+ 1775	1538
+ 499	2495
+ 4321	2810
+ 2127	3779
+ 4548	548
+ 3510	1657
+ 3687	3890
+ 3997	343
+ 4034	2700
+ 3244	1520
+ 3086	788
+ 2223	1518
+ 334	4162
+ 2457	4342
+ 2011	355
+ 2325	1134
+ 3619	81
+ 1342	1824
+ 4233	3048
+ 2603	3036
+ 1427	2747
+ 461	3177
+ 3355	1438
+ 3804	1260
+ 440	2293
+ 3816	1242
+ 2787	398
+ 31	727
+ 122	1720
+ 3879	46
+ 3306	3435
+ 3953	4472
+ 1123	4461
+ 1505	1773
+ 4652	2220
+ 2113	2405
+ 876	496
+ 79	484
+ 1316	1460
+ 922	115
+ 4141	565
+ 3043	3843
+ 4339	703
+ 3382	3600
+ 3418	3974
+ 1870	3807
+ 2403	2829
+ 3764	2530
+ 4527	833
+ 719	4036
+ 4260	1005
+ 2221	3623
+ 1688	23
+ 3472	2516
+ 2761	4722
+ 2598	1868
+ 294	4826
+ 1655	1863
+ 692	2363
+ 2899	597
+ 1468	2354
+ 562	2537
+ 1421	2105
+ 1433	312
+ 3665	4651
+ 2585	3737
+ 1223	4836
+ 2155	1079
+ 744	1554
+ 2125	2771
+ 2993	3195
+ 4822	4088
+ 428	500
+ 1689	1466
+ 4214	151
+ 4150	152
+ 2047	2290
+ 2008	3194
+ 3261	1727
+ 1402	867
+ 4206	856
+ 4816	1463
+ 3869	2501
+ 4107	928
+ 1730	3469
+ 1422	4026
+ 1309	2861
+ 128	1311
+ 2981	438
+ 2608	3470
+ 4076	2364
+ 1513	710
+ 2514	3732
+ 1587	3134
+ 3878	206
+ 110	715
+ 197	4041
+ 3846	2514
+ 24	391
+ 3337	3028
+ 3977	2287
+ 170	2631
+ 3900	1194
+ 88	123
+ 3065	4592
+ 188	277
+ 2416	2326
+ 2019	2748
+ 3628	3504
+ 3859	924
+ 413	619
+ 247	4870
+ 3935	3850
+ 1215	4167
+ 1588	1101
+ 4318	4140
+ 3703	766
+ 1699	1891
+ 2605	1496
+ 87	4939
+ 900	644
+ 820	2414
+ 3521	1562
+ 4985	3872
+ 2989	2215
+ 3726	1588
+ 4116	2639
+ 2872	2560
+ 2832	2279
+ 2535	2443
+ 4582	4402
+ 926	4655
+ 1342	4480
+ 4952	4901
+ 3490	341
+ 359	305
+ 3909	4324
+ 2792	1951
+ 3254	14
+ 3787	3854
+ 3075	880
+ 4916	4505
+ 4930	4518
+ 4215	1528
+ 2635	4243
+ 161	4095
+ 1537	2961
+ 3464	311
+ 802	1449
+ 1858	2324
+ 1662	468
+ 2059	2473
+ 3859	1563
+ 3289	3871
+ 4974	2913
+ 3299	2899
+ 1132	1073
+ 3264	3017
+ 3953	4301
+ 2053	4023
+ 784	551
+ 2768	90
+ 2535	3029
+ 4793	1648
+ 2511	4685
+ 2102	976
+ 1374	982
+ 935	2584
+ 4584	2406
+ 266	1173
+ 4702	3930
+ 2143	3992
+ 1911	911
+ 3464	849
+ 2671	3635
+ 4258	2277
+ 2573	1607
+ 1355	29
+ 3494	82
+ 3196	272
+ 2525	1534
+ 4953	2862
+ 833	4754
+ 2812	418
+ 4298	1141
+ 1246	2026
+ 2434	770
+ 3924	2608
+ 1387	2131
+ 3956	441
+ 4090	4180
+ 2289	4354
+ 1903	795
+ 2612	4769
+ 763	1581
+ 963	2508
+ 4445	4503
+ 4242	4689
+ 1815	2138
+ 3208	3292
+ 3027	2048
+ 3690	395
+ 2594	1819
+ 2212	478
+ 1712	208
+ 2874	4321
+ 1398	3294
+ 4802	1640
+ 1420	167
+ 2525	5
+ 876	4178
+ 1723	1834
+ 1148	3713
+ 2004	2149
+ 4013	2367
+ 1670	1253
+ 1520	204
+ 834	3111
+ 3264	4449
+ 1481	2683
+ 2	4988
+ 4798	2147
+ 198	732
+ 1819	1357
+ 1060	3917
+ 1580	319
+ 4740	3094
+ 907	1806
+ 3906	4650
+ 1768	101
+ 4969	4967
+ 231	398
+ 4601	3939
+ 2090	4107
+ 1466	2337
+ 3069	851
+ 1870	3955
+ 2648	102
+ 126	2222
+ 4019	686
+ 1577	3741
+ 3016	968
+ 4095	2712
+ 275	488
+ 1974	956
+ 1778	690
+ 4615	3298
+ 3988	2694
+ 1892	2333
+ 1665	4931
+ 1147	3796
+ 3637	4560
+ 1966	645
+ 1515	581
+ 1020	2926
+ 4537	4901
+ 4514	2689
+ 2783	4433
+ 1588	1405
+ 4990	3292
+ 4652	2888
+ 191	4013
+ 1591	3471
+ 2932	168
+ 2041	4456
+ 4552	4720
+ 4776	4453
+ 4429	4117
+ 2824	2375
+ 2582	3363
+ 3946	4136
+ 2995	3745
+ 3967	167
+ 1865	3087
+ 1352	4609
+ 960	1646
+ 683	4389
+ 4735	1456
+ 1481	4425
+ 3699	2917
+ 2430	4657
+ 1544	1800
+ 1561	1044
+ 4743	1425
+ 663	3782
+ 491	2425
+ 1428	4095
+ 869	1416
+ 4580	2816
+ 3364	413
+ 1974	3796
+ 751	3957
+ 3978	2315
+ 3899	2126
+ 2214	2044
+ 1383	2471
+ 1717	3459
+ 2052	4748
+ 4902	1421
+ 1844	3858
+ 677	1622
+ 2978	229
+ 3278	380
+ 4435	4617
+ 4428	3374
+ 1580	3808
+ 1764	3077
+ 1116	4586
+ 1649	2215
+ 3542	4339
+ 2675	4985
+ 3958	424
+ 720	2452
+ 1305	291
+ 1536	3946
+ 4372	1594
+ 2297	1835
+ 4114	3363
+ 4970	1969
+ 471	2417
+ 1387	4232
+ 4653	4947
+ 4693	898
+ 3359	2214
+ 2240	1298
+ 3436	703
+ 4073	1227
+ 2380	830
+ 364	363
+ 1681	1015
+ 2294	1552
+ 787	4606
+ 1149	829
+ 2497	4280
+ 4863	423
+ 1769	2388
+ 2033	135
+ 4727	1579
+ 122	2486
+ 2286	4365
+ 1906	4797
+ 2118	4930
+ 3263	1688
+ 3088	2708
+ 2289	2116
+ 1277	1649
+ 478	4609
+ 1081	1459
+ 3239	3968
+ 4656	1214
+ 273	4011
+ 3714	4843
+ 4268	2406
+ 4301	3934
+ 4997	2804
+ 4498	2268
+ 2911	4916
+ 3479	742
+ 143	2437
+ 453	3325
+ 689	4914
+ 3551	4587
+ 4972	2351
+ 673	2539
+ 1477	3984
+ 3593	2242
+ 4517	465
+ 894	3442
+ 3391	1615
+ 4097	239
+ 40	521
+ 3228	1794
+ 4007	4421
+ 464	1223
+ 4668	2811
+ 717	2937
+ 614	2074
+ 55	1696
+ 3146	3132
+ 3510	675
+ 1350	1220
+ 3816	3426
+ 924	3638
+ 1358	4729
+ 2662	4895
+ 2232	1492
+ 2093	1246
+ 3460	2636
+ 2983	4305
+ 716	3422
+ 4439	604
+ 2474	2909
+ 1666	90
+ 2459	4224
+ 3135	808
+ 3126	502
+ 3919	1024
+ 897	687
+ 180	3210
+ 196	1687
+ 4069	2656
+ 1680	660
+ 851	3393
+ 1004	2825
+ 3400	1935
+ 2861	1996
+ 4437	4207
+ 3698	707
+ 3376	486
+ 991	3155
+ 2041	88
+ 372	3840
+ 2338	512
+ 3021	3100
+ 993	3750
+ 3329	3017
+ 4049	2256
+ 1020	3064
+ 2926	3921
+ 3467	2001
+ 2158	729
+ 1582	670
+ 1870	1700
+ 1253	3116
+ 1653	1307
+ 235	3804
+ 881	1336
+ 4143	444
+ 204	4146
+ 3445	1637
+ 3313	1085
+ 1310	6
+ 4311	4663
+ 2166	3650
+ 4486	2396
+ 2743	1014
+ 4155	3788
+ 546	1313
+ 825	3687
+ 3607	4149
+ 1825	4262
+ 3654	664
+ 2291	1078
+ 3288	3564
+ 2951	715
+ 3765	140
+ 2618	2470
+ 955	668
+ 617	1932
+ 525	4907
+ 4223	2236
+ 2903	2416
+ 662	347
+ 2029	825
+ 2192	2010
+ 4708	2029
+ 460	626
+ 1644	3359
+ 3768	3768
+ 76	4158
+ 1097	1548
+ 3431	4586
+ 2832	4862
+ 3448	2913
+ 4220	3783
+ 4908	2940
+ 2005	4404
+ 2053	3109
+ 3603	1968
+ 2646	2924
+ 140	4606
+ 2053	4749
+ 1654	2117
+ 739	2869
+ 4747	2752
+ 3596	2013
+ 1669	1103
+ 4195	3315
+ 424	1819
+ 596	2468
+ 1351	431
+ 4069	4286
+ 3031	261
+ 4150	4342
+ 2836	2612
+ 4994	180
+ 1310	4413
+ 1229	1028
+ 4700	1730
+ 4492	1630
+ 1835	799
+ 4895	2132
+ 635	1545
+ 4572	2231
+ 614	292
+ 1299	2409
+ 2583	784
+ 793	2911
+ 1301	3446
+ 3933	1539
+ 3726	4473
+ 49	1365
+ 2035	4450
+ 4482	65
+ 1473	2483
+ 2331	547
+ 1137	548
+ 2150	1323
+ 1917	982
+ 2756	615
+ 1296	1976
+ 4094	3125
+ 3017	2191
+ 2728	1047
+ 910	4871
+ 626	2180
+ 3074	2593
+ 1940	1582
+ 2564	2213
+ 3189	3903
+ 2880	2666
+ 4592	4591
+ 4695	3268
+ 2499	1999
+ 2899	1458
+ 528	3849
+ 4416	590
+ 2751	2320
+ 4829	405
+ 1068	4348
+ 2015	2412
+ 3161	4066
+ 2210	1613
+ 4482	633
+ 2011	3935
+ 2173	1742
+ 2478	3472
+ 152	3465
+ 305	1672
+ 4172	3751
+ 3936	883
+ 3596	4512
+ 1690	4220
+ 3886	4575
+ 3276	4822
+ 4605	4183
+ 2590	4607
+ 1182	133
+ 2527	467
+ 4308	3365
+ 631	2208
+ 4990	978
+ 4975	1008
+ 3584	3522
+ 1914	402
+ 2945	4376
+ 170	4126
+ 1849	158
+ 952	2322
+ 2466	3967
+ 301	2426
+ 1080	2415
+ 2941	2859
+ 3180	1821
+ 2978	2701
+ 1352	1448
+ 4960	4899
+ 4695	3131
+ 1686	1473
+ 4658	2465
+ 3941	4131
+ 1622	3683
+ 4671	3211
+ 4747	529
+ 832	3339
+ 2396	2685
+ 1764	2532
+ 4149	1213
+ 2221	2640
+ 4279	2695
+ 2165	4676
+ 3731	3417
+ 792	610
+ 1081	3682
+ 438	2044
+ 2449	2352
+ 4463	3461
+ 3110	1392
+ 2931	4447
+ 3936	315
+ 1973	1845
+ 2037	1696
+ 3000	2682
+ 4925	4950
+ 809	843
+ 4872	893
+ 753	4409
+ 3098	2026
+ 918	1867
+ 795	2676
+ 3410	3733
+ 4634	3443
+ 4153	2953
+ 3033	2844
+ 371	1608
+ 3508	2423
+ 4415	3530
+ 3187	1180
+ 3804	3806
+ 802	1298
+ 3552	974
+ 4113	937
+ 1563	270
+ 4198	2681
+ 992	1351
+ 289	2226
+ 3101	4801
+ 580	748
+ 394	2004
+ 2903	929
+ 2237	4569
+ 4433	4219
+ 4122	4473
+ 4700	965
+ 2373	2663
+ 3051	1223
+ 861	500
+ 4229	242
+ 3024	4311
+ 2205	21
+ 3985	3170
+ 3467	2804
+ 1750	1960
+ 397	137
+ 2243	2992
+ 3674	2064
+ 880	4019
+ 203	1299
+ 2621	2278
+ 2232	1856
+ 455	1074
+ 4663	1065
+ 587	4960
+ 3058	2237
+ 1494	3026
+ 1365	4613
+ 2186	4897
+ 4278	966
+ 4594	3274
+ 3553	3006
+ 3146	1716
+ \.
+ 
+ \set ECHO all
+ RESET client_min_messages;
+ 
+ --
+ --  Test getfdr
+ --
+ SELECT getfdr('t1,t2,t3');
+ 
+ --
+ --  Test fulldisjunction
+ --
+ SELECT * FROM fulldisjunction('t1,t2,t3') AS RECORD (a0 int4,a1 int4,a2 int4) ORDER BY a0, a1, a2;
+ 
diff -crN pgsql/contrib/fulldisjunctions/tset.c pgsql-fd/contrib/fulldisjunctions/tset.c
*** pgsql/contrib/fulldisjunctions/tset.c	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/tset.c	2006-07-29 20:54:13.000000000 -0400
***************
*** 0 ****
--- 1,1002 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #include "algstructs.h"
+ #include "tset.h"
+ #include "tsetfuncs.h"
+ #include "algutils.h"
+ #include "queues.h"
+ #include "queuesfuncs.h"
+ void
+ freeTSet(alg_fctx * fctx, TSet tset)
+ {
+ 	if (tset->t == NULL)
+ 	{
+ 		int			i;
+ 
+ 		for (i = 0; i < NUMATT; i++)
+ 			if (tset->v[i] != (Datum) NULL)
+ 				myDatumFree(tset->v[i], fctx->tupleSetDesc->attrs[i]->attbyval,
+ 							fctx->tupleSetDesc->attrs[i]->attlen);
+ 	}
+ 	else
+ 		heap_freetuple(tset->t);
+ 	if (tset->v != NULL)
+ 	{
+ 		pfree(tset->v);
+ 		pfree(tset->n);
+ 	}
+ 	pfree(tset);
+ }
+ 
+ TSet
+ newEmptyTSet(alg_fctx * fctx)
+ {
+ 	TSet		tset = ((TSet) palloc(sizeof(deformedTSet)));
+ 	bytea	   *b = (bytea *) palloc(VARHDRSZ);
+ 
+ 	VARATT_SIZEP(b) = VARHDRSZ;
+ 	tset->v = NULL;
+ 	tset->n = NULL;
+ 	Datum		tsetVals[NUMATT];
+ 	char		tsetNulls[NUMATT];
+ 	int			i;
+ 
+ 	for (i = FIRSTATT; i < NUMATT; i++)
+ 		tsetNulls[i] = 'n';
+ 	tsetNulls[LABELS] = ' ';
+ 	tsetVals[LABELS] = (Datum) b;
+ 	tset->t = heap_formtuple(fctx->tupleSetDesc, tsetVals, tsetNulls);
+ 	tset->size = tupleSize(tset->t);
+ 	bool		isnull = false;
+ 
+ 	tset->tids = (bytea *) fastgetattr(tset->t, LABELS_ALIGNED, fctx->tupleSetDesc, &isnull);
+ 	return tset;
+ }
+ Datum
+ setTSetValue(alg_fctx * fctx, TSet tset, Datum value, int position)
+ {
+ 	if ((tset->v[position] != (Datum) NULL) && (tset->t == NULL))
+ 	{
+ 		myDatumFree(tset->v[position], fctx->tupleSetDesc->attrs[position]->attbyval,
+ 					fctx->tupleSetDesc->attrs[position]->attlen);
+ 		tset->size -= datumGetSize(tset->v[position], fctx->tupleSetDesc->attrs[position]->attbyval,
+ 								fctx->tupleSetDesc->attrs[position]->attlen);
+ 	}
+ 	tset->v[position] = myDatumCopy(value, fctx->tupleSetDesc->attrs[position]->attbyval,
+ 								fctx->tupleSetDesc->attrs[position]->attlen);
+ 	tset->size += datumGetSize(value, fctx->tupleSetDesc->attrs[position]->attbyval,
+ 							   fctx->tupleSetDesc->attrs[position]->attlen);
+ 	return tset->v[position];
+ }
+ Datum
+ setTSetValueNoCopy(alg_fctx * fctx, TSet tset, Datum value, int position)
+ {
+ 	if ((tset->v[position] != (Datum) NULL) && (tset->t == NULL))
+ 	{
+ 		myDatumFree(tset->v[position], fctx->tupleSetDesc->attrs[position]->attbyval,
+ 					fctx->tupleSetDesc->attrs[position]->attlen);
+ 		tset->size -= datumGetSize(tset->v[position], fctx->tupleSetDesc->attrs[position]->attbyval,
+ 								fctx->tupleSetDesc->attrs[position]->attlen);
+ 	}
+ 	tset->v[position] = value;
+ 	tset->size += datumGetSize(value, fctx->tupleSetDesc->attrs[position]->attbyval,
+ 							   fctx->tupleSetDesc->attrs[position]->attlen);
+ 	return tset->v[position];
+ }
+ 
+ TSet
+ newTSet_(alg_fctx * fctx)
+ {
+ 	TSet		newtset = (TSet) palloc(sizeof(deformedTSet));
+ 
+ 	newtset->t = NULL;
+ 	newtset->size = 0;
+ 	newtset->v = (Datum *) palloc(NUMATT * sizeof(Datum));
+ 	newtset->n = (char *) palloc(NUMATT * sizeof(char));
+ 	int			i = 0;
+ 
+ 	for (i = 0; i < NUMATT; i++)
+ 	{
+ 		newtset->v[i] = (Datum) NULL;
+ 		newtset->n[i] = 'n';
+ 	}
+ 	newtset->tids = NULL;
+ 	return newtset;
+ }
+ 
+ DTuple
+ newDTuple(alg_fctx * fctx, int numAtt)
+ {
+ 	DTuple		tuple = (DTuple) palloc(sizeof(deformedTuple));
+ 
+ 	tuple->t = NULL;
+ 	tuple->deformed = false;
+ 	tuple->v = (Datum *) palloc((numAtt + DTupleTupleHeaderSize) * sizeof(Datum));
+ 	tuple->n = (char *) palloc((numAtt + DTupleTupleHeaderSize) * sizeof(char));
+ 	return tuple;
+ }
+ bool
+ tsetAttIsNull(TSet tset, int pos)
+ {
+ 	if (tset->v != NULL)
+ 		return (tset->n[pos] == 'n');
+ 	else
+ 		return heap_attisnull(tset->t, pos + 1);
+ }
+ 
+ TSet
+ newTSet(alg_fctx * fctx, HeapTuple tupleTSet)
+ {
+ 	TSet		newtset = (TSet) palloc(sizeof(deformedTSet));
+ 
+ 	newtset->size = tupleSize(tupleTSet);
+ 	newtset->t = tupleTSet;
+ 	newtset->v = (Datum *) palloc(NUMATT * sizeof(Datum));
+ 	newtset->n = (char *) palloc(NUMATT * sizeof(char));
+ 	heap_deformtuple(tupleTSet, fctx->tupleSetDesc, newtset->v, newtset->n);
+ 	newtset->tids = (bytea *) newtset->v[LABELS];
+ 	return newtset;
+ }
+ 
+ TSet
+ newTSet___(alg_fctx * fctx, HeapTuple tupleTSet)
+ {
+ 	TSet		newtset = (TSet) palloc(sizeof(deformedTSet));
+ 
+ 	newtset->size = tupleSize(tupleTSet);
+ 	newtset->t = tupleTSet;
+ 	newtset->v = NULL;
+ 	newtset->n = NULL;
+ 	bool		isnull;
+ 
+ 	newtset->tids = (bytea *) fastgetattr(tupleTSet, LABELS_ALIGNED, fctx->tupleSetDesc, &isnull);
+ 	return newtset;
+ }
+ 
+ TSet
+ newTSet____(alg_fctx * fctx)
+ {
+ 	TSet		newtset = (TSet) palloc(sizeof(deformedTSet));
+ 
+ 	newtset->size = 0;
+ 	newtset->t = NULL;
+ 	newtset->v = NULL;
+ 	newtset->n = NULL;
+ 	newtset->tids = NULL;
+ 	return newtset;
+ }
+ char *
+ printTSetToCStr(alg_fctx * fctx, bytea *b)
+ {
+ 	int			numSets = numOfTuplesInTSet(fctx, b);
+ 	char	   *tsetStr;
+ 
+ 	if (numSets > 0)
+ 	{
+ 		int			i;
+ 
+ 		tsetStr = palloc(1600 * sizeof(char));
+ 		char	   *to = tsetStr;
+ 		ItemPointerData *ctid = palloc(sizeof(ItemPointerData));
+ 
+ 		for (i = 0; i < numSets; i++)
+ 		{
+ 			memcpy(ctid, VARDATA(b) + i * (sizeof(ItemPointerData) + 1) + 1, sizeof(ItemPointerData));
+ 			char		tempStr[100];
+ 
+ 			sprintf(tempStr, "[TRID:%d, ROWID:%d,%d,%d]"
+ 				,getTableIdOfRelsOidInTSet(fctx, b, i), ctid->ip_blkid.bi_hi,
+ 					ctid->ip_blkid.bi_lo, ctid->ip_posid);
+ 			to = (char *) stpcpy(to, tempStr);
+ 		}
+ 		pfree(ctid);
+ 	}
+ 	else
+ 	{
+ 		tsetStr = palloc(8 * sizeof(char));
+ 		char	   *to = tsetStr;
+ 
+ 		to = (char *) stpcpy(to, "[]");
+ 	}
+ 	return tsetStr;
+ }
+ void
+ printTSet(alg_fctx * fctx, HeapTuple tset)
+ {
+ 	if (tset == NULL)
+ 		elog(ERROR, "tset in printTSet was NULL!");
+ 	Datum		values[NUMATT];
+ 	char		nulls[NUMATT];
+ 
+ 	heap_deformtuple(tset, fctx->tupleSetDesc, values, nulls);
+ 	bytea	   *b = (bytea *) values[LABELS];
+ 	char	   *tsetStr = printTSetToCStr(fctx, b);
+ 	bool		tofree = true;
+ 	int			bufSize = tupleSize(tset);
+ 	char	   *buf = (char *) palloc(bufSize);
+ 	char	   *to = buf;
+ 
+ 	buf = addAndExpandStringBy(buf, &bufSize, tsetStr, BLCKSZ, &to, &tofree);
+ 	pfree(tsetStr);
+ 	int			i = 0;
+ 
+ 	for (i = FIRSTATT; i < fctx->tupleSetNatt; i++)
+ 		if (nulls[i] == ' ')
+ 		{
+ 			buf = addAndExpandStringBy(buf, &bufSize, ",", BLCKSZ, &to, &tofree);
+ 			buf = addAndExpandStringBy(buf, &bufSize, SPI_getvalue(tset, fctx->tupleSetDesc, i + 1)
+ 									   ,BLCKSZ, &to, &tofree);
+ 		}
+ 		else
+ 			buf = addAndExpandStringBy(buf, &bufSize, ", NULL", BLCKSZ, &to, &tofree);
+ 	/* PRINT TO SCREEN HERE (DON'T DELETE!!). */
+ 	elog(INFO, "%s", buf);
+ 	if (tofree)
+ 		pfree(buf);
+ }
+ void
+ printDTSet(alg_fctx * fctx, TSet tset)
+ {
+ 	if (tset == NULL)
+ 		elog(ERROR, "tset in printTSet was NULL!");
+ 	Datum	   *values;
+ 	char	   *nulls;
+ 
+ 	values = tset->v;
+ 	nulls = tset->n;
+ 	bytea	   *b = (bytea *) values[LABELS];
+ 	char	   *tsetStr = printTSetToCStr(fctx, b);
+ 	bool		tofree = true;
+ 	int			bufSize = 10 * (tset->size + 1);
+ 	char	   *buf = (char *) palloc(bufSize);
+ 	char	   *to = buf;
+ 
+ 	buf = addAndExpandStringBy(buf, &bufSize, tsetStr, BLCKSZ, &to, &tofree);
+ 	pfree(tsetStr);
+ 	int			i = 0;
+ 	char		value[1000];
+ 
+ 	for (i = FIRSTATT; i < fctx->tupleSetNatt; i++)
+ 		if (nulls[i] == ' ')
+ 		{
+ 			buf = addAndExpandStringBy(buf, &bufSize, ",", BLCKSZ, &to, &tofree);
+ 			sprintf(value, "%d", (int) values[i]);
+ 			buf = addAndExpandStringBy(buf, &bufSize, value
+ 									   ,BLCKSZ, &to, &tofree);
+ 		}
+ 		else
+ 			buf = addAndExpandStringBy(buf, &bufSize, ", NULL", BLCKSZ, &to, &tofree);
+ 	/* PRINT TO SCREEN HERE (DON'T DELETE!!). */
+ 	elog(INFO, "%s", buf);
+ 	if (tofree)
+ 		pfree(buf);
+ }
+ void
+ printDTuple(alg_fctx * fctx, DTuple dtuple, int tupleRelID)
+ {
+ 	if (dtuple == NULL)
+ 		elog(ERROR, "dtuple in printTSet was NULL!");
+ 	Datum	   *values = dtuple->v;
+ 	char	   *nulls = dtuple->n;
+ 	bool		tofree = true;
+ 	int			bufSize = 256 * sizeof(char);
+ 	char	   *buf = (char *) palloc(bufSize);
+ 	char	   *to = buf;
+ 	int			i = 0;
+ 	char		value[1000];
+ 
+ 	sprintf(value, "RelID[%d]", tupleRelID);
+ 	buf = addAndExpandStringBy(buf, &bufSize, value, BLCKSZ, &to, &tofree);
+ 	for (i = 0; i < fctx->relsNAtt[tupleRelID]; i++)
+ 		if (nulls[i] == ' ')
+ 		{
+ 			buf = addAndExpandStringBy(buf, &bufSize, ",", BLCKSZ, &to, &tofree);
+ 			sprintf(value, "%d", (int) values[i]);
+ 			buf = addAndExpandStringBy(buf, &bufSize, value
+ 									   ,BLCKSZ, &to, &tofree);
+ 		}
+ 		else
+ 			buf = addAndExpandStringBy(buf, &bufSize, ", NULL", BLCKSZ, &to, &tofree);
+ 	/* PRINT TO SCREEN HERE (DON'T DELETE!!). */
+ 	elog(INFO, "%s", buf);
+ 	if (tofree)
+ 		pfree(buf);
+ }
+ 
+ /*
+  * Creates a bytea containing a the set of Rowids of the tuple set.
+  * including the tableOidinRelsOID preceding each one.
+  * we use one byte for each tableOidinRelsOID i.e there could be only
+  * 256 tables (more than enough).
+  * remember to FREE the returned value!
+   */
+ bytea *
+ createNewTSetRowIDs(alg_fctx * fctx, ItemPointer tid, int tupleInRelsOid)
+ {
+ 	bytea	   *b = palloc(VARHDRSZ + 1 + sizeof(ItemPointerData));
+ 
+ 	VARATT_SIZEP(b) = 1 + sizeof(ItemPointerData) + VARHDRSZ;
+ 	VARDATA(b)[0] = (char) tupleInRelsOid;
+ 	memcpy(VARDATA(b) + 1, tid, sizeof(ItemPointerData));
+ 	return b;
+ }
+ bytea *
+ addRowIDtoTSet__(alg_fctx * fctx, bytea *baseSet, bytea *setContainingTRID
+ 				 ,int tupleInRelsOid)
+ {
+ 	int			i = 0;
+ 	int			SCTRIDnumTSets = numOfTuplesInTSet(fctx, setContainingTRID);
+ 	int			SCTRIDpos = -1;
+ 
+ 	for (i = 0; i < SCTRIDnumTSets; i++)
+ 		if (tupleInRelsOid == getTableIdOfRelsOidInTSet(fctx, setContainingTRID, i))
+ 		{
+ 			SCTRIDpos = i;
+ 			break;
+ 		}
+ 	if (SCTRIDpos < 0)
+ 		elog(ERROR, "Incorrect input in addRowIDtoTSet__");
+ 	int			biggerPosition = -1;
+ 	int			numTSets = numOfTuplesInTSet(fctx, baseSet);
+ 	int			relID = -1;
+ 
+ 	for (i = 0; i < numTSets; i++)
+ 	{
+ 		relID = getTableIdOfRelsOidInTSet(fctx, baseSet, i);
+ 		if (relID == tupleInRelsOid)
+ 		{
+ 			bytea	   *sameb = palloc(VARATT_SIZEP(baseSet));
+ 
+ 			memcpy(sameb, baseSet, VARATT_SIZEP(baseSet));
+ 			return sameb;
+ 		}
+ 		else if (relID > tupleInRelsOid)
+ 		{
+ 			biggerPosition = i;
+ 			break;
+ 		}
+ 	}
+ 	if (biggerPosition < 0)
+ 		biggerPosition = numTSets + 1;
+ 	else
+ 		biggerPosition++;
+ 	bytea	   *outb = palloc(VARATT_SIZEP(baseSet) + 1 + sizeof(ItemPointerData));
+ 
+ 	memcpy(outb, baseSet, VARHDRSZ + (biggerPosition - 1) * (1 + sizeof(ItemPointerData)));
+ 	VARDATA(outb)[(biggerPosition - 1) * (1 + sizeof(ItemPointerData))] = (char) tupleInRelsOid;
+ 	memcpy(VARDATA(outb) + (biggerPosition - 1) * (1 + sizeof(ItemPointerData)) + 1
+ 		   ,VARDATA(setContainingTRID) + SCTRIDpos * (1 + sizeof(ItemPointerData)) + 1
+ 		   ,sizeof(ItemPointerData));
+ 	memcpy(VARDATA(outb) + (biggerPosition) * (1 + sizeof(ItemPointerData))
+ 	 ,VARDATA(baseSet) + (biggerPosition - 1) * (1 + sizeof(ItemPointerData))
+ 		   ,VARATT_SIZEP(baseSet) - VARHDRSZ - (biggerPosition - 1) * (1 + sizeof(ItemPointerData)));
+ 	VARATT_SIZEP(outb) += 1 + sizeof(ItemPointerData);
+ 	return outb;
+ }
+ bytea *
+ addRowIDtoTSet_(alg_fctx * fctx, bytea *b, ItemPointer tid, int tupleInRelsOid)
+ {
+ 	int			i = 0;
+ 	int			biggerPosition = -1;
+ 	int			numTSets = numOfTuplesInTSet(fctx, b);
+ 	int			relID = -1;
+ 
+ 	for (i = 0; i < numTSets; i++)
+ 	{
+ 		relID = getTableIdOfRelsOidInTSet(fctx, b, i);
+ 		if (relID == tupleInRelsOid)
+ 		{
+ 			bytea	   *sameb = palloc(VARATT_SIZEP(b));
+ 
+ 			memcpy(sameb, b, VARATT_SIZEP(b));
+ 			return sameb;
+ 		}
+ 		else if (relID > tupleInRelsOid)
+ 		{
+ 			biggerPosition = i;
+ 			break;
+ 		}
+ 	}
+ 	if (biggerPosition < 0)
+ 		biggerPosition = numTSets + 1;
+ 	else
+ 		biggerPosition++;
+ 	bytea	   *outb = palloc(VARATT_SIZEP(b) + 1 + sizeof(ItemPointerData));
+ 
+ 	memcpy(outb, b, VARHDRSZ + (biggerPosition - 1) * (1 + sizeof(ItemPointerData)));
+ 	VARDATA(outb)[(biggerPosition - 1) * (1 + sizeof(ItemPointerData))] = (char) tupleInRelsOid;
+ 	memcpy(VARDATA(outb) + (biggerPosition - 1) * (1 + sizeof(ItemPointerData)) + 1
+ 		   ,tid, sizeof(ItemPointerData));
+ 	memcpy(VARDATA(outb) + (biggerPosition) * (1 + sizeof(ItemPointerData))
+ 		   ,VARDATA(b) + (biggerPosition - 1) * (1 + sizeof(ItemPointerData))
+ 		   ,VARATT_SIZEP(b) - VARHDRSZ - (biggerPosition - 1) * (1 + sizeof(ItemPointerData)));
+ 	VARATT_SIZEP(outb) += 1 + sizeof(ItemPointerData);
+ 	return outb;
+ }
+ bytea *
+ addRowIDtoTSet(alg_fctx * fctx, TSet tset, ItemPointer tid, int tupleInRelsOid)
+ {
+ 	return ((bytea *) addRowIDtoTSet_(fctx, tset->tids
+ 									  ,tid, tupleInRelsOid));
+ }
+ bytea *
+ mergeRowIDs(alg_fctx * fctx, bytea *bLeft, bytea *bRight)
+ {
+ 	bytea	   *tempOutb = palloc(VARATT_SIZEP(bLeft) + VARATT_SIZEP(bRight) - VARHDRSZ);
+ 	int			maxl = numOfTuplesInTSet(fctx, bLeft);
+ 	int			maxr = numOfTuplesInTSet(fctx, bRight);
+ 	int			l = 0,
+ 				r = 0;
+ 	int			lTID = -1,
+ 				rTID = -1;
+ 	int			pos = 0;
+ 
+ 	while (true)
+ 	{
+ 		lTID = getTableIdOfRelsOidInTSet(fctx, bLeft, l);
+ 		rTID = getTableIdOfRelsOidInTSet(fctx, bRight, r);
+ 		if (lTID < rTID)
+ 		{
+ 			memcpy(VARDATA(tempOutb) + pos * (1 + sizeof(ItemPointerData))
+ 				   ,VARDATA(bLeft) + l * (1 + sizeof(ItemPointerData)), (1 + sizeof(ItemPointerData)));
+ 			l++;
+ 		}
+ 		else
+ 		{
+ 			memcpy(VARDATA(tempOutb) + pos * (1 + sizeof(ItemPointerData))
+ 				   ,VARDATA(bRight) + r * (1 + sizeof(ItemPointerData)), (1 + sizeof(ItemPointerData)));
+ 			if (lTID == rTID)
+ 			{
+ 				r++;
+ 				l++;
+ 			}
+ 			else
+ 				r++;
+ 		}
+ 		pos++;
+ 		if ((l == maxl) && (r == maxr))
+ 		{
+ 			break;
+ 		}
+ 		else if (l == maxl)
+ 		{
+ 			memcpy(VARDATA(tempOutb) + pos * (1 + sizeof(ItemPointerData))
+ 				   ,VARDATA(bRight) + r * (1 + sizeof(ItemPointerData))
+ 				   ,VARATT_SIZEP(bRight) - VARHDRSZ - r * (1 + sizeof(ItemPointerData)));
+ 			pos += maxr - r;
+ 			break;
+ 		}
+ 		else if (r == maxr)
+ 		{
+ 			memcpy(VARDATA(tempOutb) + pos * (1 + sizeof(ItemPointerData))
+ 				   ,VARDATA(bLeft) + l * (1 + sizeof(ItemPointerData))
+ 				   ,VARATT_SIZEP(bLeft) - VARHDRSZ - l * (1 + sizeof(ItemPointerData)));
+ 			pos += maxl - l;
+ 			break;
+ 		}
+ 	}
+ 	VARATT_SIZEP(tempOutb) = VARHDRSZ + pos * (1 + sizeof(ItemPointerData));
+ 	repalloc(tempOutb, VARATT_SIZEP(tempOutb));
+ 	return tempOutb;
+ }
+ bool
+ areTSetsEqual(alg_fctx * fctx, TSet tset1, TSet tset2)
+ {
+ 	if (VARATT_SIZEP(tset1->tids) != VARATT_SIZEP(tset2->tids))
+ 		return false;
+ 	return (memcmp(tset1->tids, tset2->tids, VARATT_SIZEP(tset1->tids)) == 0);
+ }
+ bool
+ isLeftSubsumedRightTset(alg_fctx * fctx, TSet left, TSet right)
+ {
+ 	bytea	   *bLeft = left->tids;
+ 	bytea	   *bRight = right->tids;
+ 
+ 	if (VARATT_SIZEP(bLeft) > VARATT_SIZEP(bRight))
+ 		return false;
+ 	int			i = 0;
+ 	int			leftPos = 0;
+ 	int			leftRelOid = 0;
+ 	int			rightRelOid = 0;
+ 	int			numLeftTuples = numOfTuplesInTSet(fctx, bLeft);
+ 	int			numRightTuples = numOfTuplesInTSet(fctx, bRight);
+ 
+ 	leftRelOid = getTableIdOfRelsOidInTSet(fctx, bLeft, leftPos);
+ 	for (i = 0; i < numRightTuples; i++)
+ 	{
+ 		rightRelOid = getTableIdOfRelsOidInTSet(fctx, bRight, i);
+ 		if (leftRelOid == rightRelOid && (memcmp(VARDATA(bLeft) + leftPos * (1 + sizeof(ItemPointerData))
+ 												 ,VARDATA(bRight) + i * (1 + sizeof(ItemPointerData)), 1 + (sizeof(ItemPointerData))) == 0))
+ 		{
+ 			leftPos++;
+ 			if (leftPos == numLeftTuples)
+ 				return true;
+ 			leftRelOid = getTableIdOfRelsOidInTSet(fctx, bLeft, leftPos);
+ 		}
+ 	}
+ 	return false;
+ }
+ bool
+ heap_deformtuple_iterative(HeapTuple tuple,
+ 						   TupleDesc tupleDesc,
+ 						   Datum *values,
+ 						   char *nulls, deformTupleIterativeState * ds,
+ 						   int tillAttNum, bool finishAll)
+ {
+ 	if (ds->firstIteration)
+ 	{
+ 		ds->firstIteration = false;
+ 		ds->tup = tuple->t_data;
+ 		ds->att = tupleDesc->attrs;
+ 		ds->tdesc_natts = tupleDesc->natts;
+ 		/* ptr to null bitmask in tuple */
+ 		ds->bp = ds->tup->t_bits;
+ 		/* can we use/set attcacheoff? */
+ 		ds->slow = false;
+ 		ds->natts = ds->tup->t_natts;
+ 
+ 		/*
+ 		 * In inheritance situations, it is possible that the given tuple
+ 		 * tually has more fields than the caller is expecting.  Don't run the
+ 		 * end of the caller's arrays.
+ 		 */
+ 		ds->natts = Min(ds->natts, ds->tdesc_natts);
+ 		ds->tp = (char *) ds->tup + ds->tup->t_hoff;
+ 		ds->off = 0;
+ 		ds->attnum = 0;
+ 	}
+ 	for (; ds->attnum < ds->natts; ds->attnum++)
+ 	{
+ 		if (HeapTupleHasNulls(tuple) && att_isnull(ds->attnum, ds->bp))
+ 		{
+ 			values[ds->attnum] = (Datum) 0;
+ 			nulls[ds->attnum] = 'n';
+ 			/* can't use attcacheoff anymore */
+ 			ds->slow = true;
+ 			if (!finishAll && ds->attnum >= tillAttNum)
+ 			{
+ 				ds->attnum++;
+ 				if (ds->attnum < ds->natts)
+ 					return true;
+ 				else
+ 					return false;
+ 			}
+ 			else
+ 				continue;
+ 		}
+ 		nulls[ds->attnum] = ' ';
+ 		if (!ds->slow && ds->att[ds->attnum]->attcacheoff >= 0)
+ 			ds->off = ds->att[ds->attnum]->attcacheoff;
+ 		else
+ 		{
+ 			ds->off = att_align(ds->off, ds->att[ds->attnum]->attalign);
+ 			if (!ds->slow)
+ 				ds->att[ds->attnum]->attcacheoff = ds->off;
+ 		}
+ 		values[ds->attnum] = fetchatt(ds->att[ds->attnum], ds->tp + ds->off);
+ 		ds->off = att_addlength(ds->off, ds->att[ds->attnum]->attlen, ds->tp + ds->off);
+ 		if (ds->att[ds->attnum]->attlen <= 0)
+ 			/* can't use attcacheoff anymore */
+ 			ds->slow = true;
+ 		if (!finishAll && ds->attnum >= tillAttNum)
+ 		{
+ 			ds->attnum++;
+ 			if (ds->attnum < ds->natts)
+ 				return true;
+ 			else
+ 				return false;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * If tuple doesn't have all the atts indicated by tupleDesc, read the est
+ 	 * as null
+ 	 */
+ 	for (; ds->attnum < ds->tdesc_natts; ds->attnum++)
+ 	{
+ 		values[ds->attnum] = (Datum) 0;
+ 		nulls[ds->attnum] = 'n';
+ 		if (!finishAll && ds->attnum >= tillAttNum)
+ 		{
+ 			ds->attnum++;
+ 			if (ds->attnum < ds->tdesc_natts)
+ 				return true;
+ 			else
+ 				return false;
+ 		}
+ 	}
+ 	return false;
+ }
+ 
+ /*
+  * Will return null on failure
+   */
+ TSet
+ JCCTSetAndTupleAndReturnTSet(alg_fctx * fctx, TSet tset, DTuple tuple
+ 							 ,TupleDesc tupleTupleDesc, int tupleRelID, deformTupleIterativeState * dsTuple)
+ {
+ 	bytea	   *b = tset->tids;
+ 	int			numAtts = fctx->relsNAtt[tupleRelID];
+ 	int			i = 0;
+ 	int			curTSetAtt = -1;
+ 
+ 	for (i = 0; i < numAtts; i++)
+ 	{
+ 		curTSetAtt = fctx->relsAttNumsAtTupleSet[tupleRelID][i];
+ 		if ((tset->n[curTSetAtt] == 'n') || (heap_attisnull(tuple->t, i + 1)))
+ 		{
+ 			if (isResultTupleAttOccursInSomeRelID(fctx, curTSetAtt, b))
+ 			{
+ 				return NULL;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			if (!tuple->deformed)
+ 				heap_deformtuple_iterative(tuple->t, tupleTupleDesc, tuple->v, tuple->n, dsTuple, i, false);
+ 			if (!areAttributesEqual_RT(fctx, curTSetAtt
+ 									   ,tset->v[curTSetAtt]
+ 									   ,tuple->v[i]
+ 									   ))
+ 			{
+ 				return NULL;
+ 			}
+ 		}
+ 	}
+ 	if (!tuple->deformed)
+ 	{
+ 		heap_deformtuple_iterative(tuple->t, tupleTupleDesc, tuple->v, tuple->n, dsTuple, -1, true);
+ 		tuple->tid = (ItemPointer) tuple->v[numAtts];
+ 		tuple->deformed = true;
+ 	}
+ 	for (i = 0; i < numAtts; i++)
+ 	{
+ 		curTSetAtt = fctx->relsAttNumsAtTupleSet[tupleRelID][i];
+ 		if ((tset->n[curTSetAtt] == 'n') && (tuple->n[i] == ' '))
+ 		{
+ 			tset->n[curTSetAtt] = ' ';
+ 			setTSetValue(fctx, tset, tuple->v[i], curTSetAtt);
+ 		}
+ 	}
+ 	bytea	   *outb =
+ 	(bytea *) addRowIDtoTSet_(fctx, b, tuple->tid, tupleRelID);
+ 
+ 	setTSetValueNoCopy(fctx, tset, (Datum) outb, LABELS);
+ 	tset->tids = (bytea *) tset->v[LABELS];
+ 	return tset;
+ }
+ 
+ /*
+  * returns null on failure
+   * */
+ TSet
+ unifyAndJCC(alg_fctx * fctx, TSet leftTSet, Datum *leftTSetVals, char *leftTSetNulls,
+ 			TSet rightTSet, RBITS relationsToCheck, RBITS relationsOfLeft, RBITS relationsOfRight)
+ {
+ 	int			i = 0;
+ 	deformTupleIterativeState dsLeftTSet;
+ 
+ 	dsLeftTSet.firstIteration = true;
+ 	bool		isLeftAttNull,
+ 				isRightAttNull;
+ 
+ 	for (i = FIRSTATT; i < NUMATT; i++)
+ 	{
+ 		isLeftAttNull = (leftTSetNulls[i] == 'n');
+ 		isRightAttNull = (rightTSet->n[i] == 'n');
+ 		if (isLeftAttNull && isRightAttNull)
+ 		{
+ 			if (isResultTupleAttOccursInSomeRelIDWithSpecifiedRelations(fctx, i,
+ 														   relationsToCheck))
+ 			{
+ 				return NULL;
+ 			}
+ 		}
+ 		else if (isLeftAttNull && (!isRightAttNull))
+ 		{
+ 			if (isResultTupleAttOccursInSomeRelIDWithSpecifiedRelations(fctx, i,
+ 									   (relationsToCheck & relationsOfLeft)))
+ 			{
+ 				return NULL;
+ 			}
+ 		}
+ 		else if ((!isLeftAttNull) && isRightAttNull)
+ 		{
+ 			if (isResultTupleAttOccursInSomeRelIDWithSpecifiedRelations(fctx, i,
+ 									  (relationsToCheck & relationsOfRight)))
+ 			{
+ 				return NULL;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			if (!areAttributesEqual_RT(fctx, i, leftTSetVals[i], rightTSet->v[i]))
+ 			{
+ 				return NULL;
+ 			}
+ 		}
+ 	}
+ 	for (i = FIRSTATT; i < NUMATT; i++)
+ 	{
+ 		if ((leftTSetNulls[i] == 'n') && (rightTSet->n[i] == ' '))
+ 		{
+ 			leftTSetNulls[i] = ' ';
+ 			setTSetValue(fctx, leftTSet, rightTSet->v[i], i);
+ 		}
+ 	}
+ 	bytea	   *outb = mergeRowIDs(fctx, leftTSet->tids, rightTSet->tids);
+ 
+ 	setTSetValueNoCopy(fctx, leftTSet, (Datum) outb, LABELS);
+ 	leftTSet->tids = (bytea *) outb;
+ 	return leftTSet;
+ }
+ 
+ TSet
+ simpleUnify(alg_fctx * fctx, TSet leftTSet, Datum *leftTSetVals,
+ 			char *leftTSetNulls, TSet rightTSet)
+ {
+ 	int			i = 0;
+ 
+ 	for (i = FIRSTATT; i < NUMATT; i++)
+ 	{
+ 		if ((leftTSetNulls[i] == 'n') && (rightTSet->n[i] == ' '))
+ 		{
+ 			leftTSetNulls[i] = ' ';
+ 			setTSetValue(fctx, leftTSet, rightTSet->v[i], i);
+ 		}
+ 	}
+ 	bytea	   *outb = mergeRowIDs(fctx, leftTSet->tids, rightTSet->tids);
+ 
+ 	setTSetValueNoCopy(fctx, leftTSet, (Datum) outb, LABELS);
+ 	leftTSet->tids = (bytea *) outb;
+ 	return leftTSet;
+ }
+ 
+ TSet
+ create_tuple_set(alg_fctx * fctx, DTuple inBaseTuple
+ 				 ,TupleDesc baseTupleDesc, int baseTupleInRelsOid)
+ {
+ 	int			i;
+ 	TSet		returnTSet = newTSet_(fctx);
+ 
+ 	returnTSet->n[LABELS] = ' ';
+ 	for (i = 0; i < fctx->relsNAtt[baseTupleInRelsOid]; i++)
+ 	{
+ 		if (inBaseTuple->n[i] == ' ')
+ 		{
+ 			setTSetValue(fctx, returnTSet, inBaseTuple->v[i], fctx->relsAttNumsAtTupleSet[baseTupleInRelsOid][i]);
+ 			returnTSet->n[fctx->relsAttNumsAtTupleSet[baseTupleInRelsOid][i]] = ' ';
+ 		}
+ 	}
+ 	bytea	   *b = createNewTSetRowIDs(fctx, inBaseTuple->tid, baseTupleInRelsOid);
+ 
+ 	setTSetValueNoCopy(fctx, returnTSet, (Datum) b, LABELS);
+ 	returnTSet->tids = b;
+ 	return returnTSet;
+ }
+ bool
+ JCCTupleInTSetAndDTuple(alg_fctx * fctx, DTuple dtuple, int dtupleRelID,
+ 						TSet tset, int tupleRelIDInTSet)
+ {
+ 	int			dtupleNumAtts = fctx->relsNAtt[dtupleRelID];
+ 	int			tsetTupleNumAtts = fctx->relsNAtt[tupleRelIDInTSet];
+ 	int		  **dtupleAtts = fctx->sortedRelsAttNumsAtTupleSet[dtupleRelID];
+ 	int		  **tsetTupleAtts = fctx->sortedRelsAttNumsAtTupleSet[tupleRelIDInTSet];
+ 	int			i = 0,
+ 				j = 0;
+ 
+ 	while (true)
+ 	{
+ 		if (dtupleAtts[i][0] == tsetTupleAtts[j][0])
+ 		{
+ 			if ((tset->n[tsetTupleAtts[j][0]] == 'n') || (dtuple->n[dtupleAtts[i][1]] == 'n'))
+ 				return false;
+ 			if (!areAttributesEqual_RT(fctx, tsetTupleAtts[j][0], tset->v[tsetTupleAtts[j][0]]
+ 									   ,dtuple->v[dtupleAtts[i][1]]))
+ 			{
+ 				return false;
+ 			}
+ 			i++;
+ 			j++;
+ 			if ((i == dtupleNumAtts) || (j == tsetTupleNumAtts))
+ 				return true;
+ 		}
+ 		else if (dtupleAtts[i][0] < tsetTupleAtts[j][0])
+ 		{
+ 			i++;
+ 			if (i == dtupleNumAtts)
+ 				return true;
+ 		}
+ 		else
+ 		{
+ 			j++;
+ 			if (j == tsetTupleNumAtts)
+ 				return true;
+ 		}
+ 	}
+ }
+ 
+ TSet
+ generateAlternativeTupleSet(alg_fctx * fctx, TSet tset, RBITS relsOfTuples,
+ 						  DTuple dtuple, TupleDesc tupleDesc, int tupleRelID)
+ {
+ 	/* In phase 1 we find all the JCC tuples in dtset with dtuple */
+ 	RBITS		toCheckForJCC = 0;
+ 	RBITS		tmpBits = relsOfTuples;
+ 	int			i = -1,
+ 				j = -1;
+ 
+ 	while (tmpBits)
+ 	{
+ 		i++;
+ 		while (tmpBits && !(varNthBitFromLeft(tmpBits, 0)))
+ 		{
+ 			i++;
+ 			tmpBits <<= 1;
+ 		}
+ 		if (!tmpBits)
+ 			break;
+ 		else
+ 			tmpBits <<= 1;
+ 		j++;
+ 		if (tupleRelID == i)
+ 		{
+ 			makeSureDeformedTuple(fctx, dtuple, tupleDesc, tupleRelID);
+ 			if (!(memcmp(dtuple->tid, TSET_GET_CTID(tset, j), (CTID_SIZE))))
+ 			{
+ 				return NULL;
+ 			}
+ 		}
+ 	}
+ 	toCheckForJCC = fctx->bits_scheme_graph[tupleRelID] & relsOfTuples;
+ 	if (toCheckForJCC == 0)
+ 		return NULL;
+ 
+ 	/*
+ 	 * in phase 2 we add all the tuples that are connected to the tuples that
+ 	 * were found JCC without the rejected ones in the component. i.e. we
+ 	 * create a tuple set that is a connected component that contains dtuple.
+ 	 */
+ 	RBITS		tuplesToAddToNewTSet = 0;
+ 	RBITS		tuplesRejected = 0;
+ 
+ 	makeSureDeformedTSet(fctx, tset);
+ 	makeSureDeformedTuple(fctx, dtuple, tupleDesc, tupleRelID);
+ 	tmpBits = toCheckForJCC;
+ 	i = -1;
+ 	while (tmpBits)
+ 	{
+ 		i++;
+ 		while (tmpBits && !(varNthBitFromLeft(tmpBits, 0)))
+ 		{
+ 			i++;
+ 			tmpBits <<= 1;
+ 		}
+ 		if (!tmpBits)
+ 			break;
+ 		else
+ 			tmpBits <<= 1;
+ 		if (JCCTupleInTSetAndDTuple(fctx, dtuple, tupleRelID, tset, i))
+ 			varSetNthBitFromLeft(tuplesToAddToNewTSet, i);
+ 		else
+ 			varSetNthBitFromLeft(tuplesRejected, i);
+ 	}
+ 	if (tuplesToAddToNewTSet == 0)
+ 		return NULL;
+ 	varUnSetNthBitFromLeft(tuplesToAddToNewTSet, tupleRelID);
+ 	/* find all the rest of the component */
+ 	tmpBits = ~tuplesToAddToNewTSet;
+ 	while (tmpBits != tuplesToAddToNewTSet)
+ 	{
+ 		tmpBits = tuplesToAddToNewTSet;
+ 		for (i = 0; i < fctx->numRels; i++)
+ 			if ((i != tupleRelID) && (!varNthBitFromLeft(tuplesRejected, i))
+ 				&& (varNthBitFromLeft(relsOfTuples, i)))
+ 				if (tuplesToAddToNewTSet & fctx->bits_scheme_graph[i])
+ 					varSetNthBitFromLeft(tuplesToAddToNewTSet, i);
+ 	}
+ 	/* Lets create a new tuple set with the base tuple */
+ 	int			curTSetAtt;
+ 	int			numAtts = fctx->relsNAtt[tupleRelID];
+ 	TSet		newTSet = newTSet_(fctx);
+ 
+ 	for (i = FIRSTATT; i < NUMATT; i++)
+ 		newTSet->n[i] = 'n';
+ 	newTSet->n[LABELS] = ' ';
+ 	for (i = 0; i < numAtts; i++)
+ 	{
+ 		curTSetAtt = fctx->relsAttNumsAtTupleSet[tupleRelID][i];
+ 		if (dtuple->n[i] == ' ')
+ 		{
+ 			setTSetValue(fctx, newTSet, dtuple->v[i], curTSetAtt);
+ 			newTSet->n[curTSetAtt] = ' ';
+ 		}
+ 	}
+ 	/* Lets add the rest of the tuples. */
+ 	int			k;
+ 	int			numTupleInNewTSet = 1;
+ 
+ 	varUnSetNthBitFromLeft(tuplesToAddToNewTSet, tupleRelID);
+ 	tmpBits = tuplesToAddToNewTSet;
+ 	i = -1;
+ 	while (tmpBits)
+ 	{
+ 		i++;
+ 		while (tmpBits && !(varNthBitFromLeft(tmpBits, 0)))
+ 		{
+ 			i++;
+ 			tmpBits <<= 1;
+ 		}
+ 		if (!tmpBits)
+ 			break;
+ 		else
+ 			tmpBits <<= 1;
+ 		numTupleInNewTSet++;
+ 		numAtts = fctx->relsNAtt[i];
+ 		for (k = 0; k < numAtts; k++)
+ 		{
+ 			curTSetAtt = fctx->relsAttNumsAtTupleSet[i][k];
+ 			if ((newTSet->n[curTSetAtt] == 'n') && (tset->n[curTSetAtt] == ' '))
+ 			{
+ 				newTSet->n[curTSetAtt] = ' ';
+ 				setTSetValue(fctx, newTSet, tset->v[curTSetAtt], curTSetAtt);
+ 			}
+ 		}
+ 	}
+ 	/* finally lets build the tableID:CTID pairs array. */
+ 	int			z = -1;
+ 	RBITS		tmpRelsOfTuples = relsOfTuples;
+ 
+ 	tmpBits = tuplesToAddToNewTSet;
+ 	varSetNthBitFromLeft(tmpBits, tupleRelID);
+ 	i = -1, j = -1;
+ 	bytea	   *outb = palloc(VARHDRSZ + (numTupleInNewTSet * TABLEID_CTID_SIZE));
+ 
+ 	VARATT_SIZEP(outb) = VARHDRSZ + (numTupleInNewTSet * TABLEID_CTID_SIZE);
+ 	while (tmpBits)
+ 	{
+ 		i++;
+ 		if (varNthBitFromLeft(tmpRelsOfTuples, 0))
+ 			z++;
+ 		while (tmpBits && !(varNthBitFromLeft(tmpBits, 0)))
+ 		{
+ 			i++;
+ 			tmpBits <<= 1;
+ 			tmpRelsOfTuples <<= 1;
+ 			if (varNthBitFromLeft(tmpRelsOfTuples, 0))
+ 				z++;
+ 		}
+ 		if (!tmpBits)
+ 			break;
+ 		else
+ 		{
+ 			tmpBits <<= 1;
+ 			tmpRelsOfTuples <<= 1;
+ 		}
+ 		j++;
+ 		VARDATA(outb)[j * TABLEID_CTID_SIZE] = (char) i;
+ 		if (i == tupleRelID)
+ 			memcpy(VARDATA(outb) + (j * TABLEID_CTID_SIZE) + 1
+ 				   ,dtuple->tid
+ 				   ,CTID_SIZE);
+ 		else
+ 			memcpy(VARDATA(outb) + (j * TABLEID_CTID_SIZE) + 1
+ 				   ,VARDATA(tset->tids) + (z * TABLEID_CTID_SIZE) + 1
+ 				   ,CTID_SIZE);
+ 	}
+ 	newTSet->v[LABELS] = (Datum) outb;
+ 	newTSet->tids = outb;
+ 	return newTSet;
+ }
+ HeapTuple
+ clearValuesFromTSet(alg_fctx * fctx, TSet tset, char *nulls)
+ {
+ 	Datum		valuesTSet[1];
+ 
+ 	valuesTSet[0] = (Datum) tset->tids;
+ 	return (heap_formtuple(fctx->tupleSetDesc, valuesTSet, nulls));
+ }
diff -crN pgsql/contrib/fulldisjunctions/tsetfuncs.h pgsql-fd/contrib/fulldisjunctions/tsetfuncs.h
*** pgsql/contrib/fulldisjunctions/tsetfuncs.h	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/tsetfuncs.h	2006-07-29 20:54:13.000000000 -0400
***************
*** 0 ****
--- 1,118 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ #ifndef TSETFUNCS_H
+ #define TSETFUNCS_H
+ 
+ #ifdef DEBUG
+ #else
+ #define NDEBUG 
+ #endif
+ 
+ #include <assert.h>
+ 
+ #include "tset.h"
+ #include "algstructs.h"
+ #include "utils/palloc.h"
+ #define numOfTuplesInTSet(fctx, b)\
+ ((VARATT_SIZEP(b) - VARHDRSZ) / (1 + sizeof(ItemPointerData)))
+ #define getTableIdOfRelsOidInTSet(fctx, b, position)\
+ ((int) VARDATA(b)[position * (1 + sizeof(ItemPointerData))])
+ #define makeSureFormedTSet(fctx,tset)\
+ do\
+ {\
+ 	if ((tset)->t == NULL)\
+ 	{\
+ 			(tset)->t = heap_formtuple((fctx)->tupleSetDesc, (tset)->v, (tset)->n);	\
+ 			freeDatumArray(fctx, (tset)->v);\
+ 			pfree((tset)->v);\
+ 			pfree((tset)->n);\
+ 			(tset)->v = NULL;\
+ 			(tset)->n = NULL;\
+ 		{\
+ 				bool		isnull;	\
+ 				(tset)->tids = (bytea *) fastgetattr((tset->t), LABELS_ALIGNED, (fctx)->tupleSetDesc, &isnull);\
+ 		} \
+ 			(tset)->size = tupleSize((tset)->t);\
+ 	} \
+ } while (0)
+ 
+ #define makeSureDeformedTSet(fctx,tset)\
+ do\
+ {\
+ 		if ((tset)->v == NULL)\
+ 	{\
+ 			(tset)->v = (Datum *) palloc(NUMATT * sizeof(Datum));\
+ 			(tset)->n = (char *) palloc(NUMATT * sizeof(char));	\
+ 			heap_deformtuple((tset->t), (fctx)->tupleSetDesc, (tset)->v, (tset)->n);\
+ 	} \
+ } while (0)
+ 
+ #define makeSureDeformedTuple(fctx,tuple,tupleDesc,relID)\
+ do\
+ {\
+ 		if ((tuple)->deformed == false)\
+ 	{\
+ 			heap_deformtuple((tuple->t), (tupleDesc), (tuple)->v, (tuple)->n);\
+ 			tuple->tid = (ItemPointer) tuple->v[fctx->relsNAtt[relID]];\
+ 			(tuple)->deformed = true;\
+ 	} \
+ } while (0)
+ 
+ #define freeDatumArray(fctx,arr)\
+ do\
+ {\
+ 		int			i;\
+ 		for (i = 0; i < NUMATT; i++)\
+ 			if ((arr)[i] != (Datum) NULL)\
+ 				myDatumFree((arr)[i], (fctx)->tupleSetDesc->attrs[i]->attbyval, \
+ 							(fctx)->tupleSetDesc->attrs[i]->attlen);\
+ } while (0)
+ 
+ #define setTSet(fctx,tset,tupleTSet,isnull)\
+ do\
+ {\
+ 	(tset)->size = tupleSize((tupleTSet));\
+ 		(tset)->t = (tupleTSet);\
+ 		(tset)->tids = (bytea *) fastgetattr((tupleTSet), LABELS_ALIGNED, (fctx)->tupleSetDesc, &(isnull));\
+ } while (0)
+ 
+ extern bool tsetAttIsNull(TSet tset, int pos);
+ extern void freeTSet(alg_fctx * fctx, TSet tset);
+ extern TSet newEmptyTSet(alg_fctx * fctx);
+ extern Datum setTSetValue(alg_fctx * fctx, TSet tset, Datum value
+ 			 ,int position);
+ extern Datum setTSetValueNoCopy(alg_fctx * fctx, TSet tset, Datum value, int position);
+ extern TSet newTSet_(alg_fctx * fctx);
+ extern TSet newTSet(alg_fctx * fctx, HeapTuple tset);
+ extern DTuple newDTuple(alg_fctx * fctx, int numAtt);
+ extern TSet newTSet___(alg_fctx * fctx, HeapTuple tset);
+ extern TSet newTSet____(alg_fctx * fctx);
+ extern char *printTSetToCStr(alg_fctx * fctx, bytea *b);
+ extern void printTSet(alg_fctx * fctx, HeapTuple tset);
+ extern void printDTSet(alg_fctx * fctx, TSet tset);
+ extern void printDTuple(alg_fctx * fctx, DTuple dtuple, int tupleRelID);
+ extern bytea *createNewTSetRowIDs(alg_fctx * fctx, ItemPointer tid, int tupleInRelsOid);
+ extern bytea *addRowIDtoTSet__(alg_fctx * fctx, bytea *baseSet, bytea *setContainingTRID
+ 				 ,int tupleInRelsOid);
+ extern bytea *addRowIDtoTSet_(alg_fctx * fctx, bytea *b, ItemPointer tid, int tupleInRelsOid);
+ extern bytea *addRowIDtoTSet(alg_fctx * fctx, TSet tset, ItemPointer tid, int tupleInRelsOid);
+ extern bytea *mergeRowIDs(alg_fctx * fctx, bytea *bLeft, bytea *bRight);
+ extern bool areTSetsEqual(alg_fctx * fctx, TSet tset1, TSet tset2);
+ extern bool isLeftSubsumedRightTset(alg_fctx * fctx, TSet left, TSet right);
+ extern TSet JCCTSetAndTupleAndReturnTSet(alg_fctx * fctx, TSet tset, DTuple tuple
+ 							 ,TupleDesc tupleTupleDesc, int tupleRelID, deformTupleIterativeState * dsTuple);
+ extern TSet unifyAndJCC(alg_fctx * fctx, TSet leftTSet, Datum *leftTSetVals,
+ 			char *leftTSetNulls, TSet rightTSet, RBITS relationsToCheck,
+ 			RBITS relationsOfLeft, RBITS relationsOfRight);
+ extern TSet simpleUnify(alg_fctx * fctx, TSet leftTSet, Datum *leftTSetVals,
+ 			char *leftTSetNulls, TSet rightTSet);
+ extern TSet create_tuple_set(alg_fctx * fctx, DTuple inBaseTuple
+ 				 ,TupleDesc baseTupleDesc, int baseTupleInRelsOid);
+ extern TSet generateAlternativeTupleSet(alg_fctx * fctx, TSet tset, RBITS relsOfTuples,
+ 						 DTuple dtuple, TupleDesc tupleDesc, int tupleRelID);
+ extern HeapTuple clearValuesFromTSet(alg_fctx * fctx, TSet tset, char *nulls);
+ 
+ #endif   /* TSETFUNCS_H */
diff -crN pgsql/contrib/fulldisjunctions/tset.h pgsql-fd/contrib/fulldisjunctions/tset.h
*** pgsql/contrib/fulldisjunctions/tset.h	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/tset.h	2006-07-29 20:54:13.000000000 -0400
***************
*** 0 ****
--- 1,73 ----
+ /* 
+  * Copyright (c) 2006 Itzhak Fadida.  All Rights Reserved. 
+  * See the README file for a detailed BSD License.
+  * This file and all related files are under the BSD license.
+  * */
+ 
+ #ifndef TSET_H
+ #define TSET_H
+ 
+ #ifdef DEBUG
+ #else
+ #define NDEBUG 
+ #endif
+ 
+ #include <assert.h>
+ 
+ #include "postgres.h"
+ #include "utils/palloc.h"
+ #include "funcapi.h"
+ #define LABELS 0
+ #define LABELS_ALIGNED 1
+  /* first attribute */
+ #define FIRSTATT 1
+ #define LASTATT (fctx->tupleSetNatt-1)
+ #define NUMATT (fctx->tupleSetNatt)
+ #define CTID_SIZE sizeof(ItemPointerData)
+ #define TABLEID_CTID_SIZE (1+CTID_SIZE)
+ typedef struct
+ {
+ 	int			size;
+ 	/* tuple ids. TID or TID AND ROWID. */
+ 	bytea	   *tids;
+ 	HeapTuple	t;
+ 	/* values */
+ 	Datum	   *v;
+ 	/* nulls */
+ 	char	   *n;
+ }	deformedTSet;
+ typedef deformedTSet *TSet;
+ 
+ #define TSET_GET_CTID(tset,relative_pos) VARDATA(tset->tids)+relative_pos*(TABLEID_CTID_SIZE)+1
+ #define DTupleTupleHeaderSize 1
+ typedef struct
+ {
+ 	HeapTuple	t;
+ 	/* values */
+ 	Datum	   *v;
+ 	/* nulls */
+ 	char	   *n;
+ 	ItemPointer tid;
+ 	bool		deformed;
+ }	deformedTuple;
+ typedef deformedTuple *DTuple;
+ typedef struct
+ {
+ 	HeapTupleHeader tup;
+ 	Form_pg_attribute *att;
+ 	int			tdesc_natts;
+ 	/* number of atts to extract */
+ 	int			natts;
+ 	int			attnum;
+ 	/* ptr to tuple data */
+ 	char	   *tp;
+ 	/* offset in tuple data */
+ 	long		off;
+ 	/* ptr to null bitmask in tuple */
+ 	bits8	   *bp;
+ 	/* can we use/set attcacheoff? */
+ 	bool		slow;
+ 	bool		firstIteration;
+ }	deformTupleIterativeState;
+ 
+ #endif   /* TSET_H */
diff -crN pgsql/contrib/fulldisjunctions/uninstall_fulldisjunctions.sql pgsql-fd/contrib/fulldisjunctions/uninstall_fulldisjunctions.sql
*** pgsql/contrib/fulldisjunctions/uninstall_fulldisjunctions.sql	1969-12-31 19:00:00.000000000 -0500
--- pgsql-fd/contrib/fulldisjunctions/uninstall_fulldisjunctions.sql	2006-07-30 15:52:03.000000000 -0400
***************
*** 0 ****
--- 1,13 ----
+ /*
+  * Uninstall SQL Script for Full Disjunctions
+  */
+ SET search_path = public;
+ DROP FUNCTION fulldisjunction(text);
+ DROP FUNCTION fulldisjunction(text,text);
+ DROP FUNCTION fulldisjunction(text,text,boolean);
+ DROP FUNCTION fulldisjunction(text,text,boolean,boolean);
+ DROP FUNCTION fulldisjunction(text,text,boolean,boolean,text);
+ DROP FUNCTION fulldisjunction(text,text,boolean,boolean,text,int);
+ DROP FUNCTION odmbfd(text,text,boolean,boolean,text,int);
+ DROP FUNCTION getfdr(text);
+ DROP FUNCTION getfdr(text,text);
diff -crN pgsql/contrib/Makefile pgsql-fd/contrib/Makefile
*** pgsql/contrib/Makefile	2006-05-30 09:25:57.000000000 -0400
--- pgsql-fd/contrib/Makefile	2006-07-30 16:11:35.000000000 -0400
***************
*** 13,18 ****
--- 13,19 ----
  		dblink		\
  		dbmirror	\
  		earthdistance	\
+ 		fulldisjunctions	\
  		fulltextindex	\
  		fuzzystrmatch	\
  		intagg		\
#2Bruce Momjian
bruce@momjian.us
In reply to: Tzahi Fadida (#1)
Re: [PATCHES] Adding fulldisjunctions to the contrib

I have looked over this addition, and I think I finally understand it.
Given three tables, A, B, C, which join as A->B, B->C, C->A, you can
really join them as A->B->C, and A->C->B. What full disjunction does is
to perform both of those joins, and return a one row for each join. Here
is an example from the README:

Example of an input and output of a full disjunctions:
INPUT:

--A---|---B---|---C--
X---Y-|-Y---Z-|-X---Z
a-|-b-|-b-|-c-|-a-|-d

A,B and C are relations. X,Y and Z are attributes. a,b,c and d are values.

Note that A,B and C are connected in a cycle. That is:
A is connected to B on attribute Y,
B is connected to C on attribute Z,
C is connected to A on attribute X.

The output of the full disjunctions FD(A,B,C):

FD
X---Y---Z
a-|-b-|-c
a-|-b-|-d

This code is pretty complex, so I can see why it should be in /contrib.
Are there reasonable use cases for this capability?

---------------------------------------------------------------------------

Tzahi Fadida wrote:

Hi,
I wish to add the fulldisjunctions function to the contrib.
With the help of Jonah, we (or rather he :) created a patch with
regression tests. The function is finished programmatically but
still a little more code documentation touches and improved error messages
are needed. All the rest was extensively tested.

Attached is the patch.

Works great. Just compiled from a fresh cvs which i patched with the
attached diff. ran the fulldijsjunction.sql in the
share/contrib/fulldisjunction and let it run and it works great.
10x.

--
Regards,
????????Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS: ?see at
http://members.lycos.co.uk/my2nis/spamwarning.html

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

http://www.postgresql.org/docs/faq

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#3Tzahi Fadida
Tzahi.ML@gmail.com
In reply to: Bruce Momjian (#2)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On Friday 11 August 2006 07:18, Bruce Momjian wrote:

I have looked over this addition, and I think I finally understand it.
Given three tables, A, B, C, which join as A->B, B->C, C->A, you can
really join them as A->B->C, and A->C->B. What full disjunction does is
to perform both of those joins, and return a one row for each join. Here

What it does is to return all the possible natural joins, i.e.:
A
B
C
A,B
A,C
...
A,B,C

And, it removes any redundant information so that if we have a tuple
that already contains another tuple's information that tuple is discarded.
Also, note that the full disjunction algorithm i implemented
is commonly used in cases where the scheme graph is cyclic
and thus, you cannot use natural full outer join
to compute the FD.

Finally, you can FD(A,B,C,D,...) any number of relations (limited to 32 in
the implementation) with no regard to the order between them.

A case study and comparison can be found here:
http://www.technion.ac.il/~tzahi/soc.html

is an example from the README:

Example of an input and output of a full disjunctions:
INPUT:

--A---|---B---|---C--
X---Y-|-Y---Z-|-X---Z
a-|-b-|-b-|-c-|-a-|-d

A,B and C are relations. X,Y and Z are attributes. a,b,c and d are
values.

Note that A,B and C are connected in a cycle. That is:
A is connected to B on attribute Y,
B is connected to C on attribute Z,
C is connected to A on attribute X.

The output of the full disjunctions FD(A,B,C):

FD
X---Y---Z
a-|-b-|-c
a-|-b-|-d

This code is pretty complex, so I can see why it should be in /contrib.
Are there reasonable use cases for this capability?

---------------------------------------------------------------------------

Tzahi Fadida wrote:

Hi,
I wish to add the fulldisjunctions function to the contrib.
With the help of Jonah, we (or rather he :) created a patch with
regression tests. The function is finished programmatically but
still a little more code documentation touches and improved error
messages are needed. All the rest was extensively tested.

Attached is the patch.

Works great. Just compiled from a fresh cvs which i patched with the
attached diff. ran the fulldijsjunction.sql in the
share/contrib/fulldisjunction and let it run and it works great.
10x.

--
Regards,
????????Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS: ?see at
http://members.lycos.co.uk/my2nis/spamwarning.html

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

http://www.postgresql.org/docs/faq

--
Regards,
        Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS:  see at
http://members.lycos.co.uk/my2nis/spamwarning.html

#4Bruce Momjian
bruce@momjian.us
In reply to: Tzahi Fadida (#3)
Re: [PATCHES] Adding fulldisjunctions to the contrib

I am still waiting for someone to tell us that they would use this
capability for a real-world problem.

---------------------------------------------------------------------------

Tzahi Fadida wrote:

On Friday 11 August 2006 07:18, Bruce Momjian wrote:

I have looked over this addition, and I think I finally understand it.
Given three tables, A, B, C, which join as A->B, B->C, C->A, you can
really join them as A->B->C, and A->C->B. What full disjunction does is
to perform both of those joins, and return a one row for each join. Here

What it does is to return all the possible natural joins, i.e.:
A
B
C
A,B
A,C
...
A,B,C

And, it removes any redundant information so that if we have a tuple
that already contains another tuple's information that tuple is discarded.
Also, note that the full disjunction algorithm i implemented
is commonly used in cases where the scheme graph is cyclic
and thus, you cannot use natural full outer join
to compute the FD.

Finally, you can FD(A,B,C,D,...) any number of relations (limited to 32 in
the implementation) with no regard to the order between them.

A case study and comparison can be found here:
http://www.technion.ac.il/~tzahi/soc.html

is an example from the README:

Example of an input and output of a full disjunctions:
INPUT:

--A---|---B---|---C--
X---Y-|-Y---Z-|-X---Z
a-|-b-|-b-|-c-|-a-|-d

A,B and C are relations. X,Y and Z are attributes. a,b,c and d are
values.

Note that A,B and C are connected in a cycle. That is:
A is connected to B on attribute Y,
B is connected to C on attribute Z,
C is connected to A on attribute X.

The output of the full disjunctions FD(A,B,C):

FD
X---Y---Z
a-|-b-|-c
a-|-b-|-d

This code is pretty complex, so I can see why it should be in /contrib.
Are there reasonable use cases for this capability?

---------------------------------------------------------------------------

Tzahi Fadida wrote:

Hi,
I wish to add the fulldisjunctions function to the contrib.
With the help of Jonah, we (or rather he :) created a patch with
regression tests. The function is finished programmatically but
still a little more code documentation touches and improved error
messages are needed. All the rest was extensively tested.

Attached is the patch.

Works great. Just compiled from a fresh cvs which i patched with the
attached diff. ran the fulldijsjunction.sql in the
share/contrib/fulldisjunction and let it run and it works great.
10x.

--
Regards,
????????Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS: ?see at
http://members.lycos.co.uk/my2nis/spamwarning.html

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

http://www.postgresql.org/docs/faq

--
Regards,
????????Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS: ?see at
http://members.lycos.co.uk/my2nis/spamwarning.html

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#5Tzahi Fadida
Tzahi.ML@gmail.com
In reply to: Bruce Momjian (#4)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On Saturday 12 August 2006 07:22, Bruce Momjian wrote:

I am still waiting for someone to tell us that they would use this
capability for a real-world problem.

I suggest looking into web applications.
The example here
http://www.technion.ac.il/~tzahi/soc.html

shows a possible 3 separate web resources.
I.e. heterogeneous sources. Naturally, since the sources
did not know each other in advance, they did not form
relations that would not end up cyclic in the scheme graph.
XMLs are usually like these. Obviously you have to turn them into
relations first of course.
In addition, i have recently added a feature where you give alias to column
names so if you have "country" column and a "state" column that really means
country, you can do "country=public.relation_with_state.state,..." dictionary
style. This is commonly needed in web applications.

Here is another example (improvising :) ):
site1: user_name,email,favorite_book_isbn
site2: user_name,email,favorite_chat_room
site3: user_name,credit_card

So, let's say i wanted to advertise discounts using a certain credit card
for certain books, i would do FD(site1,site2,site3).
Natural join will give - so you get data on people who read some books and
visit certain chat rooms and users credit cards.
FD will give - some people did not buy books but have a credit card and a
chat room so you want to advertise anyway.
Some people did buy books and uses
a certain credit cards but you don't know where they chat, however,
you know you want to adv some best seller that most buy anyway.
certain people did buy books and visit chat rooms but you can't offer
a specific discount, so you will advertise all credit cards.
...

However, caution. FD is a very,very expensive operation even with the new
algorithms so it is best to do FD separately and put the results into a table
and use that table. Unless of course, as common to web applications, the
relations are quite small (few thousands of rows) and they don't connect
strongly. In this cases, on my p1.6 it comes out about 2-3 secs.
However, i can generate the same experiment with strong connectivity
between the relations and it can take hours to compute.
On the other hand i have seen experiments with 100 thousans of records
that finished in a matter of minutes so it all depends on how many join
combination there are in the data.

---------------------------------------------------------------------------

Tzahi Fadida wrote:

On Friday 11 August 2006 07:18, Bruce Momjian wrote:

I have looked over this addition, and I think I finally understand it.
Given three tables, A, B, C, which join as A->B, B->C, C->A, you can
really join them as A->B->C, and A->C->B. What full disjunction does
is to perform both of those joins, and return a one row for each join.
Here

What it does is to return all the possible natural joins, i.e.:
A
B
C
A,B
A,C
...
A,B,C

And, it removes any redundant information so that if we have a tuple
that already contains another tuple's information that tuple is
discarded. Also, note that the full disjunction algorithm i implemented
is commonly used in cases where the scheme graph is cyclic
and thus, you cannot use natural full outer join
to compute the FD.

Finally, you can FD(A,B,C,D,...) any number of relations (limited to 32
in the implementation) with no regard to the order between them.

A case study and comparison can be found here:
http://www.technion.ac.il/~tzahi/soc.html

is an example from the README:

Example of an input and output of a full disjunctions:
INPUT:

--A---|---B---|---C--
X---Y-|-Y---Z-|-X---Z
a-|-b-|-b-|-c-|-a-|-d

A,B and C are relations. X,Y and Z are attributes. a,b,c and d are
values.

Note that A,B and C are connected in a cycle. That is:
A is connected to B on attribute Y,
B is connected to C on attribute Z,
C is connected to A on attribute X.

The output of the full disjunctions FD(A,B,C):

FD
X---Y---Z
a-|-b-|-c
a-|-b-|-d

This code is pretty complex, so I can see why it should be in /contrib.
Are there reasonable use cases for this capability?

-----------------------------------------------------------------------
----

Tzahi Fadida wrote:

Hi,
I wish to add the fulldisjunctions function to the contrib.
With the help of Jonah, we (or rather he :) created a patch with
regression tests. The function is finished programmatically but
still a little more code documentation touches and improved error
messages are needed. All the rest was extensively tested.

Attached is the patch.

Works great. Just compiled from a fresh cvs which i patched with the
attached diff. ran the fulldijsjunction.sql in the
share/contrib/fulldisjunction and let it run and it works great.
10x.

--
Regards,
????????Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS: ?see at
http://members.lycos.co.uk/my2nis/spamwarning.html

[ Attachment, skipping... ]

---------------------------(end of
broadcast)--------------------------- TIP 3: Have you checked our
extensive FAQ?

http://www.postgresql.org/docs/faq

--
Regards,
????????Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS: ?see at
http://members.lycos.co.uk/my2nis/spamwarning.html

--
Regards,
        Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS:  see at
http://members.lycos.co.uk/my2nis/spamwarning.html

#6AgentM
agentm@themactionfaction.com
In reply to: Tzahi Fadida (#5)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On Aug 12, 2006, at 6:01 , Tzahi Fadida wrote:

On Saturday 12 August 2006 07:22, Bruce Momjian wrote:

I am still waiting for someone to tell us that they would use this
capability for a real-world problem.

Notice that if you google "full disjunction" that the first link is
this project.

You won't find anyone to vouch for it because this is the first
implementation of full disjunctions in any database. That doesn't
mean it isn't useful- it means no one is using it because it hasn't
existed until now.

This is the point where one needs to decide whether PostgreSQL is a
copier of features from other databases or whether it can lead with a
few unique features of its own.

#7Bruce Momjian
bruce@momjian.us
In reply to: AgentM (#6)
Re: [PATCHES] Adding fulldisjunctions to the contrib

AgentM wrote:

On Aug 12, 2006, at 6:01 , Tzahi Fadida wrote:

On Saturday 12 August 2006 07:22, Bruce Momjian wrote:

I am still waiting for someone to tell us that they would use this
capability for a real-world problem.

Notice that if you google "full disjunction" that the first link is
this project.

You won't find anyone to vouch for it because this is the first
implementation of full disjunctions in any database. That doesn't
mean it isn't useful- it means no one is using it because it hasn't
existed until now.

This is the point where one needs to decide whether PostgreSQL is a
copier of features from other databases or whether it can lead with a
few unique features of its own.

OK, that is helpful. Now, does any current user think they will use
full disjunctions? Is that a fair question?

The point is not whether it should work with PostgreSQL, but whether we
ship it in /contrib, or it is on pgfoundry.

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: AgentM (#6)
Re: [PATCHES] Adding fulldisjunctions to the contrib

AgentM <agentm@themactionfaction.com> writes:

You won't find anyone to vouch for it because this is the first
implementation of full disjunctions in any database. That doesn't
mean it isn't useful- it means no one is using it because it hasn't
existed until now.

This is the point where one needs to decide whether PostgreSQL is a
copier of features from other databases or whether it can lead with a
few unique features of its own.

Somewhere along here we need to remember that "most new ideas are bad".

More seriously: the current state of affairs is that the
full-disjunction code exists as a pgfoundry project. If it's indeed the
second greatest thing since sliced bread, then I think we could assume
that people will find it and use it from pgfoundry. The question that's
on the table is whether it needs to be in contrib right now. I have not
seen either a technical argument or popularity argument why it ought to
move into contrib.

regards, tom lane

#9Jonah H. Harris
jonah.harris@gmail.com
In reply to: Tom Lane (#8)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On 8/12/06, Tom Lane <tgl@sss.pgh.pa.us> wrote:

More seriously: the current state of affairs is that the
full-disjunction code exists as a pgfoundry project. If it's indeed the
second greatest thing since sliced bread, then I think we could assume
that people will find it and use it from pgfoundry.

That goes back to assuming people not only know about pgfoundry, but
are similarly willing to search it.

The question that's on the table is whether it needs to be in contrib right now.
I have not seen either a technical argument or popularity argument why it
ought to move into contrib.

In addition to knowing that Tzahi has put a *very* significant amount
of work into his research as well as this code over the past few
months, I have to agree with several items stated by "Agent M".

This is the *first* implementation of this concept in any database
system, so there's not going to be anyone jumping up and down singing
it's praises just yet. However, when people do get a chance to play
with it, I believe we'll have a number of them saying how useful it
is. There are several contrib modules still included in the system
that aren't that heavily used... I don't see the harm in including
this one for at least this release. If no one uses it, take it out
for 8.3.

IMHO, this is just a really cool piece of technology that provides
functionality which can't be done any other way; why not give it a
chance?

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#10Bruce Momjian
bruce@momjian.us
In reply to: Jonah H. Harris (#9)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Jonah H. Harris wrote:

On 8/12/06, Tom Lane <tgl@sss.pgh.pa.us> wrote:

More seriously: the current state of affairs is that the
full-disjunction code exists as a pgfoundry project. If it's indeed the
second greatest thing since sliced bread, then I think we could assume
that people will find it and use it from pgfoundry.

That goes back to assuming people not only know about pgfoundry, but
are similarly willing to search it.

The question that's on the table is whether it needs to be in contrib right now.
I have not seen either a technical argument or popularity argument why it
ought to move into contrib.

In addition to knowing that Tzahi has put a *very* significant amount
of work into his research as well as this code over the past few
months, I have to agree with several items stated by "Agent M".

This is the *first* implementation of this concept in any database
system, so there's not going to be anyone jumping up and down singing
it's praises just yet. However, when people do get a chance to play
with it, I believe we'll have a number of them saying how useful it
is. There are several contrib modules still included in the system
that aren't that heavily used... I don't see the harm in including
this one for at least this release. If no one uses it, take it out
for 8.3.

IMHO, this is just a really cool piece of technology that provides
functionality which can't be done any other way; why not give it a
chance?

Our distribution is not a place to experiment with things. That's what
separate pgfoundry projects are for. The fact we have some unusual
things in /contrib is not a reason to add more.

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#11David Fetter
david@fetter.org
In reply to: Bruce Momjian (#10)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On Sun, Aug 13, 2006 at 10:07:06AM -0400, Bruce Momjian wrote:

Jonah H. Harris wrote:

On 8/12/06, Tom Lane <tgl@sss.pgh.pa.us> wrote:

More seriously: the current state of affairs is that the
full-disjunction code exists as a pgfoundry project. If it's
indeed the second greatest thing since sliced bread, then I
think we could assume that people will find it and use it from
pgfoundry.

That goes back to assuming people not only know about pgfoundry,
but are similarly willing to search it.

The question that's on the table is whether it needs to be in
contrib right now. I have not seen either a technical argument
or popularity argument why it ought to move into contrib.

In addition to knowing that Tzahi has put a *very* significant
amount of work into his research as well as this code over the
past few months, I have to agree with several items stated by
"Agent M".

This is the *first* implementation of this concept in any database
system, so there's not going to be anyone jumping up and down
singing it's praises just yet. However, when people do get a
chance to play with it, I believe we'll have a number of them
saying how useful it is. There are several contrib modules still
included in the system that aren't that heavily used... I don't
see the harm in including this one for at least this release. If
no one uses it, take it out for 8.3.

IMHO, this is just a really cool piece of technology that provides
functionality which can't be done any other way; why not give it a
chance?

Our distribution is not a place to experiment with things. That's
what separate pgfoundry projects are for. The fact we have some
unusual things in /contrib is not a reason to add more.

If it's on track to become part of PostgreSQL, as other innovative
features have in the past, it very much does belong there. Why
marginalize the very thing that PostgreSQL is really good
at--innovative new features--by putting it somewhere where few people
will ever even see it?

If there were some very, very clear language every place a person
could download, check references, or install PostgreSQL that new
experimental features are at pgFoundry, that might be different. As
it is, you have to be truly dedicated even to discover that pgFoundry
exists.

Let's get full disjunctions in contrib with a good README and have
people figure out what to do with them from there. If no one demands
full inclusion in a couple of versions, let's take it out.

Cheers,
D
--
David Fetter <david@fetter.org> http://fetter.org/
phone: +1 415 235 3778 AIM: dfetter666
Skype: davidfetter

Remember to vote!

#12Bruce Momjian
bruce@momjian.us
In reply to: David Fetter (#11)
Re: [PATCHES] Adding fulldisjunctions to the contrib

David Fetter wrote:

Our distribution is not a place to experiment with things. That's
what separate pgfoundry projects are for. The fact we have some
unusual things in /contrib is not a reason to add more.

If it's on track to become part of PostgreSQL, as other innovative
features have in the past, it very much does belong there. Why
marginalize the very thing that PostgreSQL is really good
at--innovative new features--by putting it somewhere where few people
will ever even see it?

If there were some very, very clear language every place a person
could download, check references, or install PostgreSQL that new
experimental features are at pgFoundry, that might be different. As
it is, you have to be truly dedicated even to discover that pgFoundry
exists.

Let's get full disjunctions in contrib with a good README and have
people figure out what to do with them from there. If no one demands
full inclusion in a couple of versions, let's take it out.

Where does it stop, then? Do we have everything in /contrib. I don't
see how this scales. When we took the code from Berkely, it had
everyone's doctoral thesis in there, and we had to remove alot of it
because it was just too messy.

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#13David Fetter
david@fetter.org
In reply to: Bruce Momjian (#12)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On Sun, Aug 13, 2006 at 11:45:43AM -0400, Bruce Momjian wrote:

David Fetter wrote:

Our distribution is not a place to experiment with things.
That's what separate pgfoundry projects are for. The fact we
have some unusual things in /contrib is not a reason to add
more.

If it's on track to become part of PostgreSQL, as other innovative
features have in the past, it very much does belong there. Why
marginalize the very thing that PostgreSQL is really good
at--innovative new features--by putting it somewhere where few
people will ever even see it?

If there were some very, very clear language every place a person
could download, check references, or install PostgreSQL that new
experimental features are at pgFoundry, that might be different.
As it is, you have to be truly dedicated even to discover that
pgFoundry exists.

Let's get full disjunctions in contrib with a good README and have
people figure out what to do with them from there. If no one
demands full inclusion in a couple of versions, let's take it out.

Where does it stop, then? Do we have everything in /contrib. I
don't see how this scales. When we took the code from Berkely, it
had everyone's doctoral thesis in there, and we had to remove alot
of it because it was just too messy.

If it were just me laying out the boundary, I'd say that anything that
changes the grammar of SQL--for example, adding FULL
DISJUNCTION--can't really be a viable trial outside the main
distribution channels and deserves a couple of versions' stay in one
of those channels if it passes the scrutiny of -hackers. I'd love to
see a "main distribution channel" that's not contrib, but that's for
the future and full disjunctions are now. :)

Cheers,
D
--
David Fetter <david@fetter.org> http://fetter.org/
phone: +1 415 235 3778 AIM: dfetter666
Skype: davidfetter

Remember to vote!

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Jonah H. Harris (#9)
Re: [PATCHES] Adding fulldisjunctions to the contrib

"Jonah H. Harris" <jonah.harris@gmail.com> writes:

I don't see the harm in including this one for at least this release.
If no one uses it, take it out for 8.3.

Once stuff is in contrib, it tends to stay there. The above argument
is completely disingenuous --- we'd have to have the same argument
again at the end of the 8.3 cycle, only then we'd already have expended
a development cycle's worth of maintenance work on a quite-large module.

There is quite a lot of stuff in contrib that the core committee
wouldn't accept nowadays ... it got in when there wasn't any alternative
such as pgfoundry. These days I think something has to be pretty
clearly useful to a wide variety of people before we'll accept it into
contrib, and I'm not seeing that that case has been made for
fulldisjunctions. FD may be cool, but coolness isn't a (sufficient)
criterion.

regards, tom lane

#15Andrew Dunstan
andrew@dunslane.net
In reply to: David Fetter (#13)
Re: [PATCHES] Adding fulldisjunctions to the contrib

David Fetter wrote:

If it were just me laying out the boundary, I'd say that anything that
changes the grammar of SQL--for example, adding FULL
DISJUNCTION--can't really be a viable trial outside the main
distribution channels and deserves a couple of versions' stay in one
of those channels if it passes the scrutiny of -hackers. I'd love to
see a "main distribution channel" that's not contrib, but that's for
the future and full disjunctions are now. :)

As I understand it, FD as implemented does not require a grammar change.
Arguably, a more complete implementation would have support at the SQL
level, and then it would have to go in core, without question.

cheers

andrew

#16Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Fetter (#13)
Re: [PATCHES] Adding fulldisjunctions to the contrib

David Fetter <david@fetter.org> writes:

If it were just me laying out the boundary, I'd say that anything that
changes the grammar of SQL--for example, adding FULL
DISJUNCTION--can't really be a viable trial outside the main
distribution channels and deserves a couple of versions' stay in one
of those channels if it passes the scrutiny of -hackers.

Well, one of the things I don't especially like about this patch is
exactly that it doesn't change the grammar (it can't really, as a
contrib module :-(). There's no way that it would get into core without
a different API and probably a complete code rewrite. So what we've got
here, at bottom, is a toy prototype that might serve for people to
experiment with the feature and find out whether it's useful or not ---
but it's not code that could be mainstream with just a bit more
maturity.

Perhaps contrib/dblink is a useful comparison point; that code will
never get into core in its current form either. The reason it's in
contrib is that the use-case is so compelling that we're willing to
accept it even though we all know it's pretty klugy.

The case for FD seems to be basically "if you build it they will come",
and I'm sorry but I'm not sold. If it gets some traction as a pgfoundry
project then we could look at doing a second-generation implementation
in a form that could actually get into core... but until then I'm
inclined to see it as an academic curiosity.

regards, tom lane

#17Josh Berkus
josh@agliodbs.com
In reply to: Bruce Momjian (#4)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Bruce Momjian wrote:

I am still waiting for someone to tell us that they would use this
capability for a real-world problem.

It's extremely useful for data mining and data consolidation where
you're given messy or sparse data to "clean up" and present intelligently.

For example, if it had existed 4 years ago, I would have used it for
importing ELBS data from the UDF table (with lots of null rows) into
PostgreSQL.

--Josh

#18Josh Berkus
josh@agliodbs.com
In reply to: Tom Lane (#16)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Tom,

The case for FD seems to be basically "if you build it they will come",
and I'm sorry but I'm not sold. If it gets some traction as a pgfoundry
project then we could look at doing a second-generation implementation
in a form that could actually get into core... but until then I'm
inclined to see it as an academic curiosity.

I've given my reason for wanting it in another post (data mining and
conversion). Let me say this additionally: full disjunctions is an
example of the kind of thing we need to have in order to defend our
title as "the most advanced open source database". Stuff like
partitioning and PITR don't cut it anymore ... MySQL and Ingres have
those. We need to keep adding innovative features or we lose a great
deal of our reason for existance as "yet another DBMS", and our ability
to attract new, smart, original developers.

The reason why it makes sense for FD to be in /contrib is that if it
works out it will be a new join type, which is definitely core-code stuff.

If QBE were ready, I'd be pushing for that too. Now, if the statement
is that FD is too buggy to include in contrib at this time, I'm happy to
accept that, but I've not seen that argument.

--Josh Berkus

#19Jonah H. Harris
jonah.harris@gmail.com
In reply to: Josh Berkus (#18)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On 8/13/06, Josh Berkus <josh@agliodbs.com> wrote:

My sentiments exactly.

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#20Dave Page
dpage@vale-housing.co.uk
In reply to: Jonah H. Harris (#19)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Josh,

I agree entirely with your thoughts - we have to innovate to stay ahead and be noticed. Unless there are real code problems, let's include FD in /contrib.

Regards, Dave

Regards, Dave

-----Original Message-----
From: "Josh Berkus" <josh@agliodbs.com>
To: "Tom Lane" <tgl@sss.pgh.pa.us>
Cc: "David Fetter" <david@fetter.org>; "Bruce Momjian" <bruce@momjian.us>; "Jonah H. Harris" <jonah.harris@gmail.com>; "AgentM" <agentm@themactionfaction.com>; "PostgreSQL-development" <pgsql-hackers@postgresql.org>
Sent: 13/08/06 22:01
Subject: Re: [HACKERS] [PATCHES] Adding fulldisjunctions to the contrib

Tom,

The case for FD seems to be basically "if you build it they will come",
and I'm sorry but I'm not sold. If it gets some traction as a pgfoundry
project then we could look at doing a second-generation implementation
in a form that could actually get into core... but until then I'm
inclined to see it as an academic curiosity.

I've given my reason for wanting it in another post (data mining and
conversion). Let me say this additionally: full disjunctions is an
example of the kind of thing we need to have in order to defend our
title as "the most advanced open source database". Stuff like
partitioning and PITR don't cut it anymore ... MySQL and Ingres have
those. We need to keep adding innovative features or we lose a great
deal of our reason for existance as "yet another DBMS", and our ability
to attract new, smart, original developers.

The reason why it makes sense for FD to be in /contrib is that if it
works out it will be a new join type, which is definitely core-code stuff.

If QBE were ready, I'd be pushing for that too. Now, if the statement
is that FD is too buggy to include in contrib at this time, I'm happy to
accept that, but I've not seen that argument.

--Josh Berkus

---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to majordomo@postgresql.org so that your
message can get through to the mailing list cleanly

#21Tom Lane
tgl@sss.pgh.pa.us
In reply to: Josh Berkus (#17)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Josh Berkus <josh@agliodbs.com> writes:

Bruce Momjian wrote:

I am still waiting for someone to tell us that they would use this
capability for a real-world problem.

It's extremely useful for data mining and data consolidation where
you're given messy or sparse data to "clean up" and present intelligently.

Could we see a concrete, real-world example? So far I've seen a lot of
arm-waving but nothing very specific.

regards, tom lane

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Josh Berkus (#18)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Josh Berkus <josh@agliodbs.com> writes:

The reason why it makes sense for FD to be in /contrib is that if it
works out it will be a new join type, which is definitely core-code stuff.

You seem to have missed my point, which is that implementation as a new
join type would probably have nothing in common with the externally-coded
version. The one and only reason for it to be in contrib instead of
on pgfoundry is that you think it will get more attention that way.
Which might be true, but it's a fairly weak argument for asking the
core developers to take on maintenance and bug-fixing for what is
ultimately going to be a dead-end code base. This code will either die
for lack of interest, or be largely rewritten so it can go into core.
I don't pretend to know which will happen, but I see no technical
advantage to it being in contrib meanwhile.

Let me say this additionally: full disjunctions is an
example of the kind of thing we need to have in order to defend our
title as "the most advanced open source database".

[ shrug... ] As an argument for having it in contrib instead of
pgfoundry, that impresses me not at all. You could argue that the
ability to do this (even poorly) *without* any core code changes is
a far greater demonstration of Postgres' basic strength --- namely
extensibility --- than it would be in core.

regards, tom lane

#23Josh Berkus
josh@agliodbs.com
In reply to: Tom Lane (#21)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Tom,

Could we see a concrete, real-world example? So far I've seen a lot of
arm-waving but nothing very specific.

Sure. Imagine that you work for an arts nonprofit and you have 3 (or more)
separate box office lists from last season, each of which has different
amounts of contact information. You want to get "best of" the information
-- that is, address if it's available, zip if it's there, phone if it's
there, etc. FD will reduce that process from several procedural loops
to a single query for the first pre-deduplication run.

Or, imagine that you have 5 weblogs in different formats from 5 different
servers. Due to the different logging/site design, you have and lack
different information from each log. You want to munge all of the data
together to extract the maximum amount of data about each visitor you can
get, without having multiple records per visit.

This supposition holds true for court records, customer records, etc ...
anywhere you may have relational data with a high degree of incompleteness
from different sources ... something I encountered on about 30% of all the
DB development projects I worked on.

You seem to have missed my point, which is that implementation as a new
join type would probably have nothing in common with the
externally-coded version. The one and only reason for it to be in
contrib instead of on pgfoundry is that you think it will get more
attention that way. Which might be true, but it's a fairly weak argument
for asking the core developers to take on maintenance and bug-fixing for
what is ultimately going to be a dead-end code base.

OK, point taken. I'll admit that I had hopes for it for PR reasons, which
is not usually why we make decisions. It would be cool to be the first
database system to ship with any implementation of Full Disjunctions, and
I can't announce that if it's on pgFoundry.

--
--Josh

Josh Berkus
PostgreSQL @ Sun
San Francisco

#24Andrew Dunstan
andrew@dunslane.net
In reply to: Josh Berkus (#23)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Josh Berkus wrote:

I'll admit that I had hopes for it for PR reasons, which
is not usually why we make decisions. It would be cool to be the first
database system to ship with any implementation of Full Disjunctions, and
I can't announce that if it's on pgFoundry.

I don't see that having it on pgfoundry makes it less announceable. But
if/when we get support at the SQL level, then we'll *really* have
something worth announcing.

cheers

andrew

#25Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#24)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Andrew Dunstan <andrew@dunslane.net> writes:

Josh Berkus wrote:

I'll admit that I had hopes for it for PR reasons, which
is not usually why we make decisions. It would be cool to be the first
database system to ship with any implementation of Full Disjunctions, and
I can't announce that if it's on pgFoundry.

I don't see that having it on pgfoundry makes it less announceable. But
if/when we get support at the SQL level, then we'll *really* have
something worth announcing.

I think Andrew's first point here is spot-on. We *must* take pgfoundry
seriously as part of the available technology for Postgres. Otherwise
all the effort we've put into building up pgfoundry (and gborg before it)
was a waste of time. Are Perl modules taken less seriously because
they're on CPAN rather than part of the minimal Perl distribution?
No, they're not. That is the model that we've got to strive for,
because the core developers simply haven't got enough cycles to deal
with core Postgres development and the entire kitchen sink as well.

This is not just a matter of core-developer laziness, either. The
concept of a cloud of useful code around a small core is something
I think is absolutely critical to PG's long-term success. We have
a built-in advantage here because of PG's historical commitment to
extensibility. Can you see Oracle, DB2, or MySQL operating that way?
No, you can't, because their core code is not open, or they have a
business need to control everything going on, or both.

We need to *exploit* our ability to support important outside-the-core
projects. Not assume that anything outside core can't be important.

regards, tom lane

#26Joshua D. Drake
jd@commandprompt.com
In reply to: Josh Berkus (#23)
Re: [PATCHES] Adding fulldisjunctions to the contrib

OK, point taken. I'll admit that I had hopes for it for PR reasons, which
is not usually why we make decisions. It would be cool to be the first
database system to ship with any implementation of Full Disjunctions, and
I can't announce that if it's on pgFoundry.

You could announce it as an available module however.

Joshua D. Drake

#27Bruce Momjian
bruce@momjian.us
In reply to: Tzahi Fadida (#1)
Re: Adding fulldisjunctions to the contrib

Sorry, we did not get enough feedback to include this in 8.2. Please
add it to pgfoundry and let's see how it goes.

---------------------------------------------------------------------------

Tzahi Fadida wrote:

Hi,
I wish to add the fulldisjunctions function to the contrib.
With the help of Jonah, we (or rather he :) created a patch with
regression tests. The function is finished programmatically but
still a little more code documentation touches and improved error messages
are needed. All the rest was extensively tested.

Attached is the patch.

Works great. Just compiled from a fresh cvs which i patched with the
attached diff. ran the fulldijsjunction.sql in the
share/contrib/fulldisjunction and let it run and it works great.
10x.

--
Regards,
????????Tzahi.
--
Tzahi Fadida
Blog: http://tzahi.blogsite.org | Home Site: http://tzahi.webhop.info
WARNING TO SPAMMERS: ?see at
http://members.lycos.co.uk/my2nis/spamwarning.html

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

http://www.postgresql.org/docs/faq

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +

#28Jonah H. Harris
jonah.harris@gmail.com
In reply to: Bruce Momjian (#27)
Re: Adding fulldisjunctions to the contrib

On 8/25/06, Bruce Momjian <bruce@momjian.us> wrote:

Sorry, we did not get enough feedback to include this in 8.2. Please
add it to pgfoundry and let's see how it goes.

Yep... it's too bad. A new feature no other database has now goes to
it's final resting place on pgfoundry.

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#29Andrew Dunstan
andrew@dunslane.net
In reply to: Jonah H. Harris (#28)
Re: Adding fulldisjunctions to the contrib

Jonah H. Harris wrote:

On 8/25/06, Bruce Momjian <bruce@momjian.us> wrote:

Sorry, we did not get enough feedback to include this in 8.2. Please
add it to pgfoundry and let's see how it goes.

Yep... it's too bad. A new feature no other database has now goes to
it's final resting place on pgfoundry.

Jonah,

this is inaccurate, irresponsible and insulting to those of us who spend
time maintaining pgfoundry. It is not a graveyard. Plenty of stuff
outside the core gets included in packaged distributions - just see for
example what goes into the Windows distro, or the packages that CP
distributes.

The implication of your statement is that anything not accepted into the
core is automatically somehow considered unworthy. Please refer to Tom's
recent remarks about playing on extensibility as one of our strengths.

My impression (please correct me if I'm wrong) is that proper full
disjunction support would include grammar support, in which case contrib
is not where it should belong anyway. If that's so, then the next step
would be for somebody to pick up the work that Tzahi has done and take
it the rest of the way. That would be a worth goal for 8.3.

cheers

andrew

#30Joshua D. Drake
jd@commandprompt.com
In reply to: Andrew Dunstan (#29)
Re: Adding fulldisjunctions to the contrib

this is inaccurate, irresponsible and insulting to those of us who spend
time maintaining pgfoundry. It is not a graveyard. Plenty of stuff
outside the core gets included in packaged distributions - just see for
example what goes into the Windows distro, or the packages that CP
distributes.

Jonah,

Your attitude has been lacking about this whole thing, as has a lot of
other people. PgFoundry is the official sub project site for PostgreSQL.

It is not a graveyard, projects on PgFoundry should receive full
advocacy and promotion about their abilities and their linkage PostgreSQL.

If we spent half as much time promoting and helping the various sub
project succeed as we doing whining on this list, we would be far more
dominant in the industry then we are.

I am sick of all the moaning that goes on, with this list about -- "oh
please, we need this in core". It is a crock we have a huge repository
of PostgreSQL projects that are not in core and this attitude is
detrimental and negative to all who are involved with those projects.

When full disjunctons is ready, I am sure it will be considered for
core. It currently is not and pgFoundry is the perfect place for until
until then.

We can still promote and announce we have a full disjunctions
implementation, just as we can advertise we have full text indexing.

Sincerely,

Joshua D. Drake

--

=== The PostgreSQL Company: Command Prompt, Inc. ===
Sales/Support: +1.503.667.4564 || 24x7/Emergency: +1.800.492.2240
Providing the most comprehensive PostgreSQL solutions since 1997
http://www.commandprompt.com/

#31Jonah H. Harris
jonah.harris@gmail.com
In reply to: Andrew Dunstan (#29)
Re: Adding fulldisjunctions to the contrib

On 8/26/06, Andrew Dunstan <andrew@dunslane.net> wrote:

this is inaccurate, irresponsible and insulting to those of us who spend
time maintaining pgfoundry.

Andrew,

I'm sorry if it sounded that way... it wasn't meant as such.

It is not a graveyard. Plenty of stuff outside the core gets included in
packaged distributions - just see for example what goes into the Windows
distro, or the packages that CP distributes.

I'm not saying that *everything* on pgfoundry is junk... but I can
start naming dead projects if you'd like. It's like SourceForge
before SourceForge jumped the shark... now 90% of SourceForge is
either projects dead-and-gone or which hadn't even started. It's
almost not even worth the time to search SF.net anymore. I believe
that's the direction pgfoundry is headed. Not because of poor
management or administration... just that when you have a large number
of projects, the majority of which are dead or not even worth viewing,
it takes the credibility of the site down as a whole. Look at
gborg... there was some good stuff there and there still is; if you
already know about it. Both gborg and pgfoundry have projects on
there won't even work with a current version of PostgreSQL.

Outside of all us hackers... how many people actually use pgfoundry?
Does anyone have the stats? Has anyone polled users? How many of the
users are newbies and how many are already familiar with PostgreSQL?
If we don't have these basic answers, continuing to praise pgfoundry
as the home for all-things-PostgreSQL is pointless.

The implication of your statement is that anything not accepted into the
core is automatically somehow considered unworthy.

Not at all. I'm referring to this case in particular.

Please refer to Tom's recent remarks about playing on extensibility
as one of our strengths.

I never said it wasn't... extensibility is, IMHO, our *core* strength.
However, I don't think that's a good reason for pushing everything to
pgfoundry.

My impression (please correct me if I'm wrong) is that proper full
disjunction support would include grammar support, in which case contrib
is not where it should belong anyway. If that's so, then the next step
would be for somebody to pick up the work that Tzahi has done and take
it the rest of the way. That would be a worth goal for 8.3.

You are correct, a *full* implementation would most likely include
integration into the core; grammar and all. However, being as it's an
entirely new feature in any database system ever seen, I don't think
it should be required. It's kind of funny though; it's difficult
enough to convince -hackers to adopt a feature that every other
database system in the world has, yet we're going to make it even
more difficult for an innovative feature. I can only imagine trying
to get a consensus on the grammar and implementation of a totally
nonstandard feature that only a few people really understand.

As I see it, the full disjunction code will likely end up being a low
profile project on pgfoundry because Tzahi won't have time to continue
maintaining it and not many of us have enough insight into it to do so
ourselves. As such, I don't think it's going to get enough attention
and enough of a user following to make it worth the time of one of the
core developers to pick it up.

Of course, I may always be wrong. Perhaps pgfoundry is more popular
than I've seen in past experience. Maybe one of the core developers
does want to pick up full disjunctions for 8.3.

Guess we'll just have to wait and see...

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#32Jonah H. Harris
jonah.harris@gmail.com
In reply to: Joshua D. Drake (#30)
Re: Adding fulldisjunctions to the contrib

On 8/26/06, Joshua D. Drake <jd@commandprompt.com> wrote:

Your attitude has been lacking about this whole thing, as has a lot of
other people. PgFoundry is the official sub project site for PostgreSQL.

That may be the case. However, all I've seen+heard is conjecture that
pgfoundry is a good thing; where's the proof? Show me and other
fellow "whiners" that a lot of people use pgfoundry and I'll gladly
shut up about it.

It is not a graveyard, projects on PgFoundry should receive full
advocacy and promotion about their abilities and their linkage PostgreSQL.

See previous email to Andrew regarding projects that don't work with
the latest versions of PostgreSQL. I think I've even seen a pgfoundry
project last updated for 7.x; that's certainly the case for gborg.

If we spent half as much time promoting and helping the various sub
project succeed as we doing whining on this list, we would be far more
dominant in the industry then we are.

So, subprojects [pgfoundry] is the source of all industry dominance?
I wish I would've known that before :) Sorry, I was itchin' to say
it.

I am sick of all the moaning that goes on,

So am I... in general.

When full disjunctons is ready, I am sure it will be considered for
core. It currently is not and pgFoundry is the perfect place for until
until then.

As it's not a common feature, I don't think many of the hackers know
what it is or what it does. Certainly, very few have spoken on this
thread.

It's odd, only 10 people have commented on this thread; 4 of which are
core members, 2 in favor and 2 against. Yet, we're having an argument
on why this wasn't included. Unless this is the new math, 2 vs. 2
seems like a tie to me.

We can still promote and announce we have a full disjunctions
implementation, just as we can advertise we have full text indexing.

Wherever it ends up, I look forward to seeing the promotion and
announcements. Tzahi has put a lot of work into it over the past few
months.

I'm done on this topic but would gladly appreciate public or private
proof regarding pgfoundry's popularity.

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#33Andrew Dunstan
andrew@dunslane.net
In reply to: Jonah H. Harris (#32)
Re: Adding fulldisjunctions to the contrib

Jonah H. Harris wrote:

On 8/26/06, Joshua D. Drake <jd@commandprompt.com> wrote:

Your attitude has been lacking about this whole thing, as has a lot of
other people. PgFoundry is the official sub project site for PostgreSQL.

That may be the case. However, all I've seen+heard is conjecture that
pgfoundry is a good thing; where's the proof? Show me and other
fellow "whiners" that a lot of people use pgfoundry and I'll gladly
shut up about it.

true story.

I walked into my new boss's office the other day. He knew I was
connected with PostgreSQL (after all, that's why he gave me the job),
but we had never discussed pgfoundry - in fact he was very surprised
yesterday to hear I had anything to do with it. But that day his browser
was open on the pgfoundry home page.

So, yes, it is used, and by far more that just hard core hackers.

cheers

andrew

#34Jonah H. Harris
jonah.harris@gmail.com
In reply to: Andrew Dunstan (#33)
Re: Adding fulldisjunctions to the contrib

On 8/26/06, Andrew Dunstan <andrew@dunslane.net> wrote:

So, yes, it is used, and by far more that just hard core hackers.

OK. Kewl. I just hadn't run into many people (except hackers) that
knew about it. Thanks for sharing that.

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#35Dave Page
dpage@vale-housing.co.uk
In reply to: Tzahi Fadida (#1)
Re: Adding fulldisjunctions to the contrib

-----Original Message-----
From: pgsql-patches-owner@postgresql.org on behalf of Jonah H. Harris
Sent: Sun 8/27/2006 3:24 AM
To: Joshua D. Drake
Cc: Andrew Dunstan; Bruce Momjian; Tzahi Fadida; pgsql-patches@postgresql.org
Subject: Re: [PATCHES] Adding fulldisjunctions to the contrib

It's odd, only 10 people have commented on this thread; 4 of which are
core members, 2 in favor and 2 against. Yet, we're having an argument
on why this wasn't included. Unless this is the new math, 2 vs. 2
seems like a tie to me.

Y'know I was gonna check up on that because my recollection was that it was a 2/2 split as well, though I thought that was of people who made their view clear rather than just -core (whose opinion in this case is no more important than any of the other long time contributors imho). Don't suppose you noted the views of the other 6?

Regards, Dave.

#36Peter Eisentraut
peter_e@gmx.net
In reply to: Jonah H. Harris (#31)
Re: [PATCHES] Adding fulldisjunctions to the contrib

Jonah H. Harris wrote:

I'm not saying that *everything* on pgfoundry is junk... but I can
start naming dead projects if you'd like.

Well, make a list and tell the admins to delete those projects.

--
Peter Eisentraut
http://developer.postgresql.org/~petere/

#37Joshua D. Drake
jd@commandprompt.com
In reply to: Dave Page (#35)
Re: Adding fulldisjunctions to the contrib

Dave Page wrote:

-----Original Message-----
From: pgsql-patches-owner@postgresql.org on behalf of Jonah H. Harris
Sent: Sun 8/27/2006 3:24 AM
To: Joshua D. Drake
Cc: Andrew Dunstan; Bruce Momjian; Tzahi Fadida; pgsql-patches@postgresql.org
Subject: Re: [PATCHES] Adding fulldisjunctions to the contrib

It's odd, only 10 people have commented on this thread; 4 of which are
core members, 2 in favor and 2 against. Yet, we're having an argument
on why this wasn't included. Unless this is the new math, 2 vs. 2
seems like a tie to me.

Y'know I was gonna check up on that because my recollection was that it was a 2/2 split as well, though I thought that was of people who made their view clear rather than just -core (whose opinion in this case is no more important than any of the other long time contributors imho). Don't suppose you noted the views of the other 6?

IIRC some of the rejection points, was the code:

1. Is not quite complete
2. Does not follow postgresql style guidelines

Those two items make it impossible to include Full disjunctions in core.
I believe those two points were made by Tom but I can't find his
response so if I am on crack -- I apologize in advance.

Sincerely,

Joshua D. Drake

--

=== The PostgreSQL Company: Command Prompt, Inc. ===
Sales/Support: +1.503.667.4564 || 24x7/Emergency: +1.800.492.2240
Providing the most comprehensive PostgreSQL solutions since 1997
http://www.commandprompt.com/

#38Jonah H. Harris
jonah.harris@gmail.com
In reply to: Peter Eisentraut (#36)
Re: [PATCHES] Adding fulldisjunctions to the contrib

On 8/27/06, Peter Eisentraut <peter_e@gmx.net> wrote:

Well, make a list and tell the admins to delete those projects.

Alright. When I come across one, I'll forward it on.

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#39Jonah H. Harris
jonah.harris@gmail.com
In reply to: Joshua D. Drake (#37)
Re: Adding fulldisjunctions to the contrib

On 8/27/06, Joshua D. Drake <jd@commandprompt.com> wrote:

1. Is not quite complete

Only because it wasn't merged into the core. Which, like I said,
would be difficult to get consensus on design, grammar, and
implementation when it's a brand new and non-standard feature only a
few people understand. I honestly don't think a project like that
would've ever gotten off the ground in -hackers. Being a contrib
module makes it a bit more flexible and gives people the chance to try
it out; that way we'll see if it's worth merging into the core. Think
of it as a Phase I of Full DIsjunctions... Phase II is a bit of a
redesign and merge into 8.3.

2. Does not follow postgresql style guidelines

This statement was not made.

I believe those two points were made by Tom but I can't find his
response so if I am on crack -- I apologize in advance.

One of the points, taken a little out of context, was made by Tom.

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#40Jonah H. Harris
jonah.harris@gmail.com
In reply to: Dave Page (#35)
Re: Adding fulldisjunctions to the contrib

Y'know I was gonna check up on that because my recollection was that it was a 2/2 split as well, though I thought that was of people who made their view clear rather than just -core (whose opinion in this case is no more important than any of the other long time contributors imho). Don't suppose you noted the views of the other 6?

As counted, regarding inclusion in /contrib the thread sits at 5 for,
4 against, and 1 seems to lean toward making it a contrib.

Just in case my counting is wrong, this is what I've marked:

Tzahi Fadida - For
Bruce Momjian - Against
AgentM - Possibly For
Tom Lane - Against
Jonah Harris - For
David Fetter - For
Josh Drake - Against
Andrew Dunstan - Against
Josh Berkus - For
Dave Page - For

--
Jonah H. Harris, Software Architect | phone: 732.331.1300
EnterpriseDB Corporation | fax: 732.331.1301
33 Wood Ave S, 2nd Floor | jharris@enterprisedb.com
Iselin, New Jersey 08830 | http://www.enterprisedb.com/

#41Andrew Dunstan
andrew@dunslane.net
In reply to: Jonah H. Harris (#40)
Re: Adding fulldisjunctions to the contrib

Jonah H. Harris wrote:

Y'know I was gonna check up on that because my recollection was that
it was a 2/2 split as well, though I thought that was of people who
made their view clear rather than just -core (whose opinion in this
case is no more important than any of the other long time
contributors imho). Don't suppose you noted the views of the other 6?

As counted, regarding inclusion in /contrib the thread sits at 5 for,
4 against, and 1 seems to lean toward making it a contrib.

Just in case my counting is wrong, this is what I've marked:

Tzahi Fadida - For
Bruce Momjian - Against
AgentM - Possibly For
Tom Lane - Against
Jonah Harris - For
David Fetter - For
Josh Drake - Against
Andrew Dunstan - Against
Josh Berkus - For
Dave Page - For

Well, I don't think all your 9 qualify as long time contributors, if you
want to count numbers.

Even if there is a vote in favor, somebody has to commit it and take
responsibility for it. I at least don't have time right now.

cheers

andrew

#42Bruce Momjian
bruce@momjian.us
In reply to: Jonah H. Harris (#40)
Re: Adding fulldisjunctions to the contrib

Jonah H. Harris wrote:

Y'know I was gonna check up on that because my recollection was that it was a 2/2 split as well, though I thought that was of people who made their view clear rather than just -core (whose opinion in this case is no more important than any of the other long time contributors imho). Don't suppose you noted the views of the other 6?

As counted, regarding inclusion in /contrib the thread sits at 5 for,
4 against, and 1 seems to lean toward making it a contrib.

Just in case my counting is wrong, this is what I've marked:

Tzahi Fadida - For
Bruce Momjian - Against
AgentM - Possibly For
Tom Lane - Against
Jonah Harris - For
David Fetter - For
Josh Drake - Against
Andrew Dunstan - Against
Josh Berkus - For
Dave Page - For

I didn't realize the vote was even close for acceptance. I only
remember Josh saying he would use it. Saying we should have it to
remain "cutting-edge" doesn't strike me as a valid reason for inclusion,
but more of a philosophical one, which worries me. I would like it
added because people want its functionality, not because it is somehow
cool.

--
Bruce Momjian bruce@momjian.us
EnterpriseDB http://www.enterprisedb.com

+ If your life is a hard drive, Christ can be your backup. +