/*-
 * Public Domain 2014-2016 MongoDB, Inc.
 * Public Domain 2008-2014 WiredTiger, Inc.
 *
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * In jurisdictions that recognize copyright laws, the author or authors
 * of this software dedicate any and all copyright interest in the
 * software to the public domain. We make this dedication for the benefit
 * of the public at large and to the detriment of our heirs and
 * successors. We intend this dedication to be an overt act of
 * relinquishment in perpetuity of all present and future rights to this
 * software under copyright law.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
#include "test_util.h"

/*
 * JIRA ticket reference: WT-2447
 *
 * Test case description: This test case is adapted from the submitted test
 * program in the JIRA ticket. We create a database of 10,000 entries, with
 * every key i having pair of values (i, i).  Create indices on both values,
 * and establish a join: table.v1 >= 5000 AND table.v2 < 5001.  There's a
 * Bloom filter on v2.  We expect that although we iterate from 5000 to
 * 10000, we'll only have accesses to the main table for key 5000, as
 * 5001-10000 will generally not be in the Bloom filter.  For key 5000,
 * we technically have two accesses to the main table - one occurs when we
 * see key 5000 is in the Bloom filter, and we need to do a full test, we
 * make an access to the projection table:tablename(v2), that's just to get
 * the value of v2, which we'll check by comparison to the cursor at 5001.
 * That counts as a main table access, and when we see it is satisfied and
 * return the complete set of values, we'll access the main table with the
 * full projection (that's the second main table access).
 *
 * Failure mode: Before fixes of WT-2447, we saw lots of accesses to the main
 * table.
 */

#define	N_RECORDS	10000

static void
get_stat_total(WT_SESSION *session, WT_CURSOR *jcursor, const char *descmatch,
    uint64_t *pval)
{
	WT_CURSOR *statcursor;
	uint64_t val;
	int ret;
	bool match;
	char *desc, *valstr;

	match = false;
	*pval = 0;
	testutil_check(session->open_cursor(session, "statistics:join", jcursor,
	    NULL, &statcursor));

	while ((ret = statcursor->next(statcursor)) == 0) {
		testutil_assert(statcursor->get_value(
		    statcursor, &desc, &valstr, &val) == 0);

		printf("statistics: %s: %s: %" PRIu64 "\n", desc, valstr, val);

		if (strstr(desc, descmatch) != NULL) {
			*pval += val;
			match = true;
		}
	}
	testutil_assert(ret == WT_NOTFOUND);
	testutil_check(statcursor->close(statcursor));
	testutil_assert(match);
}

int
main(int argc, char *argv[])
{
	TEST_OPTS *opts, _opts;
	WT_CURSOR *cursor1, *cursor2, *jcursor;
	WT_ITEM d;
	WT_SESSION *session;
	uint64_t maincount;
	int half, i, j;
	const char *tablename;
	char bloom_cfg[128], index1uri[256], index2uri[256], joinuri[256];

	opts = &_opts;
	memset(opts, 0, sizeof(*opts));
	testutil_check(testutil_parse_opts(argc, argv, opts));
	testutil_make_work_dir(opts->home);

	tablename = strchr(opts->uri, ':');
	testutil_assert(tablename != NULL);
	tablename++;
	testutil_check(__wt_snprintf(
	    index1uri, sizeof(index1uri), "index:%s:index1", tablename));
	testutil_check(__wt_snprintf(
	    index2uri, sizeof(index2uri), "index:%s:index2", tablename));
	testutil_check(__wt_snprintf(
	    joinuri, sizeof(joinuri), "join:%s", opts->uri));

	testutil_check(wiredtiger_open(opts->home, NULL,
	    "statistics=(all),create", &opts->conn));
	testutil_check(
	    opts->conn->open_session(opts->conn, NULL, NULL, &session));

	testutil_check(session->create(session, opts->uri,
	    "key_format=i,value_format=iiu,columns=(k,v1,v2,d)"));
	testutil_check(session->create(session, index1uri, "columns=(v1)"));
	testutil_check(session->create(session, index2uri, "columns=(v2)"));

	testutil_check(session->open_cursor(session, opts->uri, NULL, NULL,
	    &cursor1));

	d.size = 4100;
	d.data = dmalloc(d.size);
	memset((char *)d.data, 7, d.size);

	for (i = 0; i < N_RECORDS; ++i)
	{
		cursor1->set_key(cursor1, i);
		cursor1->set_value(cursor1, i, i, &d);
		testutil_check(cursor1->insert(cursor1));
	}

	free((void*)d.data);

	testutil_check(opts->conn->close(opts->conn, NULL));
	testutil_check(wiredtiger_open(opts->home, NULL,
	    "statistics=(all),create,cache_size=1GB", &opts->conn));
	testutil_check(opts->conn->open_session(opts->conn, NULL, NULL,
	    &session));

	testutil_check(session->open_cursor(session, index1uri, NULL, NULL,
	    &cursor1));
	testutil_check(session->open_cursor(session, index2uri, NULL, NULL,
	    &cursor2));

	half = N_RECORDS / 2;
	cursor1->set_key(cursor1, half);
	testutil_check(cursor1->search(cursor1));

	cursor2->set_key(cursor2, half + 1);
	testutil_check(cursor2->search(cursor2));

	testutil_check(__wt_snprintf(bloom_cfg, sizeof(bloom_cfg),
	    "compare=lt,strategy=bloom,count=%d", half));

	testutil_check(session->open_cursor(session, joinuri, NULL, NULL,
	    &jcursor));
	testutil_check(session->join(session, jcursor, cursor1, "compare=ge"));
	testutil_check(session->join(session, jcursor, cursor2, bloom_cfg));

	/* Expect one value returned */
	testutil_assert(jcursor->next(jcursor) == 0);
	i = 0;
	testutil_assert(jcursor->get_key(jcursor, &i) == 0);
	testutil_assert(i == (int)half);
	i = j = 0;
	memset(&d, 0, sizeof(d));
	testutil_assert(jcursor->get_value(jcursor, &i, &j, &d) == 0);
	testutil_assert(i == (int)half);
	testutil_assert(j == (int)half);
	testutil_assert(d.size == 4100);
	for (i = 0; i < 4100; i++)
		testutil_assert(((char *)d.data)[i] == 7);

	testutil_assert(jcursor->next(jcursor) == WT_NOTFOUND);

	/*
	 * Make sure there have been 2 accesses to the main table,
	 * explained in the discussion above.
	 */
	get_stat_total(session, jcursor, "accesses to the main table",
	    &maincount);
	testutil_assert(maincount == 2);

	testutil_cleanup(opts);
	return (EXIT_SUCCESS);
}
