1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-11 14:34:35 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Allan Bowe
3ce771d587 Merge pull request #205 from sasjs/issue204
fix: updating mp_hashdataset to cope with STRICT mode.  Adding test a…
2022-03-29 15:47:17 +03:00
munja
72d6b446c3 fix: updating mp_hashdataset to cope with STRICT mode. Adding test and improving sasjs/server compatibility. 2022-03-29 13:22:24 +01:00
Allan Bowe
40d694eec8 Merge pull request #203 from sasjs/invisibles
fix: support SOH, STX and DC1 control characters in mp_jsonout
2022-03-29 12:02:09 +03:00
munja
6af1423666 fix: more invisibles 2022-03-28 16:26:58 +01:00
munja
23a01347f1 fix: support SOH, STX and DC1 control characters in mp_jsonout 2022-03-28 15:54:28 +01:00
Allan Bowe
7c86d6163a Merge pull request #201 from sasjs/mm_assignlib_fix
fix: enabling more descriptive mm_assignlib abort messages when library cannot be assigned
2022-03-21 18:10:56 +02:00
munja
d7233208f1 fix: enabling more descriptive mm_assignlib abort messages when library cannot be assigned 2022-03-21 16:07:27 +00:00
Allan Bowe
7f587ba720 Merge pull request #200 from sasjs/server
fix: headers in ms_createfile.sas
2022-03-19 02:23:57 +02:00
munja
21ecc1b675 fix: headers in ms_createfile.sas 2022-03-19 00:20:05 +00:00
Allan Bowe
6b13dc2b87 Merge pull request #199 from sasjs/server
fix: missing dependency in mv_deleteviyafolder
2022-03-16 18:07:40 +02:00
munja
bb89184212 fix: missing dependency in mv_deleteviyafolder 2022-03-16 16:01:22 +00:00
Allan Bowe
56338caaca Merge pull request #198 from sasjs/server
feat: enabling delete file for sasjs/server
2022-03-15 17:41:24 +02:00
munja
d7e2ff8ac9 fix: diffs for format loads not showing in audit table 2022-03-15 15:20:25 +00:00
munja
582ec0a1f9 feat: enabling delete file for sasjs/server 2022-03-15 13:46:31 +00:00
Allan Bowe
53785f5644 Update README.md 2022-03-14 13:44:13 +00:00
Allan Bowe
a8acadb8f1 Merge pull request #197 from sasjs/devops
gitpod & git hook updates
2022-03-11 23:25:20 +02:00
Allan Bowe
23dbda302e fix: ensuring pre-commit fails when sasjs lint fails 2022-03-11 21:24:34 +00:00
Allan Bowe
7e7ab4275d fix: gitpod launching sasjs 2022-03-11 21:23:52 +00:00
21 changed files with 501 additions and 171 deletions

View File

@@ -1,5 +1,12 @@
#!/bin/bash
sasjs lint
# Ensure lint is passing
LINT=`sasjs lint`
if [[ "$LINT" != "✔ All matched files use @sasjs/lint code style!" ]]; then
echo "$LINT"
echo "To commit in spite of these warnings, use the -n parameter."
exit 1
fi
# Avoid commits to the master branch
BRANCH=`git rev-parse --abbrev-ref HEAD`

View File

@@ -1,7 +1,7 @@
tasks:
- init: |
nvm install --lts
npm i -g @sasjs/cli
- init: npm install -g npm
- command: npm i
- command: npm i -g @sasjs/cli
image:
file: .gitpod.dockerfile
@@ -24,4 +24,4 @@ github:
# add a "Review in Gitpod" button to pull requests (defaults to false)
addBadge: false
# add a label once the prebuild is ready to pull requests (defaults to false)
addLabel: prebuilt-in-gitpod
addLabel: prebuilt-in-gitpod

View File

