static	char	RuleLockUtilities_c[] = "$Header: util1.c,v 1.1 89/02/20 16:27:27 hirohama Exp $";

/*----------------------------------------------------------------------
 * 
 * Some Useful Routines that operate on locks stored in
 * "intermediate" format.
 */

#include <stdio.h>
#include "postgres.h"

#include "log.h"

#include "rlock.h"
#include "internal.h"


/*----------------------------------------------------------
 * 
 * FindRuleLockSize
 *   Takes as an argument the "intermediate" representation of 
 * a lock, and calculates the number of rule-packs and the number of
 * locks.
 *
 */

FindRuleLockSize(intermdt, count_rulepacks, count_locks)
    RuleLockIntermediate *intermdt;
    long *count_rulepacks;
    long *count_locks;
{
    RuleLockIntermediate *r;
    RuleLockIntermediateLockData *l;

    *count_rulepacks = 0;
    *count_locks = 0;

    for (r=intermdt; r!=NULL; r=r->next_rulepack) {
	(*count_rulepacks) ++;
	for (l=r->locks; l!=NULL; l=l->next_lock) {
	    (*count_locks) ++;
	}
    }

}

/*----------------------------------------------------------
 * 
 * intermdt_to_intern
 *   Transform from the "intermediate" form to the "internal".
 * The argument is the rule lock in "intermediate" form and
 * the returned value is the "internal" form, i.e. a pointer
 * to a struct of type "RuleLockData". The space occupied by this
 * struct has been allocated by this routine, and can be freed
 * when nesceassary by the caller...
 */

RuleLock
RuleLockIntermediateToInternal (intermdt)
    RuleLockIntermediate *intermdt;
{
    long size;
    long datasize;
    long count_headers;
    long count_locks;
    RuleLockIntermediate *r;
    RuleLockIntermediateLockData *l;
    RuleLock result;
    char *p;

    /*
     * Calculate size of the "RuleLockData" structure. Note that
     * this size includes the first field of this structure,
     * which is a long integer specifying the length...
     */
    FindRuleLockSize(intermdt, &count_headers, &count_locks);
    datasize = count_headers * sizeof(RuleLockHeader) +
	       count_locks * sizeof(RuleLockBody);
    size = sizeof(result->lockLength) + datasize;

    result = (RuleLock)MyAlloc((unsigned long)size);
    if (result==NULL) {
	elog(WARN,
	     "Error in intermdt-to-intern, couldn't alloc %ld bytes\n",
	     size);
	exit(1);
    }

    result->lockLength = datasize;

    p = (char *) result->lockData;
    for (r=intermdt; r!=NULL; r=r->next_rulepack) {
	/*
	 * Copy the header info, set the 8th bit if this is the 
	 * last rule-pack  and update "p" to point to the next 
	 * free byte of the buffer result->lockData.
	 */
	((RuleLockHeader *)p)->ruleid = r->ruleid; 
	((RuleLockHeader *)p)->priority = r->priority; 
	((RuleLockHeader *)p)->ruletype = r->ruletype; 
	if (r->isearly) {
	    ((RuleLockHeader *)p) -> ruletype |= RuleLockEarlyMask;
	}
	if (r->next_rulepack == NULL) {
	    ((RuleLockHeader *)p) -> ruletype |= RuleLockEndMask;
	}
	((RuleLockHeader *)p) ++;

	for (l=r->locks; l!=NULL; l=l->next_lock) {
	    ((RuleLockBody *)p)-> locktype = l->locktype;
	    ((RuleLockBody *)p)-> attrno = l->attrno;
	    ((RuleLockBody *)p)-> planno = l->planno;
	    if (l->next_lock == NULL) {
		((RuleLockBody *)p) -> locktype |= RuleLockEndMask;
	    }
	    ((RuleLockBody *)p) ++;
	}
    }

    return(result);
}

/*----------------------------------------------------------
 *
 * intern_to_intermdt
 *    Transform from the "internal" representation to the
 * "intermediate". Space allocated for the "intermediate" representation
 * can be freed by a call to "free_intermdt".
 */

