/***************************************
  $Revision: 1.22 $

  Core functions for update lower layer 

  Status: NOT REVUED, NOT TESTED

 Author(s):       Chris Ottrey, Andrei Robachevsky

  ******************/ /******************
  Modification History:
        andrei (17/01/2000) Created.
  ******************/ /******************
  Copyright (c) 2000                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ***************************************/
#include "ud.h"
#include "ud_int.h"

static int perform_update(Transaction_t *tr);

static int perform_create(Transaction_t *tr);

static void each_primary_key_select(void *element_data, void *result_ptr);

static void each_attribute_process(void *element_data, void *tr_ptr);

static void update_attr(Attribute_t *attr, Transaction_t *tr);

static int create_dummy(Attribute_t *attr, Transaction_t *tr);

static int auth_member_of(Attribute_t *attr, Transaction_t *tr);

/***************************************************
* char *s_split(char *line)                        *
*                                                  *
* Consequently returns words of the 'line'         * 
* When there are no words it returns NULL          *
* You need to retreive all words !                 *
*                                                  *
* NB This function damages 'line' replacing        *
* whitespace with '\0'                             *
* *************************************************/
#define ATTR_DELIMITERS " ,"


/**********************************************************
* Attribute expansion/conversion functions                *
***********************************************************/
/* Convert ifaddr attribute into numbers */
er_ret_t convert_if(char *avalue, unsigned int *pif_address)
{
char *delim;
ip_addr_t ip_addr;
er_ret_t ret;

  if ((delim=index(avalue, ' '))!=NULL) *delim='\0';
  ret=IP_addr_a2v4(avalue, &ip_addr,  pif_address );
  return(ret);
}


/* Convert refer attribute. Free host after use ! */
char *convert_rf(char *avalue, int *type, int *port)
{
char *delim, *token;
char buff[STR_M];
char *host;

  host=NULL;
  strcpy(buff, avalue);
  g_strchug(buff);
  delim=index(buff, ' ');
  *delim='\0';
  delim++; 

/* convert the type      */
  if(strcmp(buff, S_RIPE)==0)*type=RF_RIPE;
   else if(strcmp(buff, S_INTERNIC)==0)*type=RF_INTERNIC;
    else if(strcmp(buff, S_SIMPLE)==0)*type=RF_SIMPLE;
     else if(strcmp(buff, S_CLIENTADDERSS)==0)*type=RF_CLIENTADDRESS;

  token=delim;
  g_strchug(token);
  delim=index(token, ' ');
  if(delim){
   *delim='\0';
   delim++; 
  }	      
/* convert the hostname      */
  host = g_strdup(token);
      
/* convert port number      */
  if(delim){
    token=delim;	
    *port = atoi(token);
    if (*port==0) *port=RF_DEF_PORT; /* default port number*/
  } else *port=RF_DEF_PORT;
  return(host);
}


/* Convert AS# into integer */
static int convert_as(char *as)
{
char *ptr;
 ptr=as; ptr++; ptr++; 
 return(atoi(ptr));   
}

/* Convert AS range (AS4321 - AS5672) into numbers */
int convert_as_range(const char *as_range, int *begin, int *end)
{
char *range;
char *token=as_range;
  
  range=g_strdup(as_range);
  token=range;
  *begin=convert_as(strsep(&token, " -"));
  *end=convert_as(strsep(&token, " -"));
  free(range);
  return(0);
}

/* Convert time in ASCII format (19991224) into time_t unix time */
time_t convert_time(char *asc_time)
{
struct tm tm;
char buf[STR_S];
char *ptr;

  
  bzero(&tm, sizeof(tm));
  
  strncpy(buf, asc_time, 4); ptr=buf+4; *ptr='\0';
  tm.tm_year = atoi(buf) - 1900;
  
  strncpy(buf, (asc_time+4), 2); ptr=buf+2; *ptr='\0';
  tm.tm_mon = atoi(buf) - 1;
  
  strncpy(buf, (asc_time+6), 2); ptr=buf+2; *ptr='\0';
  tm.tm_mday = atoi(buf);
  
  return(mktime(&tm));

}     


/************************************************************
*  char *get_set_name()                                     *
*                                                           *
* Returns set name for the specified object class           *
*                                                           *
* **********************************************************/
static char *get_set_name(C_Type_t class_type)
{
 switch(class_type){
  case C_RT:   return("route_set");
  case C_AN:   return("as_set");
  default:     return(NULL);
 }
}


/************************************************************
* long get_object_id()                                      *
* Queries the database for an object.                       *
* For constructing a query uses each_primary_key_select()   *
*                                                           *
* Returns:                                                  *
* >0 - object exists, returns object_id                     *
* 0  - object does not exist                                *
* -1 - error (f.e. more than one object with the same PK)   *
* Error code is stored in tr->error                         *
*                                                           *
* **********************************************************/
long get_object_id(Transaction_t *tr)
{
Object_t *obj;
SQ_result_set_t *sql_result;
SQ_row_t *sql_row;
char *sql_str;
GString *query;
long object_id=0;
int sql_err;

 obj=tr->object;

 if ((query = g_string_sized_new(STR_XL)) == NULL){ 
  fprintf(stderr, "E: cannot allocate gstring\n"); 
  tr->succeeded=0;
  tr->error |= ERROR_U_MEM;
  return(-1); 
 }
 
/* compose query */
 g_string_sprintf(query, "SELECT object_id FROM %s WHERE",DF_get_class_sql_table(obj->type));
 /* add all primary keys */ 
 g_slist_foreach(obj->attributes, each_primary_key_select, query);
 /* truncate the last ' AND '*/
 g_string_truncate(query, (query->len) - 4); 
        
/* execute query */
 sql_err=SQ_execute_query(tr->sql_connection, query->str, &sql_result);
 g_string_free(query, TRUE);
 
/* in case of an error copy error code and return */ 
 if(sql_err) {
   fprintf(stderr,"ERROR: %s\n", SQ_error(tr->sql_connection));
   tr->succeeded=0;
   tr->error |= ERROR_U_DBS;
   return(-1);
 }

/* Fetch the row */ 
 if ((sql_row = SQ_row_next(sql_result)) != NULL) {
/* Object exists */
#define OBJECT_ID 0
   sql_str = SQ_get_column_string(sql_result, sql_row, OBJECT_ID);
   if (sql_str != NULL) {
     object_id = atol(sql_str);
     free(sql_str);
   }

/* We must process all the rows of the result */
/* otherwise we'll have them as part of the next qry */      
   while ( (sql_row = SQ_row_next(sql_result)) != NULL) object_id=-1;
 } else 
      object_id=0;  /* object does not exist*/
   
 SQ_free_result(sql_result);
 return(object_id);
}


