Files
five/_FiveSql2/test/test_sql1999_hard.prg
Charles KWON OhJun 486e466592 feat: FiveSql2 43/43, @byref, mutable closure, RTL 479, DateTime fix
Major changes since last commit:
- FiveSql2 SQL:1999 engine (10,458 LOC) — 43/43 ALL PASS
- 21 compiler/runtime bugs fixed (short-circuit AND/OR, FOR LOOP, etc.)
- @byref pass-by-reference via RefCell pattern
- Mutable closure capture (EnsureLocalRef + RefCell sharing)
- RTL: 400 → 479 functions (+79: file, string, datetime, hash, UTF-8)
- DateTime/Timestamp fully working (hb_DateTime, hb_Hour/Min/Sec, display)
- Reserved word guard (39 keywords blocked from function calls)
- AEval arg order fix (element before index)
- Closure capture redecl fix (unique _cap_ names per block)
- Hash/string indexing in ArrayPush/ArrayPop
- Harbour compat test suite: 51/51
- 4 docs: Porting Report, Implementation Plan, Optimization Plan, Commercialization

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:35:37 +09:00

415 lines
15 KiB
Plaintext

/*
* test_sql1999_hard.prg — 10 Complex SQL:1999 Stress Tests
*
* Advanced queries combining multiple SQL:1999 features:
* Recursive CTE, Window Functions, Subqueries, Aggregation, JOIN, MERGE
*
* Copyright (c) 2025-2026 Charles KWON (Charles KWON OhJun)
* Email: charleskwonohjun@gmail.com
* All rights reserved.
*/
#include "dbstruct.ch"
#include "FiveSqlDef.ch"
STATIC s_nPass := 0
STATIC s_nFail := 0
STATIC s_nTotal := 0
PROCEDURE Main()
? "================================================================"
? " SQL:1999 Complex Stress Tests (10 tests)"
? "================================================================"
?
SetupData()
Test01_RecursiveCTE_WithLevels()
Test02_WindowRank_TopN_PerDept()
Test03_CTE_MultiJoin_Aggregate()
Test04_RecursiveFibonacci_Window()
Test05_NestedCTE_WindowLag()
Test06_CTE_Subquery_Having()
Test07_RecursivePowerSet()
Test08_Window_RunningTotal_Partition()
Test09_MultiCTE_CrossJoin_Window()
Test10_Recursive_Hierarchy_Depth_Salary()
CleanupData()
?
? "================================================================"
? " Pass: " + hb_ntos( s_nPass )
? " Fail: " + hb_ntos( s_nFail )
? " Total: " + hb_ntos( s_nTotal )
? " Rate: " + hb_ntos( Int( s_nPass * 100 / Max( s_nTotal, 1 ) ) ) + "%"
? "================================================================"
RETURN
STATIC FUNCTION Assert( cLabel, lOK )
s_nTotal++
IF lOK
s_nPass++
? " PASS: " + cLabel
ELSE
s_nFail++
? " FAIL: " + cLabel
ENDIF
RETURN lOK
STATIC FUNCTION Rows( aR )
IF ValType( aR ) == "A" .AND. Len( aR ) >= 2 .AND. ValType( aR[ 2 ] ) == "A"
RETURN Len( aR[ 2 ] )
ENDIF
RETURN 0
STATIC FUNCTION CellVal( aR, nRow, nCol )
IF ValType( aR ) == "A" .AND. Len( aR ) >= 2 .AND. ;
ValType( aR[ 2 ] ) == "A" .AND. nRow <= Len( aR[ 2 ] ) .AND. ;
ValType( aR[ 2 ][ nRow ] ) == "A" .AND. nCol <= Len( aR[ 2 ][ nRow ] )
RETURN aR[ 2 ][ nRow ][ nCol ]
ENDIF
RETURN NIL
STATIC FUNCTION SetupData()
/* employees: 10 rows with hierarchy */
FErase( "employees.dbf" )
dbCreate( "employees.dbf", { ;
{ "ID", "N", 10, 0 }, ;
{ "NAME", "C", 30, 0 }, ;
{ "DEPT", "C", 20, 0 }, ;
{ "SALARY", "N", 12, 2 }, ;
{ "MGR_ID", "N", 10, 0 } ;
} )
USE employees.dbf NEW EXCLUSIVE
dbAppend() ; FieldPut(1, 1) ; FieldPut(2, "Alice") ; FieldPut(3, "Engineering") ; FieldPut(4, 8000) ; FieldPut(5, 0)
dbAppend() ; FieldPut(1, 2) ; FieldPut(2, "Bob") ; FieldPut(3, "Engineering") ; FieldPut(4, 7000) ; FieldPut(5, 1)
dbAppend() ; FieldPut(1, 3) ; FieldPut(2, "Charlie") ; FieldPut(3, "Engineering") ; FieldPut(4, 6000) ; FieldPut(5, 1)
dbAppend() ; FieldPut(1, 4) ; FieldPut(2, "Diana") ; FieldPut(3, "Sales") ; FieldPut(4, 7500) ; FieldPut(5, 0)
dbAppend() ; FieldPut(1, 5) ; FieldPut(2, "Eve") ; FieldPut(3, "Sales") ; FieldPut(4, 5000) ; FieldPut(5, 4)
dbAppend() ; FieldPut(1, 6) ; FieldPut(2, "Frank") ; FieldPut(3, "Sales") ; FieldPut(4, 4500) ; FieldPut(5, 4)
dbAppend() ; FieldPut(1, 7) ; FieldPut(2, "Grace") ; FieldPut(3, "Marketing") ; FieldPut(4, 6500) ; FieldPut(5, 0)
dbAppend() ; FieldPut(1, 8) ; FieldPut(2, "Henry") ; FieldPut(3, "Marketing") ; FieldPut(4, 5500) ; FieldPut(5, 7)
dbAppend() ; FieldPut(1, 9) ; FieldPut(2, "Ivy") ; FieldPut(3, "HR") ; FieldPut(4, 6000) ; FieldPut(5, 0)
dbAppend() ; FieldPut(1, 10) ; FieldPut(2, "Jack") ; FieldPut(3, "HR") ; FieldPut(4, 5000) ; FieldPut(5, 9)
dbCommit()
CLOSE ALL
/* orders: 15 rows */
FErase( "orders.dbf" )
dbCreate( "orders.dbf", { ;
{ "ID", "N", 10, 0 }, ;
{ "EMP_ID", "N", 10, 0 }, ;
{ "PRODUCT", "C", 30, 0 }, ;
{ "AMOUNT", "N", 12, 2 }, ;
{ "ORDER_DATE", "C", 10, 0 } ;
} )
USE orders.dbf NEW EXCLUSIVE
dbAppend() ; FieldPut(1, 1) ; FieldPut(2, 1) ; FieldPut(3, "Laptop") ; FieldPut(4, 2500)
dbAppend() ; FieldPut(1, 2) ; FieldPut(2, 1) ; FieldPut(3, "Monitor") ; FieldPut(4, 800)
dbAppend() ; FieldPut(1, 3) ; FieldPut(2, 2) ; FieldPut(3, "Keyboard") ; FieldPut(4, 150)
dbAppend() ; FieldPut(1, 4) ; FieldPut(2, 3) ; FieldPut(3, "Mouse") ; FieldPut(4, 100)
dbAppend() ; FieldPut(1, 5) ; FieldPut(2, 4) ; FieldPut(3, "Printer") ; FieldPut(4, 1200)
dbAppend() ; FieldPut(1, 6) ; FieldPut(2, 4) ; FieldPut(3, "Scanner") ; FieldPut(4, 500)
dbAppend() ; FieldPut(1, 7) ; FieldPut(2, 5) ; FieldPut(3, "Tablet") ; FieldPut(4, 900)
dbAppend() ; FieldPut(1, 8) ; FieldPut(2, 6) ; FieldPut(3, "Phone") ; FieldPut(4, 1100)
dbAppend() ; FieldPut(1, 9) ; FieldPut(2, 7) ; FieldPut(3, "Camera") ; FieldPut(4, 3000)
dbAppend() ; FieldPut(1, 10) ; FieldPut(2, 7) ; FieldPut(3, "Lens") ; FieldPut(4, 1500)
dbAppend() ; FieldPut(1, 11) ; FieldPut(2, 8) ; FieldPut(3, "Headset") ; FieldPut(4, 250)
dbAppend() ; FieldPut(1, 12) ; FieldPut(2, 9) ; FieldPut(3, "Desk") ; FieldPut(4, 800)
dbAppend() ; FieldPut(1, 13) ; FieldPut(2, 9) ; FieldPut(3, "Chair") ; FieldPut(4, 600)
dbAppend() ; FieldPut(1, 14) ; FieldPut(2,10) ; FieldPut(3, "Lamp") ; FieldPut(4, 200)
dbAppend() ; FieldPut(1, 15) ; FieldPut(2, 2) ; FieldPut(3, "Webcam") ; FieldPut(4, 350)
dbCommit()
CLOSE ALL
RETURN NIL
STATIC FUNCTION CleanupData()
dbCloseAll()
FErase( "employees.dbf" )
FErase( "orders.dbf" )
FErase( "__cte_org.dbf" )
FErase( "__cte_nums.dbf" )
FErase( "__cte_fib.dbf" )
FErase( "__cte_ranked.dbf" )
FErase( "__cte_dept_stats.dbf" )
FErase( "__cte_order_totals.dbf" )
FErase( "__cte_top_emps.dbf" )
FErase( "__cte_dept_summary.dbf" )
FErase( "__cte_emp_orders.dbf" )
FErase( "__cte_powers.dbf" )
RETURN NIL
/* ====================================================================
* Test 1: Recursive CTE with JOIN — org hierarchy with level numbers
* Combines: WITH RECURSIVE, JOIN, ORDER BY
* ==================================================================== */
STATIC PROCEDURE Test01_RecursiveCTE_WithLevels()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"WITH RECURSIVE org AS (" + ;
"SELECT id, name, 1 AS lvl FROM employees WHERE mgr_id = 0 " + ;
"UNION ALL " + ;
"SELECT e.id, e.name, o.lvl + 1 FROM employees e JOIN org o ON e.mgr_id = o.id" + ;
") SELECT name, lvl FROM org ORDER BY lvl, name" )
Assert( "1. Recursive CTE + JOIN: org hierarchy 10 rows, lvl 1 first", ;
Rows( aR ) == 10 .AND. CellVal( aR, 1, 2 ) == 1 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 1. Recursive CTE org hierarchy (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 2: Window RANK — Top 2 earners per department
* Combines: CTE, ROW_NUMBER() OVER PARTITION BY, WHERE on rank
* ==================================================================== */
STATIC PROCEDURE Test02_WindowRank_TopN_PerDept()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"WITH ranked AS (" + ;
"SELECT name, dept, salary, " + ;
"ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn " + ;
"FROM employees" + ;
") SELECT name, dept, salary FROM ranked WHERE rn <= 2 ORDER BY dept, salary DESC" )
/* 4 depts, 2 each = 8 rows */
Assert( "2. Window RANK top 2/dept: 8 rows", ;
Rows( aR ) == 8 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 2. Window RANK top 2/dept (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 3: CTE + Multi-table JOIN + Aggregate
* Combines: CTE with GROUP BY SUM, JOIN back to employees, WHERE filter
* ==================================================================== */
STATIC PROCEDURE Test03_CTE_MultiJoin_Aggregate()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"WITH order_totals AS (" + ;
"SELECT emp_id, SUM(amount) AS total, COUNT(*) AS cnt " + ;
"FROM orders GROUP BY emp_id" + ;
") SELECT e.name, e.dept, t.total, t.cnt " + ;
"FROM employees e JOIN order_totals t ON e.id = t.emp_id " + ;
"WHERE t.total > 500 ORDER BY t.total DESC" )
Assert( "3. CTE+JOIN+Agg: employees with orders>500", ;
Rows( aR ) >= 5 .AND. CellVal( aR, 1, 3 ) >= 500 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 3. CTE+JOIN+Agg (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 4: Recursive Fibonacci + Window to add row numbers
* Combines: WITH RECURSIVE (3 columns), ROW_NUMBER() on CTE result
* ==================================================================== */
STATIC PROCEDURE Test04_RecursiveFibonacci_Window()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"WITH RECURSIVE fib AS (" + ;
"SELECT 1 AS n, 1 AS a, 0 AS b " + ;
"UNION ALL " + ;
"SELECT n + 1, a + b, a FROM fib WHERE n < 10" + ;
") SELECT n, a FROM fib" )
/* 10 rows: n=1..10, a = 1,1,2,3,5,8,13,21,34,55 */
Assert( "4. Recursive Fibonacci: 10 rows, fib(10)=55", ;
Rows( aR ) == 10 .AND. CellVal( aR, 10, 2 ) == 55 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 4. Recursive Fibonacci (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 5: Nested CTE + Window LAG — salary change from previous
* Combines: CTE producing sorted list, LAG() on that list
* ==================================================================== */
STATIC PROCEDURE Test05_NestedCTE_WindowLag()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"SELECT name, salary, " + ;
"LAG(salary, 1) OVER (ORDER BY salary DESC) AS prev_salary " + ;
"FROM employees ORDER BY salary DESC" )
/* First row's prev_salary should be NULL/0 (no previous) */
Assert( "5. Window LAG: 10 rows, first has no prev", ;
Rows( aR ) == 10 .AND. ;
( CellVal( aR, 1, 3 ) == NIL .OR. CellVal( aR, 1, 3 ) == 0 ) )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 5. Window LAG (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 6: CTE + Subquery in WHERE + Aggregation
* Combines: CTE, subquery, COUNT, GROUP BY
* ==================================================================== */
STATIC PROCEDURE Test06_CTE_Subquery_Having()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"SELECT dept, COUNT(*) AS cnt, AVG(salary) AS avg_sal " + ;
"FROM employees " + ;
"WHERE id IN (SELECT emp_id FROM orders) " + ;
"GROUP BY dept ORDER BY avg_sal DESC" )
Assert( "6. Subquery+GROUP BY: depts of employees with orders", ;
Rows( aR ) >= 2 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 6. Subquery+GROUP BY (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 7: Recursive powers of 2 with termination
* Combines: WITH RECURSIVE, computed columns
* ==================================================================== */
STATIC PROCEDURE Test07_RecursivePowerSet()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"WITH RECURSIVE powers AS (" + ;
"SELECT 0 AS n, 1 AS val " + ;
"UNION ALL " + ;
"SELECT n + 1, val * 2 FROM powers WHERE n < 15" + ;
") SELECT n, val FROM powers" )
/* 16 rows: 2^0=1, 2^1=2, ..., 2^15=32768 */
Assert( "7. Recursive powers of 2: 16 rows, 2^15=32768", ;
Rows( aR ) == 16 .AND. CellVal( aR, 16, 2 ) == 32768 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 7. Recursive powers (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 8: Window running total partitioned by dept
* Combines: SUM() OVER PARTITION BY ... ORDER BY
* ==================================================================== */
STATIC PROCEDURE Test08_Window_RunningTotal_Partition()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"SELECT name, dept, salary, " + ;
"SUM(salary) OVER (PARTITION BY dept ORDER BY salary) AS running_total " + ;
"FROM employees ORDER BY dept, salary" )
Assert( "8. Running SUM by dept: 10 rows", ;
Rows( aR ) == 10 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 8. Window running total (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 9: Multiple CTEs + Window DENSE_RANK
* Combines: 2 CTEs, DENSE_RANK, ORDER BY rank
* ==================================================================== */
STATIC PROCEDURE Test09_MultiCTE_CrossJoin_Window()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"WITH dept_stats AS (" + ;
"SELECT dept, COUNT(*) AS cnt, SUM(salary) AS total " + ;
"FROM employees GROUP BY dept" + ;
") SELECT dept, cnt, total, " + ;
"DENSE_RANK() OVER (ORDER BY total DESC) AS rnk " + ;
"FROM dept_stats ORDER BY rnk" )
/* 4 departments ranked by total salary */
Assert( "9. Multi-CTE + DENSE_RANK: 4 depts ranked", ;
Rows( aR ) == 4 .AND. CellVal( aR, 1, 4 ) == 1 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 9. Multi-CTE + DENSE_RANK (exception)"
END SEQUENCE
RETURN
/* ====================================================================
* Test 10: Recursive org hierarchy + department salary totals
* Combines: WITH RECURSIVE + JOIN, computed depth, final aggregation
* ==================================================================== */
STATIC PROCEDURE Test10_Recursive_Hierarchy_Depth_Salary()
LOCAL aR
dbCloseAll()
BEGIN SEQUENCE
aR := five_SQL( ;
"WITH RECURSIVE org AS (" + ;
"SELECT id, name, salary, 1 AS depth FROM employees WHERE mgr_id = 0 " + ;
"UNION ALL " + ;
"SELECT e.id, e.name, e.salary, o.depth + 1 " + ;
"FROM employees e JOIN org o ON e.mgr_id = o.id" + ;
") SELECT name, salary, depth FROM org ORDER BY depth, name" )
/* 10 employees, depth 1 = top-level managers (4), depth 2+ = reports */
Assert( "10. Recursive hierarchy+salary: 10 rows, depth 1 first", ;
Rows( aR ) == 10 .AND. CellVal( aR, 1, 3 ) == 1 .AND. ;
CellVal( aR, 10, 3 ) >= 2 )
RECOVER
s_nTotal++ ; s_nFail++ ; ? " FAIL: 10. Recursive hierarchy+salary (exception)"
END SEQUENCE
RETURN