;;;;
;;;; FILE
;;;;	rules/linf/rappend.l
;;;;
;;;; DESCRIPTION
;;;;	Routines related to processing APPEND queries.
;;;;
(defvar *RCS-rappend*
  "$Header: rappend.l,v 1.2 89/02/21 01:23:50 hirohama Exp $")
;;;;
;;;; EXPORTS
;;;;	DoAppend
;;;;	calculateOneFieldInAppend
;;;;	ProcessR3Locks

;;;
;;; XXX Variables global to all concurrent executors
;;;
(defvar *rule-now-plans-queue*  nil
  "Queue of rule plans that want to be run as a Now time qual transacation")
(defvar *rule-self-plans-queue* nil
  "Queue of rule plans that want to be run as a Self Time qual transaction")

;;;
;;; DoAppend
;;;
(defun DoAppend (theTuple
		 theTargetList
		 relationDescriptor
		 relationId
		 attrDesc
		 bufferPage)
  (prog (theTupleLocks res fieldsAlreadyCalculated)
	(setq theTupleLocks
	      (FindTheLocksOfTheTuple theTuple bufferPage relationDescriptor))
	;;
	;; Check for never rules...
	;;
	(dolist (theField theTargetList)
		(setq res
		      (checkForNeverRules 'APPEND
					  theTuple
					  theField
					  theTupleLocks))
		(if (nequal (car res) 'OK)
		    (return res)))

;;;-------------------------------------------
;;; UNDER THE CURRENT IMPLEMENTATION OF RELATION LEVEL LOCKS AND
;;; LATE REPLACE ONLY RULES, WE DO NOTHING WHEN A NEW TUPLE IS
;;; INSERTED
;;;    (setq fieldsAlreadyCalculated nil)
;;;    (dolist (theField theTargetList)
;;;	(setq res (calculateOneFieldInAppend theTuple theField theTupleLocks
;;;	    fieldsAlreadyCalculated  nil
;;;	    relationDescriptor relationId attrDesc bufferPage))
;;;	(if (nequal (car res) 'OK) then (return res))
;;;	(setq theTupleLocks (cadr res))
;;;	(setq fieldsAlreadyCalculated (caddr res))
;;;	(setq theTuple (cadddr res))
;;;	(setq bufferPage (caddddr res))
;;;    )
;;;    (ProcessR3Locks
;;;	    theTuple
;;;	    theTupleLocks
;;;	    fieldsAlreadyCalculated
;;;	    relationDescriptor
;;;	    relationId
;;;	    attrDesc
;;;	    bufferPage)
;;;    ; !!!! SOS !!!!!!
;;;    ; We need to put back only the EW locks (because of the relation
;;;    ; level lock scheme used...)
;;;    (setq theTuple (PutBackLocks theTuple bufferPage theTupleLocks))
;;;    (setq bufferPage (ReturnInvalidBuffer))

	(RuleLockIntermediateFree theTupleLocks)
	(return (list 'OK theTuple bufferPage))))

;;;
;;; calculateOneFieldInAppend.
;;;
;;; Calculate the correct value for one given field, in the tuple to be
;;; appended.  It returns a list with 3 (5?) elements:
;;;
;;;   1) Status (one of OK, LOOP)
;;;   2) The new locks for the tuple
;;;   3) The fields of the tuple that are calculated so far.
;;;   4) The new tuple
;;;   5) the buffer page for the new tuple
;;;
(defun calculateOneFieldInAppend (theTuple
				  theField
				  theTupleLocks
				  fieldsAlreadyCalculated
				  fieldsToBeCalculated
				  relDesc
				  relId
				  attrDesc
				  bufferPage)
  (prog (firsttime result res do-res)
	;;
	;; Is this field Already calulated ?
	;;
	(if (member theField fieldsAlreadyCalculated)
	    (return (list 'OK
			  theTupleLocks
			  fieldsAlreadyCalculated
			  theTuple
			  bufferPage)))
	;;
	;; If this field is in the list of fields for which their calculation
	;; is pending, then we have entered a loop.
	;;
	(if (member theField fieldsToBeCalculated)
	    (return (list 'LOOP
			  theTupleLocks
			  (cons theField fieldsAlreadyCalculated)
			  theTuple
			  bufferPage)))
	;;
	;; OK, go  on...
	;;
	(GetPlanForFieldInAppend theField theTupleLocks 0)	;C function
	(setq firsttime 1)
	(setq do-res
	      (do ((planString
		    (GetPlanForFieldInAppend theField theTupleLocks 1)
		    (GetPlanForFieldInAppend theField theTupleLocks 1)))
		  ((eq 0 planString))
		  (setq result (runRule	planString
					theTuple
					theField
					theTupleLocks
					fieldsAlreadyCalculated
					fieldsToBeCalculated
					relDesc
					relId
					bufferPage))
		  (setq theTupleLocks (cadr result))
		  (setq fieldsAlreadyCalculated (caddr result))
		  (setq theTuple (caddr result))
		  (setq bufferPage (cadddr result))
		  (caseq (car result)
			 ('OK
			  (if (equal firsttime 1)
			      ;;
			      ;; An early rule has computed a value for the
			      ;; field.  If this happens for the first time
			      ;; (i.e. if this is the highest priority early
			      ;; rule) then update the tuple.
			      ;; XXX We need to test for user/rule conflict....
			      ;;
			      (setq theTuple (modifyOneFieldOfTheTuple 
					      theTuple
					      bufferPage
					      relDesc
					      theField
					      (cadddr result)
					      (caddddr result)))
			    (setq bufferPage (ReturnInvalidBuffer))
			    (setq fieldsAlreadyCalculated (cadr result))
			    (setq firsttime 0)))
			 ('LOOP
			  (return (list	'LOOP
					theTupleLocks
					(cons theField fieldsAlreadyCalculated)
					theTuple
					bufferPage)))
			 ('NOVALUEFOUND
			  ;;
			  ;; This early rule is no applicable. Remove the
			  ;; corresponding EW lock.
			  (setq theTupleLocks
				(RemoveCurrentEWLock theField
						     theTupleLocks))))))
	(if (not (null do-res))
	    (return res))
	;;
	;; Use the value stored in the field.
	;;
	(return (list 'OK
		      theTupleLocks
		      (cons theField fieldsAlreadyCalculated)
		      theTuple
		      bufferPage))))

(defun ProcessR3Locks (theTuple
		       theTupleLocks
		       fieldsAlreadyCalculated
		       relDesc
		       relId
		       attrDesc
		       bufferPage)
  (prog (do-res1
	 do-res2
	 theLocks
	 theLispPlanString
	 planstring
	 theOldPlanParseTree
	 theOldPlan
	 theParseTree
	 fieldsNeededInPlan
	 thePlan
	 state
	 plan1
	 plan2
	 plan3
	 res)
	;;
	;; Find all the plans corresponding to R3 locks...
	;;
	(GetPlanForR3 theLocks 0)
	(do ((planString (GetPlanForR3 theLocks 1) (GetPlanForR3 theLocks 1)))
	    ((eq 0 planString))
	    ;;
	    ;; Body of loop
	    ;;
	    (setq theLispPlanString (string-c-to-l planstring))
	    (c_free planstring)
	    (setq theOldPlanParseTree (string-to-plan theLispPlanString))
	    (setq theOldPlan (cadr theOldPlanParseTree))
	    (setq theParseTree (car theOldPlanParseTree))

	    (setq fieldsNeededInPlan (find-parameters theOldPlan))
	    (setq do-res2
		  (dolist (field fieldsNeededInPlan)
			  (setq res (calculateOneField theTuple
						       field
						       theTupleLocks
						       fieldsAlreadyCalculated
						       (cons field nil)
						       relDesc
						       relId
						       attrDesc
						       bufferPage))
			  (setq theTupleLocks (cadr res))
			  (setq fieldsAlreadyCalculated (caddr res))
			  (setq theTuple (cadddr res))
			  (setq bufferPage (caddddr res))
			  (if (nequal (car res) 'OK)
			      (return (list (car res)
					    fieldsAlreadyCalculated
					    theTupleLocks
					    nil
					    nil)))))
	    (if (not (null do-res2))
		(return do-res2))

	    (setq thePlan (substituteTupleIntoPlan
			   theOldPlan
			   theTuple
			   relDesc
			   relId
			   bufferPage))
	    ;;
	    ;; Now put the plan in the *plans-to-be-executed* global variable
	    ;;
	    (setq state (make-exec-state))
	    (setq plan1 (list (cadar theParseTree)
			      theParseTree
			      thePlan
			      state
			      '(start)))
	    (setq plan2 (list (cadar theParseTree)
			      theParseTree
			      thePlan
			      state
			      '(exec-delete-rule)))
	    (setq plan3 (list (cadar theParseTree)
			      theParseTree
			      thePlan
			      state
			      '(end)))
	    (setq *rule-self-plans-queue*
		  (cons plan3 *rule-self-plans-queue*))
	    (setq *rule-self-plans-queue*
		  (cons plan2 *rule-self-plans-queue*))
	    (setq *rule-self-plans-queue*
		  (cons plan1 *rule-self-plans-queue*))
	    (setq plan2 (list (cadar theParseTree)
			      theParseTree
			      thePlan
			      state
			      '(insert-rule)))
	    (setq *rule-now-plans-queue*
		  (cons plan3 *rule-now-plans-queue*))
	    (setq *rule-now-plans-queue*
		  (cons plan2 *rule-now-plans-queue*))
	    (setq *rule-now-plans-queue*
		  (cons plan1 *rule-now-plans-queue*)))))
