1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-03 23:50:06 +00:00

Compare commits

...

22 Commits

Author SHA1 Message Date
Allan Bowe
09136cfdbb Merge pull request #122 from sasjs/serverfix
fix: making ms_webout work with SAS on Windows Desktop
2021-12-18 14:12:35 +00:00
munja
0ca16f3d04 fix: quoting the server option in mm_assigndirectlib() to avoid assignment errors when the name contains dashes aetc 2021-12-18 14:11:38 +00:00
Allan Bowe
1e72f13f2d fix: making ms_webout work with SAS on Windows Desktop 2021-12-17 23:01:43 +00:00
Allan Bowe
5e8e8e02d3 Merge pull request #121 from sasjs/mp_ds2md
BREAKING CHANGE: renamed mp_mdtablewrite.sas to mp_ds2md.sas
2021-12-17 17:31:01 +00:00
munja
b2e2c7c798 chore(lint): fix indentation 2021-12-17 17:14:41 +00:00
munja
b29dd38188 fix: preventing merged cells in sas-generated markdown tables.
BREAKING CHANGE: renamed mp_mdtablewrite.sas to mp_ds2md.sas and modified the parameter from fref to outref.  This makes it more consistent with the other mp_ds2xx range of macros.
2021-12-17 17:14:03 +00:00
Allan Bowe
2e122c2ada Merge pull request #120 from sasjs/fix_makedata
fix: check charvars and numvars exist. Closes #119
2021-12-17 15:25:36 +00:00
Ivor Townsend
8b68c3bb27 fix: check charvars and numvars exist. Closes #119 2021-12-17 15:08:56 +00:00
Allan Bowe
8c7523deda Merge pull request #118 from sasjs/fmting
More Macros
2021-12-17 10:28:37 +00:00
munja
e8f656f48a chore: lint fixes 2021-12-17 00:57:35 +00:00
munja
1eb90202b9 fix: failing test for mp_getformats 2021-12-17 00:55:51 +00:00
munja
82108f4b97 fix: ensuring test for mp_sortinplace passes, fixing uninitialised var in mp_mdtablewrite, fix for non pk table in mp_getformats 2021-12-17 00:53:13 +00:00
munja
ab1030afb1 feat: finishing mp_formats and adding a test, including prefix in mp_init, allowing mp_sortinplace to work when there is no primary key, sand other small fixes 2021-12-17 00:32:49 +00:00
munja
26292740bb feat: mp_getformats() macro. Extracts bformat summary and detail 2021-12-16 00:02:52 +00:00
munja
96cc131305 chore(docs): updating header in mf_getquotedstr 2021-12-16 00:01:58 +00:00
munja
724cd72876 feat: adding mf_getfmtlist() and mf_getfmtname() macros and associated tests. Also added &sasjswork as a global macro variable in mp_init(). 2021-12-15 21:05:12 +00:00
munja
fa5d9ef744 feat: adding ls=max to mp_init.sas to reduce log ever so slightly and also to avoid word truncation 2021-12-15 19:20:14 +00:00
Allan Bowe
dc63b4adf5 Merge pull request #117 from sasjs/mf_dedup
`mf_dedup()` macro - removes duplicates from a macro string
2021-12-15 17:44:54 +00:00
munja
3f20ca03dd chore: updating all.sas 2021-12-15 17:04:27 +00:00
munja
3a826dccf1 feat: mf_dedup and associated test 2021-12-15 17:04:08 +00:00
munja
a1ce68ce56 fix: avoiding uninitialised variables in mm_getdetails and mm_gettables.sas 2021-12-15 15:10:17 +00:00
munja
a45384aacb chore: updating all.sas 2021-12-15 12:18:56 +00:00
27 changed files with 1083 additions and 193 deletions

View File

@@ -6,7 +6,7 @@
"hasMacroParentheses": true, "hasMacroParentheses": true,
"noNestedMacros": false, "noNestedMacros": false,
"noSpacesInFileNames": true, "noSpacesInFileNames": true,
"maxLineLength": 230, "maxLineLength": 300,
"lowerCaseFileNames": true, "lowerCaseFileNames": true,
"noTabIndentation": true, "noTabIndentation": true,
"indentationMultiple": 2 "indentationMultiple": 2

565
all.sas
View File

