mirror of
https://github.com/sasjs/core.git
synced 2026-01-01 23:00:06 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27cf2a2532 | ||
|
|
d096cbddeb | ||
|
|
117503f214 | ||
|
|
5cc5fae750 | ||
|
|
d93032e1a9 | ||
|
|
507557b2cb | ||
|
|
ee5c3c185a | ||
|
|
e5592a2eb2 | ||
|
|
59200a6e73 | ||
|
|
f468f60ae1 | ||
|
|
9f60d827b6 |
241
all.sas
241
all.sas
@@ -1642,24 +1642,47 @@ Usage:
|
||||
recognise this and fetch the log of the parent session instead)
|
||||
@li STP environments must finish cleanly to avoid the log being sent to
|
||||
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
||||
and set SYSCC=0. For 9.4m3 we take a unique approach - we open a macro
|
||||
but don't close it! This provides a graceful abort, EXCEPT when called
|
||||
called within a %include within a macro (and that macro contains additional
|
||||
logic). See mp_abort.test.nofix.sas for the example case.
|
||||
If you know of another way to gracefully abort a 9.4m3 STP session, we'd
|
||||
love to hear about it!
|
||||
and set SYSCC=0. We take a unique "soft abort" approach - we open a macro
|
||||
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
||||
a macro. For that, we recommend you use mp_include.sas to perform the
|
||||
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
||||
OUTSIDE of the top-parent macro).
|
||||
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
@param errds= (work.mp_abort_errds) There is no clean way to end a process
|
||||
within a %include called within a macro. Furthermore, there is no way to
|
||||
test if a macro is called within a %include. To handle this particular
|
||||
scenario, the %include should be switched for the mp_include.sas macro.
|
||||
This provides an indicator that we are running a macro within a \%include
|
||||
(`_SYSINCLUDEFILEDEVICE`) and allows us to provide a dataset with the abort
|
||||
values (msg, mac).
|
||||
We can then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back to the calling program to run a regular \%mp_abort().
|
||||
The dataset will contain the following fields:
|
||||
@li iftrue (1=1)
|
||||
@li msg (the message)
|
||||
@li mac (the mac param)
|
||||
|
||||
@version 9.4M3
|
||||
@param mode= (REGULAR) If mode=INCLUDE then the &errds dataset is checked for
|
||||
an abort status.
|
||||
Valid values:
|
||||
@li REGULAR (default)
|
||||
@li INCLUDE
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_include.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
, errds=work.mp_abort_errds
|
||||
, mode=REGULAR
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%global sysprocessmode sysprocessname;
|
||||
@@ -1670,9 +1693,44 @@ Usage:
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
|
||||
%if %symexist(_SYSINCLUDEFILEDEVICE) %then %do;
|
||||
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
||||
data &errds;
|
||||
iftrue='1=1';
|
||||
length mac $100 msg $5000;
|
||||
mac=symget('mac');
|
||||
msg=symget('msg');
|
||||
run;
|
||||
data _null_;
|
||||
abort cancel FILE;
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson) or "&SYSPROCESSNAME "="Compute Server " %then %do;
|
||||
%if %symexist(_metaperson)
|
||||
or "&SYSPROCESSNAME "="Compute Server "
|
||||
or &mode=INCLUDE
|
||||
%then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
%if &mode=INCLUDE %then %do;
|
||||
%if %sysfunc(exist(&errds))=1 %then %do;
|
||||
data _null_;
|
||||
set &errds;
|
||||
call symputx('iftrue',iftrue,'l');
|
||||
call symputx('mac',mac,'l');
|
||||
call symputx('msg',msg,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if (&iftrue)=0 %then %return;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname: No include errors found;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* extract log errs / warns, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
@@ -1759,7 +1817,9 @@ Usage:
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSSITE"" : ""&syssite"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
@@ -1776,11 +1836,22 @@ Usage:
|
||||
rc=stpsrvset('program error', 0);
|
||||
call symputx("syscc",0,"g");
|
||||
run;
|
||||
%if "%substr(&sysvlong.xxxxxxxxx,1,9)" ne "9.04.01M3" %then %do;
|
||||
%put NOTE: Ending SAS session due to:;
|
||||
%put NOTE- &msg;
|
||||
endsas;
|
||||
%end;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* Use mp_include() to handle this.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
||||
/* endsas kills the session making it harder to fetch results */
|
||||
@@ -1796,24 +1867,6 @@ Usage:
|
||||
abort cancel nolist;
|
||||
run;
|
||||
%end;
|
||||
%else %if "%substr(&sysvlong.xxxxxxxxx,1,9)" = "9.04.01M3" %then %do;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* See tests/mp_abort.test.1 for an example case.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%abort cancel;
|
||||
%end;
|
||||
@@ -3591,10 +3644,13 @@ run;
|
||||
options:
|
||||
@li SAS (default) - suitable for regular proc sql
|
||||
@li PGSQL - Used for Postgres databases
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfileref.sas
|
||||
@li mf_getvarcount.sas
|
||||
@li mf_getvarformat.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
|
||||
@@ -3603,6 +3659,7 @@ run;
|
||||
**/
|
||||
|
||||
%macro mp_ds2inserts(ds, outref=0,schema=0,outds=0,flavour=SAS,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not %sysfunc(exist(&ds)) %then %do;
|
||||
@@ -3680,10 +3737,11 @@ data _null_;
|
||||
length _____str $32767;
|
||||
format _numeric_ best.;
|
||||
format _character_ ;
|
||||
%local i comma var vtype;
|
||||
%local i comma var vtype vfmt;
|
||||
%do i=1 %to %sysfunc(countw(&varlist));
|
||||
%let var=%scan(&varlist,&i);
|
||||
%let vtype=%mf_getvartype(&ds,&var);
|
||||
%let vfmt=%upcase(%mf_getvarformat(&ds,&var,force=1));
|
||||
%if &i=1 %then %do;
|
||||
%if &flavour=SAS %then %do;
|
||||
put "insert into &schema.&outds set ";
|
||||
@@ -3713,7 +3771,13 @@ data _null_;
|
||||
%end;
|
||||
%else %if &flavour=PGSQL %then %do;
|
||||
if missing(&var) then put 'NULL';
|
||||
else put &var;
|
||||
%if &applydttm=YES and "%substr(&vfmt.xxxxxxxx,1,8)"="DATETIME"
|
||||
%then %do;
|
||||
else put "TIMESTAMP '" &var E8601DT25.6 "'";
|
||||
%end;
|
||||
%else %do;
|
||||
else put &var;
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
@@ -4116,7 +4180,7 @@ run;
|
||||
|
||||
data &outds;
|
||||
if &sqlrc or &syscc or &syserr then do;
|
||||
REASON_CD='VALIDATION_ERROR: '!!
|
||||
REASON_CD='VALIDATION_ERR'!!'OR: '!!
|
||||
coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
|
||||
output;
|
||||
end;
|
||||
@@ -5386,12 +5450,115 @@ create table &outds (rename=(
|
||||
run;
|
||||
%end;
|
||||
%mend mp_hashdataset;/**
|
||||
@file
|
||||
@brief Performs a wrapped \%include
|
||||
@details This macro wrapper is necessary if you need your included code to
|
||||
know that it is being \%included.
|
||||
|
||||
If you are using %include in a regular program, you could make use of the
|
||||
following macro variables:
|
||||
|
||||
@li SYSINCLUDEFILEDEVICE
|
||||
@li SYSINCLUDEFILEDIR
|
||||
@li SYSINCLUDEFILEFILEREF
|
||||
@li SYSINCLUDEFILENAME
|
||||
|
||||
However these variables are NOT available inside a macro, as documented here:
|
||||
https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1kg1o0606gsv9.htm
|
||||
|
||||
This macro can be used in place of the %include statement, and will insert
|
||||
the following (equivalent) global variables:
|
||||
|
||||
@li _SYSINCLUDEFILEDEVICE
|
||||
@li _SYSINCLUDEFILEDIR
|
||||
@li _SYSINCLUDEFILEFILEREF
|
||||
@li _SYSINCLUDEFILENAME
|
||||
|
||||
These can be used whenever testing _within a macro_. Outside of the macro,
|
||||
the regular automatic variables will still be available (thanks to a
|
||||
concatenated file list in the include statement).
|
||||
|
||||
Example usage:
|
||||
|
||||
filename example temp;
|
||||
data _null_;
|
||||
file example;
|
||||
put '%macro test();';
|
||||
put '%put &=_SYSINCLUDEFILEFILEREF;';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
put '%mend; %test()';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
run;
|
||||
%mp_include(example)
|
||||
|
||||
@param [in] fileref The fileref of the file to be included. Must be provided.
|
||||
@param [in] prefix= (_) The prefix to apply to the global variables.
|
||||
@param [in] opts= (SOURCE2) The options to apply to the %inc statement
|
||||
@param [in] errds= (work.mp_abort_errds) There is no clean way to end a
|
||||
process within a %include called within a macro. Furthermore, there is no
|
||||
way to test if a macro is called within a %include. To handle this
|
||||
particular scenario, the %mp_abort() macro will test for the existence of
|
||||
the `_SYSINCLUDEFILEDEVICE` variable and return the outputs (msg,mac) inside
|
||||
this dataset.
|
||||
It will then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back.
|
||||
NOTE - it is NOT possible to read this dataset as part of _this_ macro -
|
||||
when running abort cancel FILE, ALL macros are closed, so instead it is
|
||||
necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of any macro wrappers.
|
||||
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_include(fileref
|
||||
,prefix=_
|
||||
,opts=SOURCE2
|
||||
,errds=work.mp_abort_errds
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* prepare precode */
|
||||
%local tempref;
|
||||
%let tempref=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &tempref;
|
||||
set sashelp.vextfl(where=(fileref="%upcase(&fileref)"));
|
||||
put '%let _SYSINCLUDEFILEDEVICE=' xengine ';';
|
||||
name=scan(xpath,-1,'/\');
|
||||
put '%let _SYSINCLUDEFILENAME=' name ';';
|
||||
path=subpad(xpath,1,length(xpath)-length(name)-1);
|
||||
put '%let _SYSINCLUDEFILEDIR=' path ';';
|
||||
put '%let _SYSINCLUDEFILEFILEREF=' "&fileref;";
|
||||
run;
|
||||
|
||||
/* prepare the errds */
|
||||
data &errds;
|
||||
length msg mac $1000;
|
||||
iftrue='1=0';
|
||||
run;
|
||||
|
||||
/* include the include */
|
||||
%inc &tempref &fileref/&opts;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=%str(&_SYSINCLUDEFILEDIR/&_SYSINCLUDEFILENAME)
|
||||
,msg=%str(syscc=&syscc after executing &_SYSINCLUDEFILENAME)
|
||||
)
|
||||
|
||||
filename &tempref clear;
|
||||
|
||||
%mend mp_include;/**
|
||||
@file mp_jsonout.sas
|
||||
@brief Writes JSON in SASjs format to a fileref
|
||||
@details PROC JSON is faster but will produce errs like the ones below if
|
||||
special chars are encountered.
|
||||
|
||||
> ERROR: Some code points did not transcode.
|
||||
> (ERR)OR: Some code points did not transcode.
|
||||
|
||||
> An object or array close is not valid at this point in the JSON text.
|
||||
|
||||
@@ -5725,6 +5892,8 @@ select distinct lowcase(memname)
|
||||
@param [out] outref= Output fileref in which to create the insert statements.
|
||||
If it exists, it will be appended to, otherwise it will be created.
|
||||
@param [out] schema= (0) The schema of the target database, or the libref.
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -5735,6 +5904,7 @@ select distinct lowcase(memname)
|
||||
,outref=0
|
||||
,schema=0
|
||||
,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* Find the tables */
|
||||
@@ -5764,6 +5934,7 @@ select distinct lowcase(memname)
|
||||
,outds=&ds
|
||||
,flavour=&flavour
|
||||
,maxobs=&maxobs
|
||||
,applydttm=&applydttm
|
||||
)
|
||||
%end;
|
||||
|
||||
|
||||
@@ -15,24 +15,47 @@
|
||||
recognise this and fetch the log of the parent session instead)
|
||||
@li STP environments must finish cleanly to avoid the log being sent to
|
||||
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
||||
and set SYSCC=0. For 9.4m3 we take a unique approach - we open a macro
|
||||
but don't close it! This provides a graceful abort, EXCEPT when called
|
||||
called within a %include within a macro (and that macro contains additional
|
||||
logic). See mp_abort.test.nofix.sas for the example case.
|
||||
If you know of another way to gracefully abort a 9.4m3 STP session, we'd
|
||||
love to hear about it!
|
||||
and set SYSCC=0. We take a unique "soft abort" approach - we open a macro
|
||||
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
||||
a macro. For that, we recommend you use mp_include.sas to perform the
|
||||
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
||||
OUTSIDE of the top-parent macro).
|
||||
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
@param errds= (work.mp_abort_errds) There is no clean way to end a process
|
||||
within a %include called within a macro. Furthermore, there is no way to
|
||||
test if a macro is called within a %include. To handle this particular
|
||||
scenario, the %include should be switched for the mp_include.sas macro.
|
||||
This provides an indicator that we are running a macro within a \%include
|
||||
(`_SYSINCLUDEFILEDEVICE`) and allows us to provide a dataset with the abort
|
||||
values (msg, mac).
|
||||
We can then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back to the calling program to run a regular \%mp_abort().
|
||||
The dataset will contain the following fields:
|
||||
@li iftrue (1=1)
|
||||
@li msg (the message)
|
||||
@li mac (the mac param)
|
||||
|
||||
@version 9.4M3
|
||||
@param mode= (REGULAR) If mode=INCLUDE then the &errds dataset is checked for
|
||||
an abort status.
|
||||
Valid values:
|
||||
@li REGULAR (default)
|
||||
@li INCLUDE
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_include.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
, errds=work.mp_abort_errds
|
||||
, mode=REGULAR
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%global sysprocessmode sysprocessname;
|
||||
@@ -43,9 +66,44 @@
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
|
||||
%if %symexist(_SYSINCLUDEFILEDEVICE) %then %do;
|
||||
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
||||
data &errds;
|
||||
iftrue='1=1';
|
||||
length mac $100 msg $5000;
|
||||
mac=symget('mac');
|
||||
msg=symget('msg');
|
||||
run;
|
||||
data _null_;
|
||||
abort cancel FILE;
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson) or "&SYSPROCESSNAME "="Compute Server " %then %do;
|
||||
%if %symexist(_metaperson)
|
||||
or "&SYSPROCESSNAME "="Compute Server "
|
||||
or &mode=INCLUDE
|
||||
%then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
%if &mode=INCLUDE %then %do;
|
||||
%if %sysfunc(exist(&errds))=1 %then %do;
|
||||
data _null_;
|
||||
set &errds;
|
||||
call symputx('iftrue',iftrue,'l');
|
||||
call symputx('mac',mac,'l');
|
||||
call symputx('msg',msg,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if (&iftrue)=0 %then %return;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname: No include errors found;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* extract log errs / warns, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
@@ -132,7 +190,9 @@
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSSITE"" : ""&syssite"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
@@ -149,11 +209,22 @@
|
||||
rc=stpsrvset('program error', 0);
|
||||
call symputx("syscc",0,"g");
|
||||
run;
|
||||
%if "%substr(&sysvlong.xxxxxxxxx,1,9)" ne "9.04.01M3" %then %do;
|
||||
%put NOTE: Ending SAS session due to:;
|
||||
%put NOTE- &msg;
|
||||
endsas;
|
||||
%end;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* Use mp_include() to handle this.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
||||
/* endsas kills the session making it harder to fetch results */
|
||||
@@ -169,24 +240,6 @@
|
||||
abort cancel nolist;
|
||||
run;
|
||||
%end;
|
||||
%else %if "%substr(&sysvlong.xxxxxxxxx,1,9)" = "9.04.01M3" %then %do;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* See tests/mp_abort.test.1 for an example case.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%abort cancel;
|
||||
%end;
|
||||
|
||||
@@ -25,10 +25,13 @@
|
||||
options:
|
||||
@li SAS (default) - suitable for regular proc sql
|
||||
@li PGSQL - Used for Postgres databases
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfileref.sas
|
||||
@li mf_getvarcount.sas
|
||||
@li mf_getvarformat.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
|
||||
@@ -37,6 +40,7 @@
|
||||
**/
|
||||
|
||||
%macro mp_ds2inserts(ds, outref=0,schema=0,outds=0,flavour=SAS,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not %sysfunc(exist(&ds)) %then %do;
|
||||
@@ -114,10 +118,11 @@ data _null_;
|
||||
length _____str $32767;
|
||||
format _numeric_ best.;
|
||||
format _character_ ;
|
||||
%local i comma var vtype;
|
||||
%local i comma var vtype vfmt;
|
||||
%do i=1 %to %sysfunc(countw(&varlist));
|
||||
%let var=%scan(&varlist,&i);
|
||||
%let vtype=%mf_getvartype(&ds,&var);
|
||||
%let vfmt=%upcase(%mf_getvarformat(&ds,&var,force=1));
|
||||
%if &i=1 %then %do;
|
||||
%if &flavour=SAS %then %do;
|
||||
put "insert into &schema.&outds set ";
|
||||
@@ -147,7 +152,13 @@ data _null_;
|
||||
%end;
|
||||
%else %if &flavour=PGSQL %then %do;
|
||||
if missing(&var) then put 'NULL';
|
||||
else put &var;
|
||||
%if &applydttm=YES and "%substr(&vfmt.xxxxxxxx,1,8)"="DATETIME"
|
||||
%then %do;
|
||||
else put "TIMESTAMP '" &var E8601DT25.6 "'";
|
||||
%end;
|
||||
%else %do;
|
||||
else put &var;
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
|
||||
@@ -78,7 +78,7 @@ run;
|
||||
|
||||
data &outds;
|
||||
if &sqlrc or &syscc or &syserr then do;
|
||||
REASON_CD='VALIDATION_ERROR: '!!
|
||||
REASON_CD='VALIDATION_ERR'!!'OR: '!!
|
||||
coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
|
||||
output;
|
||||
end;
|
||||
|
||||
104
base/mp_include.sas
Normal file
104
base/mp_include.sas
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
@file
|
||||
@brief Performs a wrapped \%include
|
||||
@details This macro wrapper is necessary if you need your included code to
|
||||
know that it is being \%included.
|
||||
|
||||
If you are using %include in a regular program, you could make use of the
|
||||
following macro variables:
|
||||
|
||||
@li SYSINCLUDEFILEDEVICE
|
||||
@li SYSINCLUDEFILEDIR
|
||||
@li SYSINCLUDEFILEFILEREF
|
||||
@li SYSINCLUDEFILENAME
|
||||
|
||||
However these variables are NOT available inside a macro, as documented here:
|
||||
https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1kg1o0606gsv9.htm
|
||||
|
||||
This macro can be used in place of the %include statement, and will insert
|
||||
the following (equivalent) global variables:
|
||||
|
||||
@li _SYSINCLUDEFILEDEVICE
|
||||
@li _SYSINCLUDEFILEDIR
|
||||
@li _SYSINCLUDEFILEFILEREF
|
||||
@li _SYSINCLUDEFILENAME
|
||||
|
||||
These can be used whenever testing _within a macro_. Outside of the macro,
|
||||
the regular automatic variables will still be available (thanks to a
|
||||
concatenated file list in the include statement).
|
||||
|
||||
Example usage:
|
||||
|
||||
filename example temp;
|
||||
data _null_;
|
||||
file example;
|
||||
put '%macro test();';
|
||||
put '%put &=_SYSINCLUDEFILEFILEREF;';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
put '%mend; %test()';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
run;
|
||||
%mp_include(example)
|
||||
|
||||
@param [in] fileref The fileref of the file to be included. Must be provided.
|
||||
@param [in] prefix= (_) The prefix to apply to the global variables.
|
||||
@param [in] opts= (SOURCE2) The options to apply to the %inc statement
|
||||
@param [in] errds= (work.mp_abort_errds) There is no clean way to end a
|
||||
process within a %include called within a macro. Furthermore, there is no
|
||||
way to test if a macro is called within a %include. To handle this
|
||||
particular scenario, the %mp_abort() macro will test for the existence of
|
||||
the `_SYSINCLUDEFILEDEVICE` variable and return the outputs (msg,mac) inside
|
||||
this dataset.
|
||||
It will then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back.
|
||||
NOTE - it is NOT possible to read this dataset as part of _this_ macro -
|
||||
when running abort cancel FILE, ALL macros are closed, so instead it is
|
||||
necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of any macro wrappers.
|
||||
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_include(fileref
|
||||
,prefix=_
|
||||
,opts=SOURCE2
|
||||
,errds=work.mp_abort_errds
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* prepare precode */
|
||||
%local tempref;
|
||||
%let tempref=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &tempref;
|
||||
set sashelp.vextfl(where=(fileref="%upcase(&fileref)"));
|
||||
put '%let _SYSINCLUDEFILEDEVICE=' xengine ';';
|
||||
name=scan(xpath,-1,'/\');
|
||||
put '%let _SYSINCLUDEFILENAME=' name ';';
|
||||
path=subpad(xpath,1,length(xpath)-length(name)-1);
|
||||
put '%let _SYSINCLUDEFILEDIR=' path ';';
|
||||
put '%let _SYSINCLUDEFILEFILEREF=' "&fileref;";
|
||||
run;
|
||||
|
||||
/* prepare the errds */
|
||||
data &errds;
|
||||
length msg mac $1000;
|
||||
iftrue='1=0';
|
||||
run;
|
||||
|
||||
/* include the include */
|
||||
%inc &tempref &fileref/&opts;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=%str(&_SYSINCLUDEFILEDIR/&_SYSINCLUDEFILENAME)
|
||||
,msg=%str(syscc=&syscc after executing &_SYSINCLUDEFILENAME)
|
||||
)
|
||||
|
||||
filename &tempref clear;
|
||||
|
||||
%mend mp_include;
|
||||
@@ -4,7 +4,7 @@
|
||||
@details PROC JSON is faster but will produce errs like the ones below if
|
||||
special chars are encountered.
|
||||
|
||||
> ERROR: Some code points did not transcode.
|
||||
> (ERR)OR: Some code points did not transcode.
|
||||
|
||||
> An object or array close is not valid at this point in the JSON text.
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
@param [out] outref= Output fileref in which to create the insert statements.
|
||||
If it exists, it will be appended to, otherwise it will be created.
|
||||
@param [out] schema= (0) The schema of the target database, or the libref.
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -38,6 +40,7 @@
|
||||
,outref=0
|
||||
,schema=0
|
||||
,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* Find the tables */
|
||||
@@ -67,6 +70,7 @@ select distinct lowcase(memname)
|
||||
,outds=&ds
|
||||
,flavour=&flavour
|
||||
,maxobs=&maxobs
|
||||
,applydttm=&applydttm
|
||||
)
|
||||
%end;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user