/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2010 Oracle and/or its affiliates.  All rights reserved.
 */

/*
** This file contains code used to implement the .stat command. Any other shell
** features should also be added here.
*/
#include <errno.h>
#include <assert.h>

#include "sqlite3.h"
#include "btreeInt.h"

/*
 * Print statistics for tables and/or indexes using DB->stat_print()
 *
 * If msgfile is NULL, then statistics will be printed into stdout, otherwise
 * the statistics will be printed into the designated output stream.
 *
 * If name input is NULL, then statistics for all tables and indexes are
 * printed, otherwise only the statistics for that table or index is printed.
 *
 * Returns SQLITE_OK if there are no errors, -1 if an error occurred.
 */
SQLITE_API int bdbSqlDbStatPrint(sqlite3 *db, FILE *msgfile, char *name)
{
	BtCursor cur, *pCur = NULL;
	Btree *p;
	char **azResult = NULL;
	char *zErrMsg = NULL;
	char *zSql = NULL;
	DB *dbp;
	FILE *out;
	int i, rc, nRow, iTable, openTransaction = 0;

	if (!db || !db->aDb)
		return -1;

	p = db->aDb[0].pBt;
	assert(p);

	if (!(out = msgfile))
		out = stdout;

	/* Construct query to get root page number(s) */
	if (!name) {
		zSql = sqlite3_mprintf(
		    "SELECT type,name,rootpage FROM sqlite_master");
	}
	else {
		zSql = sqlite3_mprintf(
		    "SELECT type,name,rootpage FROM sqlite_master "
		    "WHERE name='%q'", name);
	}

	if (!zSql) {
		fprintf(stderr, "Error: memory allocation failed\n");
		goto err;
	}

	rc = sqlite3_get_table(db, zSql, &azResult, &nRow, 0, &zErrMsg);
	(void)sqlite3_free(zSql);

	if (zErrMsg) {
		fprintf(stderr, "Error: %s\n", zErrMsg);
		(void)sqlite3_free(zErrMsg);
		if (rc == SQLITE_OK)
			rc = -1;
		goto err;
	}
	else if (rc != SQLITE_OK) {
		fprintf(stderr, "Error: querying sqlite_master\n");
		goto err;
	}
	else if (nRow < 1)
		goto err;

	rc = sqlite3BtreeBeginTrans(p, 0);
	if (rc != SQLITE_OK) {
		fprintf(stderr, "Error: could not enter a transaction\n");
		goto err;
	}
	openTransaction = 1;

	/* Print stats for all tables in query's result */
	for (i = 1; i <= nRow; i++) {
		fprintf(out, "Statistics for %s \"%s\"\n",
		    azResult[3*i], azResult[3*i + 1]);
		iTable = atoi(azResult[3*i + 2]);

		pCur = &cur;
		(void)sqlite3BtreeCursorZero(pCur);

		/* Acquire a read cursor to retrieve the dbp for the table */
		rc = sqlite3BtreeCursor(p, iTable, 0, NULL, pCur);
		if (pCur->eState == CURSOR_FAULT)
			rc = pCur->error;
		if (rc != SQLITE_OK) {
			fprintf(stderr, "Error: could not create cursor\n");
			goto err;
		}

		assert(pCur->cached_db && pCur->cached_db->dbp);
		dbp = pCur->cached_db->dbp;
		(void)dbp->set_msgfile(dbp, out);
		(void)dbp->stat_print(dbp, DB_STAT_ALL);

		(void)sqlite3BtreeCloseCursor(&cur);
		pCur = NULL;
	}

err:
	if (pCur)
		(void)sqlite3BtreeCloseCursor(pCur);
	if (openTransaction)
		sqlite3BtreeCommit(p);
	if (azResult)
		(void)sqlite3_free_table(azResult);

	return rc;
}

/*
 * Print statistics for the database using DB_ENV->stat_print()
 *
 * If msgfile is NULL, then statistics will be printed into stdout, otherwise
 * the statistics will be printed into the designated output stream.
 *
 * Returns SQLITE_OK if there are no errors, -1 if an error occurred.
 */
SQLITE_API int bdbSqlEnvStatPrint(sqlite3 *db, FILE *msgfile)
{
	BtShared *pBt;
	Btree *p;
	FILE *out;

	if (!db || !db->aDb)
		return -1;

	p = db->aDb[0].pBt;
	assert(p);

	pBt = p->pBt;
	assert(pBt);

	if (!p->connected || !(pBt->dbenv))
		return -1;

	if (!(out = msgfile))
		out = stdout;

	fprintf(out, "Statistics for environment\n");
	(void)(pBt->dbenv)->set_msgfile(pBt->dbenv, out);
	(void)(pBt->dbenv)->stat_print(pBt->dbenv, DB_STAT_ALL);

	return SQLITE_OK;
}