/************************************************************
* get_qresult_str()                                         *
*                                                           *
* Returns string containing query result                    *
*                                                           *
*                                                           *
* Returns:                                                  *
*  String containing the result.Needs to be freed after use *
*  NULL in case of an error                                 *
*  - SQL error                                              *
*  - if query returns more than one string (row)            *
*                                                           *
*************************************************************/
char *get_qresult_str(SQ_connection_t *sql_connection, char *query)
{
SQ_result_set_t *sql_result;
SQ_row_t *sql_row;
char *sql_str;
int sql_err;


/*fprintf(stderr, "D:<get_field_str>:query: %s\n", query);*/
 sql_err=SQ_execute_query(sql_connection, query, &sql_result);
 
 if(sql_err) {
    fprintf(stderr,"ERROR: %s\n", SQ_error(sql_connection));
    die;
 }
        
	 
 if ((sql_row = SQ_row_next(sql_result)) != NULL) {
	sql_str = SQ_get_column_string(sql_result, sql_row, 0);

     /* We must process all the rows of the result,*/
     /* otherwise we'll have them as part of the next qry */
	while ( (sql_row = SQ_row_next(sql_result)) != NULL) {
	  fprintf(stderr, "E:<get_field_str> error : Dupl PK[%s]\n", query);
	  if(sql_str)free(sql_str); sql_str=NULL;
	}
 }
 else sql_str=NULL;
 
 SQ_free_result(sql_result);
 return(sql_str);
}



/************************************************************
* get_field_str()                                           *
*                                                           *
* Returns string containing the field.                      *
*  field - field name to be retrieved                       *
*  ref_tbl_name - name of the table containing the field    *
*  ref_name - reference name                                *
*  attr_value - reference value                             *
*  condition - additional condition ( f.e. 'AND dummy=0'    *
*                                                           *
* Returns:                                                  *
*  String containing the field. Needs to be freed after use *
*  NULL in case of an error                                 *
*                                                           *
*************************************************************/
char *get_field_str(SQ_connection_t *sql_connection, char *field, 
			   char *ref_tbl_name, char *ref_name, 
			   char * attr_value, char *condition)
{
char query[STR_L];

 sprintf(query, "SELECT %s FROM %s "
                "WHERE %s='%s' ",
		field, ref_tbl_name, ref_name, attr_value);
 if (condition)strcat(query, condition);

 return( get_qresult_str(sql_connection, query));

} 

/************************************************************
* long get_sequence_id(Transaction_t *tr)
* >0 - success
* -1 - sql error
*
* **********************************************************/

long get_sequence_id(Transaction_t *tr)
{
char *sql_str;
char str_id[STR_M];
long sequence_id=-1;


  sprintf(str_id, "%ld", tr->object_id);
  sql_str= get_field_str(tr->sql_connection, "sequence_id", "last", "object_id", str_id, NULL);
  if(sql_str) {
       	  sequence_id = atol(sql_str);
/*       	  fprintf(stderr, "D: Retrieved set serial id = %ld\n", sequence_id);*/
       	  free(sql_str);
  }
  
  return(sequence_id);

}


/************************************************************
* long get_ref_id(char *ref_tbl_name, char *ref_name, char * attr_value)
* >0 - success
* -1 - sql error
*
* **********************************************************/

static long get_ref_id(Transaction_t *tr, char *ref_tbl_name, char *ref_name, char * attr_value, char *condition)
{
char *sql_str;
long ref_id=-1;

/*fprintf(stderr, "D:<get_ref_id>: entering...\n");*/

	sql_str= get_field_str(tr->sql_connection, "object_id", ref_tbl_name, ref_name, attr_value, condition);
	if(sql_str) {
		 ref_id = atol(sql_str);
/*		 fprintf(stderr, "D: Retrieved set serial id = %ld\n", ref_id);*/
		 free(sql_str);
	}
	return(ref_id);	
}


/************************************************************
* int isdummy()
*
* Returns 1 if the object in question is a dummy, 
* otherwise returns 0.
* 
* In case of error:
* -1 - sql error or object does not exist
*
***********************************************************/

int isdummy(Transaction_t *tr)
{
char *sql_str;
char str_id[STR_M];
int object_type=-1;

  sprintf(str_id, "%ld", tr->object_id);
  sql_str= get_field_str(tr->sql_connection, "object_type", "last", "object_id", str_id, NULL);
  if(sql_str) {
       	  object_type = atoi(sql_str);
       	  free(sql_str);
  }
  
  if (object_type==-1) return(-1);
  if (object_type==DUMMY_TYPE) return(1);
  else return(0);

}

/* it may be either a legacy name reference, or a nic-handle  */
/* we rely on other parsers/syntax checkers, so no surprises  */
/* thus, the check is simple - if there is a space - not a nh */
static int isnichandle(char *name)
{
 if(index(name, ' ')) return(0);
 else return(1);	
/* return(MA_isset(WK_new(name), WK_NIC_HDL)); */
}


/************************************************************
* process_reverse_domain()                                  *
*                                                           *
* Tries to insert additional data for reverse domains       *
* This data includes prefix and perfix length for reverse   *
* delegation block. It is stored in inaddr_arpa table for   *
* IPv4 and ip6int table for IPv6 address spaces             *
*                                                           *
* Returns:                                                  *
* 0  success                                                *
* -1 sql error                                              *
*                                                           *
*************************************************************/

static int process_reverse_domain(Transaction_t *tr, 
				  ip_prefix_t *prefptr,
				  int op)
{
  unsigned prefix, prefix_length; /* ipv4 */
  ip_v6word_t high, low;          /* ipv6 */
  char query[STR_L];
  int num;
  int sql_err;

  				  
  if( IP_pref_b2_space(prefptr) == IP_V4 ) {  /* ipv4 */
    if(op==0) { /* insert record */
      IP_revd_b2v4(prefptr, &prefix, &prefix_length);
      sprintf(query, "INSERT INTO inaddr_arpa SET thread_id=%d, object_id=%ld, prefix=%u, prefix_length=%d ", 
	      tr->thread_ins, tr->object_id, prefix, prefix_length);
    }
    else {
      /* update record */
      sprintf(query, "UPDATE inaddr_arpa SET thread_id=%d WHERE object_id=%ld ", 
	      tr->thread_upd, tr->object_id);
    }
  }
  else { /* ipv6 */
    if(op==0) { /* insert record */   
      IP_revd_b2v6(prefptr, &high, &low, &prefix_length);
      sprintf(query, "INSERT INTO ip6int SET thread_id=%d, object_id=%ld, high=%llu, low=%llu, prefix_length=%d ", 
	      tr->thread_ins, tr->object_id, high, low, prefix_length);
    }
    else {
      /* update record */
      sprintf(query, "UPDATE ip6int SET thread_id=%d WHERE object_id=%ld ", 
	      tr->thread_upd, tr->object_id);
    }
  }

  sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
  num = mysql_affected_rows(tr->sql_connection); 
  
  /* Check for errors */
  if (sql_err) {
   fprintf(stderr, "E: insert inaddr:%s[%s]\n", SQ_error(tr->sql_connection), query);
   return(-1);
  }
  /* If nothing was affected then WHERE clause returned nothing - DB error */
  if(num == 0) {
   fprintf(stderr, "E: insert inaddr:no effect [%s]\n", query);
   return(-1);
  } 	  
  return(0);
}

#define insert_reverse_domain(tr, pr) process_reverse_domain(tr, pr, 0)
#define update_reverse_domain(tr, pr) process_reverse_domain(tr, pr, 1)


