DEFINE VARIABLE tth                AS HANDLE NO-UNDO.
DEFINE VARIABLE bh                 AS HANDLE NO-UNDO.
DEFINE VARIABLE qh                 AS HANDLE NO-UNDO.
DEFINE VARIABLE buf-ord-hndl       AS HANDLE NO-UNDO.
DEFINE VARIABLE buf-rep-hndl       AS HANDLE NO-UNDO.
DEFINE VARIABLE fld-ordnum-hndl    AS HANDLE NO-UNDO.
DEFINE VARIABLE fld-repname-hndl   AS HANDLE NO-UNDO.
DEFINE VARIABLE fld-orderdate-hndl AS HANDLE NO-UNDO.
DEFINE VARIABLE cLogFile           AS CHARACTER NO-UNDO.
DEFINE VARIABLE cTestID            AS CHARACTER NO-UNDO.
DEFINE VARIABLE exit               AS LOGICAL NO-UNDO.
DEFINE VARIABLE i                  AS INTEGER INITIAL 0 NO-UNDO.
DEFINE VARIABLE runs               AS INTEGER INITIAL 500 NO-UNDO.

/* Use the GUID as the primary unique identifier for the test session */
cTestID = STRING(GUID(GENERATE-UUID)). /* Ensure GUID() output is handled correctly */
cLogFile = "TestLog-" + cTestID + ".txt".

/* --- Logging Helper Procedure --- */
PROCEDURE WriteLogEntry:
    IF i > -1 THEN RETURN.
    DEFINE INPUT PARAMETER pcMessage AS CHARACTER NO-UNDO.
    OUTPUT TO VALUE(cLogFile) APPEND.
    PUT UNFORMATTED pcMessage SKIP.
    OUTPUT CLOSE.
END PROCEDURE.

PROCEDURE CheckErrors:
    /* Check for query preparation error (Using ERROR-STATUS:ERROR for maximum detail) */
    IF ERROR-STATUS:ERROR OR ERROR-STATUS:NUM-MESSAGES > 0 THEN DO:
      DEFINE VARIABLE ix AS INTEGER NO-UNDO.
      
      DO ix = 1 TO ERROR-STATUS:NUM-MESSAGES:
        RUN WriteLogEntry("ERROR: " + ERROR-STATUS:GET-MESSAGE(ix)).
      END. /* Ensure cleanup on failure */
      exit = YES.
    END.
END.

/* ---------------------------------------------------------------------- */
/* --- 1. REUSABLE QUERY EXECUTION PROCEDURE ---------------------------- */
/* ---------------------------------------------------------------------- */
PROCEDURE runSingleQuery:
    DEFINE INPUT PARAMETER pcQueryString AS CHARACTER NO-UNDO.
    DEFINE INPUT PARAMETER phBuffer      AS HANDLE    NO-UNDO.
    
    DEFINE VARIABLE txt AS CHARACTER NO-UNDO.
    
    RUN WriteLogEntry("------------------------------------------------------------------").
    RUN WriteLogEntry("TESTING QUERY: " + pcQueryString).
    
    /* Get field handles needed for logging */
    fld-ordnum-hndl  = phBuffer:BUFFER-FIELD("OrderNum").
    fld-repname-hndl = phBuffer:BUFFER-FIELD("RepName").
    
    CREATE QUERY qh.
    qh:SET-BUFFERS(phBuffer).
    
    exit = NO.
    
    /* Prepare and Open the dynamic query */
    qh:QUERY-PREPARE(pcQueryString) NO-ERROR.  
    RUN CheckErrors.
    
    qh:QUERY-OPEN() NO-ERROR.
    RUN CheckErrors.
    
    IF exit THEN RETURN.
    
    /* Log the header */
    RUN WriteLogEntry(STRING("OrderNum", "X(10)") + " | " + STRING("RepName", "X(20)")).
    
    /* Iterate and log results */
    /* FIX: Added DO TRANSACTION block to satisfy EXCLUSIVE-LOCK requirement (Error 2819) */
    DO TRANSACTION: 
        REPEAT:
            qh:GET-NEXT().
            IF qh:QUERY-OFF-END THEN LEAVE.
            
            /* Get and format the values */
            txt = STRING(fld-ordnum-hndl:BUFFER-VALUE()) + " | " +  
                  STRING(fld-repname-hndl:BUFFER-VALUE()).
            
            RUN WriteLogEntry(INPUT txt).
        END.
    END. /* END TRANSACTION */
    
    RUN WriteLogEntry("Query finished. Total records found: " + STRING(qh:NUM-RESULTS)).
    
    qh:QUERY-CLOSE().
END PROCEDURE.

