@sasjs/utils 2.49.0 → 2.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/fs/internal/helper.js +19 -24
  2. package/fs/internal/helper.js.map +1 -1
  3. package/macros/mf_abort.sas +27 -0
  4. package/macros/mf_dedup.sas +53 -0
  5. package/macros/mf_deletefile.sas +31 -0
  6. package/macros/mf_existds.sas +30 -0
  7. package/macros/mf_existfeature.sas +59 -0
  8. package/macros/mf_existfileref.sas +33 -0
  9. package/macros/mf_existfunction.sas +37 -0
  10. package/macros/mf_existvar.sas +39 -0
  11. package/macros/mf_existvarlist.sas +56 -0
  12. package/macros/mf_fmtdttm.sas +42 -0
  13. package/macros/mf_getapploc.sas +78 -0
  14. package/macros/mf_getattrc.sas +34 -0
  15. package/macros/mf_getattrn.sas +34 -0
  16. package/macros/mf_getengine.sas +54 -0
  17. package/macros/mf_getfilesize.sas +64 -0
  18. package/macros/mf_getfmtlist.sas +61 -0
  19. package/macros/mf_getfmtname.sas +44 -0
  20. package/macros/mf_getkeyvalue.sas +32 -0
  21. package/macros/mf_getplatform.sas +75 -0
  22. package/macros/mf_getquotedstr.sas +57 -0
  23. package/macros/mf_getschema.sas +43 -0
  24. package/macros/mf_getuniquefileref.sas +60 -0
  25. package/macros/mf_getuniquelibref.sas +57 -0
  26. package/macros/mf_getuniquename.sas +22 -0
  27. package/macros/mf_getuser.sas +44 -0
  28. package/macros/mf_getvalue.sas +36 -0
  29. package/macros/mf_getvarcount.sas +49 -0
  30. package/macros/mf_getvarformat.sas +72 -0
  31. package/macros/mf_getvarlen.sas +56 -0
  32. package/macros/mf_getvarlist.sas +74 -0
  33. package/macros/mf_getvarnum.sas +58 -0
  34. package/macros/mf_getvartype.sas +52 -0
  35. package/macros/mf_getxengine.sas +43 -0
  36. package/macros/mf_increment.sas +29 -0
  37. package/macros/mf_isblank.sas +27 -0
  38. package/macros/mf_isdir.sas +34 -0
  39. package/macros/mf_isint.sas +36 -0
  40. package/macros/mf_islibds.sas +40 -0
  41. package/macros/mf_loc.sas +29 -0
  42. package/macros/mf_mkdir.sas +67 -0
  43. package/macros/mf_mval.sas +19 -0
  44. package/macros/mf_nobs.sas +26 -0
  45. package/macros/mf_trimstr.sas +50 -0
  46. package/macros/mf_uid.sas +21 -0
  47. package/macros/mf_verifymacvars.sas +72 -0
  48. package/macros/mf_wordsinstr1andstr2.sas +53 -0
  49. package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
  50. package/macros/mf_writefile.sas +67 -0
  51. package/macros/mp_abort.sas +301 -0
  52. package/macros/mp_appendfile.sas +57 -0
  53. package/macros/mp_applyformats.sas +181 -0
  54. package/macros/mp_assert.sas +56 -0
  55. package/macros/mp_assertcols.sas +145 -0
  56. package/macros/mp_assertcolvals.sas +168 -0
  57. package/macros/mp_assertdsobs.sas +121 -0
  58. package/macros/mp_assertscope.sas +147 -0
  59. package/macros/mp_base64copy.sas +117 -0
  60. package/macros/mp_binarycopy.sas +79 -0
  61. package/macros/mp_chop.sas +194 -0
  62. package/macros/mp_cleancsv.sas +78 -0
  63. package/macros/mp_cntlout.sas +85 -0
  64. package/macros/mp_copyfolder.sas +84 -0
  65. package/macros/mp_coretable.sas +73 -0
  66. package/macros/mp_createconstraints.sas +70 -0
  67. package/macros/mp_createwebservice.sas +32 -0
  68. package/macros/mp_csv2ds.sas +140 -0
  69. package/macros/mp_deleteconstraints.sas +52 -0
  70. package/macros/mp_deletefolder.sas +70 -0
  71. package/macros/mp_dictionary.sas +52 -0
  72. package/macros/mp_dirlist.sas +263 -0
  73. package/macros/mp_distinctfmtvalues.sas +50 -0
  74. package/macros/mp_dropmembers.sas +46 -0
  75. package/macros/mp_ds2cards.sas +291 -0
  76. package/macros/mp_ds2csv.sas +212 -0
  77. package/macros/mp_ds2ddl.sas +30 -0
  78. package/macros/mp_ds2fmtds.sas +105 -0
  79. package/macros/mp_ds2inserts.sas +180 -0
  80. package/macros/mp_ds2md.sas +103 -0
  81. package/macros/mp_ds2squeeze.sas +120 -0
  82. package/macros/mp_dsmeta.sas +110 -0
  83. package/macros/mp_filtercheck.sas +251 -0
  84. package/macros/mp_filtergenerate.sas +105 -0
  85. package/macros/mp_filterstore.sas +254 -0
  86. package/macros/mp_filtervalidate.sas +105 -0
  87. package/macros/mp_getcols.sas +67 -0
  88. package/macros/mp_getconstraints.sas +115 -0
  89. package/macros/mp_getdbml.sas +335 -0
  90. package/macros/mp_getddl.sas +400 -0
  91. package/macros/mp_getformats.sas +124 -0
  92. package/macros/mp_getmaxvarlengths.sas +130 -0
  93. package/macros/mp_getpk.sas +281 -0
  94. package/macros/mp_gsubfile.sas +58 -0
  95. package/macros/mp_guesspk.sas +332 -0
  96. package/macros/mp_hashdataset.sas +91 -0
  97. package/macros/mp_hashdirectory.sas +162 -0
  98. package/macros/mp_include.sas +106 -0
  99. package/macros/mp_init.sas +76 -0
  100. package/macros/mp_jsonout.sas +406 -0
  101. package/macros/mp_lib2cards.sas +78 -0
  102. package/macros/mp_lib2inserts.sas +77 -0
  103. package/macros/mp_loadformat.sas +303 -0
  104. package/macros/mp_lockanytable.sas +243 -0
  105. package/macros/mp_lockfilecheck.sas +103 -0
  106. package/macros/mp_makedata.sas +107 -0
  107. package/macros/mp_md5.sas +58 -0
  108. package/macros/mp_perflog.sas +43 -0
  109. package/macros/mp_prevobs.sas +88 -0
  110. package/macros/mp_recursivejoin.sas +90 -0
  111. package/macros/mp_replace.sas +151 -0
  112. package/macros/mp_reseterror.sas +27 -0
  113. package/macros/mp_resetoption.sas +39 -0
  114. package/macros/mp_retainedkey.sas +247 -0
  115. package/macros/mp_runddl.sas +49 -0
  116. package/macros/mp_searchcols.sas +103 -0
  117. package/macros/mp_searchdata.sas +131 -0
  118. package/macros/mp_setkeyvalue.sas +55 -0
  119. package/macros/mp_sortinplace.sas +122 -0
  120. package/macros/mp_stackdiffs.sas +592 -0
  121. package/macros/mp_storediffs.sas +230 -0
  122. package/macros/mp_stprequests.sas +74 -0
  123. package/macros/mp_streamfile.sas +212 -0
  124. package/macros/mp_testjob.sas +92 -0
  125. package/macros/mp_testservice.sas +39 -0
  126. package/macros/mp_testwritespeedlibrary.sas +59 -0
  127. package/macros/mp_tree.sas +71 -0
  128. package/macros/mp_unzip.sas +90 -0
  129. package/macros/mp_updatevarlength.sas +93 -0
  130. package/macros/mp_validatecol.sas +97 -0
  131. package/macros/mp_wait4file.sas +35 -0
  132. package/macros/mp_webin.sas +59 -0
  133. package/macros/mp_zip.sas +85 -0
  134. package/package.json +4 -1
  135. package/scripts/copySASjsCore.ts +36 -0
  136. package/utils/index.d.ts +1 -2
  137. package/utils/index.js +2 -3
  138. package/utils/index.js.map +1 -1
  139. package/utils/utils.d.ts +1 -0
  140. package/utils/utils.js +7 -1
  141. package/utils/utils.js.map +1 -1
  142. package/utils/getNodeModulePath.d.ts +0 -2
  143. package/utils/getNodeModulePath.js +0 -83
  144. package/utils/getNodeModulePath.js.map +0 -1
