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

Compare commits

...

27 Commits

Author SHA1 Message Date
Allan Bowe
0b555bb31c Merge pull request #58 from sasjs/apploc
feat: new mf_getapploc macro
2021-08-04 22:04:55 +03:00
Allan Bowe
40b513a9e3 feat: new mf_getapploc macro 2021-08-04 22:00:18 +03:00
Allan Bowe
4eacf4deae Merge pull request #57 from sasjs/mf_existfref
fix: showing filerefs that exist (even when underlying does not) in m…
2021-08-03 14:32:23 +03:00
Allan Bowe
5824423c13 fix: showing filerefs that exist (even when underlying does not) in mf_existfileref, along with 3 tests 2021-08-03 14:16:52 +03:00
Allan Bowe
ce5bfd41dc chore(docs): updating header for mm_gettables 2021-08-02 10:37:01 +03:00
Allan Bowe
0c67a07e42 Merge pull request #56 from sasjs/ddlworkz
feat: new mp_lib2inserts macro.  In addition, modified mp_getddl to i…
2021-07-30 10:11:27 +03:00
Allan Bowe
187504600a chore: fixing test 2021-07-30 00:21:02 +03:00
Allan Bowe
658d67feaa chore: fixing comments 2021-07-30 00:17:44 +03:00
Allan Bowe
5207a77591 feat: new mp_lib2inserts macro. In addition, modified mp_getddl to ignore views, closing #5. Created a test, which highlighted another issue in mp_getddl (labels were being double quoted which caused macro resolution attempts when %including). Changed to single quotes. Switched 'outlib' to 'outschema' in mp_ds2inserts to harmonise with mp_getddl. Added a maxobs option, to speed up testing. 2021-07-30 00:14:29 +03:00
Allan Bowe
4456adf1dc fix: switching default flavour from BASE to SAS to be consistent with mp_getddl 2021-07-29 20:35:57 +03:00
Allan Bowe
03962c2a50 fix: for PGSQL DDL generation, ignore tables with over 1600 columns (as they are not supported in Postgres) 2021-07-29 15:45:16 +03:00
Allan Bowe
6d2fc7e265 fix: removing bug introduced to mp_getddl and adding a test to prevent regression 2021-07-29 13:02:58 +03:00
Allan Bowe
39b2e7c5f9 fix: supporting salts over 32 chars in mp_hashdataset() 2021-07-28 23:22:43 +03:00
Allan Bowe
f99adf5c3e Merge pull request #55 from sasjs/insertforpg
feat: updating mp_ds2inserts to support postgres database
2021-07-28 19:11:38 +03:00
Allan Bowe
69f8e91a2d feat: adding salt as option for mp_hashdataset 2021-07-28 17:04:41 +03:00
Allan Bowe
5b5d01993f fix: updating mp_getddl to enable append to a fileref 2021-07-28 17:01:49 +03:00
Allan Bowe
00fa464a7c feat: updating mp_ds2inserts to support postgres database 2021-07-27 11:07:12 +03:00
Allan Bowe
a5baf46233 chore: removing unnecessary proc format and generating the all.sas file 2021-07-26 22:30:02 +03:00
Allan Bowe
d63d2a4ec1 Merge pull request #54 from sasjs/mp_ds2inserts
feat: mp_ds2inserts macro
2021-07-26 22:10:44 +03:00
Allan Bowe
900f694065 chore: removing extra period 2021-07-26 21:59:48 +03:00
Allan Bowe
838324c15e chore: updating header 2021-07-26 19:06:53 +03:00
Allan Bowe
e3205ec06c feat: mp_ds2inserts macro for creating programs for inserting data (and corresponding test) 2021-07-26 19:04:49 +03:00
Allan Bowe
154a33434e chore: more contributors 2021-07-24 21:06:30 +03:00
Allan Bowe
bfa1bbaeb1 chore: all contributors update in README 2021-07-24 21:03:49 +03:00
Allan Bowe
1f0128aec4 Merge pull request #53 from sasjs/base64doublebytefix
fix: mp_base64copy.sas fixes, removed renegade % symbol and issue wit…
2021-07-18 17:16:58 +03:00
Allan Bowe
69f03f4e14 fix: mp_base64copy.sas fixes, removed renegade % symbol and issue with truncation at character 76. Added two tests, including one to test double byte encoded characters. 2021-07-18 17:05:05 +03:00
Allan Bowe
a932f321d8 Merge pull request #52 from sasjs/sasjs-cli-version-bump
fix: bump sasjs/cli version + 'prepare' support windows CMD/Powershell
2021-07-10 09:40:48 +03:00
17 changed files with 1139 additions and 178 deletions

104
.all-contributorsrc Normal file
View File

@@ -0,0 +1,104 @@
{
"projectName": "core",
"projectOwner": "sasjs",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "angular",
"contributors": [
{
"login": "allanbowe",
"name": "Allan Bowe",
"avatar_url": "https://avatars.githubusercontent.com/u/4420615?v=4",
"profile": "https://github.com/allanbowe",
"contributions": [
"business",
"code",
"content",
"doc",
"infra",
"maintenance",
"mentoring",
"question",
"review",
"test"
]
},
{
"login": "rafgag",
"name": "rafgag",
"avatar_url": "https://avatars.githubusercontent.com/u/69139928?v=4",
"profile": "https://github.com/rafgag",
"contributions": [
"code"
]
},
{
"login": "tmoody",
"name": "Trevor Moody",
"avatar_url": "https://avatars.githubusercontent.com/u/79837106?v=4",
"profile": "https://github.com/tmoody",
"contributions": [
"code"
]
},
{
"login": "krishna-acondy",
"name": "Krishna Acondy",
"avatar_url": "https://avatars.githubusercontent.com/u/2980428?v=4",
"profile": "https://krishna-acondy.io/",
"contributions": [
"code",
"infra",
"blog",
"content",
"ideas",
"video"
]
},
{
"login": "saadjutt01",
"name": "Muhammad Saad ",
"avatar_url": "https://avatars.githubusercontent.com/u/8914650?v=4",
"profile": "https://github.com/saadjutt01",
"contributions": [
"code",
"ideas"
]
},
{
"login": "YuryShkoda",
"name": "Yury Shkoda",
"avatar_url": "https://avatars.githubusercontent.com/u/25773492?v=4",
"profile": "https://www.erudicat.com/",
"contributions": [
"code",
"infra",
"video"
]
},
{
"login": "medjedovicm",
"name": "Mihajlo Medjedovic",
"avatar_url": "https://avatars.githubusercontent.com/u/18329105?v=4",
"profile": "https://github.com/medjedovicm",
"contributions": [
"infra"
]
},
{
"login": "kkchandok",
"name": "kkchandok",
"avatar_url": "https://avatars.githubusercontent.com/u/46090627?v=4",
"profile": "https://github.com/kkchandok",
"contributions": [
"ideas"
]
}
],
"contributorsPerLine": 7
}

