/*
 *  smgr.c -- public interface routines to storage manager switch.
 *
 *	All file system operations in POSTGRES dispatch through these
 *	routines.
 */

#include "tmp/c.h"
#include "tmp/postgres.h"

#include "machine.h"
#include "storage/ipci.h"
#include "storage/smgr.h"
#include "storage/block.h"
#include "utils/rel.h"
#include "utils/log.h"

RcsId("$Header: /private/postgres/src/storage/smgr/RCS/smgr.c,v 1.9 1992/07/04 03:50:40 mao Exp $");

typedef struct f_smgr {
    int		(*smgr_init)();		/* may be NULL */
    int		(*smgr_shutdown)();	/* may be NULL */
    int		(*smgr_create)();
    int		(*smgr_unlink)();
    int		(*smgr_extend)();
    int		(*smgr_open)();
    int		(*smgr_close)();
    int		(*smgr_read)();
    int		(*smgr_write)();
    int		(*smgr_flush)();
    int		(*smgr_blindwrt)();
    int		(*smgr_nblocks)();
    int		(*smgr_commit)();	/* may be NULL */
    int		(*smgr_abort)();	/* may be NULL */
} f_smgr;

/* routines defined elsewhere */
extern int	mdinit(), mdcreate(), mdunlink();
extern int	mdextend(), mdopen(), mdclose(), mdread();
extern int	mdwrite(), mdflush(), mdblindwrt(), mdnblocks();
extern int	mdcommit(), mdabort();

#ifdef SONY_JUKEBOX
extern int	sjinit(), sjshutdown(), sjcreate(), sjunlink();
extern int	sjextend(), sjopen(), sjclose(), sjread();
extern int	sjwrite(), sjflush(), sjblindwrt(), sjnblocks();
extern int	sjcommit(), sjabort();
#endif /* SONY_JUKEBOX */

#ifdef MAIN_MEMORY
extern int	mminit(), mmshutdown(), mmcreate(), mmunlink();
extern int	mmextend(), mmopen(), mmclose(), mmread();
extern int	mmwrite(), mmflush(), mmblindwrt(), mmnblocks();
extern int	mmcommit(), mmabort();
#endif /* MAIN_MEMORY */

/*
 *  The weird placement of commas in this init block is to keep the compiler
 *  happy, regardless of what storage managers we have (or don't have).
 */

static f_smgr smgrsw[] = {

    /* magnetic disk */
    { mdinit, NULL, mdcreate, mdunlink, mdextend, mdopen, mdclose,
      mdread, mdwrite, mdflush, mdblindwrt, mdnblocks, mdcommit, mdabort }

#ifdef SONY_JUKEBOX
    /* sony jukebox */
    , { sjinit, sjshutdown, sjcreate, sjunlink, sjextend, sjopen, sjclose,
        sjread, sjwrite, sjflush, sjblindwrt, sjnblocks, sjcommit, sjabort }

#endif /* SONY_JUKEBOX */

#ifdef MAIN_MEMORY
    /* main memory */
    , { mminit, mmshutdown, mmcreate, mmunlink, mmextend, mmopen, mmclose,
        mmread, mmwrite, mmflush, mmblindwrt, mmnblocks, mmcommit, mmabort }

#endif /* MAIN_MEMORY */
};

/*
 *  This array records which storage managers are write-once, and which
 *  support overwrite.  A 'true' entry means that the storage manager is
 *  write-once.  In the best of all possible worlds, there would be no
 *  write-once storage managers.
 */

static bool smgrwo[] = {
    false		/* magnetic disk */
#ifdef SONY_JUKEBOX
    , true		/* sony jukebox */
#endif /* SONY_JUKEBOX */
#ifdef MAIN_MEMORY
    , false		/* main memory*/
#endif /* MAIN_MEMORY */
};
static int NSmgr = lengthof(smgrsw);

/*
 *  smgrinit(), smgrshutdown() -- Initialize or shut down all storage
 *				  managers.
 *
 */

