/*-
 * Copyright (c) 2014-2019 MongoDB, Inc.
 * Copyright (c) 2008-2014 WiredTiger, Inc.
 *	All rights reserved.
 *
 * See the file LICENSE for redistribution information.
 */

#include "wt_internal.h"

/*
 * __wt_curstat_colgroup_init --
 *     Initialize the statistics for a column group.
 */
int
__wt_curstat_colgroup_init(
  WT_SESSION_IMPL *session, const char *uri, const char *cfg[], WT_CURSOR_STAT *cst)
{
    WT_COLGROUP *colgroup;
    WT_DECL_ITEM(buf);
    WT_DECL_RET;

    WT_RET(__wt_schema_get_colgroup(session, uri, false, NULL, &colgroup));

    WT_RET(__wt_scr_alloc(session, 0, &buf));
    WT_ERR(__wt_buf_fmt(session, buf, "statistics:%s", colgroup->source));
    ret = __wt_curstat_init(session, buf->data, NULL, cfg, cst);

err:
    __wt_scr_free(session, &buf);
    return (ret);
}

/*
 * __wt_curstat_index_init --
 *     Initialize the statistics for an index.
 */
int
__wt_curstat_index_init(
  WT_SESSION_IMPL *session, const char *uri, const char *cfg[], WT_CURSOR_STAT *cst)
{
    WT_DECL_ITEM(buf);
    WT_DECL_RET;
    WT_INDEX *idx;

    WT_RET(__wt_schema_get_index(session, uri, false, false, &idx));

    WT_RET(__wt_scr_alloc(session, 0, &buf));
    WT_ERR(__wt_buf_fmt(session, buf, "statistics:%s", idx->source));
    ret = __wt_curstat_init(session, buf->data, NULL, cfg, cst);

err:
    __wt_scr_free(session, &buf);
    return (ret);
}

/*
 * __curstat_size_only --
 *     For very simple tables we can avoid getting table handles if configured to only retrieve the
 *     size. It's worthwhile because workloads that create and drop a lot of tables can put a lot of
 *     pressure on the table list lock.
 */
static int
__curstat_size_only(WT_SESSION_IMPL *session, const char *uri, bool *was_fast, WT_CURSOR_STAT *cst)
{
    WT_CONFIG cparser;
    WT_CONFIG_ITEM ckey, colconf, cval;
    WT_DECL_RET;
    WT_ITEM namebuf;
    wt_off_t filesize;
    char *tableconf;
    bool exist;

    WT_CLEAR(namebuf);
    *was_fast = false;

    /* Retrieve the metadata for this table. */
    WT_RET(__wt_metadata_search(session, uri, &tableconf));

    /*
     * The fast path only works if the table consists of a single file and does not have any
     * indexes. The absence of named columns is how we determine that neither of those conditions
     * can be satisfied.
     */
    WT_ERR(__wt_config_getones(session, tableconf, "columns", &colconf));
    __wt_config_subinit(session, &cparser, &colconf);
    if ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0)
        goto err;

    /* Build up the file name from the table URI. */
    WT_ERR(__wt_buf_fmt(session, &namebuf, "%s.wt", uri + strlen("table:")));

    /*
     * Get the size of the underlying file. This will fail for anything other than simple tables
     * (LSM for example) and will fail if there are concurrent schema level operations (for example
     * drop). That is fine - failing here results in falling back to the slow path of opening the
     * handle.
     */
    WT_ERR(__wt_fs_exist(session, namebuf.data, &exist));
    if (exist) {
        WT_ERR(__wt_fs_size(session, namebuf.data, &filesize));

        /* Setup and populate the statistics structure */
        __wt_stat_dsrc_init_single(&cst->u.dsrc_stats);
        cst->u.dsrc_stats.block_size = filesize;
        __wt_curstat_dsrc_final(cst);

        *was_fast = true;
    }

err:
    __wt_free(session, tableconf);
    __wt_buf_free(session, &namebuf);

    return (ret);
}

/*
 * __wt_curstat_table_init --
 *     Initialize the statistics for a table.
 */
int
__wt_curstat_table_init(
  WT_SESSION_IMPL *session, const char *uri, const char *cfg[], WT_CURSOR_STAT *cst)
{
    WT_CURSOR *stat_cursor;
    WT_DECL_ITEM(buf);
    WT_DECL_RET;
    WT_DSRC_STATS *new, *stats;
    WT_TABLE *table;
    u_int i;
    const char *name;
    bool was_fast;

    /*
     * If only gathering table size statistics, try a fast path that avoids the schema and table
     * list locks.
     */
    if (F_ISSET(cst, WT_STAT_TYPE_SIZE)) {
        WT_RET(__curstat_size_only(session, uri, &was_fast, cst));
        if (was_fast)
            return (0);
    }

    name = uri + strlen("table:");
    WT_RET(__wt_schema_get_table(session, name, strlen(name), false, 0, &table));

    WT_ERR(__wt_scr_alloc(session, 0, &buf));

    /*
     * Process the column groups.
     *
     * Set the cursor to reference the data source statistics; we don't
     * initialize it, instead we copy (rather than aggregate), the first
     * column's statistics, which has the same effect.
     */
    stats = &cst->u.dsrc_stats;
    for (i = 0; i < WT_COLGROUPS(table); i++) {
        WT_ERR(__wt_buf_fmt(session, buf, "statistics:%s", table->cgroups[i]->name));
        WT_ERR(__wt_curstat_open(session, buf->data, NULL, cfg, &stat_cursor));
        new = (WT_DSRC_STATS *)WT_CURSOR_STATS(stat_cursor);
        if (i == 0)
            *stats = *new;
        else
            __wt_stat_dsrc_aggregate_single(new, stats);
        WT_ERR(stat_cursor->close(stat_cursor));
    }

    /* Process the indices. */
    WT_ERR(__wt_schema_open_indices(session, table));
    for (i = 0; i < table->nindices; i++) {
        WT_ERR(__wt_buf_fmt(session, buf, "statistics:%s", table->indices[i]->name));
        WT_ERR(__wt_curstat_open(session, buf->data, NULL, cfg, &stat_cursor));
        new = (WT_DSRC_STATS *)WT_CURSOR_STATS(stat_cursor);
        __wt_stat_dsrc_aggregate_single(new, stats);
        WT_ERR(stat_cursor->close(stat_cursor));
    }

    __wt_curstat_dsrc_final(cst);

err:
    WT_TRET(__wt_schema_release_table(session, &table));

    __wt_scr_free(session, &buf);
    return (ret);
}
