Date: Mon, 10 Dec 2007 01:32:32 -0800
Reply-To: chris@OVIEW.CO.UK
Sender: "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From: chris@OVIEW.CO.UK
Organization: http://groups.google.com
Subject: Re: Q: SAS Macro question
Content-Type: text/plain; charset=ISO-8859-1
Hi Patrick,
Here's a solution that works for your test case - but see the caveats
following it:
data test;
do x = 1 to 10;
output;
end;
run;
%macro AddSteps;
proc print data=work.test;
run;
%mend;
%macro SetOptions;
/* some options */
label='My DS');
if _n_ = 1 then call execute('%addsteps');
*
%mend;
data work.test(%SetOptions) ;
set test;
run;
How it works: the SetOptions macro is modified to close the dataset
options early by including the closing bracket (parenthesis) and semi-
colon. It then inserts a CALL EXECUTE statement that will only be
executed once, based on checking _N_. It then inserts a single
asterisk, effectively starting a new comment, so that the original
closing bracket following the SetOptions call doesn't cause an error.
The final data step resolves to:
data work.test(label='My DS');
if _n_ = 1 then call execute('%addsteps');
*) ;
set test;
run;
So far so good, but this is a very fragile solution. In your case,
where you can't or don't want to modify all cases where SetOptions is
called, it's unlikely to be sufficiently robust. For example, it will
cause an error in a case like this:
data test1 (%setoptions) test2 (%setoptions);
More dangerously, in this case:
data test (%setoptions compress=no);
it won't cause an error, but will silently suppress the 'compress=no'
option, causing possible problems later on.
The weakness of the method is that it's using a code injection
technique to force data step statements in where they shouldn't be
allowed. The ideal solution would involve a purely macro-language way
to call CALL EXECUTE, but I can't find such a way at the moment....
Interestingly, every clinical reporting system I've ever worked on
(including those validated for FDA use) is vulnerable to code
injection attacks using this sort of technique.
HTH,
Chris.
--------------------------------------------------------
Elvis SAS Log Analyser - http://www.oview.co.uk/elvis
Recover runnable SAS code from your log's MPRINT lines.
--------------------------------------------------------
On 10 Dec, 04:22, Patrick <patrick.mat...@gmx.ch> wrote:
> Hi all
>
> Just posting that again in the hope to get an answer.
>
> Can anyone of you think of a solution for the following under SAS
> 9.1.3 SP4, Unix or Windows?
>
> What I have:
> - A bunch of programs.
> - I have to add some checks after certain steps (proc's or datasteps)
> within this programs.
> - All steps after which I have to add code are calling the same macro
> in the same way.
> - This macro does nothing else than generating some data set options
> -
> eg: data test(%SetOptions);
>
> What I try to achieve:
> - Not to make direct changes to the script of all the programs (DI
> jobs, sometimes with user written code, sometimes within loops...)
> - Change the %SetOptions macro in a way that it does the job for me.
>
> Where I am:
> - call execute() is behaving close to what I need. But as much as I
> understand it, that won't be the solution.
> - doubting that there is a solution at all for what I try to achieve.
>
> Below is a code example to illustrate what I try to do. The example
> doesn't work - and I understand also why. It's just outlining the
> idea.
>
> Any idea for a solution or info why it can't be done at all would be
> highly appreciated.
>
> Patrick
>
> Example:
> %macro AddSteps;
> proc print data=work.test;
> run;
> %mend;
>
> %macro SetOptions;
> /* some options */
> label='My DS'
> /* how to call macro so that it executes after current step
> boundary? */
> call execute('%AddSteps'); /* ' */
> %mend;
>
> data work.test(%SetOptions) ;
> a=1;
> output;
> run;