/************************************************************
* auth_member_of()                                          *
*                                                           *
* Function that checks the authorization for membership     *
* (i.e. if the object is authorized to be a memeber by      *
* mbrs-by-ref attribute of the set is refers by member-of   *
* attribute).                                               *
* First checks if 'mbrs-by-ref: ANY'                        *
* If not then checks that maintner referenced by            *
* mbrs-by-ref attribute of the set is the one in mnt-by.    *
*                                                           *
* Returns:                                                  *
* 0  success                                                *
* 1  not allowed                                            *
* -1 SQL error                                              *  
*                                                           *
*************************************************************/
static int auth_member_of(Attribute_t *attr, Transaction_t *tr)
{
GString *query;
char *set_name;
char *qresult;

/* Check if set has mbrs_by_ref==ANY 
   In such case mbrs_by_ref.mnt_id==0 
*/

 if ((query = g_string_sized_new(STR_XL)) == NULL){
  tr->succeeded=0;
  tr->error |= ERROR_U_MEM; 
  fprintf(stderr, "E: cannot allocate gstring\n"); 
  return(-1); 
 }
 
 set_name = get_set_name(tr->class_type);
/* fprintf(stderr, "D:<auth_member_of>: Got set name: %s\n", set_name);	*/

/* Check if the set protects itself with mbrs-by-ref attribute */
   g_string_sprintf(query,"SELECT COUNT(*) FROM mbrs_by_ref, %s "
   		          "WHERE mbrs_by_ref.object_id=%s.object_id "
			  "AND %s.%s='%s' ",
			  set_name, set_name, set_name, set_name, attr->value);

   qresult = get_qresult_str(tr->sql_connection, query->str);
   /* should be '0' if there is no mbrs-by-ref attribute */
   if (strcmp(qresult, "0")==0){
	   /* there is no mbrs-by-ref attribute - so we cannot go ahead */
	   fprintf(stderr, "E:<auth_member_of> : Membership is not autorized[%s]\n",query->str);
	   g_string_free(query, TRUE);
           return(1);
   }
   else free(qresult);

/* Check if membership is protected by the keyword "ANY" */
/* There is a dummy mntmer object in the database corresponding to "ANY" */
/* Its object_id==0 */
/* EXAMPLE:

   SELECT route_set.object_id 
   FROM   mbrs_by_ref, route_set
   WHERE  mbrs_by_ref.object_id=route_set.object_id
   AND    route_set.route_set=<setname>
   AND    mbrs_by_ref.mnt_id=0
*/   
    g_string_sprintf(query,"SELECT %s.object_id FROM mbrs_by_ref, %s "
                           "WHERE mbrs_by_ref.object_id=%s.object_id "
		           "AND %s.%s='%s' AND mbrs_by_ref.mnt_id=0 ", 
			   set_name, set_name, set_name, set_name, set_name, attr->value);
/* fprintf(stderr, "D:<auth_member_of>: query: %s\n", query->str);*/
  
    qresult = get_qresult_str(tr->sql_connection, query->str);
  /* if such record exists - go ahead */
    if(qresult) {
	free(qresult);  
	g_string_free(query, TRUE);
        return(0);  
    }

/* Now check if our mnt_by belongs to mbrs_by_ref list of the set */
/* we search only mnt_by.thread_id!=0 to check against new/updated mnt-by attribute */
    g_string_sprintf(query, "SELECT mbrs_by_ref.object_id FROM route_set, mbrs_by_ref, mnt_by "
 			    "WHERE mbrs_by_ref.mnt_id=mnt_by.mnt_id "
    			    "AND mnt_by.object_id=%ld "
    			    "AND %s.object_id=mbrs_by_ref.object_id "
    			    "AND %s.%s='%s' "
    			    "AND mnt_by.thread_id!=0 ",
    			    tr->object_id, set_name, set_name, set_name, attr->value);

/*  fprintf(stderr, "D:<auth_member_of>: query: %s\n", query->str);						*/

    qresult = get_qresult_str(tr->sql_connection, query->str);
    /* If our mntner is listed (non-empty result)  membership is authorized */
    if (qresult) {
	 free(qresult);g_string_free(query, TRUE);
	 return(0);
    } else {
	 fprintf(stderr, "E:<auth_member_of> : Membership is not autorized[%s]\n", query->str);
	 g_string_free(query, TRUE);
	 return(1);
    }
 }/* auth_member_of()  */
	

/************************************************************
* create_dummy()                                            *
*                                                           *
* Function that creates a dummy object (that is one that    *
* is referenced from an object but does not                 *
* exist in the database).                                   *
* Dummy object exists only in relevant main and 'last'      *
* tables. Its creation is controlled by tr->dummy_allowed.  *
* Queries for the dummies are defined in Dummy[] array.     *
*                                                           *
* Returns:                                                  *
* 0  success                                                *
* 1  no rf integrity and dummy not allowed
* -1 SQL error                                              *
*                                                           *
*************************************************************/
static int create_dummy(Attribute_t *attr, Transaction_t *tr) 
{
const char *query_fmt;
long dummy_id;
char query[STR_L];
int result=0;
char *set_name;
char *p_name;
int query_type;
long timestamp;
char str_id[STR_M];
gchar *attr_value=NULL;
int sql_err;
char *token=NULL;

  query_fmt = DF_get_dummy_query(attr->type);
  if (strcmp(query_fmt, "") == 0) { 
     fprintf(stderr, "E:<create_dummy>: empty query string\n"); 
     return(1); 
  }
  
  /* We allow creating dummy sets in any mode */
  /* For others attributes return if we are in protected mode */
  if ((attr->type!=A_MO) &&  (tr->dummy != 1)) return(1);

  /* Insert dummy in the last table */
  sprintf(str_id, "%ld", tr->object_id);
  timestamp=time(NULL);
  sprintf(query, "INSERT INTO last SET thread_id=%d, timestamp=%ld, object_type=%d, object='DUMMY for %s'", 
                  tr->thread_ins, timestamp, DUMMY_TYPE, str_id);
/* fprintf(stderr, "D: making dummy entry in the last table\n %s\n", query);*/
  sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
  
  /* Check for errors */
  if (sql_err) {
   fprintf(stderr, "E: dummy->last:[%s]\n", query);
   return(-1);
  }	
	
  /* insert dummy in the main table */
  dummy_id=mysql_insert_id(tr->sql_connection); 
  /* Record dummy's object_id, it'll be needed in commit/rollback */
  tr->dummy_id[tr->ndummy]=dummy_id; tr->ndummy++;
  
  /* compose the query */
  query_type=DF_get_dummy_query_type(attr->type);
  switch (query_type) {	
	 
	 /* person_role */
	 case UD_AX_PR:
    	      sprintf(query, query_fmt, tr->thread_ins, dummy_id, attr->value, DUMMY_TYPE);
	      break;
	 
	 /* maintner */
	 case UD_AX_MT:	
	      sprintf(query, query_fmt, tr->thread_ins, dummy_id, attr->value, DUMMY_TYPE);
	      break;
         
         /* as_set, route_set */
	 case UD_AX_MO:	
	      set_name = get_set_name(tr->class_type);
	      sprintf(query, query_fmt, set_name, tr->thread_ins, dummy_id, set_name, attr->value);	  
	      break;
	      
	 default:
	      fprintf(stderr, "E: query not defined for this type of attribute[%d]\n", attr->type);
	      return(-1);
              break;
  }
	
  /*fprintf(stderr, "D: query: %s\n", query);*/
  sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
  /*fprintf(stderr, "D: query: %d rows affected\n", num);*/
  if (sql_err) {
   fprintf(stderr, "E: dummy->main:%s[%s]\n", SQ_error(tr->sql_connection), query);
   return(-1);
  }
  
  /* for legacy person/role reference (without nic-handle) create records in names table */
  if( (query_type == UD_AX_PR) && (!isnichandle (attr->value)) ){
   /* parse the names */
    /*fprintf(stderr,"adding names for dummy\n");*/
    query_fmt = DF_get_insert_query(A_PN);
    attr_value = g_strdup(attr->value); 
    token = attr_value;
    while((p_name=strsep(&token, " "))){
		sprintf(query, query_fmt, tr->thread_ins, dummy_id, DUMMY_TYPE, p_name);
/*		fprintf(stderr, "D: query: %s\n", query);*/
		sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
		if (sql_err)
		 if(SQ_errno(tr->sql_connection) != ER_DUP_ENTRY) {
		  fprintf(stderr, "E: insert dummy names:%s[%s]\n", SQ_error(tr->sql_connection), query);
		  result=-1;
		 }
    }
    free(attr_value);
  }
 return(result);
}