RuleLockIntermediate *
RuleLockInternalToIntermediate(intern)
    RuleLock intern;
{
    RuleLockIntermediate *result;
    RuleLockIntermediate *r;
    RuleLockIntermediateLockData *l;
    RuleLockIntermediate **rr;
    RuleLockIntermediateLockData **ll;
    bool is_lastheader;
    bool is_lastlock;
    char *p;


    if (intern==NULL || intern->lockLength == 0) {
	/*
	 * Empty lock...
	 */
	return(NULL);
    }

    rr = &result;
    p = (char *) intern->lockData;
    do {
	/*
	 * "p" is pointing to a header. Aloccate space for
	 * a new "RuleLockIntermediate" and copy the header information
	 */
	r = (RuleLockIntermediate *)MyAlloc((unsigned long)
					    sizeof(RuleLockIntermediate));
	if (r==NULL) {
	    elog(WARN,
		"Error in intern-to-intermdt, couldn't alloc %ld bytes\n",
		sizeof(RuleLockIntermediate));
	    exit(1);
	}
	/*
	 * Make the previous "RuleLockIntermediate" in the list point to
	 * the one just created.
	 */
	(*rr) = r;
	rr = &(r->next_rulepack);

	r->ruleid = ((RuleLockHeader *)p)->ruleid;
	r->priority = ((RuleLockHeader *)p)->priority;
	r->ruletype = ((RuleLockHeader *)p)->ruletype & RuleLockRuleTypeMask;
	r->isearly = (bool)(((RuleLockHeader *)p)->ruletype &
			      RuleLockEarlyMask);
	is_lastheader = (bool)(((RuleLockHeader *)p)->ruletype &
				 RuleLockEndMask);
	if (is_lastheader)
	    r->next_rulepack = NULL;
	((RuleLockHeader *)p)++;


	ll = &(r->locks);
	do {
	    /*
	     * Now "p" points to a "RuleLockBody".
	     */
	    l = (RuleLockIntermediateLockData *)
		 MyAlloc((unsigned long)sizeof(RuleLockIntermediateLockData));
	    if (r==NULL) {
		elog(WARN,
		    "Error in intern-to-intermdt, couldn't alloc %ld bytes\n",
		    sizeof(RuleLockIntermediateLockData));
		exit(1);
	    }
	    (*ll) = l;
	    ll = &(l->next_lock);

	    l->planno = ((RuleLockBody *)p)->planno;
	    l->attrno = ((RuleLockBody *)p)->attrno;
	    l->locktype=((RuleLockBody *)p)->locktype & RuleLockLockTypeMask;
	    is_lastlock = (bool)(((RuleLockBody *)p)->locktype &
				   RuleLockEndMask);
	    if (is_lastlock)
		l->next_lock = NULL;
	    ((RuleLockBody *)p)++;
	} while (!is_lastlock);
    } while (!is_lastheader);

    return(result);
}


/*---------------------------------------------------------------------
 * 
 * RuleLockIntermediateSort
 *    The "intermediate" form of locks is a linked list of
 * "RuleLockIntermediate" sorted in rule priority order (higher priority
 * first). To assure that this is the case, here is a (crude) sort 
 * routine...
 *
 * NOTE that the original list is rearranged, and the routine returns
 * a pointer to the first record of the list. However the original
 * pointer which is passed as an argument may NOT poin to the first
 * record of the new sorted list... (Why? we leave this as an exercise to
 * the reader...) Therefore this routine must be * called as follows:
 *    pack_list = RuleLockIntermediateSort(pack_list);
 * where "pack_list" is a pointer to "RuleLockIntermediate".
 *
 */

