perf(fivesql2): stable temp alias — 1.67x on JOIN bench

AcquireTemp now returns the purpose string (upper-cased table name)
as the alias when available, and falls back to FA_#### only when the
same purpose is already in-flight this query — i.e., self-joins.
Previously every call returned a fresh FA_####, so the WA cache
(keyed by alias) could never hit on JOIN queries and the file got
reopened every iteration.

Bench deltas vs prior HEAD:
  B6_INNER_JOIN    217 → 130 us   -40%  (1.67x)
  B15_CTE_WIN_JOIN 1678 → 1595 us -5%
Single-table benches unchanged — they were already hitting the
cache via the table-name alias path.

B8 recursive CTE stays flat: its sub-executors at nDepth>1 still
cycle through fresh purposes that don't stabilise across queries.

FiveSql2 43/43, Harbour compat 56/56.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 13:31:01 +09:00
parent 9bb361b2e0
commit 6746ae4cee

View File

@@ -119,11 +119,36 @@ RETURN cAlias
/*
* AcquireTemp — create a unique alias for temp/derived tables.
* AcquireTemp — create a stable alias for temp/derived tables.
*
* Returns the purpose (usually the upper-cased table name) itself
* when it's a valid alias and not already in use in this query.
* This lets the WA cache hit on repeated queries of the form
* `FROM table t` — previously every call returned a fresh FA_####
* that no cache entry could match, so every query re-opened the
* file. Self-joins (`FROM emp e1, emp e2`) still need two distinct
* workareas, so the second acquire for the same purpose falls back
* to the unique counter.
*/
METHOD AcquireTemp( cPurpose ) CLASS TSqlAlias
LOCAL cAlias
LOCAL cAlias, cUp, i, lTaken
cUp := Upper( cPurpose )
IF ! Empty( cUp ) .AND. IsAlpha( Left( cUp, 1 ) )
lTaken := .F.
FOR i := 1 TO Len( ::aSlots )
IF ::aSlots[ i ][ 1 ] == cUp
lTaken := .T.
EXIT
ENDIF
NEXT
IF ! lTaken
AAdd( ::aSlots, { cUp, cPurpose, cPurpose, .F. } )
RETURN cUp
ENDIF
ENDIF
s_nGlobalSeq++
cAlias := "FA_" + StrZero( s_nGlobalSeq, 4 )