1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-05 00:20:05 +00:00

Compare commits

..

12 Commits

Author SHA1 Message Date
munja
d49b21f3f1 fix: initialising name/uri in mv_jobexecute 2021-12-14 08:42:39 +00:00
munja
a45d280a51 fix: avoiding uninitialised variables in mv_getjobcode and mv_getfoldermembers 2021-12-14 08:20:32 +00:00
Allan Bowe
2536e299ad Merge pull request #112 from sasjs/mf_isint
feat: new macro to determine if a macro variable value is an integer …
2021-12-13 18:55:41 +00:00
munja
8b5238230b feat: new macro to determine if a macro variable value is an integer - mf_isint (and associated test) 2021-12-13 18:36:22 +00:00
munja
0ce7efee3e fix: declaring msg variable prior to set statement in mp_copyfolder.sas 2021-12-13 11:26:17 +00:00
munja
357677e45c chore: switching pre-commit hook to bash from shell 2021-12-13 09:14:29 +00:00
Allan Bowe
a4a332926e Merge pull request #111 from sasjs/issue110
feat: adding varinitchk=ERROR to mp_init.  Closes #110.
2021-12-13 08:44:12 +00:00
munja
0a29006914 chore: running all.sas 2021-12-13 01:08:37 +00:00
munja
0885bad859 fix: updating the tests following varinitchk=error enablement. Removing the word 'error' from documenttion. 2021-12-13 01:07:54 +00:00
munja
42bd1750bd feat: adding varinitchk=ERROR to mp_init. Closes #110. Also updated the comments / documentation 2021-12-12 22:57:25 +00:00
Allan Bowe
58784b2f28 Merge pull request #109 from sasjs/issue108
fix: rebuilding mp_searchdata into data step to avoid SQL warning.
2021-12-11 14:34:08 +00:00
munja
e125aace9b fix: rebuilding mp_searchdata into data step to avoid SQL warning. Added test and refreshed docs. Closes #108 2021-12-10 15:56:39 +00:00
28 changed files with 328 additions and 118 deletions

View File

@@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
sasjs lint sasjs lint
# Avoid commits to the master branch # Avoid commits to the master branch

188
all.sas
View File

