/*
 * © Copyright 1996-2012 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "mars.h"
#if defined(sun) || defined(sgi) || defined(hpux)
#define EMPRESS
#endif

#ifdef EMPRESS
#define UN_FIRST_GUESS


#include "mcs.h" /* includes <mscc.h> which includes <stdio.h> */

#include <ctype.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>


/* db prototypes */

static void mcs_init(void);
static err mcs_open(void* data, request* r, request* e, int mode);
static err mcs_close(void* data);
static err mcs_read(void* data, request* r, void* buffer, long* length);
static err mcs_write(void* data, request* r, void* buffer, long* length);
static err mcs_query(void* data, request* r, long* length);
static boolean mcs_check(void* data, request* r);


/* local prototypes */

static int build_attr_list(mcs_private* priv);
static err read_or_query(void* data, request* r, void* buffer, long* length, boolean get_data);
static int qual_attr(request* r, char* parname, addr attr, addr* qual);
static int new_table(mcs_private* priv);
static int check_dupl(mcs_private* priv, request* r);
static int open_table(mcs_private* priv, char mode);
static boolean compare_inodes(char* p1, char* p2);


/* fill some structures: */

static option mcs_option[] = {
    {
        "dbname",                    /* name */
        "MARS_CACHING_SYSTEM",       /* env-var, if not: NULL */
        "-dbase",                    /* cmd line option */
        MCS_DBASE,                   /* default value */
        t_str,                       /* type */
        sizeof(char*),               /* size */
        OFFSET(mcs_private, dbname), /* position in priv */
    },
    {
        "sql",                    /* name */
        NULL,                     /* env-var, if not: NULL */
        "-sql",                   /* cmd line option */
        MCS_SQL,                  /* default value */
        t_str,                    /* type */
        sizeof(char*),            /* size */
        OFFSET(mcs_private, sql), /* position in priv */
    },
    {
        "chk",                    /* name */
        NULL,                     /* env-var, if not: NULL */
        "-chk",                   /* cmd line option */
        MCS_CHK,                  /* default value */
        t_str,                    /* type */
        sizeof(char*),            /* size */
        OFFSET(mcs_private, chk), /* position in priv */
    },
    {
        "maxtry",                    /* name */
        "MSLOCKRETRY",               /* env-var, if not: NULL */
        NULL,                        /* cmd line option */
        "120",                       /* default value */
        t_int,                       /* type */
        sizeof(int),                 /* size */
        OFFSET(mcs_private, maxtry), /* position in priv */
    },
    {
        "sleeptime",                    /* name */
        "MSLOCKSLEEP",                  /* env-var, if not: NULL */
        NULL,                           /* cmd line option */
        "5",                            /* default value */
        t_int,                          /* type */
        sizeof(int),                    /* size */
        OFFSET(mcs_private, sleeptime), /* position in priv */
    },
    {
        "compress",                    /* name */
        "MCS_COMPRESS",                /* env-var, if not: NULL */
        "-compress",                   /* cmd line option */
        "false",                       /* default value */
        t_boolean,                     /* type */
        sizeof(boolean),               /* size */
        OFFSET(mcs_private, compress), /* position in priv */
    },
};


static base_class _mcs_base = {

    NULL,       /* parent class */
    "mcs_base", /* name         */

    false, /* inited       */

    sizeof(mcs_private),

    NUMBER(mcs_option),
    mcs_option,

    mcs_init, /* init         */

    mcs_open,  /* open         */
    mcs_close, /* close        */

    mcs_read,  /* read         */
    mcs_write, /* write        */

    NULL, /* control      */

    mcs_check, /* check */
    mcs_query  /* query        */

};


/* the only 'public' variable ... */

base_class* mcs_base = &_mcs_base;


/* administrative attributes which are the same in all tables */
/* not used int retrievals */

