/*
 * This will use heap scan.
 */
void
test__caql_switch2(void **state)
{
	const char		   *query = "INSERT into pg_proc";
	struct caql_hash_cookie *hash_cookie;
	cqContext			context = {0}, *pCtx;
	Datum				keys[] = {};
	cq_list			   *pcql = CaQL(query, 0, keys);
	RelationData		dummyrel;

	dummyrel.rd_id = ProcedureRelationId;

	hash_cookie = cq_lookup(query, strlen(query), pcql);

	/* setup heap_open */
	expect__heap_open(ProcedureRelationId, true,
					  RowExclusiveLock, true,
					  &dummyrel);

	pCtx = caql_switch(hash_cookie, &context, pcql);

	assert_true(pCtx != NULL);
	assert_true(pCtx->cq_sysScan == NULL);
	assert_int_equal(RelationGetRelid(pCtx->cq_heap_rel), ProcedureRelationId);
}
/*
 * This tests DELETE
 */
void
test__caql_switch6(void **state)
{
	const char		   *query = "DELETE FROM pg_class WHERE oid = :1";
	struct caql_hash_cookie *hash_cookie;
	cqContext			context = {0}, *pCtx;
	Datum				keys[] = {ObjectIdGetDatum(ProcedureRelationId)};
	cq_list			   *pcql = CaQL(query, 1, keys);
	RelationData		dummyrel;

	dummyrel.rd_id = RelationRelationId;
	hash_cookie = cq_lookup(query, strlen(query), pcql);

	/* setup heap_open */
	expect__heap_open(RelationRelationId, true,
					  RowExclusiveLock, true,
					  &dummyrel);

	pCtx = caql_switch(hash_cookie, &context, pcql);

	assert_true(pCtx != NULL);
	assert_true(pCtx->cq_usesyscache);
	assert_true(pCtx->cq_heap_rel->rd_id == RelationRelationId);
	assert_true(pCtx->cq_useidxOK);
}
/*
 * This will use syscache.
 */
void
test__caql_switch1(void **state)
{
	const char		   *query = "SELECT * FROM pg_class WHERE oid = :1";
	struct caql_hash_cookie *hash_cookie;
	cqContext			context = {0}, *pCtx;
	Datum				keys[] = {ObjectIdGetDatum(ProcedureRelationId)};
	cq_list			   *pcql = CaQL(query, 1, keys);

	hash_cookie = cq_lookup(query, strlen(query), pcql);

	pCtx = caql_switch(hash_cookie, &context, pcql);

	assert_true(pCtx != NULL);
	assert_true(pCtx->cq_sysScan == NULL);
	assert_true(pCtx->cq_usesyscache);

	hash_cookie = cq_lookup(query, strlen(query), pcql);
}
/*
 * This tests if non-equal predicates also use index scan.
 */
void
test__caql_switch5(void **state)
{
	const char		   *query = "SELECT * FROM pg_attribute "
								"WHERE attrelid = :1 and attnum > :2";
	struct caql_hash_cookie *hash_cookie;
	cqContext			context = {0}, *pCtx;
	Datum				keys[] = {ObjectIdGetDatum(ProcedureRelationId),
								  Int16GetDatum(0)};
	cq_list			   *pcql = CaQL(query, 2, keys);
	RelationData		dummyrel;
	SysScanDescData		dummydesc;

	dummyrel.rd_id = AttributeRelationId;

	hash_cookie = cq_lookup(query, strlen(query), pcql);

	/*
	 * Add explicit relation, and set indexOK = true and syscache = false.
	 */
	pCtx = caql_addrel(cqclr(&context), &dummyrel);
	pCtx = caql_indexOK(pCtx, true);
	pCtx = caql_syscache(pCtx, false);

	/* setup ScanKeyInit */
	expect__ScanKeyInit(NULL, false,
						Anum_pg_attribute_attrelid, true,
						BTEqualStrategyNumber, true,
						F_OIDEQ, true,
						NULL, false);

	/* setup ScanKeyInit */
	expect__ScanKeyInit(NULL, false,
						Anum_pg_attribute_attnum, true,
						BTGreaterStrategyNumber, true,
						F_INT2GT, true,
						NULL, false);

	/* setup systable_beginscan */
	expect__systable_beginscan(&dummyrel, true,
							   AttributeRelidNumIndexId, true,
							   true, true,
							   SnapshotNow, true,
							   2, true,
							   NULL, false,
							   &dummydesc);

	pCtx = caql_switch(hash_cookie, pCtx, pcql);

	assert_true(pCtx != NULL);
	assert_true(pCtx->cq_sysScan == &dummydesc);
	assert_true(pCtx->cq_heap_rel == &dummyrel);
	assert_false(pCtx->cq_usesyscache);
	assert_true(pCtx->cq_useidxOK);
}
/*
 * This will use heap scan.
 */
