modules/nh/nh.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- get_min_range
- NH_convert
- NH_parse
- NH_check
- NH_free
- NH_register
- free_nh
- get_range
- update_range
- create_range
#include <glib.h>
#include <stdio.h>
#include <strings.h>
#include <glib.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include "nh.h"
#include <stubs.h>
/*+ String sizes +*/
#define STR_S 63
#define STR_M 255
#define STR_L 1023
#define STR_XL 4095
#define STR_XXL 16383
#define STR_XXXL 65535
#define get_min_range(prange, sql_connection) get_range(-1, prange, sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
static long get_range(long nic_id, range_t *prange, SQ_connection_t *sql_connection);
static long update_range(long range_id, range_t *p_newrange, SQ_connection_t *sql_connection);
static long create_range(range_t *p_range, SQ_connection_t *sql_connection);
/************************************************************
* int NH_convert() *
* *
* Converts space & nic_id into a database nic-handle *
* *
* *
* Returns: *
* The size of the nic_handle in characters *
* *
************************************************************/
int NH_convert(char *nic, nic_handle_t *nh_ptr)
/* [<][>][^][v][top][bottom][index][help] */
{
/* Check for special cases */
/* Is is and AUTO nic-handle ? */
if(nh_ptr->nic_id == AUTO_NIC_ID) return(-1);
if(nh_ptr->space) nic+=sprintf(nic, "%s", nh_ptr->space);
/* No nic-id ? */
if(nh_ptr->nic_id != NULL_NIC_ID) nic+=sprintf(nic, "%ld", nh_ptr->nic_id);
/* No source ? */
if (nh_ptr->source) sprintf(nic, "%s", nh_ptr->source);
return(1);
}
/************************************************************
* int NH_parse() *
* *
* Parse a nic handle as supplied by DBupdate *
* The format is: <space>[<nic_id>|*][SOURCE] *
* Also extracts nic_id and space for regular nic-handles *
* *
* *
* Returns: *
* >0 - success *
* 0 - AUTO NIC *
* -1 - error (not defined and processed yet) *
* *
************************************************************/
int NH_parse(char *nic, nic_handle_t **nh_ptr_ptr)
/* [<][>][^][v][top][bottom][index][help] */
{
char *ptr;
int res = 1;
nic_handle_t *nh_ptr;
if(!(nh_ptr=calloc(1, sizeof(nic_handle_t)))) die;
ptr=nic;
/* extract space */
while(isalpha((int)*ptr))ptr++;
if(!(nh_ptr->space=malloc(ptr-nic+1))) die;
strncpy(nh_ptr->space, nic, ptr-nic); *(nh_ptr->space+(ptr-nic))='\0';
/* If there are no digits, then this is no nic-hdl */
/* We reserve NULL_NIC_ID for such pretty identifiers */
if(*ptr == '\0') {
nh_ptr->nic_id=NULL_NIC_ID;
nh_ptr->source=NULL;
}
else {
/* Check if it is and AUTO nic */
if (*ptr == '*') {
/* For AUTO nic_id we reserve AUTO_NIC_ID */
nh_ptr->nic_id=AUTO_NIC_ID;
res=0;
ptr++;
} else {
nic=ptr;
/* convert digits (if any) and store first invalid characted in ptr */
nh_ptr->nic_id=(int)strtol(nic, &ptr, 10);
/* Check if there were any digits at all */
if(ptr == nic) nh_ptr->nic_id=NULL_NIC_ID;
}
/* check if there is any suffix */
if (*ptr == '\0') nh_ptr->source=NULL;
/* Copy suffix into source */
else {
if(!(nh_ptr->source=malloc(strlen(ptr)+1))) die;
strcpy(nh_ptr->source, ptr);
}
}
*nh_ptr_ptr=nh_ptr;
return(res);
}
/************************************************************
* int NH_check() *
* *
* Check a NIC handle in the repository *
* *
* *
* Returns: *
* 1 - success *
* 0 - error(nic_id exists or space is fully occupied) *
* -1 - error (f.e. more than one object with the same PK) *
* *
************************************************************/
int NH_check(nic_handle_t *nh_ptr, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
{
range_t range;
long range_id;
long nic_id=nh_ptr->nic_id;
range.space=nh_ptr->space;
if(nh_ptr->source)range.source=nh_ptr->source; else range.source="";
if (nic_id == AUTO_NIC_ID) {
/* NIC handle is an AUTO one */
/* get first range (with min range_end) for a given space */
range_id = get_min_range(&range, sql_connection);
if(range_id<0) return(-1); /* in case of an error */
if ( range_id==0 ) {
/* Nothing found */
/* Allocate a hic-hdl in a new space with the first range {0-1} in it*/
nic_id=1;
} else {
if ( range.end == MAX_NIC_ID ) return(0); /* space is fully occupied */
/* attach to range and may be join with next */
nic_id = range.end+1;
}
}
/* if not AUTO */
else {
range_id = get_range(nic_id, &range, sql_connection);
if(range_id <0) return(-1); /* in case of an error */
if(range_id!=0) return(0); /* this nic_id already exists */
}
nh_ptr->nic_id=nic_id;
return(1);
}
/************************************************************
* long NH_free() *
* *
* Delete a NIC handle from the repository *
* *
* To finalize changes make commit/rollback *
* *
* Returns: *
* 1 - success *
* 0 - error (range is not founnd) *
* -1 - error (f.e. more than one object with the same PK) *
* *
************************************************************/
int NH_free(nic_handle_t *nh_ptr, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
{
range_t range;
long range_id;
int old_start;
long nic_id=nh_ptr->nic_id;
range.space=nh_ptr->space;
if(nh_ptr->source)range.source=nh_ptr->source; else range.source="";
/* Search for the range containing the nic-handle */
range_id = get_range(nic_id, &range, sql_connection);
/* If range is not found or an error occcured - return */
if(range_id==0) { return(0); }
if(range_id<0) { return(-1); }
if(nic_id == range.start) {
/* update range start and may be detele range and space */
range.start+=1;
range_id=update_range(range_id, &range, sql_connection);
if(range_id<=0) { return(-1); }
}
else if(nic_id == range.end) {
/* update range end and may be detele range and space */
range.end-=1;
range_id=update_range(range_id, &range, sql_connection);
if(range_id<=0) { return(-1); }
}
else {
/* split the range into two */
/* shrink the old one */
old_start=range.start;
range.start=nic_id+1;
range_id=update_range(range_id, &range, sql_connection);
if(range_id<=0) { return(-1); }
/* create a new one */
range.start=old_start;
range.end=nic_id-1;
range_id=create_range(&range, sql_connection);
if(range_id<=0) { return(-1); }
}
return(1);
}
/************************************************************
* int NH_register() *
* *
* Get a NIC handle from the repository *
* *
* *
* Returns: *
* 1 - success *
* 0 - nic_id already exists or space is fully occupied *
* -1 - error (f.e. more than one object with the same PK) *
* *
************************************************************/
int NH_register(nic_handle_t *nh_ptr, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
{
range_t range;
long range_id;
long nic_id=nh_ptr->nic_id;
/* Yiu should check for nh first for AUTO nic-handles */
if (nic_id == AUTO_NIC_ID) { return(0); };
range.space=nh_ptr->space;
if(nh_ptr->source)range.source=nh_ptr->source; else range.source="";
range_id = get_range(nic_id, &range, sql_connection);
if(range_id <0) { return(-1); } /* in case of an error */
if(range_id!=0) { return(0); } /* this nic_id already exists */
/* check if we can attach to existing next range */
range_id = get_range(nic_id+1, &range, sql_connection);
if(range_id <0) { return(-1); } /* in case of an error */
if( range_id>0 ) {
/* attach to range and may be join with previous */
range.start-=1;
range_id=update_range(range_id, &range, sql_connection);
if(range_id<=0) { return(-1); }
}
else {
/* check if we can attach to existing previous range */
if(nic_id>0) range_id = get_range(nic_id-1, &range, sql_connection);
else range_id=0; /* there is no previous range in this case (nic_id==0) */
if(range_id <0) { return(-1); } /* in case of an error */
if( range_id>0 ) {
/* attach to range and may be join with next */
range.end+=1;
range_id=update_range(range_id, &range, sql_connection);
if(range_id<=0) { return(-1); }
}
else {
/* If we cannot attach to any existing range - create new {nic_id-nic_id} */
range.end=range.start=nic_id;
range_id=create_range(&range, sql_connection);
if(range_id <=0) { return(-1); } /* in case of an error */
}
}
return(1);
}
/*
Free nic_handle_t structure
*/
void free_nh(nic_handle_t *nh_ptr)
/* [<][>][^][v][top][bottom][index][help] */
{
if(nh_ptr){
if(nh_ptr->space)free(nh_ptr->space);
if(nh_ptr->source)free(nh_ptr->source);
free(nh_ptr);
}
}
/************************************************************
* long get_range() *
* *
* Searches for the range of the space containing *
* the specified nic_id *
* *
* To request to search for the firt (min) range, nic_id *
* should be set to -1. *
* *
* Returns: *
* >0 - range exists, returns range_id *
* 0 - range does not exist *
* -1 - DB error (f.e. more than one object with the same PK)*
* *
* **********************************************************/
static long get_range(long nic_id, range_t *prange, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
{
SQ_result_set_t *sql_result;
SQ_row_t *sql_row;
char *sql_str;
GString *query;
long range_id=0;
int sql_err;
if ((query = g_string_sized_new(STR_L)) == NULL){
fprintf(stderr, "E: cannot allocate gstring\n");
return(-1);
}
/* Define row numbers in the result of the query */
#define RANGE_ID 0
#define RANGE_START 1
#define RANGE_END 2
if (nic_id<0) {
/* requesting the first (min) range */
g_string_sprintf(query, "SELECT range_id, range_start, range_end "
"FROM nic_hdl "
"WHERE space='%s' "
"AND source='%s' "
"AND (range_start=0 "
"OR range_start=1) ",
prange->space, prange->source);
} else {
g_string_sprintf(query, "SELECT range_id, range_start, range_end "
"FROM nic_hdl "
"WHERE space='%s' "
"AND source='%s' "
"AND range_start<=%ld "
"AND range_end>=%ld ",
prange->space, prange->source, nic_id, nic_id);
}
/* execute query */
/* fprintf(stderr, "get_range[%s]\n", query->str); */
sql_err=SQ_execute_query(sql_connection, query->str, &sql_result);
g_string_free(query, TRUE);
if(sql_err) {
fprintf(stderr,"ERROR: %s\n", SQ_error(sql_connection));
return(-1);
}
if ((sql_row = SQ_row_next(sql_result)) != NULL) {
/* Object exists */
sql_str = SQ_get_column_string(sql_result, sql_row, RANGE_ID);
if (sql_str != NULL) {
range_id = atol(sql_str);
free(sql_str);
}
sql_str = SQ_get_column_string(sql_result, sql_row, RANGE_START);
if (sql_str != NULL) {
prange->start = atoi(sql_str);
free(sql_str);
}
sql_str = SQ_get_column_string(sql_result, sql_row, RANGE_END);
if (sql_str != NULL) {
prange->end = atoi(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) range_id=-1;
} else
range_id=0; // object does not exist
if(sql_result)SQ_free_result(sql_result);
return(range_id);
}
/************************************************************
* long update_range() *
* *
* Updates the range by changing the boundaries *
* Deletes the range if nothing left *
* Merges with neighbor ranges if there is no gap between *
* *
* We never update range. We create a new one with specified *
* limits and mark old one(s) for deletion, so that we can *
* make commit/rollback properly. This is possible as the *
* primary keys are (range_id, range_start, range_end) *
* *
* To finalize changes make commit/rollback *
* *
* Returns: *
* >0 - returns range_id on success *
* -1 - error (f.e. more than one object with the same PK) *
* *
************************************************************/
static long update_range(long range_id, range_t *p_newrange, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
{
GString *query;
range_t range;
long prev_range_id, next_range_id;
int num;
int sql_err;
/* Allocate memory */
if ((query = g_string_sized_new(STR_L)) == NULL){
fprintf(stderr, "E: cannot allocate gstring\n");
return(-1);
}
/* Do range check */
if (( p_newrange->end > MAX_RANGE ) || ( p_newrange->start < MIN_RANGE )) return(-1);
/* Check if the range collapses */
if ( p_newrange->end < p_newrange->start ) {
/* then delete the range */
/* Do this by marking the range for deletion for further commit/rollback */
g_string_sprintf(query, "DELETE FROM nic_hdl "
"WHERE range_id=%ld ",
range_id);
/* fprintf(stderr, "update_range[%s]\n", query->str); */
sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
if(sql_err) {
/* An error occured */
g_string_free(query, TRUE);
return(-1);
}
num = mysql_affected_rows(sql_connection);
/* this should not happen */
if(num==0) die;
}
else {
/* update the range for the same space/source */
range.space=p_newrange->space;
range.source=p_newrange->source;
/* Check if we can join with previous range of the same space */
prev_range_id=get_range(p_newrange->start-1, &range, sql_connection);
/* Check if such range exists and it is not ours (this happens when we are shrinking */
if((prev_range_id>0) && (prev_range_id!=range_id)) {
/* acquire the previous range */
/* mark it for deletion for commit/rollback */
g_string_sprintf(query, "DELETE FROM nic_hdl "
"WHERE range_id=%ld ",
prev_range_id);
/* fprintf(stderr, "update_range[%s]\n", query->str); */
sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
if(sql_err) {
/* An error occured */
g_string_free(query, TRUE);
return(-1);
}
num = mysql_affected_rows(sql_connection);
/* this should not happen */
if(num==0) die;
/* expand the boundaries */
p_newrange->start=range.start;
}
/* Check if we can join with next range of the same space */
next_range_id=get_range(p_newrange->end+1, &range, sql_connection);
/* Check if such range exists and it is not ours (this happens when we are shrinking) */
if((next_range_id>0) && (next_range_id!=range_id)) {
/* acquire the next range */
/* mark it for deletion for commit/rollback */
g_string_sprintf(query, "DELETE FROM nic_hdl "
"WHERE range_id=%ld ",
next_range_id);
/* fprintf(stderr, "update_range[%s]\n", query->str); */
sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
if(sql_err) {
/* An error occured */
g_string_free(query, TRUE);
return(-1);
}
num = mysql_affected_rows(sql_connection);
/* this should not happen */
if(num==0) die;
/* expand the boundaries */
p_newrange->end=range.end;
}
/* Now make a larger range. Mark it for commit/rollback */
g_string_sprintf(query, "UPDATE nic_hdl "
"SET range_start=%ld, range_end=%ld "
"WHERE range_id=%ld",
p_newrange->start, p_newrange->end, range_id);
/* fprintf(stderr, "update_range[%s]\n", query->str); */
sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
if(sql_err) {
/* An error occured */
g_string_free(query, TRUE);
return(-1);
}
num = mysql_affected_rows(sql_connection);
/* this should not happen */
if(num==0) die;
} /* update the range */
g_string_free(query, TRUE);
return (range_id);
}
/************************************************************
* long create_range() *
* *
* Creates a new range in a given name space *
* *
* To finalize changes make commit/rollback *
* *
* Returns: *
* >0 - returns range_id on success *
* -1 - error (f.e. more than one object with the same PK) *
* *
************************************************************/
static long create_range(range_t *p_range, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
{
GString *query;
int sql_err, num;
/* Allocate memory */
if ((query = g_string_sized_new(STR_L)) == NULL){
fprintf(stderr, "E: cannot allocate gstring\n");
return(-1);
}
g_string_sprintf(query, "INSERT nic_hdl "
"SET space='%s', source='%s', range_start=%ld, range_end=%ld ",
p_range->space, p_range->source, p_range->start, p_range->end);
/* fprintf(stderr, "create_range[%s]\n", query->str); */
sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
g_string_free(query, TRUE);
if(sql_err) {
/* An error occured */
return(-1);
}
num = mysql_affected_rows(sql_connection);
/* this should not happen */
if(num==0) die;
return(mysql_insert_id(sql_connection));
}