@@ -217,6 +217,15 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
![](https://starchart.cc/sasjs/core.svg)
## Other SAS Repositories
The following repositories are also worth checking out:
* [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros)
* [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs)
* [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros)
* [scottbass/sas](https://github.com/scottbass/SAS)
* [yabwon/sas_packages](https://github.com/yabwon/SAS_PACKAGES)
## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

251
all.sas
View File

@@ -167,19 +167,17 @@ options noquotelenmax;
%put %mf_existfeature(PROCLUA);
@param feature the feature to detect. Leave blank to list all in log.
@param [in] feature The feature to detect.
@return output returns 1 or 0 (or -1 if not found)
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@version 8
@author Allan Bowe
**/
/** @cond */
%macro mf_existfeature(feature
)/*/STORE SOURCE*/;
%let feature=%upcase(&feature);
@@ -187,7 +185,11 @@ options noquotelenmax;
%let platform=%mf_getplatform();
%if &feature= %then %do;
%put Supported features: PROCLUA;
%put No feature was requested for detection;
%end;
%else %if &feature=COLCONSTRAINTS %then %do;
%if %substr(&sysver,1,1)=4 %then 0;
%else 1;
%end;
%else %if &feature=PROCLUA %then %do;
/* https://blogs.sas.com/content/sasdummy/2015/08/03/using-lua-within-your-sas-programs */
@@ -201,8 +203,8 @@ options noquotelenmax;
%put &sysmacroname: &feature not found;
%end;
%mend mf_existfeature;
/** @endcond *//**
/** @endcond */
/**
@file
@brief Checks whether a fileref exists
@details You can probably do without this macro as it is just a one liner.
@@ -2192,7 +2194,7 @@ Usage:
%end;
/* Stored Process Server web app context */
%if %symexist(_metaperson)
%if %symexist(_METAFOLDER)
or "&SYSPROCESSNAME "="Compute Server "
or &mode=INCLUDE
%then %do;
@@ -2268,12 +2270,14 @@ Usage:
/* send response in SASjs JSON format */
data _null_;
file _webout mod lrecl=32000 encoding='utf-8';
length msg $32767 ;
length msg syswarningtext syserrortext $32767 ;
sasdatetime=datetime();
msg=symget('msg');
%if &logline>0 %then %do;
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
%end;
/* escape the escapes */
msg=tranwrd(msg,'\','\\');
/* escape the quotes */
msg=tranwrd(msg,'"','\"');
/* ditch the CRLFs as chrome complains */
@@ -2367,7 +2371,8 @@ Usage:
%end;
%mend mp_abort;
/** @endcond *//**
/** @endcond */
/**
@file
@brief Append (concatenate) two or more files.
@details Will append one more more `appendrefs` (filerefs) to a `baseref`.
@@ -7949,8 +7954,8 @@ run;
%mend mp_guesspk;/**
@file
@brief Returns a unique hash for a dataset
@details Ignores metadata attributes, used only to hash values. Compared
datasets must be in the same order.
@details Ignores metadata attributes, used only to hash values. If used to
compare datasets, they must have their columns and rows in the same order.
%mp_hashdataset(sashelp.class,outds=myhash)
@@ -7965,7 +7970,10 @@ run;
@li mf_getattrn.sas
@li mf_getuniquename.sas
@li mf_getvarlist.sas
@li mf_getvartype.sas
@li mp_md5.sas
<h4> Related Files </h4>
@li mp_hashdataset.test.sas
@param [in] libds dataset to hash
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
@@ -7983,51 +7991,52 @@ run;
%macro mp_hashdataset(
libds,
outds=,
outds=work._data_,
salt=,
iftrue=%str(1=1)
)/*/STORE SOURCE*/;
%if not(%eval(%unquote(&iftrue))) %then %return;
%local keyvar /* roll up the md5 */
prevkeyvar /* retain prev record md5 */
lastvar /* last var in input ds */
cvars nvars;
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
%put %str(WARN)ING: Dataset &libds is empty, or is not a dataset;
%end;
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
%put %str(ERR)OR: Dataset &libds is not a dataset;
%end;
%else %do;
%local keyvar /* roll up the md5 */
prevkeyvar /* retain prev record md5 */
lastvar /* last var in input ds */
varlist var i;
/* avoid naming conflict for hash key vars */
%let keyvar=%mf_getuniquename();
%let prevkeyvar=%mf_getuniquename();
%let lastvar=%mf_getuniquename();
%let varlist=%mf_getvarlist(&libds);
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
length &prevkeyvar &keyvar $32;
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
set &libds end=&lastvar;
/* hash should include previous row */
&keyvar=put(md5(&prevkeyvar
/* loop every column, hashing every individual value */
%do i=1 %to %sysfunc(countw(&varlist));
%let var=%scan(&varlist,&i,%str( ));
%if %mf_getvartype(&libds,&var)=C %then %do;
!!put(md5(trim(&var)),$hex32.)
%end;
%else %do;
!!put(md5(trim(put(&var*1,binary64.))),$hex32.)
%end;
%end;
),$hex32.);
&prevkeyvar=&keyvar;
if &lastvar then output;
run;
%end;
%mend mp_hashdataset;/**
%if not(%eval(%unquote(&iftrue))) %then %return;
/* avoid naming conflict for hash key vars */
%let keyvar=%mf_getuniquename();
%let prevkeyvar=%mf_getuniquename();
%let lastvar=%mf_getuniquename();
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
data &outds;
length hashkey $32;
retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)";
output;
stop;
run;
%put &sysmacroname: Dataset &libds is empty, or is not a dataset;
%put &sysmacroname: hashkey of &outds is based on salt (&salt) only;
%end;
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
%put %str(ERR)OR: Dataset &libds is not a dataset;
%end;
%else %do;
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
length &prevkeyvar &keyvar $32;
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
set &libds end=&lastvar;
/* hash should include previous row */
&keyvar=%mp_md5(
cvars=%mf_getvarlist(&libds,typefilter=C) &prevkeyvar,
nvars=%mf_getvarlist(&libds,typefilter=N)
);
&prevkeyvar=&keyvar;
if &lastvar then output;
run;
%end;
%mend mp_hashdataset;
/**
@file
@brief Performs a wrapped \%include
@details This macro wrapper is necessary if you need your included code to
@@ -8412,8 +8421,15 @@ options
prxchange('s/'!!'0A'x!!'/\n/',-1,
prxchange('s/'!!'0D'x!!'/\r/',-1,
prxchange('s/'!!'09'x!!'/\t/',-1,
prxchange('s/'!!'00'x!!'/\\u0000/',-1, /* NUL */
prxchange('s/'!!'0E'x!!'/\\u000E/',-1, /* SS */
prxchange('s/'!!'0F'x!!'/\\u000F/',-1, /* SF */
prxchange('s/'!!'01'x!!'/\\u0001/',-1, /* SOH */
prxchange('s/'!!'02'x!!'/\\u0002/',-1, /* STX */
prxchange('s/'!!'02'x!!'/\\u0010/',-1, /* DLE */
prxchange('s/'!!'11'x!!'/\\u0011/',-1, /* DC1 */
prxchange('s/\\/\\\\/',-1,&&name&i)
)))))!!'"';
))))))))))))!!'"';
%end;
%end;
run;
@@ -8768,7 +8784,7 @@ run;
* First, extract only relevant formats from the catalog
*/
proc sql noprint;
select distinct fmtname into: fmtlist separated by ' ' from &libds;
select distinct upcase(fmtname) into: fmtlist separated by ' ' from &libds;
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
@@ -8778,8 +8794,11 @@ select distinct fmtname into: fmtlist separated by ' ' from &libds;
*/
%mddl_sas_cntlout(libds=&template)
data &inlibds;
length &delete_col $3;
if 0 then set &template;
set &libds;
if &delete_col='' then &delete_col='No';
fmtname=upcase(fmtname);
if missing(type) then do;
if substr(fmtname,1,1)='$' then type='C';
else type='N';
@@ -8790,7 +8809,6 @@ data &inlibds;
end;
run;
/**
* Identify new records
*/
@@ -8897,7 +8915,7 @@ options ibufsize=&ibufsize;
%end;
%mp_storediffs(&libcat-FC
,&inlibds
,&base_fmts
,FMTNAME START
,delds=&outds_del
,modds=&outds_mod
@@ -13353,8 +13371,8 @@ run;
<h4> SAS Macros </h4>
@li mp_abort.sas
@param libref the libref (not name) of the metadata library
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
@param [in] libref The libref (not name) of the metadata library
@param [in] mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
silently return
@returns libname statement
@@ -13368,11 +13386,11 @@ run;
libref
,mAbort=HARD
)/*/STORE SOURCE*/;
%local mp_abort msg;
%let mp_abort=0;
%if %sysfunc(libref(&libref)) %then %do;
%local mp_abort msg; %let mp_abort=0;
data _null_;
length liburi LibName $200;
length liburi LibName msg $200;
call missing(of _all_);
nobj=metadata_getnobj("omsobj:SASLibrary?@Libref='&libref'",1,liburi);
if nobj=1 then do;
@@ -13386,7 +13404,24 @@ run;
* not always helpful though. One example, previously received:
* NOTE: Libref XX refers to the same library metadata as libref XX.
*/
call symputx('msg',sysmsg(),'l');
msg=sysmsg();
if msg=:'ERROR: Libref SAVE is not assigned.' then do;
msg=catx(" ",
"Could not assign %upcase(&libref).",
"Please check metadata permissions! Libname:",libname,
"Liburi:",liburi
);
end;
else if msg="ERROR: User does not have appropriate authorization "!!
"level for library SAVE."
then do;
msg=catx(" ",
"ERROR: User does not have appropriate authorization level",
"for library %upcase(&libref), libname:",libname,
"Liburi:",liburi
);
end;
call symputx('msg',msg,'l');
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
end;
else do;
@@ -13405,22 +13440,18 @@ run;
end;
run;
%if &mp_abort=1 %then %do;
%mp_abort(iftrue= (&mp_abort=1)
,mac=mm_assignlib.sas
,msg=&msg
)
%return;
%end;
%else %if %length(&msg)>2 %then %do;
%put NOTE: &msg;
%return;
%end;
%put NOTE: &msg;
%end;
%else %do;
%put NOTE: Library &libref is already assigned;
%end;
%mp_abort(iftrue= (&mp_abort=1)
,mac=mm_assignlib.sas
,msg=%superq(msg)
)
%mend mm_assignlib;
/**
@file
@@ -14907,8 +14938,15 @@ data _null_;
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
put ' prxchange(''s/''!!''00''x!!''/\\u0000/'',-1, /* NUL */ ';
put ' prxchange(''s/''!!''0E''x!!''/\\u000E/'',-1, /* SS */ ';
put ' prxchange(''s/''!!''0F''x!!''/\\u000F/'',-1, /* SF */ ';
put ' prxchange(''s/''!!''01''x!!''/\\u0001/'',-1, /* SOH */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/''!!''11''x!!''/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))!!''"''; ';
put ' ))))))))))))!!''"''; ';
put ' %end; ';
put ' %end; ';
put ' run; ';
@@ -18864,9 +18902,10 @@ run;
,mdebug=0
);
%local fname0 fname1 boundary fname statcd msg;
%local fname0 fname1 fname2 boundary fname statcd msg;
%let fname0=%mf_getuniquefileref();
%let fname1=%mf_getuniquefileref();
%let fname2=%mf_getuniquefileref();
%let boundary=%mf_getuniquename();
data _null_;
@@ -18890,17 +18929,25 @@ data _null_;
end;
run;
data _null_;
file &fname1;
put "Content-Type: multipart/form-data; boundary=&boundary";
run;
%if &mdebug=1 %then %do;
data _null_;
infile &fname0;
input;
put _infile_;
data _null_;
infile &fname1;
input;
put _infile_;
run;
%end;
proc http method='POST' in=&fname0 out=&fname1
proc http method='POST' in=&fname0 headerin=&fname1 out=&fname2
url="&_sasjs_apiserverurl/SASjsApi/drive/file";
headers "Content-Type"="multipart/form-data; boundary=&boundary";
%if &mdebug=1 %then %do;
debug level=1;
%end;
@@ -18908,7 +18955,7 @@ run;
%let statcd=0;
data _null_;
infile &fname1;
infile &fname2;
input;
putlog _infile_;
if _infile_='{"status":"success"}' then call symputx('statcd',1,'l');
@@ -18922,6 +18969,41 @@ run;
)
%mend ms_createfile;
/**
@file
@brief Deletes a file from SASjs Drive
@details Deletes a file from SASjs Drive, if it exists.
Example:
filename stpcode temp;
data _null_;
file stpcode;
put '%put hello world;';
run;
%ms_createfile(/some/stored/program.sas, inref=stpcode)
%ms_deletefile(/some/stored/program.sas)
@param [in] driveloc The full path to the file in SASjs Drive
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
**/
%macro ms_deletefile(driveloc
,mdebug=0
);
proc http method='DELETE'
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
%if &mdebug=1 %then %do;
debug level=2;
%end;
run;
%mend ms_deletefile;
/**
@file
@brief Gets a file from SASjs Drive
@@ -18946,7 +19028,7 @@ run;
filename &outref temp;
proc http method='GET' out=&outref
url="&_sasjs_apiserverurl/SASjsApi/drive/file?filePath=&driveloc";
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
%if &mdebug=1 %then %do;
debug level=2;
%end;
@@ -19146,6 +19228,7 @@ run;
data _null_;
set &tempds;
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
if not (upcase(name)=:"_DATA_");
i+1;
call symputx(cats('wt',i),name,'l');
call symputx('wtcnt',i,'l');
@@ -20324,8 +20407,15 @@ data _null_;
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
put ' prxchange(''s/''!!''00''x!!''/\\u0000/'',-1, /* NUL */ ';
put ' prxchange(''s/''!!''0E''x!!''/\\u000E/'',-1, /* SS */ ';
put ' prxchange(''s/''!!''0F''x!!''/\\u000F/'',-1, /* SF */ ';
put ' prxchange(''s/''!!''01''x!!''/\\u0001/'',-1, /* SOH */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/''!!''11''x!!''/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))!!''"''; ';
put ' ))))))))))))!!''"''; ';
put ' %end; ';
put ' %end; ';
put ' run; ';
@@ -21087,6 +21177,7 @@ libname &libref1a clear;
<h4> SAS Macros </h4>
@li mp_abort.sas
@li mf_existds.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas

