1
0
mirror of https://github.com/sasjs/core.git synced 2026-06-09 12:10:22 +00:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Allan Bowe f484d10913 Merge pull request #427 from sasjs/jobflowfix
Jobflowfix
2026-05-14 17:30:59 +01:00
github-actions 70efaf84e1 chore: updating all.sas 2026-05-14 16:29:48 +00:00
4gl 6c6c45b85c fix: issue with running jobs in viya2026 (all params must be strings) 2026-05-14 17:29:26 +01:00
Allan Bowe 5453b76fe6 Merge pull request #426 from sasjs/mx_append2pgm
Mx append2pgm
2026-05-14 15:08:31 +01:00
github-actions c8e1126a40 chore: updating all.sas 2026-05-14 14:02:39 +00:00
4gl 872353dc8d feat: new mx_append2pgm macro 2026-05-14 15:02:14 +01:00
Allan Bowe 9b44875142 fix: Merge pull request #425 from sasjs/allbump
fix: updating all.sas and mv_webout
2026-05-13 14:21:00 +01:00
github-actions c5a681e6ed chore: updating all.sas 2026-05-13 13:19:41 +00:00
4gl a759aa9915 fix: using 128 instead of 131 in _debug due to compute task bug 2026-05-13 14:18:47 +01:00
Allan Bowe 1244aff5e4 Merge pull request #424 from sasjs/exectask
Exectask
2026-05-13 14:06:38 +01:00
github-actions 4934e6675e chore: updating all.sas 2026-05-13 13:04:37 +00:00
4gl 5f5fb0116a fix: adding weboutBEGIN/END when exectask=true 2026-05-13 14:04:13 +01:00
Allan Bowe 9a725b9770 Merge pull request #423 from sasjs/CS0409737
Cs0409737
2026-05-13 09:59:47 +01:00
github-actions 6c77556fdb chore: updating all.sas 2026-05-13 08:57:58 +00:00
4gl 0e37bff1ee fix: workaround for sas track CS0409737 2026-05-13 09:57:33 +01:00
9 changed files with 627 additions and 64 deletions
+211 -27
View File
@@ -26312,13 +26312,13 @@ data _null_;
put '); '; put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name '; put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS; '; put ' sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS; ';
put '%if %index("&_debug",log) %then %let _debug=131; '; put '%if %index("&_debug",log) %then %let _debug=128; ';
put ' '; put ' ';
put '%local i tempds table; '; put '%local i tempds table; ';
put '%let action=%upcase(&action); '; put '%let action=%upcase(&action); ';
put ' '; put ' ';
put '%if &action=FETCH %then %do; '; put '%if &action=FETCH %then %do; ';
put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; '; put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 128 %then %do; ';
put ' options mprint notes mprintnest; '; put ' options mprint notes mprintnest; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
@@ -26326,6 +26326,11 @@ data _null_;
put ' %let _webin_file_count=%eval(&_webin_file_count+0); '; put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
put ' %let _webin_fileuri1=&_webin_fileuri; '; put ' %let _webin_fileuri1=&_webin_fileuri; ';
put ' %let _webin_name1=&_webin_name; '; put ' %let _webin_name1=&_webin_name; ';
put ' %if &_EXECUTIONTASKS=true %then %do; ';
put ' /* TODO - remove this once SAS Track CS0409737 is resolved */ ';
put ' /* links: https://github.com/sasjs/adapter/issues/884 */ ';
put ' %if %upcase(&_webin_name)=_SASJS_NOOP %then %let _webin_file_count=0; ';
put ' %end; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' /* if the sasjs_tables param is passed, we expect param based upload */ '; put ' /* if the sasjs_tables param is passed, we expect param based upload */ ';
@@ -26376,7 +26381,7 @@ data _null_;
put ' infile indata termstr=crlf lrecl=32767; '; put ' infile indata termstr=crlf lrecl=32767; ';
put ' input; '; put ' input; ';
put ' if _n_=1 then call symputx(''input_statement'',_infile_); '; put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&_debug) ge 128 %then %do; ';
put ' if _n_<20 then putlog _infile_; '; put ' if _n_<20 then putlog _infile_; ';
put ' else stop; '; put ' else stop; ';
put ' %end; '; put ' %end; ';
@@ -26409,6 +26414,9 @@ data _null_;
put ' '; put ' ';
put ' /* setup json */ '; put ' /* setup json */ ';
put ' data _null_;file &fref; '; put ' data _null_;file &fref; ';
put ' %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do; ';
put ' put ''>>weboutBEGIN<<''; ';
put ' %end; ';
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; '; put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; '; put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; '; put ' run; ';
@@ -26521,7 +26529,9 @@ data _null_;
put ' memsize=quote(cats(memsize)); '; put ' memsize=quote(cats(memsize)); ';
put ' put '',"MEMSIZE" : '' memsize; '; put ' put '',"MEMSIZE" : '' memsize; ';
put ' put "}"; '; put ' put "}"; ';
put ' '; put ' %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do; ';
put ' put ''>>weboutEND<<''; ';
put ' %end; ';
put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; '; put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; ';
put ' data _null_; rc=fcopy("&fref","_webout");run; '; put ' data _null_; rc=fcopy("&fref","_webout");run; ';
put ' %end; '; put ' %end; ';
@@ -29271,7 +29281,7 @@ run;
@li mf_nobs.sas @li mf_nobs.sas
@li mp_abort.sas @li mp_abort.sas
@li mf_getplatform.sas @li mf_getplatform.sas
@li mf_getuniquefileref.sas @li mf_getvarlist.sas
@li mf_existvarlist.sas @li mf_existvarlist.sas
@li mv_jobwaitfor.sas @li mv_jobwaitfor.sas
@li mv_jobexecute.sas @li mv_jobexecute.sas
@@ -29384,14 +29394,12 @@ data _null_;
if last then call symputx('flowcnt',cnt,'l'); if last then call symputx('flowcnt',cnt,'l');
run; run;
/* prepare temporary datasets and frefs */ /* prepare temporary datasets */
%local fid jid jds jjson jdsapp jdsrunning jdswaitfor jfref; %local fid jid jds jdsapp jdsrunning jdswaitfor;
data;run;%let jds=&syslast; data;run;%let jds=&syslast;
data;run;%let jjson=&syslast;
data;run;%let jdsapp=&syslast; data;run;%let jdsapp=&syslast;
data;run;%let jdsrunning=&syslast; data;run;%let jdsrunning=&syslast;
data;run;%let jdswaitfor=&syslast; data;run;%let jdswaitfor=&syslast;
%let jfref=%mf_getuniquefileref();
/* start loop */ /* start loop */
%do fid=1 %to &flowcnt; %do fid=1 %to &flowcnt;
@@ -29409,23 +29417,39 @@ data;run;%let jdswaitfor=&syslast;
&dbg. if _n_= 1 then putlog "Loop &fid"; &dbg. if _n_= 1 then putlog "Loop &fid";
&dbg. putlog (_all_)(=); &dbg. putlog (_all_)(=);
run; run;
/* build list of char and num vars in json format */
/* Viya 2026 expects all values to be strings */
%local nvars cvars ii _vnm;
%let cvars=%mf_getvarlist(&jds,typefilter=C);
%let nvars=%mf_getvarlist(&jds,typefilter=N);
%put exporting job variables in json format; %put exporting job variables in json format;
%do jid=1 %to &jcnt; %do jid=1 %to &jcnt;
data &jjson;
set &jds;
if _n_=&jid then do;
output;
stop;
end;
run;
proc json out=&jfref;
export &jjson / nosastags fmtnumeric;
run;
data _null_; data _null_;
infile &jfref lrecl=32767; set &jds;
input; if _n_=&jid;
jparams=cats('jparams',symget('jid')); length _param $32767;
call symputx(jparams,substr(_infile_,3,length(_infile_)-4)); _param='';
%if %length(&cvars)>0 %then %do ii=1 %to %sysfunc(countw(&cvars,%str( )));
%let _vnm=%scan(&cvars,&ii,%str( ));
if _param ne '' then _param=cats(_param,',');
_param=cats(_param,'"'
,"%lowcase(&_vnm)"
,'":'
,quote(trim(&_vnm))
);
%end;
%if %length(&nvars)>0 %then %do ii=1 %to %sysfunc(countw(&nvars,%str( )));
%let _vnm=%scan(&nvars,&ii,%str( ));
if _param ne '' then _param=cats(_param,',');
_param=cats(_param,'"'
,"%lowcase(&_vnm)"
,'":"'
,strip(put(&_vnm,best32.))
,'"'
);
%end;
call symputx(cats('jparams',&jid),_param,'l');
stop;
run; run;
%local jobuid&jid; %local jobuid&jid;
%let jobuid&jid=0; /* used in next loop */ %let jobuid&jid=0; /* used in next loop */
@@ -29470,6 +29494,7 @@ data;run;%let jdswaitfor=&syslast;
,paramstring=%superq(jparams&jid) ,paramstring=%superq(jparams&jid)
,outds=&jdsapp ,outds=&jdsapp
,contextname=&&context&jid ,contextname=&&context&jid
,mdebug=&mdebug
) )
data &jdsapp; data &jdsapp;
format jobparams $32767.; format jobparams $32767.;
@@ -30433,13 +30458,13 @@ filename &fref1 clear;
); );
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name %global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS; sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS;
%if %index("&_debug",log) %then %let _debug=131; %if %index("&_debug",log) %then %let _debug=128;
%local i tempds table; %local i tempds table;
%let action=%upcase(&action); %let action=%upcase(&action);
%if &action=FETCH %then %do; %if &action=FETCH %then %do;
%if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 128 %then %do;
options mprint notes mprintnest; options mprint notes mprintnest;
%end; %end;
@@ -30447,6 +30472,11 @@ filename &fref1 clear;
%let _webin_file_count=%eval(&_webin_file_count+0); %let _webin_file_count=%eval(&_webin_file_count+0);
%let _webin_fileuri1=&_webin_fileuri; %let _webin_fileuri1=&_webin_fileuri;
%let _webin_name1=&_webin_name; %let _webin_name1=&_webin_name;
%if &_EXECUTIONTASKS=true %then %do;
/* TODO - remove this once SAS Track CS0409737 is resolved */
/* links: https://github.com/sasjs/adapter/issues/884 */
%if %upcase(&_webin_name)=_SASJS_NOOP %then %let _webin_file_count=0;
%end;
%end; %end;
/* if the sasjs_tables param is passed, we expect param based upload */ /* if the sasjs_tables param is passed, we expect param based upload */
@@ -30497,7 +30527,7 @@ filename &fref1 clear;
infile indata termstr=crlf lrecl=32767; infile indata termstr=crlf lrecl=32767;
input; input;
if _n_=1 then call symputx('input_statement',_infile_); if _n_=1 then call symputx('input_statement',_infile_);
%if %str(&_debug) ge 131 %then %do; %if %str(&_debug) ge 128 %then %do;
if _n_<20 then putlog _infile_; if _n_<20 then putlog _infile_;
else stop; else stop;
%end; %end;
@@ -30530,6 +30560,9 @@ filename &fref1 clear;
/* setup json */ /* setup json */
data _null_;file &fref; data _null_;file &fref;
%if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
put '>>weboutBEGIN<<';
%end;
put '{"SYSDATE" : "' "&SYSDATE" '"'; put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"'; put ',"SYSTIME" : "' "&SYSTIME" '"';
run; run;
@@ -30642,7 +30675,9 @@ filename &fref1 clear;
memsize=quote(cats(memsize)); memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize; put ',"MEMSIZE" : ' memsize;
put "}"; put "}";
%if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
put '>>weboutEND<<';
%end;
%if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do;
data _null_; rc=fcopy("&fref","_webout");run; data _null_; rc=fcopy("&fref","_webout");run;
%end; %end;
@@ -31449,6 +31484,155 @@ endsub;
%end; %end;
%mend mcf_string2file;/** %mend mcf_string2file;/**
@file
@brief Appends a text file to a SASjs Stored Program, Viya SAS program, or
SAS 9 Stored Process
@details Extracts the source code from a SASjs Stored Program, Viya SAS
program (file in SAS Drive), or SAS 9 Stored Process, appends the contents
of a provided text file, then deletes and recreates the target item with the
combined content.
This is useful for dynamically modifying deployed programs, for example to
add test-specific configuration or runtime settings.
Usage:
%* compile macros ;
filename mc url
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* write some content to append;
filename append temp;
data _null_;
file append;
put "libname mylib '/some/path';";
run;
%* append to existing program;
%mx_append2pgm(/Public/app/common/settings, inref=append)
@param [in] loc The full path to the Viya SAS program, SAS 9 Stored Process,
or SASjs Stored Program in Drive or Metadata, WITHOUT the .sas extension
(SASjs only)
@param [in] inref= (0) Fileref pointing to the content to be appended to the
target program.
@param [in] mdebug= (0) Set to 1 to show debug messages in the log
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mm_createstp.sas
@li mm_deletestp.sas
@li mm_getstpcode.sas
@li ms_createfile.sas
@li ms_deletefile.sas
@li mv_createfile.sas
@li mv_deletefoldermember.sas
@li mx_getcode.sas
<h4> Related Macros </h4>
@li mx_append2pgm.test.sas
@li mx_getcode.sas
@li mx_createjob.sas
@author Allan Bowe
**/
%macro mx_append2pgm(loc
,inref=0
,mdebug=0
)/*/STORE SOURCE*/;
%local platform name shortloc coderef combref work tmpfile viyaref;
%let platform=%mf_getplatform();
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
%end;
%if &syscc ne 0 %then %do;
%put syscc=&syscc - &sysmacroname will not execute in this state;
%return;
%end;
/* extract name & path from loc */
data _null_;
length name shortloc $500;
loc=symget('loc');
name=scan(loc,-1,'/');
shortloc=substr(loc,1,length(loc)-length(name)-1);
call symputx('name',name,'l');
call symputx('shortloc',shortloc,'l');
run;
/* create a combined fileref with original + appended content */
%let combref=%mf_getuniquefileref();
%let work=%sysfunc(pathname(work));
%let tmpfile=&combref..sas;
filename &combref "&work/&tmpfile" lrecl=32000;
%if &platform=SASVIYA %then %do;
/* On Viya, read the SAS program file from SAS Drive using filesrvc */
%let viyaref=%mf_getuniquefileref();
filename &viyaref filesrvc folderpath="&shortloc";
data _null_;
file &combref lrecl=32000 termstr=crlf;
infile &viyaref("&name..sas") lrecl=32000 end=last;
input;
put _infile_;
run;
filename &viyaref clear;
%symdel _FILESRVC_&viyaref._URI;
%end;
%else %do;
/* For SAS9 and SASJS, use mx_getcode */
%let coderef=%mf_getuniquefileref();
%mx_getcode(&loc, outref=&coderef)
data _null_;
file &combref lrecl=32000 termstr=crlf;
infile &coderef lrecl=32000 end=last;
input;
put _infile_;
run;
filename &coderef clear;
%end;
/* append the new content */
data _null_;
file &combref lrecl=32000 termstr=crlf mod;
infile &inref lrecl=32000;
input;
put _infile_;
run;
/* delete and recreate the target item */
%if &platform=SASJS %then %do;
%ms_deletefile(&loc..sas)
%ms_createfile(&loc..sas, inref=&combref, mdebug=&mdebug)
%end;
%else %if &platform=SASVIYA %then %do;
%mv_deletefoldermember(path=&shortloc, name=&name..sas, contenttype=file)
%mv_createfile(path=&shortloc, name=&name..sas, inref=&combref)
%end;
%else %do;
/* SAS 9 */
%mm_deletestp(target=&loc)
%mm_createstp(stpname=&name
,filename=&tmpfile
,directory=&work
,tree=&shortloc
,stptype=2
,mDebug=&mdebug
,minify=NO
)
%end;
filename &combref clear;
%mend mx_append2pgm;
/**
@file mx_createjob.sas @file mx_createjob.sas
@brief Create a job in SAS 9, Viya or SASjs @brief Create a job in SAS 9, Viya or SASjs
@details Creates a Stored Process in SAS 9, a Job Execution Service in SAS @details Creates a Stored Process in SAS 9, a Job Execution Service in SAS
+3
View File
@@ -12,6 +12,8 @@
/* location in metadata or SAS Drive for temporary files */ /* location in metadata or SAS Drive for temporary files */
%let mcTestAppLoc=/Users/&sysuserid/testresults/sasjs_core/%mf_uid(); %let mcTestAppLoc=/Users/&sysuserid/testresults/sasjs_core/%mf_uid();
%let mcTestContext=SAS Job Execution compute context;
/* set defaults */ /* set defaults */
%mp_init() %mp_init()
@@ -23,6 +25,7 @@ options lrecl=80;
%macro loglevel(); %macro loglevel();
%if "&_debug"="2477" or "&_debug"="fields,log,trace" or "&_debug"="131" %if "&_debug"="2477" or "&_debug"="fields,log,trace" or "&_debug"="131"
or "&_debug"="128"
%then %do; %then %do;
%put debug mode activated; %put debug mode activated;
options mprint mprintnest; options mprint mprintnest;
+5 -5
View File
@@ -33,19 +33,19 @@ data _null_;
; ;
run; run;
%mv_createjob(path=/Public/temp,name=demo1,code=testprog) %mv_createjob(path=&mcTestAppLoc,name=demo1,code=testprog)
%mv_createjob(path=/Public/temp,name=demo2,code=testprog) %mv_createjob(path=&mcTestAppLoc,name=demo2,code=testprog)
data work.inputjobs; data work.inputjobs;
_contextName='SAS Job Execution compute context'; _contextName="&mcTestContext";
do flow_id=1 to 2; do flow_id=1 to 2;
do i=1 to 4; do i=1 to 4;
_program='/Public/temp/demo1'; _program="&mcTestAppLoc/demo1";
macrovar1=10*i; macrovar1=10*i;
macrovar2=4*i; macrovar2=4*i;
output; output;
i+1; i+1;
_program='/Public/temp/demo2'; _program="&mcTestAppLoc/demo2";
macrovar1=40*i; macrovar1=40*i;
macrovar2=44*i; macrovar2=44*i;
output; output;
+5 -5
View File
@@ -29,19 +29,19 @@ data _null_;
; ;
run; run;
%mv_createjob(path=/Public/temp,name=demo1,code=testprog) %mv_createjob(path=&mcTestAppLoc,name=demo1,code=testprog)
%mv_createjob(path=/Public/temp,name=demo2,code=testprog) %mv_createjob(path=&mcTestAppLoc,name=demo2,code=testprog)
data work.inputjobs; data work.inputjobs;
_contextName='SAS Job Execution compute context'; _contextName="&mcTestContext";
do flow_id=1 to 2; do flow_id=1 to 2;
do i=1 to 4; do i=1 to 4;
_program='/Public/temp/demo1'; _program="&mcTestAppLoc/demo1";
macrovar1=10*i; macrovar1=10*i;
macrovar2=4*i; macrovar2=4*i;
output; output;
i+1; i+1;
_program='/Public/temp/demo2'; _program="&mcTestAppLoc/demo2";
macrovar1=40*i; macrovar1=40*i;
macrovar2=44*i; macrovar2=44*i;
output; output;
+192
View File
@@ -0,0 +1,192 @@
/**
@file
@brief Testing mx_append2pgm.sas macro
Be sure to run <code>%let mcTestAppLoc=/Public/temp/macrocore;</code> when
running in Studio
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_uid.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li ms_createfile.sas
@li mv_createfile.sas
@li mm_createstp.sas
@li mx_append2pgm.sas
@li mx_getcode.sas
**/
/**
* Test 1 - Append content to an existing program and verify combined output
* Also checking for scope leakage
*/
/* create a unique name for the program */
%let item=test_%mf_uid();
/* create the initial program with some code */
filename initpgm temp;
data _null_;
file initpgm;
put '%put ORIGINAL LINE;';
run;
%macro setup_pgm();
%let platform=%mf_getplatform();
%if &platform=SASJS %then %do;
%ms_createfile(&mcTestAppLoc/temp/&item..sas, inref=initpgm)
%end;
%else %if &platform=SASVIYA %then %do;
%mv_createfile(path=&mcTestAppLoc/temp, name=&item..sas, inref=initpgm)
%end;
%else %do;
%let work=%sysfunc(pathname(work));
data _null_;
file "&work/&item..sas";
infile initpgm;
input;
put _infile_;
run;
%mm_createstp(stpname=&item
,filename=&item..sas
,directory=&work
,tree=&mcTestAppLoc/temp
,stptype=2
,minify=NO
)
%end;
%mend setup_pgm;
%setup_pgm()
/* create the content to append */
filename toappnd temp;
data _null_;
file toappnd;
put '%put APPENDED LINE;';
run;
/* run the macro under test with scope checks */
%mp_assertscope(SNAPSHOT)
%mx_append2pgm(&mcTestAppLoc/temp/&item, inref=toappnd)
%mp_assertscope(COMPARE,
desc=Test 1: mx_append2pgm does not leak scope,
outds=work.test_results,
ignorelist=MC2_JADP1LEN MC2_JADP2LEN MC2_JADPNUM MC2_JADVLEN MC2_JADP3LEN
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 1: No errors after appending content to program,
outds=work.test_results
)
/**
* Test 2 - Verify the appended content is present
* Fetch the modified program and check both original and appended lines exist
*/
%let test2_orig=0;
%let test2_appd=0;
%macro verify_test2();
%let platform=%mf_getplatform();
%if &platform=SASVIYA %then %do;
filename verifrf filesrvc folderpath="&mcTestAppLoc/temp";
data _null_;
infile verifrf("&item..sas") lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test2_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test2_appd','1');
run;
filename verifrf clear;
%end;
%else %do;
%mx_getcode(&mcTestAppLoc/temp/&item, outref=verifrf)
data _null_;
infile verifrf lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test2_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test2_appd','1');
run;
%end;
%mend verify_test2;
%verify_test2()
%mp_assert(
iftrue=(&test2_orig=1),
desc=Test 2a: Original content is preserved after append,
outds=work.test_results
)
%mp_assert(
iftrue=(&test2_appd=1),
desc=Test 2b: Appended content is present in modified program,
outds=work.test_results
)
/**
* Test 3 - Append multiple times to ensure repeated appends work
*/
filename toappd2 temp;
data _null_;
file toappd2;
put '%put SECOND APPEND;';
run;
%mp_assertscope(SNAPSHOT)
%mx_append2pgm(&mcTestAppLoc/temp/&item, inref=toappd2)
%mp_assertscope(COMPARE,
desc=Test 3: mx_append2pgm does not leak scope on second call,
outds=work.test_results
)
/* verify all three pieces of content exist */
%let test3_orig=0;
%let test3_appd=0;
%let test3_app2=0;
%macro verify_test3();
%let platform=%mf_getplatform();
%if &platform=SASVIYA %then %do;
filename verifr2 filesrvc folderpath="&mcTestAppLoc/temp";
data _null_;
infile verifr2("&item..sas") lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test3_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test3_appd','1');
if index(_infile_,'SECOND APPEND') then call symputx('test3_app2','1');
run;
filename verifr2 clear;
%end;
%else %do;
%mx_getcode(&mcTestAppLoc/temp/&item, outref=verifr2)
data _null_;
infile verifr2 lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test3_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test3_appd','1');
if index(_infile_,'SECOND APPEND') then call symputx('test3_app2','1');
run;
%end;
%mend verify_test3;
%verify_test3()
%mp_assert(
iftrue=(&test3_orig=1),
desc=Test 3a: Original content still present after second append,
outds=work.test_results
)
%mp_assert(
iftrue=(&test3_appd=1),
desc=Test 3b: First appended content still present after second append,
outds=work.test_results
)
%mp_assert(
iftrue=(&test3_app2=1),
desc=Test 3c: Second appended content is present,
outds=work.test_results
)
+14 -4
View File
@@ -595,13 +595,13 @@ data _null_;
put '); '; put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name '; put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS; '; put ' sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS; ';
put '%if %index("&_debug",log) %then %let _debug=131; '; put '%if %index("&_debug",log) %then %let _debug=128; ';
put ' '; put ' ';
put '%local i tempds table; '; put '%local i tempds table; ';
put '%let action=%upcase(&action); '; put '%let action=%upcase(&action); ';
put ' '; put ' ';
put '%if &action=FETCH %then %do; '; put '%if &action=FETCH %then %do; ';
put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; '; put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 128 %then %do; ';
put ' options mprint notes mprintnest; '; put ' options mprint notes mprintnest; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
@@ -609,6 +609,11 @@ data _null_;
put ' %let _webin_file_count=%eval(&_webin_file_count+0); '; put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
put ' %let _webin_fileuri1=&_webin_fileuri; '; put ' %let _webin_fileuri1=&_webin_fileuri; ';
put ' %let _webin_name1=&_webin_name; '; put ' %let _webin_name1=&_webin_name; ';
put ' %if &_EXECUTIONTASKS=true %then %do; ';
put ' /* TODO - remove this once SAS Track CS0409737 is resolved */ ';
put ' /* links: https://github.com/sasjs/adapter/issues/884 */ ';
put ' %if %upcase(&_webin_name)=_SASJS_NOOP %then %let _webin_file_count=0; ';
put ' %end; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' /* if the sasjs_tables param is passed, we expect param based upload */ '; put ' /* if the sasjs_tables param is passed, we expect param based upload */ ';
@@ -659,7 +664,7 @@ data _null_;
put ' infile indata termstr=crlf lrecl=32767; '; put ' infile indata termstr=crlf lrecl=32767; ';
put ' input; '; put ' input; ';
put ' if _n_=1 then call symputx(''input_statement'',_infile_); '; put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&_debug) ge 128 %then %do; ';
put ' if _n_<20 then putlog _infile_; '; put ' if _n_<20 then putlog _infile_; ';
put ' else stop; '; put ' else stop; ';
put ' %end; '; put ' %end; ';
@@ -692,6 +697,9 @@ data _null_;
put ' '; put ' ';
put ' /* setup json */ '; put ' /* setup json */ ';
put ' data _null_;file &fref; '; put ' data _null_;file &fref; ';
put ' %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do; ';
put ' put ''>>weboutBEGIN<<''; ';
put ' %end; ';
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; '; put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; '; put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; '; put ' run; ';
@@ -804,7 +812,9 @@ data _null_;
put ' memsize=quote(cats(memsize)); '; put ' memsize=quote(cats(memsize)); ';
put ' put '',"MEMSIZE" : '' memsize; '; put ' put '',"MEMSIZE" : '' memsize; ';
put ' put "}"; '; put ' put "}"; ';
put ' '; put ' %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do; ';
put ' put ''>>weboutEND<<''; ';
put ' %end; ';
put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; '; put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; ';
put ' data _null_; rc=fcopy("&fref","_webout");run; '; put ' data _null_; rc=fcopy("&fref","_webout");run; ';
put ' %end; '; put ' %end; ';
+34 -19
View File
@@ -121,7 +121,7 @@
@li mf_nobs.sas @li mf_nobs.sas
@li mp_abort.sas @li mp_abort.sas
@li mf_getplatform.sas @li mf_getplatform.sas
@li mf_getuniquefileref.sas @li mf_getvarlist.sas
@li mf_existvarlist.sas @li mf_existvarlist.sas
@li mv_jobwaitfor.sas @li mv_jobwaitfor.sas
@li mv_jobexecute.sas @li mv_jobexecute.sas
@@ -234,14 +234,12 @@ data _null_;
if last then call symputx('flowcnt',cnt,'l'); if last then call symputx('flowcnt',cnt,'l');
run; run;
/* prepare temporary datasets and frefs */ /* prepare temporary datasets */
%local fid jid jds jjson jdsapp jdsrunning jdswaitfor jfref; %local fid jid jds jdsapp jdsrunning jdswaitfor;
data;run;%let jds=&syslast; data;run;%let jds=&syslast;
data;run;%let jjson=&syslast;
data;run;%let jdsapp=&syslast; data;run;%let jdsapp=&syslast;
data;run;%let jdsrunning=&syslast; data;run;%let jdsrunning=&syslast;
data;run;%let jdswaitfor=&syslast; data;run;%let jdswaitfor=&syslast;
%let jfref=%mf_getuniquefileref();
/* start loop */ /* start loop */
%do fid=1 %to &flowcnt; %do fid=1 %to &flowcnt;
@@ -259,23 +257,39 @@ data;run;%let jdswaitfor=&syslast;
&dbg. if _n_= 1 then putlog "Loop &fid"; &dbg. if _n_= 1 then putlog "Loop &fid";
&dbg. putlog (_all_)(=); &dbg. putlog (_all_)(=);
run; run;
/* build list of char and num vars in json format */
/* Viya 2026 expects all values to be strings */
%local nvars cvars ii _vnm;
%let cvars=%mf_getvarlist(&jds,typefilter=C);
%let nvars=%mf_getvarlist(&jds,typefilter=N);
%put exporting job variables in json format; %put exporting job variables in json format;
%do jid=1 %to &jcnt; %do jid=1 %to &jcnt;
data &jjson;
set &jds;
if _n_=&jid then do;
output;
stop;
end;
run;
proc json out=&jfref;
export &jjson / nosastags fmtnumeric;
run;
data _null_; data _null_;
infile &jfref lrecl=32767; set &jds;
input; if _n_=&jid;
jparams=cats('jparams',symget('jid')); length _param $32767;
call symputx(jparams,substr(_infile_,3,length(_infile_)-4)); _param='';
%if %length(&cvars)>0 %then %do ii=1 %to %sysfunc(countw(&cvars,%str( )));
%let _vnm=%scan(&cvars,&ii,%str( ));
if _param ne '' then _param=cats(_param,',');
_param=cats(_param,'"'
,"%lowcase(&_vnm)"
,'":'
,quote(trim(&_vnm))
);
%end;
%if %length(&nvars)>0 %then %do ii=1 %to %sysfunc(countw(&nvars,%str( )));
%let _vnm=%scan(&nvars,&ii,%str( ));
if _param ne '' then _param=cats(_param,',');
_param=cats(_param,'"'
,"%lowcase(&_vnm)"
,'":"'
,strip(put(&_vnm,best32.))
,'"'
);
%end;
call symputx(cats('jparams',&jid),_param,'l');
stop;
run; run;
%local jobuid&jid; %local jobuid&jid;
%let jobuid&jid=0; /* used in next loop */ %let jobuid&jid=0; /* used in next loop */
@@ -320,6 +334,7 @@ data;run;%let jdswaitfor=&syslast;
,paramstring=%superq(jparams&jid) ,paramstring=%superq(jparams&jid)
,outds=&jdsapp ,outds=&jdsapp
,contextname=&&context&jid ,contextname=&&context&jid
,mdebug=&mdebug
) )
data &jdsapp; data &jdsapp;
format jobparams $32767.; format jobparams $32767.;
+14 -4
View File
@@ -56,13 +56,13 @@
); );
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name %global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS; sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS;
%if %index("&_debug",log) %then %let _debug=131; %if %index("&_debug",log) %then %let _debug=128;
%local i tempds table; %local i tempds table;
%let action=%upcase(&action); %let action=%upcase(&action);
%if &action=FETCH %then %do; %if &action=FETCH %then %do;
%if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 128 %then %do;
options mprint notes mprintnest; options mprint notes mprintnest;
%end; %end;
@@ -70,6 +70,11 @@
%let _webin_file_count=%eval(&_webin_file_count+0); %let _webin_file_count=%eval(&_webin_file_count+0);
%let _webin_fileuri1=&_webin_fileuri; %let _webin_fileuri1=&_webin_fileuri;
%let _webin_name1=&_webin_name; %let _webin_name1=&_webin_name;
%if &_EXECUTIONTASKS=true %then %do;
/* TODO - remove this once SAS Track CS0409737 is resolved */
/* links: https://github.com/sasjs/adapter/issues/884 */
%if %upcase(&_webin_name)=_SASJS_NOOP %then %let _webin_file_count=0;
%end;
%end; %end;
/* if the sasjs_tables param is passed, we expect param based upload */ /* if the sasjs_tables param is passed, we expect param based upload */
@@ -120,7 +125,7 @@
infile indata termstr=crlf lrecl=32767; infile indata termstr=crlf lrecl=32767;
input; input;
if _n_=1 then call symputx('input_statement',_infile_); if _n_=1 then call symputx('input_statement',_infile_);
%if %str(&_debug) ge 131 %then %do; %if %str(&_debug) ge 128 %then %do;
if _n_<20 then putlog _infile_; if _n_<20 then putlog _infile_;
else stop; else stop;
%end; %end;
@@ -153,6 +158,9 @@
/* setup json */ /* setup json */
data _null_;file &fref; data _null_;file &fref;
%if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
put '>>weboutBEGIN<<';
%end;
put '{"SYSDATE" : "' "&SYSDATE" '"'; put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"'; put ',"SYSTIME" : "' "&SYSTIME" '"';
run; run;
@@ -265,7 +273,9 @@
memsize=quote(cats(memsize)); memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize; put ',"MEMSIZE" : ' memsize;
put "}"; put "}";
%if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
put '>>weboutEND<<';
%end;
%if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do;
data _null_; rc=fcopy("&fref","_webout");run; data _null_; rc=fcopy("&fref","_webout");run;
%end; %end;
+149
View File
@@ -0,0 +1,149 @@
/**
@file
@brief Appends a text file to a SASjs Stored Program, Viya SAS program, or
SAS 9 Stored Process
@details Extracts the source code from a SASjs Stored Program, Viya SAS
program (file in SAS Drive), or SAS 9 Stored Process, appends the contents
of a provided text file, then deletes and recreates the target item with the
combined content.
This is useful for dynamically modifying deployed programs, for example to
add test-specific configuration or runtime settings.
Usage:
%* compile macros ;
filename mc url
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* write some content to append;
filename append temp;
data _null_;
file append;
put "libname mylib '/some/path';";
run;
%* append to existing program;
%mx_append2pgm(/Public/app/common/settings, inref=append)
@param [in] loc The full path to the Viya SAS program, SAS 9 Stored Process,
or SASjs Stored Program in Drive or Metadata, WITHOUT the .sas extension
(SASjs only)
@param [in] inref= (0) Fileref pointing to the content to be appended to the
target program.
@param [in] mdebug= (0) Set to 1 to show debug messages in the log
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mm_createstp.sas
@li mm_deletestp.sas
@li mm_getstpcode.sas
@li ms_createfile.sas
@li ms_deletefile.sas
@li mv_createfile.sas
@li mv_deletefoldermember.sas
@li mx_getcode.sas
<h4> Related Macros </h4>
@li mx_append2pgm.test.sas
@li mx_getcode.sas
@li mx_createjob.sas
@author Allan Bowe
**/
%macro mx_append2pgm(loc
,inref=0
,mdebug=0
)/*/STORE SOURCE*/;
%local platform name shortloc coderef combref work tmpfile viyaref;
%let platform=%mf_getplatform();
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
%end;
%if &syscc ne 0 %then %do;
%put syscc=&syscc - &sysmacroname will not execute in this state;
%return;
%end;
/* extract name & path from loc */
data _null_;
length name shortloc $500;
loc=symget('loc');
name=scan(loc,-1,'/');
shortloc=substr(loc,1,length(loc)-length(name)-1);
call symputx('name',name,'l');
call symputx('shortloc',shortloc,'l');
run;
/* create a combined fileref with original + appended content */
%let combref=%mf_getuniquefileref();
%let work=%sysfunc(pathname(work));
%let tmpfile=&combref..sas;
filename &combref "&work/&tmpfile" lrecl=32000;
%if &platform=SASVIYA %then %do;
/* On Viya, read the SAS program file from SAS Drive using filesrvc */
%let viyaref=%mf_getuniquefileref();
filename &viyaref filesrvc folderpath="&shortloc";
data _null_;
file &combref lrecl=32000 termstr=crlf;
infile &viyaref("&name..sas") lrecl=32000 end=last;
input;
put _infile_;
run;
filename &viyaref clear;
%symdel _FILESRVC_&viyaref._URI;
%end;
%else %do;
/* For SAS9 and SASJS, use mx_getcode */
%let coderef=%mf_getuniquefileref();
%mx_getcode(&loc, outref=&coderef)
data _null_;
file &combref lrecl=32000 termstr=crlf;
infile &coderef lrecl=32000 end=last;
input;
put _infile_;
run;
filename &coderef clear;
%end;
/* append the new content */
data _null_;
file &combref lrecl=32000 termstr=crlf mod;
infile &inref lrecl=32000;
input;
put _infile_;
run;
/* delete and recreate the target item */
%if &platform=SASJS %then %do;
%ms_deletefile(&loc..sas)
%ms_createfile(&loc..sas, inref=&combref, mdebug=&mdebug)
%end;
%else %if &platform=SASVIYA %then %do;
%mv_deletefoldermember(path=&shortloc, name=&name..sas, contenttype=file)
%mv_createfile(path=&shortloc, name=&name..sas, inref=&combref)
%end;
%else %do;
/* SAS 9 */
%mm_deletestp(target=&loc)
%mm_createstp(stpname=&name
,filename=&tmpfile
,directory=&work
,tree=&shortloc
,stptype=2
,mDebug=&mdebug
,minify=NO
)
%end;
filename &combref clear;
%mend mx_append2pgm;