int
smgrinit()
{
    int i;
    extern char *smgrout();

    for (i = 0; i < NSmgr; i++) {
	if (smgrsw[i].smgr_init) {
	    if ((*(smgrsw[i].smgr_init))() == SM_FAIL)
		elog(FATAL, "initialization failed on %s", smgrout(i));
	}
    }

    /* register the shutdown proc */
    on_exitpg(smgrshutdown, 0);

    return (SM_SUCCESS);
}

void
smgrshutdown(dummy)
    int dummy;
{
    int i;
    extern char *smgrout();

    for (i = 0; i < NSmgr; i++) {
	if (smgrsw[i].smgr_shutdown) {
	    if ((*(smgrsw[i].smgr_shutdown))() == SM_FAIL)
		elog(FATAL, "shutdown failed on %s", smgrout(i));
	}
    }
}

/*
 *  smgrcreate() -- Create a new relation.
 *
 *	This routine takes a reldesc, creates the relation on the appropriate
 *	device, and returns a file descriptor for it.
 */

int
smgrcreate(which, reln)
    int16 which;
    Relation reln;
{
    int fd;

    if ((fd = (*(smgrsw[which].smgr_create))(reln)) < 0)
	elog(WARN, "cannot open %16s", &(reln->rd_rel->relname.data[0]));

    return (fd);
}

/*
 *  smgrunlink() -- Unlink a relation.
 *
 *	The relation is removed from the store.
 */

int
smgrunlink(which, reln)
    int16 which;
    Relation reln;
{
    int status;

    if ((status = (*(smgrsw[which].smgr_unlink))(reln)) == SM_FAIL)
	elog(WARN, "cannot unlink %16s", &(reln->rd_rel->relname.data[0]));

    return (status);
}

/*
 *  smgrextend() -- Add a new block to a file.
 *
 *	Returns SM_SUCCESS on success; aborts the current transaction on
 *	failure.
 */

int
smgrextend(which, reln, buffer)
    int16 which;
    Relation reln;
    char *buffer;
{
    int status;

    /* new blocks are zero-filled */
    bzero((char *) buffer, BLCKSZ);

    status = (*(smgrsw[which].smgr_extend))(reln, buffer);

    if (status == SM_FAIL)
	elog(WARN, "%16s: cannot extend", &(reln->rd_rel->relname.data[0]));

    return (status);
}

/*
 *  smgropen() -- Open a relation using a particular storage manager.
 *
 *	Returns the fd for the open relation on success, aborts the
 *	transaction on failure.
 */

int
smgropen(which, reln)
    int16 which;
    Relation reln;
{
    int fd;

    if ((fd = (*(smgrsw[which].smgr_open))(reln)) < 0)
	elog(WARN, "cannot open %16s", &(reln->rd_rel->relname.data[0]));

    return (fd);
}

/*
 *  smgrclose() -- Close a relation.
 *
 *	Returns SM_SUCCESS on success, aborts on failure.
 */

int
smgrclose(which, reln)
    int16 which;
    Relation reln;
{
    if ((*(smgrsw[which].smgr_close))(reln) == SM_FAIL)
	elog(WARN, "cannot close %16s", &(reln->rd_rel->relname.data[0]));

    return (SM_SUCCESS);
}

/*
 *  smgrread() -- read a particular block from a relation into the supplied
 *		  buffer.
 *
 *	This routine is called from the buffer manager in order to
 *	instantiate pages in the shared buffer cache.  All storage managers
 *	return pages in the format that POSTGRES expects.  This routine
 *	dispatches the read.  On success, it returns SM_SUCCESS.  On failure,
 *	the current transaction is aborted.
 */

int
smgrread(which, reln, blocknum, buffer)
    int16 which;
    Relation reln;
    BlockNumber blocknum;
    char *buffer;
{
    int status;

    status = (*(smgrsw[which].smgr_read))(reln, blocknum, buffer);

    if (status == SM_FAIL)
	elog(WARN, "cannot read block %d of %16s",
	     blocknum, &(reln->rd_rel->relname.data[0]));

    return (status);
}

/*
 *  smgrwrite() -- Write the supplied buffer out.
 *
 *	This is not a synchronous write -- the interface for that is
 *	smgrflush().  The buffer is written out via the appropriate
 *	storage manager.  This routine returns SM_SUCCESS or aborts
 *	the current transaction.
 */