View File

@@ -9,19 +9,17 @@
%put %mf_existfeature(PROCLUA);
@param feature the feature to detect. Leave blank to list all in log.
@param [in] feature The feature to detect.
@return output returns 1 or 0 (or -1 if not found)
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@version 8
@author Allan Bowe
**/
/** @cond */
%macro mf_existfeature(feature
)/*/STORE SOURCE*/;
%let feature=%upcase(&feature);
@@ -29,7 +27,11 @@
%let platform=%mf_getplatform();
%if &feature= %then %do;
%put Supported features: PROCLUA;
%put No feature was requested for detection;
%end;
%else %if &feature=COLCONSTRAINTS %then %do;
%if %substr(&sysver,1,1)=4 %then 0;
%else 1;
%end;
%else %if &feature=PROCLUA %then %do;
/* https://blogs.sas.com/content/sasdummy/2015/08/03/using-lua-within-your-sas-programs */
@@ -43,5 +45,4 @@
%put &sysmacroname: &feature not found;
%end;
%mend mf_existfeature;
/** @endcond */
/** @endcond */

View File

@@ -85,7 +85,7 @@
%end;
/* Stored Process Server web app context */
%if %symexist(_metaperson)
%if %symexist(_METAFOLDER)
or "&SYSPROCESSNAME "="Compute Server "
or &mode=INCLUDE
%then %do;
@@ -161,12 +161,14 @@
/* send response in SASjs JSON format */
data _null_;
file _webout mod lrecl=32000 encoding='utf-8';
length msg $32767 ;
length msg syswarningtext syserrortext $32767 ;
sasdatetime=datetime();
msg=symget('msg');
%if &logline>0 %then %do;
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
%end;
/* escape the escapes */
msg=tranwrd(msg,'\','\\');
/* escape the quotes */
msg=tranwrd(msg,'"','\"');
/* ditch the CRLFs as chrome complains */
@@ -260,4 +262,4 @@
%end;
%mend mp_abort;
/** @endcond */
/** @endcond */

View File

@@ -1,8 +1,8 @@
/**
@file
@brief Returns a unique hash for a dataset
@details Ignores metadata attributes, used only to hash values. Compared
datasets must be in the same order.
@details Ignores metadata attributes, used only to hash values. If used to
compare datasets, they must have their columns and rows in the same order.
%mp_hashdataset(sashelp.class,outds=myhash)
@@ -17,7 +17,10 @@
@li mf_getattrn.sas
@li mf_getuniquename.sas
@li mf_getvarlist.sas
@li mf_getvartype.sas
@li mp_md5.sas
<h4> Related Files </h4>
@li mp_hashdataset.test.sas
@param [in] libds dataset to hash
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
@@ -35,48 +38,48 @@
%macro mp_hashdataset(
libds,
outds=,
outds=work._data_,
salt=,
iftrue=%str(1=1)
)/*/STORE SOURCE*/;
%if not(%eval(%unquote(&iftrue))) %then %return;
%local keyvar /* roll up the md5 */
prevkeyvar /* retain prev record md5 */
lastvar /* last var in input ds */
cvars nvars;
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
%put %str(WARN)ING: Dataset &libds is empty, or is not a dataset;
%end;
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
%put %str(ERR)OR: Dataset &libds is not a dataset;
%end;
%else %do;
%local keyvar /* roll up the md5 */
prevkeyvar /* retain prev record md5 */
lastvar /* last var in input ds */
varlist var i;
/* avoid naming conflict for hash key vars */
%let keyvar=%mf_getuniquename();
%let prevkeyvar=%mf_getuniquename();
%let lastvar=%mf_getuniquename();
%let varlist=%mf_getvarlist(&libds);
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
length &prevkeyvar &keyvar $32;
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
set &libds end=&lastvar;
/* hash should include previous row */
&keyvar=put(md5(&prevkeyvar
/* loop every column, hashing every individual value */
%do i=1 %to %sysfunc(countw(&varlist));
%let var=%scan(&varlist,&i,%str( ));
%if %mf_getvartype(&libds,&var)=C %then %do;
!!put(md5(trim(&var)),$hex32.)
%end;
%else %do;
!!put(md5(trim(put(&var*1,binary64.))),$hex32.)
%end;
%end;
),$hex32.);
&prevkeyvar=&keyvar;
if &lastvar then output;
run;
%end;
%mend mp_hashdataset;
%if not(%eval(%unquote(&iftrue))) %then %return;
/* avoid naming conflict for hash key vars */
%let keyvar=%mf_getuniquename();
%let prevkeyvar=%mf_getuniquename();
%let lastvar=%mf_getuniquename();
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
data &outds;
length hashkey $32;
retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)";
output;
stop;
run;
%put &sysmacroname: Dataset &libds is empty, or is not a dataset;
%put &sysmacroname: hashkey of &outds is based on salt (&salt) only;
%end;
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
%put %str(ERR)OR: Dataset &libds is not a dataset;
%end;
%else %do;
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
length &prevkeyvar &keyvar $32;
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
set &libds end=&lastvar;
/* hash should include previous row */
&keyvar=%mp_md5(
cvars=%mf_getvarlist(&libds,typefilter=C) &prevkeyvar,
nvars=%mf_getvarlist(&libds,typefilter=N)
);
&prevkeyvar=&keyvar;
if &lastvar then output;
run;
%end;
%mend mp_hashdataset;