RuleLockIntermediate *
RuleLockIntermediateSort(rulepacklist)
    RuleLockIntermediate *rulepacklist;
{
    RuleLockIntermediate *result;
    bool packs_are_sorted;
    bool locks_are_sorted;
    RuleLockIntermediate *r;
    RuleLockIntermediate **rr;
    RuleLockIntermediateLockData **ll;
    RuleLockIntermediate *pack_temp;
    RuleLockIntermediateLockData *lock_temp;

    if (rulepacklist==NULL){
	/*
	 * No locks at all...
	 */
	return(NULL);
    }

    /*
     * First sort all the locks of each RuleLockIntermediate 
     * in increasing attrno/planno order.
     */
    for ( r = rulepacklist ; r != NULL; r = r->next_rulepack ) {
	/*
	 * For this "RuleLockIntermediate", sort all its 
	 * locks ("RuleLockIntermediateLockData") by attrno/planno order.
	 */
	do {
	    locks_are_sorted = true;
	    for (ll = &(r->locks); (*ll)->next_lock!=NULL;
		    ll = &((*ll)->next_lock) ) {
		if ((*ll)->attrno > (*ll)->next_lock->attrno ||
		    (*ll)->attrno == (*ll)->next_lock->attrno &&
		    (*ll)->planno > (*ll)->next_lock->planno ){
		    /*
		     * Excahnge locks...
		     */
		    locks_are_sorted = false;
		    lock_temp = *ll;
		    (*ll) = lock_temp->next_lock;
		    lock_temp->next_lock = (*ll)->next_lock;
			(*ll)->next_lock = lock_temp;
		} /*if*/
	    } /*for*/
	} while (!locks_are_sorted);
    }

    /*
     * Now sort the RuleLockIntermediates in decreasing priority order.
     */

    result = rulepacklist;
    do {
	packs_are_sorted = true;

	/*
	 * Do a bubble sort...
	 *
	 * "*rr" is a pointer to a "RuleLockIntermediate"...
	 */
	for ( rr = (&result) ; (*rr)->next_rulepack != NULL;
	      rr = &((*rr)->next_rulepack) ) {

	    if ((*rr)->priority < (*rr)->next_rulepack->priority) {
		/*
		 * Exchange the two RuleLockIntermediates
		 */
		packs_are_sorted = false;
		pack_temp = *rr;
		(*rr) = pack_temp->next_rulepack;
		pack_temp->next_rulepack = (*rr)->next_rulepack;
		(*rr)->next_rulepack = pack_temp;
	    }
	} /*for*/
    } while (!packs_are_sorted);

    return(result);
}


/*----------------------------------------------------------------------
 * 
 * RuleLockIntermediateCopy
 *    Make a copy of the linked list of "RuleLockIntermediate" which is passed
 * as an argument..
 */

RuleLockIntermediate *
RuleLockIntermediateCopy(x)
    RuleLockIntermediate *x;
{
    RuleLockIntermediate *copy;
    RuleLockIntermediate *r;
    RuleLockIntermediateLockData *l;
    RuleLockIntermediate *new_r;
    RuleLockIntermediateLockData *new_l;
    RuleLockIntermediate **rr;
    RuleLockIntermediateLockData **ll;

    copy = NULL;
    rr = &copy;

    for (r= x; r!=NULL; r=r->next_rulepack) {
	new_r = (RuleLockIntermediate *)
		MyAlloc((unsigned long)sizeof(RuleLockIntermediate));
	if (new_r==NULL) {
	    elog(WARN,
		"Error in RuleLockIntermediateCopy: couldn;t alloc %d bytes\n",
		sizeof(RuleLockIntermediateLockData));
	    exit(1);
	} /*if*/
	(*rr) = new_r;
	rr = &(new_r->next_rulepack);
	new_r->next_rulepack = NULL;
	new_r->ruleid = r->ruleid;
	new_r->ruletype = r->ruletype;
	new_r->priority = r->priority;
	new_r->isearly = r->isearly;

	ll = &(new_r->locks);
	for(l=r->locks; l!=NULL; l=l->next_lock) {
	    new_l = (RuleLockIntermediateLockData *)
		    MyAlloc((unsigned long)
			    sizeof(RuleLockIntermediateLockData));
	    if (new_l==NULL) {
		elog(WARN,
		    "Error in RuleLockIntermediateCopy: couldn;t alloc %d bytes\n",
		    sizeof(RuleLockIntermediateLockData));
		exit(1);
	    } /*if*/
	    (*ll) = new_l;
	    ll = &(new_l->next_lock);
	    new_l->next_lock = NULL;
	    new_l->locktype = l->locktype;
	    new_l->planno = l->planno;
	    new_l->attrno = l->attrno;
	}/*for*/
    }/*for*/

    return(copy);
}


/*----------------------------------------------------------------------
 *
 * RuleLockIntermediateFree
 *   Free the space used by a linked list of "RuleLockIntermediate" and
 * the corresponding "RuleLockIntermediateLockData".
 */

