/** @file  tst_psDB.c
 *
 * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
 * vim: set cindent ts=8 sw=4 expandtab:
 *
 *  @brief Contains the tests for psDB.[ch]
 *
 *
 *  @author Aaron Culliney, MHPCC
 *
 *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-06-03 23:46:07 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "psTest.h"
#include "pslib_strict.h"

#define STR_1 "hello world!"
#define STR_2 "foobar"
#define S32_1 1974
#define S32_2 -18
#define F32_1 3.14159
#define F32_2 3.33
#define F64_1 2.7182818
#define F64_2 1.23456789
#define B_1 TRUE
#define B_2 FALSE

static psDB *_init_psDB( void );
static psMetadata *_get_CreateTableMetadata( void );
static psMetadata *_get_RowMetadataValues(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4);
static psMetadata *_get_RowMetadata(
    const char *key0, const char *comm0, const char *val0,
    const char *key1, const char *comm1, psS32  val1,
    const char *key2, const char *comm2, psF32  val2,
    const char *key3, const char *comm3, psF64  val3,
    const char *key4, const char *comm4, psBool val4,
    const char *key5, const char *comm5, psS32  val5);
static psMetadata *_get_row( void );
static psMetadata *_get_where( void );
static psMetadata *_get_update_values(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4);
static void _print_row( psMetadata *row );

//static psS32 TPDBCreate( void );
//static psS32 TPDBDrop( void );
static psS32 TPDBInit( void );
static psS32 TPDBChange( void );
static psS32 TPDBCreateTable( void );
static psS32 TPDBDropTable( void );
static psS32 TPDBSelectColumn( void );
static psS32 TPDBSelectColumnNum( void );
static psS32 TPDBSelectRows( void );
static psS32 TPDBInsertOneRow( void );
static psS32 TPDBInsertRows( void );
static psS32 TPDBDumpRows( void );
static psS32 TPDBDumpCols( void );
static psS32 TPDBUpdateRows( void );
static psS32 TPDBDeleteRows( void );

testDescription tests[] = {
                              {TPDBInit,            -1,  "dbInit",            0, false},
                              {TPDBChange,          -2,  "dbChange",          0, false},
                              {TPDBCreateTable,     -3,  "dbCreateTable",     0, false},
                              {TPDBDropTable,       -4,  "dbDropTable",       0, false},
                              {TPDBSelectColumn,    -5,  "dbSelectColumn",    0, false},
                              {TPDBSelectColumnNum, -6,  "dbSelectColumnNum", 0, false},
                              {TPDBSelectRows,      -7,  "dbSelectRows",      0, false},
                              {TPDBInsertOneRow,    -8,  "dbInsertOneRow",    0, false},
                              {TPDBInsertRows,      -9,  "dbInsertRows",      0, false},
                              {TPDBDumpRows,        -10, "dbDumpRows",        0, false},
                              {TPDBDumpCols,        -11, "dbDumpCols",        0, false},
                              {TPDBUpdateRows,      -12, "dbUpdateRows",      0, false},
                              {TPDBDeleteRows,      -13, "dbDeleteRows",      0, false},
                              //{TPDBCreate,        -14, "dbCreate",          0, false},
                              //{TPDBDrop,          -15, "dbDrop",            0, false},
                              {NULL}
                          };

static const char *host    = "localhost";
static const char *user    = "test";
static const char *passwd  = "";
static const char *dbname  = "test";

// internal method to initialize DB
psDB *_init_psDB( void )
{
    psDB *dbh = NULL;

    psLogMsg( __func__, PS_LOG_INFO, "initialize database connection...\n" );

    dbh = psDBInit(host, user, passwd, dbname);
    if (dbh == NULL) {
        psError(PS_ERR_UNKNOWN, true, "could not initialize DB connection in %s!", __func__ );
    }

    return dbh;
}

psMetadata *_get_CreateTableMetadata( void )
{
    return _get_RowMetadata(
               "key_string", "comment-string",     "90000", // value used to determine size of field
               "key_s32",    "Primary Key",        0,       // values unused here ...
               "key_f32",    "Key",                0.0,
               "key_f64",    "",                   0.0,
               "key_bool",   "",                   FALSE,
               "key_s32_0",  "Key,AUTO_INCREMENT", 0);
}

psMetadata *_get_RowMetadataValues(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4)
{
    return _get_RowMetadata(
               "key_string", "comment-string", val0,
               "key_s32",    "comment-s32",    val1,
               "key_f32",    "comment-f32",    val2,
               "key_f64",    "comment-f64",    val3,
               "key_bool",   "comment-bool",   val4,
               NULL,         NULL,             0.0);
}

psMetadata *_get_RowMetadata(
    const char *key0, const char *comm0, const char *val0,
    const char *key1, const char *comm1, psS32  val1,
    const char *key2, const char *comm2, psF32  val2,
    const char *key3, const char *comm3, psF64  val3,
    const char *key4, const char *comm4, psBool val4,
    const char *key5, const char *comm5, psS32  val5)
{
    psMetadata *md = NULL;
    psMetadataItem *str=NULL, *s32_0=NULL, *s32=NULL, *f32=NULL, *f64=NULL, *b;

    md = psMetadataAlloc();
    if (md == NULL) {
        psError(PS_ERR_UNKNOWN, true, "could not create psMetadata in %s!", __func__ );
        return NULL;
    }
    if (key0 != NULL) {
        str = psMetadataItemAllocStr(key0, comm0, val0);
    }
    if (key5 != NULL) {
        s32_0 = psMetadataItemAllocS32(key5, comm5, val5);
    }
    s32 = psMetadataItemAllocS32 (key1, comm1, val1);
    f32 = psMetadataItemAllocF32 (key2, comm2, val2);
    f64 = psMetadataItemAllocF64 (key3, comm3, val3);
    b   = psMetadataItemAllocBool(key4, comm4, val4);

    if ( ((key0 != NULL) && (str == NULL)) || (s32 == NULL) || (f32 == NULL) || (f64 == NULL) || (b == NULL) ||
            ((key5 != NULL) && (s32_0 == NULL)) ) {
        psError(PS_ERR_UNKNOWN, true, "could not create psMetadataItem(s) in %s!", __func__ );
        return NULL;
    }
    if ( ((key0 != NULL) && !psMetadataAddItem(md, str, 0, 0)) ||
            !psMetadataAddItem(md, s32, 0, 0) || !psMetadataAddItem(md, f32, 0, 0) ||
            !psMetadataAddItem(md, f64, 0, 0) || !psMetadataAddItem(md, b,   0, 0) ||
            ((key5 != NULL) && !psMetadataAddItem(md, s32_0, 0, 0))) {
        psError(PS_ERR_UNKNOWN, true, "could not add psMetadataItem(s) to psMetadata in %s!", __func__ );
        return NULL;
    }

    if (str != NULL) {
        psFree(str);
    }
    psFree(s32);
    psFree(f32);
    psFree(f64);
    psFree(b);
    if (s32_0 != NULL) {
        psFree(s32_0);
    }

    return md;
}

psMetadata *_get_const_row1( void )
{
    char buf[32];
    snprintf(buf, 32, "%s","randrow1" );
    return _get_RowMetadata(
               "key_string", "comment-string", buf,
               //               "key_s32",    "comment-s32",    rand(),
               "key_s32",    "comment-s32",    12345,
               //               "key_f32",    "comment-f32",    (double)rand()/RAND_MAX,
               "key_f32",    "comment-f32",    9876.5,
               //               "key_f64",    "comment-f64",    (double)rand()/RAND_MAX,
               "key_f64",    "comment-f64",    456.75,
               //               "key_bool",   "comment-bool",   round((double)rand()/RAND_MAX),
               "key_bool",   "comment-bool",   true,
               NULL,         NULL,             0.0);
}

psMetadata *_get_const_row2( void )
{
    char buf[32];
    snprintf(buf, 32, "%s","randrow2");
    return _get_RowMetadata(
               "key_string", "comment-string", buf,
               //               "key_s32",    "comment-s32",    rand(),
               "key_s32",    "comment-s32",    2345,
               //               "key_f32",    "comment-f32",    (double)rand()/RAND_MAX,
               "key_f32",    "comment-f32",    876.5,
               //               "key_f64",    "comment-f64",    (double)rand()/RAND_MAX,
               "key_f64",    "comment-f64",    56.75,
               //               "key_bool",   "comment-bool",   round((double)rand()/RAND_MAX),
               "key_bool",   "comment-bool",   false,
               NULL,         NULL,             0.0);
}

psMetadata *_get_row( void )
{
    return _get_RowMetadata(
               "key_string", "comment-string", STR_1,
               "key_s32",    "comment-s32",    S32_1,
               "key_f32",    "comment-f32",    F32_1,
               "key_f64",    "comment-f64",    F64_1,
               "key_bool",   "comment-bool",   B_1,
               NULL,         NULL,             0.0);
}

psMetadata *_get_where( void )
{
    psMetadata *md = NULL;
    psMetadataItem *s32=NULL;

    md = psMetadataAlloc();
    if (md == NULL) {
        psError(PS_ERR_UNKNOWN, true, "could not create psMetadata in %s!", __func__ );
        return NULL;
    }
    s32 = psMetadataItemAllocS32("key_s32", "", S32_1);
    if (s32 == NULL) {
        psError(PS_ERR_UNKNOWN, true, "could not create psMetadataItem in %s!", __func__ );
        return NULL;
    }
    if (!psMetadataAddItem(md, s32, 0, 0)) {
        psError(PS_ERR_UNKNOWN, true, "could not add psMetadataItem(s) to psMetadata in %s!", __func__ );
        return NULL;
    }

    psFree(s32);
    return md;
}

psMetadata *_get_update_values(const char *val0, psS32 val1, psF32 val2, psF64 val3, psBool val4)
{
    return _get_RowMetadataValues(val0, val1, val2, val3, val4);
}

void _print_row( psMetadata *row )
{
    psMetadataIterator *iter=NULL;
    psMetadataItem *item=NULL;

    iter = psMetadataIteratorAlloc(row, 0, NULL);
    while ((item = psMetadataGetAndIncrement(iter)) != NULL) {
        if (item->type == PS_META_STR) {
            psLogMsg( __func__, PS_LOG_INFO, "[%s]=[%s]\n", item->name, item->data.V );
        } else if (item->type == PS_META_S32) {
            psLogMsg( __func__, PS_LOG_INFO, "[%s]=[%d]\n", item->name, item->data.S32 );
        } else if (item->type == PS_META_F32) {
            psLogMsg( __func__, PS_LOG_INFO, "[%s]=[%g]\n", item->name, item->data.F32 );
        } else if (item->type == PS_META_F64) {
            psLogMsg( __func__, PS_LOG_INFO, "[%s]=[%g]\n", item->name, item->data.F64 );
        } else if (item->type == PS_META_BOOL) {
            psLogMsg( __func__, PS_LOG_INFO, "[%s]=[%s]\n", item->name, item->data.B ? "Yay" : "Nay" );
        }
    }

    psFree(iter);
}

psS32 main(psS32 argc, char* argv[])
{
    psLogSetLevel( PS_LOG_INFO );

    srand(time(NULL));

    return ( ! runTestSuite( stderr, "psDB", tests, argc, argv ) );
}

// Testpoint #XXX, initialize/break-down MySQL connection.
psS32 TPDBInit( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;

    psLogMsg( __func__, PS_LOG_INFO, "psDBInit/psDBCleanup shall initialize/cleanup database connection.\n" );

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, psDBChange shall change databases.
psS32 TPDBChange( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBChange shall change to a new test database.\n" );

    failed = ! psDBChange(dbh, dbname);

    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, psDBCreateTable shall create tables in the test database ...
psS32 TPDBCreateTable( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table1";
    psMetadata *md = _get_CreateTableMetadata();

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBCreateTable shall create a new table in the new test database.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {
        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, psDBDropTable shall create tables in the test database ...
psS32 TPDBDropTable( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table2";
    psMetadata *row=NULL, *md = _get_CreateTableMetadata();

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBDropTable shall create/drop a new table in the new test database.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {
        failed = ! psDBDropTable(dbh, table);

        // insert should fail ...
        psLogMsg( __func__, PS_LOG_INFO, "psDBDropTable: insert should fail here...\n" );
        row = _get_row();
        if (psDBInsertOneRow(dbh, table, row)) {
            psLogMsg( __func__, PS_LOG_INFO, "oops, insert unexpectedly succeeded!\n" );
            failed = 1;
        }
        psFree(row);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBSelectColumn shall write/select a column from a test table ...
psS32 TPDBSelectColumn( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table3";
    psArray *ary = NULL;
    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
    char *ptr=NULL;

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumn shall select data from a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {

        row = _get_const_row1();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_const_row2();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_row();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        ary = psDBSelectColumn(dbh, table, "key_string", 0);
        if (ary == NULL) {
            failed = 1;
        } else {
            ptr = psArrayGet(ary, 2);
            psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumn: [%s]\n", ptr );

            ptr = psArrayGet(ary, 1);
            psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumn: [%s]\n", ptr );

            ptr = psArrayGet(ary, 0);
            psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumn: [%s]\n", ptr );

            if (strcmp(ptr, STR_1)) {
                psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected string output!\n" );
                failed = 1;
            } else {
                psLogMsg( __func__, PS_LOG_INFO, "found expected string output.\n" );
            }

            psFree(ary);
        }

        ary = psDBSelectColumn(dbh, table, "key_bool", 0);
        if (ary == NULL) {
            failed = 1;
        } else {
            ptr = psArrayGet(ary, 2);
            psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumn: [%s]\n", ptr );

            ptr = psArrayGet(ary, 1);
            psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumn: [%s]\n", ptr );

            ptr = psArrayGet(ary, 0);
            psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumn: [%s]\n", ptr );

            if (atoi(ptr) != B_1) {
                psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output [%d] != [%d]!\n",
                          atoi(ptr), B_1 );
                failed = 1;
            } else {
                psLogMsg( __func__, PS_LOG_INFO, "found expected string output.\n" );
            }

            psFree(ary);
        }

        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBSelectColumnNum shall write/select a column from a test table ...
psS32 TPDBSelectColumnNum( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table4";
    psVector *vec = NULL;
    psMetadata *row=NULL, *md = _get_CreateTableMetadata();

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBSelectColumnNum shall select data from a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {

        row = _get_const_row1();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_const_row2();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_row();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        vec = psDBSelectColumnNum(dbh, table, "key_s32", PS_TYPE_S32, 0);
        if (vec == NULL) {
            failed = 1;
        } else {
            psLogMsg( __func__, PS_LOG_INFO, "str=[%d]\n", (vec->data.S32)[2] );
            psLogMsg( __func__, PS_LOG_INFO, "str=[%d]\n", (vec->data.S32)[1] );
            psLogMsg( __func__, PS_LOG_INFO, "str=[%d]\n", (vec->data.S32)[0] );
            if ((vec->data.S32)[0] != S32_1) {
                psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected S32 output!\n" );
                failed = 1;
            } else {
                psLogMsg( __func__, PS_LOG_INFO, "found expected S32 output.\n" );
            }
            psFree(vec);
        }

        vec = psDBSelectColumnNum(dbh, table, "key_bool", PS_TYPE_BOOL, 0);
        if (vec == NULL) {
            failed = 1;
        } else {
            psLogMsg( __func__, PS_LOG_INFO, "bool=[%d]\n", (vec->data.U8)[2] );
            psLogMsg( __func__, PS_LOG_INFO, "bool=[%d]\n", (vec->data.U8)[1] );
            psLogMsg( __func__, PS_LOG_INFO, "bool=[%d]\n", (vec->data.U8)[0] );
            if ((vec->data.U8)[0] != B_1) {
                psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected bool output!\n" );
                failed = 1;
            } else {
                psLogMsg( __func__, PS_LOG_INFO, "found expected bool output.\n" );
            }
            psFree(vec);
        }

        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBSelectRows shall write/select data from a test table ...
psS32 TPDBSelectRows( void )
{
    int i;
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table5";
    psArray *ary=NULL, *ary1=NULL, *ary2=NULL;
    psMetadata *where=NULL, *row=NULL, *md = _get_CreateTableMetadata();
    psMetadataItem *item = NULL;

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBSelectRows shall select data from a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {

        row = _get_const_row1();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_const_row2();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_row();
        failed = ! psDBInsertOneRow(dbh, table, row);
        psFree(row);

        if (!failed) {
            where = _get_where();
            ary1 = psDBSelectRows(dbh, table, where, 0);
            ary2 = psDBSelectRows(dbh, table, where, 1); /* specify limit */
            psFree(where);
            for (i=0; i<2; i++) {
                ary = i ? ary2 : ary1;
                if (ary == NULL) {
                    failed = 1;
                } else {
                    row = (psMetadata*)psArrayGet(ary, 0);
                    _print_row(row);

                    item = psMetadataLookup(row, "key_s32");
                    if (item == NULL) {
                        psLogMsg( __func__, PS_LOG_INFO, "oops, key_s32 not found!\n" );
                        failed = 1;
                    } else if (item->data.S32 != S32_1) {
                        psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output (%d vs %d)!\n",
                                  item->data.S32,S32_1);
                        failed = 1;
                    } else {
                        psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
                    }

                    item = psMetadataLookup(row, "key_bool");
                    if (item == NULL) {
                        psLogMsg( __func__, PS_LOG_INFO, "oops, key_bool not found!\n" );
                        failed = 1;
                    } else if (item->data.B != B_1) {
                        psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output (%d vs %d)!\n",
                                  item->data.B,B_1);
                        failed = 1;
                    } else {
                        psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
                    }

                    psFree(ary);
                }
            }
        }
        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBInsertOneRow shall write a row of data into a test table ...