View File

@@ -203,8 +203,15 @@
prxchange('s/'!!'0A'x!!'/\n/',-1,
prxchange('s/'!!'0D'x!!'/\r/',-1,
prxchange('s/'!!'09'x!!'/\t/',-1,
prxchange('s/'!!'00'x!!'/\\u0000/',-1, /* NUL */
prxchange('s/'!!'0E'x!!'/\\u000E/',-1, /* SS */
prxchange('s/'!!'0F'x!!'/\\u000F/',-1, /* SF */
prxchange('s/'!!'01'x!!'/\\u0001/',-1, /* SOH */
prxchange('s/'!!'02'x!!'/\\u0002/',-1, /* STX */
prxchange('s/'!!'02'x!!'/\\u0010/',-1, /* DLE */
prxchange('s/'!!'11'x!!'/\\u0011/',-1, /* DC1 */
prxchange('s/\\/\\\\/',-1,&&name&i)
)))))!!'"';
))))))))))))!!'"';
%end;
%end;
run;

View File

@@ -134,7 +134,7 @@ run;
* First, extract only relevant formats from the catalog
*/
proc sql noprint;
select distinct fmtname into: fmtlist separated by ' ' from &libds;
select distinct upcase(fmtname) into: fmtlist separated by ' ' from &libds;
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
@@ -144,8 +144,11 @@ select distinct fmtname into: fmtlist separated by ' ' from &libds;
*/
%mddl_sas_cntlout(libds=&template)
data &inlibds;
length &delete_col $3;
if 0 then set &template;
set &libds;
if &delete_col='' then &delete_col='No';
fmtname=upcase(fmtname);
if missing(type) then do;
if substr(fmtname,1,1)='$' then type='C';
else type='N';
@@ -156,7 +159,6 @@ data &inlibds;
end;
run;
/**
* Identify new records
*/
@@ -263,7 +265,7 @@ options ibufsize=&ibufsize;
%end;
%mp_storediffs(&libcat-FC
,&inlibds
,&base_fmts
,FMTNAME START
,delds=&outds_del
,modds=&outds_mod

View File

@@ -13,8 +13,8 @@
<h4> SAS Macros </h4>
@li mp_abort.sas
@param libref the libref (not name) of the metadata library
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
@param [in] libref The libref (not name) of the metadata library
@param [in] mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
silently return
@returns libname statement
@@ -28,11 +28,11 @@
libref
,mAbort=HARD
)/*/STORE SOURCE*/;
%local mp_abort msg;
%let mp_abort=0;
%if %sysfunc(libref(&libref)) %then %do;
%local mp_abort msg; %let mp_abort=0;
data _null_;
length liburi LibName $200;
length liburi LibName msg $200;
call missing(of _all_);
nobj=metadata_getnobj("omsobj:SASLibrary?@Libref='&libref'",1,liburi);
if nobj=1 then do;
@@ -46,7 +46,24 @@
* not always helpful though. One example, previously received:
* NOTE: Libref XX refers to the same library metadata as libref XX.
*/
call symputx('msg',sysmsg(),'l');
msg=sysmsg();
if msg=:'ERROR: Libref SAVE is not assigned.' then do;
msg=catx(" ",
"Could not assign %upcase(&libref).",
"Please check metadata permissions! Libname:",libname,
"Liburi:",liburi
);
end;
else if msg="ERROR: User does not have appropriate authorization "!!
"level for library SAVE."
then do;
msg=catx(" ",
"ERROR: User does not have appropriate authorization level",
"for library %upcase(&libref), libname:",libname,
"Liburi:",liburi
);
end;
call symputx('msg',msg,'l');
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
end;
else do;
@@ -65,20 +82,16 @@
end;
run;
%if &mp_abort=1 %then %do;
%mp_abort(iftrue= (&mp_abort=1)
,mac=mm_assignlib.sas
,msg=&msg
)
%return;
%end;
%else %if %length(&msg)>2 %then %do;
%put NOTE: &msg;
%return;
%end;
%put NOTE: &msg;
%end;
%else %do;
%put NOTE: Library &libref is already assigned;
%end;
%mp_abort(iftrue= (&mp_abort=1)
,mac=mm_assignlib.sas
,msg=%superq(msg)
)
%mend mm_assignlib;