@@ -43,6 +43,60 @@ options noquotelenmax;
%mend mf_abort; %mend mf_abort;
/** @endcond *//** /** @endcond *//**
@file
@brief de-duplicates a macro string
@details Removes all duplicates from a string of words. A delimeter can be
chosen. Is inspired heavily by this excellent [macro](
https://github.com/scottbass/SAS/blob/master/Macro/dedup_mstring.sas) from
[Scott Base](https://www.linkedin.com/in/scottbass). Case sensitive.
Usage:
%let str=One two one two and through and through;
%put %mf_dedup(&str);
%put %mf_dedup(&str,outdlm=%str(,));
Which returns:
> One two one and through
> One,two,one,and,through
@param [in] str String to be deduplicated
@param [in] indlm= ( ) Delimeter of the input string
@param [out] outdlm= ( ) Delimiter of the output string
<h4> Related Macros </h4>
@li mf_trimstr.sas
@li mf_wordsinstr1butnotstr2.sas
@version 9.2
@author Allan Bowe
**/
%macro mf_dedup(str
,indlm=%str( )
,outdlm=%str( )
)/*/STORE SOURCE*/;
%local num word i pos out;
%* loop over each token, searching the target for that token ;
%let num=%sysfunc(countc(%superq(str),%str(&indlm)));
%do i=1 %to %eval(&num+1);
%let word=%scan(%superq(str),&i,%str(&indlm));
%let pos=%sysfunc(indexw(&out,&word,%str(&outdlm)));
%if (&pos eq 0) %then %do;
%if (&i gt 1) %then %let out=&out%str(&outdlm);
%let out=&out&word;
%end;
%end;
%unquote(&out)
%mend mf_dedup;
/**
@file mf_existds.sas @file mf_existds.sas
@brief Checks whether a dataset OR a view exists. @brief Checks whether a dataset OR a view exists.
@details Can be used in open code, eg as follows: @details Can be used in open code, eg as follows:
@@ -515,6 +569,109 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
%end; %end;
%mend mf_getfilesize ;/** %mend mf_getfilesize ;/**
@file
@brief Returns a distinct list of formats from a table
@details Reads the dataset header and returns a distinct list of formats
applied.
%put NOTE- %mf_getfmtlist(sashelp.prdsale);
%put NOTE- %mf_getfmtlist(sashelp.shoes);
%put NOTE- %mf_getfmtlist(sashelp.demographics);
returns:
> DOLLAR $CHAR W MONNAME
> $CHAR BEST DOLLAR
> BEST Z $CHAR COMMA PERCENTN
@param [in] libds Two part library.dataset reference.
<h4> SAS Macros </h4>
@li mf_getfmtname.sas
@version 9.2
@author Allan Bowe
**/
%macro mf_getfmtlist(libds
)/*/STORE SOURCE*/;
/* declare local vars */
%local out dsid nvars x rc fmt;
/* open dataset in macro */
%let dsid=%sysfunc(open(&libds));
/* continue if dataset exists */
%if &dsid %then %do;
/* loop each variable in the dataset */
%let nvars=%sysfunc(attrn(&dsid,NVARS));
%do x=1 %to &nvars;
/* grab format and check it exists */
%let fmt=%sysfunc(varfmt(&dsid,&x));
%if %quote(&fmt) ne %quote() %then %let fmt=%mf_getfmtname(&fmt);
%else %do;
/* assign default format depending on variable type */
%if %sysfunc(vartype(&dsid, &x))=C %then %let fmt=$CHAR;
%else %let fmt=BEST;
%end;
/* concatenate unique list of formats */
%if %sysfunc(indexw(&out,&fmt,%str( )))=0 %then %let out=&out &fmt;
%end;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put &sysmacroname: Unable to open &libds (rc=&dsid);
%put &sysmacroname: SYSMSG= %sysfunc(sysmsg());
%let rc=%sysfunc(close(&dsid));
%end;
/* send them out without spaces or quote markers */
%do;%unquote(&out)%end;
%mend mf_getfmtlist;/**
@file
@brief Extracts a format name from a fully defined format
@details Converts formats in like $thi3. and th13.2 $THI and TH.
Usage:
%put %mf_getfmtname(8.);
%put %mf_getfmtname($4.);
%put %mf_getfmtname(comma14.10);
Returns:
> W
> $CHAR
> COMMA
Note that system defaults are inferred from the values provided.
@param [in] fmt The fully defined format. If left blank, nothing is returned.
@returns The name (without width or decimal) of the format.
@version 9.2
@author Allan Bowe
**/
%macro mf_getfmtname(fmt
)/*/STORE SOURCE*/ /minoperator mindelimiter=' ';
%local out dsid nvars x rc fmt;
/* extract actual format name from the format definition */
%let fmt=%scan(&fmt,1,.);
%do %while(%substr(&fmt,%length(&fmt),1) in 1 2 3 4 5 6 7 8 9 0);
%if %length(&fmt)=1 %then %let fmt=W;
%else %let fmt=%substr(&fmt,1,%length(&fmt)-1);
%end;
%if &fmt=$ %then %let fmt=$CHAR;
/* send them out without spaces or quote markers */
%do;%unquote(%upcase(&fmt))%end;
%mend mf_getfmtname;/**
@file @file
@brief retrieves a key value pair from a control dataset @brief retrieves a key value pair from a control dataset
@details By default, control dataset is work.mp_setkeyvalue. Usage: @details By default, control dataset is work.mp_setkeyvalue. Usage:
@@ -627,8 +784,8 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
> "these","words","are","double","quoted" > "these","words","are","double","quoted"
@param [in] in_str The unquoted, spaced delimited string to transform @param [in] in_str The unquoted, spaced delimited string to transform
@param [in] dlm= The delimeter to be applied to the output (default comma) @param [in] dlm= (,) The delimeter to be applied to the output (default comma)
@param [in] indlm= (,) The delimeter used for the input (default is space) @param [in] indlm= ( ) The delimeter used for the input (default is space)
@param [in] quote= (S) The quote mark to apply (S=Single, D=Double, N=None). @param [in] quote= (S) The quote mark to apply (S=Single, D=Double, N=None).
If any other value than uppercase S or D is supplied, then that value will If any other value than uppercase S or D is supplied, then that value will
be used as the quoting character. be used as the quoting character.
@@ -639,7 +796,10 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
**/ **/
%macro mf_getquotedstr(IN_STR,DLM=%str(,),QUOTE=S,indlm=%str( ) %macro mf_getquotedstr(IN_STR
,DLM=%str(,)
,QUOTE=S
,indlm=%str( )
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* credit Rowland Hale - byte34 is double quote, 39 is single quote */ /* credit Rowland Hale - byte34 is double quote, 39 is single quote */
%if &quote=S %then %let quote=%qsysfunc(byte(39)); %if &quote=S %then %let quote=%qsysfunc(byte(39));
@@ -4192,6 +4352,103 @@ data _null_;
run; run;
%mend mp_ds2inserts;/** %mend mp_ds2inserts;/**
@file
@brief Create a Markdown Table from a dataset
@details A markdown table is a simple table representation for use in
documents written in markdown format.
An online generator is available here:
https://www.tablesgenerator.com/markdown_tables
This structure is also used by the Macro Core library for documenting input/
output datasets, as well as the sasjs/cli tool for documenting inputs/outputs
for web services.
We take the standard definition one step further by embedding the informat
in the table header row, like so:
|var1:$32|var2:best.|var3:date9.|
|---|---|---|
|some text|42|01JAN1960|
|blah|1|31DEC1999|
Which resolves to:
|var1:$32|var2:best.|var3:date9.|
|---|---|---|
|some text|42|01JAN1960|
|blah|1|31DEC1999|
Usage:
%mp_ds2md(sashelp.class)
@param [in] libds the library / dataset to create or read from.
@param [out] outref= (mdtable) Fileref to contain the markdown
@param [out] showlog= (YES) Set to NO to avoid printing markdown to the log
<h4> SAS Macros </h4>
@li mf_getvarlist.sas
@li mf_getvarformat.sas
@version 9.3
@author Allan Bowe
**/
%macro mp_ds2md(
libds,
outref=mdtable,
showlog=YES
)/*/STORE SOURCE*/;
/* check fileref is assigned */
%if %sysfunc(fileref(&outref)) > 0 %then %do;
filename &outref temp;
%end;
%local vars;
%let vars=%upcase(%mf_getvarlist(&libds));
/* create the header row */
data _null_;
file &outref;
length line $32767;
call missing(line);
put '|'
%local i var fmt;
%do i=1 %to %sysfunc(countw(&vars));
%let var=%scan(&vars,&i);
%let fmt=%lowcase(%mf_getvarformat(&libds,&var,force=1));
"&var:&fmt|"
%end;
;
put '|'
%do i=1 %to %sysfunc(countw(&vars));
"---|"
%end;
;
run;
/* write out the data */
data _null_;
file &outref mod dlm='|' lrecl=32767;
set &libds ;
length line $32767;
line='|`'!!cats(%mf_getvarlist(&libds,dlm=%str(%)!!' `|`'!!cats%()))!!' `|';
put line;
run;
%if %upcase(&showlog)=YES %then %do;
options ps=max;
data _null_;
infile &outref;
input;
putlog _infile_;
run;
%end;
%mend mp_ds2md;/**
@file @file
@brief Checks an input filter table for validity @brief Checks an input filter table for validity
@details Performs checks on the input table to ensure it arrives in the @details Performs checks on the input table to ensure it arrives in the
@@ -5495,6 +5752,146 @@ run;
%end; %end;
%mend mp_getddl;/** %mend mp_getddl;/**
@file
@brief Export format definitions
@details Formats are exported from the first (if any) catalog entry in the
FMTSEARCH path.
Formats are taken from the library / dataset reference and / or a static
format list.
Example usage:
%mp_getformats(lib=sashelp,ds=prdsale,outsummary=work.dictable)
@param [in] lib= (0) The libref for which to return formats.
@todo Enable exporting of formats for an entire library
@param [in] ds= (0) The dataset from which to obtain format definitions
@param [in] fmtlist= (0) A list of additional format names
@param [out] outsummary= (work.mp_getformats_summary) Output dataset
containing summary definitions - structure taken from dictionary.formats as
follows:
|libname:$8.|memname:$32.|path:$1024.|objname:$32.|fmtname:$32.|fmttype:$1.|source:$1.|minw:best.|mind:best.|maxw:best.|maxd:best.|defw:best.|defd:best.|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| | | | |$|F|B|1|0|32767|0|1|0|
| | | | |$|I|B|1|0|32767|0|1|0|
|` `|` `|/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWIANYDT|$ANYDTIF|I|U|1|0|60|0|19|0|
| | |/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWFASCII|$ASCII|F|U|1|0|32767|0|1|0|
| | |/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWIASCII|$ASCII|I|U|1|0|32767|0|1|0|
| | |/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWFBASE6|$BASE64X|F|U|1|0|32767|0|1|0|
@param [out] outdetail= (0) Provide an output dataset in which to export all
the custom format definitions (from proc format CNTLOUT). Definitions:
https://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#a002473477.htm
Sample data:
|FMTNAME:$32.|START:$16.|END:$16.|LABEL:$256.|MIN:best.|MAX:best.|DEFAULT:best.|LENGTH:best.|FUZZ:best.|PREFIX:$2.|MULT:best.|FILL:$1.|NOEDIT:best.|TYPE:$1.|SEXCL:$1.|EEXCL:$1.|HLO:$13.|DECSEP:$1.|DIG3SEP:$1.|DATATYPE:$8.|LANGUAGE:$8.|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|`WHICHPATH `|`0 `|`0 `|`path1 `|`1 `|`40 `|`28 `|`28 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`N `|` `|` `|` `|` `|` `|
|`WHICHPATH `|`**OTHER** `|`**OTHER** `|`big fat problem if not path1 `|`1 `|`40 `|`28 `|`28 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`N `|`O `|` `|` `|` `|` `|
<h4> SAS Macros </h4>
@li mf_dedup.sas
@li mf_getfmtlist.sas
@li mf_getfmtname.sas
@li mf_getquotedstr.sas
@li mf_getuniquename.sas
<h4> Related Macros </h4>
@li mp_getformats.test.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_getformats(lib=0
,ds=0
,fmtlist=0
,outsummary=work.mp_getformats_summary
,outdetail=0
);
%local i fmt allfmts tempds fmtcnt;
%if "&fmtlist" ne "0" %then %do i=1 %to %sysfunc(countw(&fmtlist,,%str( )));
/* ensure format list contains format _name_ only */
%let fmt=%scan(&fmtlist,&i,%str( ));
%let fmt=%mf_getfmtname(&fmt);
%let allfmts=&allfmts &fmt;
%end;
%if &ds=0 and &lib ne 0 %then %do;
/* grab formats from library */
/* to do */
%end;
%else %if &ds ne 0 and &lib ne 0 %then %do;
/* grab formats from dataset */
%let allfmts=%mf_getfmtlist(&lib..&ds) &allfmts;
%end;
/* ensure list is unique */
%let allfmts=%mf_dedup(%upcase(&allfmts));
/* create summary table */
%if %index(&outsummary,.)=0 %then %let outsummary=WORK.&outsummary;
proc sql;
create table &outsummary as
select * from dictionary.formats
where fmtname in (%mf_getquotedstr(&allfmts,quote=D))
and fmttype='F';
%if "&outdetail" ne "0" %then %do;
/* ensure base table always exists */
proc sql;
create table &outdetail(
FMTNAME char(32) label='Format name'
,START char(16) label='Starting value for format'
,END char(16) label='Ending value for format'
,LABEL char(256) label='Format value label'
,MIN num length=3 label='Minimum length'
,MAX num length=3 label='Maximum length'
,DEFAULT num length=3 label='Default length'
,LENGTH num length=3 label='Format length'
,FUZZ num label='Fuzz value'
,PREFIX char(2) label='Prefix characters'
,MULT num label='Multiplier'
,FILL char(1) label='Fill character'
,NOEDIT num length=3 label='Is picture string noedit?'
,TYPE char(1) label='Type of format'
,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information'
,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings'
);
/* grab the location of each format */
%let fmtcnt=0;
data _null_;
set &outsummary;
if not missing(libname);
x+1;
call symputx(cats('fmtloc',x),cats(libname,'.',memname),'l');
call symputx(cats('fmtname',x),fmtname,'l');
call symputx('fmtcnt',x,'l');
run;
/* export each format and append to the output table */
%let tempds=%mf_getuniquename(prefix=mp_getformats);
%do i=1 %to &fmtcnt;
proc format library=&&fmtloc&i CNTLOUT=&tempds;
select &&fmtname&i;
run;
proc append base=&outdetail data=&tempds;
run;
%end;
%end;
%mend mp_getformats;/**
@file mp_getmaxvarlengths.sas @file mp_getmaxvarlengths.sas
@brief Scans a dataset to find the max length of the variable values @brief Scans a dataset to find the max length of the variable values
@details @details
@@ -6405,23 +6802,25 @@ filename &tempref clear;
**/ **/
%macro mp_init(prefix= %macro mp_init(prefix=SASJS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%global %global
&prefix._INIT_NUM /* initialisation time as numeric */ &prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */ &prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
; ;
%if %eval(&&&prefix._INIT_NUM>0) %then %return; /* only run once */ %if %eval(&&&prefix._INIT_NUM>0) %then %return; /* only run once */
data _null_; data _null_;
dttm=datetime(); dttm=datetime();
call symputx("&prefix._init_num",dttm); call symputx("&prefix._init_num",dttm,'g');
call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6)); call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6),'g');
call symputx("&prefix.work",pathname('WORK'),'g');
run; run;
options options
autocorrect /* disallow mis-spelled procedure names */ noautocorrect /* disallow misspelled 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 */
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */ %str(err)orcheck=STRICT /* catch errs in libname/filename statements */
@@ -6431,6 +6830,7 @@ filename &tempref clear;
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 */
ls=max /* reduce log even more and avoid word truncation */
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 */ varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
@@ -7240,13 +7640,13 @@ data &ds1;
&n1=ranuni(1)*5000000; &n1=ranuni(1)*5000000;
drop &c1 &n1; drop &c1 &n1;
%let charvars=%mf_getvarlist(&libds,typefilter=C); %let charvars=%mf_getvarlist(&libds,typefilter=C);
%do i=1 %to %sysfunc(countw(&charvars)); %if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars));
%let col=%scan(&charvars,&i); %let col=%scan(&charvars,&i);
&col=subpad(&c1,1,%mf_getvarlen(&libds,&col)); &col=subpad(&c1,1,%mf_getvarlen(&libds,&col));
%end; %end;
%let numvars=%mf_getvarlist(&libds,typefilter=N); %let numvars=%mf_getvarlist(&libds,typefilter=N);
%do i=1 %to %sysfunc(countw(&numvars)); %if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars));
%let col=%scan(&numvars,&i); %let col=%scan(&numvars,&i);
&col=&n1; &col=&n1;
%end; %end;
@@ -7261,103 +7661,6 @@ proc sql;
drop table &ds1; drop table &ds1;
%mend mp_makedata;/** %mend mp_makedata;/**
@file
@brief Create a Markdown Table from a dataset
@details A markdown table is a simple table representation for use in
documents written in markdown format.
An online generator is available here:
https://www.tablesgenerator.com/markdown_tables
This structure is also used by the Macro Core library for documenting input/
output datasets, as well as the sasjs/cli tool for documenting inputs/outputs
for web services.
We take the standard definition one step further by embedding the informat
in the table header row, like so:
|var1:$32|var2:best.|var3:date9.|
|---|---|---|
|some text|42|01JAN1960|
|blah|1|31DEC1999|
Which resolves to:
|var1:$32|var2:best.|var3:date9.|
|---|---|---|
|some text|42|01JAN1960|
|blah|1|31DEC1999|
Usage:
%mp_mdtablewrite(libds=sashelp.class,showlog=YES)
<h4> SAS Macros </h4>
@li mf_getvarlist.sas
@li mf_getvarformat.sas
@param [in] libds= the library / dataset to create or read from.
@param [out] fref= Fileref to contain the markdown. Default=mdtable.
@param [out] showlog= set to YES to show the markdown in the log. Default=NO.
@version 9.3
@author Allan Bowe
**/
%macro mp_mdtablewrite(
libds=,
fref=mdtable,
showlog=NO
)/*/STORE SOURCE*/;
/* check fileref is assigned */
%if %sysfunc(fileref(&fref)) > 0 %then %do;
filename &fref temp;
%end;
%local vars;
%let vars=%mf_getvarlist(&libds);
/* create the header row */
data _null_;
file &fref;
length line $32767;
put '|'
%local i var fmt;
%do i=1 %to %sysfunc(countw(&vars));
%let var=%scan(&vars,&i);
%let fmt=%mf_getvarformat(&libds,&var,force=1);
"&var:&fmt|"
%end;
;
put '|'
%do i=1 %to %sysfunc(countw(&vars));
"---|"
%end;
;
run;
/* write out the data */
data _null_;
file &fref mod dlm='|' lrecl=32767;
set &libds ;
length line $32767;
line=cats('|',%mf_getvarlist(&libds,dlm=%str(,'|',)),'|');
put line;
run;
%if %upcase(&showlog)=YES %then %do;
options ps=max;
data _null_;
infile &fref;
input;
putlog _infile_;
run;
%end;
%mend mp_mdtablewrite;/**
@file @file
@brief Logs the time the macro was executed in a control dataset. @brief Logs the time the macro was executed in a control dataset.
@details If the dataset does not exist, it is created. Usage: @details If the dataset does not exist, it is created. Usage:
@@ -7975,13 +8278,15 @@ select distinct memname into: table_list separated by ' '
sort it before performing operations such as merges / joins etc. sort it before performing operations such as merges / joins etc.
That said, there are a few edge cases where it can be desirable: That said, there are a few edge cases where it can be desirable:
@li To improve performance for particular scenarios
@li To allow adjacent records to be viewed directly in the dataset @li To allow adjacent records to be viewed directly in the dataset
@li To reduce dataset size (eg when there are deleted records) @li To apply compression, or to remove deleted records
@li To improve performance for specific queries
This macro will only work for BASE (V9) engine libraries. It works by This macro will only work for BASE (V9) engine libraries. It works by
creating a copy of the dataset (without data, WITH constraints) in the same creating a copy of the dataset (without data, WITH constraints) in the same
library, appending a sorted view into it, and finally - renaming it. library, appending a sorted view into it, and finally - renaming it. By
default, COMPRESS=CHAR and REUSE=YES will be applied, this behaviour can
be adjusted using the `dsoptions=` parameter.
Example usage: Example usage:
@@ -7999,6 +8304,7 @@ select distinct memname into: table_list separated by ' '
@li mf_getengine.sas @li mf_getengine.sas
@li mf_getquotedstr.sas @li mf_getquotedstr.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvarlist.sas
@li mf_nobs.sas @li mf_nobs.sas
@li mp_abort.sas @li mp_abort.sas
@li mp_getpk.sas @li mp_getpk.sas
@@ -8044,9 +8350,13 @@ select distinct memname into: table_list separated by ' '
%return; %return;
%end; %end;
/* fallback sortkey is all fields */
%let sortkey=%mf_getvarlist(&libds);
/* overlay actual sort key if it exists */
data _null_; data _null_;
set work.&tempds1; set work.&tempds1;
call symputx('sortkey',pk_fields); call symputx('sortkey',coalescec(pk_fields,symget('sortkey')));
run; run;
@@ -8152,10 +8462,10 @@ run;
'Did value change? (1/0/-1). Always -1 for appends and deletes.', 'Did value change? (1/0/-1). Always -1 for appends and deletes.',
tgtvar_type char(1) label='Either (C)haracter or (N)umeric', tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
tgtvar_nm char(32) label='Target variable name (32 chars)', tgtvar_nm char(32) label='Target variable name (32 chars)',
oldval_num num label='Old (numeric) value', oldval_num num format=best32. label='Old (numeric) value',
newval_num num label='New (numeric) value', newval_num num format=best32. label='New (numeric) value',
oldval_char char(32767) label='Old (character) value', oldval_char char(32765) label='Old (character) value',
newval_char char(32767) label='New (character) value', newval_char char(32765) label='New (character) value',
constraint pk_mpe_audit constraint pk_mpe_audit
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm) primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
); );
@@ -8299,18 +8609,18 @@ create table &outds as
,b.tgtvar_type length=1 ,b.tgtvar_type length=1
,case when b.move_type='D' then b.newval_num ,case when b.move_type='D' then b.newval_num
else a.newval_num else a.newval_num
end as oldval_num end as oldval_num format=best32.
,case when b.move_type='D' then . ,case when b.move_type='D' then .
else b.newval_num else b.newval_num
end as newval_num end as newval_num format=best32.
,case when b.move_type='D' then b.newval_char ,case when b.move_type='D' then b.newval_char
else a.newval_char else a.newval_char
end as oldval_char length=32767 end as oldval_char length=32765
,case when b.move_type='D' then '' ,case when b.move_type='D' then ''
else b.newval_char else b.newval_char
end as newval_char length=32767 end as newval_char length=32765
from &ds4(where=(move_type='O')) as a from &ds4(where=(move_type='O')) as a
full join &ds4(where=(move_type ne 'O')) as b right join &ds4(where=(move_type ne 'O')) as b
on a.tgtvar_nm=b.tgtvar_nm on a.tgtvar_nm=b.tgtvar_nm
and a.key_hash=b.key_hash and a.key_hash=b.key_hash
order by move_type, key_hash,is_pk desc, tgtvar_nm; order by move_type, key_hash,is_pk desc, tgtvar_nm;
@@ -9834,7 +10144,7 @@ run;
prop='Connection.DBMS.Property.SERVER.Name.xmlKey.txt'; prop='Connection.DBMS.Property.SERVER.Name.xmlKey.txt';
rc=metadata_getprop(uri,prop,server,""); rc=metadata_getprop(uri,prop,server,"");
end; end;
if server^='' then server='server='!!server; if server^='' then server='server='!!quote(cats(server));
call symputx('server',server,'l'); call symputx('server',server,'l');
/* get SCHEMA value */ /* get SCHEMA value */
@@ -9980,11 +10290,11 @@ run;
run; run;
%put NOTE: Executing the following:/; %put NOTE-; %put NOTE: Executing the following:/; %put NOTE-;
%put NOTE- libname &libref TERADATA server=&path schema=&schema ; %put NOTE- libname &libref TERADATA server="&path" schema=&schema ;
%put NOTe- authdomain=&authdomain; %put NOTe- authdomain=&authdomain;
%put NOTE-; %put NOTE-;
libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain; libname &libref TERADATA server="&path" schema=&schema authdomain=&authdomain;
%end; %end;
%else %if &engine= %then %do; %else %if &engine= %then %do;
%put NOTE: Libref &libref is not registered in metadata; %put NOTE: Libref &libref is not registered in metadata;
@@ -12232,7 +12542,7 @@ run;
data &outattrs; data &outattrs;
keep type name value; keep type name value;
length type $4 name $256 value $32767; length type $4 name $256 value $32767;
rc1=1;n1=1;type='Prop'; rc1=1;n1=1;type='Prop';name='';value='';
do while(rc1>0); do while(rc1>0);
rc1=metadata_getnprp("&uri",n1,name,value); rc1=metadata_getnprp("&uri",n1,name,value);
if rc1>0 then output; if rc1>0 then output;
@@ -13705,14 +14015,13 @@ run;
%if %length(&tree)>0 %then %do; %if %length(&tree)>0 %then %do;
/* get tree info */ /* get tree info */
%mm_gettree(tree=&tree,inds=&outds, outds=&outds, mDebug=&mDebug) %mm_gettree(tree=&tree,inds=&outds, outds=&outds, mDebug=&mDebug)
%if %mf_nobs(&outds)=0 %then %do; %if %mf_nobs(&outds)=0 %then %do;
%put NOTE: Tree &tree did not exist!!; %put NOTE: Tree &tree did not exist!!;
%return; %return;
%end; %end;
%end; %end;
data &outds ; data &outds ;
set &outds(rename=(treeuri=treeuri_compare)); set &outds(rename=(treeuri=treeuri_compare));
length treeuri query stpuri $256; length treeuri query stpuri $256;
@@ -13857,7 +14166,7 @@ run;
data &outds; data &outds;
length uri serveruri conn_uri domainuri libname ServerContext AuthDomain length uri serveruri conn_uri domainuri libname ServerContext AuthDomain
path_schema usingpkguri type tableuri $256 id $17 path_schema usingpkguri type tableuri $256 id $17
libdesc $200 libref engine $8 IsDBMSLibname $1 libdesc $200 libref engine $8 IsDBMSLibname IsPreassigned $1
tablename $50 /* metadata table names can be longer than $32 */ tablename $50 /* metadata table names can be longer than $32 */
; ;
keep libname libdesc libref engine ServerContext path_schema AuthDomain keep libname libdesc libref engine ServerContext path_schema AuthDomain

