/*
 * © 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 <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <ctype.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