static attribute priv_attr[] = {
    {
        NULL,    /* next */
        "_data", /* name */
        ADDRNIL, /* attribute descriptor */
        false,   /* qualification flag */
    },
    {
        NULL,         /* next */
        "_data_size", /* name */
        ADDRNIL,      /* attribute descriptor */
        false,        /* qualification flag */
    },
    {
        NULL,         /* next */
        "_bulk_size", /* name */
        ADDRNIL,      /* attribute descriptor */
        false,        /* qualification flag */
    },
    {
        NULL,     /* next */
        "_owner", /* name */
        ADDRNIL,  /* attribute descriptor */
        false,    /* qualification flag */
    },
    {
        NULL,           /* next */
        "_last_access", /* name */
        ADDRNIL,        /* attribute descriptor */
        false,          /* qualification flag */
    },
    {
        NULL,           /* next */
        "_access_numb", /* name */
        ADDRNIL,        /* attribute descriptor */
        false,          /* qualification flag */
    },
    {
        NULL,        /* next */
        "_compress", /* name */
        ADDRNIL,     /* attribute descriptor */
        false,       /* qualification flag */
    },
};


static void mcs_exit(int code, void* data) {

    /* uninitialize empress */

    msexit(code);
}

static void mcs_init(void) {


    marslog(LOG_DBUG, "mcs_init:Initialize empress");


    /* initialize empress */

    msinit();

    /* switch use of temporary file for bulk data off */

    msdtfsta(true);

    /* install exitproc */

    install_exit_proc(mcs_exit, NULL);
}


static err mcs_open(void* data, request* r, request* e, int mode) {


    mcs_private* priv = (mcs_private*)data; /* get private info */


    marslog(LOG_DBUG, "mcs_open:Database is '%s'", priv->dbname);

    /* set some parameters in priv */

    priv->_date_cnt     = 0;
    priv->_date         = ADDRNIL;
    priv->_tab_name     = ADDRNIL;
    priv->_tab          = ADDRNIL;
    priv->_rec          = ADDRNIL;
    priv->_new_rec      = ADDRNIL;
    priv->_qual         = ADDRNIL;
    priv->_qual_is_null = true;

    priv->_first = (attribute*)NULL;

    priv->_dim_priv_attr = NUMBER(priv_attr);

    /* put request in private */

#ifdef UN_FIRST_GUESS
    priv->_r = un_first_guess(r);
#else
    priv->_r = r;
#endif

    if (mars.debug)
        print_one_request(priv->_r);

    /* set type */
    priv->_type = get_value(r, "TYPE", 0);

    /* build sql name */

    sprintf(priv->_sql, "%s/%s", priv->sql, priv->_type);

    /* create attribute list */

    marslog(LOG_DBUG, "mcs_open:Create attribute list");
    if (build_attr_list(priv))
        return ATTRLIST_ERR;


    marslog(LOG_DBUG, "mcs_open:Return:No error");
    marslock(priv->dbname, true);

    return NOERR;
}


static err mcs_close(void* data) {


    int i;
    char* p;
    attribute* a;

    mcs_private* priv = (mcs_private*)data; /* get private info */


    marslog(LOG_DBUG, "mcs_close:Close db '%s'", priv->dbname);


    /* clean up */

    marslog(LOG_DBUG, "mcs_close:Clean up and close table");

    if (priv->_tab_name != ADDRNIL) { /* table name */
        free(priv->_tab_name);
        priv->_tab_name = ADDRNIL;
    }

    if (priv->_date != ADDRNIL) /* date */
        priv->_date = ADDRNIL;

    if (priv->_rec != ADDRNIL) { /* record descriptor */
        mrfrrec(priv->_rec);
        priv->_rec = ADDRNIL;
    }


    if (priv->_new_rec != ADDRNIL) { /* new record descriptor */
        mrfrrec(priv->_new_rec);
        priv->_new_rec = ADDRNIL;
    }


    if (priv->_tab != ADDRNIL) { /* table descriptor */
        mrclose(priv->_tab);
        priv->_tab = ADDRNIL;
    }

#ifdef UN_FIRST_GUESS
    if (priv->_r != NULL)
        free_all_requests(priv->_r);
#endif

    /* free attribute list */

    marslog(LOG_DBUG, "mcs_close:Free attribute list");

    while (priv->_first) {
        free(priv->_first->name);
        a            = priv->_first;
        priv->_first = priv->_first->next;
        free(a);
    }


    /* free rules */

    free_rule(priv->_rules);

    marslog(LOG_DBUG, "mcs_close:Return:No error");
    marsunlock(priv->dbname);

    return NOERR;
}