void
RuleLockIntermediateFree(x)
    RuleLockIntermediate *x;
{
    RuleLockIntermediate *r;
    RuleLockIntermediateLockData *l;
    RuleLockIntermediate *r2;
    RuleLockIntermediateLockData *l2;

    for (r= x; r!=NULL; r=r2) {
	for(l=r->locks; l!=NULL; l=l2) {
	    l2 = l->next_lock;
	    MyFree(l);
	}
	r2 = r->next_rulepack;
	MyFree(r);
    }
}

/*----------------------------------------------------------------------
 *
 * RuleLockIntermediateUnion
 *    Finds and returns the union of two linked lists of
 * "RuleLockIntermediate". These two lists must be  sorted by rule-priority
 * order. For each rule-pack (uniquely identified by its ruleid) the
 * list of locks must be sorted by increasing "attrno" order and if thw
 * attrnos are equal by increasing planno order.
 *
 * Both lists are rearranged/destroyed so you may need to make
 * a copy of them (via "copy_rulelock"). The value returned is
 * a pointer to the first struct of the union.
 *
 * NOTE: Not only the "next_rulepack" and "next_lock" pointers will
 * be rearranged, but some "RuleLockIntermediate" may be freed also! (if
 * they belong to rules that appear in both lists)
 */

RuleLockIntermediate *
RuleLockIntermediateUnion(r1, r2)
    RuleLockIntermediate *r1;
    RuleLockIntermediate *r2;
{
    RuleLockIntermediate *result;
    RuleLockIntermediate **rr;
    RuleLockIntermediate *temp;
    RuleLockIntermediateLockData * RuleLockIntermediateSingleLockUnion();

    result = NULL;
    rr = & result;

    while (r1!=NULL && r2!=NULL) {
	if (r1->ruleid == r2->ruleid) {
	    /*
	     * This rule appears in both lists. Merge its locks,
	     * use one of the two headers and free the other..
	     */
	    r1->locks = RuleLockIntermediateSingleLockUnion(r1->locks,
							    r2->locks);
	    *rr = r1;
	    rr = &(r1->next_rulepack);
	    r1 = r1->next_rulepack;
	    temp = r2->next_rulepack;
	    MyFree(r2);
	    r2 = temp;

	} else if (r1->priority >= r2->priority) {
	    *rr = r1;
	    rr = &(r1->next_rulepack);
	    r1 = r1->next_rulepack;
	} else {
	    temp = r1;
	    r1 = r2;
	    r2 = temp;
	}
    }

    /*
     * One of the "r1", "r2" (or both) is NULL.
     * Copy the remainong non null list to result...
     */
    if (r1!=NULL) {
	*rr = r1;
    } else {
	*rr = r2;
    }

    return(result);

}

RuleLockIntermediateLockData *
RuleLockIntermediateSingleLockUnion(l1,l2)
    RuleLockIntermediateLockData *l1;
    RuleLockIntermediateLockData *l2;
{

    RuleLockIntermediateLockData *result;
    RuleLockIntermediateLockData **ll;
    RuleLockIntermediateLockData *temp;

    result = NULL;
    ll = &result;

    while (l1!=NULL && l2!=NULL) {
	if (l1->attrno == l2->attrno && l1->planno ==l2->planno) {
	    /*
	     * This lock appears in both lists. 
	     */
	    *ll = l1;
	    ll = &(l1->next_lock);
	    l1 = l1->next_lock;
	    l2 = l2->next_lock;
	    MyFree(l2);

	} else if (l1->attrno < l2->attrno ||
	    (l1->attrno == l2->attrno && l1->planno < l2->planno)) {
	    *ll = l1;
	    ll = &(l1->next_lock);
	    l1 = l1->next_lock;
	} else {
	    temp = l1;
	    l1 = l2;
	    l2 = temp;
	}
    }

    /*
     * One of the "l1", "l2" (or both) is NULL.
     * Copy the remaining non null list to result...
     */
    if (l1!=NULL) {
	*ll = l1;
    } else {
	*ll = l2;
    }

    return(result);

}

