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

#include <stdio.h>
#include "postgres.h"
#include "log.h"
#include "rlock.h"
#include "internal.h"

/*
 * The general format of a the lock attribute is as follows...
 * ( Note that "{ X }*" means 0 or more repetitions of "X", and 
 * "{ X }+" means 1 or more repetitions....)
 *
 * LOCK ::= { LOCK_PACKAGE }*
 * LOCK_PACKAGE ::= LOCK_HEADER { LOCK_DATA }+
 * LOCK_HEADER ::= RULEID PRIORITY RULETYPE
 * LOCK_DATA ::= LOCKTYPE ATTRNO PLANNO
 *
 * RULEID is the ObjectId of the corresponding rule
 * PRIORITY is the priority of the rule (one byte long)
 * RULETYPE is one byte long and its bits are divides as following:
 *    The low order 3 bits specify the type of the rule as follows...
 *		0 = Retrieve
 *		1 = Replace
 *		2 = Append
 *		3 = Delete
 *		4 = Refuce Retrieve
 *		5 = Refuse Replace
 *		6 = Refuce Append
 *		7 = Refuce Delete
 *    The 7th bit is 0 if the rule is late or 1 if the rule is early
 *    The 8th bit is 1 if and only if the LOCK_HEADER in question belongs
 *    to the last LOCK_PACKAGE of the LOCK.
 *
 * ATTRNO is the number of attribute to be locked.
 * PLANNO specifies the plan to be executed when the lock is broken.
 * LOCKTYPE is one byte long and its 2 first bits specify the type of
 * lock as follows...
 *     0=W
 *     1=R1
 *     2=R2
 *     3=R3
 *
 * The 8th bit is 1 if and only if this is the last LOCK_DATA in the 
 * LOCKPACKAGE.
 *
 */

    
/*-----------------------------------------------------------------
 *
 *  RuleLockBeginScan:
 *    It takes one argument which is the lock attribute of a tuple
 *    exactly as it is returned from a call to "amgetattr".
 *    It returns a structure to be used by the "RuleLockGetNextLock" and
 *    "RuleLockEndScan".
 *
 */

RuleLockParseStatus *
RuleLockBeginScan(lock)
    RuleLock lock;
{
    long length;
    RuleLockParseStatus *stat;
    char *p;
    char *p2;
    long i;

    /*
     * Test for the trivial case... 
     */
    if (lock == NULL) {
	length = sizeof(RuleLockParseStatus);
	stat = (RuleLockParseStatus *) MyAlloc((unsigned long)length);
	stat->end_of_scan_reached = true;
	return (RuleLockParseStatus *) stat;
    }


    /*
     * Create enough space to hold the RuleLockParseStatus struct
     * and the lockData bytes. (we do not store the lockLength field!).
     */
    length = sizeof(RuleLockParseStatus) + lock->lockLength;
    stat = (RuleLockParseStatus *) MyAlloc((unsigned long)length);
    if (lock->lockLength == 0)
	stat->end_of_scan_reached = true;
    else
	stat->end_of_scan_reached = false;
    stat->points_to_header = true;

    /*
     * Now we copy the lock information (i.e. lockData) just after
     * the end of the structure "RuleLockParseStatus".
     */
    p = (char *) (stat+1);
    stat->pointer = p;
    p2 = (char *) lock->lockData;
    for (i=0; i<lock->lockLength; i++)
	*(p++) = *(p2++);
    
    return((RuleLockParseStatus *) stat);

}

/*-----------------------------------------------------------------
 *
 * RuleLockGetNextLock
 *   Returns one by one the locks. It returns 0 if no locks  are
 *   remaining.
 */

int
RuleLockGetNextLock(stat, ruleid, priority, ruletype, isearly, 
		    locktype, attrno, planno)
    RuleLockParseStatus *stat;
    ObjectId  *ruleid;
    char *priority;
    char *ruletype;
    bool *isearly;
    char *locktype;
    long *attrno;
    long *planno;
{
    
    RuleLockHeader *lock_header_ptr;
    RuleLockBody *lock_data_ptr;

    if (stat->end_of_scan_reached) {
	return(0);
    }

    /*
     * If the item pointed by "stat->pointer" is a lock_header, then
     * read it and store it....
     */
    if (stat->points_to_header) {
	lock_header_ptr = (RuleLockHeader *) stat->pointer;
	((RuleLockHeader *) stat->pointer) ++;
	stat->current_header.ruleid = lock_header_ptr->ruleid;
	stat->current_header.priority= lock_header_ptr->priority;
	stat->current_header.ruletype= lock_header_ptr->ruletype;
    }

    /*
     * Now read the next lock_data
     */
     lock_data_ptr = (RuleLockBody *) stat->pointer;
     ((RuleLockBody *) stat->pointer) ++;

     /*
      * Now copy the values to the output arguments...
      */
    *ruleid = stat->current_header.ruleid;
    *priority = stat->current_header.priority;
    *ruletype = (stat->current_header.ruletype) & 0x7 ;
    *isearly = (bool) ((stat->current_header.ruletype) & 0x40) ;

    *locktype = (lock_data_ptr->locktype) & 0x3;
    *attrno = lock_data_ptr->attrno;
    *planno = lock_data_ptr->planno;

    /*
     * Was this the last lock_data of the lock package???
     * If yes then the "stat->pointer" points to a header.
     */
    stat->points_to_header = (bool) (lock_data_ptr->locktype & 0x80);

    /*
     * But, if this was also the last lock_package then we have
     * reached the end of the lock scan...
     */
    stat->end_of_scan_reached = (bool) (stat->points_to_header &&
				(stat->current_header.ruletype & 0x80));
    
    return(1);
}


/*-----------------------------------------------------------------
 *
 * RuleLockEndScan
 *    Pfree the space occupied by "stat".
 */
void
RuleLockEndScan(stat)
    RuleLockParseStatus *stat;
{
    MyFree((char *)stat);
}