void
test__caql_switch3(void **state)
{
	const char		   *query = "SELECT * FROM pg_class "
								"WHERE oid = :1 AND relname = :2";
	struct caql_hash_cookie *hash_cookie;
	cqContext			context = {0}, *pCtx;
	NameData			relname = {"pg_class"};
	Datum				keys[] = {ObjectIdGetDatum(ProcedureRelationId),
								  NameGetDatum(&relname)};
	cq_list			   *pcql = CaQL(query, 1, keys);
	RelationData		dummyrel;
	SysScanDescData		dummydesc;

	dummyrel.rd_id = RelationRelationId;

	hash_cookie = cq_lookup(query, strlen(query), pcql);

	/* setup heap_open */
	expect__heap_open(RelationRelationId, true,
					  AccessShareLock, true,
					  &dummyrel);

	/* setup ScanKeyInit */
	expect__ScanKeyInit(NULL, false,
						ObjectIdAttributeNumber, true,
						BTEqualStrategyNumber, true,
						F_OIDEQ, true,
						NULL, false);

	/* setup ScanKeyInit */
	expect__ScanKeyInit(NULL, false,
						Anum_pg_class_relname, true,
						BTEqualStrategyNumber, true,
						F_NAMEEQ, true,
						NULL, false);

	/* setup systable_beginscan */
	expect__systable_beginscan(&dummyrel, true,
							   0, false,
							   0, false,
							   SnapshotNow, true,
							   2, true,
							   NULL, false,
							   &dummydesc);

	pCtx = caql_switch(hash_cookie, &context, pcql);

	assert_true(pCtx != NULL);
	assert_true(pCtx->cq_sysScan == &dummydesc);
	assert_true(pCtx->cq_heap_rel == &dummyrel);
	assert_false(pCtx->cq_usesyscache);
}
static void
common__cq_lookup_fail(void **state, const char *query, Datum keys[], int nkeys)
{
	struct caql_hash_cookie *hash_cookie;
	cq_list			   *pcql = CaQL(query, nkeys, keys);
	bool				result = false;

	PG_TRY();
	{
		hash_cookie = cq_lookup(query, strlen(query), pcql);
	}
	PG_CATCH();
	{
		result = true;
	}
	PG_END_TRY();

	assert_true(result);
}
Example #7
0
/* ----------------------------------------------------------------
 * caql_beginscan()
 * Initialize the scan and open relations/acquire locks as necessary
 * ----------------------------------------------------------------
 */