/************************************************************
* update_attr()                                             *
*                                                           *
* Function that updates an attribute if it already exists.  *
* Called from each_attribute_proces() function if it        *
* cannot insert the row.                                    *
* Queries for the attributes are defined in Update[] array. *
*                                                           *
* Returns: Nothing. Error code is stored in tr->error.      *
*                                                           *
*************************************************************/
static void update_attr(Attribute_t *attr, Transaction_t *tr)
{
int num;
const char *query_fmt;
char *set_name;
unsigned int if_address;
char * rf_host;
int rf_port, rf_type;
char *a_value;
int sq_info[3];
char * condition;
char *sq_error;
char query[STR_XL];
ip_prefix_t dn_pref;
int sql_err;

/* It may be needed to update second attribute stored in the main table, like inetnum, filter-set, etc. */
 if((tr->load_pass!=0)&&(DF_get_update_query_type(attr->type)!=UD_MA_U2)) return;

/*	fprintf(stderr, "D: updating attribute...\n");*/

   /* Do some additional processing for reverse domains */
   /* XXX Later we will implement this under UD_MA_DN case */
   if ((attr->type == A_DN) && (IP_revd_e2b(&dn_pref, attr->value)==IP_OK)) {
     if(update_reverse_domain(tr, &dn_pref) !=0 ){
       tr->error|=ERROR_U_DBS;
       tr->succeeded=0;
       g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,
			 ERROR_U_DBS, attr->type, attr->value, SQ_error(tr->sql_connection));	
     }
   }
   
/*   query_fmt = Update[attr->type].qry; */
   query_fmt =  DF_get_update_query(attr->type);

   if (strcmp(query_fmt, "") == 0) return;

   switch (DF_get_update_query_type(attr->type)) {
         case UD_MAIN_: sprintf(query, query_fmt, tr->thread_upd, tr->object_id);
                        break;
	 case UD_MA_PR: 
	 		sprintf(query, query_fmt, tr->thread_upd, tr->class_type, tr->object_id);
			break;	
	 case UD_MA_U2: /* save the new value of the attribute for commit*/
                  /* this is necessary for filter(filter-set), netname (inet?num), */
		  /* local-as(inet-rtr) attributes, as they are another field in the record */
		        if((tr->load_pass != 0)){
		      /* for fast loader we need to update the field as we have no commit */
		          sprintf(query, query_fmt, DF_get_class_sql_table(tr->class_type), 0, attr->value, tr->object_id);
		        }
		        else {
            	         tr->save=g_strdup(attr->value);
/*            	         fprintf(stderr, "D:<e_a_p> attribute saved: %s\n", tr->save);*/
            	         return;
            	        }	 
			break;			
	 case UD_AX_PR:
                        /* This is for non-conformant admin-c, etc.*/
                        a_value=attr->value;
	 		if(strlen(attr->value)>MAX_NIC_HDL)*(attr->value + MAX_NIC_HDL)='\0';
	 		
	 		if (tr->dummy!=1)condition="AND dummy=0 "; else condition=NULL;
	 		sprintf(query, query_fmt, tr->thread_upd, tr->object_id, 
	 			get_ref_id(tr, "person_role", "nic_hdl", attr->value, condition));
	 		break;
	 case UD_AX_MT: 
	 		if (tr->dummy!=1)condition="AND dummy=0 "; else condition=NULL;
	 		sprintf(query, query_fmt, tr->thread_upd, tr->object_id, 
	 			get_ref_id(tr, "mntner", "mntner", attr->value, condition));
	 		break;
	 case UD_AX_MO: 
			set_name = get_set_name(tr->class_type);
/*	    	      fprintf(stderr, "D: retrieved set name: %s\n", set_name);*/
			if (tr->dummy!=1)condition="AND dummy=0 "; else condition=NULL;
			sprintf(query, query_fmt, tr->thread_upd, tr->object_id, 
					get_ref_id(tr, set_name, set_name, attr->value, condition));
			break;	    			
    	 case UD_AX_MR:
      			if ((strcmp(attr->value, "ANY")==0) || (strcmp(attr->value, "any")==0) || (strcmp(attr->value, "Any")==0))
      		 	sprintf(query, query_fmt, tr->thread_upd, tr->object_id, 
      		 	 	get_ref_id(tr, "mntner", "mntner", "ANY",NULL));
      			else {  
      		 	 if (tr->dummy!=1)condition="AND dummy=0 "; else condition=NULL;
      		 	 sprintf(query, query_fmt, tr->thread_upd, tr->object_id, 
      		 	 	get_ref_id(tr, "mntner", "mntner", attr->value, condition));
      		 	}
			break;
	 case UD_LEAF_: 
	 		sprintf(query, query_fmt, tr->thread_upd, tr->object_id, attr->value);
			break;
	 case UD_LF_IF:
		/* Convert ascii ip -> numeric one */
      	                convert_if(attr->value, &if_address);
			sprintf(query, query_fmt, tr->thread_upd, tr->object_id, if_address);
    			break;
	 case UD_LF_RF:
			rf_host=convert_rf(attr->value, &rf_type, &rf_port);
			sprintf(query, query_fmt, tr->thread_upd, tr->object_id, rf_type, rf_host, rf_port);
			if(rf_host)free(rf_host);
			break;			
  	 case UD_LF_AY:
                  	sprintf(query, query_fmt, tr->thread_upd, tr->object_id, convert_time(attr->value));
                   	break;		
	   default:
		fprintf(stderr, "E:<e_a_u> query not defined for this type of attribute:[%d]\n", attr->type);
			tr->error|=ERROR_U_BUG;
			tr->succeeded=0;
			g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:no update qry\n" ,ERROR_U_BUG, attr->type, attr->value);
    			break;
        }
