/* -------------------------------------------------------------------------
 *
 * pg_backup_null.c
 *
 *	Implementation of an archive that is never saved; it is used by
 *	pg_dump to output a plain text SQL script instead of saving
 *	a real archive.
 *
 *	See the headers to pg_restore for more details.
 *
 * Copyright (c) 2000, Philip Warner
 *		Rights are granted to use this software in any way so long
 *		as this notice is not removed.
 *
 *	The author is not responsible for loss or damages that may
 *	result from it's use.
 *
 *
 * IDENTIFICATION
 *		src/bin/pg_dump/pg_backup_null.c
 *
 * -------------------------------------------------------------------------
 */

#include "pg_backup_archiver.h"
#include "dumpmem.h"
#include "dumputils.h"

#include <unistd.h> /* for dup */

#include "libpq/libpq-fs.h"

#ifdef GAUSS_SFT_TEST
#include "gauss_sft.h"
#endif

static size_t _WriteData(ArchiveHandle* AH, const void* data, size_t dLen);
static size_t _WriteBlobData(ArchiveHandle* AH, const void* data, size_t dLen);
static void _EndData(ArchiveHandle* AH, TocEntry* te);
static int _WriteByte(ArchiveHandle* AH, const int i);
static size_t _WriteBuf(ArchiveHandle* AH, const void* buf, size_t len);
static void _CloseArchive(ArchiveHandle* AH);
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions* ropt);
static void _StartBlobs(ArchiveHandle* AH, TocEntry* te);
static void _StartBlob(ArchiveHandle* AH, TocEntry* te, Oid oid);
static void _EndBlob(ArchiveHandle* AH, TocEntry* te, Oid oid);
static void _EndBlobs(ArchiveHandle* AH, TocEntry* te);

/*
 *	Initializer
 */
void InitArchiveFmt_Null(ArchiveHandle* AH)
{
    /* Assuming static functions, this can be copied for each format. */
    AH->WriteDataptr = _WriteData;
    AH->EndDataptr = _EndData;
    AH->WriteByteptr = _WriteByte;
    AH->WriteBufptr = _WriteBuf;
    AH->Closeptr = _CloseArchive;
    AH->Reopenptr = NULL;
    AH->PrintTocDataptr = _PrintTocData;

    AH->StartBlobsptr = _StartBlobs;
    AH->StartBlobptr = _StartBlob;
    AH->EndBlobptr = _EndBlob;
    AH->EndBlobsptr = _EndBlobs;
    AH->Cloneptr = NULL;
    AH->DeCloneptr = NULL;

    /* Initialize LO buffering */
    AH->lo_buf_size = LOBBUFSIZE;
    AH->lo_buf = (void*)pg_malloc(LOBBUFSIZE);

    /*
     * Now prevent reading...
     */
    if (AH->mode == archModeRead)
        exit_horribly(NULL, "this format cannot be read\n");
}

/*
 * - Start a new TOC entry
 */

/*
 * Called by dumper via archiver from within a data dump routine
 */
static size_t _WriteData(ArchiveHandle* AH, const void* data, size_t dLen)
{
    /* Just send it to output */
    ahwrite(data, 1, dLen, AH);
    return dLen;
}

/*
 * Called by dumper via archiver from within a data dump routine
 * We substitute this for _WriteData while emitting a BLOB
 */
static size_t _WriteBlobData(ArchiveHandle* AH, const void* data, size_t dLen)
{
    if (dLen > 0) {
        PQExpBuffer buf = createPQExpBuffer();

        appendByteaLiteralAHX(buf, (const unsigned char*)data, dLen, AH);

        ahprintf(AH, "SELECT pg_catalog.lowrite(0, %s);\n", buf->data);

        destroyPQExpBuffer(buf);
    }
    return dLen;
}

static void _EndData(ArchiveHandle* AH, TocEntry* te)
{
    ahprintf(AH, "\n\n");
}

/*
 * Called by the archiver when starting to save all BLOB DATA (not schema).
 * This routine should save whatever format-specific information is needed
 * to read the BLOBs back into memory.
 *
 * It is called just prior to the dumper's DataDumper routine.
 *
 * Optional, but strongly recommended.
 */
static void _StartBlobs(ArchiveHandle* AH, TocEntry* te)
{
    ahprintf(AH, "START TRANSACTION;\n\n");
}

/*
 * Called by the archiver when the dumper calls StartBlob.
 *
 * Mandatory.
 *
 * Must save the passed OID for retrieval at restore-time.
 */
static void _StartBlob(ArchiveHandle* AH, TocEntry* te, Oid oid)
{
    bool old_blob_style = (AH->version < K_VERS_1_12);

    if (oid == 0)
        exit_horribly(NULL, "invalid OID for large object\n");

    /* With an old archive we must do drop and create logic here */
    if (old_blob_style && AH->ropt->dropSchema)
        DropBlobIfExists(AH, oid);

    if (old_blob_style)
        ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n", oid, INV_WRITE);
    else
        ahprintf(AH, "SELECT pg_catalog.lo_open('%u', %d);\n", oid, INV_WRITE);

    AH->WriteDataptr = _WriteBlobData;
}

/*
 * Called by the archiver when the dumper calls EndBlob.
 *
 * Optional.
 */
static void _EndBlob(ArchiveHandle* AH, TocEntry* te, Oid oid)
{
    AH->WriteDataptr = _WriteData;

    ahprintf(AH, "SELECT pg_catalog.lo_close(0);\n\n");
}

/*
 * Called by the archiver when finishing saving all BLOB DATA.
 *
 * Optional.
 */
static void _EndBlobs(ArchiveHandle* AH, TocEntry* te)
{
    ahprintf(AH, "COMMIT;\n\n");
}

/* ------
 * Called as part of a RestoreArchive call; for the NULL archive, this
 * just sends the data for a given TOC entry to the output.
 * ------
 */
static void _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions* ropt)
{
    if ((te->dataDumper) != NULL) {
        AH->currToc = te;

        if (strcmp(te->desc, "BLOBS") == 0)
            _StartBlobs(AH, te);

        (*te->dataDumper)((Archive*)AH, te->dataDumperArg);

        if (strcmp(te->desc, "BLOBS") == 0)
            _EndBlobs(AH, te);

        AH->currToc = NULL;
    }
}

static int _WriteByte(ArchiveHandle* AH, const int i)
{
    /* Don't do anything */
    return 0;
}

static size_t _WriteBuf(ArchiveHandle* AH, const void* buf, size_t len)
{
    /* Don't do anything */
    return len;
}

static void _CloseArchive(ArchiveHandle* AH)
{
    /* Nothing to do */
}