54
base/mf_dedup.sas Normal file
View File

@@ -0,0 +1,54 @@
/**
@file
@brief de-duplicates a macro string
@details Removes all duplicates from a string of words. A delimeter can be
chosen. Is inspired heavily by this excellent [macro](
https://github.com/scottbass/SAS/blob/master/Macro/dedup_mstring.sas) from
[Scott Base](https://www.linkedin.com/in/scottbass). Case sensitive.
Usage:
%let str=One two one two and through and through;
%put %mf_dedup(&str);
%put %mf_dedup(&str,outdlm=%str(,));
Which returns:
> One two one and through
> One,two,one,and,through
@param [in] str String to be deduplicated
@param [in] indlm= ( ) Delimeter of the input string
@param [out] outdlm= ( ) Delimiter of the output string
<h4> Related Macros </h4>
@li mf_trimstr.sas
@li mf_wordsinstr1butnotstr2.sas
@version 9.2
@author Allan Bowe
**/
%macro mf_dedup(str
,indlm=%str( )
,outdlm=%str( )
)/*/STORE SOURCE*/;
%local num word i pos out;
%* loop over each token, searching the target for that token ;
%let num=%sysfunc(countc(%superq(str),%str(&indlm)));
%do i=1 %to %eval(&num+1);
%let word=%scan(%superq(str),&i,%str(&indlm));
%let pos=%sysfunc(indexw(&out,&word,%str(&outdlm)));
%if (&pos eq 0) %then %do;
%if (&i gt 1) %then %let out=&out%str(&outdlm);
%let out=&out&word;
%end;
%end;
%unquote(&out)
%mend mf_dedup;