cqContext *
caql_beginscan(cqContext *pCtx0, cq_list *pcql)
{
	const char*				 caql_str = pcql->caqlStr;
	const char*				 filenam  = pcql->filename;
	int						 lineno	  = pcql->lineno;
	struct caql_hash_cookie	*pchn	  = cq_lookup(caql_str, strlen(caql_str), pcql);
	cqContext				*pCtx;

	/* use the provided context, or *allocate* a clean one */
	if (pCtx0)
		pCtx = pCtx0;
	else
	{
		pCtx = (cqContext *) palloc0(sizeof(cqContext));
		pCtx->cq_free = true;  /* free this context in caql_endscan */
	}

	if (NULL == pchn)
		elog(ERROR, "invalid caql string: %s\nfile: %s, line %d",
			 caql_str, filenam, lineno);

	if (pchn->bCount)
		elog(ERROR,
			 "Cannot scan: %s -- COUNTing or DELETing\nfile: %s, line %d",
			 caql_str, filenam, lineno);

	pCtx = caql_switch(pchn, pCtx, pcql);
	/* NOTE: caql_switch frees the pcql */

	pCtx->cq_bScanBlock = true; /* started a scan block */
	pCtx->cq_freeScan	= true;

	if (pchn->bInsert) /* INSERT allowed, but no subsequent fetches */
	{
		pCtx->cq_freeScan = false; /* didn't allocate a scanner */
		pCtx->cq_EOF	  = true;
	}

	return pCtx;
}
Example #8
0
/* ----------------------------------------------------------------
 * caql_begin_CacheList()
 * Return a catclist
 *
 * In general, catquery will choose the syscache when the cql
 * statement contains an equality predicate on *all* of the syscache
 * primary key index columns, eg:
 *
 *   cql("SELECT * FROM pg_amop WHERE amopopr = :1 and amopclaid = :2 ")
 *
 * will use the AMOPOPID syscache with index
 * AccessMethodOperatorIndexId.  However, the cql statement for a
 * list-search requires an equality predicate on a subset of the
 * initial columns of the index, with *all* of the index columns
 * specified in an ORDER BY clause, eg:
 *
 *   cql("SELECT * FROM pg_amop WHERE amopopr = :1 "
 *       " ORDER BY amopopr, amopclaid ")
 *
 * will use a syscache list-search if this cql statement is an
 * argument to caql_begin_CacheList().  However, the syscache will
 * *not* be used for this statement if it is supplied for
 * caql_beginscan(), since SearchSysCache() can only return (at most)
 * a single tuple.
 *
 * NOTE: caql_begin_CacheList() will assert (Insist!) at runtime if
 * the cql statement does not map to a syscache lookup.
 * NOTE: it may be possible to "collapse" this API into the existing
 * beginscan/getnext/endscan.
 * ----------------------------------------------------------------
 */
struct catclist *
caql_begin_CacheList(cqContext *pCtx0,
					 cq_list *pcql)
{
	const char*				 caql_str = pcql->caqlStr;
	const char*				 filenam  = pcql->filename;
	int						 lineno	  = pcql->lineno;
	struct caql_hash_cookie	*pchn	  = cq_lookup(caql_str, strlen(caql_str), pcql);
	cqContext				*pCtx;
	cqContext				 cqc;

	if (NULL == pchn)
		elog(ERROR, "invalid caql string: %s\nfile: %s, line %d",
			 caql_str, filenam, lineno);

	Assert(!pchn->bInsert); /* INSERT not allowed */
	Assert(!pchn->bUpdate); /* UPDATE not allowed */
	Assert(!pchn->bDelete); /* DELETE not allowed */

	/* use the provided context, or provide a clean local ctx  */
	if (pCtx0)
		pCtx = pCtx0;
	else
		pCtx = cqclr(&cqc);

	/* NOTE: for case of cache list search, we must use syscache */
	pCtx->cq_bCacheList = true;

	pCtx = caql_switch(pchn, pCtx, pcql);
	/* NOTE: caql_switch frees the pcql */

	/* NOTE: must use the SysCache */
	Insist (pCtx->cq_usesyscache);

	caql_heapclose(pCtx);

	return SearchSysCacheKeyArrayList(pCtx->cq_cacheId,
									  pCtx->cq_NumKeys,
									  pCtx->cq_cacheKeys);
}
Example #9
0
/* ----------------------------------------------------------------
 * caql_getfirst_only()
 * Return a copy the first tuple, pallocd in the current memory context,
 * and end the scan.  Clients should heap_freetuple() as necessary.
 * If pbOnly is not NULL, return TRUE if a second tuple is not found,
 * else return FALSE
 * NOTE: this function will return NULL if no tuples satisfy the
 * caql predicate -- use HeapTupleIsValid() to detect this condition.
 * ----------------------------------------------------------------
 */