@@ -79,7 +79,7 @@ options noquotelenmax;
Run without arguments to see a list of detectable features. Run without arguments to see a list of detectable features.
Note - this list is based on known versions of SAS rather than Note - this list is based on known versions of SAS rather than
actual feature detection, as that is tricky / impossible to do actual feature detection, as that is tricky / impossible to do
without generating errors in most cases. without generating errs in most cases.
%put %mf_existfeature(PROCLUA); %put %mf_existfeature(PROCLUA);
@@ -362,7 +362,7 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
@param attr full list in [documentation]( @param attr full list in [documentation](
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000147794.htm) https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000147794.htm)
@return output returns result of the attrc value supplied, or -1 and log @return output returns result of the attrc value supplied, or -1 and log
message if error. message if err.
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@@ -395,7 +395,7 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
@param attr Common values are NLOBS and NVARS, full list in [documentation]( @param attr Common values are NLOBS and NVARS, full list in [documentation](
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm) http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm)
@return output returns result of the attrn value supplied, or -1 and log @return output returns result of the attrn value supplied, or -1 and log
message if error. message if err.
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@@ -1340,6 +1340,38 @@ Usage:
&is_directory &is_directory
%mend mf_isdir;/** %mend mf_isdir;/**
@file
@brief Returns 1 if the variable contains only digits 0-9, else 0
@details Note that numerics containing any punctuation (including decimals
or exponents) will be flagged zero.
If you'd like support for this, then do raise an issue (or even better, a
pull request!)
Usage:
%put %mf_isint(1) returns 1;
%put %mf_isint(1.1) returns 0;
%put %mf_isint(%str(1,1)) returns 0;
@param [in] arg input value to check
@version 9.2
**/
%macro mf_isint(arg
)/*/STORE SOURCE*/;
/* remove minus sign if exists */
%local val;
%if "%substr(%str(&arg),1,1)"="-" %then %let val=%substr(%str(&arg),2);
%else %let val=&arg;
/* check remaining chars */
%if %sysfunc(findc(%str(&val),,kd)) %then %do;0%end;
%else %do;1%end;
%mend mf_isint;/**
@file @file
@brief Returns physical location of various SAS items @brief Returns physical location of various SAS items
@details Returns location of the PlatformObjectFramework tools @details Returns location of the PlatformObjectFramework tools
@@ -1421,7 +1453,7 @@ Usage:
%end; %end;
/* /*
Now create the directory. Complain loudly of any errors. Now create the directory. Complain loudly of any errs.
*/ */
%let dname = %sysfunc(dcreate(&child, &parent)); %let dname = %sysfunc(dcreate(&child, &parent));
@@ -1468,7 +1500,7 @@ Usage:
@param libds library.dataset @param libds library.dataset
@return output returns result of the attrn value supplied, or log message @return output returns result of the attrn value supplied, or log message
if error. if err.
@version 9.2 @version 9.2
@@ -2790,6 +2822,8 @@ run;
/* create folders and copy content */ /* create folders and copy content */
data _null_; data _null_;
length msg $200;
call missing(msg);
set work.&tempds; set work.&tempds;
if _n_ = 1 then dpos+sum(length(directory),2); if _n_ = 1 then dpos+sum(length(directory),2);
filepath2="&target/"!!substr(filepath,dpos); filepath2="&target/"!!substr(filepath,dpos);
@@ -2799,9 +2833,9 @@ run;
rc1=filename(fref1,filepath,'disk','recfm=n'); rc1=filename(fref1,filepath,'disk','recfm=n');
rc2=filename(fref2,filepath2,'disk','recfm=n'); rc2=filename(fref2,filepath2,'disk','recfm=n');
if fcopy(fref1,fref2) ne 0 then do; if fcopy(fref1,fref2) ne 0 then do;
sysmsg=sysmsg(); msg=sysmsg();
putlog "%str(ERR)OR: Unable to copy " filepath " to " filepath2; putlog "%str(ERR)OR: Unable to copy " filepath " to " filepath2;
putlog sysmg=; putlog msg=;
end; end;
end; end;
rc=filename(fref1); rc=filename(fref1);
@@ -3876,17 +3910,23 @@ run;
%mend mp_ds2csv;/** %mend mp_ds2csv;/**
@file @file
@brief Converts every value in a dataset to it's formatted value @brief Converts every value in a dataset to formatted value
@details Converts every value to it's formatted value. All variables will @details Converts every value to it's formatted value. All variables will
become character, and will be in the same order. become character, and will be in the same order as the original dataset.
Lengths will be adjusted according to the format lengths, where applicable.
Usage: Usage:
%mp_ds2fmtds(sashelp.cars,work.cars) %mp_ds2fmtds(sashelp.cars,work.cars)
%mp_ds2fmtds(sashelp.vallopt,vw_vallopt)
@param [in] libds The library.dataset to be converted @param [in] libds The library.dataset to be converted
@param [out] outds The dataset to create. @param [out] outds The dataset to create.
<h4> SAS Macros </h4>
@li mf_existds.sas
<h4> Related Macros <h4> <h4> Related Macros <h4>
@li mp_jsonout.sas @li mp_jsonout.sas
@@ -3898,8 +3938,9 @@ run;
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* validations */ /* validations */
%if not %sysfunc(exist(&libds)) %then %do;
%put %str(WARN)ING: &libds does not exist; %if not %mf_existds(libds=&libds) %then %do;
%put %str(WARN)ING: &libds does not exist as either a VIEW or DATASET;
%return; %return;
%end; %end;
%if %index(&libds,.)=0 %then %let libds=WORK.&libds; %if %index(&libds,.)=0 %then %let libds=WORK.&libds;
@@ -4089,6 +4130,7 @@ data _null_;
if _n_>&maxobs then stop; if _n_>&maxobs then stop;
%end; %end;
length _____str $32767; length _____str $32767;
call missing(_____str);
format _numeric_ best.; format _numeric_ best.;
format _character_ ; format _character_ ;
%local i comma var vtype vfmt; %local i comma var vtype vfmt;
@@ -4489,8 +4531,8 @@ filename &outref temp;
@param [in] targetds The target dataset against which to verify the query @param [in] targetds The target dataset against which to verify the query
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions @param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
@param [out] outds= (work.mp_filtervalidate) Output dataset containing the @param [out] outds= (work.mp_filtervalidate) Output dataset containing the
error / warning message, if one exists. If this table contains any rows, err / warning message, if one exists. If this table contains any rows,
there are problems! there are problems!
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquefileref.sas @li mf_getuniquefileref.sas
@@ -4674,17 +4716,17 @@ run;
%let vw=%mf_getuniquename(prefix=mp_getconstraints_vw_); %let vw=%mf_getuniquename(prefix=mp_getconstraints_vw_);
data &vw /view=&vw; data &vw /view=&vw;
set sashelp.vcncolu; set sashelp.vcncolu;
where TABLE_CATALOG="&lib"; where table_catalog="&lib";
/* use retain approach to reset the constraint order with each constraint */ /* use retain approach to reset the constraint order with each constraint */
length tmp $1000; length tmp $1000;
retain tmp; retain tmp;
drop tmp; drop tmp;
if tmp ne catx('|',libref,table_name,constraint_type,constraint_name) then do; if tmp ne catx('|',table_catalog,table_name,constraint_name) then do;
constraint_order=1; constraint_order=1;
end; end;
else constraint_order+1; else constraint_order+1;
tmp=catx('|',libref, table_name, constraint_type,constraint_name); tmp=catx('|',table_catalog, table_name,constraint_name);
run; run;
/* must use SQL as proc datasets does not support length changes */ /* must use SQL as proc datasets does not support length changes */
@@ -5194,7 +5236,7 @@ run;
%let curds=%scan(&dsnlist,&x); %let curds=%scan(&dsnlist,&x);
data _null_; data _null_;
file &fref mod; file &fref mod;
length nm lab $1024 typ $20; length lab $1024 typ $20;
set &colinfo (where=(upcase(memname)="&curds")) end=last; set &colinfo (where=(upcase(memname)="&curds")) end=last;
if _n_=1 then do; if _n_=1 then do;
@@ -6333,15 +6375,25 @@ filename &tempref clear;
%mend mp_include;/** %mend mp_include;/**
@file @file
@brief Initialise session with useful settings and variables @brief Initialise session with useful settings and variables
@details Implements a set of recommended options for general SAS use. This @details Implements a "strict" set of SAS options for use in defensive
macro is NOT used elsewhere within the core library (other than in tests), programming. Highly recommended, if you want your code to run on some
but it is used by the SASjs team when building web services for other machine.
SAS-Powered applications elsewhere.
If you have a good idea for an option, setting, or useful global variable - This macro is recommended to be compiled and invoked in the `initProgram`
feel free to [raise an issue](https://github.com/sasjs/core/issues/new)! for SASjs [Jobs](https://cli.sasjs.io/sasjsconfig.html#jobConfig_initProgram
), [Services](
https://cli.sasjs.io/sasjsconfig.html#serviceConfig_initProgram) and [Tests]
(https://cli.sasjs.io/sasjsconfig.html#testConfig_initProgram).
All global variables are prefixed with "SASJS_" (unless modfied with the For non SASjs projects, you could invoke in the autoexec, or in your own
solution initialisation macro.
If you have a good idea for another useful option, setting, or global
variable - feel free to [raise an issue](
https://github.com/sasjs/core/issues/new)!
All global variables are prefixed with "SASJS" (unless modified with the
prefix parameter). prefix parameter).
@param [in] prefix= (SASJS) The prefix to apply to the global macro variables @param [in] prefix= (SASJS) The prefix to apply to the global macro variables
@@ -6371,15 +6423,16 @@ filename &tempref clear;
autocorrect /* disallow mis-spelled procedure names */ autocorrect /* disallow mis-spelled procedure names */
compress=CHAR /* default is none so ensure we have something! */ compress=CHAR /* default is none so ensure we have something! */
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */ datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
errorcheck=STRICT /* catch errors in libname/filename statements */ %str(err)orcheck=STRICT /* catch errs in libname/filename statements */
fmterr /* ensure err when a format cannot be found */ fmterr /* ensure err when a format cannot be found */
mergenoby=%str(ERR)OR /* Throw err when a merge has no BY variables */ mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
missing=. /* some sites change this which causes hard to detect errors */ missing=. /* changing this can cause hard to detect errs */
noquotelenmax /* avoid warnings for long strings */ noquotelenmax /* avoid warnings for long strings */
noreplace /* avoid overwriting permanent datasets */ noreplace /* avoid overwriting permanent datasets */
ps=max /* reduce log size slightly */ ps=max /* reduce log size slightly */
validmemname=COMPATIBLE /* avoid special characters etc in table names */ validmemname=COMPATIBLE /* avoid special characters etc in table names */
validvarname=V7 /* avoid special characters etc in variable names */ validvarname=V7 /* avoid special characters etc in variable names */
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */ varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
; ;
@@ -6448,7 +6501,7 @@ filename &tempref clear;
%if &action=OPEN %then %do; %if &action=OPEN %then %do;
options nobomfile; options nobomfile;
data _null_;file &jref encoding='utf-8'; data _null_;file &jref encoding='utf-8';
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"'; put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
run; run;
%end; %end;
%else %if (&action=ARR or &action=OBJ) %then %do; %else %if (&action=ARR or &action=OBJ) %then %do;
@@ -7010,7 +7063,7 @@ run;
%let abortme=1; %let abortme=1;
%end; %end;
/* catch errors - mp_abort must be called outside of a logic block */ /* catch errs - mp_abort must be called outside of a logic block */
%mp_abort(iftrue=(&abortme=1), %mp_abort(iftrue=(&abortme=1),
msg=%superq(msg), msg=%superq(msg),
mac=&sysmacroname mac=&sysmacroname
@@ -7742,22 +7795,28 @@ drop table &tempds;
Usage: Usage:
%mp_searchdata(lib=sashelp, string=Jan) %mp_searchdata(lib=sashelp, string=Jan)
%mp_searchdata(lib=sashelp, numval=1) %mp_searchdata(lib=sashelp, ds=bird, numval=1)
%mp_searchdata(lib=sashelp, ds=class, string=l,outobs=5)
Outputs zero or more tables to an MPSEARCH library with specific records. Outputs zero or more tables to an MPSEARCH library with specific records.
@param lib= the libref to search (should be already assigned) @param [in] lib= The libref to search (should be already assigned)
@param ds= the dataset to search (leave blank to search entire library) @param [in] ds= The dataset to search (leave blank to search entire library)
@param string= the string value to search @param [in] string= String value to search (case sensitive, can be partial)
@param numval= the numeric value to search (must be exact) @param [in] numval= Numeric value to search (must be exact)
@param outloc= the directory in which to create the output datasets with @param [out] outloc= (0) Optionally specify the directory in which to
matching rows. Will default to a subfolder in the WORK library. create the the output datasets with matching rows. By default it will
@param outobs= set to a positive integer to restrict the number of write them to a temporary subdirectory within the WORK folder.
@param [out] outlib= (MPSEARCH) Assign a different libref to the output
library containing the matching datasets / records
@param [in] outobs= set to a positive integer to restrict the number of
observations observations
@param filter_text= add a (valid) filter clause to further filter the results @param [in] filter_text= (1=1) Add a (valid) filter clause to further filter
the results.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquename.sas
@li mf_getvarlist.sas @li mf_getvarlist.sas
@li mf_getvartype.sas @li mf_getvartype.sas
@li mf_mkdir.sas @li mf_mkdir.sas
@@ -7767,11 +7826,12 @@ drop table &tempds;
@author Allan Bowe @author Allan Bowe
**/ **/
%macro mp_searchdata(lib=sashelp %macro mp_searchdata(lib=
,ds= ,ds=
,string= /* the query will use a contains (?) operator */ ,string= /* the query will use a contains (?) operator */
,numval= /* numeric must match exactly */ ,numval= /* numeric must match exactly */
,outloc=%sysfunc(pathname(work))/mpsearch ,outloc=0
,outlib=MPSEARCH
,outobs=-1 ,outobs=-1
,filter_text=%str(1=1) ,filter_text=%str(1=1)
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
@@ -7788,8 +7848,12 @@ drop table &tempds;
%if &string = %then %let type=N; %if &string = %then %let type=N;
%else %let type=C; %else %let type=C;
%if "&outloc"="0" %then %do;
%let outloc=%sysfunc(pathname(work))/%mf_getuniquename();
%end;
%mf_mkdir(&outloc) %mf_mkdir(&outloc)
libname mpsearch "&outloc"; libname &outlib "&outloc";
/* get the list of tables in the library */ /* get the list of tables in the library */
proc sql noprint; proc sql noprint;
@@ -7801,11 +7865,6 @@ select distinct memname into: table_list separated by ' '
%end; %end;
; ;
/* check that we have something to check */ /* check that we have something to check */
proc sql
%if &outobs>-1 %then %do;
outobs=&outobs
%end;
;
%if %length(&table_list)=0 %then %put library &lib contains no tables!; %if %length(&table_list)=0 %then %put library &lib contains no tables!;
/* loop through each table */ /* loop through each table */
%else %do table_num=1 %to %sysfunc(countw(&table_list,%str( ))); %else %do table_num=1 %to %sysfunc(countw(&table_list,%str( )));
@@ -7816,10 +7875,10 @@ proc sql
%end; %end;
%else %do; %else %do;
%let check_tm=%sysfunc(datetime()); %let check_tm=%sysfunc(datetime());
/* build sql statement */ /* prep input */
create table mpsearch.&table as select * from &lib..&table data &outlib..&table;
where %unquote(&filter_text) and set &lib..&table;
(0 where %unquote(&filter_text) and ( 0
/* loop through columns */ /* loop through columns */
%do colnum=1 %to %sysfunc(countw(&vars,%str( ))); %do colnum=1 %to %sysfunc(countw(&vars,%str( )));
%let col=%scan(&vars,&colnum,%str( )); %let col=%scan(&vars,&colnum,%str( ));
@@ -7833,15 +7892,20 @@ proc sql
or ("&col"n = &numval) or ("&col"n = &numval)
%end; %end;
%end; %end;
); );
%if &outobs>-1 %then %do;
if _n_ > &outobs then stop;
%end;
run;
%put Search query for &table took %put Search query for &table took
%sysevalf(%sysfunc(datetime())-&check_tm) seconds; %sysevalf(%sysfunc(datetime())-&check_tm) seconds;
%if &sqlrc ne 0 %then %do; %if &syscc ne 0 %then %do;
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table; %put %str(ERR)ROR: SYSCC=&syscc when processing &lib..&table;
%return; %return;
%end; %end;
%if %mf_nobs(mpsearch.&table)=0 %then %do; %if %mf_nobs(&outlib..&table)=0 %then %do;
drop table mpsearch.&table; proc sql;
drop table &outlib..&table;
%end; %end;
%end; %end;
%end; %end;
@@ -11101,7 +11165,7 @@ data _null_;
put '%if &action=OPEN %then %do; '; put '%if &action=OPEN %then %do; ';
put ' options nobomfile; '; put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8''; '; put ' data _null_;file &jref encoding=''utf-8''; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; '; put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
@@ -16183,7 +16247,7 @@ data _null_;
put '%if &action=OPEN %then %do; '; put '%if &action=OPEN %then %do; ';
put ' options nobomfile; '; put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8''; '; put ' data _null_;file &jref encoding=''utf-8''; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; '; put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
@@ -17407,6 +17471,8 @@ options noquotelenmax;
%local href cnt; %local href cnt;
%let cnt=0; %let cnt=0;
data _null_; data _null_;
length rel href $512;
call missing(rel,href);
set &libref1..links; set &libref1..links;
if rel='members' then do; if rel='members' then do;
url=cats("'","&base_uri",href,"?limit=10000'"); url=cats("'","&base_uri",href,"?limit=10000'");
@@ -17430,6 +17496,7 @@ options noquotelenmax;
libname &libref2 JSON fileref=&fname2; libname &libref2 JSON fileref=&fname2;
data &outds; data &outds;
length id $36 name $128 uri $64 type $32 description $256; length id $36 name $128 uri $64 type $32 description $256;
if _n_=1 then call missing (of _all_);
set &libref2..items; set &libref2..items;
run; run;
filename &fname2 clear; filename &fname2 clear;
@@ -17725,6 +17792,8 @@ data;run;
%local joburi; %local joburi;
%let joburi=0; %let joburi=0;
data _null_; data _null_;
length name uri $512;
call missing(name,uri);
set &foldermembers; set &foldermembers;
if name="&name" and uri=:'/jobDefinitions/definitions' if name="&name" and uri=:'/jobDefinitions/definitions'
then call symputx('joburi',uri); then call symputx('joburi',uri);
@@ -18881,6 +18950,8 @@ data;run;
%local joburi; %local joburi;
%let joburi=0; %let joburi=0;
data _null_; data _null_;
length name uri $512;
call missing(name,uri);
set &foldermembers; set &foldermembers;
if name="&name" and uri=:'/jobDefinitions/definitions' if name="&name" and uri=:'/jobDefinitions/definitions'
then call symputx('joburi',uri); then call symputx('joburi',uri);
@@ -19514,6 +19585,7 @@ run;
data &outds; data &outds;
format _program uri $128. state $32. stateDetails $32. timestamp datetime19. format _program uri $128. state $32. stateDetails $32. timestamp datetime19.
jobparams $32767.; jobparams $32767.;
call missing (of _all_);
stop; stop;
run; run;

View File

@@ -5,7 +5,7 @@
Run without arguments to see a list of detectable features. Run without arguments to see a list of detectable features.
Note - this list is based on known versions of SAS rather than Note - this list is based on known versions of SAS rather than
actual feature detection, as that is tricky / impossible to do actual feature detection, as that is tricky / impossible to do
without generating errors in most cases. without generating errs in most cases.
%put %mf_existfeature(PROCLUA); %put %mf_existfeature(PROCLUA);

View File

@@ -10,7 +10,7 @@
@param attr full list in [documentation]( @param attr full list in [documentation](
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000147794.htm) https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000147794.htm)
@return output returns result of the attrc value supplied, or -1 and log @return output returns result of the attrc value supplied, or -1 and log
message if error. message if err.
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe

View File

@@ -10,7 +10,7 @@
@param attr Common values are NLOBS and NVARS, full list in [documentation]( @param attr Common values are NLOBS and NVARS, full list in [documentation](
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm) http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm)
@return output returns result of the attrn value supplied, or -1 and log @return output returns result of the attrn value supplied, or -1 and log
message if error. message if err.
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe

33
base/mf_isint.sas Normal file
View File

@@ -0,0 +1,33 @@
/**
@file
@brief Returns 1 if the variable contains only digits 0-9, else 0
@details Note that numerics containing any punctuation (including decimals
or exponents) will be flagged zero.
If you'd like support for this, then do raise an issue (or even better, a
pull request!)
Usage:
%put %mf_isint(1) returns 1;
%put %mf_isint(1.1) returns 0;
%put %mf_isint(%str(1,1)) returns 0;
@param [in] arg input value to check
@version 9.2
**/
%macro mf_isint(arg
)/*/STORE SOURCE*/;
/* remove minus sign if exists */
%local val;
%if "%substr(%str(&arg),1,1)"="-" %then %let val=%substr(%str(&arg),2);
%else %let val=&arg;
/* check remaining chars */
%if %sysfunc(findc(%str(&val),,kd)) %then %do;0%end;
%else %do;1%end;
%mend mf_isint;

View File

@@ -51,7 +51,7 @@ Usage:
%end; %end;
/* /*
Now create the directory. Complain loudly of any errors. Now create the directory. Complain loudly of any errs.
*/ */
%let dname = %sysfunc(dcreate(&child, &parent)); %let dname = %sysfunc(dcreate(&child, &parent));

View File

@@ -12,7 +12,7 @@
@param libds library.dataset @param libds library.dataset
@return output returns result of the attrn value supplied, or log message @return output returns result of the attrn value supplied, or log message
if error. if err.
@version 9.2 @version 9.2

View File

@@ -54,6 +54,8 @@
/* create folders and copy content */ /* create folders and copy content */
data _null_; data _null_;
length msg $200;
call missing(msg);
set work.&tempds; set work.&tempds;
if _n_ = 1 then dpos+sum(length(directory),2); if _n_ = 1 then dpos+sum(length(directory),2);
filepath2="&target/"!!substr(filepath,dpos); filepath2="&target/"!!substr(filepath,dpos);
@@ -63,9 +65,9 @@
rc1=filename(fref1,filepath,'disk','recfm=n'); rc1=filename(fref1,filepath,'disk','recfm=n');
rc2=filename(fref2,filepath2,'disk','recfm=n'); rc2=filename(fref2,filepath2,'disk','recfm=n');
if fcopy(fref1,fref2) ne 0 then do; if fcopy(fref1,fref2) ne 0 then do;
sysmsg=sysmsg(); msg=sysmsg();
putlog "%str(ERR)OR: Unable to copy " filepath " to " filepath2; putlog "%str(ERR)OR: Unable to copy " filepath " to " filepath2;
putlog sysmg=; putlog msg=;
end; end;
end; end;
rc=filename(fref1); rc=filename(fref1);

View File

@@ -1,16 +1,22 @@
/** /**
@file @file
@brief Converts every value in a dataset to it's formatted value @brief Converts every value in a dataset to formatted value
@details Converts every value to it's formatted value. All variables will @details Converts every value to it's formatted value. All variables will
become character, and will be in the same order. become character, and will be in the same order as the original dataset.
Lengths will be adjusted according to the format lengths, where applicable.
Usage: Usage:
%mp_ds2fmtds(sashelp.cars,work.cars) %mp_ds2fmtds(sashelp.cars,work.cars)
%mp_ds2fmtds(sashelp.vallopt,vw_vallopt)
@param [in] libds The library.dataset to be converted @param [in] libds The library.dataset to be converted
@param [out] outds The dataset to create. @param [out] outds The dataset to create.
<h4> SAS Macros </h4>
@li mf_existds.sas
<h4> Related Macros <h4> <h4> Related Macros <h4>
@li mp_jsonout.sas @li mp_jsonout.sas
@@ -22,8 +28,9 @@
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* validations */ /* validations */
%if not %sysfunc(exist(&libds)) %then %do;
%put %str(WARN)ING: &libds does not exist; %if not %mf_existds(libds=&libds) %then %do;
%put %str(WARN)ING: &libds does not exist as either a VIEW or DATASET;
%return; %return;
%end; %end;
%if %index(&libds,.)=0 %then %let libds=WORK.&libds; %if %index(&libds,.)=0 %then %let libds=WORK.&libds;

View File

@@ -116,6 +116,7 @@ data _null_;
if _n_>&maxobs then stop; if _n_>&maxobs then stop;
%end; %end;
length _____str $32767; length _____str $32767;
call missing(_____str);
format _numeric_ best.; format _numeric_ best.;
format _character_ ; format _character_ ;
%local i comma var vtype vfmt; %local i comma var vtype vfmt;

View File

@@ -33,8 +33,8 @@
@param [in] targetds The target dataset against which to verify the query @param [in] targetds The target dataset against which to verify the query
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions @param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
@param [out] outds= (work.mp_filtervalidate) Output dataset containing the @param [out] outds= (work.mp_filtervalidate) Output dataset containing the
error / warning message, if one exists. If this table contains any rows, err / warning message, if one exists. If this table contains any rows,
there are problems! there are problems!
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquefileref.sas @li mf_getuniquefileref.sas

View File

@@ -48,17 +48,17 @@
%let vw=%mf_getuniquename(prefix=mp_getconstraints_vw_); %let vw=%mf_getuniquename(prefix=mp_getconstraints_vw_);
data &vw /view=&vw; data &vw /view=&vw;
set sashelp.vcncolu; set sashelp.vcncolu;
where TABLE_CATALOG="&lib"; where table_catalog="&lib";
/* use retain approach to reset the constraint order with each constraint */ /* use retain approach to reset the constraint order with each constraint */
length tmp $1000; length tmp $1000;
retain tmp; retain tmp;
drop tmp; drop tmp;
if tmp ne catx('|',libref,table_name,constraint_type,constraint_name) then do; if tmp ne catx('|',table_catalog,table_name,constraint_name) then do;
constraint_order=1; constraint_order=1;
end; end;
else constraint_order+1; else constraint_order+1;
tmp=catx('|',libref, table_name, constraint_type,constraint_name); tmp=catx('|',table_catalog, table_name,constraint_name);
run; run;
/* must use SQL as proc datasets does not support length changes */ /* must use SQL as proc datasets does not support length changes */

View File

@@ -139,7 +139,7 @@ run;
%let curds=%scan(&dsnlist,&x); %let curds=%scan(&dsnlist,&x);
data _null_; data _null_;
file &fref mod; file &fref mod;
length nm lab $1024 typ $20; length lab $1024 typ $20;
set &colinfo (where=(upcase(memname)="&curds")) end=last; set &colinfo (where=(upcase(memname)="&curds")) end=last;
if _n_=1 then do; if _n_=1 then do;

View File

@@ -1,15 +1,25 @@
/** /**
@file @file
@brief Initialise session with useful settings and variables @brief Initialise session with useful settings and variables
@details Implements a set of recommended options for general SAS use. This @details Implements a "strict" set of SAS options for use in defensive
macro is NOT used elsewhere within the core library (other than in tests), programming. Highly recommended, if you want your code to run on some
but it is used by the SASjs team when building web services for other machine.
SAS-Powered applications elsewhere.
If you have a good idea for an option, setting, or useful global variable - This macro is recommended to be compiled and invoked in the `initProgram`
feel free to [raise an issue](https://github.com/sasjs/core/issues/new)! for SASjs [Jobs](https://cli.sasjs.io/sasjsconfig.html#jobConfig_initProgram
), [Services](
https://cli.sasjs.io/sasjsconfig.html#serviceConfig_initProgram) and [Tests]
(https://cli.sasjs.io/sasjsconfig.html#testConfig_initProgram).
All global variables are prefixed with "SASJS_" (unless modfied with the For non SASjs projects, you could invoke in the autoexec, or in your own
solution initialisation macro.
If you have a good idea for another useful option, setting, or global
variable - feel free to [raise an issue](
https://github.com/sasjs/core/issues/new)!
All global variables are prefixed with "SASJS" (unless modified with the
prefix parameter). prefix parameter).
@param [in] prefix= (SASJS) The prefix to apply to the global macro variables @param [in] prefix= (SASJS) The prefix to apply to the global macro variables
@@ -39,15 +49,16 @@
autocorrect /* disallow mis-spelled procedure names */ autocorrect /* disallow mis-spelled procedure names */
compress=CHAR /* default is none so ensure we have something! */ compress=CHAR /* default is none so ensure we have something! */
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */ datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
errorcheck=STRICT /* catch errors in libname/filename statements */ %str(err)orcheck=STRICT /* catch errs in libname/filename statements */
fmterr /* ensure err when a format cannot be found */ fmterr /* ensure err when a format cannot be found */
mergenoby=%str(ERR)OR /* Throw err when a merge has no BY variables */ mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
missing=. /* some sites change this which causes hard to detect errors */ missing=. /* changing this can cause hard to detect errs */
noquotelenmax /* avoid warnings for long strings */ noquotelenmax /* avoid warnings for long strings */
noreplace /* avoid overwriting permanent datasets */ noreplace /* avoid overwriting permanent datasets */
ps=max /* reduce log size slightly */ ps=max /* reduce log size slightly */
validmemname=COMPATIBLE /* avoid special characters etc in table names */ validmemname=COMPATIBLE /* avoid special characters etc in table names */
validvarname=V7 /* avoid special characters etc in variable names */ validvarname=V7 /* avoid special characters etc in variable names */
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */ varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
; ;

View File

@@ -63,7 +63,7 @@
%if &action=OPEN %then %do; %if &action=OPEN %then %do;
options nobomfile; options nobomfile;
data _null_;file &jref encoding='utf-8'; data _null_;file &jref encoding='utf-8';
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"'; put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
run; run;
%end; %end;
%else %if (&action=ARR or &action=OBJ) %then %do; %else %if (&action=ARR or &action=OBJ) %then %do;

View File

@@ -239,7 +239,7 @@ run;
%let abortme=1; %let abortme=1;
%end; %end;
/* catch errors - mp_abort must be called outside of a logic block */ /* catch errs - mp_abort must be called outside of a logic block */
%mp_abort(iftrue=(&abortme=1), %mp_abort(iftrue=(&abortme=1),
msg=%superq(msg), msg=%superq(msg),
mac=&sysmacroname mac=&sysmacroname

View File

@@ -11,22 +11,28 @@
Usage: Usage:
%mp_searchdata(lib=sashelp, string=Jan) %mp_searchdata(lib=sashelp, string=Jan)
%mp_searchdata(lib=sashelp, numval=1) %mp_searchdata(lib=sashelp, ds=bird, numval=1)
%mp_searchdata(lib=sashelp, ds=class, string=l,outobs=5)
Outputs zero or more tables to an MPSEARCH library with specific records. Outputs zero or more tables to an MPSEARCH library with specific records.
@param lib= the libref to search (should be already assigned) @param [in] lib= The libref to search (should be already assigned)
@param ds= the dataset to search (leave blank to search entire library) @param [in] ds= The dataset to search (leave blank to search entire library)
@param string= the string value to search @param [in] string= String value to search (case sensitive, can be partial)
@param numval= the numeric value to search (must be exact) @param [in] numval= Numeric value to search (must be exact)
@param outloc= the directory in which to create the output datasets with @param [out] outloc= (0) Optionally specify the directory in which to
matching rows. Will default to a subfolder in the WORK library. create the the output datasets with matching rows. By default it will
@param outobs= set to a positive integer to restrict the number of write them to a temporary subdirectory within the WORK folder.
@param [out] outlib= (MPSEARCH) Assign a different libref to the output
library containing the matching datasets / records
@param [in] outobs= set to a positive integer to restrict the number of
observations observations
@param filter_text= add a (valid) filter clause to further filter the results @param [in] filter_text= (1=1) Add a (valid) filter clause to further filter
the results.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquename.sas
@li mf_getvarlist.sas @li mf_getvarlist.sas
@li mf_getvartype.sas @li mf_getvartype.sas
@li mf_mkdir.sas @li mf_mkdir.sas
@@ -36,11 +42,12 @@
@author Allan Bowe @author Allan Bowe
**/ **/
%macro mp_searchdata(lib=sashelp %macro mp_searchdata(lib=
,ds= ,ds=
,string= /* the query will use a contains (?) operator */ ,string= /* the query will use a contains (?) operator */
,numval= /* numeric must match exactly */ ,numval= /* numeric must match exactly */
,outloc=%sysfunc(pathname(work))/mpsearch ,outloc=0
,outlib=MPSEARCH
,outobs=-1 ,outobs=-1
,filter_text=%str(1=1) ,filter_text=%str(1=1)
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
@@ -57,8 +64,12 @@
%if &string = %then %let type=N; %if &string = %then %let type=N;
%else %let type=C; %else %let type=C;
%if "&outloc"="0" %then %do;
%let outloc=%sysfunc(pathname(work))/%mf_getuniquename();
%end;
%mf_mkdir(&outloc) %mf_mkdir(&outloc)
libname mpsearch "&outloc"; libname &outlib "&outloc";
/* get the list of tables in the library */ /* get the list of tables in the library */
proc sql noprint; proc sql noprint;
@@ -70,11 +81,6 @@ select distinct memname into: table_list separated by ' '
%end; %end;
; ;
/* check that we have something to check */ /* check that we have something to check */
proc sql
%if &outobs>-1 %then %do;
outobs=&outobs
%end;
;
%if %length(&table_list)=0 %then %put library &lib contains no tables!; %if %length(&table_list)=0 %then %put library &lib contains no tables!;
/* loop through each table */ /* loop through each table */
%else %do table_num=1 %to %sysfunc(countw(&table_list,%str( ))); %else %do table_num=1 %to %sysfunc(countw(&table_list,%str( )));
@@ -85,10 +91,10 @@ proc sql
%end; %end;
%else %do; %else %do;
%let check_tm=%sysfunc(datetime()); %let check_tm=%sysfunc(datetime());
/* build sql statement */ /* prep input */
create table mpsearch.&table as select * from &lib..&table data &outlib..&table;
where %unquote(&filter_text) and set &lib..&table;
(0 where %unquote(&filter_text) and ( 0
/* loop through columns */ /* loop through columns */
%do colnum=1 %to %sysfunc(countw(&vars,%str( ))); %do colnum=1 %to %sysfunc(countw(&vars,%str( )));
%let col=%scan(&vars,&colnum,%str( )); %let col=%scan(&vars,&colnum,%str( ));
@@ -102,15 +108,20 @@ proc sql
or ("&col"n = &numval) or ("&col"n = &numval)
%end; %end;
%end; %end;
); );
%if &outobs>-1 %then %do;
if _n_ > &outobs then stop;
%end;
run;
%put Search query for &table took %put Search query for &table took
%sysevalf(%sysfunc(datetime())-&check_tm) seconds; %sysevalf(%sysfunc(datetime())-&check_tm) seconds;
%if &sqlrc ne 0 %then %do; %if &syscc ne 0 %then %do;
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table; %put %str(ERR)ROR: SYSCC=&syscc when processing &lib..&table;
%return; %return;
%end; %end;
%if %mf_nobs(mpsearch.&table)=0 %then %do; %if %mf_nobs(&outlib..&table)=0 %then %do;
drop table mpsearch.&table; proc sql;
drop table &outlib..&table;
%end; %end;
%end; %end;
%end; %end;

View File

@@ -95,7 +95,7 @@ data _null_;
put '%if &action=OPEN %then %do; '; put '%if &action=OPEN %then %do; ';
put ' options nobomfile; '; put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8''; '; put ' data _null_;file &jref encoding=''utf-8''; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; '; put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';

View File

@@ -0,0 +1,33 @@
/**
@file
@brief Testing mf_isint macro
<h4> SAS Macros </h4>
@li mf_isint.sas
@li mp_assert.sas
**/
%mp_assert(
iftrue=(
"%mf_isint(1)"="1"
),
desc=Checking basic mf_isint(1),
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_isint(1.1)"="0"
),
desc=Checking basic mf_isint(1.1),
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_isint(-1)"="1"
),
desc=Checking mf_isint(-1),
outds=work.test_results
)

View File

@@ -15,10 +15,12 @@ filename inc temp;
data _null_; data _null_;
set work.test; set work.test;
file inc; file inc;
line=cats('%mp_ds2fmtds(sashelp.',memname,',',memname,')'); libds=cats('sashelp.',memname);
if exist(libds) then line=cats('%mp_ds2fmtds(',libds,',',memname,')');
put line; put line;
run; run;
options obs=50;
%inc inc; %inc inc;
%mp_assert( %mp_assert(

View File

@@ -0,0 +1,29 @@
/**
@file
@brief Testing mp_searchdata.sas
<h4> SAS Macros </h4>
@li mp_searchdata.sas
@li mp_assert.sas
**/
/** Test 1 - generic useage */
%mp_searchdata(lib=sashelp, ds=class, string=a)
%mp_assert(
iftrue=(&syscc=0),
desc=No errors in regular usage,
outds=work.test_results
)
/** Test 2 - with obs issue */
%mp_searchdata(lib=sashelp, ds=class, string=l,outobs=5)
%mp_assert(
iftrue=("&SYSWARNINGTEXT" = ""),
desc=Ensuring WARN status is clean,
outds=work.test_results
)

View File

@@ -33,6 +33,7 @@ run;
%put TEST1: checking web service code; %put TEST1: checking web service code;
data work.test_results; data work.test_results;
length test_description $256 test_result $4 test_comments $256; length test_description $256 test_result $4 test_comments $256;
if _n_=1 then call missing (of _all_);
infile compare end=eof; infile compare end=eof;
input; input;
if eof then do; if eof then do;

View File

@@ -243,7 +243,7 @@ data _null_;
put '%if &action=OPEN %then %do; '; put '%if &action=OPEN %then %do; ';
put ' options nobomfile; '; put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8''; '; put ' data _null_;file &jref encoding=''utf-8''; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; '; put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';

View File

@@ -100,6 +100,8 @@ options noquotelenmax;
%local href cnt; %local href cnt;
%let cnt=0; %let cnt=0;
data _null_; data _null_;
length rel href $512;
call missing(rel,href);
set &libref1..links; set &libref1..links;
if rel='members' then do; if rel='members' then do;
url=cats("'","&base_uri",href,"?limit=10000'"); url=cats("'","&base_uri",href,"?limit=10000'");
@@ -123,6 +125,7 @@ options noquotelenmax;
libname &libref2 JSON fileref=&fname2; libname &libref2 JSON fileref=&fname2;
data &outds; data &outds;
length id $36 name $128 uri $64 type $32 description $256; length id $36 name $128 uri $64 type $32 description $256;
if _n_=1 then call missing (of _all_);
set &libref2..items; set &libref2..items;
run; run;
filename &fname2 clear; filename &fname2 clear;

View File

@@ -92,6 +92,8 @@ data;run;
%local joburi; %local joburi;
%let joburi=0; %let joburi=0;
data _null_; data _null_;
length name uri $512;
call missing(name,uri);
set &foldermembers; set &foldermembers;
if name="&name" and uri=:'/jobDefinitions/definitions' if name="&name" and uri=:'/jobDefinitions/definitions'
then call symputx('joburi',uri); then call symputx('joburi',uri);

View File

@@ -115,6 +115,8 @@ data;run;
%local joburi; %local joburi;
%let joburi=0; %let joburi=0;
data _null_; data _null_;
length name uri $512;
call missing(name,uri);
set &foldermembers; set &foldermembers;
if name="&name" and uri=:'/jobDefinitions/definitions' if name="&name" and uri=:'/jobDefinitions/definitions'
then call symputx('joburi',uri); then call symputx('joburi',uri);

View File

@@ -168,6 +168,7 @@ run;
data &outds; data &outds;
format _program uri $128. state $32. stateDetails $32. timestamp datetime19. format _program uri $128. state $32. stateDetails $32. timestamp datetime19.
jobparams $32767.; jobparams $32767.;
call missing (of _all_);
stop; stop;
run; run;