61
base/mf_getfmtlist.sas Normal file
View File

@@ -0,0 +1,61 @@
/**
@file
@brief Returns a distinct list of formats from a table
@details Reads the dataset header and returns a distinct list of formats
applied.
%put NOTE- %mf_getfmtlist(sashelp.prdsale);
%put NOTE- %mf_getfmtlist(sashelp.shoes);
%put NOTE- %mf_getfmtlist(sashelp.demographics);
returns:
> DOLLAR $CHAR W MONNAME
> $CHAR BEST DOLLAR
> BEST Z $CHAR COMMA PERCENTN
@param [in] libds Two part library.dataset reference.
<h4> SAS Macros </h4>
@li mf_getfmtname.sas
@version 9.2
@author Allan Bowe
**/
%macro mf_getfmtlist(libds
)/*/STORE SOURCE*/;
/* declare local vars */
%local out dsid nvars x rc fmt;
/* open dataset in macro */
%let dsid=%sysfunc(open(&libds));
/* continue if dataset exists */
%if &dsid %then %do;
/* loop each variable in the dataset */
%let nvars=%sysfunc(attrn(&dsid,NVARS));
%do x=1 %to &nvars;
/* grab format and check it exists */
%let fmt=%sysfunc(varfmt(&dsid,&x));
%if %quote(&fmt) ne %quote() %then %let fmt=%mf_getfmtname(&fmt);
%else %do;
/* assign default format depending on variable type */
%if %sysfunc(vartype(&dsid, &x))=C %then %let fmt=$CHAR;
%else %let fmt=BEST;
%end;
/* concatenate unique list of formats */
%if %sysfunc(indexw(&out,&fmt,%str( )))=0 %then %let out=&out &fmt;
%end;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put &sysmacroname: Unable to open &libds (rc=&dsid);
%put &sysmacroname: SYSMSG= %sysfunc(sysmsg());
%let rc=%sysfunc(close(&dsid));
%end;
/* send them out without spaces or quote markers */
%do;%unquote(&out)%end;
%mend mf_getfmtlist;