HeapTuple caql_getfirst_only(cqContext *pCtx0, bool *pbOnly, cq_list *pcql)
{
	const char*				 caql_str = pcql->caqlStr;
	const char*				 filenam  = pcql->filename;
	int						 lineno	  = pcql->lineno;
	struct caql_hash_cookie	*pchn	  = cq_lookup(caql_str, strlen(caql_str), pcql);
	cqContext				*pCtx;
	cqContext				 cqc;
	HeapTuple				 tuple, newTup = NULL;

	if (NULL == pchn)
		elog(ERROR, "invalid caql string: %s\nfile: %s, line %d", 
			 caql_str, filenam, lineno);

	Assert(!pchn->bInsert); /* INSERT not allowed */

	/* use the provided context, or provide a clean local ctx  */
	if (pCtx0)
		pCtx = pCtx0;
	else
		pCtx = cqclr(&cqc);

	pCtx = caql_switch(pchn, pCtx, pcql);
	/* NOTE: caql_switch frees the pcql */

	if (pbOnly) *pbOnly = true;

	/* use the SysCache */
	if (pCtx->cq_usesyscache)
	{
		tuple = SearchSysCacheKeyArray(pCtx->cq_cacheId, 
									   pCtx->cq_NumKeys, 
									   pCtx->cq_cacheKeys);

		if (HeapTupleIsValid(tuple))
		{
			newTup = heap_copytuple(tuple);
			ReleaseSysCache(tuple);
			/* only one */
		}
		caql_heapclose(pCtx);

		pCtx->cq_lasttup = newTup; /* need this for update/delete */

		return (newTup);
	}

	if (HeapTupleIsValid(tuple = systable_getnext(pCtx->cq_sysScan)))
	{
		/* always copy the tuple, because the endscan releases tup memory */
		newTup = heap_copytuple(tuple);
 
		if (pbOnly)
		{
			*pbOnly = 
				!(HeapTupleIsValid(systable_getnext(pCtx->cq_sysScan)));
		}
	}
	systable_endscan(pCtx->cq_sysScan); 
	caql_heapclose(pCtx);

	pCtx->cq_lasttup = newTup; /* need this for update/delete */
	return (newTup);
}
Example #10
0
/* ----------------------------------------------------------------
 * caql_getcount()
 * Perform COUNT(*) or DELETE
 * ----------------------------------------------------------------
 */
int caql_getcount(cqContext *pCtx0, cq_list *pcql)
{
	const char*				 caql_str = pcql->caqlStr;
	const char*				 filenam  = pcql->filename;
	int						 lineno	  = pcql->lineno;
	struct caql_hash_cookie	*pchn	  = cq_lookup(caql_str, strlen(caql_str), pcql);
	cqContext				*pCtx;
	cqContext				 cqc;
	HeapTuple				 tuple;
	Relation				 rel;
	int						 ii		  = 0;

	if (NULL == pchn)
		elog(ERROR, "invalid caql string: %s\nfile: %s, line %d", 
			 caql_str, filenam, lineno);

	Assert(!pchn->bInsert); /* INSERT not allowed */

	/* use the provided context, or provide a clean local ctx  */
	if (pCtx0)
		pCtx = pCtx0;
	else
		pCtx = cqclr(&cqc);

	pCtx = caql_switch(pchn, pCtx, pcql);
	/* NOTE: caql_switch frees the pcql */
	rel  = pCtx->cq_heap_rel;

	/* use the SysCache */
	if (pCtx->cq_usesyscache)
	{
		tuple = SearchSysCacheKeyArray(pCtx->cq_cacheId, 
									   pCtx->cq_NumKeys, 
									   pCtx->cq_cacheKeys);

		if (HeapTupleIsValid(tuple))
		{
			ii++;
			pCtx->cq_lasttup = tuple;
			if (pchn->bDelete)
				simple_heap_delete(rel, &tuple->t_self);

			ReleaseSysCache(tuple);
			/* only one */
		}
		caql_heapclose(pCtx);
		return (ii);
	}

	while (HeapTupleIsValid(tuple = systable_getnext(pCtx->cq_sysScan)))
	{
		if (HeapTupleIsValid(tuple) && pchn->bDelete)
		{
			pCtx->cq_lasttup = tuple;
			if (pchn->bDelete)
				simple_heap_delete(rel, &tuple->t_self);
		}

		ii++;
	}
	systable_endscan(pCtx->cq_sysScan); 
	caql_heapclose(pCtx);

	return (ii);
}
Example #11
0
/* ----------------------------------------------------------------
 * caql_getcstring_plus()
 * Return a cstring column from the first tuple and end the scan.
 * ----------------------------------------------------------------
 */