View File

@@ -236,8 +236,15 @@ data _null_;
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
put ' prxchange(''s/''!!''00''x!!''/\\u0000/'',-1, /* NUL */ ';
put ' prxchange(''s/''!!''0E''x!!''/\\u000E/'',-1, /* SS */ ';
put ' prxchange(''s/''!!''0F''x!!''/\\u000F/'',-1, /* SF */ ';
put ' prxchange(''s/''!!''01''x!!''/\\u0001/'',-1, /* SOH */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/''!!''11''x!!''/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))!!''"''; ';
put ' ))))))))))))!!''"''; ';
put ' %end; ';
put ' %end; ';
put ' run; ';

View File

@@ -4,11 +4,8 @@
"base",
"ddl",
"fcmp",
"meta",
"metax",
"server",
"viya",
"lua",
"server",
"tests/crossplatform",
"tests/ddl"
],
@@ -39,6 +36,7 @@
},
"appLoc": "/Public/temp/macrocore",
"macroFolders": [
"viya",
"tests/viyaonly"
],
"programFolders": [],
@@ -58,6 +56,8 @@
},
"appLoc": "/Shared Data/temp/macrocore",
"macroFolders": [
"meta",
"metax",
"tests/sas9only"
],
"programFolders": [],
@@ -71,13 +71,14 @@
},
{
"name": "server",
"serverUrl": "https://sas.analytium.co.uk:5000",
"serverUrl": " ",
"serverType": "SASJS",
"httpsAgentOptions": {
"allowInsecureRequests": false
},
"appLoc": "/sasjs/core",
"macroFolders": [
"server",
"tests/serveronly"
],
"programFolders": [],
@@ -92,6 +93,10 @@
"serverType": "SAS9",
"appLoc": "dummy",
"macroFolders": [
"meta",
"metax",
"server",
"viya",
"tests/sas9only",
"tests/viyaonly"
]
@@ -102,6 +107,7 @@
"serverType": "SASVIYA",
"appLoc": "/Public/temp/macrocore",
"macroFolders": [
"viya",
"tests/viyaonly"
],
"deployConfig": {
@@ -110,4 +116,4 @@
"contextName": "SAS Job Execution compute context"
}
]
}
}