44
base/mf_getfmtname.sas Normal file
View File

@@ -0,0 +1,44 @@
/**
@file
@brief Extracts a format name from a fully defined format
@details Converts formats in like $thi3. and th13.2 $THI and TH.
Usage:
%put %mf_getfmtname(8.);
%put %mf_getfmtname($4.);
%put %mf_getfmtname(comma14.10);
Returns:
> W
> $CHAR
> COMMA
Note that system defaults are inferred from the values provided.
@param [in] fmt The fully defined format. If left blank, nothing is returned.
@returns The name (without width or decimal) of the format.
@version 9.2
@author Allan Bowe
**/
%macro mf_getfmtname(fmt
)/*/STORE SOURCE*/ /minoperator mindelimiter=' ';
%local out dsid nvars x rc fmt;
/* extract actual format name from the format definition */
%let fmt=%scan(&fmt,1,.);
%do %while(%substr(&fmt,%length(&fmt),1) in 1 2 3 4 5 6 7 8 9 0);
%if %length(&fmt)=1 %then %let fmt=W;
%else %let fmt=%substr(&fmt,1,%length(&fmt)-1);
%end;
%if &fmt=$ %then %let fmt=$CHAR;
/* send them out without spaces or quote markers */
%do;%unquote(%upcase(&fmt))%end;
%mend mf_getfmtname;

View File

