# publicare_scripturi.ps1 - publica scripturile de migrare .sql catre clientii ROA. # Doi pasi, exact ca formularul din tasks.exe (clase\generare_script.vcx): # 1. import - scrie fiecare .sql in CONTAFIN_ORACLE.UPD_DATABASE de pe ROA_CENTRAL # (script_order = 0, un rand = un script intreg); daca script_name exista deja, # face update pe id, ca sa se republice continutul corectat; # 2. arhiva - regenereaza database_.zip / databasen_.zip si indexii # roa_database.xml / roa_databasen.xml in Y:\ROAUPDATE\_UPDATE. # Continutul din arhiva se citeste INAPOI din baza, nu de pe disc, si se compara octet cu octet cu # fisierul sursa - deci arhiva contine exact ce vor primi clientii. Optional (implicit pornit), # arhiva generata se citeste inapoi prin zip_util_pkg, cititorul Oracle al clientului. # # Configurarea se citeste din settings.ini-ul lui tasks.exe (aceleasi chei pe care le foloseste # formularul): [connection] host_database/username_database/password_database, [folder] # script_folder (\SCRIPTURI\ -> \SCRIPTURI_CLAR\), roa_output (+ _UPDATE) si sqlplus_exe, [script] # prefixele valide. Aliasul TNS se ia din DSN-ul ODBC numit in host_database (cheia ServerName din # registri). Orice parametru dat explicit are prioritate fata de ini. # # Utilizare: # powershell -File COMUN\utile\publicare_scripturi.ps1 -DryRun # powershell -File COMUN\utile\publicare_scripturi.ps1 -Fisier a.sql,b.sql # powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 # powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 -DoarArhiva param( [string[]]$Fisier, [string]$Luna, [string]$Settings = 'D:\PROIECTE\fox\tasks\settings.ini', [string]$DirScripturi, [string]$ZipPath, [string]$BackupPath, [switch]$DoarImport, [switch]$DoarArhiva, [switch]$Toate, [switch]$FaraVerificareZip, [switch]$DryRun, [switch]$PastreazaTemp, [string]$Alias, [string]$User, [string]$Password, [string]$SqlPlus ) $ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.IO.Compression.FileSystem # octeti per bucata trimisa/citita ca hexazecimal (2 caractere per octet, sub limita de linie sqlplus) $BUCATA = 900 # bucati per bloc PL/SQL (sursa unui bloc trebuie sa ramana sub ~64k) $BUCATI_PER_BLOC = 15 function Scrie-Titlu { param([string]$Text) Write-Output '' Write-Output ('===== ' + $Text + ' =====') } function Opreste { param([string]$Mesaj) Write-Output '' Write-Output ('EROARE: ' + $Mesaj) Curata-Temp exit 1 } function Curata-Temp { if ($PastreazaTemp) { if (Test-Path $TempDir) { Write-Output ('Temp pastrat: ' + $TempDir) } return } if (Test-Path $TempDir) { Remove-Item -LiteralPath $TempDir -Recurse -Force -ErrorAction SilentlyContinue } } function Invoke-SqlPlus { # ruleaza un script sqlplus; intoarce textul si codul de iesire (whenever sqlerror => cod <> 0) param([string]$Sql, [string]$Nume = 'q') $f = Join-Path $TempDir ($Nume + '_' + [guid]::NewGuid().ToString('N') + '.sql') $antet = "whenever sqlerror exit failure rollback`r`nwhenever oserror exit failure`r`nset define off`r`nset feedback off`r`n" [System.IO.File]::WriteAllText($f, ($antet + $Sql), [System.Text.Encoding]::ASCII) try { $out = & $SqlPlus -L -S $ConnStr ('@' + $f) 2>&1 $cod = $LASTEXITCODE } catch { $out = 'EROARE lansare sqlplus: ' + $_.Exception.Message $cod = -1 } if (-not $PastreazaTemp) { Remove-Item -LiteralPath $f -Force -ErrorAction SilentlyContinue } $text = ($out | ForEach-Object { [string]$_ }) -join "`n" [PSCustomObject]@{ Text = $text; Cod = $cod } } function Test-Rezultat { param($R, [string]$Context) if ($R.Cod -ne 0 -or $R.Text -match 'ORA-\d{5}|SP2-\d{4}|PLS-\d{5}') { Write-Output $R.Text Opreste ($Context + ' - sqlplus a raportat eroare (cod ' + $R.Cod + ').') } } function ConvertTo-Hex { param([byte[]]$Octeti) if ($Octeti.Length -eq 0) { return '' } [System.BitConverter]::ToString($Octeti).Replace('-', '') } function ConvertFrom-Hex { param([string]$Hex) $n = [int]($Hex.Length / 2) $b = New-Object byte[] $n for ($i = 0; $i -lt $n; $i++) { $b[$i] = [Convert]::ToByte($Hex.Substring($i * 2, 2), 16) } ,$b } function Citeste-Ini { param([string]$Cale) $ini = @{} $sectiune = '' foreach ($linie in [System.IO.File]::ReadAllLines($Cale, [System.Text.Encoding]::GetEncoding(1252))) { $linie = $linie.Trim() if (-not $linie -or $linie.StartsWith(';') -or $linie.StartsWith('#')) { continue } if ($linie -match '^\[(.+)\]$') { $sectiune = $Matches[1].ToLower() if (-not $ini.ContainsKey($sectiune)) { $ini[$sectiune] = @{} } continue } $p = $linie.IndexOf('=') if ($p -lt 1 -or -not $sectiune) { continue } $ini[$sectiune][$linie.Substring(0, $p).Trim().ToLower()] = $linie.Substring($p + 1).Trim() } $ini } function Ia-Cheie { param($Ini, [string]$Sectiune, [string]$Cheie) $s = $Sectiune.ToLower() $c = $Cheie.ToLower() if ($Ini.ContainsKey($s) -and $Ini[$s].ContainsKey($c)) { return $Ini[$s][$c] } '' } function Rezolva-Alias { # numele din host_database e un DSN ODBC; aliasul TNS e in cheia ServerName a DSN-ului param([string]$Dsn) foreach ($radacina in @('HKCU:\SOFTWARE\ODBC\ODBC.INI', 'HKLM:\SOFTWARE\ODBC\ODBC.INI', 'HKLM:\SOFTWARE\WOW6432Node\ODBC\ODBC.INI')) { $cale = Join-Path $radacina $Dsn if (Test-Path $cale) { $v = (Get-ItemProperty -Path $cale -Name 'ServerName' -ErrorAction SilentlyContinue).ServerName if ($v) { return $v } } } # nu e DSN ODBC - il tratam ca alias TNS direct $Dsn } function Compara-Octeti { param([byte[]]$A, [byte[]]$B) if ($null -eq $A -or $null -eq $B) { return $false } if ($A.Length -ne $B.Length) { return $false } for ($i = 0; $i -lt $A.Length; $i++) { if ($A[$i] -ne $B[$i]) { return $false } } $true } function Split-Hex { # taie un sir hexazecimal in bucati de $BUCATA octeti param([string]$Hex) $l = New-Object System.Collections.ArrayList $pas = $BUCATA * 2 for ($i = 0; $i -lt $Hex.Length; $i += $pas) { [void]$l.Add($Hex.Substring($i, [Math]::Min($pas, $Hex.Length - $i))) } ,$l } function Parseaza-NumeScript { # _YYYY_MM_DD_NN_.sql -> obiect cu elementele componente (ca parsescriptname din VFP) param([string]$Cale) $stem = [System.IO.Path]::GetFileNameWithoutExtension($Cale) if ($stem -notmatch '^([A-Za-z]+)_(\d{4})_(\d{2})_(\d{2})_(\d+)(?:_(.+))?$') { return $null } $an = [int]$Matches[2]; $luna = [int]$Matches[3]; $zi = [int]$Matches[4] try { $data = Get-Date -Year $an -Month $luna -Day $zi -Hour 0 -Minute 0 -Second 0 } catch { return $null } $tip = '' if ($Matches[6]) { $tip = $Matches[6].ToUpper() } [PSCustomObject]@{ Cale = $Cale NumeScript = ($stem.ToUpper() + '.SQL') Stem = $stem.ToUpper() Prefix = $Matches[1].ToUpper() Data = $data DataYmd = $data.ToString('yyyyMMdd') Secventa = [int]$Matches[5] Tip = $tip } } function Citeste-MetadateLuna { # metadatele scripturilor intregi (script_order = 0) din luna, in ordinea din arhiva param([datetime]$Inceput, [datetime]$Sfarsit) $sql = @" set pagesize 0 linesize 600 trimspool on heading off select 'M|' || id || '|' || script_name || '|' || script_type || '|' || to_char(script_date,'yyyymmdd') || '|' || to_char(script_seq) || '|' || to_char(script_order) || '|' || nvl(script_appver,'') || '|' || nvl(length(script_content),0) from upd_database where script_order = 0 and script_date between to_date('$($Inceput.ToString('yyyyMMdd'))','yyyymmdd') and to_date('$($Sfarsit.ToString('yyyyMMdd'))','yyyymmdd') order by script_name, id; exit "@ $r = Invoke-SqlPlus -Sql $sql -Nume 'meta' Test-Rezultat -R $r -Context 'citire metadate din UPD_DATABASE' $l = New-Object System.Collections.ArrayList foreach ($linie in ($r.Text -split "`r?`n")) { $linie = $linie.Trim() if (-not $linie.StartsWith('M|')) { continue } $c = $linie.Substring(2) -split '\|' if ($c.Count -lt 8) { continue } [void]$l.Add([PSCustomObject]@{ Id = [int]$c[0] NumeScript = $c[1].Trim() Stem = [System.IO.Path]::GetFileNameWithoutExtension($c[1].Trim()) Tip = $c[2].Trim() DataYmd = $c[3].Trim() Secventa = [int]$c[4] Ordine = [int]$c[5] AppVer = $c[6].Trim() Lungime = [int]$c[7] }) } ,$l } function Citeste-Continut { # citeste inapoi CLOB-urile ca octeti CP1252 (hexazecimal), pentru toate id-urile date param($Metadate) $rezultat = @{} if ($Metadate.Count -eq 0) { return $rezultat } $maxBucati = 1 foreach ($m in $Metadate) { $rezultat[$m.Id] = New-Object System.Text.StringBuilder $b = [Math]::Ceiling($m.Lungime / $BUCATA) if ($b -gt $maxBucati) { $maxBucati = [int]$b } } $toateId = @($Metadate | ForEach-Object { $_.Id }) $grupuri = New-Object System.Collections.ArrayList for ($i = 0; $i -lt $toateId.Count; $i += 500) { [void]$grupuri.Add('c.id in (' + (($toateId[$i..([Math]::Min($i + 499, $toateId.Count - 1))]) -join ',') + ')') } $ids = $grupuri -join ' or ' $sql = @" set pagesize 0 linesize 32767 long 2000000000 longchunksize 32767 trimspool on heading off arraysize 100 select '#' || c.id || '#' || lpad(to_char(l.n),7,'0') || '#' || rawtohex(utl_i18n.string_to_raw(dbms_lob.substr(c.script_content, $BUCATA, (l.n-1)*$BUCATA+1), 'WE8MSWIN1252')) from upd_database c, (select level n from dual connect by level <= $maxBucati) l where ($ids) and (l.n-1)*$BUCATA < nvl(length(c.script_content),0) order by c.id, l.n; exit "@ $r = Invoke-SqlPlus -Sql $sql -Nume 'citire' Test-Rezultat -R $r -Context 'citire continut scripturi din UPD_DATABASE' foreach ($linie in ($r.Text -split "`r?`n")) { $linie = $linie.Trim() if (-not $linie.StartsWith('#')) { continue } $c = $linie.Substring(1) -split '#', 3 if ($c.Count -lt 3) { continue } $id = [int]$c[0] if ($rezultat.ContainsKey($id)) { [void]$rezultat[$id].Append($c[2]) } } $final = @{} foreach ($k in $rezultat.Keys) { $final[$k] = ConvertFrom-Hex $rezultat[$k].ToString() } $final } function Importa-Script { # insert sau update pe script_name existent; continutul se trimite hexazecimal, bucata cu bucata param($Info, [byte[]]$Octeti) $hex = ConvertTo-Hex $Octeti $bucati = Split-Hex $hex $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine('set serveroutput on size 1000000') [void]$sb.AppendLine('variable b_id number') [void]$sb.AppendLine('declare') [void]$sb.AppendLine(' l_id number;') [void]$sb.AppendLine('begin') [void]$sb.AppendLine(" select max(id) into l_id from upd_database where script_name = '$($Info.NumeScript)';") [void]$sb.AppendLine(' if l_id is null then') [void]$sb.AppendLine(' insert into upd_database') [void]$sb.AppendLine(' (script_name, script_type, script_date, script_seq, script_content, script_order, script_appver)') [void]$sb.AppendLine(" values ('$($Info.NumeScript)', '$($Info.Tip)', to_date('$($Info.DataYmd)','yyyymmdd'), $($Info.Secventa), empty_clob(), 0, null)") [void]$sb.AppendLine(' returning id into l_id;') [void]$sb.AppendLine(' else') [void]$sb.AppendLine(' update upd_database') [void]$sb.AppendLine(" set script_type = '$($Info.Tip)',") [void]$sb.AppendLine(" script_date = to_date('$($Info.DataYmd)','yyyymmdd'),") [void]$sb.AppendLine(" script_seq = $($Info.Secventa),") [void]$sb.AppendLine(' script_content = empty_clob(),') [void]$sb.AppendLine(' script_order = 0,') [void]$sb.AppendLine(' script_appver = null') [void]$sb.AppendLine(' where id = l_id;') [void]$sb.AppendLine(' end if;') [void]$sb.AppendLine(' :b_id := l_id;') [void]$sb.AppendLine(' commit;') [void]$sb.AppendLine(" dbms_output.put_line('IDSCRIPT=' || l_id);") [void]$sb.AppendLine('end;') [void]$sb.AppendLine('/') for ($i = 0; $i -lt $bucati.Count; $i += $BUCATI_PER_BLOC) { [void]$sb.AppendLine('declare s varchar2(32767); l clob;') [void]$sb.AppendLine('begin') [void]$sb.AppendLine(' select script_content into l from upd_database where id = :b_id for update;') for ($j = $i; $j -lt [Math]::Min($i + $BUCATI_PER_BLOC, $bucati.Count); $j++) { [void]$sb.AppendLine(" s := utl_i18n.raw_to_char(hextoraw('$($bucati[$j])'),'WE8MSWIN1252'); dbms_lob.writeappend(l, length(s), s);") } [void]$sb.AppendLine(' commit;') [void]$sb.AppendLine('end;') [void]$sb.AppendLine('/') } [void]$sb.AppendLine('exit') $r = Invoke-SqlPlus -Sql $sb.ToString() -Nume ('imp_' + $Info.Stem) Test-Rezultat -R $r -Context ('import ' + $Info.NumeScript) if ($r.Text -notmatch 'IDSCRIPT=(\d+)') { Opreste ('import ' + $Info.NumeScript + ' - nu am primit id-ul randului.') } [int]$Matches[1] } function Scrie-XmlArhiva { # acelasi format ca CURSORTOXML din genereazadatabasexml2: Windows-1252, CRLF, tab-uri, # script_appver doar cand e completat, script_content gol (continutul e in fisierul separat) param($Metadate, [string]$Cale) $sb = New-Object System.Text.StringBuilder [void]$sb.Append("`r`n") [void]$sb.Append("`r`n") foreach ($m in $Metadate) { [void]$sb.Append("`t`r`n") [void]$sb.Append("`t`t$($m.Id)`r`n") [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.NumeScript))`r`n") [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.Tip))`r`n") [void]$sb.Append("`t`t$($m.DataYmd)`r`n") [void]$sb.Append("`t`t$($m.Secventa)`r`n") [void]$sb.Append("`t`t$($m.Ordine)`r`n") if ($m.AppVer) { [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.AppVer))`r`n") } [void]$sb.Append("`t`t$($m.Stem).sql`r`n") [void]$sb.Append("`t`t`r`n") [void]$sb.Append("`t`r`n") } [void]$sb.Append("`r`n") [System.IO.File]::WriteAllText($Cale, $sb.ToString(), [System.Text.Encoding]::GetEncoding(1252)) } function Scrie-Arhiva { # xml-ul primul, apoi scripturile in ordinea din xml (ca ZipOpen/ZipFile din VFP) param([string]$CaleZip, [string]$CaleXml, $Fisiere) if (Test-Path $CaleZip) { Remove-Item -LiteralPath $CaleZip -Force } $z = [System.IO.Compression.ZipFile]::Open($CaleZip, 'Create') try { [void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($z, $CaleXml, [System.IO.Path]::GetFileName($CaleXml), 'Optimal') foreach ($f in $Fisiere) { [void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($z, $f, [System.IO.Path]::GetFileName($f), 'Optimal') } } finally { $z.Dispose() } } function Scrie-IndexArhive { # roa_database.xml / roa_databasen.xml - lista arhivelor existente, sortata dupa nume de fisier param([string]$Prefix, [string]$Cale) $arhive = @(Get-ChildItem -LiteralPath $ZipPath -Filter ($Prefix + '_*.zip') -File | Where-Object { $_.Name -like ($Prefix + '_*.zip') } | Sort-Object Name) $sb = New-Object System.Text.StringBuilder [void]$sb.Append("`r`n") [void]$sb.Append("`r`n") foreach ($a in $arhive) { $parti = [System.IO.Path]::GetFileNameWithoutExtension($a.Name) -split '_' if ($parti.Count -lt 3) { continue } [void]$sb.Append("`t`r`n") [void]$sb.Append("`t`t|BASEPATH|/$($a.Name)`r`n") [void]$sb.Append("`t`t$($parti[1])`r`n") [void]$sb.Append("`t`t$($parti[2])`r`n") [void]$sb.Append("`t`r`n") } [void]$sb.Append("`r`n") [System.IO.File]::WriteAllText($Cale, $sb.ToString(), [System.Text.Encoding]::GetEncoding(1252)) } function Verifica-ArhivaPrinOracle { # citeste arhiva inapoi cu cititorul clientului (pack_utils.decodebase64 + zip_util_pkg.get_file) # si compara lungimea + MD5 al fiecarui fisier extras cu cel de pe disc param([string]$CaleZip, $FisiereAsteptate) $octeti = [System.IO.File]::ReadAllBytes($CaleZip) $b64 = [Convert]::ToBase64String($octeti) $sb = New-Object System.Text.StringBuilder [void]$sb.AppendLine('set serveroutput on size unlimited') [void]$sb.AppendLine('set pagesize 0 linesize 400 trimspool on heading off') [void]$sb.AppendLine('variable b_z clob') [void]$sb.AppendLine('begin dbms_lob.createtemporary(:b_z, true); end;') [void]$sb.AppendLine('/') # bucati multiplu de 4, ca sirul base64 sa ramana valid oriunde s-ar taia $pas = 1920 for ($i = 0; $i -lt $b64.Length; $i += $pas * $BUCATI_PER_BLOC) { [void]$sb.AppendLine('declare s varchar2(32767);') [void]$sb.AppendLine('begin') for ($j = $i; $j -lt [Math]::Min($i + $pas * $BUCATI_PER_BLOC, $b64.Length); $j += $pas) { $bucata = $b64.Substring($j, [Math]::Min($pas, $b64.Length - $j)) [void]$sb.AppendLine(" s := '$bucata'; dbms_lob.writeappend(:b_z, length(s), s);") } [void]$sb.AppendLine('end;') [void]$sb.AppendLine('/') } [void]$sb.AppendLine('declare') [void]$sb.AppendLine(' l_zip blob; l_f blob;') [void]$sb.AppendLine('begin') [void]$sb.AppendLine(' l_zip := pack_utils.decodebase64(:b_z);') [void]$sb.AppendLine(" dbms_output.put_line('ZIP|' || dbms_lob.getlength(l_zip));") foreach ($f in $FisiereAsteptate) { [void]$sb.AppendLine(" l_f := zip_util_pkg.get_file(l_zip, '$f');") [void]$sb.AppendLine(" if l_f is null then dbms_output.put_line('LIPSA|$f');") [void]$sb.AppendLine(" else dbms_output.put_line('FIS|$f|' || dbms_lob.getlength(l_f) || '|' || rawtohex(dbms_crypto.hash(l_f, 2))); end if;") } [void]$sb.AppendLine('end;') [void]$sb.AppendLine('/') [void]$sb.AppendLine('exit') $r = Invoke-SqlPlus -Sql $sb.ToString() -Nume 'verifzip' Test-Rezultat -R $r -Context ('verificare arhiva ' + [System.IO.Path]::GetFileName($CaleZip)) $citite = @{} foreach ($linie in ($r.Text -split "`r?`n")) { $linie = $linie.Trim() if ($linie -match '^FIS\|([^|]+)\|(\d+)\|([0-9A-F]+)$') { $citite[$Matches[1]] = [PSCustomObject]@{ Lungime = [int]$Matches[2]; Md5 = $Matches[3] } } elseif ($linie -match '^LIPSA\|(.+)$') { Opreste ('verificare arhiva - zip_util_pkg nu gaseste ' + $Matches[1] + ' in ' + $CaleZip) } } $md5 = [System.Security.Cryptography.MD5]::Create() $erori = 0 foreach ($f in $FisiereAsteptate) { if (-not $citite.ContainsKey($f)) { Write-Output (' LIPSA ' + $f); $erori++; continue } $local = Join-Path $TempDir $f $b = [System.IO.File]::ReadAllBytes($local) $h = (ConvertTo-Hex $md5.ComputeHash($b)) if ($citite[$f].Lungime -eq $b.Length -and $citite[$f].Md5 -eq $h) { Write-Output (' ok ' + $f + ' (' + $b.Length + ' octeti)') } else { Write-Output (' DIFERIT ' + $f + ' - disc ' + $b.Length + '/' + $h + ', extras ' + $citite[$f].Lungime + '/' + $citite[$f].Md5) $erori++ } } $md5.Dispose() if ($erori -gt 0) { Opreste ('verificare arhiva - ' + $erori + ' fisiere nu se citesc corect prin zip_util_pkg.') } } # ---------- pregatire ---------- $TempDir = Join-Path $env:TEMP ('publicare_scripturi_' + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Force -Path $TempDir | Out-Null if (-not (Test-Path -LiteralPath $Settings)) { Opreste ('nu gasesc settings.ini la ' + $Settings + ' - da caile cu -DirScripturi/-ZipPath/-Alias/-User/-Password sau alt -Settings.') } $ini = Citeste-Ini $Settings # parametrii dati explicit bat ini-ul if (-not $PSBoundParameters.ContainsKey('User')) { $User = Ia-Cheie $ini 'connection' 'username_database' } if (-not $PSBoundParameters.ContainsKey('Password')) { $Password = Ia-Cheie $ini 'connection' 'password_database' } if (-not $PSBoundParameters.ContainsKey('Alias')) { $dsn = Ia-Cheie $ini 'connection' 'host_database' if ($dsn) { $Alias = Rezolva-Alias $dsn } } if (-not $PSBoundParameters.ContainsKey('DirScripturi')) { $f = Ia-Cheie $ini 'folder' 'script_folder' if ($f) { $DirScripturi = ($f.TrimEnd('\') -replace '\\SCRIPTURI$', '\SCRIPTURI_CLAR') } } if (-not $PSBoundParameters.ContainsKey('ZipPath')) { $f = Ia-Cheie $ini 'folder' 'roa_output' if ($f) { $ZipPath = (Join-Path $f '_UPDATE') } } if (-not $PSBoundParameters.ContainsKey('SqlPlus')) { $SqlPlus = Ia-Cheie $ini 'folder' 'sqlplus_exe' } foreach ($p in @(@('User', $User), @('Password', $Password), @('Alias', $Alias), @('DirScripturi', $DirScripturi), @('ZipPath', $ZipPath), @('SqlPlus', $SqlPlus))) { if (-not $p[1]) { Opreste ('lipseste ' + $p[0] + ' - nu e in ' + $Settings + ' si nu a fost dat ca parametru.') } } # prefixele valide (o schema tinta per prefix), pentru validarea numelor de scripturi $prefixe = @() if ($ini.ContainsKey('script')) { $prefixe = @($ini['script'].Values | ForEach-Object { $_.TrimEnd('_').ToUpper() }) } if (-not (Test-Path $SqlPlus)) { Opreste ('nu gasesc sqlplus la ' + $SqlPlus) } $env:TNS_ADMIN = Split-Path $SqlPlus -Parent $ConnStr = $User + '/' + $Password + '@' + $Alias if ($DoarImport -and $DoarArhiva) { Opreste '-DoarImport si -DoarArhiva se exclud reciproc.' } # ---------- 1. ce se publica ---------- Scrie-Titlu '1. SCRIPTURI DE PUBLICAT' $candidati = New-Object System.Collections.ArrayList if ($Fisier) { foreach ($f in $Fisier) { $cale = $f if (-not [System.IO.Path]::IsPathRooted($cale)) { $cale = (Resolve-Path -LiteralPath $f -ErrorAction SilentlyContinue) if (-not $cale) { Opreste ('nu gasesc fisierul ' + $f) } $cale = $cale.Path } if (-not (Test-Path -LiteralPath $cale)) { Opreste ('nu gasesc fisierul ' + $cale) } $info = Parseaza-NumeScript $cale if (-not $info) { Opreste ('nume de script neconform (_YYYY_MM_DD_NN_.sql): ' + $cale) } [void]$candidati.Add($info) } $luni = @($candidati | ForEach-Object { $_.Data.ToString('yyyy-MM') } | Sort-Object -Unique) if ($luni.Count -gt 1) { Opreste ('scripturile date sunt din luni diferite (' + ($luni -join ', ') + ') - ruleaza cate o luna.') } if (-not $Luna) { $Luna = $luni[0] } } else { if (-not $Luna) { $Luna = (Get-Date).ToString('yyyy-MM') } } if ($Luna -notmatch '^(\d{4})-(\d{2})$') { Opreste ('-Luna trebuie in formatul YYYY-MM (primit: ' + $Luna + ')') } $anLuna = [int]$Matches[1] $lunaLuna = [int]$Matches[2] $dataInceput = Get-Date -Year $anLuna -Month $lunaLuna -Day 1 -Hour 0 -Minute 0 -Second 0 $dataSfarsit = $dataInceput.AddMonths(1).AddDays(-1) $etichetaLuna = $dataInceput.ToString('yyyyMMdd') + '_' + $dataSfarsit.ToString('yyyyMMdd') $dirLuna = Join-Path (Join-Path $DirScripturi $dataInceput.ToString('yyyy')) $dataInceput.ToString('MM') if (-not $Fisier -and -not $DoarArhiva) { if (-not (Test-Path -LiteralPath $dirLuna)) { Opreste ('nu gasesc directorul lunii: ' + $dirLuna) } foreach ($f in (Get-ChildItem -LiteralPath $dirLuna -Filter '*.sql' -File | Sort-Object Name)) { $info = Parseaza-NumeScript $f.FullName if (-not $info) { Write-Output (' [sarit] nume neconform: ' + $f.Name); continue } [void]$candidati.Add($info) } } Write-Output ('Luna: ' + $Luna + ' arhiva: ' + $etichetaLuna) Write-Output ('Configurare: ' + $Settings) Write-Output ('Baza: ' + $User + '@' + $Alias + ' (' + $SqlPlus + ')') Write-Output ('Scripturi: ' + $dirLuna) Write-Output ('Arhive: ' + $ZipPath) foreach ($c in $candidati) { if ($prefixe.Count -gt 0 -and $prefixe -notcontains $c.Prefix) { Write-Output (' [atentie] prefix necunoscut "' + $c.Prefix + '" in ' + [System.IO.Path]::GetFileName($c.Cale) + ' (in ini: ' + ($prefixe -join ', ') + ')') } $octeti = [System.IO.File]::ReadAllBytes($c.Cale) if ($octeti.Length -eq 0) { Opreste ('fisier gol: ' + $c.Cale) } $text = [System.Text.Encoding]::GetEncoding(1252).GetString($octeti) if ($text -match "(? id ' + $id) } $metadate = Citeste-MetadateLuna -Inceput $dataInceput -Sfarsit $dataSfarsit $dupaNume = @{} foreach ($m in $metadate) { $dupaNume[$m.NumeScript] = $m } $verif = Citeste-Continut -Metadate @($deImportat | ForEach-Object { $dupaNume[$_.NumeScript] }) $erori = 0 foreach ($c in $deImportat) { $m = $dupaNume[$c.NumeScript] $inBaza = $verif[$m.Id] if (Compara-Octeti -A $inBaza -B $c.Octeti) { Write-Output (' verificat ' + $c.NumeScript + ' (' + $c.Octeti.Length + ' octeti, identic cu fisierul)') } else { Write-Output (' DIFERIT ' + $c.NumeScript + ' - continutul din baza nu coincide cu fisierul!') $erori++ } } if ($erori -gt 0) { Opreste ($erori + ' scripturi nu s-au scris corect in baza - nu generez arhiva.') } } } if ($DoarImport) { Scrie-Titlu 'GATA (doar import)' Write-Output 'Arhiva NU a fost regenerata - clientii primesc scripturile abia dupa pasul de arhiva.' Curata-Temp exit 0 } # ---------- 4. arhiva lunara ---------- Scrie-Titlu '4. ARHIVA LUNARA' if (-not (Test-Path -LiteralPath $ZipPath)) { Opreste ('nu gasesc directorul arhivelor: ' + $ZipPath) } if ($DryRun) { Write-Output ('DryRun - as regenera database_' + $etichetaLuna + '.zip si databasen_' + $etichetaLuna + '.zip din ' + $metadate.Count + ' scripturi.') Curata-Temp exit 0 } $metadate = Citeste-MetadateLuna -Inceput $dataInceput -Sfarsit $dataSfarsit if ($metadate.Count -eq 0) { Opreste ('nu exista scripturi in UPD_DATABASE pentru luna ' + $Luna + ' - nu am ce arhiva.') } $continuturi = Citeste-Continut -Metadate $metadate $fisiereSql = New-Object System.Collections.ArrayList foreach ($m in $metadate) { $b = $continuturi[$m.Id] if (-not $b -or $b.Length -ne $m.Lungime) { Opreste ('citire incompleta pentru ' + $m.NumeScript + ' (asteptat ' + $m.Lungime + ' octeti, citit ' + $(if ($b) { $b.Length } else { 0 }) + ').') } $numeFis = $m.Stem + '.sql' [System.IO.File]::WriteAllBytes((Join-Path $TempDir $numeFis), $b) [void]$fisiereSql.Add($numeFis) } Write-Output ('Extrase din baza: ' + $fisiereSql.Count + ' scripturi.') $numeFisiere = @($fisiereSql) $rezultat = @() foreach ($prefix in @('database', 'databasen')) { $numeXml = $prefix + '_' + $etichetaLuna + '.xml' $caleXml = Join-Path $TempDir $numeXml Scrie-XmlArhiva -Metadate $metadate -Cale $caleXml $caleZip = Join-Path $TempDir ($prefix + '_' + $etichetaLuna + '.zip') Scrie-Arhiva -CaleZip $caleZip -CaleXml $caleXml -Fisiere @($numeFisiere | ForEach-Object { Join-Path $TempDir $_ }) Write-Output ('Generat ' + [System.IO.Path]::GetFileName($caleZip) + ' (' + (Get-Item -LiteralPath $caleZip).Length + ' octeti)') $rezultat += [PSCustomObject]@{ Zip = $caleZip; Xml = $numeXml } } # ---------- 5. verificare cu cititorul clientului ---------- if (-not $FaraVerificareZip) { Scrie-Titlu '5. VERIFICARE ARHIVA PRIN ZIP_UTIL_PKG' Verifica-ArhivaPrinOracle -CaleZip $rezultat[0].Zip -FisiereAsteptate (@($rezultat[0].Xml) + $numeFisiere) } else { Scrie-Titlu '5. VERIFICARE ARHIVA PRIN ZIP_UTIL_PKG' Write-Output 'Sarit (-FaraVerificareZip).' } # ---------- 6. publicare in _UPDATE ---------- Scrie-Titlu '6. PUBLICARE IN _UPDATE' if (-not $BackupPath) { $BackupPath = Join-Path $ZipPath ('_backup\' + (Get-Date).ToString('yyyyMMdd_HHmmss')) } New-Item -ItemType Directory -Force -Path $BackupPath | Out-Null foreach ($n in @(($rezultat | ForEach-Object { [System.IO.Path]::GetFileName($_.Zip) }) + 'roa_database.xml' + 'roa_databasen.xml')) { $tinta = Join-Path $ZipPath $n if (Test-Path -LiteralPath $tinta) { Copy-Item -LiteralPath $tinta -Destination (Join-Path $BackupPath $n) -Force Write-Output (' salvat ' + $n + ' -> ' + $BackupPath) } } foreach ($r in $rezultat) { Copy-Item -LiteralPath $r.Zip -Destination (Join-Path $ZipPath ([System.IO.Path]::GetFileName($r.Zip))) -Force Write-Output (' publicat ' + [System.IO.Path]::GetFileName($r.Zip)) } Scrie-IndexArhive -Prefix 'database' -Cale (Join-Path $ZipPath 'roa_database.xml') Scrie-IndexArhive -Prefix 'databasen' -Cale (Join-Path $ZipPath 'roa_databasen.xml') Write-Output ' publicat roa_database.xml, roa_databasen.xml' Scrie-Titlu 'GATA' Write-Output ('Luna ' + $Luna + ': ' + $metadate.Count + ' scripturi in arhiva, ' + $deImportat.Count + ' importate acum.') Write-Output ('Backup-ul fisierelor inlocuite: ' + $BackupPath) Curata-Temp exit 0