static err mcs_query(void* data, request* r, long* length) {

    marslog(LOG_DBUG, "mcs_query:Call read_or_query");

    return read_or_query(data, r, (void*)NULL, length, false);
}

static err mcs_read(void* data, request* r, void* buffer, long* length) {

    marslog(LOG_DBUG, "mcs_read:Call read_or_query");

    return read_or_query(data, r, buffer, length, true);
}

static err read_or_query(void* data, request* r, void* buffer, long* length, boolean get_data) {

    int i;
    long l;
    int getflag;
    char* b;
    boolean compress_flag;
    boolean iscomp;
    attribute* a;

    mcs_private* priv = (mcs_private*)data; /* get private stuff */


    marslog(LOG_DBUG, "mcs:read_or_query:At the begin");

    if (priv->_tab == NULL) {
        priv->_date = get_value(priv->_r, "DATE", priv->_date_cnt);
        if (open_table(priv, 'r'))
            return OPENTABLE_ERR;
    }

    for (;;) {

        if (priv->_qual_is_null) {

            /* build qualification */

            marslog(LOG_DBUG, "mcs:read_or_query:Build qualification");

            priv->_qual = ADDRNIL;

            for (a = priv->_first; a; a = a->next)
                /* if(a->qual_flag) */
                if (qual_attr(priv->_r, a->name, a->emp_desc, &priv->_qual))
                    return QUALBUILD_ERR;

            /* initialize retrieval */

            marslog(LOG_DBUG, "mcs:read_or_query:Initialize qualification");

            priv->_ret = mrtgtbegin(priv->_qual, priv->_rec, ADDRNIL);

            if (priv->_ret == ADDRNIL) {
                marslog(LOG_WARN, "mcs:read_or_query:EMPRESS:'%s'", mrerrmsg());
                return QUALBUILD_ERR;
            }

            priv->_qual_is_null = false;
        }

        getflag = mrtget(priv->_ret);

        marslog(LOG_DBUG, "mcs:read_or_query:Get record with returncode:'%d'", getflag);

        if (getflag == -1) {

            marslog(LOG_DBUG, "mcs:read_or_query:Try to get locked record");

            /* try again */

            for (i = 0; i < priv->maxtry; i++) {

                sleep(priv->sleeptime);
                getflag = mrreget(priv->_ret);

                if (getflag == 1)
                    break;
            }

            if (getflag != 1) {
                mrgetend(priv->_ret);
                marslog(LOG_WARN, "mcs:read_or_query:Failed to get locked record");
                return LOCK_ERR;
            }
        }

        if (getflag == 1) {

            /* get size */

            l = *length;

            /* if request wants data in compressed form set flag */
            iscomp = (count_values(r, "_COMPRESS")) ? true : false;

            *length = strtol(mrgetvs(priv->_rec, priv_attr[((iscomp) ? 2 : 1)].emp_desc), (char**)NULL, 10);

            if (get_data) {

                marslog(LOG_DBUG, "mcs:read_or_query:Try to get bulk data");

                if (l < *length) {

                    mrgetend(priv->_ret);
                    marslog(LOG_WARN, "mcs:read_or_query:Buffer for bulk data is too small");
                    return BUF_TO_SMALL;
                }

                /* get compress flag */

                compress_flag = (*(mrgetvs(priv->_rec, priv_attr[6].emp_desc)) == 't') ? true : false;

                if (iscomp)
                    compress_flag = false;

                /* get data */

                b = mrgeti(priv->_rec, priv_attr[0].emp_desc);


                if (compress_flag) {

                    marslog(LOG_DBUG, "mcs:read_or_query:Uncompress bulk data");

                    if (uncompress(b + (sizeof(long)), buffer, *(long*)b, &l)) {
                        mrgetend(priv->_ret);
                        marslog(LOG_WARN, "mcs:read_or_query:Uncompressing bulk data failed");
                        return COMPRESS_ERR;
                    }

                    if (*length != l)
                        marslog(LOG_WARN, "mcs:read_or_query:Original size is not uncompressed size");
                }
                else
                    memcpy((char*)buffer, b + (sizeof(long)), *length);
            }

            /* fill in date in request */

            marslog(LOG_DBUG, "mcs:read_or_query:Fill values in request");

            set_value(r, "DATE", priv->_date);

            /* get other values and fill request */

            for (a = priv->_first; a; a = a->next)
                if (*(b = mrgetvs(priv->_rec, a->emp_desc)))
                    set_value(r, a->name, b);

            for (i = 1; i < priv->_dim_priv_attr; i++)
                if (*(b = mrgetvs(priv->_rec, priv_attr[i].emp_desc)))
                    set_value(r, priv_attr[i].name, b);


            /* update _last_access, _access_numb */

            marslog(LOG_DBUG, "mcs:read_or_query:Update _last_access");

            mrcopyr(priv->_new_rec, priv->_rec);
            mrputvs(priv->_new_rec, priv_attr[4].emp_desc, "NOW");

            marslog(LOG_DBUG, "mcs:read_or_query:Update _access_numb");

            mrputvi(priv->_new_rec, priv_attr[5].emp_desc,
                    mrgetvi(priv->_rec, priv_attr[5].emp_desc) + 1);

            if (!mrtput(priv->_new_rec, priv->_rec)) {
                marslog(LOG_DBUG, "mcs:read_or_query:EMPRESS:'%s'", mrerrmsg());
                return UPDATE_ERR;
            }

            marslog(LOG_DBUG, "mcs:read_or_query:Return:No error");

            return NOERR;
        }
        else if (getflag == 0) {

            marslog(LOG_DBUG, "mcs:read_or_query:No more records for this date");

            mrgetend(priv->_ret);

            priv->_date_cnt++;

            priv->_date = get_value(priv->_r, "DATE", priv->_date_cnt);

            if (priv->_date == NULL) {
                marslog(LOG_DBUG, "mcs:read_or_query:No more records.Return:No error");
                return EOF;
            }

            /* open table for reading */

            marslog(LOG_DBUG,
                    "mcs:read_or_query:No more records for this date.Open new table '%s%s'", priv->_tab_name, priv->_date);

            if (open_table(priv, 'r'))
                return OPENTABLE_ERR;

            priv->_qual_is_null = true;

            continue;
        }
    }
}


