Pages

SyntaxHighlighter

Showing posts with label uuidgen. Show all posts
Showing posts with label uuidgen. Show all posts

Saturday, April 25, 2020

Lock Release

Sometimes a SAS data set can get locked such that you can read it but no longer update it. Of course, it is best to attempt to find out who is holding the lock and on simple systems you can use things like the UNIX fuser command to find the process id and then kill that process. However, things are not that easy on a SAS grid environment with many possible servers.

One way to free up the file is to move the data set (and its index file) to another folder then move it back to release the pointer. The below macro makes it very easy to accomplish those actions. The only required argument is the data set name (dsn). The libref is used to obtain the path via the pathname function. A unique sub-folder is created by using the UUIDGEN() function.

The advantage of this technique versus an X command is that it does not rely on the XCMD setting and provides a return code to verify things behaved as expected. You can read thru the below code to see how this is all handled. The %getattr() macro is simply a wrapper of the ATTRN() function. The source code for the %movefile(), %copyfile() and deletefile() macros can be found here.


/*******************************************************************************
     Program: lockrelease.sas
      Author: Tom Bellmer
     Created: 04/25/2020 @ 8:17:58 PM
 SAS Version: SAS 9.4 (TS1M3)
          OS: LIN X64
     Purpose: Moves data set (and index) to sub-folder then back.  This is 
              needed as data sets get locked and this releases it
       Usage: %let x = %lockrelease(dsn = mylib.mymem);
       Notes: Better to see if anyone has file locked but if no other option
              then use this.  It works by changing the file pointer.  

                             Modifications in descending order
FL-YYYYMMDD                             Description
----------- --------------------------------------------------------------------

         1    1    2    2    3    3    4    4    5    5    6    6    7    7    8
....5....0....5....0....5....0....5....0....5....0....5....0....5....0....5....0
*******************************************************************************/

%macro lockrelease(dsn = );
  %local
    libref 
    path
    memname
    guid
    starttime
    temppath
    indexed
    rc
  ;
  %let starttime = %sysfunc(datetime());

  %if not %sysfunc(exist(&dsn)) %then %do;
    %put %str(E)RROR: The dsn: &dsn does not exist;
    %return;
  %end;

  %if not %index(&dsn, .) %then %do;
    %put %str(E)RROR: This only supports two level (permanent) data sets;
    %return;
  %end;

  %let libref   = %scan(&dsn, 1, .);
  %let memname  = %scan(&dsn, 2, .);
  %let guid     = %sysfunc(uuidgen());
  %let path     = %sysfunc(pathname(&libref));
  %let temppath = %sysfunc(dcreate(&guid, &path));

  %if %isblank(&temppath) %then %do;
    %put %str(E)RROR: Unable to create sub-folder.;
    %return;
  %end;
  %else %do;
    %let indexed = %getattr(dsn = &dsn, attr = isindex);

    /********   Move dataset (and index) to sub-folder   *********************/
    %let rc = %movefile(infile = &path/&memname..sas7bdat
                 , outfile = &temppath/&memname..sas7bdat);
    %if &indexed and &rc %then %do;
      %let rc = %movefile(infile = &path/&memname..sas7bndx
                 , outfile = &temppath/&memname..sas7bndx);
    %end; 

    /*****   Move dataset (and index) from sub-folder back to original   *****/
    %if &rc %then %do;
      %let rc = %movefile(infile = &temppath/&memname..sas7bdat
                          , outfile = &path/&memname..sas7bdat);
      %if &indexed and &rc %then %do;
        %let rc = %movefile(infile = &temppath/&memname..sas7bndx
                    , outfile = &path/&memname..sas7bndx);
      %end; 
    %end;

    %if &rc %then %let rc = %deletefile(&temppath);    
  %end;

  %put %str(N)OTE Elapsed Time: %left(%sysfunc(putn(%sysevalf(%sysfunc(
     datetime()) - &starttime), mmss12.3)));

  &rc
%mend;

/*EOF: lockrelease.sas */

Sunday, July 9, 2017

Random Libref macro function

Certain SAS procedures such as PROC DATASETS show examples with fixed library references or librefs such as source and dest as follows:

   proc datasets library = source ;
      copy out = dest ;
   run ;

A libref is 1 to 8 characters long, begins with a letter or underscore and contains only letters, numbers or underscores. If you use the SAS dictionary tables you will see that all libnames are stored in upper case.

What if the popularly named librefs of SORUCE and/or DEST are already in use and are not defined in metadata? Since a libref is simply a pointer to a folder (used by Windows and Linux), why not use a random value assigned to a macro variable to avoid any conflicts? The below macro function named %uuidgenlibref() does just that.

The SAS UUIDGEN() function returns a universally unique identifier (UUID) also know as a globally unique identifier (GUID) that looks like this: 83a6f563-67ee-2a47-8498-8f8dc706d0db. As you can see, the first character starts with an 8 which would violate the libref requirement to start with a letter. Here is an example of generating 10 UUIDs from a DO loop:

According to Wikipedia, "In its canonical textual representation, the sixteen octets of a UUID are represented as 32 hexadecimal (base 16) digits, displayed in five groups separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters (32 alphanumeric characters and four hyphens)."

The macro appends the first seven characters from the UUIDGEN() function using the SUBSTR() function to the 'X' prefix. Here is an example:

   %let sourcelibref = %uuidgenlibref() ;
   %let destlibref = %uuidgenlibref() ;

   libname &sourcelibref. "/lev1/abc" ;
   libname &destlibref. "/lev1/def" ;
 
   proc datasets library = &sourcelibref. ;
      copy out = &destlibref. ;
   run ;

   libanme &sourcelibref. clear ;
   libname &destlibref. clear ;   

The source consists of a single line of code as you can see below.

/**************************************************************************
*     Program: uuidgenlibref.sas
*      Author: Tom Bellmer
*     Created: 09Jul2017
*     Purpose: Creates an upper case 8 character libref
*       Usage: %put %uuidgenlibref() ;
**************************************************************************/
%macro uuidgenlibref() ; 
   X%upcase( %substr( %sysfunc( uuidgen() ), 1, 7 ) ) 
%mend ; 
 
/*EOF: uuidgenlibref.sas */