/* fprintf(stderr, "D: update: [%s]", query); */
   
   /* Execute the query */
    sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
    if(sql_err) { /* an error occured*/
     /* Error - copy the error condition and return */
        sq_error=SQ_error(tr->sql_connection);
	fprintf(stderr, "E:<each_attribute_create> %s:[%s]\n", sq_error, query);
	tr->error|=ERROR_U_DBS;
	tr->succeeded=0;
	g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,ERROR_U_DBS, attr->type, attr->value, sq_error);
	return;
    }
    else {
     /* Query OK */
      num = mysql_affected_rows(tr->sql_connection);
      if(num == 0) { /* check for duplicates*/
  	SQ_get_info(tr->sql_connection, sq_info); /* UPDATE ... SET*/
  	if ((sq_info[SQL_DUPLICATES]==0) && (sq_info[SQL_MATCHES]==0)) { 
  	/* Condition with zero duplicates and matches may occur when the object is a dummy */
  	/* and we are running in protected mode ( dummies are not allowed, tr->dummy==0). */
  	/* In such case we will append "AND dummy=0" to the query, which won't */
  	/* return a match if the object in question is a dummy */
  	  fprintf(stderr, "E: Dummy prevents update: [%s]\n", query);
  	  tr->error|=ERROR_U_OBJ;
  	  tr->succeeded=0;
  	  g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:dummy update\n" ,ERROR_U_OBJ, attr->type, attr->value);
  	} /* else duplicate entry - silently drop it  */
      }	
      /* For member_of attribute we need to check membership claim in protected mode */
      if ((attr->type == A_MO) && (tr->dummy!=1)){
/*	fprintf(stderr, "D:<e_a_p>: need to auth membership\n");*/
	  if(auth_member_of(attr, tr)!=0){
	  tr->error|=ERROR_U_AUT;
	  tr->succeeded=0;
	  fprintf(stderr, "E:<each_attribute_create>: membership not allowed\n");
	  g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:membership not allowed\n" ,ERROR_U_AUT, attr->type, attr->value);	
	}
      }
    }  
return;
}/*  update_attr()  */


/************************************************************
* each_attribute_proces()                                   *
*                                                           *
* Main function that processes object attributes one by one.*
* Called from g_slist_foreach() function.                   * 
* First it tries to insert an attribute.                    *
* If an error it assumes that attribute is already in       *
* a table and calls update_attr() to update it.             *
* Queries for the attributes are defined in Insert[] array. * 
*                                                           *
* Returns: Nothing. Error code is stored in tr->error.      *
*                                                           *
*************************************************************/
static void each_attribute_process(void *element_data, void *tr_ptr) 
{
int num;
const char *query_fmt;
int query_type;
int do_query;
Attribute_t *attr = element_data;
Transaction_t *tr = (Transaction_t *)tr_ptr;
unsigned int prefix, prefix_length, if_address;
unsigned int begin_in, end_in;
ip_v6word_t  high, low;

int begin_as, end_as;
char query[STR_XL];
char * set_name;
char * rf_host; /* needs to be freed after use*/
int rf_type, rf_port;
char *a_value;
int sq_info[3];
char *mu_mntner, *mu_prefix;
int dummy_err;
char *sq_error;
ip_prefix_t dn_pref;
int sql_err;
int res;
char *token;
gchar *attr_value=NULL;

/* In this structure we keep data for the radix tree */
/* XXX now we are keeping this in tr structure */
/* static rp_upd_pack_t data_pack; */

/* we still want to continue to collect all possible errors*/
/*  if(tr->succeeded == 0) return; */
 
 /* To switch off querying for some types of attributes */
  do_query=1;
  
 /* Determine the query type */ 
  query_type=DF_get_insert_query_type(attr->type);

/* For loadind pass #1 we need to process only main tables */
  if(tr->load_pass==1){ 
	switch(query_type) {
	 case UD_MAIN_:
	 case UD_MA_U2:
	 case UD_MA_PR:
	 case UD_MA_RT:
	 case UD_MA_IN:
	 case UD_MA_I6:
	 case UD_MA_OR:
	 case UD_MA_AK:
	 		break;
	 default:	return;	/* return for other than MAIN tables*/
	}
  }
  
    query_fmt = DF_get_insert_query(attr->type);

/* return if no query is defined for this attribute */
  if (strcmp(query_fmt, "") == 0) return;

 /* compose the query depending on the attribute */
  switch (query_type) {
   case UD_MAIN_: /* for MAIN tables */
   		if (ACT_UPDATE(tr->action)) do_query=0;
    		else
    		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, attr->value);
    		break;
   case UD_MA_OR: /* for the origin attribute */
   		if (ACT_UPDATE(tr->action)) do_query=0;
    		else {
		  sprintf(query, query_fmt, tr->thread_ins, attr->value, tr->object_id);
		  tr->action |= TA_UPD_RX;
		  RP_pack_set_orig(attr->type, tr->packptr, attr->value);
		}
    		break;
   case UD_MA_PR: /* for person_role table*/
   		if (ACT_UPDATE(tr->action)) do_query=0;
   		else
   		 sprintf(query, query_fmt, tr->thread_ins, tr->class_type, tr->object_id,  attr->value);
   		
   		/* check if we need to update NHR */
    		if (ACT_UPD_NHR(tr->action)) {
		 /* Check if we can allocate it */	 
		  res = NH_check(tr->nh, tr->sql_connection);
		  if(res == -1) { /* we cannot allocate this NIC handle (DB error) */
		     tr->succeeded=0;
		     tr->error |= ERROR_U_DBS;
		     g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:cannot allocate nic-handle\n", ERROR_U_DBS, attr->type, attr->value);
                     return;
                  }
                  else 
                  if(res == 0) { /* we cannot allocate this NIC handle (full space or ID in use) */
		    tr->succeeded=0; 
		    tr->error |= ERROR_U_OBJ;
		    g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:nic-handle already in use\n", ERROR_U_OBJ, attr->type, attr->value); 
		    return;
		  }
		}
    		break;	
   case UD_MA_RT: /* for route table*/
    		if (ACT_UPDATE(tr->action)) do_query=0;
    		else {
                  tr->action |= TA_UPD_RX;
    		  RP_pack_set_pref4(attr->type, attr->value, tr->packptr, &prefix, &prefix_length);
		  /*fprintf(stderr, "D: route: %u/%u\n", prefix, prefix_length);     		*/
		  sprintf(query, query_fmt, tr->thread_ins,  
			  tr->object_id, prefix, prefix_length);
	    	}
    		break;
   case UD_MA_IN: /* for inetnum table*/
    		if (ACT_UPDATE(tr->action)) do_query=0;
    		else {
		  tr->action |= TA_UPD_RX;
    		  RP_pack_set_rang(attr->type, attr->value, tr->packptr, &begin_in, &end_in);
		  /* XXX error handling ? */
		  sprintf(query, query_fmt, tr->thread_ins, tr->object_id, begin_in, end_in);
		}	
    		break;
   case UD_MA_I6: /* for inet6num table*/
                if (ACT_UPDATE(tr->action)) do_query=0;
    		else {
    		  tr->action |= TA_UPD_RX;
		  RP_pack_set_pref6(attr->type, attr->value, tr->packptr, &high, &low, &prefix_length);
		  /* XXX error handling ? */
		  sprintf(query, query_fmt, tr->thread_ins, tr->object_id, high, low, prefix_length);
		}	
    		break;	
   case UD_MA_U2: /* This is actually an update - go to update_attr - this is more natural */
                 do_query=0;
            	break;
   case UD_MA_AK: /* for as_block table*/
   		if (ACT_UPDATE(tr->action)) do_query=0;
   		else {
   		  convert_as_range(attr->value, &begin_as, &end_as);
		  sprintf(query, query_fmt, tr->thread_ins, tr->object_id, begin_as, end_as);
   		}
   		break;         	    		
   case UD_AUX__: /* for AUX tables*/
    		if((attr->type==A_AC) || (attr->type==A_TC) || (attr->type==A_ZC))
    		 if(strlen(attr->value)>MAX_NIC_HDL)*(attr->value + MAX_NIC_HDL)='\0';

    		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
    		if(tr->dummy!=1)strcat(query, " AND dummy=0 ");
    		break;
   case UD_AX_MO: /* for member_of table*/
		set_name = get_set_name(tr->class_type);
/*    		fprintf(stderr, "D: retrieved set name: %s\n", set_name);*/
    		sprintf(query, query_fmt, tr->thread_ins,  
	    	 tr->object_id, set_name, tr->class_type, set_name, set_name, set_name, attr->value);
    		break;	
   case UD_AX_MR: /* for mbrs_by_ref table*/
      		if ((strcmp(attr->value, "ANY")==0) || (strcmp(attr->value, "any")==0) || (strcmp(attr->value, "Any")==0))
      		 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, "ANY");
      		else  
      		 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
		break;	
   case UD_AX_MU: /* for mnt_routes table*/
   		a_value=g_strdup(attr->value); 
		token = attr_value;
		mu_mntner=strsep(&token, " ");
		mu_prefix=token;
   		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, mu_prefix, tr->class_type, mu_mntner);
   		free(a_value);
   		if(tr->dummy!=1)strcat(query, " AND dummy=0 ");
   		break;
   case UD_LEAF_: /* for LEAF tables*/
    		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, attr->value);
    		break;
   case UD_LF_OT: /* for LEAF tables containing object_type field*/
   		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
   		break; 		    		
   case UD_LF_AT: /* check PGPKEY. If yes - check the existence of key-cert.*/
      		if(tr->dummy!=1){
      		 if(strncmp("PGPKEY", attr->value, 6)==0) {
      		   if(get_ref_id(tr, "key_cert", "key_cert", attr->value, NULL)<=0) { 
      		    fprintf(stderr, "E:<e_a_p>: No key-cert object.\n");
      		    tr->error|=ERROR_U_OBJ;
      		    tr->succeeded=0;
      		    g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:no key-cert object\n" ,ERROR_U_OBJ, attr->type, attr->value);
      		    return;
      	           }
      		 }
      		} 
      		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
      		break;      
   case UD_LF_IF: /* for ifaddr tables*/
    		/* Convert ascii ip -> numeric one*/
    		convert_if(attr->value, &if_address);
		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, if_address);
    		break;
   case UD_LF_RF: /* for refer table*/
    		rf_host=convert_rf(attr->value, &rf_type, &rf_port);
    		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, rf_type, rf_host, rf_port);
    		if(rf_host)free(rf_host);
    		break;	
   case UD_LF_AY: /* for auth_override table*/
   		sprintf(query, query_fmt, tr->thread_ins, tr->object_id, convert_time(attr->value));
   		break;
    	default:
                fprintf(stderr, "E: query not defined for this type of attribute\n");
                tr->succeeded=0;
                tr->error |= ERROR_U_BUG;
                g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:query not defined for the attribute\n" ,ERROR_U_BUG, attr->type, attr->value);
                return;
    		break;
  }
  