/*----------------------------------------------------------------------
 * 
 * RuleLockIntermediateRemove
 *    Remove a single lock from a linked list of "RuleLockIntermediate".
 * We must specify the ruleid and all the lock data of th elock to be
 * removed (loctype, attrno, planno). If this happens to be the last
 * lock of the rule, then the correpsonding "RuleLockIntermediate" is removed.
 * The space occupied by all removed lock/headers is freed.
 *
 * NOTE: If the lock removal causes the removal of the first
 * "RuleLockIntermediate" of the list, then the pointer passed as argument
 * will be daggling. Therefore this routine must be used as follows...
 *
 * list = RuleLockIntermediateRemove(list, ruleid, locktype, planno,
 *                                   attrno, &status);
 *    RuleLockIntermediate *list;
 *    ObjectId ruleid;
 *    char locktype;
 *    long planno, attrno;
 *    int status
 *
 * The output argument 'status' is a pointer to an integer status variable
 * which take the value 1 if the removal has benn completed, or
 * 0 if the lock to be removed was not found in the list.
 *
 */

RuleLockIntermediate *
RuleLockIntermediateRemove(x, this_ruleid, this_locktype, this_attrno,
		    this_planno, status)
    RuleLockIntermediate *x;
    ObjectId this_ruleid;
    char this_locktype;
    unsigned long this_attrno;
    unsigned long this_planno;
    int *status;
{
    RuleLockIntermediate *result;
    RuleLockIntermediate **rr;
    RuleLockIntermediateLockData **ll;
    RuleLockIntermediateLockData *temp;
    RuleLockIntermediate *temp2;

    *status = 0;

    if (x==NULL)
	return(NULL);

    result = x;

    for (rr = &result; (*rr)!=NULL; /* do nothing */ ) {
	if (this_ruleid == (*rr)->ruleid) {
	    for (ll = &((*rr)->locks); (*ll)!=NULL; /* do nothing */) {
		if (this_locktype == (*ll)->locktype &&
			this_attrno == (*ll)->attrno &&
			this_planno == (*ll)->planno) {
		    temp = *ll;
		    (*ll) = (*ll)->next_lock;
		    MyFree(temp);
		    *status = 1;
		} else {
		    ll = &((*ll)->next_lock);
		} /*if-then-else*/
	    } /*for*/
	    if ((*rr)->locks == NULL) {
		/*
		 * This RuleLockIntermediate has no locks.
		 * Remove it.
		 */
		temp2 = (*rr);
		(*rr) = (*rr)->next_rulepack;
		MyFree (temp2);
	    } else {
		rr = &((*rr)->next_rulepack);
	    } /* if-then-else */
	} else {
	    rr = &((*rr)->next_rulepack);
	}
    } /*for*/

    return(result);
}


/*-----------------------------------------------------------
 * 
 * RuleLockIntermediatePrint
 * (used for debugging purposes...)
 *
 */
void
RuleLockIntermediatePrint(fp, x)
    FILE *fp;
    RuleLockIntermediate *x;
{

    RuleLockIntermediate *r;
    RuleLockIntermediateLockData *l;

    fprintf(fp,"PRINT_INTERMDT[%lo]:",(long)x);

    if (x==NULL) {
	fprintf(fp,"NULL\n");
    }

    fprintf(fp,"[");
    for (r=x; r!=NULL; r=r->next_rulepack) {
	fprintf(fp,"\n{ruleid: %ld,priority: %d, ruletype: %d, isearly: %d ",
		(long)r->ruleid, (int)r->priority,
		(int)r->ruletype, (int)r->isearly);
	for (l=r->locks; l!=NULL; l=l->next_lock) {
	    fprintf(fp,"\n\t (locktype: %d, attrno: %ld, planno: %ld)", (int)l->locktype,
		    l->attrno, l->planno);
	}
	fprintf(fp,"}");
    }

    fprintf(fp,"].\n");
}

/*----------------------------------------------------------------------
 * 
 * RuleLockIntermediateRemoveEarlyWrite
 *    Remove all Early Write lock from a linked list of "RuleLockIntermediate".
 * The space occupied by all removed lock/headers is freed.
 *
 * NOTE: If the lock removal causes the removal of the first
 * "RuleLockIntermediate" of the list, then the pointer passed as argument
 * will be daggling. Therefore this routine must be used as follows...
 *
 * list = RuleLockIntermediateRemove(list)
 *    RuleLockIntermediate *list;
 *
 */