@@ -16,8 +16,8 @@
> "these","words","are","double","quoted" > "these","words","are","double","quoted"
@param [in] in_str The unquoted, spaced delimited string to transform @param [in] in_str The unquoted, spaced delimited string to transform
@param [in] dlm= The delimeter to be applied to the output (default comma) @param [in] dlm= (,) The delimeter to be applied to the output (default comma)
@param [in] indlm= (,) The delimeter used for the input (default is space) @param [in] indlm= ( ) The delimeter used for the input (default is space)
@param [in] quote= (S) The quote mark to apply (S=Single, D=Double, N=None). @param [in] quote= (S) The quote mark to apply (S=Single, D=Double, N=None).
If any other value than uppercase S or D is supplied, then that value will If any other value than uppercase S or D is supplied, then that value will
be used as the quoting character. be used as the quoting character.
@@ -28,7 +28,10 @@
**/ **/
%macro mf_getquotedstr(IN_STR,DLM=%str(,),QUOTE=S,indlm=%str( ) %macro mf_getquotedstr(IN_STR
,DLM=%str(,)
,QUOTE=S
,indlm=%str( )
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* credit Rowland Hale - byte34 is double quote, 39 is single quote */ /* credit Rowland Hale - byte34 is double quote, 39 is single quote */
%if &quote=S %then %let quote=%qsysfunc(byte(39)); %if &quote=S %then %let quote=%qsysfunc(byte(39));

View File

@@ -29,44 +29,44 @@
Usage: Usage:
%mp_mdtablewrite(libds=sashelp.class,showlog=YES) %mp_ds2md(sashelp.class)
@param [in] libds the library / dataset to create or read from.
@param [out] outref= (mdtable) Fileref to contain the markdown
@param [out] showlog= (YES) Set to NO to avoid printing markdown to the log
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getvarlist.sas @li mf_getvarlist.sas
@li mf_getvarformat.sas @li mf_getvarformat.sas
@param [in] libds= the library / dataset to create or read from.
@param [out] fref= Fileref to contain the markdown. Default=mdtable.
@param [out] showlog= set to YES to show the markdown in the log. Default=NO.
@version 9.3 @version 9.3
@author Allan Bowe @author Allan Bowe
**/ **/
%macro mp_mdtablewrite( %macro mp_ds2md(
libds=, libds,
fref=mdtable, outref=mdtable,
showlog=NO showlog=YES
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* check fileref is assigned */ /* check fileref is assigned */
%if %sysfunc(fileref(&fref)) > 0 %then %do; %if %sysfunc(fileref(&outref)) > 0 %then %do;
filename &fref temp; filename &outref temp;
%end; %end;
%local vars; %local vars;
%let vars=%mf_getvarlist(&libds); %let vars=%upcase(%mf_getvarlist(&libds));
/* create the header row */ /* create the header row */
data _null_; data _null_;
file &fref; file &outref;
length line $32767; length line $32767;
call missing(line);
put '|' put '|'
%local i var fmt; %local i var fmt;
%do i=1 %to %sysfunc(countw(&vars)); %do i=1 %to %sysfunc(countw(&vars));
%let var=%scan(&vars,&i); %let var=%scan(&vars,&i);
%let fmt=%mf_getvarformat(&libds,&var,force=1); %let fmt=%lowcase(%mf_getvarformat(&libds,&var,force=1));
"&var:&fmt|" "&var:&fmt|"
%end; %end;
; ;
@@ -79,20 +79,20 @@ run;
/* write out the data */ /* write out the data */
data _null_; data _null_;
file &fref mod dlm='|' lrecl=32767; file &outref mod dlm='|' lrecl=32767;
set &libds ; set &libds ;
length line $32767; length line $32767;
line=cats('|',%mf_getvarlist(&libds,dlm=%str(,'|',)),'|'); line='|`'!!cats(%mf_getvarlist(&libds,dlm=%str(%)!!' `|`'!!cats%()))!!' `|';
put line; put line;
run; run;
%if %upcase(&showlog)=YES %then %do; %if %upcase(&showlog)=YES %then %do;
options ps=max; options ps=max;
data _null_; data _null_;
infile &fref; infile &outref;
input; input;
putlog _infile_; putlog _infile_;
run; run;
%end; %end;
%mend mp_mdtablewrite; %mend mp_ds2md;

141
base/mp_getformats.sas Normal file
View File

@@ -0,0 +1,141 @@
/**
@file
@brief Export format definitions
@details Formats are exported from the first (if any) catalog entry in the
FMTSEARCH path.
Formats are taken from the library / dataset reference and / or a static
format list.
Example usage:
%mp_getformats(lib=sashelp,ds=prdsale,outsummary=work.dictable)
@param [in] lib= (0) The libref for which to return formats.
@todo Enable exporting of formats for an entire library
@param [in] ds= (0) The dataset from which to obtain format definitions
@param [in] fmtlist= (0) A list of additional format names
@param [out] outsummary= (work.mp_getformats_summary) Output dataset
containing summary definitions - structure taken from dictionary.formats as
follows:
|libname:$8.|memname:$32.|path:$1024.|objname:$32.|fmtname:$32.|fmttype:$1.|source:$1.|minw:best.|mind:best.|maxw:best.|maxd:best.|defw:best.|defd:best.|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| | | | |$|F|B|1|0|32767|0|1|0|
| | | | |$|I|B|1|0|32767|0|1|0|
|` `|` `|/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWIANYDT|$ANYDTIF|I|U|1|0|60|0|19|0|
| | |/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWFASCII|$ASCII|F|U|1|0|32767|0|1|0|
| | |/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWIASCII|$ASCII|I|U|1|0|32767|0|1|0|
| | |/opt/sas/sas9/SASHome/SASFoundation/9.4/sasexe|UWFBASE6|$BASE64X|F|U|1|0|32767|0|1|0|
@param [out] outdetail= (0) Provide an output dataset in which to export all
the custom format definitions (from proc format CNTLOUT). Definitions:
https://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#a002473477.htm
Sample data:
|FMTNAME:$32.|START:$16.|END:$16.|LABEL:$256.|MIN:best.|MAX:best.|DEFAULT:best.|LENGTH:best.|FUZZ:best.|PREFIX:$2.|MULT:best.|FILL:$1.|NOEDIT:best.|TYPE:$1.|SEXCL:$1.|EEXCL:$1.|HLO:$13.|DECSEP:$1.|DIG3SEP:$1.|DATATYPE:$8.|LANGUAGE:$8.|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|`WHICHPATH `|`0 `|`0 `|`path1 `|`1 `|`40 `|`28 `|`28 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`N `|` `|` `|` `|` `|` `|
|`WHICHPATH `|`**OTHER** `|`**OTHER** `|`big fat problem if not path1 `|`1 `|`40 `|`28 `|`28 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`N `|`O `|` `|` `|` `|` `|
<h4> SAS Macros </h4>
@li mf_dedup.sas
@li mf_getfmtlist.sas
@li mf_getfmtname.sas
@li mf_getquotedstr.sas
@li mf_getuniquename.sas
<h4> Related Macros </h4>
@li mp_getformats.test.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_getformats(lib=0
,ds=0
,fmtlist=0
,outsummary=work.mp_getformats_summary
,outdetail=0
);
%local i fmt allfmts tempds fmtcnt;
%if "&fmtlist" ne "0" %then %do i=1 %to %sysfunc(countw(&fmtlist,,%str( )));
/* ensure format list contains format _name_ only */
%let fmt=%scan(&fmtlist,&i,%str( ));
%let fmt=%mf_getfmtname(&fmt);
%let allfmts=&allfmts &fmt;
%end;
%if &ds=0 and &lib ne 0 %then %do;
/* grab formats from library */
/* to do */
%end;
%else %if &ds ne 0 and &lib ne 0 %then %do;
/* grab formats from dataset */
%let allfmts=%mf_getfmtlist(&lib..&ds) &allfmts;
%end;
/* ensure list is unique */
%let allfmts=%mf_dedup(%upcase(&allfmts));
/* create summary table */
%if %index(&outsummary,.)=0 %then %let outsummary=WORK.&outsummary;
proc sql;
create table &outsummary as
select * from dictionary.formats
where fmtname in (%mf_getquotedstr(&allfmts,quote=D))
and fmttype='F';
%if "&outdetail" ne "0" %then %do;
/* ensure base table always exists */
proc sql;
create table &outdetail(
FMTNAME char(32) label='Format name'
,START char(16) label='Starting value for format'
,END char(16) label='Ending value for format'
,LABEL char(256) label='Format value label'
,MIN num length=3 label='Minimum length'
,MAX num length=3 label='Maximum length'
,DEFAULT num length=3 label='Default length'
,LENGTH num length=3 label='Format length'
,FUZZ num label='Fuzz value'
,PREFIX char(2) label='Prefix characters'
,MULT num label='Multiplier'
,FILL char(1) label='Fill character'
,NOEDIT num length=3 label='Is picture string noedit?'
,TYPE char(1) label='Type of format'
,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information'
,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings'
);
/* grab the location of each format */
%let fmtcnt=0;
data _null_;
set &outsummary;
if not missing(libname);
x+1;
call symputx(cats('fmtloc',x),cats(libname,'.',memname),'l');
call symputx(cats('fmtname',x),fmtname,'l');
call symputx('fmtcnt',x,'l');
run;
/* export each format and append to the output table */
%let tempds=%mf_getuniquename(prefix=mp_getformats);
%do i=1 %to &fmtcnt;
proc format library=&&fmtloc&i CNTLOUT=&tempds;
select &&fmtname&i;
run;
proc append base=&outdetail data=&tempds;
run;
%end;
%end;
%mend mp_getformats;

View File

@@ -30,19 +30,21 @@
**/ **/
%macro mp_init(prefix= %macro mp_init(prefix=SASJS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%global %global
&prefix._INIT_NUM /* initialisation time as numeric */ &prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */ &prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
; ;
%if %eval(&&&prefix._INIT_NUM>0) %then %return; /* only run once */ %if %eval(&&&prefix._INIT_NUM>0) %then %return; /* only run once */
data _null_; data _null_;
dttm=datetime(); dttm=datetime();
call symputx("&prefix._init_num",dttm); call symputx("&prefix._init_num",dttm,'g');
call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6)); call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6),'g');
call symputx("&prefix.work",pathname('WORK'),'g');
run; run;
options options
@@ -56,6 +58,7 @@
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 */
ls=max /* reduce log even more and avoid word truncation */
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 */ varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */

View File

@@ -62,13 +62,13 @@
%put output location=&jref; %put output location=&jref;
%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' termstr=lf;
put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"'; 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;
options validvarname=upcase; options validvarname=upcase;
data _null_;file &jref mod encoding='utf-8'; data _null_;file &jref mod encoding='utf-8' termstr=lf;
put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":";
%if &engine=PROCJSON %then %do; %if &engine=PROCJSON %then %do;
@@ -147,7 +147,7 @@
run; run;
%let ds=&fmtds; %let ds=&fmtds;
%end; /* &fmt=Y */ %end; /* &fmt=Y */
data _null_;file &jref mod encoding='utf-8'; data _null_;file &jref mod encoding='utf-8' termstr=lf;
put "["; call symputx('cols',0,'l'); put "["; call symputx('cols',0,'l');
proc sort proc sort
data=sashelp.vcolumn(where=(libname='WORK' & memname="%upcase(&ds)")) data=sashelp.vcolumn(where=(libname='WORK' & memname="%upcase(&ds)"))
@@ -192,7 +192,7 @@
/* write to temp loc to avoid _webout truncation /* write to temp loc to avoid _webout truncation
- https://support.sas.com/kb/49/325.html */ - https://support.sas.com/kb/49/325.html */
filename _sjs temp lrecl=131068 encoding='utf-8'; filename _sjs temp lrecl=131068 encoding='utf-8';
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod; data _null_; file _sjs lrecl=131068 encoding='utf-8' mod termstr=lf;
set &tempds; set &tempds;
if _n_>1 then put "," @; put if _n_>1 then put "," @; put
%if &action=ARR %then "[" ; %else "{" ; %if &action=ARR %then "[" ; %else "{" ;
@@ -219,14 +219,14 @@
rc = fclose(fileid); rc = fclose(fileid);
run; run;
filename _sjs clear; filename _sjs clear;
data _null_; file &jref mod encoding='utf-8'; data _null_; file &jref mod encoding='utf-8' termstr=lf;
put "]"; put "]";
run; run;
%end; %end;
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
data _null_;file &jref encoding='utf-8' mod; data _null_;file &jref encoding='utf-8' mod termstr=lf;
put "}"; put "}";
run; run;
%end; %end;

View File

@@ -64,13 +64,13 @@ data &ds1;
&n1=ranuni(1)*5000000; &n1=ranuni(1)*5000000;
drop &c1 &n1; drop &c1 &n1;
%let charvars=%mf_getvarlist(&libds,typefilter=C); %let charvars=%mf_getvarlist(&libds,typefilter=C);
%do i=1 %to %sysfunc(countw(&charvars)); %if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars));
%let col=%scan(&charvars,&i); %let col=%scan(&charvars,&i);
&col=subpad(&c1,1,%mf_getvarlen(&libds,&col)); &col=subpad(&c1,1,%mf_getvarlen(&libds,&col));
%end; %end;
%let numvars=%mf_getvarlist(&libds,typefilter=N); %let numvars=%mf_getvarlist(&libds,typefilter=N);
%do i=1 %to %sysfunc(countw(&numvars)); %if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars));
%let col=%scan(&numvars,&i); %let col=%scan(&numvars,&i);
&col=&n1; &col=&n1;
%end; %end;

View File

@@ -5,13 +5,15 @@
sort it before performing operations such as merges / joins etc. sort it before performing operations such as merges / joins etc.
That said, there are a few edge cases where it can be desirable: That said, there are a few edge cases where it can be desirable:
@li To improve performance for particular scenarios
@li To allow adjacent records to be viewed directly in the dataset @li To allow adjacent records to be viewed directly in the dataset
@li To reduce dataset size (eg when there are deleted records) @li To apply compression, or to remove deleted records
@li To improve performance for specific queries
This macro will only work for BASE (V9) engine libraries. It works by This macro will only work for BASE (V9) engine libraries. It works by
creating a copy of the dataset (without data, WITH constraints) in the same creating a copy of the dataset (without data, WITH constraints) in the same
library, appending a sorted view into it, and finally - renaming it. library, appending a sorted view into it, and finally - renaming it. By
default, COMPRESS=CHAR and REUSE=YES will be applied, this behaviour can
be adjusted using the `dsoptions=` parameter.
Example usage: Example usage:
@@ -29,6 +31,7 @@
@li mf_getengine.sas @li mf_getengine.sas
@li mf_getquotedstr.sas @li mf_getquotedstr.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvarlist.sas
@li mf_nobs.sas @li mf_nobs.sas
@li mp_abort.sas @li mp_abort.sas
@li mp_getpk.sas @li mp_getpk.sas
@@ -74,9 +77,13 @@
%return; %return;
%end; %end;
/* fallback sortkey is all fields */
%let sortkey=%mf_getvarlist(&libds);
/* overlay actual sort key if it exists */
data _null_; data _null_;
set work.&tempds1; set work.&tempds1;
call symputx('sortkey',pk_fields); call symputx('sortkey',coalescec(pk_fields,symget('sortkey')));
run; run;