int
smgrwrite(which, reln, blocknum, buffer)
    int16 which;
    Relation reln;
    BlockNumber blocknum;
    char *buffer;
{
    int status;

    status = (*(smgrsw[which].smgr_write))(reln, blocknum, buffer);

    if (status == SM_FAIL)
	elog(WARN, "cannot write block %d of %16s",
	     blocknum, &(reln->rd_rel->relname.data[0]));

    return (status);
}

/*
 *  smgrflush() -- A synchronous smgrwrite().
 */

int
smgrflush(which, reln, blocknum, buffer)
    int16 which;
    Relation reln;
    BlockNumber blocknum;
    char *buffer;
{
    int status;

    status = (*(smgrsw[which].smgr_flush))(reln, blocknum, buffer);

    if (status == SM_FAIL)
	elog(WARN, "cannot flush block %d of %16s to stable store",
	     blocknum, &(reln->rd_rel->relname.data[0]));

    return (status);
}

/*
 *  smgrblindwrt() -- Write a page out blind.
 *
 *	In some cases, we may find a page in the buffer cache that we
 *	can't make a reldesc for.  This happens, for example, when we
 *	want to reuse a dirty page that was written by a transaction
 *	that has not yet committed, which created a new relation.  In
 *	this case, the buffer manager will call smgrblindwrt() with
 *	the name and OID of the database and the relation to which the
 *	buffer belongs.  Every storage manager must be able to force
 *	this page down to stable storage in this circumstance.
 */

int
smgrblindwrt(which, dbname, relname, dbid, relid, blkno, buffer)
    int16 which;
    Name dbname;
    Name relname;
    OID dbid;
    OID relid;
    BlockNumber blkno;
    char *buffer;
{
    char *dbstr;
    char *relstr;
    int nbytes;
    int status;

    /* do the postgres name dance */
    nbytes = sizeof(NameData) + 1;
    dbstr = (char *) palloc(nbytes);
    relstr = (char *) palloc(nbytes);
    strncpy(dbstr, dbname, sizeof(NameData));
    strncpy(relstr, relname, sizeof(NameData));
    dbstr[nbytes - 1] = '\0';
    relstr[nbytes - 1] = '\0';

    status = (*(smgrsw[which].smgr_blindwrt))(dbstr, relstr, dbid, relid,
					      blkno, buffer);

    if (status == SM_FAIL)
	elog(WARN, "cannot write block %d of %s [%s] blind",
	     blkno, relstr, dbstr);

    pfree(dbstr);
    pfree(relstr);

    return (status);
}
/*
 *  smgrnblocks() -- Calculate the number of POSTGRES blocks in the
 *		     supplied relation.
 *
 *	Returns the number of blocks on success, aborts the current
 *	transaction on failure.
 */

int
smgrnblocks(which, reln)
    int16 which;
    Relation reln;
{
    int nblocks;

    if ((nblocks = (*(smgrsw[which].smgr_nblocks))(reln)) < 0)
	elog(WARN, "cannot count blocks for %16s",
		   &(reln->rd_rel->relname.data[0]));

    return (nblocks);
}

/*
 *  smgrcommit(), smgrabort() -- Commit or abort changes made during the
 *				 current transaction.
 */

int
smgrcommit()
{
    int i;
    extern char *smgrout();

    for (i = 0; i < NSmgr; i++) {
	if (smgrsw[i].smgr_commit) {
	    if ((*(smgrsw[i].smgr_commit))() == SM_FAIL)
		elog(FATAL, "transaction commit failed on %s", smgrout(i));
	}
    }

    return (SM_SUCCESS);
}

int
smgrabort()
{
    int i;
    extern char *smgrout();

    for (i = 0; i < NSmgr; i++) {
	if (smgrsw[i].smgr_abort) {
	    if ((*(smgrsw[i].smgr_abort))() == SM_FAIL)
		elog(FATAL, "transaction abort failed on %s", smgrout(i));
	}
    }

    return (SM_SUCCESS);
}

bool
smgriswo(smgrno)
    int16 smgrno;
{
    if (smgrno < 0 || smgrno >= NSmgr)
	elog(WARN, "illegal storage manager number %d", smgrno);

    return (smgrwo[smgrno]);
}