static err mcs_write(void* data, request* r, void* buffer, long* length) {


    const char* p;
    char* emp_compress_flag = "f";
    emp_bulk* blob;
    attribute* a;
    boolean another_type = false;
    char next_sql[MCS_MAX_LINE_CHAR];

    mcs_private* priv = (mcs_private*)data; /* get private stuff */


    marslog(LOG_DBUG, "mcs_write:Size of bulk is '%d'", *length);
    if (mars.debug)
        print_one_request(r);

    if (priv->_r == NULL) {
#ifdef UN_FIRST_GUESS
        priv->_r = un_first_guess(r);
#else
        priv->_r = r;
#endif

        if (mars.debug)
            print_one_request(priv->_r);

        /* set type */

        priv->_type = get_value(r, "TYPE", 0);

        /* build sql name */

        sprintf(priv->_sql, "%s/%s", priv->sql, priv->_type);

        /* create attribute list */

        marslog(LOG_DBUG, "mcs_open:Create attribute list");
        build_attr_list(priv);
        if (priv->_r == NULL) {
            marslog(LOG_WARN | LOG_PERR, "mcs_write:No request");
            return ATTRLIST_ERR;
        }
    }


    /* check type */

    if (!(p = get_value(r, "TYPE", 0))) {
        marslog(LOG_DBUG, "mcs_write:No more type's in request.Return:No error");
        return EOF;
    }

    if (priv->_type != p) {

        another_type = true;

        /* compare inodes of sql scripts */

        strcpy(next_sql, priv->sql);
        sprintf(priv->_sql, "%s/%s", priv->sql, p);

        if (!compare_inodes(priv->_sql, next_sql)) {

            /* free attribute list */

            while (priv->_first) {
                free(priv->_first->name);
                a            = priv->_first;
                priv->_first = priv->_first->next;
                free(a);
            }

            /* create attribute list */

            marslog(LOG_DBUG, "mcs_open:Create attribute list");
            build_attr_list(priv);
            if (priv->_r == NULL) {
                marslog(LOG_WARN | LOG_PERR, "mcs_write:No request");
                return ATTRLIST_ERR;
            }
        }

        priv->_type = p;
    }

    /* check date */

    if (!(p = get_value(r, "DATE", 0))) {
        marslog(LOG_DBUG, "mcs_write:No more date's in request.Return:No error");
        return EOF;
    }

    if (priv->_date != p || another_type) {

        priv->_date = p;

        /* open table for reading and writing */

        marslog(LOG_DBUG, "mcs_write:New date in request:'%s'.Try to open new table", priv->_date);

        if (open_table(priv, 'w'))
            return OPENTABLE_ERR;
    }


    if (check_dupl(priv, r)) {
        /* marslog(LOG_WARN,"mcs_write:Duplicate record"); */
        return DUPLICATE_ERR;
    }


    /* fill empress attributes */

    marslog(LOG_DBUG, "mcs_write:Fill empress attributes");

    for (a = priv->_first; a; a = a->next) {
        if ((p = get_value(r, a->name, 0))) {
            if (!mrputvs(priv->_rec, a->emp_desc, (char*)p)) {
                marslog(LOG_WARN, "mcs_write:Convert '%s': '%s'", a->name, p);
                print_one_request(r);
                return CONVERT_ERR;
            }
        }
        else
            mrsetnv(priv->_rec, a->emp_desc);
    }


    /* fill private empress attributes */

    marslog(LOG_DBUG, "mcs_write:Fill private empress attributes");

    /* data */

    marslog(LOG_DBUG, "mcs_write:Allocate '%ld'+sizeof(long) bytes", *length);

    blob = (emp_bulk*)MALLOC(*length + sizeof(long));

    blob->len = *length;

    if (priv->compress || priv->_compress) {

        marslog(LOG_DBUG, "mcs_write:Compress bulk data");

        if (compress(buffer, &blob->buf, *length, &(blob->len))) {
            if (blob->len >= *length) {
                marslog(LOG_WARN, "could not compress bulk data");
                memcpy((char*)&blob->buf, (char*)buffer, blob->len);
            }
            else {
                marslog(LOG_WARN, "Compress bulk data failed");
                FREE(blob);
                return COMPRESS_ERR;
            }
        }
    }
    else
        memcpy((char*)&blob->buf, (char*)buffer, blob->len);

    *emp_compress_flag = (blob->len < *length) ? 't' : 'f';


    /* data_size (before compressing) */

    if (!mrputvi(priv->_rec, priv_attr[1].emp_desc, *length)) {
        marslog(LOG_WARN, "mcs_write:Convert '%s':'%ld'", priv_attr[1].name, *length);
        FREE(blob);
        return CONVERT_ERR;
    }

    /* bulk_size */

    if (!mrputvi(priv->_rec, priv_attr[2].emp_desc, blob->len)) {
        marslog(LOG_WARN, "mcs_write:Convert'%s':'%ld'", priv_attr[2].name, blob->len);
        FREE(blob);
        return CONVERT_ERR;
    }

    /* owner */
    if ((p = get_value(r, priv_attr[3].name, 0))) {
        if (!mrputvs(priv->_rec, priv_attr[3].emp_desc, (char*)p)) {
            marslog(LOG_WARN, "mcs_write:Convert '%s': '%s'", priv_attr[3].name, p);
            FREE(blob);
            return CONVERT_ERR;
        }
    }
    else
        mrsetnv(priv->_rec, priv_attr[3].emp_desc);

    /* access date */

    mrputvs(priv->_rec, priv_attr[4].emp_desc, "NOW");

    /* access number */

    mrputvi(priv->_rec, priv_attr[5].emp_desc, 1);

    /* compress flag */

    mrputvs(priv->_rec, priv_attr[6].emp_desc, emp_compress_flag);


    /* data */

    if (!mrputi(priv->_rec, priv_attr[0].emp_desc, (addr)blob)) {
        marslog(LOG_WARN, "mcs_write:Writing bulk data failed");
        FREE(blob);
        return WRITEBLOB_ERR;
    }

    marslog(LOG_DBUG, "mcs_write:Add record");

    if (!mrtadd(priv->_rec)) {
        marslog(LOG_WARN, "mcs_write:EMPRESS:'%s'", mrerrmsg());
        FREE(blob);
        return ADD_ERR;
    }

    mraddend(priv->_rec);

    FREE(blob);

    marslog(LOG_DBUG, "mcs_write:Return:No error");

    return NOERR;
}