View File

@@ -65,10 +65,10 @@
'Did value change? (1/0/-1). Always -1 for appends and deletes.', 'Did value change? (1/0/-1). Always -1 for appends and deletes.',
tgtvar_type char(1) label='Either (C)haracter or (N)umeric', tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
tgtvar_nm char(32) label='Target variable name (32 chars)', tgtvar_nm char(32) label='Target variable name (32 chars)',
oldval_num num label='Old (numeric) value', oldval_num num format=best32. label='Old (numeric) value',
newval_num num label='New (numeric) value', newval_num num format=best32. label='New (numeric) value',
oldval_char char(32767) label='Old (character) value', oldval_char char(32765) label='Old (character) value',
newval_char char(32767) label='New (character) value', newval_char char(32765) label='New (character) value',
constraint pk_mpe_audit constraint pk_mpe_audit
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm) primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
); );
@@ -212,18 +212,18 @@ create table &outds as
,b.tgtvar_type length=1 ,b.tgtvar_type length=1
,case when b.move_type='D' then b.newval_num ,case when b.move_type='D' then b.newval_num
else a.newval_num else a.newval_num
end as oldval_num end as oldval_num format=best32.
,case when b.move_type='D' then . ,case when b.move_type='D' then .
else b.newval_num else b.newval_num
end as newval_num end as newval_num format=best32.
,case when b.move_type='D' then b.newval_char ,case when b.move_type='D' then b.newval_char
else a.newval_char else a.newval_char
end as oldval_char length=32767 end as oldval_char length=32765
,case when b.move_type='D' then '' ,case when b.move_type='D' then ''
else b.newval_char else b.newval_char
end as newval_char length=32767 end as newval_char length=32765
from &ds4(where=(move_type='O')) as a from &ds4(where=(move_type='O')) as a
full join &ds4(where=(move_type ne 'O')) as b right join &ds4(where=(move_type ne 'O')) as b
on a.tgtvar_nm=b.tgtvar_nm on a.tgtvar_nm=b.tgtvar_nm
and a.key_hash=b.key_hash and a.key_hash=b.key_hash
order by move_type, key_hash,is_pk desc, tgtvar_nm; order by move_type, key_hash,is_pk desc, tgtvar_nm;

View File

@@ -295,7 +295,7 @@ run;
prop='Connection.DBMS.Property.SERVER.Name.xmlKey.txt'; prop='Connection.DBMS.Property.SERVER.Name.xmlKey.txt';
rc=metadata_getprop(uri,prop,server,""); rc=metadata_getprop(uri,prop,server,"");
end; end;
if server^='' then server='server='!!server; if server^='' then server='server='!!quote(cats(server));
call symputx('server',server,'l'); call symputx('server',server,'l');
/* get SCHEMA value */ /* get SCHEMA value */
@@ -441,11 +441,11 @@ run;
run; run;
%put NOTE: Executing the following:/; %put NOTE-; %put NOTE: Executing the following:/; %put NOTE-;
%put NOTE- libname &libref TERADATA server=&path schema=&schema ; %put NOTE- libname &libref TERADATA server="&path" schema=&schema ;
%put NOTe- authdomain=&authdomain; %put NOTe- authdomain=&authdomain;
%put NOTE-; %put NOTE-;
libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain; libname &libref TERADATA server="&path" schema=&schema authdomain=&authdomain;
%end; %end;
%else %if &engine= %then %do; %else %if &engine= %then %do;
%put NOTE: Libref &libref is not registered in metadata; %put NOTE: Libref &libref is not registered in metadata;

View File

@@ -45,7 +45,7 @@ run;
data &outattrs; data &outattrs;
keep type name value; keep type name value;
length type $4 name $256 value $32767; length type $4 name $256 value $32767;
rc1=1;n1=1;type='Prop'; rc1=1;n1=1;type='Prop';name='';value='';
do while(rc1>0); do while(rc1>0);
rc1=metadata_getnprp("&uri",n1,name,value); rc1=metadata_getnprp("&uri",n1,name,value);
if rc1>0 then output; if rc1>0 then output;

View File

@@ -63,14 +63,13 @@ run;
%if %length(&tree)>0 %then %do; %if %length(&tree)>0 %then %do;
/* get tree info */ /* get tree info */
%mm_gettree(tree=&tree,inds=&outds, outds=&outds, mDebug=&mDebug) %mm_gettree(tree=&tree,inds=&outds, outds=&outds, mDebug=&mDebug)
%if %mf_nobs(&outds)=0 %then %do; %if %mf_nobs(&outds)=0 %then %do;
%put NOTE: Tree &tree did not exist!!; %put NOTE: Tree &tree did not exist!!;
%return; %return;
%end; %end;
%end; %end;
data &outds ; data &outds ;
set &outds(rename=(treeuri=treeuri_compare)); set &outds(rename=(treeuri=treeuri_compare));
length treeuri query stpuri $256; length treeuri query stpuri $256;

View File

@@ -39,7 +39,7 @@
data &outds; data &outds;
length uri serveruri conn_uri domainuri libname ServerContext AuthDomain length uri serveruri conn_uri domainuri libname ServerContext AuthDomain
path_schema usingpkguri type tableuri $256 id $17 path_schema usingpkguri type tableuri $256 id $17
libdesc $200 libref engine $8 IsDBMSLibname $1 libdesc $200 libref engine $8 IsDBMSLibname IsPreassigned $1
tablename $50 /* metadata table names can be longer than $32 */ tablename $50 /* metadata table names can be longer than $32 */
; ;
keep libname libdesc libref engine ServerContext path_schema AuthDomain keep libname libdesc libref engine ServerContext path_schema AuthDomain

View File