@@ -0,0 +1,303 @@
1
+ /**
2
+ @file
3
+ @brief Loads a format catalog from a staging dataset
4
+ @details When loading staged data, it is common to receive only the records
5
+ that have actually changed. However, when loading a format catalog, if
6
+ records are missing they are presumed to be no longer required.
7
+
8
+ This macro will augment a staging dataset with other records from the same
9
+ format, to prevent loss of data - UNLESS the input dataset contains a marker
10
+ column, specifying that a particular row needs to be deleted (`delete_col=`).
11
+
12
+ This macro can also be used to identify which records would be (or were)
13
+ considered new, modified or deleted (`loadtarget=`) by creating the following
14
+ tables:
15
+
16
+ @li work.outds_add
17
+ @li work.outds_del
18
+ @li work.outds_mod
19
+
20
+ For example usage, see mp_loadformat.test.sas
21
+
22
+ @param [in] libcat The format catalog to be loaded
23
+ @param [in] libds The staging table to load
24
+ @param [in] loadtarget= (NO) Set to YES to actually load the target catalog
25
+ @param [in] delete_col= (_____DELETE__THIS__RECORD_____) The column used to
26
+ mark a record for deletion. Values should be "Yes" or "No".
27
+ @param [out] auditlibds= (0) For change tracking, set to the libds of an audit
28
+ table as defined in mddl_dc_difftable.sas
29
+ @param [in] locklibds= (0) For multi-user (parallel) situations, set to the
30
+ libds of the DC lock table as defined in the mddl_dc_locktable.sas macro.
31
+ @param [out] outds_add= (0) Set a libds here to see the new records added
32
+ @param [out] outds_del= (0) Set a libds here to see the records deleted
33
+ @param [out] outds_mod= (0) Set a libds here to see the modified records
34
+ @param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
35
+
36
+ <h4> SAS Macros </h4>
37
+ @li mddl_sas_cntlout.sas
38
+ @li mf_getuniquename.sas
39
+ @li mf_nobs.sas
40
+ @li mp_abort.sas
41
+ @li mp_cntlout.sas
42
+ @li mp_lockanytable.sas
43
+ @li mp_storediffs.sas
44
+
45
+ <h4> Related Macros </h4>
46
+ @li mddl_dc_difftable.sas
47
+ @li mddl_dc_locktable.sas
48
+ @li mp_loadformat.test.sas
49
+ @li mp_lockanytable.sas
50
+ @li mp_stackdiffs.sas
51
+
52
+
53
+ @version 9.2
54
+ @author Allan Bowe
55
+
56
+ **/
57
+
58
+ %macro mp_loadformat(libcat,libds
59
+ ,loadtarget=NO
60
+ ,auditlibds=0
61
+ ,locklibds=0
62
+ ,delete_col=_____DELETE__THIS__RECORD_____
63
+ ,outds_add=0
64
+ ,outds_del=0
65
+ ,outds_mod=0
66
+ ,mdebug=0
67
+ );
68
+ /* set up local macro variables and temporary tables (with a prefix) */
69
+ %local err msg prefix dslist i var fmtlist ibufsize;
70
+ %let dslist=base_fmts template inlibds ds1 stagedata storediffs;
71
+ %if &outds_add=0 %then %let dslist=&dslist outds_add;
72
+ %if &outds_del=0 %then %let dslist=&dslist outds_del;
73
+ %if &outds_mod=0 %then %let dslist=&dslist outds_mod;
74
+ %let prefix=%substr(%mf_getuniquename(),1,21);
75
+ %do i=1 %to %sysfunc(countw(&dslist));
76
+ %let var=%scan(&dslist,&i);
77
+ %local &var;
78
+ %let &var=%upcase(&prefix._&var);
79
+ %end;
80
+
81
+ /*
82
+ format values can be up to 32767 wide. SQL joins on such a wide column can
83
+ cause buffer issues. Update ibufsize and reset at the end.
84
+ */
85
+ %let ibufsize=%sysfunc(getoption(ibufsize));
86
+ options ibufsize=32767 ;
87
+
88
+ /* in DC, format catalogs maybe specified in the libds with a -FC extension */
89
+ %let libcat=%scan(&libcat,1,-);
90
+
91
+ /* perform input validations */
92
+ %let err=0;
93
+ %let msg=0;
94
+ data _null_;
95
+ if _n_=1 then putlog "&sysmacroname entry vars:";
96
+ set sashelp.vmacro;
97
+ where scope="&sysmacroname";
98
+ value=upcase(value);
99
+ if &mdebug=0 then put name '=' value;
100
+ if name=:'LOAD' and value not in ('YES','NO') then do;
101
+ call symputx('msg',"invalid value for "!!name!!":"!!value);
102
+ call symputx('err',1);
103
+ stop;
104
+ end;
105
+ else if name='LIBCAT' then do;
106
+ if exist(value,'CATALOG') le 0 then do;
107
+ call symputx('msg',"Unable to open catalog: "!!value);
108
+ call symputx('err',1);
109
+ stop;
110
+ end;
111
+ end;
112
+ else if name='LIBDS' then do;
113
+ if exist(value) le 0 then do;
114
+ call symputx('msg',"Unable to open staging table: "!!value);
115
+ call symputx('err',1);
116
+ stop;
117
+ end;
118
+ end;
119
+ else if (name=:'OUTDS' or name in ('DELETE_COL','LOCKLIBDS','AUDITLIBDS'))
120
+ and missing(value) then do;
121
+ call symputx('msg',"missing value in var: "!!name);
122
+ call symputx('err',1);
123
+ stop;
124
+ end;
125
+ run;
126
+
127
+ %mp_abort(
128
+ iftrue=(&err ne 0)
129
+ ,mac=&sysmacroname
130
+ ,msg=%str(&msg)
131
+ )
132
+
133
+ /**
134
+ * First, extract only relevant formats from the catalog
135
+ */
136
+ proc sql noprint;
137
+ select distinct upcase(fmtname) into: fmtlist separated by ' ' from &libds;
138
+
139
+ %mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
140
+
141
+
142
+ /**
143
+ * Ensure input table and base_formats have consistent lengths and types
144
+ */
145
+ %mddl_sas_cntlout(libds=&template)
146
+ data &inlibds;
147
+ length &delete_col $3;
148
+ if 0 then set &template;
149
+ set &libds;
150
+ if &delete_col='' then &delete_col='No';
151
+ fmtname=upcase(fmtname);
152
+ if missing(type) then do;
153
+ if substr(fmtname,1,1)='$' then type='C';
154
+ else type='N';
155
+ end;
156
+ if type='N' then do;
157
+ start=cats(start);
158
+ end=cats(end);
159
+ end;
160
+ run;
161
+
162
+ /**
163
+ * Identify new records
164
+ */
165
+ proc sql;
166
+ create table &outds_add(drop=&delete_col) as
167
+ select a.*
168
+ from &inlibds a
169
+ left join &base_fmts b
170
+ on a.fmtname=b.fmtname
171
+ and a.start=b.start
172
+ where b.fmtname is null
173
+ and upcase(a.&delete_col) ne "YES"
174
+ order by fmtname, start;;
175
+
176
+ /**
177
+ * Identify deleted records
178
+ */
179
+ create table &outds_del(drop=&delete_col) as
180
+ select a.*
181
+ from &inlibds a
182
+ inner join &base_fmts b
183
+ on a.fmtname=b.fmtname
184
+ and a.start=b.start
185
+ where upcase(a.&delete_col)="YES"
186
+ order by fmtname, start;
187
+
188
+ /**
189
+ * Identify modified records
190
+ */
191
+ create table &outds_mod (drop=&delete_col) as
192
+ select a.*
193
+ from &inlibds a
194
+ inner join &base_fmts b
195
+ on a.fmtname=b.fmtname
196
+ and a.start=b.start
197
+ where upcase(a.&delete_col) ne "YES"
198
+ order by fmtname, start;
199
+
200
+ options ibufsize=&ibufsize;
201
+
202
+ %mp_abort(
203
+ iftrue=(&syscc ne 0)
204
+ ,mac=&sysmacroname
205
+ ,msg=%str(SYSCC=&syscc prior to load prep)
206
+ )
207
+
208
+ %if &loadtarget=YES %then %do;
209
+ data &ds1;
210
+ merge &base_fmts(in=base)
211
+ &outds_mod(in=mod)
212
+ &outds_add(in=add)
213
+ &outds_del(in=del);
214
+ if not del and not mod;
215
+ by fmtname start;
216
+ run;
217
+ data &stagedata;
218
+ set &ds1 &outds_mod;
219
+ run;
220
+ proc sort;
221
+ by fmtname start;
222
+ run;
223
+ %end;
224
+ /* mp abort needs to run outside of conditional blocks */
225
+ %mp_abort(
226
+ iftrue=(&syscc ne 0)
227
+ ,mac=&sysmacroname
228
+ ,msg=%str(SYSCC=&syscc prior to actual load)
229
+ )
230
+ %if &loadtarget=YES %then %do;
231
+ %if %mf_nobs(&stagedata)=0 %then %do;
232
+ %put There are no changes to load in &libcat!;
233
+ %return;
234
+ %end;
235
+ %if &locklibds ne 0 %then %do;
236
+ /* prevent parallel updates */
237
+ %mp_lockanytable(LOCK
238
+ ,lib=%scan(&libcat,1,.)
239
+ ,ds=%scan(&libcat,2,.)-FC
240
+ ,ref=MP_LOADFORMAT commencing format load
241
+ ,ctl_ds=&locklibds
242
+ )
243
+ %end;
244
+ /* do the actual load */
245
+ proc format lib=&libcat cntlin=&stagedata;
246
+ run;
247
+ %if &locklibds ne 0 %then %do;
248
+ /* unlock the table */
249
+ %mp_lockanytable(UNLOCK
250
+ ,lib=%scan(&libcat,1,.)
251
+ ,ds=%scan(&libcat,2,.)-FC
252
+ ,ref=MP_LOADFORMAT completed format load
253
+ ,ctl_ds=&locklibds
254
+ )
255
+ %end;
256
+ /* track the changes */
257
+ %if &auditlibds ne 0 %then %do;
258
+ %if &locklibds ne 0 %then %do;
259
+ %mp_lockanytable(LOCK
260
+ ,lib=%scan(&auditlibds,1,.)
261
+ ,ds=%scan(&auditlibds,2,.)
262
+ ,ref=MP_LOADFORMAT commencing audit table load
263
+ ,ctl_ds=&locklibds
264
+ )
265
+ %end;
266
+
267
+ %mp_storediffs(&libcat-FC
268
+ ,&base_fmts
269
+ ,FMTNAME START
270
+ ,delds=&outds_del
271
+ ,modds=&outds_mod
272
+ ,appds=&outds_add
273
+ ,outds=&storediffs
274
+ ,mdebug=&mdebug
275
+ )
276
+
277
+ proc append base=&auditlibds data=&storediffs;
278
+ run;
279
+
280
+ %if &locklibds ne 0 %then %do;
281
+ %mp_lockanytable(UNLOCK
282
+ ,lib=%scan(&auditlibds,1,.)
283
+ ,ds=%scan(&auditlibds,2,.)
284
+ ,ref=MP_LOADFORMAT commencing audit table load
285
+ ,ctl_ds=&locklibds
286
+ )
287
+ %end;
288
+ %end;
289
+ %end;
290
+ %mp_abort(
291
+ iftrue=(&syscc ne 0)
292
+ ,mac=&sysmacroname
293
+ ,msg=%str(SYSCC=&syscc after load)
294
+ )
295
+
296
+ %if &mdebug=0 %then %do;
297
+ proc datasets lib=work;
298
+ delete &prefix:;
299
+ run;
300
+ %put &sysmacroname exit vars:;
301
+ %put _local_;
302
+ %end;
303
+ %mend mp_loadformat;
@@ -0,0 +1,243 @@
1
+ /**
2
+ @file
3
+ @brief Mechanism for locking tables to prevent parallel modifications
4
+ @details Uses a control table to enable ANY table to be locked for updates
5
+ (not just SAS datasets).
6
+ Only useful if every update uses the macro! Used heavily within
7
+ [Data Controller for SAS](https://datacontroller.io).
8
+
9
+ @param [in] action The action to be performed. Valid values:
10
+ @li LOCK - Sets the lock flag, also confirms if a SAS lock is available
11
+ @li UNLOCK - Unlocks the table
12
+ @param [in] lib= (WORK) The libref of the table to lock. Should already be
13
+ assigned.
14
+ @param [in] ds= The dataset to lock
15
+ @param [in] ref= A meaningful reference to enable the lock to be traced. Max
16
+ length is 200 characters.
17
+ @param [out] ctl_ds= (0) The control table which controls the actual locking.
18
+ Should already be assigned and available. The definition is available by
19
+ running the mddl_dc_locktable.sas macro.
20
+
21
+ @param [in] loops= (25) Number of times to check for a lock.
22
+ @param [in] loop_secs= (1) Seconds to wait between each lock attempt
23
+
24
+ <h4> SAS Macros </h4>
25
+ @li mf_fmtdttm.sas
26
+ @li mp_abort.sas
27
+ @li mp_lockfilecheck.sas
28
+ @li mf_getuser.sas
29
+
30
+ <h4> Related Macros </h4>
31
+ @li mp_lockanytable.test.sas
32
+
33
+ @version 9.2
34
+
35
+ **/
36
+
37
+ %macro mp_lockanytable(
38
+ action
39
+ ,lib= WORK
40
+ ,ds=0
41
+ ,ref=
42
+ ,ctl_ds=0
43
+ ,loops=25
44
+ ,loop_secs=1
45
+ );
46
+ data _null_;
47
+ if _n_=1 then putlog "&sysmacroname entry vars:";
48
+ set sashelp.vmacro;
49
+ where scope="&sysmacroname";
50
+ put name '=' value;
51
+ run;
52
+
53
+ %mp_abort(iftrue= ("&ds"="0" and &action ne MAKETABLE)
54
+ ,mac=&sysmacroname
55
+ ,msg=%str(dataset was not provided)
56
+ )
57
+ %mp_abort(iftrue= (&ctl_ds=0)
58
+ ,mac=&sysmacroname
59
+ ,msg=%str(Control dataset was not provided)
60
+ )
61
+
62
+ /* set up lib & mac vars */
63
+ %let lib=%upcase(&lib);
64
+ %let ds=%upcase(&ds);
65
+ %let action=%upcase(&action);
66
+ %local user x trans msg abortme;
67
+ %let user=%mf_getuser();
68
+ %let abortme=0;
69
+
70
+ %mp_abort(iftrue= (&action ne LOCK & &action ne UNLOCK & &action ne MAKETABLE)
71
+ ,mac=&sysmacroname
72
+ ,msg=%str(Invalid action (&action) provided)
73
+ )
74
+
75
+ /* if an err condition exists, exit before we even begin */
76
+ %mp_abort(iftrue= (&syscc>0 and &action=LOCK)
77
+ ,mac=&sysmacroname
78
+ ,msg=%str(aborting due to syscc=&syscc on LOCK entry)
79
+ )
80
+
81
+ /* do not bother locking work tables (else may affect all WORK libraries) */
82
+ %if (%upcase(&lib)=WORK or %str(&lib)=%str()) & &action ne MAKETABLE %then %do;
83
+ %put NOTE: WORK libraries will not be registered in the locking system.;
84
+ %return;
85
+ %end;
86
+
87
+ /* do not proceed if no observations can be processed */
88
+ %mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
89
+ ,mac=&sysmacroname
90
+ ,msg=%str(cannot continue when options obs = 0)
91
+ )
92
+
93
+ %if &ACTION=LOCK %then %do;
94
+
95
+ /* abort if a SAS lock is already in place, or cannot be applied */
96
+ %mp_lockfilecheck(&lib..&ds)
97
+
98
+ /* next, check there is a record for this table */
99
+ %local record_exists_check;
100
+ proc sql noprint;
101
+ select count(*) into: record_exists_check from &ctl_ds
102
+ where LOCK_LIB ="&lib" and LOCK_DS="&ds";
103
+ quit;
104
+ %if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
105
+ %if &record_exists_check=0 %then %do;
106
+ data _null_;
107
+ putlog "&sysmacroname: adding record to lock table..";
108
+ run;
109
+
110
+ data ;
111
+ if 0 then set &ctl_ds;
112
+ LOCK_LIB ="&lib";
113
+ LOCK_DS="&ds";
114
+ LOCK_STATUS_CD='LOCKED';
115
+ LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt;
116
+ LOCK_USER_NM="&user";
117
+ LOCK_PID="&sysjobid";
118
+ LOCK_REF="&ref";
119
+ output;stop;
120
+ run;
121
+ %let trans=&syslast;
122
+ proc append base=&ctl_ds data=&trans;
123
+ run;
124
+ %end;
125
+ /* if record does exist, perform lock attempts */
126
+ %else %do x=1 %to &loops;
127
+ data _null_;
128
+ putlog "&sysmacroname: attempting lock (iteration &x) "@;
129
+ putlog "at %sysfunc(datetime(),datetime19.) ..";
130
+ run;
131
+
132
+ proc sql;
133
+ update &ctl_ds
134
+ set LOCK_STATUS_CD='LOCKED'
135
+ , LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
136
+ , LOCK_USER_NM="&user"
137
+ , LOCK_PID="&sysjobid"
138
+ , LOCK_REF="&ref"
139
+ where LOCK_LIB ="&lib" and LOCK_DS="&ds";
140
+ quit;
141
+ /**
142
+ * NOTE - occasionally SQL server will return an err code (deadlocked
143
+ * transaction). If so, ignore it, keep calm, and carry on..
144
+ */
145
+ %if &syscc>0 %then %do;
146
+ data _null_;
147
+ putlog 'NOTE-' / 'NOTE-';
148
+ putlog "NOTE- &sysmacroname: Update failed. "@;
149
+ putlog "Resetting err conditions and re-attempting.";
150
+ putlog "NOTE- syscc=&syscc syserr=&syserr sqlrc=&sqlrc";
151
+ putlog 'NOTE-' / 'NOTE-';
152
+ run;
153
+ %let syscc=0;
154
+ %let sqlrc=0;
155
+ %end;
156
+
157
+ /* now check if the record was successfully updated */
158
+ %local success_check;
159
+ proc sql noprint;
160
+ select count(*) into: success_check from &ctl_ds
161
+ where LOCK_LIB ="&lib" and LOCK_DS="&ds"
162
+ and LOCK_PID="&sysjobid" and LOCK_STATUS_CD='LOCKED';
163
+ quit;
164
+ %if &success_check=0 %then %do;
165
+ %if &x < &loops %then %do;
166
+ /* pause before next check */
167
+ data _null_;
168
+ putlog 'NOTE-' / 'NOTE-';
169
+ putlog "NOTE- &sysmacroname: table locked, waiting "@;
170
+ putlog "%sysfunc(sleep(&loop_inc)) seconds.. ";
171
+ putlog "NOTE- (iteration &x of &loops)";
172
+ putlog 'NOTE-' / 'NOTE-';
173
+ run;
174
+ %end;
175
+ %else %do;
176
+ %let msg=Unable to lock &lib..&ds via &ctl_ds after &loops attempts.\n
177
+ Please ask your administrator to investigate!;
178
+ %let abortme=1;
179
+ %end;
180
+ %end;
181
+ %else %do;
182
+ data _null_;
183
+ putlog 'NOTE-' / 'NOTE-';
184
+ putlog "NOTE- &sysmacroname: Table &lib..&ds locked at "@;
185
+ putlog " %sysfunc(datetime(),datetime19.) (iteration &x)"@;
186
+ putlog 'NOTE-' / 'NOTE-';
187
+ run;
188
+ %if &syscc>0 %then %do;
189
+ %put setting syscc(&syscc) back to 0;
190
+ %let syscc=0;
191
+ %end;
192
+ %let x=&loops; /* no more iterations needed */
193
+ %end;
194
+ %end;
195
+ %end;
196
+ %else %if &ACTION=UNLOCK %then %do;
197
+ %local status;
198
+ proc sql noprint;
199
+ select LOCK_STATUS_CD into: status from &ctl_ds
200
+ where LOCK_LIB ="&lib" and LOCK_DS="&ds";
201
+ quit;
202
+ %if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
203
+ %if &status=LOCKED %then %do;
204
+ data _null_;
205
+ putlog "&sysmacroname: unlocking &lib..&ds:";
206
+ run;
207
+ proc sql;
208
+ update &ctl_ds
209
+ set LOCK_STATUS_CD='UNLOCKED'
210
+ , LOCK_END_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
211
+ , LOCK_USER_NM="&user"
212
+ , LOCK_PID="&sysjobid"
213
+ , LOCK_REF="&ref"
214
+ where LOCK_LIB ="&lib" and LOCK_DS="&ds";
215
+ quit;
216
+ %end;
217
+ %else %if &status=UNLOCKED %then %do;
218
+ %put %str(WAR)NING: &lib..&ds is already unlocked!;
219
+ %end;
220
+ %else %do;
221
+ %put NOTE: Unrecognised STATUS_CD (&status) in &ctl_ds;
222
+ %let abortme=1;
223
+ %end;
224
+ %end;
225
+ %else %do;
226
+ %let msg=lock_anytable given unsupported action (&action);
227
+ %let abortme=1;
228
+ %end;
229
+
230
+ /* catch errs - mp_abort must be called outside of a logic block */
231
+ %mp_abort(iftrue=(&abortme=1),
232
+ msg=%superq(msg),
233
+ mac=&sysmacroname
234
+ )
235
+
236
+ %exit_macro:
237
+ data _null_;
238
+ put "&sysmacroname: Exit vars: action=&action lib=&lib ds=&ds";
239
+ put " syscc=&syscc sqlrc=&sqlrc syserr=&syserr";
240
+ run;
241
+ %mend mp_lockanytable;
242
+
243
+
@@ -0,0 +1,103 @@
1
+ /**
2
+ @file
3
+ @brief Aborts if a SAS lock file is in place, or if one cannot be applied.
4
+ @details Used in conjuction with the mp_lockanytable macro.
5
+ More info here: https://sasensei.com/flash/24
6
+
7
+ Usage:
8
+
9
+ data work.test; a=1;run;
10
+ %mp_lockfilecheck(work.test)
11
+
12
+ @param [in] libds The libref.dataset for which to check the lock status
13
+
14
+ <h4> SAS Macros </h4>
15
+ @li mp_abort.sas
16
+ @li mf_getattrc.sas
17
+
18
+ <h4> Related Macros </h4>
19
+ @li mp_lockanytable.sas
20
+ @li mp_lockfilecheck.test.sas
21
+
22
+ @version 9.2
23
+ **/
24
+
25
+ %macro mp_lockfilecheck(
26
+ libds
27
+ )/*/STORE SOURCE*/;
28
+
29
+ data _null_;
30
+ if _n_=1 then putlog "&sysmacroname entry vars:";
31
+ set sashelp.vmacro;
32
+ where scope="&sysmacroname";
33
+ put name '=' value;
34
+ run;
35
+
36
+ %mp_abort(iftrue= (&syscc>0)
37
+ ,mac=checklock.sas
38
+ ,msg=Aborting with syscc=&syscc on entry.
39
+ )
40
+ %mp_abort(iftrue= ("&libds"="0")
41
+ ,mac=&sysmacroname
42
+ ,msg=%str(libds not provided)
43
+ )
44
+
45
+ %local msg lib ds;
46
+ %let lib=%upcase(%scan(&libds,1,.));
47
+ %let ds=%upcase(%scan(&libds,2,.));
48
+
49
+ /* in DC, format catalogs are passed with a -FC suffix. No saslock here! */
50
+ %if %scan(&libds,2,-)=FC %then %do;
51
+ %put &sysmacroname: Format Catalog detected, no lockfile applied to &libds;
52
+ %return;
53
+ %end;
54
+
55
+ /* do not proceed if no observations can be processed */
56
+ %let msg=options obs = 0. syserrortext=%superq(syserrortext);
57
+ %mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
58
+ ,mac=checklock.sas
59
+ ,msg=%superq(msg)
60
+ )
61
+
62
+ data _null_;
63
+ putlog "Checking engine & member type";
64
+ run;
65
+ %local engine memtype;
66
+ %let memtype=%mf_getattrc(&libds,MTYPE);
67
+ %let engine=%mf_getattrc(&libds,ENGINE);
68
+
69
+ %if &engine ne V9 and &engine ne BASE %then %do;
70
+ data _null_;
71
+ putlog "Lib &lib is not assigned using BASE engine - uses &engine instead";
72
+ putlog "SAS lock check will not be performed";
73
+ run;
74
+ %return;
75
+ %end;
76
+ %else %if &memtype ne DATA %then %do;
77
+ %put NOTE: Cannot lock a VIEW!! Memtype=&memtype;
78
+ %return;
79
+ %end;
80
+
81
+ data _null_;
82
+ putlog "Engine = &engine, memtype=&memtype";
83
+ putlog "Attempting lock statement";
84
+ run;
85
+
86
+ lock &libds;
87
+
88
+ %local abortme;
89
+ %let abortme=0;
90
+ %if &syscc>0 or &SYSLCKRC ne 0 %then %do;
91
+ %let msg=Unable to apply lock on &libds (SYSLCKRC=&SYSLCKRC syscc=&syscc);
92
+ %put %str(ERR)OR: &sysmacroname: &msg;
93
+ %let abortme=1;
94
+ %end;
95
+
96
+ lock &libds clear;
97
+
98
+ %mp_abort(iftrue= (&abortme=1)
99
+ ,mac=&sysmacroname
100
+ ,msg=%superq(msg)
101
+ )
102
+
103
+ %mend mp_lockfilecheck;