static boolean mcs_check(void* data, request* r) {

    char* s;
    attribute* a;

    mcs_private* priv = (mcs_private*)data; /* get private stuff */

    if (!priv->_rules)
        priv->_rules = read_check_file(priv->chk);

    if (!priv->_rules)
        return false;

    return check_one_request(priv->_rules, r);


#ifdef _NOT_DEF

    if (s = get_value(r, "STREAM", 0))
        if (strcmp(s, "OPER") != 0) {
            marslog(LOG_DBUG, "mcs_check:STREAM != OPER");
            return false;
        }

    if (s = get_value(r, "CLASS", 0))
        if (strcmp(s, "OD") != 0) {
            marslog(LOG_DBUG, "mcs_check:CLASS != OD");
            return false;
        }


    for (a = priv->_first; a; a = a->next)
        if (!count_values(r, a->name)) {
            marslog(LOG_DBUG, "mcs_check:'%s' not set", a->name);
            return false;
        }


    return true;
#endif
}


static int qual_attr(request* r, char* parname, addr attr, addr* qual) {

    int i, maxi;
    addr c;
    addr q      = ADDRNIL;
    char* op    = NULL;
    char* equal = "=";


    if (!(maxi = count_values(r, parname)))
        return 0;

    for (i = 0; i < maxi; i++) {
        if (!(c = mrcvt(attr, (char*)get_value(r, parname, i)))) {
            marslog(LOG_WARN, "mcs:qual_attr:Convert'%s': '%s'", parname, get_value(r, parname, i));
            print_one_request(r);
            return 1;
        }
        /* if(!(op=get_operator(r, parname, i))) */
        op = equal;

        q = (i == 0) ? mrqcon(op, attr, c) : mrqor(q, mrqcon(op, attr, c));
    }

    if (*qual == ADDRNIL)
        *qual = q;
    else if (q != ADDRNIL)
        *qual = mrqand(*qual, q);

    return 0;
}