/* fprintf(stderr, "D: insert: [%s]", query); */


 /* Make the query. For primary keys go straight to updates if we are updating the object */
  if(do_query){
   sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
  } 
  else {
   update_attr(attr, tr);
   return;
  }
  
/*  fprintf(stderr, "D: query: %d rows affected\n", num);*/
  if (sql_err)  {
  /* we received an error */
   if(SQ_errno(tr->sql_connection) == ER_DUP_ENTRY){ /* Only error "Duplicate entry" may be considered*/
  	if (ACT_UPDATE(tr->action)) { /* In update mode this is common (so actually not an error)*/
  		update_attr(attr, tr);
  		return;
  	}	
     /* Otherwise this is a duplicate attribute, just ignore it */
     /* In the future if we are more stringent, checks may be added here */	
   }
   else { /* Other errors reveal a database/server problem*/
        sq_error=SQ_error(tr->sql_connection);
        tr->error|=ERROR_U_DBS;
        tr->succeeded=0;
        fprintf(stderr, "E:<each_attribute_create>: %s: [%s]\n", sq_error, query);
        g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,ERROR_U_DBS, attr->type, attr->value, sq_error);
   }
  } /* if error occured */
  else { 
 /* If the query was successful */
   num = mysql_affected_rows(tr->sql_connection);
   if(num>0){ /* this is OK*/
 /* Do some additional processing for member_of attribute  */
	if ((attr->type == A_MO) && (tr->dummy!=1)){
/*		fprintf(stderr, "D:<e_a_p>: need to auth membership\n");*/
		if(auth_member_of(attr, tr)!=0){
		 tr->error|=ERROR_U_AUT;
		 tr->succeeded=0;
		 fprintf(stderr, "E:<each_attribute_create>: membership not allowed\n");
		 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:membership not allowed\n" ,ERROR_U_AUT, attr->type, attr->value);	
		}
	}
	else
	  /* Do some additional processing for reverse zones domains */
	  if ((attr->type == A_DN) 
	      && IP_revd_e2b(&dn_pref, attr->value)==IP_OK ) {
	    
	    if(insert_reverse_domain(tr, &dn_pref) != 0 ) {
		tr->error|=ERROR_U_DBS;
		tr->succeeded=0;
		g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,
				  ERROR_U_DBS, attr->type, attr->value, 
				  SQ_error(tr->sql_connection));	
	    }
	    else {
	      /* save data for the radix tree update */
	      tr->action |= TA_UPD_RX;
	      RP_pack_set_revd(attr->type, attr->value, tr->packptr);
	    }
	  }
        return;
   }
   if(num == 0) {
/* this could be an empty update or a null select */	    
  	SQ_get_info(tr->sql_connection, sq_info); 
  	if (sq_info[SQL_DUPLICATES]>0) { 
  		if (sq_info[SQL_DUPLICATES]>1) { 
  			fprintf(stderr, "E: Update: Too many duplicates for query: [%s]\n", query);
  			tr->error|=ERROR_U_DBS;
  			tr->succeeded=0;
  			g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:duplicates\n" ,ERROR_U_DBS, attr->type, attr->value);
  			return;
  		}
  		update_attr(attr, tr);
  	}
  	else { 
		
		/* try to create dummy and repeat original query*/
  		
/*		fprintf(stderr, "W: no ref. integrity. Trying to create dummy...");*/

		dummy_err = create_dummy(attr, tr);
		if (dummy_err == 0) {
/*			fprintf(stderr, "D: ... dummy OK\n");*/
			g_string_sprintfa(tr->error_script,"W[%d][%d:%s]:dummy created\n" ,0, attr->type, attr->value);
/*			fprintf(stderr, "D: repeating query: %s\n", query);*/
			sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
			num = mysql_affected_rows(tr->sql_connection);
			if (sql_err) {
			  sq_error=SQ_error(tr->sql_connection);
		          fprintf(stderr, "E: re-insert query:%s[%s]\n", sq_error, query);
		          tr->error|=ERROR_U_DBS;
		          tr->succeeded=0;
		          g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,
		                            ERROR_U_DBS, attr->type, attr->value, sq_error);
		        }                    
		        if (num==0) {
		          fprintf(stderr, "E: re-insert query:%s[%s]\n", "", query);
			  tr->error|=ERROR_U_DBS;
			  tr->succeeded=0;
			  fprintf(stderr, "E: re-insert query: [%s]\n", query);
			  g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:re-insert qry\n" ,
			                    ERROR_U_DBS, attr->type, attr->value);
			}
		}
		else 
		 if(dummy_err == 1) {
		   tr->error |= ERROR_U_OBJ;
		   tr->succeeded=0;
		   g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:dummy not allowed\n" ,ERROR_U_OBJ, attr->type, attr->value);
		 }
		 else {
		   tr->error|=ERROR_U_DBS;
		   tr->succeeded=0;
		   fprintf(stderr, "E:<each_attribute_create>: dummy not created\n");
		   g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:dummy not created\n" ,ERROR_U_DBS, attr->type, attr->value);
		}	
  	}  /* RI*/
   }/* if num == 0*/
  } /* if the query was successful */
  
  return;
} /* each_attribute_process() */