/* ---------------------------------------------------------------------- */
/* --- 2. QUERY VARIATION HARNESS --------------------------------------- */
/* ---------------------------------------------------------------------- */
PROCEDURE runQueryTests:
    DEFINE INPUT PARAMETER phBuffer AS HANDLE NO-UNDO.
    
    /* === 1. Simple Filtering and Sorting (Base Tests) === */
    
    /* 1. Simple Filter and Sort (Base Case) - Already ordered */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.OrderNum < 50 BY ordx.OrderNum", phBuffer).
    
    /* 2. Inverse Sort Order (Descending) - Already ordered */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.OrderNum < 100 BY ordx.OrderNum DESCENDING", phBuffer).
    
    /* 3. Multiple Sort Fields (Composite BY) - OrderNum as secondary sort */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK BY ordx.RepName BY ordx.OrderNum", phBuffer).

    /* === 2. String Operators and Logic === */
    
    /* 4. String Pattern Matching (MATCHES) - OrderNum as secondary sort */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.RepName MATCHES 'A*' BY ordx.RepName BY ordx.OrderNum", phBuffer).
    
    /* 5. String Start Check (BEGINS) - OrderNum as secondary sort */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.RepName BEGINS 'J' BY ordx.RepName BY ordx.OrderNum", phBuffer).

    /* === 3. Complex Logic and Ranges === */
    
    /* 6. Range Filter using >= and <= - Already ordered */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.OrderNum >= 10 AND ordx.OrderNum <= 30 BY ordx.OrderNum", phBuffer).
    
    /* 7. Complex AND/OR Logic - OrderNum as secondary sort */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE (ordx.RepName BEGINS 'S' OR ordx.RepName BEGINS 'B') AND ordx.OrderNum > 20 BY ordx.RepName BY ordx.OrderNum", phBuffer).

    /* === 4. Advanced Clauses and Options === */

    /* 8. NO-LOCK Clause Check - Already ordered */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.OrderNum > 5 BY ordx.OrderNum", phBuffer).

    /* 9. MAX-ROWS Clause Check - Already ordered */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.OrderNum > 1 BY ordx.OrderNum MAX-ROWS 5", phBuffer).
    
    /* 10. USE-INDEX Clause Check - Already ordered */
/*    RUN runSingleQuery("FOR EACH ordx USE-INDEX PrimaryIndex WHERE ordx.OrderNum > 10 BY ordx.OrderNum", phBuffer).*/

    /* 12. Complex Date Function - OrderNum as secondary sort */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE MONTH(ordx.OrderDate) = 11 AND YEAR(ordx.OrderDate) = 1997 BY ordx.OrderDate BY ordx.OrderNum", phBuffer).
    
    /* === 5. Edge Cases and Error Handling === */
    
    /* 13. Error Case (Invalid field/table name) - No need to add BY, the query will fail prepare */
    RUN runSingleQuery("FOR EACH ordx NO-LOCK WHERE ordx.InvalidField > 0", phBuffer).

END PROCEDURE.

/* ---------------------------------------------------------------------- */
/* --- 3. MAIN EXECUTION PROCEDURE (Temp-Table Setup) ------------------- */
/* ---------------------------------------------------------------------- */
PROCEDURE createTempTableTest:
    /* Get database table handles */
    buf-ord-hndl = BUFFER Order:HANDLE.
    buf-rep-hndl = BUFFER SalesRep:HANDLE.
    
    /* Create an empty, undefined TEMP-TABLE */
    CREATE TEMP-TABLE tth.
    /* Give it Order table's fields & indexes */
    tth:CREATE-LIKE(buf-ord-hndl).
    /* Add field like SalesRep.RepName */
    tth:ADD-LIKE-FIELD("RepName","SalesRep.RepName").
    /* No more fields will be added */
    tth:TEMP-TABLE-PREPARE("ordx").
    
    /* Get the buffer handle for the temp-table */
    bh = tth:DEFAULT-BUFFER-HANDLE.
    
    /* Populate the temp-table from order */
    FOR EACH Order NO-LOCK:
    bh:BUFFER-CREATE.
    bh:BUFFER-COPY(buf-ord-hndl).
    /* Add the corresponding salesrep name */
    FIND SalesRep NO-LOCK WHERE SalesRep.SalesRep = Order.SalesRep NO-ERROR.
    IF AVAILABLE SalesRep THEN
       bh:BUFFER-COPY(buf-rep-hndl,?,"RepName,repname").
    END. 
    
    /* Run a query to access the TEMP-TABLE */
    CREATE QUERY qh.
    qh:SET-BUFFERS(bh).
    qh:QUERY-PREPARE("FOR EACH ordx NO-LOCK WHERE ordx.OrderNum < 50 BY ordx.RepName BY ordx.OrderNum").
    qh:QUERY-OPEN().
    
    fld-ordnum-hndl = bh:BUFFER-FIELD("OrderNum").
    fld-repname-hndl = bh:BUFFER-FIELD("RepName").
    fld-orderdate-hndl = bh:BUFFER-FIELD("OrderDate").
    
    /* Display the order number and the salesrep name */
    REPEAT:
       qh:GET-NEXT().
       IF qh:QUERY-OFF-END THEN LEAVE.
       RUN WriteLogEntry(STRING(fld-ordnum-hndl:BUFFER-VALUE()) + " | " + STRING(fld-repname-hndl:BUFFER-VALUE()) + " | " + STRING(fld-orderdate-hndl:BUFFER-VALUE())).
    END.
    
    qh:QUERY-CLOSE().
    bh:BUFFER-RELEASE().
END PROCEDURE.

/* --- START THE TEST --- */
RUN WriteLogEntry("------------------- TEST START -------------------").
RUN createTempTableTest.

DO i = 0 TO runs:
    RUN runQueryTests(INPUT bh).
END.
DELETE OBJECT tth. 
DELETE OBJECT qh.
i = 0.
RUN WriteLogEntry("------------------- TEST END ---------------------").