static int new_table(mcs_private* priv) {


    int c;
    FILE *sqlf, *mcsp;
    static boolean first = true;
    static char cmd[MCS_MAX_LINE_CHAR];
    char* p = NULL;


    if (first) {
        if (!(p = getenv("MSPATH")))
            marslog(LOG_WARN | LOG_PERR, "mcs:new_table:MSPATH not set.Use:'%s'", MCS_DEFAULT_MSPATH);
        sprintf(cmd, "%s/%s %s", (p) ? p : MCS_DEFAULT_MSPATH, "bin/empbatch", priv->dbname);
    }


    /* open scriptfile for reading */

    if (!(sqlf = fopen(priv->_sql, "r"))) {
        marslog(LOG_WARN | LOG_PERR, "mcs:new_table:open sql script '%s'", priv->_sql);
        return 1;
    }

    /* open pipe for writing*/

    if (!(mcsp = popen(cmd, "w"))) {
        marslog(LOG_WARN | LOG_PERR, "mcs:new_table:open pipe: '%s'", cmd);
        return 1;
    }

    if (MCS_USE_TRANSACTION_AT_TABLECREATION)
        fputs("start work;\n", mcsp);

    /* find replacement pattern ##[##..] */

    while ((c = fgetc(sqlf)) != EOF) {
        if (c == '#')
            if ((c = fgetc(sqlf)) == '#') {
                fputs(priv->_date, mcsp);

                while ((c = fgetc(sqlf)) == '#')
                    ;
                if (c == EOF)
                    break;
            }
            else {
                fputc('#', mcsp);
                if (c == EOF)
                    break;
            }
        fputc(c, mcsp);
    }

    if (MCS_USE_TRANSACTION_AT_TABLECREATION)
        fputs("\ncommit work;", mcsp);

    fputs("\nstop;\n", mcsp);

    pclose(mcsp);
    fclose(sqlf);

    marslog(LOG_DBUG, "mcs:new_table:'%s%s' created", priv->_tab_name, priv->_date);

    return 0;
}