RuleLockIntermediate *
RuleLockIntermediateRemoveEarlyWrite(x)
    RuleLockIntermediate *x;
{
    RuleLockIntermediate *result;
    RuleLockIntermediate **rr;
    RuleLockIntermediateLockData **ll;
    RuleLockIntermediateLockData *temp;
    RuleLockIntermediate *temp2;

    if (x==NULL)
	return(NULL);

    result = x;

    for (rr = &result; (*rr)!=NULL; /* do nothing */ ) {
	if  ((*rr)->isearly) {
	    for (ll = &((*rr)->locks); (*ll)!=NULL; /* do nothing */) {
		if (RuleLockTypeWrite== (*ll)->locktype) {
		    temp = *ll;
		    (*ll) = (*ll)->next_lock;
		    MyFree(temp);
		} else {
		    ll = &((*ll)->next_lock);
		} /*if-then-else*/
	    } /*for*/
	    if ((*rr)->locks == NULL) {
		/*
		 * This RuleLockIntermediate has no locks.
		 * Remove it.
		 */
		temp2 = (*rr);
		(*rr) = (*rr)->next_rulepack;
		MyFree (temp2);
	    } else {
		rr = &((*rr)->next_rulepack);
	    } /* if-then-else */
	} else {
	    rr = &((*rr)->next_rulepack);
	}
    } /*for*/

    return(result);
}



/*----------------------------------------------------------------------
 * 
 * RuleLockIntermediateCopyEarlyWriteLocks
 *
 * It takes as argumenta a RuleLockIntermediate and returns
 * another (new) RuleLockIntermediate consisting of *copies*
 * of all the early write locks passed of the RulelOckIntermediate
 * passed as its argument. (NOTE: the result might be NULL if no EW lock
 * is found)
 *
 */

RuleLockIntermediate *
RuleLockIntermediateCopyEarlyWriteLocks(x)
    RuleLockIntermediate *x;
{
    RuleLockIntermediate *copy;
    RuleLockIntermediate *r;
    RuleLockIntermediateLockData *l;
    RuleLockIntermediate *new_r;
    RuleLockIntermediateLockData *new_l;
    RuleLockIntermediate **rr;
    RuleLockIntermediateLockData **ll;

    copy = NULL;
    rr = &copy;
    new_r = NULL;

    for (r= x; r!=NULL; r=r->next_rulepack) {
	for(l=r->locks; l!=NULL; l=l->next_lock) {
	    if (r->isearly && l->locktype == RuleLockTypeWrite) {
		if (new_r==NULL || r->ruleid != new_r->ruleid) {
		    /*
		     * Create a new header
		     */
		    new_r = (RuleLockIntermediate *)
			MyAlloc((unsigned long)sizeof(RuleLockIntermediate));
		    if (new_r==NULL) {
			elog(WARN,
		"Error in RuleLockIntermediateCopy: couldn;t alloc %d bytes\n",
			    sizeof(RuleLockIntermediateLockData));
			exit(1);
		    } /*if*/
		    (*rr) = new_r;
		    rr = &(new_r->next_rulepack);
		    new_r->next_rulepack = NULL;
		    new_r->ruleid = r->ruleid;
		    new_r->ruletype = r->ruletype;
		    new_r->priority = r->priority;
		    new_r->isearly = r->isearly;
		    ll = &(new_r->locks);
		}
		new_l = (RuleLockIntermediateLockData *)
			MyAlloc((unsigned long)
				sizeof(RuleLockIntermediateLockData));
		if (new_l==NULL) {
		    elog(WARN,
		"Error in RuleLockIntermediateCopy: couldn;t alloc %d bytes\n",
			sizeof(RuleLockIntermediateLockData));
		    exit(1);
		} /*if*/
		(*ll) = new_l;
		ll = &(new_l->next_lock);
		new_l->next_lock = NULL;
		new_l->locktype = l->locktype;
		new_l->planno = l->planno;
		new_l->attrno = l->attrno;
    	    } /*if*/
	}/*for*/
    }/*for*/

    return(copy);
}

