Kør operativsystem-kommandoer fra SQL med dbms_os

dbms_os-pakken kører et program fra inden for Invantive SQL og returnerer dets exit-kode, standard output og standard fejl som kolonner, sammen med funktioner til at læse, skrive, liste og hashe filer.

Tilgængelighed

Pakken er tilgængelig fra og med version 27.0 på produkter, der kører for en enkelt bruger i brugerens egen kontekst:

  • Invantive Query Tool
  • Invantive Data Hub
  • Invantive Control for Excel
  • Invantive Composition for Word

Operativsystemadgang

Invantive Script tilbød allerede local host til at starte et program:

local host "cmd.exe" "/c dotnet build" "c:\folder" "600000"

Exit-koden og output lander i script-variablerne local:hostexitcode, local:hoststdout og local:hoststderr.

For at tillade, at PSQL-logik forgrener sig på disse ved hjælp af tabel-funktionen dbms_os.run, returneres en række med resultaterne:

  • EXIT_CODE: exit-kode, tom når kommandoen blev afsluttet efter at have overskredet sin maksimale varighed.
  • TIMED_OUT: flag om timeout er sket.
  • STDOUT og STDERR: output og fejl-output, afkortet til 100 MB.
  • STDOUT_TRUNCATED og STDERR_TRUNCATED: flag om output blev afkortet.
  • DURATION_MS: varighed i millisekunder.
  • CPU_MS: CPU-brug i millisekunder.
  • PROCESS_ID: operativsystems proces-ID.

For at fejle ved en fejl, sendes raise_on_error => true til run.

Tre yderligere forskelle med Invantive Script er:

  • Ingen skal involvering: programmet startes direkte, så |, >, && og *.txt tolkes ikke. En kommandolinje, der har brug for dem, køres eksplicit gennem cmd.exe /c, præcis som før.
  • Argumenter citeret hvor nødvendigt: udover arguments, som tilføjes til kommandolinjen som den står, accepterer run argument1 op til argument10. Hvert af disse ankommer til programmet som præcis ét argument, selv når det indeholder et mellemrum eller en bagvedliggende skråstreg. For en kommandolinje sammensat ved køretid, gør funktionen dbms_os.quote_argument det muligt at citere.
  • Timeout afslutter procestre: en kommando, der overgår sin maksimale varighed, afsluttes sammen med alt, hvad den har startet.

Udover run, indeholder pakken read_file, read_file_text, write_file, write_file_text, file_exists, file_size, file_modified_utc, file_hash, copy_file, move_file, delete_file, create_directory, directory_exists, list_files, list_directories, which, get_env, path_combine, path_full, open_file og open_url.

De fleste af disse pakkeprocedurer og funktioner har en tilsvarende i os-tjenesteudbyderen.

Forespørgselsversion af program

Versionen af Git-klienten på arbejdsstationen, som data:

select rce.exit_code
,      rce.stdout
,      rce.duration_ms
from   table(dbms_os.run('git.exe', null, 10000, null, false, '--version')) rce

Parametrene efter programmet er arbejdsmappen, den maksimale varighed i millisekunder, teksten til standardinput, om der skal rejses en fejl ved en ikke-nul exit-kode, og kommandolinjen. Se billede nedenfor:

Et program, der ikke er på søgestien, navngives med sin fulde sti, og dbms_os.which('git.exe') viser, hvilken der ville blive fundet.

Revisionssikret PDF/A-arkiv af udgående fakturaer

En revisionspraksis opbevarer salgsfakturaer, som den har sendt for den syvårige opbevaringspligt. Fakturaerne forlader bogføringsprogrammet som PDF til en udbakke-mappe. Arkivet skal opbevare dem som PDF/A pr. år og måned med bevis for, at det arkiverede dokument er det, der blev sendt. Beviset er en hash pr. fil, registreret på arkiveringstidspunktet, så en senere sammenligning kan vise, om noget er ændret.

Rutinen nedenfor kører nattetid i Invantive Data Hub. Mappenavnene kommer fra Invantive Script-variabler.

local define OUTBOX_PATH "c:\acme\outbox"

local define ARCHIVE_PATH "c:\acme\archive"

local define GHOSTSCRIPT_PATH "c:\Program Files\gs\gs10.20.30\bin\gswin64c.exe"

create or replace table archive_log@InMemoryStorage
( file_name        varchar2
, archived_path    varchar2
, bytes            int64
, sha256           varchar2
, exit_code        int32
, error_message    varchar2
)

declare
  l_archive_month varchar2;
  l_target_path   varchar2;
  l_exit_code     int32;
  l_stderr        varchar2;
  l_bytes         int64;
  l_sha256        varchar2;
begin
  --
  -- En mappe pr. år og måned, så en opbevaringsperiode kan frigives som en helhed.
  --
  l_archive_month := dbms_os.path_combine
                     ( '${ARCHIVE_PATH}'
                     , to_char(sysdateutc, 'YYYY')
                     , to_char(sysdateutc, 'MM')
                     );
  dbms_os.create_directory(l_archive_month);
  --
  for r in
  ( select fle.file_path
    ,      fle.file_name
    from   table(dbms_os.list_files('${OUTBOX_PATH}', '*.pdf')) fle
  )
  loop
    l_target_path := dbms_os.path_combine(l_archive_month, r.file_name);
    --
    -- Ghostscript konverterer til PDF/A-2b. Hvert argument overføres enkeltvis, så et
    -- virksomhedsnavn med et mellemrum i stien ikke kan dele kommandolinjen.
    --
    select rce.exit_code
    ,      rce.stderr
    into   l_exit_code
    ,      l_stderr
    from   table
           ( dbms_os.run
             ( '${GHOSTSCRIPT_PATH}'
             , null
             , 300000
             , null
             , false
             , '-dPDFA=2 -dPDFACompatibilityPolicy=1 -dBATCH -dNOPAUSE -sColorConversionStrategy=UseDeviceIndependentColor -sDEVICE=pdfwrite'
             , '-sOutputFile=' || l_target_path
             , r.file_path
             )
           ) rce
    ;
    --
    -- Hash-værdien tages fra den arkiverede fil frem for den originale.
    --
    if l_exit_code = 0
    then
      l_bytes  := dbms_os.file_size(l_target_path);
      l_sha256 := dbms_os.file_hash(l_target_path);
    else
      l_bytes  := null;
      l_sha256 := null;
    end if;
    --
    insert into archive_log@InMemoryStorage
    ( file_name
    , archived_path
    , bytes
    , sha256
    , exit_code
    , error_message
    )
    values
    ( r.file_name
    , case when l_exit_code = 0 then l_target_path else null end
    , l_bytes
    , l_sha256
    , l_exit_code
    , l_stderr
    );
    --
    -- En konverteret faktura forlader udbakken. En mislykket forbliver, hvor den er, med sin
    -- årsag i loggen til at blive opfanget ved næste kørsel.
    --
    if l_exit_code = 0
    then
      dbms_os.delete_file(r.file_path);
    end if;
  end loop;
end;

--
-- Oversigt.
--
select ale.file_name
,      ale.bytes
,      ale.sha256
,      ale.error_message
from   archive_log@InMemoryStorage ale
where  ale.exit_code != 0
order
by     ale.file_name

Se også: dbms_os

Se også: local host statement

Se også: operativsystemudbyder