/************************************************************
* each_primary_key_select()                                 *
*                                                           *
* Function that forms a query for an object (w prinary keys)*
* Called from g_slist_foreach() function.                   *
* Primary keys are defined in Select[] array.               *
*                                                           *
* Returns: Nothing.                                         *
*                                                           *
*************************************************************/ 
static void each_primary_key_select(void *element_data, void *result_ptr) 
{
Attribute_t *attr = element_data;
GString *result = (GString *)result_ptr;
const char *query_fmt;
unsigned int prefix, prefix_length;
unsigned int begin_in, end_in;
int begin_as, end_as;
ip_prefix_t prefstr;
ip_range_t  rangstr;
ip_v6word_t i6_msb, i6_lsb;

   query_fmt = DF_get_select_query(attr->type);

  if (strcmp(query_fmt, "") != 0) {
    switch (DF_get_select_query_type(attr->type)) {
     case UD_MAIN_: 
     		g_string_sprintfa(result, query_fmt, attr->value);
    		break;
     case UD_MA_RT:
                IP_pref_a2v4(attr->value, &prefstr, &prefix, &prefix_length);
    		g_string_sprintfa(result, query_fmt, prefix, prefix_length);
    		break;
     case UD_MA_IN:
		IP_rang_a2v4(attr->value, &rangstr, &begin_in, &end_in);
		g_string_sprintfa(result, query_fmt, begin_in, end_in);
    		break;
     case UD_MA_I6:
		IP_pref_a2v6(attr->value, &prefstr, &i6_msb, &i6_lsb, &prefix_length);
		g_string_sprintfa(result, query_fmt, i6_msb, i6_lsb, prefix_length);
    		break;						
     case UD_MA_AK:
     		convert_as_range(attr->value, &begin_as, &end_as);
     		g_string_sprintfa(result, query_fmt, begin_as, end_as);
		break;
     default:
fprintf(stderr, "E:<e_p_k_s>: query not defined for this type of attribute:[%d]\n", attr->type);
                die;

    	break;
    } 
  }
} 

/************************************************************
* perform_create(const Object_t *obj, Transaction_t *tr)    * 
*                                                           *
* Procedure for creating a new object.                      *
* First inserts object into 'last' table and gets object_id.*
* Then processes all attributes.                            *
*                                                           *
* Returns: tr->succeeded: >0 success, 0 - error             *
* Error code is stored in tr->error.                        *
*                                                           *
*************************************************************/ 
static int perform_create(Transaction_t *tr) 
{
 Object_t *obj;
 char *str;
 char query[STR_XXXL];
 long timestamp;
 int sql_err;
  
 obj=tr->object;
  
      str = (obj->object)->str;
      timestamp=time(NULL);
      tr->sequence_id=1; /* we start with 1*/
      sprintf(query, "INSERT INTO last SET thread_id=0, timestamp=%ld, sequence_id=1, object_type=%d, object='%s' ",
      	              timestamp, tr->class_type, str);

      sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
      
     /* Check for affected rows. One row should be affected . */ 
      if (sql_err) {
        tr->error|=ERROR_U_DBS;
        tr->succeeded=0; 
        fprintf(stderr, "E ERROR!<perform_create>: INSERT last failed:%s\n", SQ_error(tr->sql_connection));
        g_string_sprintfa(tr->error_script,"E[%d][:]:%s\n" ,ERROR_U_DBS, SQ_error(tr->sql_connection));
      }
      else {
      /* Get generated (autoincrement) object_id */
        tr->object_id=mysql_insert_id(tr->sql_connection);
        g_slist_foreach(obj->attributes, each_attribute_process, tr);
      }
    return(tr->succeeded);  
} /* perform_create() */

