From 36452a2a02b5ad0259f6d74b4710de7edd6a008d Mon Sep 17 00:00:00 2001 From: 4gl <@> Date: Tue, 28 Apr 2026 18:38:57 +0100 Subject: [PATCH] fix: simplifying mv_castabload and improving tests --- tests/viyaonly/mv_castabload.test.sas | 192 +++++++++++----------- viya/mv_castabload.sas | 224 ++++++++++++++++++-------- 2 files changed, 257 insertions(+), 159 deletions(-) diff --git a/tests/viyaonly/mv_castabload.test.sas b/tests/viyaonly/mv_castabload.test.sas index 8f5ca17..6c6d3f9 100644 --- a/tests/viyaonly/mv_castabload.test.sas +++ b/tests/viyaonly/mv_castabload.test.sas @@ -7,26 +7,26 @@ @li mp_assert.sas @li mp_assertscope.sas @li mv_castabload.sas - @li mv_createfile.sas **/ options mprint; -/* ------------------------------------------------------------------------ */ -/* Setup: start a CAS session and stage a source file in the Public caslib */ -/* ------------------------------------------------------------------------ */ +/* -------------------------------------------------------------------- */ +/* Setup: start a CAS session and stage a source file in the caslib */ +/* -------------------------------------------------------------------- */ cas mysess; caslib _all_ assign; -%let testcaslib = Public; /* change this if Public isn't available */ +%let testcaslib=Public; + proc cas; table.caslibInfo result=r / ; - found = 0; + found=0; do row over r.CASLibInfo; - if upcase(row.Name) = upcase("&testcaslib") then found = 1; + if upcase(row.Name)=upcase("&testcaslib") then found=1; end; - if found = 0 then do; + if found=0 then do; print "ERROR: caslib &testcaslib not available"; exit; end; @@ -34,113 +34,117 @@ quit; %put NOTE: Using testcaslib=&testcaslib; %let tab1=T%mf_uid(); -%let tab2=T%mf_uid(); -%let tab3=T%mf_uid(); -/* Create a SASHDAT source file in the Public caslib from SASHELP.BASEBALL - so that subsequent LOAD operations have something real to pick up. */ +/* Save a sashdat source file then drop the in-memory copy so the first + mv_castabload call has something to load */ proc casutil; - load data=sashelp.baseball outcaslib="&testcaslib" casout="&tab1" replace; + load data=sashelp.baseball + outcaslib="&testcaslib" casout="&tab1" replace; save casdata="&tab1" incaslib="&testcaslib" casout="&tab1..sashdat" outcaslib="&testcaslib" replace; droptable casdata="&tab1" incaslib="&testcaslib" quiet; quit; -/* And a second hdat, with a name different from the table name, so that we - can exercise the explicit srcfile= path. */ -proc casutil; - load data=sashelp.cars outcaslib="&testcaslib" casout="&tab2" replace; - save casdata="&tab2" incaslib="&testcaslib" - casout="src_&tab2..sashdat" outcaslib="&testcaslib" replace; - droptable casdata="&tab2" incaslib="&testcaslib" quiet; +libname mylib cas caslib="&testcaslib"; + + +/* -------------------------------------------------------------------- */ +%put TEST 1 - load a table that is not in memory; +/* -------------------------------------------------------------------- */ + +/* Confirm table is absent before the call */ +%let _tabexists=0; +proc cas; + table.tableExists result=r / + caslib="&testcaslib" name="&tab1"; + if r.exists > 0 then call symputx('_tabexists','1'); quit; +%mp_assert( + iftrue=(&_tabexists=0), + desc=Check table is not in memory before mv_castabload +) -/* ------------------------------------------------------------------------ */ -%put TEST 1 - missing required parameters returns without setting RC to 0/1; -/* ------------------------------------------------------------------------ */ -%let MV_CASTABLOAD_RC=; -%mv_castabload(caslib=,table=,srcfile=) +%mv_castabload(lib=mylib, table=&tab1, mdebug=1) + +%let _tabexists=0; +proc cas; + table.tableExists result=r / + caslib="&testcaslib" name="&tab1"; + if r.exists > 0 then call symputx('_tabexists','1'); +quit; %mp_assert( - iftrue=(&MV_CASTABLOAD_RC=3), - desc=Check RC=3 (initial/failure value) when required params are missing + iftrue=(&_tabexists=1), + desc=Check table is in memory after mv_castabload ) -/* ------------------------------------------------------------------------ */ -%put TEST 2 - load a table that does not yet exist (default srcfile=table.sashdat); -/* ------------------------------------------------------------------------ */ -%mv_castabload(caslib=&testcaslib,table=&tab1,mdebug=1) +/* -------------------------------------------------------------------- */ +%put TEST 2 - reload fetches a fresh copy and discards in-memory changes; +/* -------------------------------------------------------------------- */ + +/* Append a sentinel row to the in-memory table */ +data work.extra; + set mylib.&tab1; + name='TESTROW'; + output; + stop; +run; +proc casutil; + load data=work.extra casout="&tab1" + outcaslib="&testcaslib" append; +quit; + +%let _modified=0; +proc sql noprint; + select count(*) into :_modified + from mylib.&tab1 + where name='TESTROW'; +quit; %mp_assert( - iftrue=(&MV_CASTABLOAD_RC=1), - desc=Check RC=1 when table is loaded and promoted for the first time + iftrue=(&_modified=1), + desc=Check sentinel row is present in memory before reload ) - -/* ------------------------------------------------------------------------ */ -%put TEST 3 - calling again for the same table should be a no-op (RC=0); -/* also verify no scope leakage of macro variables */ -/* ------------------------------------------------------------------------ */ -%mp_assertscope(SNAPSHOT) - -%mv_castabload(caslib=&testcaslib,table=&tab1,mdebug=1) - -%mp_assertscope(COMPARE, - desc=Check mv_castabload does not leak macro variables into GLOBAL scope, - ignorelist=MV_CASTABLOAD_RC -) - -%mp_assert( - iftrue=(&MV_CASTABLOAD_RC=0), - desc=Check RC=0 when table is already in-memory (skip load) -) - - -/* ------------------------------------------------------------------------ */ -%put TEST 4 - explicit srcfile= where file name differs from table name; -/* ------------------------------------------------------------------------ */ -%mv_castabload( - caslib=&testcaslib, - table=&tab2, - srcfile=src_&tab2..sashdat, - mdebug=1 -) - -%mp_assert( - iftrue=(&MV_CASTABLOAD_RC=1), - desc=Check RC=1 when loading with explicit srcfile= parameter -) - - -/* ------------------------------------------------------------------------ */ -%put TEST 5 - load failure when srcfile does not exist in the caslib; -/* ------------------------------------------------------------------------ */ -%mv_castabload( - caslib=&testcaslib, - table=&tab3, - srcfile=doesnotexist_%mf_uid..sashdat, - mdebug=1 -) - -%mp_assert( - iftrue=(&MV_CASTABLOAD_RC=3), - desc=Check RC=3 when source file cannot be found / load fails -) - -/* reset so that a downstream failure RC does not break testterm */ -%let syscc=0; - - -/* ------------------------------------------------------------------------ */ -/* Teardown: drop promoted tables and remove source files */ -/* ------------------------------------------------------------------------ */ +/* Drop the table and reload - source file does not have the sentinel */ proc casutil; droptable casdata="&tab1" incaslib="&testcaslib" quiet; - droptable casdata="&tab2" incaslib="&testcaslib" quiet; - deletesource casdata="&tab1..sashdat" incaslib="&testcaslib" quiet; - deletesource casdata="src_&tab2..sashdat" incaslib="&testcaslib" quiet; + droptable casdata="&tab1" incaslib="&testcaslib" quiet; +quit; + +%mp_assertscope(SNAPSHOT) + +%mv_castabload(lib=mylib, table=&tab1, mdebug=1) + +%mp_assertscope(COMPARE, + desc=Check mv_castabload does not leak macro variables into GLOBAL scope +) + +%let _after=0; +proc sql noprint; + select count(*) into :_after + from mylib.&tab1 + where name='TESTROW'; +quit; + +%mp_assert( + iftrue=(&_after=0), + desc=Check sentinel row is absent after reload from source +) + + +/* -------------------------------------------------------------------- */ +/* Teardown */ +/* -------------------------------------------------------------------- */ +libname mylib clear; + +proc casutil; + droptable casdata="&tab1" incaslib="&testcaslib" quiet; + droptable casdata="&tab1" incaslib="&testcaslib" quiet; + deletesource casdata="&tab1..sashdat" + incaslib="&testcaslib" quiet; quit; cas mysess terminate; diff --git a/viya/mv_castabload.sas b/viya/mv_castabload.sas index c7f4fd2..4bdb13a 100644 --- a/viya/mv_castabload.sas +++ b/viya/mv_castabload.sas @@ -1,73 +1,78 @@ /** @file mv_castabload.sas - @brief Checks if a CAS table exists in a CASLIB; if not, loads & promotes it - @details Runs in SPRE against an active CAS session. Uses - `table.tableExists` to check whether the table is already in-memory, - and PROC CASUTIL LOAD with the PROMOTE option to load it if not. - CASUTIL infers the file type from the source file extension. + @brief Checks if a CAS table is loaded; if not, loads and promotes it + @details Runs in SPRE against an active CAS session. Accepts a + SAS libref, derives the CAS caslib and session UUID from + sashelp.vlibnam, then checks whether the table is already + in-memory. If not, locates the owning CAS server via the + casManagement REST API, queries the table endpoint to discover + the source file and caslib, then loads and promotes the table. A CAS session must already be established by the caller, eg: cas mysess; - %mv_castabload(caslib=Public, table=BASEBALL) + libname mylib cas caslib=Public; + %mv_castabload(lib=mylib, table=BASEBALL) - or (if not a hdat source with the same name as the table): - - %mv_castabload(caslib=Public, table=BASEBALL, - srcfile=MYBASEBALL.parquet) - - @param [in] caslib= CASLIB containing the source file - @param [in] table= Name to give the in-memory CAS table - @param [in] srcfile= (0) Source file name.ext in the caslib. If not provided, - the code assumes that srcfile=&table..sashdat - @param [in] mdebug= (0) Set to 1 to enable verbose logging: + @param [in] lib= SAS libref for the CAS caslib + @param [in] table= Name of the CAS table to load + @param [in] mdebug= (0) Set to 1 to enable verbose logging: - echoes resolved parameters - prints tableExists result - enables mprint/notes during PROC calls - @returns Sets global macro variable `MV_CASTABLOAD_RC`: - 0 = table already existed (no load performed) - 1 = table was loaded & promoted successfully - 3 = action failed (including source file missing) -