char *caql_getcstring_plus(cqContext *pCtx0, int *pFetchcount,
						   bool *pbIsNull, cq_list *pcql)
{
	const char *caql_str = pcql->caqlStr;
	const char *filenam = pcql->filename;
	int			lineno = pcql->lineno;
	struct caql_hash_cookie	*pchn = cq_lookup(caql_str, strlen(caql_str), pcql);
	cqContext  *pCtx;
	cqContext   cqc;
	HeapTuple   tuple;
	char	   *result = NULL;

	if (NULL == pchn)
		elog(ERROR, "invalid caql string: %s\nfile: %s, line %d", 
			 caql_str, filenam, lineno);

	Assert(!pchn->bInsert); /* INSERT not allowed */

	/* use the provided context, or provide a clean local ctx  */
	if (pCtx0)
		pCtx = pCtx0;
	else
		pCtx = cqclr(&cqc);

	pCtx = caql_switch(pchn, pCtx, pcql);
	/* NOTE: caql_switch frees the pcql */

	if (pFetchcount)
		*pFetchcount = 0;
	if (pbIsNull)
		*pbIsNull = true;

	/* use the SysCache */
	if (pCtx->cq_usesyscache)
	{
		tuple = SearchSysCacheKeyArray(pCtx->cq_cacheId, 
									   pCtx->cq_NumKeys, 
									   pCtx->cq_cacheKeys);
	}
	else
	{
		tuple = systable_getnext(pCtx->cq_sysScan);
	}

	if (HeapTupleIsValid(tuple))
	{
		bool		isnull;
		Datum		d;

		if (pFetchcount)
			*pFetchcount = 1;

		d = caql_getattr_internal(pCtx, tuple, pchn->attnum, &isnull);

		if (!isnull)
		{
			switch (pchn->atttype)
			{
				case NAMEOID:
					result = DatumGetCString(DirectFunctionCall1(nameout, d));
					break;

				case TEXTOID:
					result = DatumGetCString(DirectFunctionCall1(textout, d));
					break;

				default:
					elog(ERROR, "column not a cstring: %s\nfile: %s, line %d", 
						 caql_str, filenam, lineno);
			}
		}
		if (pbIsNull)
			*pbIsNull = isnull;
	} /* end HeapTupleIsValid */

	if (pCtx->cq_usesyscache)
	{  
		if (HeapTupleIsValid(tuple))
			ReleaseSysCache(tuple);
	}
	else
	{	
		if (pFetchcount && HeapTupleIsValid(tuple))
		{				
			if (HeapTupleIsValid(systable_getnext(pCtx->cq_sysScan)))
			{
				*pFetchcount = 2;	
			}
		}
		systable_endscan(pCtx->cq_sysScan); 
	}		
	caql_heapclose(pCtx);

	return (result);
} /* end caql_getcstring_plus */
Example #12
0
/* ----------------------------------------------------------------
 * caql_getoid_only()
 * Return the oid of the first tuple and end the scan
 * If pbOnly is not NULL, return TRUE if a second tuple is not found,
 * else return FALSE
 * ----------------------------------------------------------------
 */