static int open_table(mcs_private* priv, char mode) {

    int i;
    char tn[MCS_TAB_CHAR];
    attribute* a;

    if (priv->_date == NULL) {
        marslog(LOG_EROR, "mcs:not date in request");
        return 1;
    }

    sprintf(tn, "%s%s", priv->_tab_name, priv->_date);

    marslog(LOG_DBUG, "mcs:open_table:Open '%s'", tn);

    if (priv->_tab != ADDRNIL) {

        mrfrrec(priv->_rec);
        priv->_rec = ADDRNIL;
        mrfrrec(priv->_new_rec);
        priv->_new_rec = ADDRNIL;
        mrclose(priv->_tab);
        priv->_tab = ADDRNIL;
    }


    if (!(priv->_tab = mrtopen(priv->dbname, tn, 'u'))) {

        /* marslog(LOG_WARN,"mcs:open_table:EMPRESS:'%s'",mrerrmsg()); */

        if (mode == 'r')
            return 1;

        marslog(LOG_DBUG, "mcs:open_table:Try to create new table %s", tn);

        if (new_table(priv))
            marslog(LOG_WARN, "mcs:open_table:'%s' could not be created", tn);

        if (!(priv->_tab = mrtopen(priv->dbname, tn, 'u'))) {
            marslog(LOG_WARN, "mcs:open_table:EMPRESS:'%s'.RETURN", mrerrmsg());
            return 1;
        }
        marslog(LOG_DBUG, "mcs:open_table:Created new table:'%s'", tn);
    }

    marslog(LOG_DBUG, "mcs:open_table:Opened table '%s'", tn);


    /* get record descriptor */

    marslog(LOG_DBUG, "mcs:open_table:Get record descriptor");
    priv->_rec     = mrmkrec(priv->_tab);
    priv->_new_rec = mrmkrec(priv->_tab);


    /* get attribute descriptors */

    marslog(LOG_DBUG, "mcs:open_table:Get attribute descriptor");

    /* for(a=priv->_first; a; a=a->next) */
    /* marslog(LOG_DBUG,"attribute : '%s'",a->name); */

    for (a = priv->_first; a; a = a->next)
        if (!(a->emp_desc = mrngeta(priv->_tab, a->name))) {
            marslog(LOG_WARN, "mcs:open_table:No attribute descriptor '%s'",
                    a->name);
            return 1;
        }

    for (i = 0; i < priv->_dim_priv_attr; i++)
        if (!(priv_attr[i].emp_desc = mrngeta(priv->_tab, priv_attr[i].name))) {
            marslog(LOG_WARN, "mcs:open_table:No attribute descriptor '%s'",
                    priv_attr[i].name);
            return 1;
        }

    return 0;
}


static int check_dupl(mcs_private* priv, request* r) {

    int i = 0;
    int getflag;
    boolean disposition = false;
    char* update;
    attribute* a;


    /* build qualification */

    priv->_qual = ADDRNIL;

    for (a = priv->_first; a; a = a->next)
        if (qual_attr(r, a->name, a->emp_desc, &(priv->_qual)))
            return 1;


    /* check for DISP=OLD */

    if (count_values(r, "DISP") && strcmp(get_value(r, "DISP", 0), "OLD") == 0) {
        marslog(LOG_DBUG, "mcs:check_dupl:DISP=OLD");
        disposition = true;
    }


    /* initialize retrieval */

    priv->_ret = mrtgtbegin(priv->_qual, priv->_rec, ADDRNIL);

    if (priv->_ret == ADDRNIL) {
        marslog(LOG_WARN, "mcs:check_dupl:EMPRESS:'%s'", mrerrmsg());
        return 1;
    }


    getflag = mrtget(priv->_ret);

    if (disposition && getflag) {

        marslog(LOG_DBUG, "mcs:check_dupl:Try to delete old record");

        if (getflag == -1) {

            marslog(LOG_DBUG, "mcs:check_dupl:Try to get locked record");

            for (i = 0; i < priv->maxtry; i++) {
                sleep(priv->sleeptime);
                getflag = mrreget(priv->_ret);
                if (getflag == 1)
                    break;
            }

            if (getflag != 1) {
                mrgetend(priv->_ret);
                marslog(LOG_WARN, "mcs:check_dupl:Failed to get locked record");
                return 1;
            }
        }

        if (!mrtdel(priv->_rec)) {
            mrgetend(priv->_ret);
            marslog(LOG_WARN, "mcs:check_dupl:EMPRESS:'%s'",
                    mrerrmsg());
            return 1;
        }
        mrdelend(priv->_rec);

        getflag = 0;
    }

    mrgetend(priv->_ret);

    return ABS(getflag);
}


