From 6841f6bd086727ddf6481f88915db7ca7ec95373 Mon Sep 17 00:00:00 2001 From: yabwon Date: Thu, 30 Jul 2020 14:45:19 +0200 Subject: [PATCH] version 2.1 recompiled with new framework setup --- packages/SQLinDS/000_libname/dssql.sas | 29 +++++++ packages/SQLinDS/001_macro/dssql_inner.sas | 65 ++++++++++++++ packages/SQLinDS/001_macro/sql.sas | 56 ++++++++++++ packages/SQLinDS/002_function/dssql.sas | 42 +++++++++ packages/SQLinDS/999_test/test1.sas | 10 +++ packages/SQLinDS/999_test/test2.sas | 29 +++++++ packages/SQLinDS/description.sas | 45 ++++++++++ packages/SQLinDS/generate_package_sqlinds.sas | 25 ++++++ packages/SQLinDS/license.sas | 19 ++++ packages/SQLinDS/macrofunctionsandwich.sas | 82 ++++++++++++++++++ packages/sqlinds.zip | Bin 0 -> 11175 bytes 11 files changed, 402 insertions(+) create mode 100644 packages/SQLinDS/000_libname/dssql.sas create mode 100644 packages/SQLinDS/001_macro/dssql_inner.sas create mode 100644 packages/SQLinDS/001_macro/sql.sas create mode 100644 packages/SQLinDS/002_function/dssql.sas create mode 100644 packages/SQLinDS/999_test/test1.sas create mode 100644 packages/SQLinDS/999_test/test2.sas create mode 100644 packages/SQLinDS/description.sas create mode 100644 packages/SQLinDS/generate_package_sqlinds.sas create mode 100644 packages/SQLinDS/license.sas create mode 100644 packages/SQLinDS/macrofunctionsandwich.sas create mode 100644 packages/sqlinds.zip diff --git a/packages/SQLinDS/000_libname/dssql.sas b/packages/SQLinDS/000_libname/dssql.sas new file mode 100644 index 0000000..8b520bf --- /dev/null +++ b/packages/SQLinDS/000_libname/dssql.sas @@ -0,0 +1,29 @@ +/*** HELP START ***/ + +/* >>> dsSQL library: <<< + * + * The dsSQL library stores temporary views + * generated during the %SQL() macro execution. + * If possible a subdirectory of WORK is created as: + + LIBNAME dsSQL BASE "%sysfunc(pathname(WORK))/dsSQLtmp"; + + * if not possible then redirects to WORK as: + + LIBNAME dsSQL BASE "%sysfunc(pathname(WORK))"; + +**/ + +/*** HELP END ***/ + +data _null_; + length rc0 $ 32767 rc1 rc2 8; + rc0 = DCREATE("dsSQLtmp", "%sysfunc(pathname(work))/" ); + rc1 = LIBNAME("dsSQL", "%sysfunc(pathname(work))/dsSQLtmp", "BASE"); + rc2 = LIBREF ("dsSQL" ); + if rc2 NE 0 then + rc1 = LIBNAME("dsSQL", "%sysfunc(pathname(work))", "BASE"); +run; + +/* list details about the library in the log */ +libname dsSQL LIST; diff --git a/packages/SQLinDS/001_macro/dssql_inner.sas b/packages/SQLinDS/001_macro/dssql_inner.sas new file mode 100644 index 0000000..b179e5c --- /dev/null +++ b/packages/SQLinDS/001_macro/dssql_inner.sas @@ -0,0 +1,65 @@ +/*** HELP START ***/ + +/* >>> %dsSQL_Inner() macro: <<< + * + * Internal macro called by dsSQL() function. + * The macro generates a uniqualy named sql view on the fly + * which is stored in DSSQL library. + * + * Recommended for SAS 9.3 and higher. + * Based on paper: + * "Use the Full Power of SAS in Your Function-Style Macros" + * by Mike Rhoads, Westat, Rockville, MD + * https://support.sas.com/resources/papers/proceedings12/004-2012.pdf + * +**/ + +/*** HELP END ***/ + +/* inner macro */ +%MACRO dsSQL_Inner() / secure; + %local query tempfile1 tempfile2 ps_tmp; + %let query = %superq(query_arg); + %let query = %sysfunc(dequote(&query)); + + %let viewname = dsSQL.dsSQLtmpview&UNIQUE_INDEX_2.; + + %let tempfile1 = A%sysfunc(datetime(), hex7.); + %let tempfile2 = B%sysfunc(datetime(), hex7.); + + filename &tempfile1. temp; + filename &tempfile2. temp; + + %let ps_tmp = %sysfunc(getoption(ps)); + options ps = MAX; + proc printto log = &tempfile1.; + run; + /* get the query shape i.e. the executed one */ + proc sql feedback noexec; + &query + ; + quit; + proc printto; + run; + options ps = &ps_tmp.; + + %put *** executed as ***; + data _null_; + infile &tempfile1. FIRSTOBS = 2; /* <- 2 to ignore header */ + file &tempfile2.; + /* create the view name */ + if _N_ = 1 then + put " create view &viewname. as "; + input; + put _infile_; + putlog ">" _infile_; + run; + %put *****************; + + proc sql; + %include &tempfile2.; /* &query */ + ; + quit; + filename &tempfile1. clear; + filename &tempfile2. clear; +%MEND dsSQL_Inner; diff --git a/packages/SQLinDS/001_macro/sql.sas b/packages/SQLinDS/001_macro/sql.sas new file mode 100644 index 0000000..67f0ecf --- /dev/null +++ b/packages/SQLinDS/001_macro/sql.sas @@ -0,0 +1,56 @@ +/*** HELP START ***/ + +/* >>> %SQL() macro: <<< + * + * Main macro which allows to use + * SQL's queries in the data step. + * Recommended for SAS 9.3 and higher. + * Based on paper: + * "Use the Full Power of SAS in Your Function-Style Macros" + * by Mike Rhoads, Westat, Rockville, MD + * https://support.sas.com/resources/papers/proceedings12/004-2012.pdf + * + * SYNTAX: + + %sql() + + * The sql querry code is limited to 32000 bytes. + * + * EXAMPLE 1: simple sql query + + data class_subset; + set %SQL(select name, sex, height from sashelp.class where age > 12); + run; + + * EXAMPLE 2: query with dataset options + + data renamed; + set %SQL(select * from sashelp.class where sex = "F")(rename = (age=age2)); + run; + + * EXAMPLE 3: dictionaries in datastep + + data dictionary; + set %SQL(select * from dictionary.macros); + run; + +**/ + +/*** HELP END ***/ + + +/* Main User macro */ +%MACRO SQL() / PARMBUFF SECURE; + %let SYSPBUFF = %superq(SYSPBUFF); /* macroquoting */ + %let SYSPBUFF = %substr(&SYSPBUFF, 2, %LENGTH(&SYSPBUFF) - 2); /* remove brackets */ + %let SYSPBUFF = %superq(SYSPBUFF); /* macroquoting */ + %let SYSPBUFF = %sysfunc(quote(&SYSPBUFF)); /* quotes */ + %put NOTE:*** the query ***; /* print out the query in the log */ + %put NOTE-&SYSPBUFF.; + %put NOTE-*****************; + + %local UNIQUE_INDEX; /* internal variable, a unique index for views */ + %let UNIQUE_INDEX = &SYSINDEX; + %sysfunc(dsSQL(&UNIQUE_INDEX, &SYSPBUFF)) /* <-- call dsSQL() function, + see the WORK.SQLinDSfcmp dataset */ +%MEND SQL; diff --git a/packages/SQLinDS/002_function/dssql.sas b/packages/SQLinDS/002_function/dssql.sas new file mode 100644 index 0000000..da6ffd3 --- /dev/null +++ b/packages/SQLinDS/002_function/dssql.sas @@ -0,0 +1,42 @@ +/*** HELP START ***/ + +/* >>> dsSQL() function: <<< + * + * Internal function called by %SQL() macro. + * The function pass query code from the %SQL() + * macro to the %dsSQL_Inner() innternal macreo. + * + * Recommended for SAS 9.3 and higher. + * Based on paper: + * "Use the Full Power of SAS in Your Function-Style Macros" + * by Mike Rhoads, Westat, Rockville, MD + * https://support.sas.com/resources/papers/proceedings12/004-2012.pdf + * +**/ + +/*** HELP END ***/ + +proc fcmp + /*inlib = work.&packageName.fcmp*/ + outlib = work.&packageName.fcmp.package +; + function dsSQL(unique_index_2, query $) $ 41; + length + query query_arg $ 32000 /* max query length */ + viewname $ 41 + ; + query_arg = dequote(query); + rc = run_macro('dsSQL_Inner' /* <-- inner macro */ + ,unique_index_2 + ,query_arg + ,viewname + ); + if rc = 0 then return(trim(viewname)); + else + do; + put 'ERROR:[function dsSQL] A problem with the dsSQL() function'; + return(" "); + end; + endsub; +run; +quit; diff --git a/packages/SQLinDS/999_test/test1.sas b/packages/SQLinDS/999_test/test1.sas new file mode 100644 index 0000000..8cde321 --- /dev/null +++ b/packages/SQLinDS/999_test/test1.sas @@ -0,0 +1,10 @@ +proc sort data=sashelp.class out=test1; + by age name; +run; + +data class; + set %SQL(select * from sashelp.class order by age, name); +run; + +proc compare base = test1 compare = class; +run; diff --git a/packages/SQLinDS/999_test/test2.sas b/packages/SQLinDS/999_test/test2.sas new file mode 100644 index 0000000..d016da2 --- /dev/null +++ b/packages/SQLinDS/999_test/test2.sas @@ -0,0 +1,29 @@ +data class_work; + set sashelp.class; +run; + +data test_work; + set %sql(select * from class_work); +run; + +options dlcreatedir; +libname user "%sysfunc(pathname(work))/user"; +%put *%sysfunc(pathname(user))*; + +data cars_user cars_user2; + set sashelp.cars; +run; + +data test_user; + set %sql(select * from cars_user); +run; + +data test_user2; + set %sql(select * from user.cars_user2); +run; + +libname user clear; +%put *%sysfunc(pathname(user))*; + +proc datasets lib = work; +run; diff --git a/packages/SQLinDS/description.sas b/packages/SQLinDS/description.sas new file mode 100644 index 0000000..91214c3 --- /dev/null +++ b/packages/SQLinDS/description.sas @@ -0,0 +1,45 @@ +/* This is the description file for the package. */ +/* The colon (:) is a field separator and is restricted */ +/* in lines of the header part. */ + +/* **HEADER** */ +Type: Package :/*required, not null, constant value*/ +Package: SQLinDS :/*required, not null, up to 24 characters, naming restrictions like for a dataset name! */ +Title: SQL queries in Data Step :/*required, not null*/ +Version: 2.1 :/*required, not null*/ +Author: Mike Rhoads (RhoadsM1@Westat.com) :/*required, not null*/ +Maintainer: Bartosz Jablonski (yabwon@gmail.com) :/*required, not null*/ +License: MIT :/*required, not null, values: MIT, GPL2, BSD, etc.*/ +Encoding: UTF8 :/*required, not null, values: UTF8, WLATIN1, LATIN2, etc. */ + +Required: "Base SAS Software" :/*optional, COMMA separated, QUOTED list, names of required SAS products, values must be like from proc setinit;run; output */ + +/* **DESCRIPTION** */ +/* All the text below will be used in help */ +DESCRIPTION START: + +The SQLinDS package is an implementation of +the macro-function-sandwich concept introduced in: +"Use the Full Power of SAS in Your Function-Style Macros" +the article by Mike Rhoads, Westat, Rockville, MD + +Copy of the article is available at: +https://support.sas.com/resources/papers/proceedings12/004-2012.pdf + +Package provides ability to "execute" SQL queries inside a datastep, e.g. + + data class; + set %SQL(select * from sashelp.class); + run; + +SQLinDS package contains the following components: + + 1) %SQL() macro - the main package macro available for the User + + 2) dsSQL() function (internal) + 3) %dsSQL_inner() macro (internal) + 4) Library DSSQL (created in a subdirectory of the WORK library) + +See help for the %SQL() macro to find more examples. + +DESCRIPTION END: diff --git a/packages/SQLinDS/generate_package_sqlinds.sas b/packages/SQLinDS/generate_package_sqlinds.sas new file mode 100644 index 0000000..b5c5fdf --- /dev/null +++ b/packages/SQLinDS/generate_package_sqlinds.sas @@ -0,0 +1,25 @@ + + +filename packages "C:\SAS_PACKAGES\SASPackagesFramework"; +%include packages(SPFinit.sas); + +ods html; +%generatePackage(filesLocation=C:\SAS_PACKAGES_DEV\SQLinDS) + + +/* + * filename reference "packages" and "package" are keywords; + * the first one should be used to point folder with packages; + * the second is used internally by macros; + +filename packages "C:\SAS_PACKAGES"; +%include packages(SPFinit.sas); + +dm 'log;clear'; +%loadpackage(SQLinDS) + +%helpPackage(SQLinDS) +%helpPackage(SQLinDS,*) + +%unloadPackage(SQLinDS) +*/ diff --git a/packages/SQLinDS/license.sas b/packages/SQLinDS/license.sas new file mode 100644 index 0000000..3d62e9f --- /dev/null +++ b/packages/SQLinDS/license.sas @@ -0,0 +1,19 @@ +Copyright (c) 2012 Mike Rhoads + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/SQLinDS/macrofunctionsandwich.sas b/packages/SQLinDS/macrofunctionsandwich.sas new file mode 100644 index 0000000..6dd2092 --- /dev/null +++ b/packages/SQLinDS/macrofunctionsandwich.sas @@ -0,0 +1,82 @@ + +options dlCreateDir; +libname dsSQL "%sysfunc(pathname(work))/dsSQLtmp"; + +/* makro zewnetrzne */ +%MACRO SQL() / PARMBUFF SECURE; + %let SYSPBUFF = %superq(SYSPBUFF); /* maskujemy znaki specjalne */ + %let SYSPBUFF = %substr(&SYSPBUFF,2,%LENGTH(&SYSPBUFF) - 2); /* kasujemy otwierajÄ…cy i zamykajÄ…cy nawias */ + %let SYSPBUFF = %superq(SYSPBUFF); /* maskujemy jeszcze raz */ + %let SYSPBUFF = %sysfunc(quote(&SYSPBUFF)); /* dodajemy cudzyslowy */ + %put ***the querry***; + %put &SYSPBUFF.; + %put ****************; + + %local UNIQUE_INDEX; /* dodatkowa zmienna indeksujaca, zeby tworzony widok byl unikalny */ + %let UNIQUE_INDEX = &SYSINDEX; /* przypisujemy jej wartosc */ + %sysfunc(dsSQL(&UNIQUE_INDEX, &SYSPBUFF)) /* <-- wywolulemy funkcje dsSQL */ +%MEND SQL; + +/* funkcja */ +%macro MacroFunctionSandwich_functions(); + +%local _cmplib_; +options APPEND=(cmplib = WORK.DATASTEPSQLFUNCTIONS) ; +%let _cmplib_ = %sysfunc(getoption(cmplib)); +%put NOTE:[&sysmacroname.] *&=_cmplib_*; + +options cmplib = _null_; + +proc fcmp outlib=work.datastepSQLfunctions.package; + function dsSQL(unique_index_2, query $) $ 41; + + length query query_arg $ 32000 viewname $ 41; /* query_arg mozna zmienic na dluzszy, np. 32000 :-) */ + query_arg = dequote(query); + rc = run_macro('dsSQL_Inner', unique_index_2, query_arg, viewname); /* <-- wywolulemy makro wewnetrzne dsSQL_Inner */ + if rc = 0 then return(trim(viewname)); + else do; + return(" "); + put 'ERROR:[function dsSQL] A problem with the function'; + end; + endsub; +run; + +options cmplib = &_cmplib_.; +%let _cmplib_ = %sysfunc(getoption(cmplib)); +%put NOTE:[&sysmacroname.] *&=_cmplib_*; + +%mend MacroFunctionSandwich_functions; +%MacroFunctionSandwich_functions() + +/* delete macro MacroFunctionSandwich_functions since it is not needed */ +proc sql; + create table _%sysfunc(datetime(), hex16.)_ as + select memname, objname + from dictionary.catalogs + where + objname = upcase('MACROFUNCTIONSANDWICH_FUNCTIONS') + and objtype = 'MACRO' + and libname = 'WORK' + order by memname, objname + ; +quit; +data _null_; + set _last_; + call execute('proc catalog cat = work.' !! strip(memname) !! ' et = macro force;'); + call execute('delete ' !! strip(objname) !! '; run;'); + call execute('quit;'); +run; +proc delete data = _last_; +run; + +/* makro wewnetrzne */ +%MACRO dsSQL_Inner() / SECURE; + %local query; + %let query = %superq(query_arg); + %let query = %sysfunc(dequote(&query)); + + %let viewname = dsSQL.dsSQLtmpview&UNIQUE_INDEX_2; + proc sql; + create view &viewname as &query; + quit; +%MEND dsSQL_Inner; diff --git a/packages/sqlinds.zip b/packages/sqlinds.zip new file mode 100644 index 0000000000000000000000000000000000000000..5ca7fd6c009b758cba2eb710a5be24f26762d80a GIT binary patch literal 11175 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