View File

@@ -29,9 +29,10 @@
,mdebug=0
);
%local fname0 fname1 boundary fname statcd msg;
%local fname0 fname1 fname2 boundary fname statcd msg;
%let fname0=%mf_getuniquefileref();
%let fname1=%mf_getuniquefileref();
%let fname2=%mf_getuniquefileref();
%let boundary=%mf_getuniquename();
data _null_;
@@ -55,17 +56,25 @@ data _null_;
end;
run;
data _null_;
file &fname1;
put "Content-Type: multipart/form-data; boundary=&boundary";
run;
%if &mdebug=1 %then %do;
data _null_;
infile &fname0;
input;
put _infile_;
data _null_;
infile &fname1;
input;
put _infile_;
run;
%end;
proc http method='POST' in=&fname0 out=&fname1
proc http method='POST' in=&fname0 headerin=&fname1 out=&fname2
url="&_sasjs_apiserverurl/SASjsApi/drive/file";
headers "Content-Type"="multipart/form-data; boundary=&boundary";
%if &mdebug=1 %then %do;
debug level=1;
%end;
@@ -73,7 +82,7 @@ run;
%let statcd=0;
data _null_;
infile &fname1;
infile &fname2;
input;
putlog _infile_;
if _infile_='{"status":"success"}' then call symputx('statcd',1,'l');

