project BLF > class Persistence (Progress) > method SaveInstance

Description

save instance data to the application database

PreCondition

This method is to be used by instance procedures only.


Parameters


ihClassinputhandle
iiInstanceIDinputintegerinstance ID
ihInstanceDatainputhandledataset with instance data
icClassNameinputcharacterclass name
oiReturnStatusoutputintegerReturn status of the method.


Internal usage


unused


program code (program1/progress.p)

/* Do not save when loaded read-only (see LoadInstance) */
viFcCount1 = 2.
if icClassName = "session"
then do while program-name(viFcCount1) <> ?:
    if program-name(viFcCount1) = "UpdateSessionData program/cacher.p"
    then return.
    viFcCount1 = viFcCount1 + 1.
end.

if ihInstanceData:name = "StateData"
and ihClass <> ?
then run GetPublicData in ihClass (input "vlRawTransferSupported", output vcRawTransferSupported, output viFcReturnSuper).

CREATE tRaw. /* Just need one dummy row to store RAW field in by its handle. */
vhContext = BUFFER tRaw:BUFFER-FIELD("taContext":U):HANDLE.

CREATE QUERY vhQuery in widget-pool "non-persistent".
    
do transaction on error undo, throw:
    
    if not (valid-handle(vhInstanceQuery) and
            viInstanceUID = vhInstanceQuery:unique-id)
    then do:
        create buffer vhInstanceBuffer for table "fcInstance":U in widget-pool "persistent".
        create query vhInstanceQuery in widget-pool "persistent".
        vhInstanceQuery:forward-only = yes.
        vhInstanceQuery:set-buffers(vhInstanceBuffer).
        vhInstanceQuery:private-data = "Persistent".    /* do not ever delete this query */
        viInstanceUID = vhInstanceQuery:unique-id.
    end.
    
    if ihInstanceData:name = "StateData"
    then do:
        /* create a separate record containing the session ID only, for usage by housekeeping */
        vhBuffer = ihInstanceData:get-buffer-handle("ibusinessData").
        vhBuffer:find-first().
        put-string(vaContext,1) = string(vhBuffer::iSessionId).
    
        vhInstanceQuery:query-prepare
         ("for each fcInstance where fcInstance.Instance_ID = ":U + string(iiInstanceId) +
          " and fcInstance.InstanceSeq = 0").
        vhInstanceQuery:query-open().
        vhInstanceQuery:get-first(exclusive-lock, no-wait).
        vhInstanceQuery:query-close().
        if vhInstanceBuffer:available and vhInstanceBuffer:locked
        then do:
            assign oiReturnStatus = -3.
            return.
        end.
        if vhInstanceBuffer:available
        then assign vhInstanceBuffer::InstanceData = vaContext.
        else do:
            vhInstanceBuffer:buffer-create().
            assign vhInstanceBuffer::Instance_ID       = iiInstanceId
                   vhInstanceBuffer::InstanceSeq       = 0
                   vhInstanceBuffer::InstanceData      = vaContext
                   vhInstanceBuffer::InstanceIsInUse   = no
                   vhInstanceBuffer::InstanceClassName = icClassName.
        end.
    end.
    
    if vcStateDirectory = "" or ihInstanceData:name <> "StateData"
    then do:
        vhInstanceQuery:query-prepare
         ("for each fcInstance where fcInstance.Instance_ID = ":U + string(iiInstanceId) +
          " and fcInstance.InstanceSeq > 0").
        vhInstanceQuery:query-open().
        vleoq = no.

        if vcRawTransferSupported = "false"
        then do:
            ihInstanceData:write-xml ("memptr",vmContext,false,?,?,false,false,false,true).
            viMaxLength = get-size(vmcontext).
            viContextPos = 1.
            
            do while viContextPos <= viMaxLength:
            
                viRawLength = minimum (viMaxLength - viContextPos + 1, 31000).
                vacontext = get-bytes (vmcontext,viContextPos,viRawLength).
                viContextPos = viContextPos + viRawLength.
    
                if vleoq = no
                then do:
                    vhInstanceQuery:get-next(exclusive-lock, no-wait).
                    
                    if vhInstanceBuffer:available and vhInstanceBuffer:locked
                    then do:
                        vhInstanceQuery:query-close().
                        assign oiReturnStatus = -3.
                        return.
                    end.
                    
                    if vhInstanceBuffer:available
                    then do:
                        if vhInstanceBuffer::InstanceClassName <> icClassName
                        then do:
                            vhInstanceBuffer:buffer-release().
                            vhInstanceQuery:query-close().
                            assign oiReturnStatus = -3.
                            <M-64 run ErrorMessage
                               (input  #T-73'Unable to save data for business class $1, cannot overwrite data for business class $2.':255(4393)T-73# (icMessage), 
                                input  icClassName + chr(2) + vhInstanceBuffer::InstanceClassName (icArguments), 
                                input  '' (icFieldName), 
                                input  '' (icFieldValue), 
                                input  '' (icRowid), 
                                input  ? (ihClass)) in Progress>
                            return.
                        end.
        
                        if vhInstanceBuffer::InstanceIsInUse = no
                        then do:
                            vhInstanceBuffer:buffer-release().
                            vhInstanceQuery:query-close().
                            assign oiReturnStatus = -3.
                            <M-19 run ErrorMessage
                               (input  #T-60'Instance is already closed.':255(93)T-60# + ' (class name = ' + icClassName + ')' (icMessage), 
                                input  '' (icArguments), 
                                input  '' (icFieldName), 
                                input  '' (icFieldValue), 
                                input  '' (icRowid), 
                                input  ? (ihClass)) in Progress>
                            return.
                        end.
                    end.
                    
                    vleoq = vhInstanceQuery:query-off-end.
                end.
            
                if vleoq = no
                then assign viContextSeq = vhInstanceBuffer::InstanceSeq.
                else do:
                    vhInstanceBuffer:buffer-create().
                    viContextSeq = viContextSeq + 1.
                end.

                assign vhInstanceBuffer::Instance_ID       = iiInstanceId
                       vhInstanceBuffer::InstanceSeq       = viContextSeq
                       vhInstanceBuffer::InstanceIsInUse   = no
                       vhInstanceBuffer::InstanceClassName = icClassName.
                       vhInstanceBuffer::InstanceData      = vaContext.
            end.
        end.
        else do:
            /* maximum number of bytes to store in a RAW field */
            if <M-62 DataServerActive  () in Progress>
            then viMaxLength = 26000.
            else viMaxLength = 31900.
    
            vhInstanceQuery:get-first(exclusive-lock, no-wait).
            if vhInstanceBuffer:available and vhInstanceBuffer:locked
            then do:
                vhInstanceQuery:query-close().
                assign oiReturnStatus = -3.
                return.
            end.
        
            if vhInstanceBuffer:available
            then do:
                if vhInstanceBuffer::InstanceClassName <> icClassName
                then do:
                    vhInstanceBuffer:buffer-release().
                    vhInstanceQuery:query-close().
                    assign oiReturnStatus = -3.
                    <M-17 run ErrorMessage
                       (input  #T-50'Unable to save data for business class $1, cannot overwrite data for business class $2.':255(4393)T-50# (icMessage), 
                        input  icClassName + chr(2) + vhInstanceBuffer::InstanceClassName (icArguments), 
                        input  '' (icFieldName), 
                        input  '' (icFieldValue), 
                        input  '' (icRowid), 
                        input  ? (ihClass)) in Progress>
                    return.
                end.
        
                if vhInstanceBuffer::InstanceIsInUse = no
                then do:
                    vhInstanceBuffer:buffer-release().
                    vhInstanceQuery:query-close().
                    assign oiReturnStatus = -3.
                    <M-9 run ErrorMessage
                       (input  #T-77'Instance is already closed.':255(93)T-77# + ' (class name = ' + icClassName + ')' (icMessage), 
                        input  '' (icArguments), 
                        input  '' (icFieldName), 
                        input  '' (icFieldValue), 
                        input  '' (icRowid), 
                        input  ? (ihClass)) in Progress>
                    return.
                end.
        
                assign vhInstanceBuffer::InstanceIsInUse = no
                       viContextSeq = vhInstanceBuffer::InstanceSeq.
            end.
            else do:
                vhInstanceBuffer:buffer-create().
                assign vhInstanceBuffer::Instance_ID       = iiInstanceId
                       vhInstanceBuffer::InstanceSeq       = viContextSeq
                       vhInstanceBuffer::InstanceIsInUse   = no
                       vhInstanceBuffer::InstanceClassName = icClassName.
            end.
        
            SET-SIZE(vmContext) = viMaxLength.
            DO viBuffer = 1 TO ihInstanceData:NUM-BUFFERS:
            
                vhBuffer = ihInstanceData:GET-BUFFER-HANDLE(viBuffer).
                vhQuery:SET-BUFFERS(vhBuffer).
                vhQuery:QUERY-PREPARE("FOR EACH ":U + vhBuffer:NAME).
                vhQuery:QUERY-OPEN().
                vhQuery:GET-FIRST().
            
                DO WHILE NOT vhQuery:QUERY-OFF-END:
            
                    vhBuffer:RAW-TRANSFER(TRUE, vhContext).
                    viRawLength = LENGTH(tRaw.taContext, "RAW":U).
                    IF viContextPos + viRawLength + 8 > viMaxLength THEN
                    DO:
                        /* The data won't fit in a single BLOB, so create another row. */
                        PUT-SHORT(vmContext, viContextPos) = 0. /* Signal end of this row. */
                        viContextPos = viContextPos + 2.
                        vaContext = get-bytes(vmContext,1,viContextPos).
                        vhInstanceBuffer::InstanceData = vaContext.
                        if not vhInstanceQuery:query-off-end
                        then vhInstanceQuery:get-next(exclusive-lock).
                        if vhInstanceQuery:query-off-end
                        then do:
                            viContextSeq = viContextSeq + 1.
                            vhInstanceBuffer:buffer-create().
                            assign vhInstanceBuffer::Instance_ID       = iiInstanceId
                                   vhInstanceBuffer::InstanceSeq       = viContextSeq
                                   vhInstanceBuffer::InstanceIsInUse   = no
                                   vhInstanceBuffer::InstanceClassName = icClassName.
                        end.
                        else assign viContextSeq = vhInstanceBuffer::InstanceSeq.
                        assign vhInstanceBuffer::InstanceIsInUse = no.
                        viContextPos = 1.
                    END.
                    PUT-SHORT(vmContext, viContextPos) = viBuffer. 
                    viContextPos = viContextPos + 2.    /* Move past the table number. */
                    PUT-LONG(vmContext, viContextPos) = viRawLength.
                    viContextPos = viContextPos + 4.   /* Move past the buffer length. */
                    PUT-BYTES(vmContext, viContextPos) = tRaw.taContext.
                    viContextPos = viContextPos + viRawLength.
                    vhQuery:GET-NEXT().
                END.
            
                vhQuery:QUERY-CLOSE().
            
                IF viBuffer = ihInstanceData:NUM-BUFFERS
                THEN DO:
                    PUT-SHORT(vmContext, viContextPos) = 0.  /* Mark the end of the data. */
                    viContextPos = viContextPos + 2.
                END.
            END.
            vaContext = get-bytes(vmContext,1,viContextPos).
            vhInstanceBuffer::InstanceData = vaContext.
        end.
        
        do while not vhInstanceQuery:query-off-end:
            vhInstanceQuery:get-next(exclusive-lock).
            if vhInstanceQuery:query-off-end then leave.
            vhInstanceBuffer:buffer-delete().
        end.
    end.
    else
    
    if vcRawTransferSupported = "false"
    then ihInstanceData:write-xml ("file",vcStateDirectory + "/" + lc(icClassName) + string(iiInstanceID) + ".lob",false,?,?,false,false,false,true).
    else do:
        /* All data is stored in a single memptr.
           Set size large enough to make an overflow VERY exceptional.
          (Size is not limited to RAW length) */
        viMaxLength = 200000.
    
        SET-SIZE(vmContext) = viMaxLength.
        
        DO viBuffer = 1 TO ihInstanceData:NUM-BUFFERS:
        
            vhBuffer = ihInstanceData:GET-BUFFER-HANDLE(viBuffer).
            vhQuery:SET-BUFFERS(vhBuffer).
            vhQuery:QUERY-PREPARE("FOR EACH ":U + vhBuffer:NAME).
            vhQuery:QUERY-OPEN().
            vhQuery:GET-FIRST().
        
            DO WHILE NOT vhQuery:QUERY-OFF-END:
        
                vhBuffer:RAW-TRANSFER(TRUE, vhContext).
                viRawLength = LENGTH(tRaw.taContext, "RAW":U).
    
                if viContextPos + viRawLength + 8 > viMaxLength
                then do:
                    /* In case an overflow does occur, double the size of the target memptr. */
                    viMaxLength = viMaxLength * 2.
                    SET-SIZE(vmContextOverflow) = viMaxLength.
                    copy-lob from vmContext starting at 1 for viContextPos - 1 to vmContextOverflow overlay at 1.
                    vmContext = vmContextOverflow.
                    set-size(vmContextOverflow) = 0.
                end.
                
                PUT-SHORT(vmContext, viContextPos) = viBuffer. 
                viContextPos = viContextPos + 2.    /* Move past the table number. */
                PUT-LONG(vmContext, viContextPos) = viRawLength.
                viContextPos = viContextPos + 4.   /* Move past the buffer length. */
                PUT-BYTES(vmContext, viContextPos) = tRaw.taContext.
                viContextPos = viContextPos + viRawLength.
    
                vhQuery:GET-NEXT().
            END.
        
            vhQuery:QUERY-CLOSE().
        END.
    
        copy-lob vmContext for viContextPos to file (vcStateDirectory + "/" + lc(icClassName) + string(iiInstanceID) + ".lob").
    end.
end.

finally:
    if vhInstanceBuffer <> ?
    then vhInstanceBuffer:buffer-release().
    if vhInstanceQuery <> ?
    then vhInstanceQuery:query-close().
    if vhQuery <> ?
    then delete object vhQuery.
    if available tRaw
    then delete tRaw.
    SET-SIZE(vmContext) = 0.
end finally.