Oid caql_getoid_only(cqContext *pCtx0, bool *pbOnly, cq_list *pcql)
{
	const char *caql_str = pcql->caqlStr;
	const char *filenam = pcql->filename;
	int			lineno = pcql->lineno;
	struct caql_hash_cookie	*pchn = cq_lookup(caql_str, strlen(caql_str), pcql);
	cqContext  *pCtx;
	cqContext	cqc;
	HeapTuple	tuple;
	Oid			result = InvalidOid;

	if (NULL == pchn)
		elog(ERROR, "invalid caql string: %s\nfile: %s, line %d", 
			 caql_str, filenam, lineno);

	Assert(!pchn->bInsert); /* INSERT not allowed */

	/* use the provided context, or provide a clean local ctx  */
	if (pCtx0)
		pCtx = pCtx0;
	else
		pCtx = cqclr(&cqc);

	pCtx = caql_switch(pchn, pCtx, pcql);
	/* NOTE: caql_switch frees the pcql */

	if (pbOnly)
		*pbOnly = true;

	/* use the SysCache */
	if (pCtx->cq_usesyscache)
	{
		tuple = SearchSysCacheKeyArray(pCtx->cq_cacheId, 
									   pCtx->cq_NumKeys, 
									   pCtx->cq_cacheKeys);
		if (HeapTupleIsValid(tuple))
		{
			result = HeapTupleGetOid(tuple);
			ReleaseSysCache(tuple);
			/* only one */
		}
		caql_heapclose(pCtx);

		return (result);
	}

	if (HeapTupleIsValid(tuple = systable_getnext(pCtx->cq_sysScan)))
	{
		result = HeapTupleGetOid(tuple);

		if (pbOnly)
		{
			*pbOnly = 
				!(HeapTupleIsValid(tuple = 
								   systable_getnext(pCtx->cq_sysScan)));
		}
	}
	systable_endscan(pCtx->cq_sysScan); 
	caql_heapclose(pCtx);
	return (result);
}
Example #13
0
/* ----------------------------------------------------------------
 * caql_getoid_plus()
 * Return an oid column from the first tuple and end the scan.
 * Note: this works for regproc columns as well, but you should cast
 * the output as RegProcedure.
 * ----------------------------------------------------------------
 */
Oid caql_getoid_plus(cqContext *pCtx0, int *pFetchcount,
					 bool *pbIsNull, cq_list *pcql)
{
	const char *caql_str = pcql->caqlStr;
	const char *filenam = pcql->filename;
	int			lineno = pcql->lineno;
	struct caql_hash_cookie	*pchn = cq_lookup(caql_str, strlen(caql_str), pcql);
	cqContext  *pCtx;
	cqContext	cqc;
	HeapTuple	tuple;
	Oid			result = InvalidOid;

	if (NULL == pchn)
		elog(ERROR, "invalid caql string: %s\nfile: %s, line %d", 
			 caql_str, filenam, lineno);

	Assert(!pchn->bInsert); /* INSERT not allowed */

	/* use the provided context, or provide a clean local ctx  */
	if (pCtx0)
		pCtx = pCtx0;
	else
		pCtx = cqclr(&cqc);

	pCtx = caql_switch(pchn, pCtx, pcql);
	/* NOTE: caql_switch frees the pcql */

	if (pFetchcount)
		*pFetchcount = 0;
	if (pbIsNull)
		*pbIsNull = true;

	/* use the SysCache */
	if (pCtx->cq_usesyscache)
	{
		tuple = SearchSysCacheKeyArray(pCtx->cq_cacheId, 
									   pCtx->cq_NumKeys, 
									   pCtx->cq_cacheKeys);
	}
	else
	{
		tuple = systable_getnext(pCtx->cq_sysScan);
	}

	if (HeapTupleIsValid(tuple))
	{
		if (pFetchcount)
			*pFetchcount = 1;
		
		/* if attnum not set, (InvalidAttrNumber == 0)
		 * use tuple oid, else extract oid from column 
		 * (includes ObjectIdAttributeNumber == -2) 
		 */
		if (pchn->attnum <= InvalidAttrNumber) 
		{
			if (pbIsNull)
				*pbIsNull = false;
			result = HeapTupleGetOid(tuple);
		}
		else /* find oid column */
		{
			bool		isnull;
			Datum		d = caql_getattr_internal(pCtx, tuple, pchn->attnum,
												  &isnull);

			if (!isnull)
			{
				switch (pchn->atttype)
				{
					case OIDOID:
					case REGPROCOID:
						result = DatumGetObjectId(d);
						break;

					default:
						elog(ERROR, "column not an oid: %s\nfile: %s, line %d", 
							 caql_str, filenam, lineno);
				}
			}
			if (pbIsNull)
				*pbIsNull = isnull;
		}
	} /* end HeapTupleIsValid */

	if (pCtx->cq_usesyscache)
	{  
		if (HeapTupleIsValid(tuple))
			ReleaseSysCache(tuple);
	}
	else
	{	
		if (pFetchcount && HeapTupleIsValid(tuple))
		{				
			if (HeapTupleIsValid(systable_getnext(pCtx->cq_sysScan)))
			{
				*pFetchcount = 2;	
			}
		}
		systable_endscan(pCtx->cq_sysScan); 
	}		
	caql_heapclose(pCtx);

	return (result);
} /* end caql_getoid_plus */
/*
 * This tests operators and orderby
 */