View File

@@ -179,3 +179,34 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="#business-allanbowe" title="Business development">💼</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Code">💻</a> <a href="#content-allanbowe" title="Content">🖋</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Documentation">📖</a> <a href="#infra-allanbowe" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑‍🏫</a> <a href="#question-allanbowe" title="Answering Questions">💬</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/rafgag"><img src="https://avatars.githubusercontent.com/u/69139928?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rafgag</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=rafgag" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tmoody"><img src="https://avatars.githubusercontent.com/u/79837106?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Trevor Moody</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=tmoody" title="Code">💻</a></td>
<td align="center"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td>
<td align="center"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=saadjutt01" title="Code">💻</a> <a href="#ideas-saadjutt01" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td>
<td align="center"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/kkchandok"><img src="https://avatars.githubusercontent.com/u/46090627?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kkchandok</b></sub></a><br /><a href="#ideas-kkchandok" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

326
all.sas
View File

@@ -133,7 +133,13 @@ options noquotelenmax;
%macro mf_existfileref(fref %macro mf_existfileref(fref
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%if %sysfunc(fileref(&fref))=0 %then %do; %local rc;
%let rc=%sysfunc(fileref(&fref));
%if &rc=0 %then %do;
1
%end;
%else %if &rc<0 %then %do;
%put &sysmacroname: Fileref &fref exists but the underlying file does not;
1 1
%end; %end;
%else %do; %else %do;
@@ -2250,7 +2256,7 @@ run;
%if &outfound=0 %then %do; %if &outfound=0 %then %do;
filename &outref temp lrecl=2097088; filename &outref temp lrecl=2097088;
%%end; %end;
%if &action=ENCODE %then %do; %if &action=ENCODE %then %do;
data _null_; data _null_;
@@ -2258,12 +2264,12 @@ run;
retain line ""; retain line "";
infile &inref recfm=F lrecl= 1 end=eof; infile &inref recfm=F lrecl= 1 end=eof;
input @1 stream $char1.; input @1 stream $char1.;
file &outref lrecl=76; file &outref recfm=N;
substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream)); substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream));
if mod(_N_,57)=0 or EOF then do; if mod(_N_,57)=0 or EOF then do;
if eof then b64=put(trim(line),$base64X76.); if eof then b64=put(trim(line),$base64X76.);
else b64=put(line, $base64X76.); else b64=put(line, $base64X76.);
put b64; put b64 + (-1) @;
line=""; line="";
end; end;
run; run;
@@ -2275,19 +2281,13 @@ run;
fileout = fopen("&outref",'O',3,'B'); fileout = fopen("&outref",'O',3,'B');
char= '20'x; char= '20'x;
do while(fread(filein)=0); do while(fread(filein)=0);
raw="1234"; length raw $4;
do i=1 to 4; do i=1 to 4;
rc=fget(filein,char,1); rc=fget(filein,char,1);
substr(raw,i,1)=char; substr(raw,i,1)=char;
end; end;
val="123"; rc = fput(fileout,input(raw,$base64X4.));
val=input(raw,$base64X4.); rc = fwrite(fileout);
do i=1 to 3;
length byte $1;
byte=byte(rank(substr(val,i,1)));
rc = fput(fileout, byte);
end;
rc =fwrite(fileout);
end; end;
rc = fclose(filein); rc = fclose(filein);
rc = fclose(fileout); rc = fclose(fileout);
@@ -3429,6 +3429,173 @@ data &outds;
run; run;
%mend mp_ds2fmtds;/** %mend mp_ds2fmtds;/**
@file
@brief Export a dataset to SQL insert statements
@details Converts dataset values to SQL insert statements for use across
multiple database types.
Usage:
%mp_ds2inserts(sashelp.class,outref=myref,outds=class)
data class;
set sashelp.class;
stop;
proc sql;
%inc myref;
@param [in] ds The dataset to be exported
@param [in] maxobs= (max) The max number of inserts to create
@param [out] outref= (0) The output fileref. If it does not exist, it is
created. If it does exist, new records are APPENDED.
@param [out] schema= (0) The library (or schema) in which the target table is
located. If not provided, is ignored.
@param [out] outds= (0) The output table to load. If not provided, will
default to the table in the &ds parameter.
@param [in] flavour= (SAS) The SQL flavour to be applied to the output. Valid
options:
@li SAS (default) - suitable for regular proc sql
@li PGSQL - Used for Postgres databases
<h4> SAS Macros </h4>
@li mf_existfileref.sas
@li mf_getvarcount.sas
@li mf_getvarlist.sas
@li mf_getvartype.sas
@version 9.2
@author Allan Bowe (credit mjsq)
**/
%macro mp_ds2inserts(ds, outref=0,schema=0,outds=0,flavour=SAS,maxobs=max
)/*/STORE SOURCE*/;
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WAR)NING: &ds does not exist;
%return;
%end;
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WAR)NING: &ds does not exist;
%return;
%end;
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%let flavour=%upcase(&flavour);
%if &flavour ne SAS and &flavour ne PGSQL %then %do;
%put %str(WAR)NING: &flavour is not supported;
%return;
%end;
%if &outref=0 %then %do;
%put %str(WAR)NING: Please provide a fileref;
%return;
%end;
%if %mf_existfileref(&outref)=0 %then %do;
filename &outref temp lrecl=66000;
%end;
%if &schema=0 %then %let schema=;
%else %let schema=&schema..;
%if &outds=0 %then %let outds=%scan(&ds,2,.);
%local nobs;
proc sql noprint;
select count(*) into: nobs TRIMMED from &ds;
%if &nobs=0 %then %do;
data _null_;
file &outref mod;
put "/* No rows found in &ds */";
run;
%end;
%local vars;
%let vars=%mf_getvarcount(&ds);
%if &vars=0 %then %do;
data _null_;
file &outref mod;
put "/* No columns found in &schema.&ds */";
run;
%return;
%end;
%else %if &vars>1600 and &flavour=PGSQL %then %do;
data _null_;
file &fref mod;
put "/* &schema.&ds contains &vars vars */";
put "/* Postgres cannot handle tables with over 1600 vars */";
put "/* No inserts will be generated for this table */";
run;
%return;
%end;
%local varlist varlistcomma;
%let varlist=%mf_getvarlist(&ds);
%let varlistcomma=%mf_getvarlist(&ds,dlm=%str(,),quote=double);
/* next, export data */
data _null_;
file &outref mod ;
if _n_=1 then put "/* &schema.&outds (&nobs rows, &vars columns) */";
set &ds;
%if &maxobs ne max %then %do;
if _n_>&maxobs then stop;
%end;
length _____str $32767;
format _numeric_ best.;
format _character_ ;
%local i comma var vtype;
%do i=1 %to %sysfunc(countw(&varlist));
%let var=%scan(&varlist,&i);
%let vtype=%mf_getvartype(&ds,&var);
%if &i=1 %then %do;
%if &flavour=SAS %then %do;
put "insert into &schema.&outds set ";
put " &var="@;
%end;
%else %if &flavour=PGSQL %then %do;
_____str=cats(
"INSERT INTO &schema.&outds ("
,symget('varlistcomma')
,") VALUES ("
);
put _____str;
put " "@;
%end;
%end;
%else %do;
%if &flavour=SAS %then %do;
put " ,&var="@;
%end;
%else %if &flavour=PGSQL %then %do;
put " ,"@;
%end;
%end;
%if &vtype=N %then %do;
%if &flavour=SAS %then %do;
put &var;
%end;
%else %if &flavour=PGSQL %then %do;
if missing(&var) then put 'NULL';
else put &var;
%end;
%end;
%else %do;
_____str="'"!!trim(tranwrd(&var,"'","''"))!!"'";
put _____str;
%end;
%end;
%if &flavour=SAS %then %do;
put ';';
%end;
%else %if &flavour=PGSQL %then %do;
put ');';
%end;
if _n_=&nobs then put /;
run;
%mend mp_ds2inserts;/**
@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
@@ -4239,6 +4406,8 @@ run;
to create tables in SAS or a database. The macro can be used at table or to create tables in SAS or a database. The macro can be used at table or
library level. The default behaviour is to create DDL in SAS format. library level. The default behaviour is to create DDL in SAS format.
Note - views are not currently supported.
Usage: Usage:
data test(index=(pk=(x y)/unique /nomiss)); data test(index=(pk=(x y)/unique /nomiss));
@@ -4250,12 +4419,14 @@ run;
%mp_getddl(work,test,flavour=tsql,showlog=YES) %mp_getddl(work,test,flavour=tsql,showlog=YES)
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_existfileref.sas
@li mf_getvarcount.sas
@li mp_getconstraints.sas @li mp_getconstraints.sas
@param lib libref of the library to create DDL for. Should be assigned. @param lib libref of the library to create DDL for. Should be assigned.
@param ds dataset to create ddl for (optional) @param ds dataset to create ddl for (optional)
@param fref= the fileref to which to write the DDL. If not preassigned, will @param fref= the fileref to which to _append_ the DDL. If it does not exist,
be assigned to TEMP. it will be created.
@param flavour= The type of DDL to create (default=SAS). Supported=TSQL @param flavour= The type of DDL to create (default=SAS). Supported=TSQL
@param showlog= Set to YES to show the DDL in the log @param showlog= Set to YES to show the DDL in the log
@param schema= Choose a preferred schema name (default is to use actual schema @param schema= Choose a preferred schema name (default is to use actual schema
@@ -4271,9 +4442,10 @@ run;
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* check fileref is assigned */ /* check fileref is assigned */
%if %sysfunc(fileref(&fref)) > 0 %then %do; %if %mf_existfileref(&fref)=0 %then %do;
filename &fref temp; filename &fref temp ;
%end; %end;
%if %length(&libref)=0 %then %let libref=WORK; %if %length(&libref)=0 %then %let libref=WORK;
%let flavour=%upcase(&flavour); %let flavour=%upcase(&flavour);
@@ -4281,6 +4453,7 @@ proc sql noprint;
create table _data_ as create table _data_ as
select * from dictionary.tables select * from dictionary.tables
where upcase(libname)="%upcase(&libref)" where upcase(libname)="%upcase(&libref)"
and memtype='DATA' /* views not currently supported */
%if %length(&ds)>0 %then %do; %if %length(&ds)>0 %then %do;
and upcase(memname)="%upcase(&ds)" and upcase(memname)="%upcase(&ds)"
%end; %end;
@@ -4352,7 +4525,7 @@ create table _data_ as
%mend addConst; %mend addConst;
data _null_; data _null_;
file &fref; file &fref mod;
put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */"; put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */";
run; run;
@@ -4375,13 +4548,15 @@ run;
put "create table &libref..&curds("; put "create table &libref..&curds(";
end; end;
else do; else do;
/* just a placeholder - we filter out views at the top */
put "create view &libref..&curds("; put "create view &libref..&curds(";
end; end;
put " "@@; put " "@@;
end; end;
else put " ,"@@; else put " ,"@@;
if length(format)>1 then fmt=" format="!!cats(format); if length(format)>1 then fmt=" format="!!cats(format);
if length(label)>1 then lab=" label="!!quote(trim(label)); if length(label)>1 then
lab=" label="!!cats("'",tranwrd(label,"'","''"),"'");
if notnull='yes' then notnul=' not null'; if notnull='yes' then notnul=' not null';
if type='char' then typ=cats('char(',length,')'); if type='char' then typ=cats('char(',length,')');
else if length ne 8 then typ='num length='!!left(length); else if length ne 8 then typ='num length='!!left(length);
@@ -4453,6 +4628,7 @@ run;
put "create table [&schema].[&curds]("; put "create table [&schema].[&curds](";
end; end;
else do; else do;
/* just a placeholder - we filter out views at the top */
put "create view [&schema].[&curds]("; put "create view [&schema].[&curds](";
end; end;
put " "@@; put " "@@;
@@ -4536,6 +4712,17 @@ run;
put "CREATE SCHEMA &schema;"; put "CREATE SCHEMA &schema;";
%do x=1 %to %sysfunc(countw(&dsnlist)); %do x=1 %to %sysfunc(countw(&dsnlist));
%let curds=%scan(&dsnlist,&x); %let curds=%scan(&dsnlist,&x);
%local curdsvarcount;
%let curdsvarcount=%mf_getvarcount(&libref..&curds);
%if &curdsvarcount>1600 %then %do;
data _null_;
file &fref mod;
put "/* &libref..&curds contains &curdsvarcount vars */";
put "/* Postgres cannot create tables with over 1600 vars */";
put "/* No DDL will be generated for this table";
run;
%end;
%else %do;
data _null_; data _null_;
file &fref mod; file &fref mod;
put "/* Postgres Flavour DDL for &schema..&curds */"; put "/* Postgres Flavour DDL for &schema..&curds */";
@@ -4548,6 +4735,7 @@ run;
put "CREATE TABLE &schema..&curds ("; put "CREATE TABLE &schema..&curds (";
end; end;
else do; else do;
/* just a placeholder - we filter out views at the top */
put "CREATE VIEW &schema..&curds ("; put "CREATE VIEW &schema..&curds (";
end; end;
put " "@@; put " "@@;
@@ -4589,18 +4777,16 @@ run;
); );
file &fref mod; file &fref mod;
by idxusage indxname; by idxusage indxname;
/* ds=cats(libname,'.',memname); */
if first.indxname then do; if first.indxname then do;
put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds("; put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds(";
put ' "' name +(-1) '"' ; put ' "' name +(-1) '"' ;
end; end;
else put ' ,"' name +(-1) '"'; else put ' ,"' name +(-1) '"';
*else put ' ,' name ;
if last.indxname then do; if last.indxname then do;
put ');'; put ');';
end; end;
run; run;
%end;
%end; %end;
%end; %end;
%if %upcase(&showlog)=YES %then %do; %if %upcase(&showlog)=YES %then %do;
@@ -5009,6 +5195,7 @@ create table &outds (rename=(
@li mf_getvartype.sas @li mf_getvartype.sas
@param [in] libds dataset to hash @param [in] libds dataset to hash
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This @param [out] outds= (work.mf_hashdataset) The output dataset to create. This
will contain one column (hashkey) with one observation (a hex32. will contain one column (hashkey) with one observation (a hex32.
representation of the input hash) representation of the input hash)
@@ -5022,7 +5209,8 @@ create table &outds (rename=(
%macro mp_hashdataset( %macro mp_hashdataset(
libds, libds,
outds= outds=,
salt=
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%if %mf_getattrn(&libds,NLOBS)=0 %then %do; %if %mf_getattrn(&libds,NLOBS)=0 %then %do;
%put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset; %put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset;
@@ -5042,7 +5230,7 @@ create table &outds (rename=(
%let varlist=%mf_getvarlist(&libds); %let varlist=%mf_getvarlist(&libds);
data &outds(rename=(&keyvar=hashkey) keep=&keyvar); data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
length &prevkeyvar &keyvar $32; length &prevkeyvar &keyvar $32;
retain &prevkeyvar; retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
set &libds end=&lastvar; set &libds end=&lastvar;
/* hash should include previous row */ /* hash should include previous row */
&keyvar=put(md5(&prevkeyvar &keyvar=put(md5(&prevkeyvar
@@ -5372,6 +5560,78 @@ select distinct lowcase(memname)
%end; %end;
%mend mp_lib2cards;/** %mend mp_lib2cards;/**
@file
@brief Convert all data in a library to SQL insert statements
@details Gets list of members then calls the <code>%mp_ds2inserts()</code>
macro.
Usage:
%mp_getddl(sashelp, schema=work, fref=tempref)
%mp_lib2inserts(sashelp, schema=work, outref=tempref)
%inc tempref;
The output will be one file in the outref fileref.
<h4> SAS Macros </h4>
@li mp_ds2inserts.sas
@param [in] lib Library in which to convert all datasets to inserts
@param [in] flavour= (SAS) The SQL flavour to be applied to the output. Valid
options:
@li SAS (default) - suitable for regular proc sql
@li PGSQL - Used for Postgres databases
@param [in] maxobs= (max) The max number of observations (per table) to create
@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.
@version 9.2
@author Allan Bowe
**/
%macro mp_lib2inserts(lib
,flavour=SAS
,outref=0
,schema=0
,maxobs=max
)/*/STORE SOURCE*/;
/* Find the tables */
%local x ds memlist;
proc sql noprint;
select distinct lowcase(memname)
into: memlist
separated by ' '
from dictionary.tables
where upcase(libname)="%upcase(&lib)"
and memtype='DATA'; /* exclude views */
%let flavour=%upcase(&flavour);
%if &flavour ne SAS and &flavour ne PGSQL %then %do;
%put %str(WAR)NING: &flavour is not supported;
%return;
%end;
/* create the inserts */
%do x=1 %to %sysfunc(countw(&memlist));
%let ds=%scan(&memlist,&x);
%mp_ds2inserts(&lib..&ds
,outref=&outref
,schema=&schema
,outds=&ds
,flavour=&flavour
,maxobs=&maxobs
)
%end;
%mend mp_lib2inserts;/**
@file @file
@brief Create a Markdown Table from a dataset @brief Create a Markdown Table from a dataset
@details A markdown table is a simple table representation for use in @details A markdown table is a simple table representation for use in
@@ -11411,18 +11671,20 @@ run;
%mend mm_gettableid;/** %mend mm_gettableid;/**
@file @file
@brief Creates a dataset with all metadata tables for a particular library @brief Creates a dataset with all metadata tables for a particular library
@details Will only show the tables to which a user has the requisite @details Will only show the tables for which the executing user has the
metadata access. requisite metadata access.
usage: usage:
%mm_gettables(uri=A5X8AHW1.B40001S5) %mm_gettables(uri=A5X8AHW1.B40001S5)
@param outds the dataset to create that contains the list of tables @param [in] uri= the uri of the library for which to return tables
@param uri the uri of the library for which to return tables @param [out] outds= (work.mm_gettables) the dataset to contain the list of
@param getauth= YES|NO - fetch the authdomain used in database connections. tables
@param [in] getauth= (YES) Fetch the authdomain used in database connections.
Set to NO to improve runtimes in larger environments, as there can be a Set to NO to improve runtimes in larger environments, as there can be a
performance hit on the `metadata_getattr(domainuri, "Name", AuthDomain)` call. performance hit on the `metadata_getattr(domainuri, "Name", AuthDomain)`
call.
@returns outds dataset containing all groups in a column named "metagroup" @returns outds dataset containing all groups in a column named "metagroup"
(defaults to work.mm_getlibs). The following columns are provided: (defaults to work.mm_getlibs). The following columns are provided:
@@ -11450,8 +11712,8 @@ data &outds;
libdesc $200 libref engine $8 IsDBMSLibname $1 libdesc $200 libref engine $8 IsDBMSLibname $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 tableuri keep libname libdesc libref engine ServerContext path_schema AuthDomain
tablename IsPreassigned IsDBMSLibname id; tableuri tablename IsPreassigned IsDBMSLibname id;
call missing (of _all_); call missing (of _all_);
uri=symget('uri'); uri=symget('uri');

View File

@@ -17,7 +17,13 @@
%macro mf_existfileref(fref %macro mf_existfileref(fref
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%if %sysfunc(fileref(&fref))=0 %then %do; %local rc;
%let rc=%sysfunc(fileref(&fref));
%if &rc=0 %then %do;
1
%end;
%else %if &rc<0 %then %do;
%put &sysmacroname: Fileref &fref exists but the underlying file does not;
1 1
%end; %end;
%else %do; %else %do;

61
base/mf_getapploc.sas Normal file
View File

@@ -0,0 +1,61 @@
/**
@file
@brief Returns the appLoc from the _program variable
@details When working with SASjs apps, web services / tests / jobs are always
deployed to a root (app) location in the SAS logical folder tree.
When building apps for use in other environments, you do not necessarily know
where the backend services will be deployed. Therefore a function like this
is handy in order to dynamically figure out the appLoc, and enable other
services to be connected by a relative reference.
SASjs apps always have the same immediate substructure (one or more of the
following):
@li /data
@li /jobs
@li /services
@li /tests/jobs
@li /tests/services
@li /tests/macros
This function works by testing for the existence of any of the above in the
automatic _program variable, and returning the part to the left of it.
Usage:
%put %mf_getapploc(&_program)
%put %mf_getapploc(/some/location/services/admin/myservice);
%put %mf_getapploc(/some/location/jobs/extract/somejob/);
%put %mf_getapploc(/some/location/tests/jobs/somejob/);
@author Allan Bowe
**/
%macro mf_getapploc(pgm);
%if "&pgm"="" %then %do;
%put &sysmacroname: No value provided;
%return;
%end;
%local root;
/**
* move up two levels to avoid matches on subfolder or service name
*/
%let root=%substr(&pgm,1,%length(&pgm)-%length(%scan(&pgm,-1,/))-1);
%let root=%substr(&root,1,%length(&root)-%length(%scan(&root,-1,/))-1);
%if %index(&root,/tests/) %then %do;
%let root=%substr(&root,1,%index(&root,/tests/)-1);
%end;
%else %if %index(&root,/services) %then %do;
%let root=%substr(&root,1,%index(&root,/services)-1);
%end;
%else %if %index(&root,/jobs) %then %do;
%let root=%substr(&root,1,%index(&root,/jobs)-1);
%end;
%else %put &sysmacroname: Could not find an app location from &pgm;
&root
%mend mf_getapploc ;

View File

@@ -76,7 +76,7 @@ run;
%if &outfound=0 %then %do; %if &outfound=0 %then %do;
filename &outref temp lrecl=2097088; filename &outref temp lrecl=2097088;
%%end; %end;
%if &action=ENCODE %then %do; %if &action=ENCODE %then %do;
data _null_; data _null_;
@@ -84,12 +84,12 @@ run;
retain line ""; retain line "";
infile &inref recfm=F lrecl= 1 end=eof; infile &inref recfm=F lrecl= 1 end=eof;
input @1 stream $char1.; input @1 stream $char1.;
file &outref lrecl=76; file &outref recfm=N;
substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream)); substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream));
if mod(_N_,57)=0 or EOF then do; if mod(_N_,57)=0 or EOF then do;
if eof then b64=put(trim(line),$base64X76.); if eof then b64=put(trim(line),$base64X76.);
else b64=put(line, $base64X76.); else b64=put(line, $base64X76.);
put b64; put b64 + (-1) @;
line=""; line="";
end; end;
run; run;
@@ -101,19 +101,13 @@ run;
fileout = fopen("&outref",'O',3,'B'); fileout = fopen("&outref",'O',3,'B');
char= '20'x; char= '20'x;
do while(fread(filein)=0); do while(fread(filein)=0);
raw="1234"; length raw $4;
do i=1 to 4; do i=1 to 4;
rc=fget(filein,char,1); rc=fget(filein,char,1);
substr(raw,i,1)=char; substr(raw,i,1)=char;
end; end;
val="123"; rc = fput(fileout,input(raw,$base64X4.));
val=input(raw,$base64X4.); rc = fwrite(fileout);
do i=1 to 3;
length byte $1;
byte=byte(rank(substr(val,i,1)));
rc = fput(fileout, byte);
end;
rc =fwrite(fileout);
end; end;
rc = fclose(filein); rc = fclose(filein);
rc = fclose(fileout); rc = fclose(fileout);

168
base/mp_ds2inserts.sas Normal file
View File

@@ -0,0 +1,168 @@
/**
@file
@brief Export a dataset to SQL insert statements
@details Converts dataset values to SQL insert statements for use across
multiple database types.
Usage:
%mp_ds2inserts(sashelp.class,outref=myref,outds=class)
data class;
set sashelp.class;
stop;
proc sql;
%inc myref;
@param [in] ds The dataset to be exported
@param [in] maxobs= (max) The max number of inserts to create
@param [out] outref= (0) The output fileref. If it does not exist, it is
created. If it does exist, new records are APPENDED.
@param [out] schema= (0) The library (or schema) in which the target table is
located. If not provided, is ignored.
@param [out] outds= (0) The output table to load. If not provided, will
default to the table in the &ds parameter.
@param [in] flavour= (SAS) The SQL flavour to be applied to the output. Valid
options:
@li SAS (default) - suitable for regular proc sql
@li PGSQL - Used for Postgres databases
<h4> SAS Macros </h4>
@li mf_existfileref.sas
@li mf_getvarcount.sas
@li mf_getvarlist.sas
@li mf_getvartype.sas
@version 9.2
@author Allan Bowe (credit mjsq)
**/
%macro mp_ds2inserts(ds, outref=0,schema=0,outds=0,flavour=SAS,maxobs=max
)/*/STORE SOURCE*/;
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WAR)NING: &ds does not exist;
%return;
%end;
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WAR)NING: &ds does not exist;
%return;
%end;
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%let flavour=%upcase(&flavour);
%if &flavour ne SAS and &flavour ne PGSQL %then %do;
%put %str(WAR)NING: &flavour is not supported;
%return;
%end;
%if &outref=0 %then %do;
%put %str(WAR)NING: Please provide a fileref;
%return;
%end;
%if %mf_existfileref(&outref)=0 %then %do;
filename &outref temp lrecl=66000;
%end;
%if &schema=0 %then %let schema=;
%else %let schema=&schema..;
%if &outds=0 %then %let outds=%scan(&ds,2,.);
%local nobs;
proc sql noprint;
select count(*) into: nobs TRIMMED from &ds;
%if &nobs=0 %then %do;
data _null_;
file &outref mod;
put "/* No rows found in &ds */";
run;
%end;
%local vars;
%let vars=%mf_getvarcount(&ds);
%if &vars=0 %then %do;
data _null_;
file &outref mod;
put "/* No columns found in &schema.&ds */";
run;
%return;
%end;
%else %if &vars>1600 and &flavour=PGSQL %then %do;
data _null_;
file &fref mod;
put "/* &schema.&ds contains &vars vars */";
put "/* Postgres cannot handle tables with over 1600 vars */";
put "/* No inserts will be generated for this table */";
run;
%return;
%end;
%local varlist varlistcomma;
%let varlist=%mf_getvarlist(&ds);
%let varlistcomma=%mf_getvarlist(&ds,dlm=%str(,),quote=double);
/* next, export data */
data _null_;
file &outref mod ;
if _n_=1 then put "/* &schema.&outds (&nobs rows, &vars columns) */";
set &ds;
%if &maxobs ne max %then %do;
if _n_>&maxobs then stop;
%end;
length _____str $32767;
format _numeric_ best.;
format _character_ ;
%local i comma var vtype;
%do i=1 %to %sysfunc(countw(&varlist));
%let var=%scan(&varlist,&i);
%let vtype=%mf_getvartype(&ds,&var);
%if &i=1 %then %do;
%if &flavour=SAS %then %do;
put "insert into &schema.&outds set ";
put " &var="@;
%end;
%else %if &flavour=PGSQL %then %do;
_____str=cats(
"INSERT INTO &schema.&outds ("
,symget('varlistcomma')
,") VALUES ("
);
put _____str;
put " "@;
%end;
%end;
%else %do;
%if &flavour=SAS %then %do;
put " ,&var="@;
%end;
%else %if &flavour=PGSQL %then %do;
put " ,"@;
%end;
%end;
%if &vtype=N %then %do;
%if &flavour=SAS %then %do;
put &var;
%end;
%else %if &flavour=PGSQL %then %do;
if missing(&var) then put 'NULL';
else put &var;
%end;
%end;
%else %do;
_____str="'"!!trim(tranwrd(&var,"'","''"))!!"'";
put _____str;
%end;
%end;
%if &flavour=SAS %then %do;
put ';';
%end;
%else %if &flavour=PGSQL %then %do;
put ');';
%end;
if _n_=&nobs then put /;
run;
%mend mp_ds2inserts;

View File

@@ -5,6 +5,8 @@
to create tables in SAS or a database. The macro can be used at table or to create tables in SAS or a database. The macro can be used at table or
library level. The default behaviour is to create DDL in SAS format. library level. The default behaviour is to create DDL in SAS format.
Note - views are not currently supported.
Usage: Usage:
data test(index=(pk=(x y)/unique /nomiss)); data test(index=(pk=(x y)/unique /nomiss));
@@ -16,12 +18,14 @@
%mp_getddl(work,test,flavour=tsql,showlog=YES) %mp_getddl(work,test,flavour=tsql,showlog=YES)
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_existfileref.sas
@li mf_getvarcount.sas
@li mp_getconstraints.sas @li mp_getconstraints.sas
@param lib libref of the library to create DDL for. Should be assigned. @param lib libref of the library to create DDL for. Should be assigned.
@param ds dataset to create ddl for (optional) @param ds dataset to create ddl for (optional)
@param fref= the fileref to which to write the DDL. If not preassigned, will @param fref= the fileref to which to _append_ the DDL. If it does not exist,
be assigned to TEMP. it will be created.
@param flavour= The type of DDL to create (default=SAS). Supported=TSQL @param flavour= The type of DDL to create (default=SAS). Supported=TSQL
@param showlog= Set to YES to show the DDL in the log @param showlog= Set to YES to show the DDL in the log
@param schema= Choose a preferred schema name (default is to use actual schema @param schema= Choose a preferred schema name (default is to use actual schema
@@ -37,9 +41,10 @@
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* check fileref is assigned */ /* check fileref is assigned */
%if %sysfunc(fileref(&fref)) > 0 %then %do; %if %mf_existfileref(&fref)=0 %then %do;
filename &fref temp; filename &fref temp ;
%end; %end;
%if %length(&libref)=0 %then %let libref=WORK; %if %length(&libref)=0 %then %let libref=WORK;
%let flavour=%upcase(&flavour); %let flavour=%upcase(&flavour);
@@ -47,6 +52,7 @@ proc sql noprint;
create table _data_ as create table _data_ as
select * from dictionary.tables select * from dictionary.tables
where upcase(libname)="%upcase(&libref)" where upcase(libname)="%upcase(&libref)"
and memtype='DATA' /* views not currently supported */
%if %length(&ds)>0 %then %do; %if %length(&ds)>0 %then %do;
and upcase(memname)="%upcase(&ds)" and upcase(memname)="%upcase(&ds)"
%end; %end;
@@ -118,7 +124,7 @@ create table _data_ as
%mend addConst; %mend addConst;
data _null_; data _null_;
file &fref; file &fref mod;
put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */"; put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */";
run; run;
@@ -141,13 +147,15 @@ run;
put "create table &libref..&curds("; put "create table &libref..&curds(";
end; end;
else do; else do;
/* just a placeholder - we filter out views at the top */
put "create view &libref..&curds("; put "create view &libref..&curds(";
end; end;
put " "@@; put " "@@;
end; end;
else put " ,"@@; else put " ,"@@;
if length(format)>1 then fmt=" format="!!cats(format); if length(format)>1 then fmt=" format="!!cats(format);
if length(label)>1 then lab=" label="!!quote(trim(label)); if length(label)>1 then
lab=" label="!!cats("'",tranwrd(label,"'","''"),"'");
if notnull='yes' then notnul=' not null'; if notnull='yes' then notnul=' not null';
if type='char' then typ=cats('char(',length,')'); if type='char' then typ=cats('char(',length,')');
else if length ne 8 then typ='num length='!!left(length); else if length ne 8 then typ='num length='!!left(length);
@@ -219,6 +227,7 @@ run;
put "create table [&schema].[&curds]("; put "create table [&schema].[&curds](";
end; end;
else do; else do;
/* just a placeholder - we filter out views at the top */
put "create view [&schema].[&curds]("; put "create view [&schema].[&curds](";
end; end;
put " "@@; put " "@@;
@@ -302,6 +311,17 @@ run;
put "CREATE SCHEMA &schema;"; put "CREATE SCHEMA &schema;";
%do x=1 %to %sysfunc(countw(&dsnlist)); %do x=1 %to %sysfunc(countw(&dsnlist));
%let curds=%scan(&dsnlist,&x); %let curds=%scan(&dsnlist,&x);
%local curdsvarcount;
%let curdsvarcount=%mf_getvarcount(&libref..&curds);
%if &curdsvarcount>1600 %then %do;
data _null_;
file &fref mod;
put "/* &libref..&curds contains &curdsvarcount vars */";
put "/* Postgres cannot create tables with over 1600 vars */";
put "/* No DDL will be generated for this table";
run;
%end;
%else %do;
data _null_; data _null_;
file &fref mod; file &fref mod;
put "/* Postgres Flavour DDL for &schema..&curds */"; put "/* Postgres Flavour DDL for &schema..&curds */";
@@ -314,6 +334,7 @@ run;
put "CREATE TABLE &schema..&curds ("; put "CREATE TABLE &schema..&curds (";
end; end;
else do; else do;
/* just a placeholder - we filter out views at the top */
put "CREATE VIEW &schema..&curds ("; put "CREATE VIEW &schema..&curds (";
end; end;
put " "@@; put " "@@;
@@ -355,18 +376,16 @@ run;
); );
file &fref mod; file &fref mod;
by idxusage indxname; by idxusage indxname;
/* ds=cats(libname,'.',memname); */
if first.indxname then do; if first.indxname then do;
put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds("; put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds(";
put ' "' name +(-1) '"' ; put ' "' name +(-1) '"' ;
end; end;
else put ' ,"' name +(-1) '"'; else put ' ,"' name +(-1) '"';
*else put ' ,' name ;
if last.indxname then do; if last.indxname then do;
put ');'; put ');';
end; end;
run; run;
%end;
%end; %end;
%end; %end;
%if %upcase(&showlog)=YES %then %do; %if %upcase(&showlog)=YES %then %do;

View File

@@ -20,6 +20,7 @@
@li mf_getvartype.sas @li mf_getvartype.sas
@param [in] libds dataset to hash @param [in] libds dataset to hash
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This @param [out] outds= (work.mf_hashdataset) The output dataset to create. This
will contain one column (hashkey) with one observation (a hex32. will contain one column (hashkey) with one observation (a hex32.
representation of the input hash) representation of the input hash)
@@ -33,7 +34,8 @@
%macro mp_hashdataset( %macro mp_hashdataset(
libds, libds,
outds= outds=,
salt=
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%if %mf_getattrn(&libds,NLOBS)=0 %then %do; %if %mf_getattrn(&libds,NLOBS)=0 %then %do;
%put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset; %put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset;
@@ -53,7 +55,7 @@
%let varlist=%mf_getvarlist(&libds); %let varlist=%mf_getvarlist(&libds);
data &outds(rename=(&keyvar=hashkey) keep=&keyvar); data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
length &prevkeyvar &keyvar $32; length &prevkeyvar &keyvar $32;
retain &prevkeyvar; retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
set &libds end=&lastvar; set &libds end=&lastvar;
/* hash should include previous row */ /* hash should include previous row */
&keyvar=put(md5(&prevkeyvar &keyvar=put(md5(&prevkeyvar

73
base/mp_lib2inserts.sas Normal file
View File

@@ -0,0 +1,73 @@
/**
@file
@brief Convert all data in a library to SQL insert statements
@details Gets list of members then calls the <code>%mp_ds2inserts()</code>
macro.
Usage:
%mp_getddl(sashelp, schema=work, fref=tempref)
%mp_lib2inserts(sashelp, schema=work, outref=tempref)
%inc tempref;
The output will be one file in the outref fileref.
<h4> SAS Macros </h4>
@li mp_ds2inserts.sas
@param [in] lib Library in which to convert all datasets to inserts
@param [in] flavour= (SAS) The SQL flavour to be applied to the output. Valid
options:
@li SAS (default) - suitable for regular proc sql
@li PGSQL - Used for Postgres databases
@param [in] maxobs= (max) The max number of observations (per table) to create
@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.
@version 9.2
@author Allan Bowe
**/
%macro mp_lib2inserts(lib
,flavour=SAS
,outref=0
,schema=0
,maxobs=max
)/*/STORE SOURCE*/;
/* Find the tables */
%local x ds memlist;
proc sql noprint;
select distinct lowcase(memname)
into: memlist
separated by ' '
from dictionary.tables
where upcase(libname)="%upcase(&lib)"
and memtype='DATA'; /* exclude views */
%let flavour=%upcase(&flavour);
%if &flavour ne SAS and &flavour ne PGSQL %then %do;
%put %str(WAR)NING: &flavour is not supported;
%return;
%end;
/* create the inserts */
%do x=1 %to %sysfunc(countw(&memlist));
%let ds=%scan(&memlist,&x);
%mp_ds2inserts(&lib..&ds
,outref=&outref
,schema=&schema
,outds=&ds
,flavour=&flavour
,maxobs=&maxobs
)
%end;
%mend mp_lib2inserts;

View File

@@ -1,18 +1,20 @@
/** /**
@file @file
@brief Creates a dataset with all metadata tables for a particular library @brief Creates a dataset with all metadata tables for a particular library
@details Will only show the tables to which a user has the requisite @details Will only show the tables for which the executing user has the
metadata access. requisite metadata access.
usage: usage:
%mm_gettables(uri=A5X8AHW1.B40001S5) %mm_gettables(uri=A5X8AHW1.B40001S5)
@param outds the dataset to create that contains the list of tables @param [in] uri= the uri of the library for which to return tables
@param uri the uri of the library for which to return tables @param [out] outds= (work.mm_gettables) the dataset to contain the list of
@param getauth= YES|NO - fetch the authdomain used in database connections. tables
@param [in] getauth= (YES) Fetch the authdomain used in database connections.
Set to NO to improve runtimes in larger environments, as there can be a Set to NO to improve runtimes in larger environments, as there can be a
performance hit on the `metadata_getattr(domainuri, "Name", AuthDomain)` call. performance hit on the `metadata_getattr(domainuri, "Name", AuthDomain)`
call.
@returns outds dataset containing all groups in a column named "metagroup" @returns outds dataset containing all groups in a column named "metagroup"
(defaults to work.mm_getlibs). The following columns are provided: (defaults to work.mm_getlibs). The following columns are provided:
@@ -40,8 +42,8 @@ data &outds;
libdesc $200 libref engine $8 IsDBMSLibname $1 libdesc $200 libref engine $8 IsDBMSLibname $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 tableuri keep libname libdesc libref engine ServerContext path_schema AuthDomain
tablename IsPreassigned IsDBMSLibname id; tableuri tablename IsPreassigned IsDBMSLibname id;
call missing (of _all_); call missing (of _all_);
uri=symget('uri'); uri=symget('uri');

View File

@@ -0,0 +1,35 @@
/**
@file
@brief Testing mf_existfileref macro
<h4> SAS Macros </h4>
@li mf_existfileref.sas
@li mp_assert.sas
**/
filename ref1 temp;
filename ref2 temp;
data _null_;
file ref1;
put 'exists';
run;
%mp_assert(
iftrue=(%mf_existfileref(ref1)=1),
desc=Checking fileref WITH target file exists,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_existfileref(ref2)=1),
desc=Checking fileref WITHOUT target file exists,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_existfileref(ref3)=0),
desc=Checking non-existant fref does not exist,
outds=work.test_results
)

View File

@@ -0,0 +1,41 @@
/**
@file
@brief Testing mf_getapploc macro
<h4> SAS Macros </h4>
@li mf_getapploc.sas
@li mp_assert.sas
**/
%mp_assert(
iftrue=(
"%mf_getapploc(/some/loc/tests/services/x/service)"="/some/loc"
),
desc=Checking test appLoc matches,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getapploc(/some/loc/tests/services/tests/service)"="/some/loc"
),
desc=Checking nested services appLoc matches,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getapploc(/some/area/services/admin/service)"="/some/area"
),
desc=Checking services appLoc matches,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getapploc(/some/area/jobs/jobs/job)"="/some/area"
),
desc=Checking jobs appLoc matches,
outds=work.test_results
)

View File

@@ -0,0 +1,67 @@
/**
@file
@brief Testing mp_base64copy.sas macro
<h4> SAS Macros </h4>
@li mp_base64copy.sas
@li mp_assert.sas
**/
/* TEST 1 - regular base64 decode */
%let string1=base ik ally;
filename tmp temp;
data _null_;
file tmp;
put "&string1";
run;
%mp_base64copy(inref=tmp, outref=myref, action=ENCODE)
data _null_;
infile myref;
input;
put _infile_;
run;
%mp_base64copy(inref=myref, outref=mynewref, action=DECODE)
data _null_;
infile mynewref lrecl=5000;
input;
put _infile_;
call symputx('string1_check',_infile_);
stop;
run;
%mp_assert(
iftrue=("&string1"="&string1_check"),
desc=Basic String Compare,
outds=work.test_results
)
/* multibyte string check */
filename tmp2 temp;
data _null_;
file tmp2;
put "'╤', '╔', '╗', '═', '╧', '╚', '╝', '║', '╟', '─', '┼', '║', '╢', '│'";
run;
%mp_base64copy(inref=tmp2, outref=myref2, action=ENCODE)
%mp_base64copy(inref=myref2, outref=newref2, action=DECODE)
data _null_;
infile newref2 lrecl=5000;
input;
list;
/* do not print the string to the log else viya 3.5 throws exception */
if trim(_infile_)=
"'╤', '╔', '╗', '═', '╧', '╚', '╝', '║', '╟', '─', '┼', '║', '╢', '│'"
then call symputx('check2',1);
else call symputx('check2',0);
stop;
run;
%mp_assert(
iftrue=("&check2"="1"),
desc=Double Byte String Compare,
outds=work.test_results
)

View File

@@ -0,0 +1,31 @@
/**
@file
@brief Testing mp_ds2inserts.sas macro
<h4> SAS Macros </h4>
@li mp_ds2inserts.sas
@li mp_assert.sas
**/
/**
* test 1 - rebuild an existing dataset
* Cars is a great dataset - it contains leading spaces, and formatted numerics
*/
%mp_ds2inserts(sashelp.cars,outref=testref,schema=work,outds=test)
data work.test;
set sashelp.cars;
stop;
proc sql;
%inc testref;
proc compare base=sashelp.cars compare=work.test;
quit;
%mp_assert(
iftrue=(&sysinfo=1),
desc=sashelp.cars is identical except for ds label,
outds=work.test_results
)

View File

@@ -0,0 +1,23 @@
/**
@file
@brief Testing mp_getddl.sas macro
<h4> SAS Macros </h4>
@li mp_getddl.sas
@li mp_assert.sas
**/
data test(index=(pk=(x y)/unique /nomiss));
x=1;
y='blah';
label x='blah';
run;
proc sql; describe table &syslast;
%mp_getddl(work,test,flavour=tsql,showlog=YES)
%mp_assert(
iftrue=(&syscc=0),
desc=mp_getddl runs without errors,
outds=work.test_results
)

View File

@@ -0,0 +1,42 @@
/**
@file
@brief Testing mp_ds2inserts.sas macro
<h4> SAS Macros </h4>
@li mf_mkdir.sas
@li mp_getddl.sas
@li mp_lib2inserts.sas
@li mp_assert.sas
**/
/* grab 20 datasets from SASHELP */
%let path=%sysfunc(pathname(work));
%mf_mkdir(&path)
libname sashlp "&path";
proc sql noprint;
create table members as
select distinct lowcase(memname) as memname
from dictionary.tables
where upcase(libname)="SASHELP"
and memtype='DATA'; /* exclude views */
data _null_;
set work.members;
call execute(cats('data sashlp.',memname,';set sashelp.',memname,';run;'));
if _n_>20 then stop;
run;
/* export DDL and inserts */
%mp_getddl(sashlp, schema=work, fref=tempref)
%mp_lib2inserts(sashlp, schema=work, outref=tempref,maxobs=50)
/* check if it actually runs */
options source2;
%inc tempref;
/* without errors.. */
%mp_assert(
iftrue=(&syscc=0),
desc=Able to export 20 tables from sashelp using mp_lib2inserts,
outds=work.test_results
)