psS32 TPDBInsertOneRow( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table6";
    psMetadata *row=NULL, *md = _get_CreateTableMetadata();

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBInsertOneRow shall insert a row into a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {
        row = _get_const_row1();
        failed = ! psDBInsertOneRow(dbh, table, row);
        psFree(row);
        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBInsertRows shall write rows of data into a test table ...
psS32 TPDBInsertRows( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table7";
    psArray *rowSet = NULL;
    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
    psMetadataItem *item=NULL;

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBInsertRows shall insert rows into a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {
        rowSet = psArrayAlloc(3);
        rowSet->n = 0;

        row = _get_const_row1();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        row = _get_const_row2();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        row = _get_row();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        failed = ! psDBInsertRows(dbh, table, rowSet);
        psFree(rowSet);

        // extra checks ...
        rowSet = psDBDumpRows(dbh, table);
        if (rowSet == NULL) {
            failed = 1;
        } else {
            _print_row((psMetadata*)psArrayGet(rowSet, 2));
            _print_row((psMetadata*)psArrayGet(rowSet, 1));
            row = (psMetadata*)psArrayGet(rowSet, 0);
            _print_row(row);

            item = psMetadataLookup(row, "key_s32");
            if ((item == NULL) || (item->data.S32 != S32_1)) {
                psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output!\n" );
                failed = 1;
            } else {
                psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
            }

            item = psMetadataLookup(row, "key_bool");
            if ((item == NULL) || (item->data.B != B_1)) {
                psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output!\n" );
                failed = 1;
            } else {
                psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
            }

            psFree(rowSet);
        }

        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBDumpRows shall dump all rows from a test table ...
psS32 TPDBDumpRows( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table8";
    psArray *ary = NULL;
    psMetadata *row=NULL, *md = _get_CreateTableMetadata();
    psMetadataItem *item=NULL;

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBDumpRows shall dump all rows from a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {

        row = _get_const_row1();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_const_row2();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_row();
        failed = ! psDBInsertOneRow(dbh, table, row);
        psFree(row);

        if (!failed) {
            ary = psDBDumpRows(dbh, table);
            if (ary == NULL) {
                failed = 1;
            } else {

                _print_row((psMetadata*)psArrayGet(ary, 2));
                _print_row((psMetadata*)psArrayGet(ary, 1));
                row = (psMetadata*)psArrayGet(ary, 0);
                _print_row(row);

                item = psMetadataLookup(row, "key_s32");
                if ((item == NULL) || (item->data.S32 != S32_1)) {
                    psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output!\n" );
                    failed = 1;
                } else {
                    psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
                }

                item = psMetadataLookup(row, "key_bool");
                if ((item == NULL) || (item->data.B != B_1)) {
                    psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output!\n" );
                    failed = 1;
                } else {
                    psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
                }

                psFree(ary);
            }
        }

        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBDumpCols shall dump all cols from a test table ...
psS32 TPDBDumpCols( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table9";
    psMetadata *row=NULL, *meta=NULL, *md = _get_CreateTableMetadata();

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBDumpCols shall dump all cols from a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {

        row = _get_const_row1();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_const_row2();
        psDBInsertOneRow(dbh, table, row);
        psFree(row);

        row = _get_row();
        failed = ! psDBInsertOneRow(dbh, table, row);
        psFree(row);

        meta = psDBDumpCols(dbh, table);
        if (meta == NULL) {
            failed = 1;
        } else {
            _print_row(meta);
            psFree(meta);
        }
        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBUpdateRows shall update rows in a test table ...
psS32 TPDBUpdateRows( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table10";
    psArray *ary=NULL, *rowSet=NULL;
    psMetadata *row=NULL, *where=NULL, *updates=NULL, *md = _get_CreateTableMetadata();
    psMetadataItem *item=NULL;

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBUpdateRows shall update rows in a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {
        rowSet = psArrayAlloc(3);
        rowSet->n = 0;

        row = _get_const_row1();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        row = _get_const_row2();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        row = _get_row();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        failed = ! psDBInsertRows(dbh, table, rowSet);
        if (!failed) {
            where = _get_where();
            updates = _get_update_values(STR_2, S32_2, F32_2, F64_2, B_2);
            failed = (psDBUpdateRows(dbh, table, where, updates) < 0);

            ary = psDBDumpRows(dbh, table);
            if (ary == NULL) {
                failed = 1;
            } else {

                row = (psMetadata*)psArrayGet(ary, 0);
                _print_row(row);

                item = psMetadataLookup(row, "key_s32");
                if ((item == NULL) || (item->data.S32 != S32_2)) {
                    psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output!\n" );
                    failed = 1;
                } else {
                    psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
                }

                item = psMetadataLookup(row, "key_bool");
                if ((item == NULL) || (item->data.B != B_2)) {
                    psLogMsg( __func__, PS_LOG_INFO, "oops, did not find expected output!\n" );
                    failed = 1;
                } else {
                    psLogMsg( __func__, PS_LOG_INFO, "found expected output.\n" );
                }

                psFree(ary);
            }

            psFree(updates);
            psFree(where);
        }
        psFree(rowSet);
        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, TPDBDeleteRows shall update rows in a test table ...
psS32 TPDBDeleteRows( void )
{
    psS32 failed = 0;
    psDB *dbh = NULL;
    const char* table = "table11";
    psArray *ary=NULL, *rowSet=NULL;
    psMetadata *where=NULL, *row=NULL, *md = _get_CreateTableMetadata();

    dbh = _init_psDB();
    if (dbh == NULL) {
        return 1;
    }

    psLogMsg( __func__, PS_LOG_INFO, "psDBDeleteRows shall delete rows from a test table.\n" );

    failed = ! psDBCreateTable(dbh, table, md);
    if (!failed) {
        rowSet = psArrayAlloc(3);
        rowSet->n = 0;

        row = _get_const_row1();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        row = _get_const_row2();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        row = _get_row();
        psArrayAdd(rowSet, 0, row);
        psFree(row);

        failed = ! psDBInsertRows(dbh, table, rowSet);
        if (!failed) {
            where = _get_where();
            failed = (psDBDeleteRows(dbh, table, where) < 0);

            ary = psDBDumpRows(dbh, table);
            if (ary == NULL) {
                failed = 1;
            } else {
                if (ary->n != 2) {
                    psLogMsg( __func__, PS_LOG_INFO, "oops, did not delete stuff!\n" );
                    failed = 1;
                } else {
                    psLogMsg( __func__, PS_LOG_INFO, "found expected two-row table.\n" );
                }

                psFree(ary);
            }

            psFree(where);
        }
        psFree(rowSet);
        psDBDropTable(dbh, table);
    }

    psFree(md);
    psDBCleanup(dbh);

    return failed;
}

// Testpoint #XXX, psDBCreate shall create a new test database.
//psS32 TPDBCreate( void )
//{
//    const char *test_db = "ps_test_db";
//    psS32 failed = 0;
//    psDB *dbh = NULL;
//
//    dbh = _init_psDB();
//    if (dbh == NULL) {
//        return 1;
//    }
//
//    psLogMsg( __func__, PS_LOG_INFO, "psDBCreate shall create new new test database.\n" );
//
//    failed = ! psDBCreate(dbh, test_db);
//    if (!failed) {
//        psDBDrop(dbh, test_db);
//    }
//
//    psDBCleanup(dbh);
//
//    return failed;
//}

// Testpoint #XXX, psDBDrop shall create a new test database.
//psS32 TPDBDrop( void )
//{
//    const char *test_db = "ps_test_db";
//    psS32 failed = 0;
//    psDB *dbh = NULL;
//
//    dbh = _init_psDB();
//    if (dbh == NULL) {
//        return 1;
//    }
//
//    psLogMsg( __func__, PS_LOG_INFO, "psDBDrop shall create/drop a new new test database.\n" );
//
//    failed = psDBCreate(dbh, test_db);
//    if (!failed) {
//        failed = ! psDBDrop(dbh, test_db);
//    }
//
//    psDBCleanup(dbh);
//
//    return failed;
//}