35
server/ms_deletefile.sas Normal file
View File

@@ -0,0 +1,35 @@
/**
@file
@brief Deletes a file from SASjs Drive
@details Deletes a file from SASjs Drive, if it exists.
Example:
filename stpcode temp;
data _null_;
file stpcode;
put '%put hello world;';
run;
%ms_createfile(/some/stored/program.sas, inref=stpcode)
%ms_deletefile(/some/stored/program.sas)
@param [in] driveloc The full path to the file in SASjs Drive
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
**/
%macro ms_deletefile(driveloc
,mdebug=0
);
proc http method='DELETE'
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
%if &mdebug=1 %then %do;
debug level=2;
%end;
run;
%mend ms_deletefile;

View File

@@ -22,7 +22,7 @@
filename &outref temp;
proc http method='GET' out=&outref
url="&_sasjs_apiserverurl/SASjsApi/drive/file?filePath=&driveloc";
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
%if &mdebug=1 %then %do;
debug level=2;
%end;

View File

@@ -114,6 +114,7 @@
data _null_;
set &tempds;
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
if not (upcase(name)=:"_DATA_");
i+1;
call symputx(cats('wt',i),name,'l');
call symputx('wtcnt',i,'l');

View File

@@ -0,0 +1,59 @@
/**
@file
@brief Testing mp_hashdataset.sas macro
<h4> SAS Macros </h4>
@li mp_hashdataset.sas
@li mp_assert.sas
**/
/* test 1 - regular DS */
data work.test;
set sashelp.vextfl;
missval=.;
misscval='';
run;
%mp_assertscope(SNAPSHOT)
%mp_hashdataset(test)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(&syscc=0),
desc=Regular test works,
outds=work.test_results
)
%mp_hashdataset(test,outds=work.test2)
%mp_assert(
iftrue=(&syscc=0),
desc=hash with output runs without errors,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(work.test2)=1),
desc=output has 1 row,
outds=work.test_results
)
data work.test3a;
set work.test;
stop;
run;
%mp_hashdataset(test3a,outds=work.test3b)
%mp_assert(
iftrue=(&syscc=0),
desc=hash with zero-row input runs without errors,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(work.test3b)=1),
desc=test 3 output has 1 row,
outds=work.test_results
)