static int build_attr_list(mcs_private* priv) {


    int i;
    char *cbuf, *p, *q;

    FILE* f;
    attribute* a;
    struct stat sbuf;


    priv->_first = NULL;


    /* get type from request */

    if (priv->_type == NULL) {
        marslog(LOG_WARN, "mcs:build_attr_list:request still empty");
        priv->_r = NULL;
        return 0;
    }


    /* get size of sql script */

    if (stat(priv->_sql, &sbuf)) {
        marslog(LOG_WARN | LOG_PERR, "mcs:build_attr_list:Failed to get size of sql script '%s'", priv->_sql);
        return 1;
    }

    marslog(LOG_DBUG, "mcs:build_attr_list:Allocate '%ld' bytes", sbuf.st_size);

    cbuf = MALLOC(sbuf.st_size * sizeof(char));


    /* open scriptfile for reading */

    if ((f = fopen(priv->_sql, "rb")) == NULL) {
        marslog(LOG_WARN | LOG_PERR, "mcs:build_attr_list:Failed to open sql script '%s'", priv->_sql);
        FREE(cbuf);
        return 1;
    }

    fread((void*)cbuf, sbuf.st_size, 1, f);

    fclose(f);

    q = p = strchr(cbuf, '#');
    while (!isspace(*p))
        p--;
    p++;

    priv->_tab_name = (char*)MALLOC(q - p + 2);

    for (i = 0; (priv->_tab_name[i] = *(p + i)) != '#'; i++)
        ;

    priv->_tab_name[i] = '\0';

    /* go to end */


    for (p = strchr(cbuf, ';'); p >= cbuf; p--) {

        if (*p == '\n')
            if (isupper(*(p + 1)) || *(p + 1) == '_' && isupper(*(p + 2))) {

                for (i = 1; !isspace(*(p + i)); i++)
                    ;

                *(p + i) = '\0';

                a       = NEW_CLEAR(attribute);
                a->name = NEW_STRING(p + 1);

                /* marslog(LOG_DBUG,"p+1 = '%s' a->name = '%s'",p+1,a->name); */

                a->qual_flag = (a->name[0] != '_') ? true : false;

                a->next      = priv->_first;
                priv->_first = a;
            }
    }

    /* check if data should be stored in compressed form */

    priv->_compress = (strstr(cbuf, MCS_COMPRESSED_PATTERN)) ? true : false;

    marslog(LOG_DBUG, "mcs:build_attr_list:Compression is %s", (priv->_compress) ? "on" : "off");

    FREE(cbuf);

    return 0;
}


static boolean compare_inodes(char* p1, char* p2) {

    struct stat sbuf1, sbuf2;


    /* get inodes of sql script */

    if (stat(p1, &sbuf1)) {
        marslog(LOG_WARN | LOG_PERR, "mcs:compare_inodes:Failed in stat for '%s'", p1);
        return false;
    }

    if (stat(p2, &sbuf2)) {
        marslog(LOG_WARN | LOG_PERR, "mcs:compare_inodes:Failed in stat for '%s'", p2);
        return false;
    }


    return (sbuf1.st_ino == sbuf2.st_ino) ? true : false;
}

#else
#include "mars.h"
extern base_class _nullbase;
base_class* mcs_base = &_nullbase;
#endif
