Compare commits

...
14 Commits
Author SHA1 Message Date
Allan BoweandGitHub d489c98b87 Merge pull request #431 from sasjs/mx_createjob
fix: updates to make tests pass on viya and sasjs server for mx_creat…
2026-06-26 12:30:35 +01:00
github-actions 9d342ff0ee chore: updating all.sas 2026-06-26 11:26:22 +00:00
allan 46ae27b182 fix: updates to make tests pass on viya and sasjs server for mx_createjob 2026-06-26 12:26:03 +01:00
Allan BoweandGitHub 8239b5e45b Merge pull request #430 from sasjs/mp_retainedkey
chore: using oidc npm token
2026-06-25 14:55:14 +01:00
Allan BoweandGitHub 49171d7467 Merge branch 'main' into mp_retainedkey 2026-06-25 14:54:53 +01:00
github-actions f8a6841948 chore: updating all.sas 2026-06-25 13:54:26 +00:00
allan 199788ffe4 chore: using oidc npm token 2026-06-25 14:54:05 +01:00
Allan BoweandGitHub 8624025a91 Merge pull request #429 from sasjs/mp_retainedkey
chore: adding new test to mp_retainedkey
2026-06-25 14:40:52 +01:00
github-actions 8bfc899ed4 chore: updating all.sas 2026-06-25 13:39:03 +00:00
allan d52b9a6eea chore: adding new test to mp_retainedkey 2026-06-25 14:38:41 +01:00
allan 3d6b9d0da5 fix: allow large numerics in mp_retainedkey 2026-06-25 14:23:53 +01:00
Allan BoweandGitHub 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
17 changed files with 178 additions and 72 deletions
+17 -1
View File
@@ -11,10 +11,24 @@ on:
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
id-token: write # required for npm Trusted Publishing (OIDC)
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
registry-url: 'https://registry.npmjs.org'
- name: Ensure npm supports Trusted Publishing
run: npm install -g npm@latest
- name: Install dependencies
run: |
npm ci
@@ -58,7 +72,9 @@ jobs:
uses: cycjimmy/semantic-release-action@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# NPM_TOKEN no longer required: package is published via npm
# Trusted Publishing (OIDC). The id-token: write permission above
# allows npm CLI to authenticate to the registry automatically.
- name: SAS Packages Release
run: |
+46 -29
View File
@@ -3495,7 +3495,7 @@ run;
)/*/STORE SOURCE*/;
data ;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_comments="&sysmacroname: Test result of "!!symget('iftrue');
%if %eval(%unquote(&iftrue)) %then %do;
@@ -3624,7 +3624,7 @@ run;
%end;
data;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
if test_description='0'
then test_description="Testing &inds for existence of &test of: &cols";
@@ -3803,7 +3803,7 @@ run;
)
data;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result/&orig values "
@@ -3918,7 +3918,7 @@ run;
%end;
data &ds;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: Dataset &inds has &nobs observations.";
@@ -4082,7 +4082,7 @@ run;
%end;
data ;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_comments=symget('test_comments');
test_result=symget('test_result');
@@ -10101,6 +10101,7 @@ filename &tempref clear;
%global
SASJS_PREFIX /* the ONLY hard-coded global macro variable in SASjs */
&prefix.PROCESSMODE &prefix._STPSRV_HEADER_LOC /* SASjs server specific vars*/
&prefix._FUNCTIONS /* used in mcf_init() to track core function compilation */
&prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
@@ -12184,7 +12185,8 @@ https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-s
%mp_abort(iftrue=(&iserr=1),mac=mp_retainedkey,msg=%superq(msg))
proc sql noprint;
select sum(max(&retained_key),0) into: maxkey from &base_libds;
select sum(max(&retained_key),0) format=32. into: maxkey trimmed
from &base_libds;
/**
* get base table RK and bus field values for lookup
@@ -12253,19 +12255,19 @@ quit;
insert into &maxkeytable
set keytable="&base_libds"
,keycolumn="&retained_key"
,max_key=%eval(&maxkey+&newkey_cnt)
,max_key=%sysevalf(&maxkey+&newkey_cnt)
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
%end;
%else %do;
update &maxkeytable
set max_key=%eval(&maxkey+&newkey_cnt)
set max_key=%sysevalf(&maxkey+&newkey_cnt)
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
where keytable="&base_libds";
%end;
%mp_lockanytable(UNLOCK
,lib=%scan(&maxkeytable,1,.)
,ds=%scan(&maxkeytable,2,.)
,ref=Updating maxkeyvalues with maxkey=%eval(&maxkey+&newkey_cnt)
,ref=Updating maxkeyvalues with maxkey=%sysevalf(&maxkey+&newkey_cnt)
,ctl_ds=&locktable
)
%end;
@@ -12273,7 +12275,7 @@ quit;
/* fill in the missing retained key values */
%let tempvar=%mf_getuniquename();
data &outds(drop=&tempvar);
retain &tempvar %eval(&maxkey+1);
retain &tempvar %sysevalf(&maxkey+1);
set &tempds2;
if &retained_key =. then &retained_key=&tempvar;
&tempvar=&tempvar+1;
@@ -29281,7 +29283,7 @@ run;
@li mf_nobs.sas
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getvarlist.sas
@li mf_existvarlist.sas
@li mv_jobwaitfor.sas
@li mv_jobexecute.sas
@@ -29394,14 +29396,12 @@ data _null_;
if last then call symputx('flowcnt',cnt,'l');
run;
/* prepare temporary datasets and frefs */
%local fid jid jds jjson jdsapp jdsrunning jdswaitfor jfref;
/* prepare temporary datasets */
%local fid jid jds jdsapp jdsrunning jdswaitfor;
data;run;%let jds=&syslast;
data;run;%let jjson=&syslast;
data;run;%let jdsapp=&syslast;
data;run;%let jdsrunning=&syslast;
data;run;%let jdswaitfor=&syslast;
%let jfref=%mf_getuniquefileref();
/* start loop */
%do fid=1 %to &flowcnt;
@@ -29419,23 +29419,39 @@ data;run;%let jdswaitfor=&syslast;
&dbg. if _n_= 1 then putlog "Loop &fid";
&dbg. putlog (_all_)(=);
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;
%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_;
infile &jfref lrecl=32767;
input;
jparams=cats('jparams',symget('jid'));
call symputx(jparams,substr(_infile_,3,length(_infile_)-4));
set &jds;
if _n_=&jid;
length _param $32767;
_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;
%local jobuid&jid;
%let jobuid&jid=0; /* used in next loop */
@@ -29480,6 +29496,7 @@ data;run;%let jdswaitfor=&syslast;
,paramstring=%superq(jparams&jid)
,outds=&jdsapp
,contextname=&&context&jid
,mdebug=&mdebug
)
data &jdsapp;
format jobparams $32767.;
+1 -1
View File
@@ -35,7 +35,7 @@
)/*/STORE SOURCE*/;
data ;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_comments="&sysmacroname: Test result of "!!symget('iftrue');
%if %eval(%unquote(&iftrue)) %then %do;
+1 -1
View File
@@ -108,7 +108,7 @@
%end;
data;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
if test_description='0'
then test_description="Testing &inds for existence of &test of: &cols";
+1 -1
View File
@@ -143,7 +143,7 @@
)
data;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result/&orig values "
+1 -1
View File
@@ -87,7 +87,7 @@
%end;
data &ds;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: Dataset &inds has &nobs observations.";
+1 -1
View File
@@ -131,7 +131,7 @@
%end;
data ;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
test_description=symget('desc');
test_comments=symget('test_comments');
test_result=symget('test_result');
+1
View File
@@ -37,6 +37,7 @@
%global
SASJS_PREFIX /* the ONLY hard-coded global macro variable in SASjs */
&prefix.PROCESSMODE &prefix._STPSRV_HEADER_LOC /* SASjs server specific vars*/
&prefix._FUNCTIONS /* used in mcf_init() to track core function compilation */
&prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
+6 -5
View File
@@ -148,7 +148,8 @@
%mp_abort(iftrue=(&iserr=1),mac=mp_retainedkey,msg=%superq(msg))
proc sql noprint;
select sum(max(&retained_key),0) into: maxkey from &base_libds;
select sum(max(&retained_key),0) format=32. into: maxkey trimmed
from &base_libds;
/**
* get base table RK and bus field values for lookup
@@ -217,19 +218,19 @@ quit;
insert into &maxkeytable
set keytable="&base_libds"
,keycolumn="&retained_key"
,max_key=%eval(&maxkey+&newkey_cnt)
,max_key=%sysevalf(&maxkey+&newkey_cnt)
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
%end;
%else %do;
update &maxkeytable
set max_key=%eval(&maxkey+&newkey_cnt)
set max_key=%sysevalf(&maxkey+&newkey_cnt)
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
where keytable="&base_libds";
%end;
%mp_lockanytable(UNLOCK
,lib=%scan(&maxkeytable,1,.)
,ds=%scan(&maxkeytable,2,.)
,ref=Updating maxkeyvalues with maxkey=%eval(&maxkey+&newkey_cnt)
,ref=Updating maxkeyvalues with maxkey=%sysevalf(&maxkey+&newkey_cnt)
,ctl_ds=&locktable
)
%end;
@@ -237,7 +238,7 @@ quit;
/* fill in the missing retained key values */
%let tempvar=%mf_getuniquename();
data &outds(drop=&tempvar);
retain &tempvar %eval(&maxkey+1);
retain &tempvar %sysevalf(&maxkey+1);
set &tempds2;
if &retained_key =. then &retained_key=&tempvar;
&tempvar=&tempvar+1;
+1 -1
View File
@@ -15,7 +15,7 @@
%let test5=%mf_getvarlist(sashelp.class,typefilter=C);
data work.test_results;
length test_description $256 test_result $4 test_comments base result $256;
length test_result $4 test_description $256 test_comments base result $256;
test_description="Basic test";
base=symget('test1');
result='Name Sex Age Height Weight';
+51
View File
@@ -5,6 +5,7 @@
<h4> SAS Macros </h4>
@li mp_assert.sas
@li mp_assertcolvals.sas
@li mp_assertscope.sas
@li mp_retainedkey.sas
**/
@@ -114,3 +115,53 @@ run;
desc=Checking append records created,
outds=work.test_results
)
/** Test 3 - large numeric RK values (avoid exponential notation issue) **/
data work.targetds3;
set sashelp.class;
rk_col=_n_+209100000000;
run;
data work.appendtable3;
set sashelp.class;
if mod(_n_,2)=0 then name=cats('New',_n_);
if _n_<7;
run;
%mp_assertscope(SNAPSHOT)
%mp_retainedkey(
base_lib=X
,base_dsn=targetds3
,append_lib=X
,append_dsn=APPENDTABLE3
,retained_key=rk_col
,business_key= name
,check_uniqueness=NO
,maxkeytable=0
,locktable=0
,outds=work.APPEND3
,filter_str=
)
%mp_assertscope(COMPARE,
desc=Checking macro scope leakage in mp_retainedkey (test 3)
)
%mp_assert(
iftrue=(&syscc=0),
desc=Checking errors in test 3 (large numeric RK),
outds=work.test_results
)
data work.check3;
do val=209100000001,209100000003,209100000005,
209100000020,209100000021,209100000022;
output;
end;
run;
%mp_assertcolvals(work.append3.rk_col,
checkvals=work.check3.val,
desc=All large numeric values have a match,
test=ALLVALS
)
+2
View File
@@ -12,6 +12,8 @@
/* location in metadata or SAS Drive for temporary files */
%let mcTestAppLoc=/Users/&sysuserid/testresults/sasjs_core/%mf_uid();
%let mcTestContext=SAS Job Execution compute context;
/* set defaults */
%mp_init()
+1 -1
View File
@@ -32,7 +32,7 @@ run;
)
%put TEST1: checking web service code;
data work.test_results;
length test_description $256 test_result $4 test_comments $256;
length test_result $4 test_description $256 test_comments $256;
if _n_=1 then call missing (of _all_);
infile compare end=eof;
input;
+5 -5
View File
@@ -33,19 +33,19 @@ data _null_;
;
run;
%mv_createjob(path=/Public/temp,name=demo1,code=testprog)
%mv_createjob(path=/Public/temp,name=demo2,code=testprog)
%mv_createjob(path=&mcTestAppLoc,name=demo1,code=testprog)
%mv_createjob(path=&mcTestAppLoc,name=demo2,code=testprog)
data work.inputjobs;
_contextName='SAS Job Execution compute context';
_contextName="&mcTestContext";
do flow_id=1 to 2;
do i=1 to 4;
_program='/Public/temp/demo1';
_program="&mcTestAppLoc/demo1";
macrovar1=10*i;
macrovar2=4*i;
output;
i+1;
_program='/Public/temp/demo2';
_program="&mcTestAppLoc/demo2";
macrovar1=40*i;
macrovar2=44*i;
output;
+5 -5
View File
@@ -29,19 +29,19 @@ data _null_;
;
run;
%mv_createjob(path=/Public/temp,name=demo1,code=testprog)
%mv_createjob(path=/Public/temp,name=demo2,code=testprog)
%mv_createjob(path=&mcTestAppLoc,name=demo1,code=testprog)
%mv_createjob(path=&mcTestAppLoc,name=demo2,code=testprog)
data work.inputjobs;
_contextName='SAS Job Execution compute context';
_contextName="&mcTestContext";
do flow_id=1 to 2;
do i=1 to 4;
_program='/Public/temp/demo1';
_program="&mcTestAppLoc/demo1";
macrovar1=10*i;
macrovar2=4*i;
output;
i+1;
_program='/Public/temp/demo2';
_program="&mcTestAppLoc/demo2";
macrovar1=40*i;
macrovar2=44*i;
output;
+4 -1
View File
@@ -26,7 +26,10 @@ parmcards4;
;;;;
%mp_assertscope(SNAPSHOT)
%mx_createjob(path=&mcTestAppLoc/jobs,name=testjob1,replace=YES)
%mp_assertscope(COMPARE)
%mp_assertscope(COMPARE,ignorelist=MC0_JADP1LEN MC0_JADP2LEN MC0_JADPNUM
MC0_JADVLEN MC2_JADP1LEN MC2_JADP2LEN MC2_JADPNUM MC2_JADVLEN MC4_JADP1LEN
MC4_JADP2LEN MC4_JADPNUM MC4_JADVLEN
)
%mp_assert(
iftrue=(&syscc=0),
+34 -19
View File
@@ -121,7 +121,7 @@
@li mf_nobs.sas
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getvarlist.sas
@li mf_existvarlist.sas
@li mv_jobwaitfor.sas
@li mv_jobexecute.sas
@@ -234,14 +234,12 @@ data _null_;
if last then call symputx('flowcnt',cnt,'l');
run;
/* prepare temporary datasets and frefs */
%local fid jid jds jjson jdsapp jdsrunning jdswaitfor jfref;
/* prepare temporary datasets */
%local fid jid jds jdsapp jdsrunning jdswaitfor;
data;run;%let jds=&syslast;
data;run;%let jjson=&syslast;
data;run;%let jdsapp=&syslast;
data;run;%let jdsrunning=&syslast;
data;run;%let jdswaitfor=&syslast;
%let jfref=%mf_getuniquefileref();
/* start loop */
%do fid=1 %to &flowcnt;
@@ -259,23 +257,39 @@ data;run;%let jdswaitfor=&syslast;
&dbg. if _n_= 1 then putlog "Loop &fid";
&dbg. putlog (_all_)(=);
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;
%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_;
infile &jfref lrecl=32767;
input;
jparams=cats('jparams',symget('jid'));
call symputx(jparams,substr(_infile_,3,length(_infile_)-4));
set &jds;
if _n_=&jid;
length _param $32767;
_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;
%local jobuid&jid;
%let jobuid&jid=0; /* used in next loop */
@@ -320,6 +334,7 @@ data;run;%let jdswaitfor=&syslast;
,paramstring=%superq(jparams&jid)
,outds=&jdsapp
,contextname=&&context&jid
,mdebug=&mdebug
)
data &jdsapp;
format jobparams $32767.;