@@ -32,7 +32,9 @@
"name": "viya", "name": "viya",
"serverUrl": "https://sas.analytium.co.uk", "serverUrl": "https://sas.analytium.co.uk",
"serverType": "SASVIYA", "serverType": "SASVIYA",
"allowInsecureRequests": false, "httpsAgentOptions": {
"allowInsecureRequests": false
},
"appLoc": "/Public/temp/macrocore", "appLoc": "/Public/temp/macrocore",
"macroFolders": [ "macroFolders": [
"tests/viyaonly" "tests/viyaonly"

View File

@@ -79,7 +79,7 @@
OPTIONS NOBOMFILE; OPTIONS NOBOMFILE;
/* setup json */ /* setup json */
data _null_;file &fref encoding='utf-8'; data _null_;file &fref encoding='utf-8' termstr=lf;
%if %str(&_debug) ge 131 %then %do; %if %str(&_debug) ge 131 %then %do;
put '>>weboutBEGIN<<'; put '>>weboutBEGIN<<';
%end; %end;
@@ -108,14 +108,14 @@
i+1; i+1;
call symputx('wt'!!left(i),name,'l'); call symputx('wt'!!left(i),name,'l');
call symputx('wtcnt',i,'l'); call symputx('wtcnt',i,'l');
data _null_; file &fref mod encoding='utf-8'; data _null_; file &fref mod encoding='utf-8' termstr=lf;
put ",""WORK"":{"; put ",""WORK"":{";
%do i=1 %to &wtcnt; %do i=1 %to &wtcnt;
%let wt=&&wt&i; %let wt=&&wt&i;
proc contents noprint data=&wt proc contents noprint data=&wt
out=_data_ (keep=name type length format:); out=_data_ (keep=name type length format:);
run;%let tempds=%scan(&syslast,2,.); run;%let tempds=%scan(&syslast,2,.);
data _null_; file &fref mod encoding='utf-8'; data _null_; file &fref mod encoding='utf-8' termstr=lf;
dsid=open("WORK.&wt",'is'); dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS'); nlobs=attrn(dsid,'NLOBS');
nvars=attrn(dsid,'NVARS'); nvars=attrn(dsid,'NVARS');
@@ -126,15 +126,15 @@
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
data _null_; file &fref mod encoding='utf-8'; data _null_; file &fref mod encoding='utf-8' termstr=lf;
put "}"; put "}";
%end; %end;
data _null_; file &fref mod encoding='utf-8'; data _null_; file &fref mod encoding='utf-8' termstr=lf termstr=lf;
put "}"; put "}";
run; run;
%end; %end;
/* close off json */ /* close off json */
data _null_;file &fref mod encoding='utf-8'; data _null_;file &fref mod encoding='utf-8' termstr=lf;
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ",""SYSUSERID"" : ""&sysuserid"" "; put ",""SYSUSERID"" : ""&sysuserid"" ";
put ",""MF_GETUSER"" : ""%mf_getuser()"" "; put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
@@ -147,7 +147,9 @@
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
put ",""SYSPROCESSNAME"" : ""&SYSPROCESSNAME"" "; length SYSPROCESSNAME $512;
SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
put ",""SYSJOBID"" : ""&sysjobid"" "; put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""SYSSCPL"" : ""&sysscpl"" "; put ",""SYSSCPL"" : ""&sysscpl"" ";
put ",""SYSSITE"" : ""&syssite"" "; put ",""SYSSITE"" : ""&syssite"" ";
@@ -156,7 +158,8 @@
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
autoexec=quote(trim(getoption('autoexec'))); length autoexec $512;
autoexec=quote(urlencode(trim(getoption('autoexec'))));
put ',"AUTOEXEC" : ' autoexec; put ',"AUTOEXEC" : ' autoexec;
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
memsize=quote(cats(memsize)); memsize=quote(cats(memsize));

View File

@@ -0,0 +1,23 @@
/**
@file
@brief Testing mf_dedup macro
<h4> SAS Macros </h4>
@li mf_dedup.sas
@li mp_assert.sas
**/
%let str=One two one two and through and through;
%mp_assert(
iftrue=("%mf_dedup(&str)"="One two one and through"),
desc=Basic test,
outds=work.test_results
)
%mp_assert(
iftrue=("%mf_dedup(&str,outdlm=%str(,))"="One,two,one,and,through"),
desc=Outdlm test,
outds=work.test_results
)

View File

@@ -0,0 +1,33 @@
/**
@file
@brief Testing mf_getfmtlist macro
<h4> SAS Macros </h4>
@li mf_getfmtlist.sas
@li mp_assert.sas
**/
%mp_assert(
iftrue=(
"%mf_getfmtlist(sashelp.prdsale)"="DOLLAR $CHAR W MONNAME"
),
desc=Checking basic numeric,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getfmtlist(sashelp.shoes)"="$CHAR BEST DOLLAR"
),
desc=Checking basic char,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getfmtlist(sashelp.demographics)"="BEST Z $CHAR COMMA PERCENTN"
),
desc=Checking longer numeric,
outds=work.test_results
)

View File

@@ -0,0 +1,33 @@
/**
@file
@brief Testing mf_getfmtname macro
<h4> SAS Macros </h4>
@li mf_getfmtname.sas
@li mp_assert.sas
**/
%mp_assert(
iftrue=(
"%mf_getfmtname(8.)"="W"
),
desc=Checking basic numeric,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getfmtname($4.)"="$CHAR"
),
desc=Checking basic char,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getfmtname(comma14.10)"="COMMA"
),
desc=Checking longer numeric,
outds=work.test_results
)

View File

@@ -0,0 +1,34 @@
/**
@file
@brief Testing mp_ds2md.sas macro
<h4> SAS Macros </h4>
@li mp_ds2md.sas
@li mp_assert.sas
**/
%mp_ds2md(sashelp.class,outref=md)
data _null_;
infile md;
input;
call symputx(cats('test',_n_),_infile_);
if _n_=4 then stop;
run;
%mp_assert(
iftrue=("&test1"="|NAME:$8.|SEX:$1.|AGE:best.|HEIGHT:best.|WEIGHT:best.|"),
desc=Checking header row,
outds=work.test_results
)
%mp_assert(
iftrue=("&test2"="|---|---|---|---|---|"),
desc=Checking divider row,
outds=work.test_results
)
%mp_assert(
iftrue=("&test3"="|`Alfred `|`M `|`14 `|`69 `|`112.5 `|"),
desc=Checking data row,
outds=work.test_results
)

View File

@@ -0,0 +1,76 @@
/**
@file
@brief Testing mp_getformats.sas macro
<h4> SAS Macros </h4>
@li mf_mkdir.sas
@li mp_getformats.sas
@li mp_assert.sas
**/
/**
* Test - setup
*/
%mf_mkdir(&sasjswork/path1)
%mf_mkdir(&sasjswork/path2)
libname path1 "&sasjswork/path1";
libname path2 "&sasjswork/path2";
PROC FORMAT library=path1;
value whichpath 0 = 'path1' other='big fat problem if not path1';
PROC FORMAT library=path2;
value whichpath 0 = 'path2' other='big fat problem if not path2';
RUN;
/** run with path1 path2 FMTSEARCH */
options insert=(fmtsearch=(path1 path2));
data _null_;
test=0;
call symputx('test1',put(test,whichpath.));
run;
%mp_assert(
iftrue=("&test1"="path1"),
desc=Check correct format is applied,
outds=work.test_results
)
%mp_getformats(fmtlist=WHICHPATH,outsummary=sum,outdetail=detail1)
%let tst1=0;
data _null_;
set detail1;
if fmtname='WHICHPATH' and start='**OTHER**' then call symputx('tst1',label);
putlog (_all_)(=);
run;
%mp_assert(
iftrue=("&tst1"="big fat problem if not path1"),
desc=Check correct detail results are applied,
outds=work.test_results
)
/** run with path2 path1 FMTSEARCH */
options insert=(fmtsearch=(path2 path1));
data _null_;
test=0;
call symputx('test2',put(test,whichpath.));
run;
%mp_assert(
iftrue=("&test2"="path2"),
desc=Check correct format is applied,
outds=work.test_results
)
%mp_getformats(fmtlist=WHICHPATH,outsummary=sum,outdetail=detail2)
%let tst2=0;
data _null_;
set detail2;
if fmtname='WHICHPATH' and start='**OTHER**' then call symputx('tst2',label);
putlog (_all_)(=);
run;
%mp_assert(
iftrue=("&tst2"="big fat problem if not path2"),
desc=Check correct detail results are applied,
outds=work.test_results
)

View File

@@ -15,7 +15,7 @@
%let initial_value=&sasjs_init_num; %let initial_value=&sasjs_init_num;
%mp_init(); %mp_init()
%mp_assert( %mp_assert(
iftrue=("&initial_value"="&sasjs_init_num"), iftrue=("&initial_value"="&sasjs_init_num"),

View File

@@ -39,4 +39,31 @@ run;
), ),
desc=Check if sort was appplied, desc=Check if sort was appplied,
outds=work.test_results outds=work.test_results
)
/** Test 2 - table without PK */
proc sql;
create table work.example2 as
select * from sashelp.classfit;
%mp_sortinplace(work.example2)
%mp_assert(
iftrue=(
%str(&syscc)=%str(0)
),
desc=Ensure no errors when no key exists,
outds=work.test_results
)
%let test2=0;
data _null_;
set work.example2;
call symputx('test2',name);
stop;
run;
%mp_assert(
iftrue=(
%str(&test2)=%str(Alfred)
),
desc=Check if sort was appplied when no index exists,
outds=work.test_results
) )

View File

@@ -61,4 +61,42 @@ run;
checkvals=work.check.val, checkvals=work.check.val,
desc=All values have a match, desc=All values have a match,
test=ALLVALS test=ALLVALS
)
/* Test for when there are no actual changes */
data work.orig work.deleted work.changed work.appended;
set sashelp.class;
output work.orig;
run;
%mp_storediffs(sashelp.class,work.orig,NAME
,delds=work.deleted
,modds=work.changed
,appds=work.appended
,outds=work.final2
,mdebug=1
)
%mp_assertdsobs(work.final2,
desc=No changes produces 0 records,
test=EQUALS 0,
outds=work.test_results
)
/* Test for deletes only */
data work.orig work.deleted work.changed work.appended;
set sashelp.class;
output work.orig;
if _n_>5 then output work.deleted;
run;
%mp_storediffs(sashelp.class,work.orig,NAME
,delds=work.deleted
,modds=work.changed
,appds=work.appended
,outds=work.final3
,mdebug=1
)
%mp_assertdsobs(work.final3,
desc=Delete has 70 records,
test=EQUALS 70,
outds=work.test_results
) )