/************************************************************
* perform_update(Transaction_t *tr)                         * 
*                                                           *
* Procedure for updating (existing) object.                 *
* First processes all attributes.                           *
* Then saves previous object in 'history' and updates       *
* 'last' table.                                             *
*                                                           *
* Returns: tr->succeeded: >0 success, 0 - error             *
* Error code is stored in tr->error.                        *
*                                                           *
*************************************************************/ 
static int perform_update(Transaction_t *tr) 
{
Object_t *obj;
char *str;
char query[STR_XXXL];
int num;
long sequence_id;
long timestamp;
char *sq_error;
int sql_err;

  obj=tr->object;

  /* process each attribute one by one */
  g_slist_foreach(obj->attributes, each_attribute_process, tr);

  /* If we've already failed or this is fast load - just return */
  if((tr->succeeded == 0) || (tr->load_pass != 0)) return(tr->succeeded);
  
    /* No return: thread_id=0 */
    /* Do it only if previous transactions finished well */
       
    /* copy object to the history table */
/*fprintf(stderr, "INSERT history\n");    */
    sprintf(query,"INSERT history "
                  "SELECT 0, object_id, sequence_id, timestamp, object_type, object "
                  "FROM last "
                  "WHERE object_id=%ld ", tr->object_id);

    sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
    
   /* Check for affected rows. One row should be affected . */
    num = mysql_affected_rows(tr->sql_connection);
    if (num < 1) {
         tr->error|=ERROR_U_DBS;
         tr->succeeded=0;
         if (sql_err) {
          sq_error=SQ_error(tr->sql_connection);
          fprintf(stderr, "E ERROR!<perform_update>: INSERT history failed:[%d][%s]%s\n", num, query, sq_error);
          g_string_sprintfa(tr->error_script,"E[%d][:]:INSERT history failed:%s\n" ,ERROR_U_DBS, sq_error);
         }
         else {
          fprintf(stderr, "E ERROR!<perform_update>: INSERT history failed:[%d][%s]\n", num, query);
          g_string_sprintfa(tr->error_script,"E[%d][:]:INSERT history failed\n" ,ERROR_U_DBS);
	/* This is to check that this is really could happen */  
	  die;
         } 
         return(tr->succeeded);
    }

    /* get sequence number */
    
    sequence_id = get_sequence_id(tr);
    if(sequence_id==-1) {
      fprintf(stderr, "E ERROR!<perform_update> cannot get sequence_id\n");
      tr->error|=ERROR_U_DBS;
      tr->succeeded=0;
      g_string_sprintfa(tr->error_script,"E[%d][:]:cannot get seq ID\n" ,ERROR_U_DBS);
      return(tr->succeeded);
    } 
    else tr->sequence_id=sequence_id; /* save it for rollback*/
        
       
    /* Insert new version into the last */
    
    /* Put a timestamp */
    str = (obj->object)->str;
    timestamp=time(NULL);
    tr->sequence_id++;
       
/*fprintf(stderr, "UPDATE last\n");       */
    /* If we are here - it's almost commit. Otherwise this row will not be updated at all. */
    sprintf(query, "UPDATE last "
                   "SET thread_id=0, sequence_id=%ld, timestamp=%ld, object_type=%d, object='%s' "
                   "WHERE object_id=%ld ",
                   tr->sequence_id, timestamp, tr->class_type, str, tr->object_id);

    sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
    
    /* Check for affected rows. One row should be affected */
    num = mysql_affected_rows(tr->sql_connection);
    if (num < 1) {
         tr->error|=ERROR_U_DBS;
         tr->succeeded=0;
         if(sql_err) {
          fprintf(stderr, "E ERROR!<perform_update>: UPDATE last failed: [%d][%s]%s\n", num, query, SQ_error(tr->sql_connection));
          g_string_sprintfa(tr->error_script,"E[%d][:]:UPDATE last failed:%s\n" ,ERROR_U_DBS,SQ_error(tr->sql_connection));
         }
         else {
          fprintf(stderr, "E ERROR!<perform_update>: UPDATE last failed: [%d][%s]\n", num, query);
          g_string_sprintfa(tr->error_script,"E[%d][:]:UPDATE last failed\n" ,ERROR_U_DBS);
	  /* This is to check that this is really could happen */  
	  die;
         } 
         return(tr->succeeded);
    }
 return(tr->succeeded);   
} /* perform_update() */




/************************************************************
* int object_process(Transaction_t *tr)                     *
*                                                           *
* This is the interface between core and upper layer        *
* All it gets is Transaction *tr, which contains all        *
* necessary information, including the object in its        *
* internal representation.                                  *
*                                                           *
* Returns: tr->succeeded: >0 success, 0 - error             *
* Error code is stored in tr->error.                        *
*                                                           *
*************************************************************/ 
int object_process(Transaction_t *tr) 
{
int res;
char nic[MAX_NH_LENGTH];

   if(ACT_DELETE(tr->action)){
	 	fprintf(stderr, "D: Action: Delete...");
	 	delete(tr);
		/* Commit nic-handle deletion to the repository */
                if(tr->succeeded && ((tr->class_type==C_PN) || (tr->class_type==C_RO)) && tr->nh ){
      	         res = NH_free(tr->nh, tr->sql_connection);
		 if(res == -1) { 
		  tr->succeeded=0; 
		  tr->error |= ERROR_U_DBS;
		  g_string_sprintfa(tr->error_script,"E[%d][]:cannot delete nic-handle\n", ERROR_U_DBS); 
		  return(tr->succeeded);
	         }
		 else if(res == 0) { 
		  tr->succeeded=0; 
		  tr->error |= ERROR_U_OBJ;
		  g_string_sprintfa(tr->error_script,"E[%d][]:nic-handle not found\n", ERROR_U_OBJ); 
		  return(tr->succeeded);
	         }
                }
	 	return(tr->succeeded); /*commit is not needed*/
    }
    else if(ACT_UPDATE(tr->action)){	 	
	 	fprintf(stderr, "D: Action: Update...");
	 	perform_update(tr);
	 	/* Commit nic-handle allocation (if any) to the repository if we are replacing dummy*/
                if(ACT_UPD_NHR(tr->action) && tr->succeeded && ((tr->class_type==C_PN) || (tr->class_type==C_RO)) && tr->nh ){
                 /* convert nh to DB nIC handle before registration */
                 /* because there nh will bee freed */
                 NH_convert(nic, tr->nh);
      	         res = NH_register(tr->nh, tr->sql_connection);
		 if(res == -1) { 
		  tr->succeeded=0; 
		  tr->error |= ERROR_U_DBS;
		  g_string_sprintfa(tr->error_script,"E[%d][]:cannot allocate nic-handle\n", ERROR_U_DBS); 
	         }
		 else if(res == 0) { 
		  tr->succeeded=0; 
		  tr->error |= ERROR_U_OBJ;
		  g_string_sprintfa(tr->error_script,"E[%d][]:nic-handle already in use\n", ERROR_U_OBJ); 
	         }
	         else { /* copy the NH to the report to return to DBupdate */
	         /* Convert nh to the database format */     
	          g_string_sprintfa(tr->error_script,"I[%d][%s]\n", A_NH, nic);
	         }	         
                }
    }
    else if(ACT_CREATE(tr->action)){
	 	fprintf(stderr, "D: Action: Create...");
	 	perform_create(tr);
		/* Commit nic-handle allocation (if any) to the repository */
                if(tr->succeeded && ((tr->class_type==C_PN) || (tr->class_type==C_RO)) && tr->nh ){
                 /* convert nh to DB nIC handle before registration */
                 /* because there nh will bee freed */
                 NH_convert(nic, tr->nh);
      	         res = NH_register(tr->nh, tr->sql_connection);
		 if(res == -1) { 
		  tr->succeeded=0; 
		  tr->error |= ERROR_U_DBS;
		  g_string_sprintfa(tr->error_script,"E[%d][]:cannot allocate nic-handle\n", ERROR_U_DBS); 
	         }
		 else if(res == 0) { 
		  tr->succeeded=0; 
		  tr->error |= ERROR_U_OBJ;
		  g_string_sprintfa(tr->error_script,"E[%d][]:nic-handle already in use\n", ERROR_U_OBJ); 
	         }
	         else { /* copy the NH to the report to return to DBupdate */
	          g_string_sprintfa(tr->error_script,"I[%d][%s]\n", A_NH, nic);
	         }
                }
	
     }
     else {
	 	fprintf(stderr, "D: Action: Unknown...");
	 	tr->succeeded=0;
	 	tr->error|=ERROR_U_BADOP;
	 	return(tr->succeeded);
     }	 	

   if(tr->load_pass == 0) { /* not for fast loader*/
      if (tr->succeeded == 1) {
/*fprintf(stderr, "D: Commit transaction...\n");      */
        commit(tr);
      }
      else {
/*fprintf(stderr, "D: Roll back transaction...\n");      */
        rollback(tr);
      }
    }  
 return(tr->succeeded);   
} /* object_process() */