void
test__caql_switch7(void **state)
{
	const char		   *query = "SELECT * FROM pg_class "
								"WHERE oid < :1 AND relnatts > :2 AND "
								"relfilenode <= :3 AND relpages >= :4 "
								"ORDER BY oid, relnatts, relfilenode";
	struct caql_hash_cookie *hash_cookie;
	cqContext			context = {0}, *pCtx;
	Datum				keys[] = {ObjectIdGetDatum(10000),
								  Int16GetDatum(10),
								  ObjectIdGetDatum(10000),
								  Int32GetDatum(5)};
	cq_list			   *pcql = CaQL(query, 1, keys);
	RelationData		dummyrel;
	SysScanDescData		dummydesc;

	dummyrel.rd_id = RelationRelationId;
	hash_cookie = cq_lookup(query, strlen(query), pcql);

	pCtx = caql_snapshot(cqclr(&context), SnapshotDirty);
	/* setup heap_open */
	expect__heap_open(RelationRelationId, true,
					  AccessShareLock, true,
					  &dummyrel);

	/* setup ScanKeyInit */
	expect__ScanKeyInit(NULL, false,
						ObjectIdAttributeNumber, true,
						BTLessStrategyNumber, true,
						F_OIDLT, true,
						NULL, false);
	expect__ScanKeyInit(NULL, false,
						Anum_pg_class_relnatts, true,
						BTGreaterStrategyNumber, true,
						F_INT2GT, true,
						NULL, false);
	expect__ScanKeyInit(NULL, false,
						Anum_pg_class_relfilenode, true,
						BTLessEqualStrategyNumber, true,
						F_OIDLE, true,
						NULL, false);
	expect__ScanKeyInit(NULL, false,
						Anum_pg_class_relpages, true,
						BTGreaterEqualStrategyNumber, true,
						F_INT4GE, true,
						NULL, false);

	/* setup systable_beginscan */
	expect__systable_beginscan(&dummyrel, true,
							   InvalidOid, false,
							   false, true,
							   SnapshotDirty, true,
							   4, true,
							   NULL, false,
							   &dummydesc);

	pCtx = caql_switch(hash_cookie, pCtx, pcql);

	assert_true(pCtx != NULL);
	assert_true(!pCtx->cq_usesyscache);
	assert_true(pCtx->cq_heap_rel->rd_id == RelationRelationId);
	assert_true(!pCtx->cq_useidxOK);
}