View File

@@ -82,3 +82,12 @@ run;
desc=Test 1 - audit table updated,
outds=work.test_results
)
data work.difftest;
set perm.audit;
where is_diff=1;
run;
%mp_assert(
iftrue=(%mf_nobs(work.difftest)>0),
desc=Test 1 - diffs were found,
outds=work.test_results
)

View File

@@ -0,0 +1,61 @@
/**
@file
@brief Testing ms_deletefile.sas macro
<h4> SAS Macros </h4>
@li ms_createfile.sas
@li ms_deletefile.sas
@li ms_getfile.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
/* first make a remote file */
filename stpcode temp;
%let fname=%mf_getuniquename();
data _null_;
file stpcode;
put "data &fname;run;";
run;
%ms_createfile(/sasjs/tests/&fname..sas
,inref=stpcode
,mdebug=1
)
%ms_getfile(/sasjs/tests/&fname..sas,outref=testref)
%let test1=0;
data _null_;
infile testref;
input;
call symputx('test1',_infile_);
run;
%mp_assert(
iftrue=("&test1"="data &fname;run;"),
desc=Make sure the file was created,
outds=work.test_results
)
%mp_assertscope(SNAPSHOT)
%ms_deletefile(/sasjs/tests/&fname..sas,mdebug=1)
%mp_assertscope(COMPARE)
%ms_getfile(/sasjs/tests/&fname..sas,outref=testref2)
%let test2=0;
data _null_;
infile testref2;
input;
call symputx('test2',_infile_);
run;
%mp_assert(
iftrue=("&test2"="%str(Err)or: File does not exist."),
desc=Make sure the file was deleted,
outds=work.test_results
)

View File

@@ -380,8 +380,15 @@ data _null_;
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
put ' prxchange(''s/''!!''00''x!!''/\\u0000/'',-1, /* NUL */ ';
put ' prxchange(''s/''!!''0E''x!!''/\\u000E/'',-1, /* SS */ ';
put ' prxchange(''s/''!!''0F''x!!''/\\u000F/'',-1, /* SF */ ';
put ' prxchange(''s/''!!''01''x!!''/\\u0001/'',-1, /* SOH */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/''!!''02''x!!''/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/''!!''11''x!!''/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))!!''"''; ';
put ' ))))))))))))!!''"''; ';
put ' %end; ';
put ' %end; ';
put ' run; ';

View File

@@ -25,6 +25,7 @@
<h4> SAS Macros </h4>
@li mp_abort.sas
@li mf_existds.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas