From ec66377e0f92cdec7ddbf5b552fc28b3d56db96f Mon Sep 17 00:00:00 2001 From: Marius Date: Tue, 25 Aug 2026 21:18:43 +0300 Subject: [PATCH] fix(oracle): calea DMPDIR devine parametru; sqlnet.ora ajunge unde e citit Gasite la instalarea de productie VADECO (Oracle 21c XE, Windows Server 2019). 1. Pe serverul VADECO procesul Oracle nu poate atinge nicio cale de pe C:, nici macar C:\Windows\Temp, desi ACL-urile sunt corecte si serviciul ruleaza ca NT SERVICE\OracleServiceXE. D: merge cu exact acelasi grant. Calea DMP era insa hardcodata "C:\DMPDIR" in 01, 02, 05, 06 si in biblioteca, deci instalarea nu se putea muta. Acum e parametrul -DmpDir, expus ca /dmpdest: in FAZA1.cmd si FAZA2.cmd si transmis tuturor pasilor. Cel mai perfid era pasul 02: recrea DIRECTORY DMPDIR pe 'C:\DMPDIR' peste valoarea pusa de 01. Chiar cu restul instalarii mutata, SYS.NEWSCHEMA ar fi cautat FIRMANOUA.dmp in alta parte, iar adaugarea unei firme noi ar fi picat peste luni, fara legatura vizibila cu instalarea. 2. New-OracleDirectory compara doar siruri de caractere: verifica ce scrie in dba_directories, nu daca baza chiar poate scrie acolo. Acum face un UTL_FILE round-trip si esueaza pe loc, cu explicatia cauzei. Inainte, directorul inutilizabil trecea drept bun si importul cadea abia in impdp cu ORA-29283, la zeci de linii distanta. Valoarea de retur nici nu era verificata in 01/03/06 - de aici si "True" ratacit in log. 3. Instalarea ROAClient seteaza TNS_ADMIN de masina spre instantclient-ul ei. Serviciul Oracle mosteneste variabilele de masina, deci citea sqlnet.ora de acolo - unde nu exista niciunul - si nu din Oracle Home, unde il scria pasul 01. Compatibilitatea pentru clienti vechi nu se aplica, iar statiile ar fi primit ORA-28040 cu fisierul scris corect, in locul gresit. Pasul 01 scrie acum sqlnet.ora si in TNS_ADMIN cand difera. 4. Pasul 05 lega schemele de DMP-uri cu "^$schema[_\.]", deci schema DANUBE putea primi DANUBE_20200914.dmp - datele altei firme, fara avertisment. Numele exact are acum intaietate, prefixul se accepta doar daca e unic, iar ambiguitatea se raporteaza. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01SzF1hf4aFS1tmJWpPMiwGp --- .gitignore | 3 + .../lxc108-oracle/roa-windows-setup/FAZA1.cmd | 13 +- .../lxc108-oracle/roa-windows-setup/FAZA2.cmd | 9 +- .../scripts/01-setup-database.ps1 | 42 +- .../scripts/02-create-sys-objects.ps1 | 870 ++++--- .../scripts/03-import-contafin.ps1 | 673 ++--- .../scripts/05-import-companies.ps1 | 880 ++++--- .../scripts/06-add-company.ps1 | 660 ++--- .../scripts/lib/oracle-functions.ps1 | 2310 +++++++++-------- 9 files changed, 2787 insertions(+), 2673 deletions(-) diff --git a/.gitignore b/.gitignore index a635ebb..084513f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ input/ # Secrets - real cPanel DNS hook config holds an API token (template is committed) cpanel-dns.config.json **/cpanel-dns.config.json + +# Cygwin/Git-Bash crash dumps +*.stackdump diff --git a/proxmox/lxc108-oracle/roa-windows-setup/FAZA1.cmd b/proxmox/lxc108-oracle/roa-windows-setup/FAZA1.cmd index 5a85929..6294c85 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/FAZA1.cmd +++ b/proxmox/lxc108-oracle/roa-windows-setup/FAZA1.cmd @@ -34,6 +34,11 @@ REM /parolavechi: parola SYS de pe serverul vechi REM /licente: licente dintr-un fisier, in loc de retea REM /roaupdate: radacina oglinzii (implicit D:\ROAUPDATE) REM /export: director export (implicit D:\ROA\EXPORT_ORADMP) +REM /dmpdest: de unde citeste baza DMP-urile (implicit C:\DMPDIR) +REM Pe unele masini procesul Oracle nu ajunge la C:\ deloc +REM (ORA-29283 la orice cale de pe C:, inclusiv C:\Windows\Temp, +REM desi ACL-urile sunt corecte). Atunci pune-l pe discul unde +REM e Oracle Home: /dmpdest:D:\DMPDIR REM /faralicente sari peste pasul 09 REM /faraiis sari peste pasul 10 REM /farabackup sari peste pasul 11 @@ -89,6 +94,7 @@ if /i "!OPT!"=="/parolavechi" set "PAROLAVECHI=!VAL!" if /i "!OPT!"=="/licente" set "LICENTEFIS=!VAL!" if /i "!OPT!"=="/roaupdate" set "ROAUPDATE=!VAL!" if /i "!OPT!"=="/export" set "EXPORTDIR=!VAL!" +if /i "!OPT!"=="/dmpdest" set "DMPDEST=!VAL!" shift goto :parsare @@ -102,6 +108,7 @@ echo. echo Kit: %KIT% echo Id client: %IDCLIENT% echo SMTP: %SMTP% +echo DMP: %DMPDEST% echo ROAUPDATE: %ROAUPDATE% echo Export: %EXPORTDIR% if not "%VECHI%"=="" echo Server vechi: %VECHI% / %SERVICIUVECHI% @@ -139,7 +146,7 @@ REM [2/9] 01 - baza si userul REM --------------------------------------------------------------------------- echo. echo [2/9] 01-setup-database.ps1 -powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\01-setup-database.ps1" +powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\01-setup-database.ps1" -DmpDir "%DMPDEST%" if errorlevel 1 goto :esec_01 REM --------------------------------------------------------------------------- @@ -147,7 +154,7 @@ REM [3/9] 02 - obiectele SYS REM --------------------------------------------------------------------------- echo. echo [3/9] 02-create-sys-objects.ps1 -powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\02-create-sys-objects.ps1" +powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\02-create-sys-objects.ps1" -DmpDir "%DMPDEST%" if errorlevel 1 goto :esec_02 REM --------------------------------------------------------------------------- @@ -155,7 +162,7 @@ REM [4/9] 03 - CONTAFIN_ORACLE REM --------------------------------------------------------------------------- echo. echo [4/9] 03-import-contafin.ps1 -powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\03-import-contafin.ps1" +powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\03-import-contafin.ps1" -DmpDir "%DMPDEST%" set RC=!ERRORLEVEL! REM Codul 5 e normal: Data Pump raporteaza "completed with errors" pentru REM ORA-31684 (userul e creat deja de pasul 01) si ORA-39082 (warning-uri de diff --git a/proxmox/lxc108-oracle/roa-windows-setup/FAZA2.cmd b/proxmox/lxc108-oracle/roa-windows-setup/FAZA2.cmd index a7d6178..48d88a8 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/FAZA2.cmd +++ b/proxmox/lxc108-oracle/roa-windows-setup/FAZA2.cmd @@ -28,6 +28,8 @@ REM /smtp: server SMTP (implicit mail.romfast.ro) REM /roaupdate: radacina oglinzii (implicit D:\ROAUPDATE) REM /export: director export (implicit D:\ROA\EXPORT_ORADMP) REM /dmp: de unde se copiaza DMP-urile (implicit \DMPDIR) +REM /dmpdest: de unde le citeste baza (implicit C:\DMPDIR) +REM Trebuie sa fie aceeasi valoare ca la FAZA1. REM /faraschema nu regenera schema.txt REM REM Exemple: @@ -72,6 +74,7 @@ if /i "!OPT!"=="/smtp" set "SMTP=!VAL!" if /i "!OPT!"=="/roaupdate" set "ROAUPDATE=!VAL!" if /i "!OPT!"=="/export" set "EXPORTDIR=!VAL!" if /i "!OPT!"=="/dmp" set "DMPSURSA=!VAL!" +if /i "!OPT!"=="/dmpdest" set "DMPDEST=!VAL!" shift goto :parsare @@ -150,12 +153,12 @@ if errorlevel 1 goto :esec_drop echo. echo 01-setup-database.ps1 (recreare user) -powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\01-setup-database.ps1" +powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\01-setup-database.ps1" -DmpDir "%DMPDEST%" if errorlevel 1 goto :esec_01 echo. echo 03-import-contafin.ps1 -powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\03-import-contafin.ps1" +powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\03-import-contafin.ps1" -DmpDir "%DMPDEST%" set RC=!ERRORLEVEL! if "!RC!"=="0" goto :dupa_03 if "!RC!"=="5" ( @@ -177,7 +180,7 @@ REM [3/6] Schemele de firma REM --------------------------------------------------------------------------- echo. echo [3/6] 05-import-companies.ps1 -powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\05-import-companies.ps1" +powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%KIT%scripts\05-import-companies.ps1" -DumpDirectory "%DMPDEST%" -DmpDir "%DMPDEST%" if errorlevel 1 goto :esec_05 REM --------------------------------------------------------------------------- diff --git a/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 b/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 index 3688494..f0ae4e7 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 +++ b/proxmox/lxc108-oracle/roa-windows-setup/scripts/01-setup-database.ps1 @@ -87,7 +87,13 @@ param( [switch]$SkipSqlnetConfig, [Parameter(Mandatory = $false)] - [switch]$ResetSystemPassword + [switch]$ResetSystemPassword, + + # Calea fizica a directorului Oracle DMPDIR. Aceeasi valoare trebuie data + # tuturor pasilor care ating DMPDIR (01, 02, 03, 05, 06), altfel ultimul + # care ruleaza repozitioneaza obiectul si ceilalti citesc din alta parte. + [Parameter(Mandatory = $false)] + [string]$DmpDir = "C:\DMPDIR" ) $ErrorActionPreference = 'Stop' @@ -355,6 +361,31 @@ NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT) Write-LogSuccess "sqlnet.ora configured at $sqlnetOra" Write-Log "Old client compatibility enabled (SQLNET.ALLOWED_LOGON_VERSION=8)" + # Daca exista TNS_ADMIN la nivel de masina, ACELA castiga - si pentru + # clienti, si pentru serviciul Oracle, care mosteneste variabilele de + # masina. Instalarea ROAClient il pune sa arate spre instantclient-ul + # ei (D:\ROA\instantclient_11_2_0_2), unde de obicei nu exista niciun + # sqlnet.ora: atunci serverul cade pe valorile implicite, adica + # ALLOWED_LOGON_VERSION_SERVER=12, si clientii vechi primesc ORA-28040 + # desi fisierul de mai sus a fost scris corect. Scriem acelasi continut + # si acolo, altfel toata configurarea de compatibilitate e degeaba. + $tnsAdmin = [Environment]::GetEnvironmentVariable('TNS_ADMIN', 'Machine') + if ($tnsAdmin) { + $tnsAdminFull = [IO.Path]::GetFullPath($tnsAdmin.TrimEnd('\')) + if ($tnsAdminFull -ne [IO.Path]::GetFullPath($networkAdmin.TrimEnd('\'))) { + Write-LogWarning "TNS_ADMIN de masina arata spre $tnsAdminFull - acolo se citeste sqlnet.ora, nu din Oracle Home." + if (-not (Test-Path $tnsAdminFull)) { + New-Item -ItemType Directory -Path $tnsAdminFull -Force | Out-Null + } + $tnsSqlnet = Join-Path $tnsAdminFull "sqlnet.ora" + if (Test-Path $tnsSqlnet) { + Copy-Item $tnsSqlnet "$tnsSqlnet.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')" -Force + } + Set-Content -Path $tnsSqlnet -Value $sqlnetContent -Encoding ASCII + Write-LogSuccess "sqlnet.ora scris si in TNS_ADMIN: $tnsSqlnet" + } + } + # Restart listener to apply changes Write-Log "Restarting listener to apply sqlnet.ora changes..." $lsnrctl = Join-Path $oraHome "bin\lsnrctl.exe" @@ -536,11 +567,12 @@ EXIT; # Step 9: Create DMPDIR directory for Data Pump Write-LogSection "Creating Data Pump Directory" - $dmpDir = "C:\DMPDIR" - New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $dmpDir + if (-not (New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $DmpDir)) { + throw "Directorul Data Pump DMPDIR ($DmpDir) nu e utilizabil de catre baza. Vezi mesajele de mai sus." + } - Write-LogSuccess "Data Pump directory DMPDIR created: $dmpDir" + Write-LogSuccess "Data Pump directory DMPDIR created: $DmpDir" # Step 10: Reset passwords with 10G verifier (required for old client auth) # After sqlnet.ora is configured, passwords must be reset to generate 10G hash diff --git a/proxmox/lxc108-oracle/roa-windows-setup/scripts/02-create-sys-objects.ps1 b/proxmox/lxc108-oracle/roa-windows-setup/scripts/02-create-sys-objects.ps1 index 32dba8d..367357e 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/scripts/02-create-sys-objects.ps1 +++ b/proxmox/lxc108-oracle/roa-windows-setup/scripts/02-create-sys-objects.ps1 @@ -1,432 +1,438 @@ -#Requires -Version 5.1 - -<# -.SYNOPSIS - Install SYS objects for ROA Oracle. - -.DESCRIPTION - Executes SQL scripts to create required SYS-owned objects including: - - AUTH_PACK package - - AUTH_DETALII, AUTH_SERII tables - - NEWSCHEMA, NEWSCHEMAPOST procedures - - NEWSCHEMAPROGRESS function - - PACK_UPDATE, PACK_UTILS packages - - Public synonyms and grants - -.PARAMETER OracleHome - Oracle home directory. If not specified, auto-detects. - -.PARAMETER ServiceName - Database service name. Default: XEPDB1 - -.PARAMETER SystemPassword - SYSTEM/SYS user password. Default: romfastsoft - -.PARAMETER SqlScriptsDir - Directory containing SYS SQL scripts. Default: ..\sql - -.EXAMPLE - .\02-create-sys-objects.ps1 - -.EXAMPLE - .\02-create-sys-objects.ps1 -ServiceName "ROA" -SystemPassword "mypassword" - -.NOTES - File Name : 02-create-sys-objects.ps1 - Prerequisite : Run 01-setup-database.ps1 first - Copyright 2024 : ROMFAST -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $false)] - [string]$ServiceName = "XEPDB1", - - [Parameter(Mandatory = $false)] - [string]$SystemPassword = "romfastsoft", - - [Parameter(Mandatory = $false)] - [string]$SqlScriptsDir -) - -$ErrorActionPreference = 'Stop' - -# Source library functions -. "$PSScriptRoot\lib\logging-functions.ps1" -. "$PSScriptRoot\lib\oracle-functions.ps1" - -# Initialize logging -$logPath = Join-Path $PSScriptRoot "..\logs\02-create-sys-objects_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" -Initialize-LogFile -LogPath $logPath -ScriptName "02-create-sys-objects.ps1" - -try { - Write-LogSection "Installing SYS Objects" - - # Validate Oracle installation - $oraHome = Get-OracleHome -OracleHome $OracleHome - Write-LogSuccess "Oracle Home: $oraHome" - - # Test connection - Write-Log "Testing database connection..." - if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYS" -Password $SystemPassword -AsSysdba)) { - throw "Cannot connect to database as SYS. Please verify ServiceName and SystemPassword." - } - Write-LogSuccess "Database connection successful" - - # Determine SQL scripts directory - if (-not $SqlScriptsDir) { - $SqlScriptsDir = Join-Path $PSScriptRoot "..\sql" - } - - $sysObjectsScript = Join-Path $SqlScriptsDir "sys-objects.sql" - - if (-not (Test-Path -Path $sysObjectsScript)) { - Write-LogWarning "sys-objects.sql not found at $sysObjectsScript" - Write-Log "Creating sys-objects.sql from embedded SQL..." - - # Create the sql directory if it doesn't exist - if (-not (Test-Path -Path $SqlScriptsDir)) { - New-Item -ItemType Directory -Path $SqlScriptsDir -Force | Out-Null - } - - # Embedded SYS objects SQL - minimal set for ROA - $sysObjectsSql = @" --- sys-objects.sql --- SYS objects required for ROA Oracle --- Run as SYS with SYSDBA privilege - -SET ECHO ON FEEDBACK ON VERIFY OFF SERVEROUTPUT ON - -PROMPT -PROMPT ============================================= -PROMPT Creating SYS objects for ROA -PROMPT ============================================= -PROMPT - --- Create DMPDIR if not exists -PROMPT Creating directory DMPDIR... -CREATE OR REPLACE DIRECTORY DMPDIR AS 'C:\DMPDIR'; -GRANT READ, WRITE ON DIRECTORY DMPDIR TO PUBLIC; - --- AUTH_DETALII table -PROMPT Creating table AUTH_DETALII... -BEGIN - EXECUTE IMMEDIATE 'DROP TABLE SYS.AUTH_DETALII CASCADE CONSTRAINTS'; -EXCEPTION WHEN OTHERS THEN NULL; -END; -/ - -CREATE TABLE SYS.AUTH_DETALII ( - ID_DETALIU NUMBER NOT NULL, - ID_PROGRAM NUMBER NOT NULL, - CAMP_DETALIU VARCHAR2(100), - VAL_DETALIU VARCHAR2(500), - CONSTRAINT PK_AUTH_DETALII PRIMARY KEY (ID_DETALIU) -); - -GRANT SELECT, INSERT, UPDATE, DELETE ON SYS.AUTH_DETALII TO PUBLIC; -CREATE PUBLIC SYNONYM AUTH_DETALII FOR SYS.AUTH_DETALII; - --- AUTH_SERII table -PROMPT Creating table AUTH_SERII... -BEGIN - EXECUTE IMMEDIATE 'DROP TABLE SYS.AUTH_SERII CASCADE CONSTRAINTS'; -EXCEPTION WHEN OTHERS THEN NULL; -END; -/ - -CREATE TABLE SYS.AUTH_SERII ( - ID_PROGRAM NUMBER NOT NULL, - SERIE VARCHAR2(50), - NR_UTIL NUMBER, - DATA_VAL DATE, - CONSTRAINT PK_AUTH_SERII PRIMARY KEY (ID_PROGRAM) -); - -GRANT SELECT, INSERT, UPDATE, DELETE ON SYS.AUTH_SERII TO PUBLIC; -CREATE PUBLIC SYNONYM AUTH_SERII FOR SYS.AUTH_SERII; - --- AUTH_PACK package specification -PROMPT Creating package AUTH_PACK... -CREATE OR REPLACE PACKAGE SYS.AUTH_PACK AS - FUNCTION selecteaza_serie(p_id_program NUMBER) RETURN VARCHAR2; - FUNCTION selecteaza_nr_util(p_id_program NUMBER) RETURN NUMBER; - FUNCTION selecteaza_data_val(p_id_program NUMBER) RETURN DATE; - PROCEDURE actualizeaza_serie(p_id_program NUMBER, p_serie VARCHAR2, p_nr_util NUMBER, p_data_val DATE); -END AUTH_PACK; -/ - --- AUTH_PACK package body -CREATE OR REPLACE PACKAGE BODY SYS.AUTH_PACK AS - - FUNCTION selecteaza_serie(p_id_program NUMBER) RETURN VARCHAR2 IS - v_serie VARCHAR2(50); - BEGIN - SELECT serie INTO v_serie FROM SYS.AUTH_SERII WHERE id_program = p_id_program; - RETURN v_serie; - EXCEPTION - WHEN NO_DATA_FOUND THEN RETURN NULL; - END; - - FUNCTION selecteaza_nr_util(p_id_program NUMBER) RETURN NUMBER IS - v_nr_util NUMBER; - BEGIN - SELECT nr_util INTO v_nr_util FROM SYS.AUTH_SERII WHERE id_program = p_id_program; - RETURN v_nr_util; - EXCEPTION - WHEN NO_DATA_FOUND THEN RETURN NULL; - END; - - FUNCTION selecteaza_data_val(p_id_program NUMBER) RETURN DATE IS - v_data_val DATE; - BEGIN - SELECT data_val INTO v_data_val FROM SYS.AUTH_SERII WHERE id_program = p_id_program; - RETURN v_data_val; - EXCEPTION - WHEN NO_DATA_FOUND THEN RETURN NULL; - END; - - PROCEDURE actualizeaza_serie(p_id_program NUMBER, p_serie VARCHAR2, p_nr_util NUMBER, p_data_val DATE) IS - BEGIN - MERGE INTO SYS.AUTH_SERII t - USING (SELECT p_id_program AS id_program FROM dual) s - ON (t.id_program = s.id_program) - WHEN MATCHED THEN - UPDATE SET serie = p_serie, nr_util = p_nr_util, data_val = p_data_val - WHEN NOT MATCHED THEN - INSERT (id_program, serie, nr_util, data_val) - VALUES (p_id_program, p_serie, p_nr_util, p_data_val); - COMMIT; - END; - -END AUTH_PACK; -/ - -GRANT EXECUTE ON SYS.AUTH_PACK TO PUBLIC; -CREATE OR REPLACE PUBLIC SYNONYM AUTH_PACK FOR SYS.AUTH_PACK; - --- VAUTH_SERII view -PROMPT Creating view VAUTH_SERII... -CREATE OR REPLACE VIEW SYS.VAUTH_SERII AS -SELECT a.id_program, - a.denumire AS nume, - auth_pack.selecteaza_serie(a.id_program) AS serie, - auth_pack.selecteaza_nr_util(a.id_program) AS nr_util, - auth_pack.selecteaza_data_val(a.id_program) AS data_val - FROM syn_nom_programe a - LEFT JOIN syn_def_programe b ON a.id_program = b.ide_program - WHERE b.sters = 0 - AND b.instalat = 1; - -GRANT SELECT ON SYS.VAUTH_SERII TO PUBLIC; -CREATE OR REPLACE PUBLIC SYNONYM VAUTH_SERII FOR SYS.VAUTH_SERII; - --- PACK_UPDATE package (minimal version) -PROMPT Creating package PACK_UPDATE... -CREATE OR REPLACE PACKAGE SYS.PACK_UPDATE AS - FUNCTION get_version RETURN VARCHAR2; - PROCEDURE execute_sql(p_sql VARCHAR2); -END PACK_UPDATE; -/ - -CREATE OR REPLACE PACKAGE BODY SYS.PACK_UPDATE AS - - FUNCTION get_version RETURN VARCHAR2 IS - BEGIN - RETURN '1.0.0'; - END; - - PROCEDURE execute_sql(p_sql VARCHAR2) IS - BEGIN - EXECUTE IMMEDIATE p_sql; - EXCEPTION - WHEN OTHERS THEN - RAISE; - END; - -END PACK_UPDATE; -/ - -GRANT EXECUTE ON SYS.PACK_UPDATE TO PUBLIC; -CREATE OR REPLACE PUBLIC SYNONYM PACK_UPDATE FOR SYS.PACK_UPDATE; - --- PACK_UTILS package (minimal version) -PROMPT Creating package PACK_UTILS... -CREATE OR REPLACE PACKAGE SYS.PACK_UTILS AS - FUNCTION get_db_version RETURN VARCHAR2; - FUNCTION get_instance_name RETURN VARCHAR2; -END PACK_UTILS; -/ - -CREATE OR REPLACE PACKAGE BODY SYS.PACK_UTILS AS - - FUNCTION get_db_version RETURN VARCHAR2 IS - v_version VARCHAR2(100); - BEGIN - SELECT version_full INTO v_version FROM v`$instance; - RETURN v_version; - EXCEPTION - WHEN OTHERS THEN RETURN NULL; - END; - - FUNCTION get_instance_name RETURN VARCHAR2 IS - v_instance VARCHAR2(100); - BEGIN - SELECT instance_name INTO v_instance FROM v`$instance; - RETURN v_instance; - EXCEPTION - WHEN OTHERS THEN RETURN NULL; - END; - -END PACK_UTILS; -/ - -GRANT EXECUTE ON SYS.PACK_UTILS TO PUBLIC; -CREATE OR REPLACE PUBLIC SYNONYM PACK_UTILS FOR SYS.PACK_UTILS; - --- Grant UTL packages to PUBLIC -PROMPT Granting UTL packages... -GRANT EXECUTE ON UTL_FILE TO PUBLIC; -GRANT EXECUTE ON DBMS_LOCK TO PUBLIC; - --- Grant UTL packages to CONTAFIN_ORACLE specifically -GRANT EXECUTE ON UTL_INADDR TO CONTAFIN_ORACLE; -GRANT EXECUTE ON UTL_TCP TO CONTAFIN_ORACLE; -GRANT EXECUTE ON UTL_SMTP TO CONTAFIN_ORACLE; -GRANT EXECUTE ON UTL_HTTP TO CONTAFIN_ORACLE; - -COMMIT; - -PROMPT -PROMPT ============================================= -PROMPT SYS objects created successfully -PROMPT ============================================= -PROMPT - -SELECT 'SYS_OBJECTS_COMPLETE' FROM dual; - -EXIT; -"@ - - Set-Content -Path $sysObjectsScript -Value $sysObjectsSql -Encoding ASCII - Write-Log "Created sys-objects.sql" - } - - # Execute SYS objects script - Write-LogSection "Executing sys-objects.sql" - - $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYS" -Password $SystemPassword -SqlFile $sysObjectsScript -AsSysdba - - if ($result -match "SYS_OBJECTS_COMPLETE") { - Write-LogSuccess "SYS objects created successfully" - } - else { - Write-LogWarning "Script completed but could not verify success" - Write-LogDebug $result - } - - # Verify created objects - Write-LogSection "Verifying SYS Objects" - - $verifySql = @" -SET PAGESIZE 100 FEEDBACK OFF VERIFY OFF HEADING ON ECHO OFF LINESIZE 200 - -PROMPT -PROMPT === SYS Objects Status === -PROMPT - -SELECT object_name, object_type, status -FROM dba_objects -WHERE owner = 'SYS' - AND object_name IN ('AUTH_PACK', 'AUTH_DETALII', 'AUTH_SERII', 'VAUTH_SERII', - 'PACK_UPDATE', 'PACK_UTILS', 'NEWSCHEMA', 'NEWSCHEMAPOST', 'NEWSCHEMAPROGRESS') -ORDER BY object_type, object_name; - -PROMPT -PROMPT === Public Synonyms === -PROMPT - -SELECT synonym_name, table_owner, table_name -FROM dba_synonyms -WHERE owner = 'PUBLIC' - AND table_name IN ('AUTH_PACK', 'AUTH_DETALII', 'AUTH_SERII', 'VAUTH_SERII', - 'PACK_UPDATE', 'PACK_UTILS', 'NEWSCHEMA', 'NEWSCHEMAPOST', 'NEWSCHEMAPROGRESS') -ORDER BY synonym_name; - -EXIT; -"@ - - $verifyResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYS" -Password $SystemPassword -SqlCommand $verifySql -AsSysdba - - Write-Host $verifyResult - - # Count invalid objects - $invalidSql = @" -SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF -SELECT 'INVALID_COUNT:' || COUNT(*) -FROM dba_objects -WHERE owner = 'SYS' - AND object_name IN ('AUTH_PACK', 'AUTH_DETALII', 'AUTH_SERII', 'VAUTH_SERII', - 'PACK_UPDATE', 'PACK_UTILS', 'NEWSCHEMA', 'NEWSCHEMAPOST', 'NEWSCHEMAPROGRESS') - AND status = 'INVALID'; -EXIT; -"@ - - $invalidResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYS" -Password $SystemPassword -SqlCommand $invalidSql -AsSysdba -Silent - - $invalidCount = 0 - if ($invalidResult -match "INVALID_COUNT:(\d+)") { - $invalidCount = [int]$Matches[1] - } - - if ($invalidCount -gt 0) { - Write-LogWarning "$invalidCount invalid SYS objects found" - Write-Log "Attempting to recompile invalid objects..." - - $recompileSql = @" -SET SERVEROUTPUT ON -BEGIN - DBMS_UTILITY.COMPILE_SCHEMA('SYS', FALSE); -END; -/ -EXIT; -"@ - Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYS" -Password $SystemPassword -SqlCommand $recompileSql -AsSysdba -Silent - } - else { - Write-LogSuccess "All SYS objects are valid" - } - - # Summary - Write-LogSection "Setup Complete" - Write-LogSuccess "SYS objects installation completed!" - Write-Log "" - Write-Log "Objects created:" - Write-Log " - AUTH_PACK package" - Write-Log " - AUTH_DETALII table" - Write-Log " - AUTH_SERII table" - Write-Log " - VAUTH_SERII view" - Write-Log " - PACK_UPDATE package" - Write-Log " - PACK_UTILS package" - Write-Log " - Public synonyms and grants" - Write-Log "" - Write-Log "Next steps:" - Write-Log " 1. Run 03-import-contafin.ps1 to import CONTAFIN_ORACLE schema" - - Close-LogFile -Success $true - exit 0 -} -catch { - Write-LogError "Setup failed: $_" - Write-LogError $_.ScriptStackTrace - Close-LogFile -Success $false - exit 1 -} +#Requires -Version 5.1 + +<# +.SYNOPSIS + Install SYS objects for ROA Oracle. + +.DESCRIPTION + Executes SQL scripts to create required SYS-owned objects including: + - AUTH_PACK package + - AUTH_DETALII, AUTH_SERII tables + - NEWSCHEMA, NEWSCHEMAPOST procedures + - NEWSCHEMAPROGRESS function + - PACK_UPDATE, PACK_UTILS packages + - Public synonyms and grants + +.PARAMETER OracleHome + Oracle home directory. If not specified, auto-detects. + +.PARAMETER ServiceName + Database service name. Default: XEPDB1 + +.PARAMETER SystemPassword + SYSTEM/SYS user password. Default: romfastsoft + +.PARAMETER SqlScriptsDir + Directory containing SYS SQL scripts. Default: ..\sql + +.EXAMPLE + .\02-create-sys-objects.ps1 + +.EXAMPLE + .\02-create-sys-objects.ps1 -ServiceName "ROA" -SystemPassword "mypassword" + +.NOTES + File Name : 02-create-sys-objects.ps1 + Prerequisite : Run 01-setup-database.ps1 first + Copyright 2024 : ROMFAST +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $false)] + [string]$ServiceName = "XEPDB1", + + [Parameter(Mandatory = $false)] + [string]$SystemPassword = "romfastsoft", + + [Parameter(Mandatory = $false)] + [string]$SqlScriptsDir, + + # Calea fizica pe care o primeste obiectul Oracle DIRECTORY DMPDIR. + # Trebuie sa fie aceeasi in toti pasii, altfel unul dintre ei repozitioneaza + # tacut directorul si SYS.NEWSCHEMA nu-si mai gaseste FIRMANOUA.dmp. + [Parameter(Mandatory = $false)] + [string]$DmpDir = "C:\DMPDIR" +) + +$ErrorActionPreference = 'Stop' + +# Source library functions +. "$PSScriptRoot\lib\logging-functions.ps1" +. "$PSScriptRoot\lib\oracle-functions.ps1" + +# Initialize logging +$logPath = Join-Path $PSScriptRoot "..\logs\02-create-sys-objects_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" +Initialize-LogFile -LogPath $logPath -ScriptName "02-create-sys-objects.ps1" + +try { + Write-LogSection "Installing SYS Objects" + + # Validate Oracle installation + $oraHome = Get-OracleHome -OracleHome $OracleHome + Write-LogSuccess "Oracle Home: $oraHome" + + # Test connection + Write-Log "Testing database connection..." + if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYS" -Password $SystemPassword -AsSysdba)) { + throw "Cannot connect to database as SYS. Please verify ServiceName and SystemPassword." + } + Write-LogSuccess "Database connection successful" + + # Determine SQL scripts directory + if (-not $SqlScriptsDir) { + $SqlScriptsDir = Join-Path $PSScriptRoot "..\sql" + } + + $sysObjectsScript = Join-Path $SqlScriptsDir "sys-objects.sql" + + if (-not (Test-Path -Path $sysObjectsScript)) { + Write-LogWarning "sys-objects.sql not found at $sysObjectsScript" + Write-Log "Creating sys-objects.sql from embedded SQL..." + + # Create the sql directory if it doesn't exist + if (-not (Test-Path -Path $SqlScriptsDir)) { + New-Item -ItemType Directory -Path $SqlScriptsDir -Force | Out-Null + } + + # Embedded SYS objects SQL - minimal set for ROA + $sysObjectsSql = @" +-- sys-objects.sql +-- SYS objects required for ROA Oracle +-- Run as SYS with SYSDBA privilege + +SET ECHO ON FEEDBACK ON VERIFY OFF SERVEROUTPUT ON + +PROMPT +PROMPT ============================================= +PROMPT Creating SYS objects for ROA +PROMPT ============================================= +PROMPT + +-- Create DMPDIR if not exists +PROMPT Creating directory DMPDIR... +CREATE OR REPLACE DIRECTORY DMPDIR AS '$DmpDir'; +GRANT READ, WRITE ON DIRECTORY DMPDIR TO PUBLIC; + +-- AUTH_DETALII table +PROMPT Creating table AUTH_DETALII... +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE SYS.AUTH_DETALII CASCADE CONSTRAINTS'; +EXCEPTION WHEN OTHERS THEN NULL; +END; +/ + +CREATE TABLE SYS.AUTH_DETALII ( + ID_DETALIU NUMBER NOT NULL, + ID_PROGRAM NUMBER NOT NULL, + CAMP_DETALIU VARCHAR2(100), + VAL_DETALIU VARCHAR2(500), + CONSTRAINT PK_AUTH_DETALII PRIMARY KEY (ID_DETALIU) +); + +GRANT SELECT, INSERT, UPDATE, DELETE ON SYS.AUTH_DETALII TO PUBLIC; +CREATE PUBLIC SYNONYM AUTH_DETALII FOR SYS.AUTH_DETALII; + +-- AUTH_SERII table +PROMPT Creating table AUTH_SERII... +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE SYS.AUTH_SERII CASCADE CONSTRAINTS'; +EXCEPTION WHEN OTHERS THEN NULL; +END; +/ + +CREATE TABLE SYS.AUTH_SERII ( + ID_PROGRAM NUMBER NOT NULL, + SERIE VARCHAR2(50), + NR_UTIL NUMBER, + DATA_VAL DATE, + CONSTRAINT PK_AUTH_SERII PRIMARY KEY (ID_PROGRAM) +); + +GRANT SELECT, INSERT, UPDATE, DELETE ON SYS.AUTH_SERII TO PUBLIC; +CREATE PUBLIC SYNONYM AUTH_SERII FOR SYS.AUTH_SERII; + +-- AUTH_PACK package specification +PROMPT Creating package AUTH_PACK... +CREATE OR REPLACE PACKAGE SYS.AUTH_PACK AS + FUNCTION selecteaza_serie(p_id_program NUMBER) RETURN VARCHAR2; + FUNCTION selecteaza_nr_util(p_id_program NUMBER) RETURN NUMBER; + FUNCTION selecteaza_data_val(p_id_program NUMBER) RETURN DATE; + PROCEDURE actualizeaza_serie(p_id_program NUMBER, p_serie VARCHAR2, p_nr_util NUMBER, p_data_val DATE); +END AUTH_PACK; +/ + +-- AUTH_PACK package body +CREATE OR REPLACE PACKAGE BODY SYS.AUTH_PACK AS + + FUNCTION selecteaza_serie(p_id_program NUMBER) RETURN VARCHAR2 IS + v_serie VARCHAR2(50); + BEGIN + SELECT serie INTO v_serie FROM SYS.AUTH_SERII WHERE id_program = p_id_program; + RETURN v_serie; + EXCEPTION + WHEN NO_DATA_FOUND THEN RETURN NULL; + END; + + FUNCTION selecteaza_nr_util(p_id_program NUMBER) RETURN NUMBER IS + v_nr_util NUMBER; + BEGIN + SELECT nr_util INTO v_nr_util FROM SYS.AUTH_SERII WHERE id_program = p_id_program; + RETURN v_nr_util; + EXCEPTION + WHEN NO_DATA_FOUND THEN RETURN NULL; + END; + + FUNCTION selecteaza_data_val(p_id_program NUMBER) RETURN DATE IS + v_data_val DATE; + BEGIN + SELECT data_val INTO v_data_val FROM SYS.AUTH_SERII WHERE id_program = p_id_program; + RETURN v_data_val; + EXCEPTION + WHEN NO_DATA_FOUND THEN RETURN NULL; + END; + + PROCEDURE actualizeaza_serie(p_id_program NUMBER, p_serie VARCHAR2, p_nr_util NUMBER, p_data_val DATE) IS + BEGIN + MERGE INTO SYS.AUTH_SERII t + USING (SELECT p_id_program AS id_program FROM dual) s + ON (t.id_program = s.id_program) + WHEN MATCHED THEN + UPDATE SET serie = p_serie, nr_util = p_nr_util, data_val = p_data_val + WHEN NOT MATCHED THEN + INSERT (id_program, serie, nr_util, data_val) + VALUES (p_id_program, p_serie, p_nr_util, p_data_val); + COMMIT; + END; + +END AUTH_PACK; +/ + +GRANT EXECUTE ON SYS.AUTH_PACK TO PUBLIC; +CREATE OR REPLACE PUBLIC SYNONYM AUTH_PACK FOR SYS.AUTH_PACK; + +-- VAUTH_SERII view +PROMPT Creating view VAUTH_SERII... +CREATE OR REPLACE VIEW SYS.VAUTH_SERII AS +SELECT a.id_program, + a.denumire AS nume, + auth_pack.selecteaza_serie(a.id_program) AS serie, + auth_pack.selecteaza_nr_util(a.id_program) AS nr_util, + auth_pack.selecteaza_data_val(a.id_program) AS data_val + FROM syn_nom_programe a + LEFT JOIN syn_def_programe b ON a.id_program = b.ide_program + WHERE b.sters = 0 + AND b.instalat = 1; + +GRANT SELECT ON SYS.VAUTH_SERII TO PUBLIC; +CREATE OR REPLACE PUBLIC SYNONYM VAUTH_SERII FOR SYS.VAUTH_SERII; + +-- PACK_UPDATE package (minimal version) +PROMPT Creating package PACK_UPDATE... +CREATE OR REPLACE PACKAGE SYS.PACK_UPDATE AS + FUNCTION get_version RETURN VARCHAR2; + PROCEDURE execute_sql(p_sql VARCHAR2); +END PACK_UPDATE; +/ + +CREATE OR REPLACE PACKAGE BODY SYS.PACK_UPDATE AS + + FUNCTION get_version RETURN VARCHAR2 IS + BEGIN + RETURN '1.0.0'; + END; + + PROCEDURE execute_sql(p_sql VARCHAR2) IS + BEGIN + EXECUTE IMMEDIATE p_sql; + EXCEPTION + WHEN OTHERS THEN + RAISE; + END; + +END PACK_UPDATE; +/ + +GRANT EXECUTE ON SYS.PACK_UPDATE TO PUBLIC; +CREATE OR REPLACE PUBLIC SYNONYM PACK_UPDATE FOR SYS.PACK_UPDATE; + +-- PACK_UTILS package (minimal version) +PROMPT Creating package PACK_UTILS... +CREATE OR REPLACE PACKAGE SYS.PACK_UTILS AS + FUNCTION get_db_version RETURN VARCHAR2; + FUNCTION get_instance_name RETURN VARCHAR2; +END PACK_UTILS; +/ + +CREATE OR REPLACE PACKAGE BODY SYS.PACK_UTILS AS + + FUNCTION get_db_version RETURN VARCHAR2 IS + v_version VARCHAR2(100); + BEGIN + SELECT version_full INTO v_version FROM v`$instance; + RETURN v_version; + EXCEPTION + WHEN OTHERS THEN RETURN NULL; + END; + + FUNCTION get_instance_name RETURN VARCHAR2 IS + v_instance VARCHAR2(100); + BEGIN + SELECT instance_name INTO v_instance FROM v`$instance; + RETURN v_instance; + EXCEPTION + WHEN OTHERS THEN RETURN NULL; + END; + +END PACK_UTILS; +/ + +GRANT EXECUTE ON SYS.PACK_UTILS TO PUBLIC; +CREATE OR REPLACE PUBLIC SYNONYM PACK_UTILS FOR SYS.PACK_UTILS; + +-- Grant UTL packages to PUBLIC +PROMPT Granting UTL packages... +GRANT EXECUTE ON UTL_FILE TO PUBLIC; +GRANT EXECUTE ON DBMS_LOCK TO PUBLIC; + +-- Grant UTL packages to CONTAFIN_ORACLE specifically +GRANT EXECUTE ON UTL_INADDR TO CONTAFIN_ORACLE; +GRANT EXECUTE ON UTL_TCP TO CONTAFIN_ORACLE; +GRANT EXECUTE ON UTL_SMTP TO CONTAFIN_ORACLE; +GRANT EXECUTE ON UTL_HTTP TO CONTAFIN_ORACLE; + +COMMIT; + +PROMPT +PROMPT ============================================= +PROMPT SYS objects created successfully +PROMPT ============================================= +PROMPT + +SELECT 'SYS_OBJECTS_COMPLETE' FROM dual; + +EXIT; +"@ + + Set-Content -Path $sysObjectsScript -Value $sysObjectsSql -Encoding ASCII + Write-Log "Created sys-objects.sql" + } + + # Execute SYS objects script + Write-LogSection "Executing sys-objects.sql" + + $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYS" -Password $SystemPassword -SqlFile $sysObjectsScript -AsSysdba + + if ($result -match "SYS_OBJECTS_COMPLETE") { + Write-LogSuccess "SYS objects created successfully" + } + else { + Write-LogWarning "Script completed but could not verify success" + Write-LogDebug $result + } + + # Verify created objects + Write-LogSection "Verifying SYS Objects" + + $verifySql = @" +SET PAGESIZE 100 FEEDBACK OFF VERIFY OFF HEADING ON ECHO OFF LINESIZE 200 + +PROMPT +PROMPT === SYS Objects Status === +PROMPT + +SELECT object_name, object_type, status +FROM dba_objects +WHERE owner = 'SYS' + AND object_name IN ('AUTH_PACK', 'AUTH_DETALII', 'AUTH_SERII', 'VAUTH_SERII', + 'PACK_UPDATE', 'PACK_UTILS', 'NEWSCHEMA', 'NEWSCHEMAPOST', 'NEWSCHEMAPROGRESS') +ORDER BY object_type, object_name; + +PROMPT +PROMPT === Public Synonyms === +PROMPT + +SELECT synonym_name, table_owner, table_name +FROM dba_synonyms +WHERE owner = 'PUBLIC' + AND table_name IN ('AUTH_PACK', 'AUTH_DETALII', 'AUTH_SERII', 'VAUTH_SERII', + 'PACK_UPDATE', 'PACK_UTILS', 'NEWSCHEMA', 'NEWSCHEMAPOST', 'NEWSCHEMAPROGRESS') +ORDER BY synonym_name; + +EXIT; +"@ + + $verifyResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYS" -Password $SystemPassword -SqlCommand $verifySql -AsSysdba + + Write-Host $verifyResult + + # Count invalid objects + $invalidSql = @" +SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF +SELECT 'INVALID_COUNT:' || COUNT(*) +FROM dba_objects +WHERE owner = 'SYS' + AND object_name IN ('AUTH_PACK', 'AUTH_DETALII', 'AUTH_SERII', 'VAUTH_SERII', + 'PACK_UPDATE', 'PACK_UTILS', 'NEWSCHEMA', 'NEWSCHEMAPOST', 'NEWSCHEMAPROGRESS') + AND status = 'INVALID'; +EXIT; +"@ + + $invalidResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYS" -Password $SystemPassword -SqlCommand $invalidSql -AsSysdba -Silent + + $invalidCount = 0 + if ($invalidResult -match "INVALID_COUNT:(\d+)") { + $invalidCount = [int]$Matches[1] + } + + if ($invalidCount -gt 0) { + Write-LogWarning "$invalidCount invalid SYS objects found" + Write-Log "Attempting to recompile invalid objects..." + + $recompileSql = @" +SET SERVEROUTPUT ON +BEGIN + DBMS_UTILITY.COMPILE_SCHEMA('SYS', FALSE); +END; +/ +EXIT; +"@ + Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYS" -Password $SystemPassword -SqlCommand $recompileSql -AsSysdba -Silent + } + else { + Write-LogSuccess "All SYS objects are valid" + } + + # Summary + Write-LogSection "Setup Complete" + Write-LogSuccess "SYS objects installation completed!" + Write-Log "" + Write-Log "Objects created:" + Write-Log " - AUTH_PACK package" + Write-Log " - AUTH_DETALII table" + Write-Log " - AUTH_SERII table" + Write-Log " - VAUTH_SERII view" + Write-Log " - PACK_UPDATE package" + Write-Log " - PACK_UTILS package" + Write-Log " - Public synonyms and grants" + Write-Log "" + Write-Log "Next steps:" + Write-Log " 1. Run 03-import-contafin.ps1 to import CONTAFIN_ORACLE schema" + + Close-LogFile -Success $true + exit 0 +} +catch { + Write-LogError "Setup failed: $_" + Write-LogError $_.ScriptStackTrace + Close-LogFile -Success $false + exit 1 +} diff --git a/proxmox/lxc108-oracle/roa-windows-setup/scripts/03-import-contafin.ps1 b/proxmox/lxc108-oracle/roa-windows-setup/scripts/03-import-contafin.ps1 index e398d94..17c9540 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/scripts/03-import-contafin.ps1 +++ b/proxmox/lxc108-oracle/roa-windows-setup/scripts/03-import-contafin.ps1 @@ -1,334 +1,339 @@ -#Requires -Version 5.1 - -<# -.SYNOPSIS - Import CONTAFIN_ORACLE schema from Data Pump dump file. - -.DESCRIPTION - Imports the CONTAFIN_ORACLE schema using Oracle Data Pump (impdp). - - Creates DMPDIR directory if not exists - - Runs impdp with optimal parameters - - Verifies import (object count, invalid objects) - -.PARAMETER DumpFile - Path to the CONTAFIN_ORACLE DMP file. If not specified, auto-detects from DmpDir. - -.PARAMETER OracleHome - Oracle home directory. If not specified, auto-detects. - -.PARAMETER ServiceName - Database service name. Default: XEPDB1 - -.PARAMETER SystemPassword - SYSTEM user password. Default: romfastsoft - -.PARAMETER DmpDir - Oracle directory path for Data Pump. Default: C:\DMPDIR - -.PARAMETER TableExistsAction - What to do if table exists: SKIP, APPEND, TRUNCATE, REPLACE. Default: REPLACE - -.PARAMETER Parallel - Number of parallel workers for import. Default: 2 - -.EXAMPLE - .\03-import-contafin.ps1 -DumpFile "C:\dumps\CONTAFIN_ORACLE.dmp" - -.EXAMPLE - .\03-import-contafin.ps1 -DumpFile "D:\backups\contafin.dmp" -ServiceName "ROA" -SystemPassword "mypass" - -.NOTES - File Name : 03-import-contafin.ps1 - Prerequisite : Run 01-setup-database.ps1 and 02-create-sys-objects.ps1 first - Copyright 2024 : ROMFAST -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory = $false)] - [string]$DumpFile, - - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $false)] - [string]$ServiceName = "XEPDB1", - - [Parameter(Mandatory = $false)] - [string]$SystemPassword = "romfastsoft", - - [Parameter(Mandatory = $false)] - [string]$DmpDir = "C:\DMPDIR", - - [Parameter(Mandatory = $false)] - [ValidateSet('SKIP', 'APPEND', 'TRUNCATE', 'REPLACE')] - [string]$TableExistsAction = "REPLACE", - - [Parameter(Mandatory = $false)] - [int]$Parallel = 1 -) - -$ErrorActionPreference = 'Stop' - -# Source library functions -. "$PSScriptRoot\lib\logging-functions.ps1" -. "$PSScriptRoot\lib\oracle-functions.ps1" - -# Initialize logging -$logPath = Join-Path $PSScriptRoot "..\logs\03-import-contafin_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" -Initialize-LogFile -LogPath $logPath -ScriptName "03-import-contafin.ps1" - -# Auto-detect DumpFile if not specified -if (-not $DumpFile) { - Write-Host "[INFO] DumpFile not specified, searching in $DmpDir..." -ForegroundColor Yellow - $candidates = Get-ChildItem -Path $DmpDir -Filter "*contafin*.dmp" -ErrorAction SilentlyContinue | - Sort-Object LastWriteTime -Descending - if ($candidates) { - $DumpFile = $candidates[0].FullName - Write-Host "[INFO] Auto-detected DumpFile: $DumpFile" -ForegroundColor Green - } else { - throw "No CONTAFIN_ORACLE dump file found in $DmpDir. Please specify -DumpFile parameter or copy *contafin*.dmp to $DmpDir" - } -} - -# Validate DumpFile exists -if (-not (Test-Path $DumpFile)) { - throw "DumpFile not found: $DumpFile" -} - -try { - Write-LogSection "Importing CONTAFIN_ORACLE Schema" - - # Validate Oracle installation - $oraHome = Get-OracleHome -OracleHome $OracleHome - Write-LogSuccess "Oracle Home: $oraHome" - - # Test connection - Write-Log "Testing database connection..." - if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword)) { - throw "Cannot connect to database. Please verify ServiceName and SystemPassword." - } - Write-LogSuccess "Database connection successful" - - # Verify CONTAFIN_ORACLE user exists - Write-Log "Verifying CONTAFIN_ORACLE user exists..." - if (-not (Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE")) { - throw "User CONTAFIN_ORACLE does not exist. Run 01-setup-database.ps1 first." - } - Write-LogSuccess "User CONTAFIN_ORACLE exists" - - # Create/verify DMPDIR - Write-LogSection "Configuring Data Pump Directory" - - if (-not (Test-Path -Path $DmpDir)) { - New-Item -ItemType Directory -Path $DmpDir -Force | Out-Null - Write-Log "Created directory: $DmpDir" - } - - New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $DmpDir - - # Copy dump file to DMPDIR if not already there - $dumpFileName = Split-Path -Path $DumpFile -Leaf - $targetDumpPath = Join-Path $DmpDir $dumpFileName - - # Resolve paths for accurate comparison (handles casing and path format differences) - $resolvedSource = (Resolve-Path -Path $DumpFile -ErrorAction SilentlyContinue).Path - $resolvedTarget = if (Test-Path $targetDumpPath) { (Resolve-Path -Path $targetDumpPath).Path } else { $null } - - if ($resolvedSource -ne $resolvedTarget) { - Write-Log "Copying dump file to DMPDIR..." - Copy-Item -Path $DumpFile -Destination $targetDumpPath -Force - Write-LogSuccess "Dump file copied to: $targetDumpPath" - } - else { - Write-Log "Dump file already in DMPDIR: $targetDumpPath" - } - - # Check dump file size - $dumpFileInfo = Get-Item $targetDumpPath - $dumpSizeMB = [math]::Round($dumpFileInfo.Length / 1MB, 2) - Write-Log "Dump file size: $dumpSizeMB MB" - - # Get current object count before import - Write-Log "Getting current object count for CONTAFIN_ORACLE..." - $beforeCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE" - - $beforeTotal = if ($beforeCounts['TOTAL']) { $beforeCounts['TOTAL'] } else { 0 } - Write-Log "Objects before import: $beforeTotal" - - # Run import - Write-LogSection "Running Data Pump Import" - - $impdpPath = Join-Path $oraHome "bin\impdp.exe" - if (-not (Test-Path -Path $impdpPath)) { - throw "impdp.exe not found at: $impdpPath" - } - - $importLogFile = "CONTAFIN_ORACLE_import_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" - - # Get listener host for connection (handles listener bound to specific IP) - $dbHost = Get-ListenerHost -OracleHome $oraHome - $dbPort = 1521 - - # Build impdp command with EZConnect format - $connString = "SYSTEM/`"$SystemPassword`"@${dbHost}:${dbPort}/${ServiceName}" - - $impdpParams = @( - "`"$connString`"", - "directory=DMPDIR", - "dumpfile=$dumpFileName", - "logfile=$importLogFile", - "schemas=CONTAFIN_ORACLE", - "table_exists_action=$TableExistsAction", - "parallel=$Parallel" # Always specify to override DMP metadata (XE requires parallel=1) - ) - - $arguments = $impdpParams -join " " - - Write-Log "Executing impdp..." - Write-Log "Parameters: directory=DMPDIR dumpfile=$dumpFileName schemas=CONTAFIN_ORACLE table_exists_action=$TableExistsAction" - - # Set Oracle environment - $env:ORACLE_HOME = $oraHome - $env:PATH = "$oraHome\bin;$env:PATH" - $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" - - $startTime = Get-Date - - # Execute impdp - $process = Start-Process -FilePath $impdpPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru - - $duration = (Get-Date) - $startTime - Write-Log "Import completed in $([math]::Round($duration.TotalMinutes, 2)) minutes" - - # Check import log - $importLogPath = Join-Path $DmpDir $importLogFile - if (Test-Path -Path $importLogPath) { - Write-Log "Import log: $importLogPath" - - # Read and analyze log for errors - $logContent = Get-Content -Path $importLogPath -Raw - - if ($logContent -match "ORA-\d{5}:") { - Write-LogWarning "Import completed with Oracle errors. Check log for details." - } - - if ($logContent -match "successfully loaded/unloaded") { - Write-LogSuccess "Objects successfully loaded" - } - - # Count errors and warnings in log - $errorCount = ([regex]::Matches($logContent, "ORA-\d{5}:")).Count - $warningCount = ([regex]::Matches($logContent, "ORA-39\d{3}:")).Count - - Write-Log "Errors in log: $errorCount" - Write-Log "Warnings in log: $warningCount" - } - - # Verify import - Write-LogSection "Verifying Import" - - # Get object counts after import - $afterCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE" - - $afterTotal = if ($afterCounts['TOTAL']) { $afterCounts['TOTAL'] } else { 0 } - $afterInvalid = if ($afterCounts['INVALID']) { $afterCounts['INVALID'] } else { 0 } - - Write-Log "Objects after import: $afterTotal" - Write-Log "Objects imported: $($afterTotal - $beforeTotal)" - - # Display object counts by type - Write-Log "" - Write-Log "Object counts by type:" - foreach ($key in $afterCounts.Keys | Sort-Object) { - if ($key -ne 'TOTAL' -and $key -ne 'INVALID') { - Write-Log " $($key): $($afterCounts[$key])" - } - } - - if ($afterInvalid -gt 0) { - Write-LogWarning "Invalid objects: $afterInvalid" - - # List invalid objects - $invalidSql = @" -SET PAGESIZE 100 FEEDBACK OFF VERIFY OFF HEADING ON ECHO OFF LINESIZE 200 -SELECT object_name, object_type -FROM dba_objects -WHERE owner = 'CONTAFIN_ORACLE' - AND status = 'INVALID' -ORDER BY object_type, object_name; -EXIT; -"@ - $invalidList = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $invalidSql -Silent - - Write-Log "Invalid objects:" - Write-Host $invalidList - - # Attempt to recompile - Write-Log "Attempting to recompile invalid objects..." - $recompileSql = @" -SET SERVEROUTPUT ON -BEGIN - DBMS_UTILITY.COMPILE_SCHEMA('CONTAFIN_ORACLE', FALSE); -END; -/ -EXIT; -"@ - Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $recompileSql -Silent - - # Recheck invalid count - $recheckCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE" - - $recheckInvalid = if ($recheckCounts['INVALID']) { $recheckCounts['INVALID'] } else { 0 } - - if ($recheckInvalid -lt $afterInvalid) { - Write-LogSuccess "Recompilation fixed $($afterInvalid - $recheckInvalid) objects" - } - - if ($recheckInvalid -gt 0) { - Write-LogWarning "Still $recheckInvalid invalid objects remaining" - } - } - else { - Write-LogSuccess "No invalid objects" - } - - # Summary - Write-LogSection "Import Complete" - - if ($process.ExitCode -eq 0) { - Write-LogSuccess "CONTAFIN_ORACLE schema imported successfully!" - } - else { - Write-LogWarning "Import completed with warnings (exit code: $($process.ExitCode))" - } - - Write-Log "" - Write-Log "Summary:" - Write-Log " Dump file: $dumpFileName ($dumpSizeMB MB)" - Write-Log " Service: $ServiceName" - Write-Log " Duration: $([math]::Round($duration.TotalMinutes, 2)) minutes" - Write-Log " Objects: $afterTotal" - Write-Log " Import log: $importLogPath" - Write-Log "" - Write-Log "Next steps:" - Write-Log " 1. Run 04-create-synonyms-grants.ps1 to create public synonyms" - Write-Log " 2. Run 05-import-companies.ps1 to import company schemas" - - Close-LogFile -Success ($process.ExitCode -eq 0) - exit $process.ExitCode -} -catch { - Write-LogError "Import failed: $_" - Write-LogError $_.ScriptStackTrace - Close-LogFile -Success $false - exit 1 -} +#Requires -Version 5.1 + +<# +.SYNOPSIS + Import CONTAFIN_ORACLE schema from Data Pump dump file. + +.DESCRIPTION + Imports the CONTAFIN_ORACLE schema using Oracle Data Pump (impdp). + - Creates DMPDIR directory if not exists + - Runs impdp with optimal parameters + - Verifies import (object count, invalid objects) + +.PARAMETER DumpFile + Path to the CONTAFIN_ORACLE DMP file. If not specified, auto-detects from DmpDir. + +.PARAMETER OracleHome + Oracle home directory. If not specified, auto-detects. + +.PARAMETER ServiceName + Database service name. Default: XEPDB1 + +.PARAMETER SystemPassword + SYSTEM user password. Default: romfastsoft + +.PARAMETER DmpDir + Oracle directory path for Data Pump. Default: C:\DMPDIR + +.PARAMETER TableExistsAction + What to do if table exists: SKIP, APPEND, TRUNCATE, REPLACE. Default: REPLACE + +.PARAMETER Parallel + Number of parallel workers for import. Default: 2 + +.EXAMPLE + .\03-import-contafin.ps1 -DumpFile "C:\dumps\CONTAFIN_ORACLE.dmp" + +.EXAMPLE + .\03-import-contafin.ps1 -DumpFile "D:\backups\contafin.dmp" -ServiceName "ROA" -SystemPassword "mypass" + +.NOTES + File Name : 03-import-contafin.ps1 + Prerequisite : Run 01-setup-database.ps1 and 02-create-sys-objects.ps1 first + Copyright 2024 : ROMFAST +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $false)] + [string]$DumpFile, + + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $false)] + [string]$ServiceName = "XEPDB1", + + [Parameter(Mandatory = $false)] + [string]$SystemPassword = "romfastsoft", + + [Parameter(Mandatory = $false)] + [string]$DmpDir = "C:\DMPDIR", + + [Parameter(Mandatory = $false)] + [ValidateSet('SKIP', 'APPEND', 'TRUNCATE', 'REPLACE')] + [string]$TableExistsAction = "REPLACE", + + [Parameter(Mandatory = $false)] + [int]$Parallel = 1 +) + +$ErrorActionPreference = 'Stop' + +# Source library functions +. "$PSScriptRoot\lib\logging-functions.ps1" +. "$PSScriptRoot\lib\oracle-functions.ps1" + +# Initialize logging +$logPath = Join-Path $PSScriptRoot "..\logs\03-import-contafin_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" +Initialize-LogFile -LogPath $logPath -ScriptName "03-import-contafin.ps1" + +# Auto-detect DumpFile if not specified +if (-not $DumpFile) { + Write-Host "[INFO] DumpFile not specified, searching in $DmpDir..." -ForegroundColor Yellow + $candidates = Get-ChildItem -Path $DmpDir -Filter "*contafin*.dmp" -ErrorAction SilentlyContinue | + Sort-Object LastWriteTime -Descending + if ($candidates) { + $DumpFile = $candidates[0].FullName + Write-Host "[INFO] Auto-detected DumpFile: $DumpFile" -ForegroundColor Green + } else { + throw "No CONTAFIN_ORACLE dump file found in $DmpDir. Please specify -DumpFile parameter or copy *contafin*.dmp to $DmpDir" + } +} + +# Validate DumpFile exists +if (-not (Test-Path $DumpFile)) { + throw "DumpFile not found: $DumpFile" +} + +try { + Write-LogSection "Importing CONTAFIN_ORACLE Schema" + + # Validate Oracle installation + $oraHome = Get-OracleHome -OracleHome $OracleHome + Write-LogSuccess "Oracle Home: $oraHome" + + # Test connection + Write-Log "Testing database connection..." + if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword)) { + throw "Cannot connect to database. Please verify ServiceName and SystemPassword." + } + Write-LogSuccess "Database connection successful" + + # Verify CONTAFIN_ORACLE user exists + Write-Log "Verifying CONTAFIN_ORACLE user exists..." + if (-not (Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE")) { + throw "User CONTAFIN_ORACLE does not exist. Run 01-setup-database.ps1 first." + } + Write-LogSuccess "User CONTAFIN_ORACLE exists" + + # Create/verify DMPDIR + Write-LogSection "Configuring Data Pump Directory" + + if (-not (Test-Path -Path $DmpDir)) { + New-Item -ItemType Directory -Path $DmpDir -Force | Out-Null + Write-Log "Created directory: $DmpDir" + } + + # Valoarea de retur nu era verificata - de aici si "True" ratacit in log. + # Mai rau: cand directorul nu era utilizabil, importul mergea mai departe si + # cadea abia in impdp cu ORA-29283, la zeci de linii distanta de cauza. + if (-not (New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $DmpDir)) { + throw "Directorul Data Pump DMPDIR ($DmpDir) nu e utilizabil de catre baza. Vezi mesajele de mai sus." + } + + # Copy dump file to DMPDIR if not already there + $dumpFileName = Split-Path -Path $DumpFile -Leaf + $targetDumpPath = Join-Path $DmpDir $dumpFileName + + # Resolve paths for accurate comparison (handles casing and path format differences) + $resolvedSource = (Resolve-Path -Path $DumpFile -ErrorAction SilentlyContinue).Path + $resolvedTarget = if (Test-Path $targetDumpPath) { (Resolve-Path -Path $targetDumpPath).Path } else { $null } + + if ($resolvedSource -ne $resolvedTarget) { + Write-Log "Copying dump file to DMPDIR..." + Copy-Item -Path $DumpFile -Destination $targetDumpPath -Force + Write-LogSuccess "Dump file copied to: $targetDumpPath" + } + else { + Write-Log "Dump file already in DMPDIR: $targetDumpPath" + } + + # Check dump file size + $dumpFileInfo = Get-Item $targetDumpPath + $dumpSizeMB = [math]::Round($dumpFileInfo.Length / 1MB, 2) + Write-Log "Dump file size: $dumpSizeMB MB" + + # Get current object count before import + Write-Log "Getting current object count for CONTAFIN_ORACLE..." + $beforeCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE" + + $beforeTotal = if ($beforeCounts['TOTAL']) { $beforeCounts['TOTAL'] } else { 0 } + Write-Log "Objects before import: $beforeTotal" + + # Run import + Write-LogSection "Running Data Pump Import" + + $impdpPath = Join-Path $oraHome "bin\impdp.exe" + if (-not (Test-Path -Path $impdpPath)) { + throw "impdp.exe not found at: $impdpPath" + } + + $importLogFile = "CONTAFIN_ORACLE_import_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" + + # Get listener host for connection (handles listener bound to specific IP) + $dbHost = Get-ListenerHost -OracleHome $oraHome + $dbPort = 1521 + + # Build impdp command with EZConnect format + $connString = "SYSTEM/`"$SystemPassword`"@${dbHost}:${dbPort}/${ServiceName}" + + $impdpParams = @( + "`"$connString`"", + "directory=DMPDIR", + "dumpfile=$dumpFileName", + "logfile=$importLogFile", + "schemas=CONTAFIN_ORACLE", + "table_exists_action=$TableExistsAction", + "parallel=$Parallel" # Always specify to override DMP metadata (XE requires parallel=1) + ) + + $arguments = $impdpParams -join " " + + Write-Log "Executing impdp..." + Write-Log "Parameters: directory=DMPDIR dumpfile=$dumpFileName schemas=CONTAFIN_ORACLE table_exists_action=$TableExistsAction" + + # Set Oracle environment + $env:ORACLE_HOME = $oraHome + $env:PATH = "$oraHome\bin;$env:PATH" + $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" + + $startTime = Get-Date + + # Execute impdp + $process = Start-Process -FilePath $impdpPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru + + $duration = (Get-Date) - $startTime + Write-Log "Import completed in $([math]::Round($duration.TotalMinutes, 2)) minutes" + + # Check import log + $importLogPath = Join-Path $DmpDir $importLogFile + if (Test-Path -Path $importLogPath) { + Write-Log "Import log: $importLogPath" + + # Read and analyze log for errors + $logContent = Get-Content -Path $importLogPath -Raw + + if ($logContent -match "ORA-\d{5}:") { + Write-LogWarning "Import completed with Oracle errors. Check log for details." + } + + if ($logContent -match "successfully loaded/unloaded") { + Write-LogSuccess "Objects successfully loaded" + } + + # Count errors and warnings in log + $errorCount = ([regex]::Matches($logContent, "ORA-\d{5}:")).Count + $warningCount = ([regex]::Matches($logContent, "ORA-39\d{3}:")).Count + + Write-Log "Errors in log: $errorCount" + Write-Log "Warnings in log: $warningCount" + } + + # Verify import + Write-LogSection "Verifying Import" + + # Get object counts after import + $afterCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE" + + $afterTotal = if ($afterCounts['TOTAL']) { $afterCounts['TOTAL'] } else { 0 } + $afterInvalid = if ($afterCounts['INVALID']) { $afterCounts['INVALID'] } else { 0 } + + Write-Log "Objects after import: $afterTotal" + Write-Log "Objects imported: $($afterTotal - $beforeTotal)" + + # Display object counts by type + Write-Log "" + Write-Log "Object counts by type:" + foreach ($key in $afterCounts.Keys | Sort-Object) { + if ($key -ne 'TOTAL' -and $key -ne 'INVALID') { + Write-Log " $($key): $($afterCounts[$key])" + } + } + + if ($afterInvalid -gt 0) { + Write-LogWarning "Invalid objects: $afterInvalid" + + # List invalid objects + $invalidSql = @" +SET PAGESIZE 100 FEEDBACK OFF VERIFY OFF HEADING ON ECHO OFF LINESIZE 200 +SELECT object_name, object_type +FROM dba_objects +WHERE owner = 'CONTAFIN_ORACLE' + AND status = 'INVALID' +ORDER BY object_type, object_name; +EXIT; +"@ + $invalidList = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $invalidSql -Silent + + Write-Log "Invalid objects:" + Write-Host $invalidList + + # Attempt to recompile + Write-Log "Attempting to recompile invalid objects..." + $recompileSql = @" +SET SERVEROUTPUT ON +BEGIN + DBMS_UTILITY.COMPILE_SCHEMA('CONTAFIN_ORACLE', FALSE); +END; +/ +EXIT; +"@ + Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $recompileSql -Silent + + # Recheck invalid count + $recheckCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE" + + $recheckInvalid = if ($recheckCounts['INVALID']) { $recheckCounts['INVALID'] } else { 0 } + + if ($recheckInvalid -lt $afterInvalid) { + Write-LogSuccess "Recompilation fixed $($afterInvalid - $recheckInvalid) objects" + } + + if ($recheckInvalid -gt 0) { + Write-LogWarning "Still $recheckInvalid invalid objects remaining" + } + } + else { + Write-LogSuccess "No invalid objects" + } + + # Summary + Write-LogSection "Import Complete" + + if ($process.ExitCode -eq 0) { + Write-LogSuccess "CONTAFIN_ORACLE schema imported successfully!" + } + else { + Write-LogWarning "Import completed with warnings (exit code: $($process.ExitCode))" + } + + Write-Log "" + Write-Log "Summary:" + Write-Log " Dump file: $dumpFileName ($dumpSizeMB MB)" + Write-Log " Service: $ServiceName" + Write-Log " Duration: $([math]::Round($duration.TotalMinutes, 2)) minutes" + Write-Log " Objects: $afterTotal" + Write-Log " Import log: $importLogPath" + Write-Log "" + Write-Log "Next steps:" + Write-Log " 1. Run 04-create-synonyms-grants.ps1 to create public synonyms" + Write-Log " 2. Run 05-import-companies.ps1 to import company schemas" + + Close-LogFile -Success ($process.ExitCode -eq 0) + exit $process.ExitCode +} +catch { + Write-LogError "Import failed: $_" + Write-LogError $_.ScriptStackTrace + Close-LogFile -Success $false + exit 1 +} diff --git a/proxmox/lxc108-oracle/roa-windows-setup/scripts/05-import-companies.ps1 b/proxmox/lxc108-oracle/roa-windows-setup/scripts/05-import-companies.ps1 index c53f9a1..a4b874d 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/scripts/05-import-companies.ps1 +++ b/proxmox/lxc108-oracle/roa-windows-setup/scripts/05-import-companies.ps1 @@ -1,432 +1,448 @@ -#Requires -Version 5.1 - -<# -.SYNOPSIS - Batch import company schemas from Data Pump dump files. - -.DESCRIPTION - Imports company schemas from dump files. CRITICAL: Reads passwords from - CONTAFIN_ORACLE.NOM_FIRME table to ensure correct authentication. - - For each company: - 1. Queries NOM_FIRME for schema name and password - 2. Finds matching DMP file in DumpDirectory - 3. Creates user with password from NOM_FIRME - 4. Runs impdp to import schema - 5. Logs progress - -.PARAMETER DumpDirectory - Directory containing company DMP files. If not specified, defaults to C:\DMPDIR. - -.PARAMETER OracleHome - Oracle home directory. If not specified, auto-detects. - -.PARAMETER ServiceName - Database service name. Default: XEPDB1 - -.PARAMETER SystemPassword - SYSTEM user password. Default: romfastsoft - -.PARAMETER ParallelJobs - Number of parallel import jobs. Default: 2 - -.PARAMETER Companies - Optional array of specific company names to import. If not specified, imports all. - -.PARAMETER DryRun - Show what would be imported without actually importing. - -.EXAMPLE - .\05-import-companies.ps1 -DumpDirectory "C:\dumps\companies" - -.EXAMPLE - .\05-import-companies.ps1 -DumpDirectory "D:\backups" -Companies "FIRMA1","FIRMA2" - -.EXAMPLE - .\05-import-companies.ps1 -DumpDirectory "C:\dumps" -DryRun - -.NOTES - File Name : 05-import-companies.ps1 - Prerequisite : Run 04-create-synonyms-grants.ps1 first - Copyright 2024 : ROMFAST -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory = $false)] - [string]$DumpDirectory = "C:\DMPDIR", - - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $false)] - [string]$ServiceName = "XEPDB1", - - [Parameter(Mandatory = $false)] - [string]$SystemPassword = "romfastsoft", - - [Parameter(Mandatory = $false)] - [int]$ParallelJobs = 2, - - [Parameter(Mandatory = $false)] - [string[]]$Companies, - - [Parameter(Mandatory = $false)] - [switch]$DryRun -) - -$ErrorActionPreference = 'Stop' - -# Source library functions -. "$PSScriptRoot\lib\logging-functions.ps1" -. "$PSScriptRoot\lib\oracle-functions.ps1" - -# Initialize logging -$logPath = Join-Path $PSScriptRoot "..\logs\05-import-companies_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" -Initialize-LogFile -LogPath $logPath -ScriptName "05-import-companies.ps1" - -try { - Write-LogSection "Batch Import Company Schemas" - - # Validate DumpDirectory exists - if (-not (Test-Path $DumpDirectory -PathType Container)) { - throw "DumpDirectory not found: $DumpDirectory" - } - Write-Log "Using dump directory: $DumpDirectory" - - if ($DryRun) { - Write-LogWarning "DRY RUN MODE - No changes will be made" - } - - # Validate Oracle installation - $oraHome = Get-OracleHome -OracleHome $OracleHome - Write-LogSuccess "Oracle Home: $oraHome" - - # Test connection - Write-Log "Testing database connection..." - if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword)) { - throw "Cannot connect to database. Please verify ServiceName and SystemPassword." - } - Write-LogSuccess "Database connection successful" - - # Get company list from NOM_FIRME - Write-LogSection "Reading Company List from NOM_FIRME" - - $companySql = @" -SET PAGESIZE 10000 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 500 -SELECT schema || '|' || parola -FROM CONTAFIN_ORACLE.NOM_FIRME -WHERE sters = 0 - AND schema IS NOT NULL -ORDER BY schema; -EXIT; -"@ - - $companyResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $companySql -Silent - - # Parse company data - $companyData = @{} - foreach ($line in $companyResult -split "`n") { - $line = $line.Trim() - if ($line -match "^([A-Z0-9_]+)\|(.*)$") { - $schema = $Matches[1] - $password = $Matches[2] - if (-not $password) { $password = "ROMFASTSOFT" } - $companyData[$schema] = $password - } - } - - Write-Log "Found $($companyData.Count) companies in NOM_FIRME" - - if ($companyData.Count -eq 0) { - Write-LogWarning "No companies found in NOM_FIRME" - Close-LogFile -Success $true - exit 0 - } - - # Filter companies if specified - if ($Companies) { - $filteredData = @{} - foreach ($company in $Companies) { - $companyUpper = $company.ToUpper() - if ($companyData.ContainsKey($companyUpper)) { - $filteredData[$companyUpper] = $companyData[$companyUpper] - } - else { - Write-LogWarning "Company $companyUpper not found in NOM_FIRME" - } - } - $companyData = $filteredData - Write-Log "Filtered to $($companyData.Count) companies" - } - - # Find matching dump files - Write-LogSection "Scanning Dump Files" - - $dumpFiles = Get-ChildItem -Path $DumpDirectory -Filter "*.dmp" -File - Write-Log "Found $($dumpFiles.Count) DMP files in $DumpDirectory" - - # Match companies with dump files - $importList = @() - $notFoundList = @() - - foreach ($schema in $companyData.Keys) { - # Try to find matching dump file (case-insensitive) - $matchingFile = $dumpFiles | Where-Object { - $_.BaseName -ieq $schema -or - $_.BaseName -imatch "^$schema[_\.]" -or - $_.BaseName -imatch "^${schema}$" - } | Select-Object -First 1 - - if ($matchingFile) { - $importList += [PSCustomObject]@{ - Schema = $schema - Password = $companyData[$schema] - DumpFile = $matchingFile.FullName - DumpSize = [math]::Round($matchingFile.Length / 1MB, 2) - } - } - else { - $notFoundList += $schema - } - } - - Write-Log "Companies to import: $($importList.Count)" - if ($notFoundList.Count -gt 0) { - Write-LogWarning "Companies without matching dump file: $($notFoundList.Count)" - foreach ($schema in $notFoundList) { - Write-Log " - $schema (no DMP file found)" - } - } - - if ($importList.Count -eq 0) { - Write-LogWarning "No companies to import" - Close-LogFile -Success $true - exit 0 - } - - # Display import plan - Write-LogSection "Import Plan" - - $totalSize = ($importList | Measure-Object -Property DumpSize -Sum).Sum - Write-Log "Total data to import: $totalSize MB" - Write-Log "" - Write-Log "Companies to import:" - foreach ($item in $importList) { - Write-Log " - $($item.Schema): $($item.DumpFile) ($($item.DumpSize) MB)" - } - - if ($DryRun) { - Write-LogSection "Dry Run Complete" - Write-Log "Would import $($importList.Count) company schemas" - Close-LogFile -Success $true - exit 0 - } - - # Ensure DMPDIR exists - $dmpDir = "C:\DMPDIR" - if (-not (Test-Path -Path $dmpDir)) { - New-Item -ItemType Directory -Path $dmpDir -Force | Out-Null - } - - # Process each company - Write-LogSection "Importing Companies" - - $successCount = 0 - $failCount = 0 - $startTime = Get-Date - - foreach ($item in $importList) { - Write-LogSection "Importing $($item.Schema)" - - try { - # Check if user already exists - $userExists = Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName $item.Schema - - if ($userExists) { - Write-Log "User $($item.Schema) already exists" - - # Get current object count - $beforeCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName $item.Schema - - $beforeTotal = if ($beforeCounts['TOTAL']) { $beforeCounts['TOTAL'] } else { 0 } - Write-Log "Existing objects: $beforeTotal" - } - else { - # Create user - Write-Log "Creating user $($item.Schema)..." - - $createUserSql = @" -SET ECHO OFF FEEDBACK ON VERIFY OFF - -CREATE USER $($item.Schema) - IDENTIFIED BY "$($item.Password)" - DEFAULT TABLESPACE ROA - TEMPORARY TABLESPACE TEMP - PROFILE DEFAULT; - -GRANT CONNECT TO $($item.Schema); -GRANT RESOURCE TO $($item.Schema); -GRANT CREATE MATERIALIZED VIEW TO $($item.Schema); -GRANT CREATE PROCEDURE TO $($item.Schema); -GRANT CREATE SEQUENCE TO $($item.Schema); -GRANT CREATE TABLE TO $($item.Schema); -GRANT CREATE TRIGGER TO $($item.Schema); -GRANT CREATE VIEW TO $($item.Schema); -GRANT DEBUG CONNECT SESSION TO $($item.Schema); -GRANT SELECT ANY TABLE TO $($item.Schema); -GRANT CREATE TYPE TO $($item.Schema); -GRANT UNLIMITED TABLESPACE TO $($item.Schema); - -COMMIT; -SELECT 'USER_CREATED' FROM dual; -EXIT; -"@ - - $createResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $createUserSql -Silent - - if ($createResult -match "USER_CREATED") { - Write-LogSuccess "User $($item.Schema) created" - } - else { - throw "Failed to create user $($item.Schema)" - } - } - - # Copy dump file to DMPDIR if not already there - $dumpFileName = Split-Path -Path $item.DumpFile -Leaf - $targetDumpPath = Join-Path $dmpDir $dumpFileName - - if ($item.DumpFile -ne $targetDumpPath) { - Write-Log "Copying dump file to DMPDIR..." - Copy-Item -Path $item.DumpFile -Destination $targetDumpPath -Force - } - - # Run import - Write-Log "Running impdp for $($item.Schema)..." - - $impdpPath = Join-Path $oraHome "bin\impdp.exe" - $importLogFile = "$($item.Schema)_import_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" - - # Use EZConnect format for impdp (handles listener bound to specific IP) - $dbHost = Get-ListenerHost -OracleHome $oraHome - $connString = "SYSTEM/`"$SystemPassword`"@${dbHost}:1521/$ServiceName" - - # Check if dump has different schema name (remapping needed) - $remapParam = "" - $dumpBaseName = [System.IO.Path]::GetFileNameWithoutExtension($dumpFileName).ToUpper() - if ($dumpBaseName -ne $item.Schema) { - $remapParam = "remap_schema=$dumpBaseName`:$($item.Schema)" - Write-Log "Schema remap: $dumpBaseName -> $($item.Schema)" - } - - $impdpParams = @( - "`"$connString`"", - "directory=DMPDIR", - "dumpfile=$dumpFileName", - "logfile=$importLogFile", - "schemas=$($item.Schema)", - "table_exists_action=REPLACE", - "parallel=1" # Required for Oracle XE - ) - - if ($remapParam) { - $impdpParams += $remapParam - } - - $arguments = $impdpParams -join " " - - # Set Oracle environment - $env:ORACLE_HOME = $oraHome - $env:PATH = "$oraHome\bin;$env:PATH" - $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" - - $importStart = Get-Date - $process = Start-Process -FilePath $impdpPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru - - $importDuration = (Get-Date) - $importStart - Write-Log "Import completed in $([math]::Round($importDuration.TotalMinutes, 2)) minutes" - - # Verify import - $afterCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName $item.Schema - - $afterTotal = if ($afterCounts['TOTAL']) { $afterCounts['TOTAL'] } else { 0 } - $afterInvalid = if ($afterCounts['INVALID']) { $afterCounts['INVALID'] } else { 0 } - - Write-Log "Objects after import: $afterTotal" - - if ($afterInvalid -gt 0) { - Write-LogWarning "Invalid objects: $afterInvalid" - - # Recompile - $recompileSql = @" -BEGIN - DBMS_UTILITY.COMPILE_SCHEMA('$($item.Schema)', FALSE); -END; -/ -EXIT; -"@ - Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $recompileSql -Silent - } - - if ($process.ExitCode -eq 0) { - Write-LogSuccess "$($item.Schema) imported successfully ($afterTotal objects)" - $successCount++ - } - else { - Write-LogWarning "$($item.Schema) import completed with warnings" - $successCount++ - } - - # Clean up copied dump file - if ($item.DumpFile -ne $targetDumpPath -and (Test-Path -Path $targetDumpPath)) { - Remove-Item -Path $targetDumpPath -Force -ErrorAction SilentlyContinue - } - } - catch { - Write-LogError "Failed to import $($item.Schema): $_" - $failCount++ - } - } - - $totalDuration = (Get-Date) - $startTime - - # Summary - Write-LogSection "Import Complete" - - Write-LogSuccess "Company import batch completed!" - Write-Log "" - Write-Log "Summary:" - Write-Log " Total companies: $($importList.Count)" - Write-Log " Successful: $successCount" - Write-Log " Failed: $failCount" - Write-Log " Total duration: $([math]::Round($totalDuration.TotalMinutes, 2)) minutes" - Write-Log "" - - if ($notFoundList.Count -gt 0) { - Write-Log "Companies skipped (no dump file):" - foreach ($schema in $notFoundList) { - Write-Log " - $schema" - } - } - - Write-Log "" - Write-Log "Next steps:" - Write-Log " 1. Run 07-verify-installation.ps1 to verify all schemas" - - Close-LogFile -Success ($failCount -eq 0) - exit $(if ($failCount -eq 0) { 0 } else { 1 }) -} -catch { - Write-LogError "Import failed: $_" - Write-LogError $_.ScriptStackTrace - Close-LogFile -Success $false - exit 1 -} +#Requires -Version 5.1 + +<# +.SYNOPSIS + Batch import company schemas from Data Pump dump files. + +.DESCRIPTION + Imports company schemas from dump files. CRITICAL: Reads passwords from + CONTAFIN_ORACLE.NOM_FIRME table to ensure correct authentication. + + For each company: + 1. Queries NOM_FIRME for schema name and password + 2. Finds matching DMP file in DumpDirectory + 3. Creates user with password from NOM_FIRME + 4. Runs impdp to import schema + 5. Logs progress + +.PARAMETER DumpDirectory + Directory containing company DMP files. If not specified, defaults to C:\DMPDIR. + +.PARAMETER OracleHome + Oracle home directory. If not specified, auto-detects. + +.PARAMETER ServiceName + Database service name. Default: XEPDB1 + +.PARAMETER SystemPassword + SYSTEM user password. Default: romfastsoft + +.PARAMETER ParallelJobs + Number of parallel import jobs. Default: 2 + +.PARAMETER Companies + Optional array of specific company names to import. If not specified, imports all. + +.PARAMETER DryRun + Show what would be imported without actually importing. + +.EXAMPLE + .\05-import-companies.ps1 -DumpDirectory "C:\dumps\companies" + +.EXAMPLE + .\05-import-companies.ps1 -DumpDirectory "D:\backups" -Companies "FIRMA1","FIRMA2" + +.EXAMPLE + .\05-import-companies.ps1 -DumpDirectory "C:\dumps" -DryRun + +.NOTES + File Name : 05-import-companies.ps1 + Prerequisite : Run 04-create-synonyms-grants.ps1 first + Copyright 2024 : ROMFAST +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $false)] + [string]$DumpDirectory = "C:\DMPDIR", + + # Calea fizica a directorului Oracle DMPDIR - de acolo citeste baza. + # Implicit e aceeasi cu DumpDirectory, deci nu trebuie data decat cand + # DMP-urile stau in alta parte decat directorul pe care il vede baza. + [Parameter(Mandatory = $false)] + [string]$DmpDir, + + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $false)] + [string]$ServiceName = "XEPDB1", + + [Parameter(Mandatory = $false)] + [string]$SystemPassword = "romfastsoft", + + [Parameter(Mandatory = $false)] + [int]$ParallelJobs = 2, + + [Parameter(Mandatory = $false)] + [string[]]$Companies, + + [Parameter(Mandatory = $false)] + [switch]$DryRun +) + +$ErrorActionPreference = 'Stop' + +# Source library functions +. "$PSScriptRoot\lib\logging-functions.ps1" +. "$PSScriptRoot\lib\oracle-functions.ps1" + +# Initialize logging +$logPath = Join-Path $PSScriptRoot "..\logs\05-import-companies_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" +Initialize-LogFile -LogPath $logPath -ScriptName "05-import-companies.ps1" + +try { + Write-LogSection "Batch Import Company Schemas" + + # Validate DumpDirectory exists + if (-not (Test-Path $DumpDirectory -PathType Container)) { + throw "DumpDirectory not found: $DumpDirectory" + } + Write-Log "Using dump directory: $DumpDirectory" + + if ($DryRun) { + Write-LogWarning "DRY RUN MODE - No changes will be made" + } + + # Validate Oracle installation + $oraHome = Get-OracleHome -OracleHome $OracleHome + Write-LogSuccess "Oracle Home: $oraHome" + + # Test connection + Write-Log "Testing database connection..." + if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword)) { + throw "Cannot connect to database. Please verify ServiceName and SystemPassword." + } + Write-LogSuccess "Database connection successful" + + # Get company list from NOM_FIRME + Write-LogSection "Reading Company List from NOM_FIRME" + + $companySql = @" +SET PAGESIZE 10000 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 500 +SELECT schema || '|' || parola +FROM CONTAFIN_ORACLE.NOM_FIRME +WHERE sters = 0 + AND schema IS NOT NULL +ORDER BY schema; +EXIT; +"@ + + $companyResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $companySql -Silent + + # Parse company data + $companyData = @{} + foreach ($line in $companyResult -split "`n") { + $line = $line.Trim() + if ($line -match "^([A-Z0-9_]+)\|(.*)$") { + $schema = $Matches[1] + $password = $Matches[2] + if (-not $password) { $password = "ROMFASTSOFT" } + $companyData[$schema] = $password + } + } + + Write-Log "Found $($companyData.Count) companies in NOM_FIRME" + + if ($companyData.Count -eq 0) { + Write-LogWarning "No companies found in NOM_FIRME" + Close-LogFile -Success $true + exit 0 + } + + # Filter companies if specified + if ($Companies) { + $filteredData = @{} + foreach ($company in $Companies) { + $companyUpper = $company.ToUpper() + if ($companyData.ContainsKey($companyUpper)) { + $filteredData[$companyUpper] = $companyData[$companyUpper] + } + else { + Write-LogWarning "Company $companyUpper not found in NOM_FIRME" + } + } + $companyData = $filteredData + Write-Log "Filtered to $($companyData.Count) companies" + } + + # Find matching dump files + Write-LogSection "Scanning Dump Files" + + $dumpFiles = Get-ChildItem -Path $DumpDirectory -Filter "*.dmp" -File + Write-Log "Found $($dumpFiles.Count) DMP files in $DumpDirectory" + + # Match companies with dump files + $importList = @() + $notFoundList = @() + + foreach ($schema in $companyData.Keys) { + # Numele exact are intaietate. Fara asta, schema DANUBE putea primi + # DANUBE_20200914.dmp - datele altei firme, fara niciun avertisment. + $matchingFile = $dumpFiles | Where-Object { $_.BaseName -ieq $schema } | Select-Object -First 1 + + if (-not $matchingFile) { + $escaped = [regex]::Escape($schema) + $prefixed = @($dumpFiles | Where-Object { $_.BaseName -imatch "^$escaped[_\.]" }) + + if ($prefixed.Count -eq 1) { + $matchingFile = $prefixed[0] + } + elseif ($prefixed.Count -gt 1) { + $nume = ($prefixed | ForEach-Object { $_.Name }) -join ', ' + Write-LogWarning " - ${schema}: $($prefixed.Count) DMP-uri ambigue ($nume) - redenumeste-l pe cel bun in $schema.dmp" + } + } + + if ($matchingFile) { + $importList += [PSCustomObject]@{ + Schema = $schema + Password = $companyData[$schema] + DumpFile = $matchingFile.FullName + DumpSize = [math]::Round($matchingFile.Length / 1MB, 2) + } + } + else { + $notFoundList += $schema + } + } + + Write-Log "Companies to import: $($importList.Count)" + if ($notFoundList.Count -gt 0) { + Write-LogWarning "Companies without matching dump file: $($notFoundList.Count)" + foreach ($schema in $notFoundList) { + Write-Log " - $schema (no DMP file found)" + } + } + + if ($importList.Count -eq 0) { + Write-LogWarning "No companies to import" + Close-LogFile -Success $true + exit 0 + } + + # Display import plan + Write-LogSection "Import Plan" + + $totalSize = ($importList | Measure-Object -Property DumpSize -Sum).Sum + Write-Log "Total data to import: $totalSize MB" + Write-Log "" + Write-Log "Companies to import:" + foreach ($item in $importList) { + Write-Log " - $($item.Schema): $($item.DumpFile) ($($item.DumpSize) MB)" + } + + if ($DryRun) { + Write-LogSection "Dry Run Complete" + Write-Log "Would import $($importList.Count) company schemas" + Close-LogFile -Success $true + exit 0 + } + + # Ensure DMPDIR exists + $dmpDir = if ($DmpDir) { $DmpDir } else { $DumpDirectory } + if (-not (Test-Path -Path $dmpDir)) { + New-Item -ItemType Directory -Path $dmpDir -Force | Out-Null + } + + # Process each company + Write-LogSection "Importing Companies" + + $successCount = 0 + $failCount = 0 + $startTime = Get-Date + + foreach ($item in $importList) { + Write-LogSection "Importing $($item.Schema)" + + try { + # Check if user already exists + $userExists = Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName $item.Schema + + if ($userExists) { + Write-Log "User $($item.Schema) already exists" + + # Get current object count + $beforeCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName $item.Schema + + $beforeTotal = if ($beforeCounts['TOTAL']) { $beforeCounts['TOTAL'] } else { 0 } + Write-Log "Existing objects: $beforeTotal" + } + else { + # Create user + Write-Log "Creating user $($item.Schema)..." + + $createUserSql = @" +SET ECHO OFF FEEDBACK ON VERIFY OFF + +CREATE USER $($item.Schema) + IDENTIFIED BY "$($item.Password)" + DEFAULT TABLESPACE ROA + TEMPORARY TABLESPACE TEMP + PROFILE DEFAULT; + +GRANT CONNECT TO $($item.Schema); +GRANT RESOURCE TO $($item.Schema); +GRANT CREATE MATERIALIZED VIEW TO $($item.Schema); +GRANT CREATE PROCEDURE TO $($item.Schema); +GRANT CREATE SEQUENCE TO $($item.Schema); +GRANT CREATE TABLE TO $($item.Schema); +GRANT CREATE TRIGGER TO $($item.Schema); +GRANT CREATE VIEW TO $($item.Schema); +GRANT DEBUG CONNECT SESSION TO $($item.Schema); +GRANT SELECT ANY TABLE TO $($item.Schema); +GRANT CREATE TYPE TO $($item.Schema); +GRANT UNLIMITED TABLESPACE TO $($item.Schema); + +COMMIT; +SELECT 'USER_CREATED' FROM dual; +EXIT; +"@ + + $createResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $createUserSql -Silent + + if ($createResult -match "USER_CREATED") { + Write-LogSuccess "User $($item.Schema) created" + } + else { + throw "Failed to create user $($item.Schema)" + } + } + + # Copy dump file to DMPDIR if not already there + $dumpFileName = Split-Path -Path $item.DumpFile -Leaf + $targetDumpPath = Join-Path $dmpDir $dumpFileName + + if ($item.DumpFile -ne $targetDumpPath) { + Write-Log "Copying dump file to DMPDIR..." + Copy-Item -Path $item.DumpFile -Destination $targetDumpPath -Force + } + + # Run import + Write-Log "Running impdp for $($item.Schema)..." + + $impdpPath = Join-Path $oraHome "bin\impdp.exe" + $importLogFile = "$($item.Schema)_import_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" + + # Use EZConnect format for impdp (handles listener bound to specific IP) + $dbHost = Get-ListenerHost -OracleHome $oraHome + $connString = "SYSTEM/`"$SystemPassword`"@${dbHost}:1521/$ServiceName" + + # Check if dump has different schema name (remapping needed) + $remapParam = "" + $dumpBaseName = [System.IO.Path]::GetFileNameWithoutExtension($dumpFileName).ToUpper() + if ($dumpBaseName -ne $item.Schema) { + $remapParam = "remap_schema=$dumpBaseName`:$($item.Schema)" + Write-Log "Schema remap: $dumpBaseName -> $($item.Schema)" + } + + $impdpParams = @( + "`"$connString`"", + "directory=DMPDIR", + "dumpfile=$dumpFileName", + "logfile=$importLogFile", + "schemas=$($item.Schema)", + "table_exists_action=REPLACE", + "parallel=1" # Required for Oracle XE + ) + + if ($remapParam) { + $impdpParams += $remapParam + } + + $arguments = $impdpParams -join " " + + # Set Oracle environment + $env:ORACLE_HOME = $oraHome + $env:PATH = "$oraHome\bin;$env:PATH" + $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" + + $importStart = Get-Date + $process = Start-Process -FilePath $impdpPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru + + $importDuration = (Get-Date) - $importStart + Write-Log "Import completed in $([math]::Round($importDuration.TotalMinutes, 2)) minutes" + + # Verify import + $afterCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName $item.Schema + + $afterTotal = if ($afterCounts['TOTAL']) { $afterCounts['TOTAL'] } else { 0 } + $afterInvalid = if ($afterCounts['INVALID']) { $afterCounts['INVALID'] } else { 0 } + + Write-Log "Objects after import: $afterTotal" + + if ($afterInvalid -gt 0) { + Write-LogWarning "Invalid objects: $afterInvalid" + + # Recompile + $recompileSql = @" +BEGIN + DBMS_UTILITY.COMPILE_SCHEMA('$($item.Schema)', FALSE); +END; +/ +EXIT; +"@ + Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $recompileSql -Silent + } + + if ($process.ExitCode -eq 0) { + Write-LogSuccess "$($item.Schema) imported successfully ($afterTotal objects)" + $successCount++ + } + else { + Write-LogWarning "$($item.Schema) import completed with warnings" + $successCount++ + } + + # Clean up copied dump file + if ($item.DumpFile -ne $targetDumpPath -and (Test-Path -Path $targetDumpPath)) { + Remove-Item -Path $targetDumpPath -Force -ErrorAction SilentlyContinue + } + } + catch { + Write-LogError "Failed to import $($item.Schema): $_" + $failCount++ + } + } + + $totalDuration = (Get-Date) - $startTime + + # Summary + Write-LogSection "Import Complete" + + Write-LogSuccess "Company import batch completed!" + Write-Log "" + Write-Log "Summary:" + Write-Log " Total companies: $($importList.Count)" + Write-Log " Successful: $successCount" + Write-Log " Failed: $failCount" + Write-Log " Total duration: $([math]::Round($totalDuration.TotalMinutes, 2)) minutes" + Write-Log "" + + if ($notFoundList.Count -gt 0) { + Write-Log "Companies skipped (no dump file):" + foreach ($schema in $notFoundList) { + Write-Log " - $schema" + } + } + + Write-Log "" + Write-Log "Next steps:" + Write-Log " 1. Run 07-verify-installation.ps1 to verify all schemas" + + Close-LogFile -Success ($failCount -eq 0) + exit $(if ($failCount -eq 0) { 0 } else { 1 }) +} +catch { + Write-LogError "Import failed: $_" + Write-LogError $_.ScriptStackTrace + Close-LogFile -Success $false + exit 1 +} diff --git a/proxmox/lxc108-oracle/roa-windows-setup/scripts/06-add-company.ps1 b/proxmox/lxc108-oracle/roa-windows-setup/scripts/06-add-company.ps1 index 2906c42..dc995fa 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/scripts/06-add-company.ps1 +++ b/proxmox/lxc108-oracle/roa-windows-setup/scripts/06-add-company.ps1 @@ -1,327 +1,333 @@ -#Requires -Version 5.1 - -<# -.SYNOPSIS - Add a new company schema to ROA Oracle. - -.DESCRIPTION - Creates a new company user/schema with either: - - Import from a template DMP file - - Empty schema with basic structures - -.PARAMETER CompanyName - Schema name for the new company. Required. Must be uppercase alphanumeric. - -.PARAMETER Password - Password for the new company schema. Default: ROMFASTSOFT - -.PARAMETER OracleHome - Oracle home directory. If not specified, auto-detects. - -.PARAMETER ServiceName - Database service name. Default: XEPDB1 - -.PARAMETER SystemPassword - SYSTEM user password. Default: romfastsoft - -.PARAMETER TemplateDump - Optional path to a template DMP file to import. - -.PARAMETER TemplateSchema - Schema name in the template DMP file if different from target (for remap). - -.PARAMETER Force - Drop and recreate if user already exists. - -.EXAMPLE - .\06-add-company.ps1 -CompanyName "NEWCOMPANY" - -.EXAMPLE - .\06-add-company.ps1 -CompanyName "FIRMA2024" -Password "SecurePass123" - -.EXAMPLE - .\06-add-company.ps1 -CompanyName "NEWFIRMA" -TemplateDump "C:\dumps\FIRMANOUA.dmp" -TemplateSchema "FIRMANOUA" - -.NOTES - File Name : 06-add-company.ps1 - Prerequisite : Run 04-create-synonyms-grants.ps1 first - Copyright 2024 : ROMFAST -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory = $true)] - [ValidatePattern('^[A-Z0-9_]+$')] - [string]$CompanyName, - - [Parameter(Mandatory = $false)] - [string]$Password = "ROMFASTSOFT", - - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $false)] - [string]$ServiceName = "XEPDB1", - - [Parameter(Mandatory = $false)] - [string]$SystemPassword = "romfastsoft", - - [Parameter(Mandatory = $false)] - [ValidateScript({ Test-Path $_ })] - [string]$TemplateDump, - - [Parameter(Mandatory = $false)] - [string]$TemplateSchema, - - [Parameter(Mandatory = $false)] - [switch]$Force -) - -$ErrorActionPreference = 'Stop' - -# Ensure company name is uppercase -$CompanyName = $CompanyName.ToUpper() - -# Source library functions -. "$PSScriptRoot\lib\logging-functions.ps1" -. "$PSScriptRoot\lib\oracle-functions.ps1" - -# Initialize logging -$logPath = Join-Path $PSScriptRoot "..\logs\06-add-company_${CompanyName}_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" -Initialize-LogFile -LogPath $logPath -ScriptName "06-add-company.ps1" - -try { - Write-LogSection "Adding New Company: $CompanyName" - - # Validate Oracle installation - $oraHome = Get-OracleHome -OracleHome $OracleHome - Write-LogSuccess "Oracle Home: $oraHome" - - # Test connection - Write-Log "Testing database connection..." - if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword)) { - throw "Cannot connect to database. Please verify ServiceName and SystemPassword." - } - Write-LogSuccess "Database connection successful" - - # Check if user already exists - Write-Log "Checking if user $CompanyName exists..." - $userExists = Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName $CompanyName - - if ($userExists) { - if ($Force) { - Write-LogWarning "User $CompanyName exists. Dropping due to -Force flag..." - - $dropSql = @" -SET ECHO OFF FEEDBACK ON VERIFY OFF -DROP USER $CompanyName CASCADE; -SELECT 'USER_DROPPED' FROM dual; -EXIT; -"@ - $dropResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $dropSql - - if ($dropResult -match "USER_DROPPED") { - Write-Log "User $CompanyName dropped" - } - else { - throw "Failed to drop user $CompanyName" - } - } - else { - throw "User $CompanyName already exists. Use -Force to drop and recreate." - } - } - - # Create user - Write-LogSection "Creating User $CompanyName" - - $createUserSql = @" -SET ECHO OFF FEEDBACK ON VERIFY OFF - -PROMPT Creating user $CompanyName... -CREATE USER $CompanyName - IDENTIFIED BY "$Password" - DEFAULT TABLESPACE ROA - TEMPORARY TABLESPACE TEMP - PROFILE DEFAULT; - -PROMPT Granting role privileges... -GRANT CONNECT TO $CompanyName; -GRANT RESOURCE TO $CompanyName; - -PROMPT Granting system privileges... -GRANT CREATE MATERIALIZED VIEW TO $CompanyName; -GRANT CREATE PROCEDURE TO $CompanyName; -GRANT CREATE SEQUENCE TO $CompanyName; -GRANT CREATE TABLE TO $CompanyName; -GRANT CREATE TRIGGER TO $CompanyName; -GRANT CREATE VIEW TO $CompanyName; -GRANT DEBUG CONNECT SESSION TO $CompanyName; -GRANT SELECT ANY TABLE TO $CompanyName; -GRANT CREATE TYPE TO $CompanyName; -GRANT UNLIMITED TABLESPACE TO $CompanyName; - -COMMIT; -SELECT 'USER_CREATED' FROM dual; -EXIT; -"@ - - $createResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $createUserSql - - if ($createResult -match "USER_CREATED") { - Write-LogSuccess "User $CompanyName created successfully" - } - else { - Write-LogError "Failed to create user" - Write-LogDebug $createResult - throw "User creation failed" - } - - # Import from template if specified - if ($TemplateDump) { - Write-LogSection "Importing from Template" - - Write-Log "Template dump: $TemplateDump" - - # Determine template schema for remapping - if (-not $TemplateSchema) { - $TemplateSchema = [System.IO.Path]::GetFileNameWithoutExtension($TemplateDump).ToUpper() - Write-Log "Assuming template schema: $TemplateSchema" - } - - # Setup DMPDIR - $dmpDir = "C:\DMPDIR" - if (-not (Test-Path -Path $dmpDir)) { - New-Item -ItemType Directory -Path $dmpDir -Force | Out-Null - } - - New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $dmpDir - - # Copy dump to DMPDIR - $dumpFileName = Split-Path -Path $TemplateDump -Leaf - $targetDumpPath = Join-Path $dmpDir $dumpFileName - - if ($TemplateDump -ne $targetDumpPath) { - Write-Log "Copying dump file to DMPDIR..." - Copy-Item -Path $TemplateDump -Destination $targetDumpPath -Force - } - - # Run import with remap - Write-Log "Running impdp..." - - $impdpPath = Join-Path $oraHome "bin\impdp.exe" - $importLogFile = "${CompanyName}_import_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" - - $connString = "SYSTEM/`"$SystemPassword`"@$ServiceName" - - $impdpParams = @( - "`"$connString`"", - "directory=DMPDIR", - "dumpfile=$dumpFileName", - "logfile=$importLogFile", - "remap_schema=$TemplateSchema`:$CompanyName", - "table_exists_action=REPLACE" - ) - - $arguments = $impdpParams -join " " - - # Set Oracle environment - $env:ORACLE_HOME = $oraHome - $env:PATH = "$oraHome\bin;$env:PATH" - $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" - - $importStart = Get-Date - $process = Start-Process -FilePath $impdpPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru - - $importDuration = (Get-Date) - $importStart - Write-Log "Import completed in $([math]::Round($importDuration.TotalMinutes, 2)) minutes" - - if ($process.ExitCode -eq 0) { - Write-LogSuccess "Template imported successfully" - } - else { - Write-LogWarning "Import completed with warnings (exit code: $($process.ExitCode))" - } - - # Clean up copied dump file - if ($TemplateDump -ne $targetDumpPath -and (Test-Path -Path $targetDumpPath)) { - Remove-Item -Path $targetDumpPath -Force -ErrorAction SilentlyContinue - } - } - - # Get final object count - Write-LogSection "Verifying Schema" - - $counts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName $CompanyName - - $totalObjects = if ($counts['TOTAL']) { $counts['TOTAL'] } else { 0 } - $invalidObjects = if ($counts['INVALID']) { $counts['INVALID'] } else { 0 } - - Write-Log "Total objects: $totalObjects" - - if ($invalidObjects -gt 0) { - Write-LogWarning "Invalid objects: $invalidObjects" - - # Recompile - Write-Log "Recompiling invalid objects..." - $recompileSql = @" -BEGIN - DBMS_UTILITY.COMPILE_SCHEMA('$CompanyName', FALSE); -END; -/ -EXIT; -"@ - Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $SystemPassword -SqlCommand $recompileSql -Silent - - # Recheck - $recheckCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` - -Password $SystemPassword -SchemaName $CompanyName - - $recheckInvalid = if ($recheckCounts['INVALID']) { $recheckCounts['INVALID'] } else { 0 } - if ($recheckInvalid -lt $invalidObjects) { - Write-LogSuccess "Recompilation fixed $($invalidObjects - $recheckInvalid) objects" - } - } - - # Test connection with new user - Write-Log "Testing connection as $CompanyName..." - if (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` - -Username $CompanyName -Password $Password) { - Write-LogSuccess "Connection test successful" - } - else { - Write-LogWarning "Could not connect as $CompanyName" - } - - # Summary - Write-LogSection "Company Added" - - Write-LogSuccess "Company $CompanyName added successfully!" - Write-Log "" - Write-Log "Summary:" - Write-Log " Schema name: $CompanyName" - Write-Log " Service: $ServiceName" - Write-Log " Objects: $totalObjects" - if ($TemplateDump) { - Write-Log " Template: $TemplateDump" - } - Write-Log "" - Write-Log "Connection string:" - Write-Log " $CompanyName/$Password@$ServiceName" - - Close-LogFile -Success $true - exit 0 -} -catch { - Write-LogError "Failed to add company: $_" - Write-LogError $_.ScriptStackTrace - Close-LogFile -Success $false - exit 1 -} +#Requires -Version 5.1 + +<# +.SYNOPSIS + Add a new company schema to ROA Oracle. + +.DESCRIPTION + Creates a new company user/schema with either: + - Import from a template DMP file + - Empty schema with basic structures + +.PARAMETER CompanyName + Schema name for the new company. Required. Must be uppercase alphanumeric. + +.PARAMETER Password + Password for the new company schema. Default: ROMFASTSOFT + +.PARAMETER OracleHome + Oracle home directory. If not specified, auto-detects. + +.PARAMETER ServiceName + Database service name. Default: XEPDB1 + +.PARAMETER SystemPassword + SYSTEM user password. Default: romfastsoft + +.PARAMETER TemplateDump + Optional path to a template DMP file to import. + +.PARAMETER TemplateSchema + Schema name in the template DMP file if different from target (for remap). + +.PARAMETER Force + Drop and recreate if user already exists. + +.EXAMPLE + .\06-add-company.ps1 -CompanyName "NEWCOMPANY" + +.EXAMPLE + .\06-add-company.ps1 -CompanyName "FIRMA2024" -Password "SecurePass123" + +.EXAMPLE + .\06-add-company.ps1 -CompanyName "NEWFIRMA" -TemplateDump "C:\dumps\FIRMANOUA.dmp" -TemplateSchema "FIRMANOUA" + +.NOTES + File Name : 06-add-company.ps1 + Prerequisite : Run 04-create-synonyms-grants.ps1 first + Copyright 2024 : ROMFAST +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [ValidatePattern('^[A-Z0-9_]+$')] + [string]$CompanyName, + + [Parameter(Mandatory = $false)] + [string]$Password = "ROMFASTSOFT", + + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $false)] + [string]$ServiceName = "XEPDB1", + + [Parameter(Mandatory = $false)] + [string]$SystemPassword = "romfastsoft", + + [Parameter(Mandatory = $false)] + [ValidateScript({ Test-Path $_ })] + [string]$TemplateDump, + + [Parameter(Mandatory = $false)] + [string]$TemplateSchema, + + [Parameter(Mandatory = $false)] + [switch]$Force, + + # Calea fizica a directorului Oracle DMPDIR, asa cum a fost pusa la instalare. + [Parameter(Mandatory = $false)] + [string]$DmpDir = "C:\DMPDIR" +) + +$ErrorActionPreference = 'Stop' + +# Ensure company name is uppercase +$CompanyName = $CompanyName.ToUpper() + +# Source library functions +. "$PSScriptRoot\lib\logging-functions.ps1" +. "$PSScriptRoot\lib\oracle-functions.ps1" + +# Initialize logging +$logPath = Join-Path $PSScriptRoot "..\logs\06-add-company_${CompanyName}_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" +Initialize-LogFile -LogPath $logPath -ScriptName "06-add-company.ps1" + +try { + Write-LogSection "Adding New Company: $CompanyName" + + # Validate Oracle installation + $oraHome = Get-OracleHome -OracleHome $OracleHome + Write-LogSuccess "Oracle Home: $oraHome" + + # Test connection + Write-Log "Testing database connection..." + if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword)) { + throw "Cannot connect to database. Please verify ServiceName and SystemPassword." + } + Write-LogSuccess "Database connection successful" + + # Check if user already exists + Write-Log "Checking if user $CompanyName exists..." + $userExists = Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName $CompanyName + + if ($userExists) { + if ($Force) { + Write-LogWarning "User $CompanyName exists. Dropping due to -Force flag..." + + $dropSql = @" +SET ECHO OFF FEEDBACK ON VERIFY OFF +DROP USER $CompanyName CASCADE; +SELECT 'USER_DROPPED' FROM dual; +EXIT; +"@ + $dropResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $dropSql + + if ($dropResult -match "USER_DROPPED") { + Write-Log "User $CompanyName dropped" + } + else { + throw "Failed to drop user $CompanyName" + } + } + else { + throw "User $CompanyName already exists. Use -Force to drop and recreate." + } + } + + # Create user + Write-LogSection "Creating User $CompanyName" + + $createUserSql = @" +SET ECHO OFF FEEDBACK ON VERIFY OFF + +PROMPT Creating user $CompanyName... +CREATE USER $CompanyName + IDENTIFIED BY "$Password" + DEFAULT TABLESPACE ROA + TEMPORARY TABLESPACE TEMP + PROFILE DEFAULT; + +PROMPT Granting role privileges... +GRANT CONNECT TO $CompanyName; +GRANT RESOURCE TO $CompanyName; + +PROMPT Granting system privileges... +GRANT CREATE MATERIALIZED VIEW TO $CompanyName; +GRANT CREATE PROCEDURE TO $CompanyName; +GRANT CREATE SEQUENCE TO $CompanyName; +GRANT CREATE TABLE TO $CompanyName; +GRANT CREATE TRIGGER TO $CompanyName; +GRANT CREATE VIEW TO $CompanyName; +GRANT DEBUG CONNECT SESSION TO $CompanyName; +GRANT SELECT ANY TABLE TO $CompanyName; +GRANT CREATE TYPE TO $CompanyName; +GRANT UNLIMITED TABLESPACE TO $CompanyName; + +COMMIT; +SELECT 'USER_CREATED' FROM dual; +EXIT; +"@ + + $createResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $createUserSql + + if ($createResult -match "USER_CREATED") { + Write-LogSuccess "User $CompanyName created successfully" + } + else { + Write-LogError "Failed to create user" + Write-LogDebug $createResult + throw "User creation failed" + } + + # Import from template if specified + if ($TemplateDump) { + Write-LogSection "Importing from Template" + + Write-Log "Template dump: $TemplateDump" + + # Determine template schema for remapping + if (-not $TemplateSchema) { + $TemplateSchema = [System.IO.Path]::GetFileNameWithoutExtension($TemplateDump).ToUpper() + Write-Log "Assuming template schema: $TemplateSchema" + } + + # Setup DMPDIR + $dmpDir = $DmpDir + if (-not (Test-Path -Path $dmpDir)) { + New-Item -ItemType Directory -Path $dmpDir -Force | Out-Null + } + + if (-not (New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $dmpDir)) { + throw "Directorul Data Pump DMPDIR ($dmpDir) nu e utilizabil de catre baza." + } + + # Copy dump to DMPDIR + $dumpFileName = Split-Path -Path $TemplateDump -Leaf + $targetDumpPath = Join-Path $dmpDir $dumpFileName + + if ($TemplateDump -ne $targetDumpPath) { + Write-Log "Copying dump file to DMPDIR..." + Copy-Item -Path $TemplateDump -Destination $targetDumpPath -Force + } + + # Run import with remap + Write-Log "Running impdp..." + + $impdpPath = Join-Path $oraHome "bin\impdp.exe" + $importLogFile = "${CompanyName}_import_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" + + $connString = "SYSTEM/`"$SystemPassword`"@$ServiceName" + + $impdpParams = @( + "`"$connString`"", + "directory=DMPDIR", + "dumpfile=$dumpFileName", + "logfile=$importLogFile", + "remap_schema=$TemplateSchema`:$CompanyName", + "table_exists_action=REPLACE" + ) + + $arguments = $impdpParams -join " " + + # Set Oracle environment + $env:ORACLE_HOME = $oraHome + $env:PATH = "$oraHome\bin;$env:PATH" + $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" + + $importStart = Get-Date + $process = Start-Process -FilePath $impdpPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru + + $importDuration = (Get-Date) - $importStart + Write-Log "Import completed in $([math]::Round($importDuration.TotalMinutes, 2)) minutes" + + if ($process.ExitCode -eq 0) { + Write-LogSuccess "Template imported successfully" + } + else { + Write-LogWarning "Import completed with warnings (exit code: $($process.ExitCode))" + } + + # Clean up copied dump file + if ($TemplateDump -ne $targetDumpPath -and (Test-Path -Path $targetDumpPath)) { + Remove-Item -Path $targetDumpPath -Force -ErrorAction SilentlyContinue + } + } + + # Get final object count + Write-LogSection "Verifying Schema" + + $counts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName $CompanyName + + $totalObjects = if ($counts['TOTAL']) { $counts['TOTAL'] } else { 0 } + $invalidObjects = if ($counts['INVALID']) { $counts['INVALID'] } else { 0 } + + Write-Log "Total objects: $totalObjects" + + if ($invalidObjects -gt 0) { + Write-LogWarning "Invalid objects: $invalidObjects" + + # Recompile + Write-Log "Recompiling invalid objects..." + $recompileSql = @" +BEGIN + DBMS_UTILITY.COMPILE_SCHEMA('$CompanyName', FALSE); +END; +/ +EXIT; +"@ + Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $SystemPassword -SqlCommand $recompileSql -Silent + + # Recheck + $recheckCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName ` + -Password $SystemPassword -SchemaName $CompanyName + + $recheckInvalid = if ($recheckCounts['INVALID']) { $recheckCounts['INVALID'] } else { 0 } + if ($recheckInvalid -lt $invalidObjects) { + Write-LogSuccess "Recompilation fixed $($invalidObjects - $recheckInvalid) objects" + } + } + + # Test connection with new user + Write-Log "Testing connection as $CompanyName..." + if (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName ` + -Username $CompanyName -Password $Password) { + Write-LogSuccess "Connection test successful" + } + else { + Write-LogWarning "Could not connect as $CompanyName" + } + + # Summary + Write-LogSection "Company Added" + + Write-LogSuccess "Company $CompanyName added successfully!" + Write-Log "" + Write-Log "Summary:" + Write-Log " Schema name: $CompanyName" + Write-Log " Service: $ServiceName" + Write-Log " Objects: $totalObjects" + if ($TemplateDump) { + Write-Log " Template: $TemplateDump" + } + Write-Log "" + Write-Log "Connection string:" + Write-Log " $CompanyName/$Password@$ServiceName" + + Close-LogFile -Success $true + exit 0 +} +catch { + Write-LogError "Failed to add company: $_" + Write-LogError $_.ScriptStackTrace + Close-LogFile -Success $false + exit 1 +} diff --git a/proxmox/lxc108-oracle/roa-windows-setup/scripts/lib/oracle-functions.ps1 b/proxmox/lxc108-oracle/roa-windows-setup/scripts/lib/oracle-functions.ps1 index c9cae59..1e7b342 100644 --- a/proxmox/lxc108-oracle/roa-windows-setup/scripts/lib/oracle-functions.ps1 +++ b/proxmox/lxc108-oracle/roa-windows-setup/scripts/lib/oracle-functions.ps1 @@ -1,1137 +1,1173 @@ -#Requires -Version 5.1 - -<# -.SYNOPSIS - Oracle connectivity and operations functions for ROA setup scripts. - -.DESCRIPTION - Provides functions for Oracle database connectivity, SQL*Plus execution, - version detection, and Data Pump operations. - -.NOTES - File Name : oracle-functions.ps1 - Prerequisite : PowerShell 5.1 or higher, Oracle Client installed - Copyright 2024 : ROMFAST -#> - -# Source logging functions -. "$PSScriptRoot\logging-functions.ps1" - -<# -.SYNOPSIS - Get the listener host address. - -.DESCRIPTION - Auto-detects the host address from listener configuration. - Returns the IP address the listener is bound to. - -.PARAMETER OracleHome - Oracle Home directory. - -.OUTPUTS - String. The host address (IP or hostname). - -.EXAMPLE - $host = Get-ListenerHost -OracleHome "C:\app\oracle\product\21c\dbhomeXE" -#> -function Get-ListenerHost { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - $lsnrctl = Join-Path $oraHome "bin\lsnrctl.exe" - - if (-not (Test-Path $lsnrctl)) { - return "localhost" - } - - try { - # Set Oracle environment - $env:ORACLE_HOME = $oraHome - $env:PATH = "$oraHome\bin;$env:PATH" - - $output = & $lsnrctl status 2>&1 | Out-String - - # Parse HOST from listener output - look for TCP endpoint (not TCPS) - # Format: (PROTOCOL=tcp)(HOST=10.0.20.130)(PORT=1521) - # Use simple IP pattern to avoid regex escaping issues - $tcpMatch = [regex]::Match($output, "PROTOCOL=tcp\).*?HOST=([0-9\.]+)") - if ($tcpMatch.Success) { - $listenerHost = $tcpMatch.Groups[1].Value - - # If listener is bound to 0.0.0.0, try to get actual IP - if ($listenerHost -eq "0.0.0.0") { - $ip = (Get-NetIPAddress -AddressFamily IPv4 | - Where-Object { $_.IPAddress -notmatch "^127\." -and $_.PrefixOrigin -ne "WellKnown" } | - Select-Object -First 1).IPAddress - if ($ip) { return $ip } - return "localhost" - } - - # If it's a specific IP (not localhost), return it - if ($listenerHost -notmatch "^127\." -and $listenerHost -ne "localhost") { - return $listenerHost - } - } - - # Fallback: try any HOST= pattern - $anyHostMatch = [regex]::Match($output, "HOST=([0-9\.]+)") - if ($anyHostMatch.Success) { - $listenerHost = $anyHostMatch.Groups[1].Value - if ($listenerHost -notmatch "^127\." -and $listenerHost -ne "0.0.0.0") { - return $listenerHost - } - } - - return "localhost" - } - catch { - return "localhost" - } -} - -<# -.SYNOPSIS - Find Oracle Home directory. - -.DESCRIPTION - Auto-detects Oracle Home from registry, environment variable, or common paths. - -.PARAMETER OracleHome - Optional explicit Oracle Home path. If not specified, auto-detects. - -.OUTPUTS - String. The Oracle Home path. - -.EXAMPLE - $oracleHome = Get-OracleHome -#> -function Get-OracleHome { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome - ) - - # If explicitly provided, validate and return - if ($OracleHome) { - if (Test-Path -Path "$OracleHome\bin\sqlplus.exe") { - return $OracleHome - } - throw "Invalid Oracle Home: sqlplus.exe not found at $OracleHome\bin\sqlplus.exe" - } - - # Try ORACLE_HOME environment variable - if ($env:ORACLE_HOME -and (Test-Path -Path "$env:ORACLE_HOME\bin\sqlplus.exe")) { - return $env:ORACLE_HOME - } - - # Try registry for Oracle XE - $regPaths = @( - 'HKLM:\SOFTWARE\Oracle\KEY_OraDB21Home1', - 'HKLM:\SOFTWARE\Oracle\KEY_OraDB18Home1', - 'HKLM:\SOFTWARE\Oracle\KEY_XE', - 'HKLM:\SOFTWARE\Wow6432Node\Oracle\KEY_OraDB21Home1', - 'HKLM:\SOFTWARE\Wow6432Node\Oracle\KEY_OraDB18Home1' - ) - - foreach ($regPath in $regPaths) { - if (Test-Path -Path $regPath) { - $oraHome = (Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue).ORACLE_HOME - if ($oraHome -and (Test-Path -Path "$oraHome\bin\sqlplus.exe")) { - return $oraHome - } - } - } - - # Try common installation paths (including user-specific paths) - $currentUser = $env:USERNAME - $commonPaths = @( - "C:\app\$currentUser\product\21c\dbhomeXE", - "C:\app\$currentUser\product\21c\dbhome_1", - "C:\app\$currentUser\product\18c\dbhomeXE", - 'C:\app\oracle\product\21c\dbhomeXE', - 'C:\app\oracle\product\21c\dbhome_1', - 'C:\app\oracle\product\18c\dbhomeXE', - 'C:\app\romfast\product\21c\dbhomeXE', - 'C:\app\romfast\product\21c\dbhome_1', - 'C:\oraclexe\app\oracle\product\11.2.0\server', - "D:\app\$currentUser\product\21c\dbhomeXE", - 'D:\app\oracle\product\21c\dbhomeXE', - 'D:\app\oracle\product\18c\dbhomeXE' - ) - - foreach ($path in $commonPaths) { - if (Test-Path -Path "$path\bin\sqlplus.exe") { - return $path - } - } - - throw "Oracle Home not found. Please specify -OracleHome parameter or set ORACLE_HOME environment variable." -} - -<# -.SYNOPSIS - Get Oracle version information. - -.DESCRIPTION - Detects Oracle version (XE vs SE), edition, and whether it's a Container Database (CDB). - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Username - Username for connection (default: SYSTEM). - -.PARAMETER Password - Password for connection. - -.OUTPUTS - PSCustomObject with Version, Edition, IsCDB, IsXE properties. - -.EXAMPLE - $version = Get-OracleVersion -ServiceName "XE" -Password "oracle" -#> -function Get-OracleVersion { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $false)] - [string]$DbHost, - - [Parameter(Mandatory = $false)] - [int]$Port = 1521, - - [Parameter(Mandatory = $false)] - [string]$Username = "SYSTEM", - - [Parameter(Mandatory = $true)] - [string]$Password - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - - $sql = @" -SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF -SELECT 'VERSION:' || version_full FROM v`$instance; -SELECT 'EDITION:' || edition FROM v`$instance; -SELECT 'CDB:' || CDB FROM v`$database; -EXIT; -"@ - - $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -DbHost $DbHost -Port $Port ` - -Username $Username -Password $Password -SqlCommand $sql -Silent - - $versionInfo = [PSCustomObject]@{ - Version = "" - Edition = "" - IsCDB = $false - IsXE = $false - FullInfo = $result - } - - foreach ($line in $result -split "`n") { - if ($line -match "^VERSION:(.+)$") { - $versionInfo.Version = $Matches[1].Trim() - } - elseif ($line -match "^EDITION:(.+)$") { - $versionInfo.Edition = $Matches[1].Trim() - $versionInfo.IsXE = $versionInfo.Edition -match "XE|Express" - } - elseif ($line -match "^CDB:(.+)$") { - $versionInfo.IsCDB = $Matches[1].Trim() -eq "YES" - } - } - - return $versionInfo -} - -<# -.SYNOPSIS - Get the default service name based on Oracle configuration. - -.DESCRIPTION - Auto-detects the appropriate service name. Returns XEPDB1 for Oracle XE CDB, - or ROA for traditional non-CDB installations. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER SystemPassword - SYSTEM user password. - -.OUTPUTS - String. The service name. - -.EXAMPLE - $serviceName = Get-ServiceName -SystemPassword "oracle" -#> -function Get-ServiceName { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $false)] - [string]$DbHost, - - [Parameter(Mandatory = $false)] - [int]$Port = 1521, - - [Parameter(Mandatory = $true)] - [string]$SystemPassword - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - - # First try to connect to XE and check if it's CDB - try { - $version = Get-OracleVersion -OracleHome $oraHome -ServiceName "XE" ` - -DbHost $DbHost -Port $Port ` - -Password $SystemPassword -ErrorAction Stop - - if ($version.IsCDB) { - # For CDB, use XEPDB1 (the default pluggable database) - return "XEPDB1" - } - else { - return "XE" - } - } - catch { - # Try ROA as service name - try { - $null = Test-OracleConnection -OracleHome $oraHome -ServiceName "ROA" ` - -DbHost $DbHost -Port $Port ` - -Username "SYSTEM" -Password $SystemPassword -ErrorAction Stop - return "ROA" - } - catch { - # Default to XEPDB1 for modern XE installations - return "XEPDB1" - } - } -} - -<# -.SYNOPSIS - Test Oracle database connection. - -.DESCRIPTION - Attempts to connect to Oracle and verifies the connection is successful. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Username - Username for connection. - -.PARAMETER Password - Password for connection. - -.OUTPUTS - Boolean. True if connection successful. - -.EXAMPLE - if (Test-OracleConnection -ServiceName "XEPDB1" -Username "SYSTEM" -Password "oracle") { - Write-Host "Connected!" - } -#> -function Test-OracleConnection { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $false)] - [string]$DbHost, - - [Parameter(Mandatory = $false)] - [int]$Port = 1521, - - [Parameter(Mandatory = $false)] - [string]$Username = "SYSTEM", - - [Parameter(Mandatory = $true)] - [string]$Password, - - [Parameter(Mandatory = $false)] - [switch]$AsSysdba - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - - $sql = "SELECT 'CONNECTED' FROM dual; EXIT;" - - try { - $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -DbHost $DbHost -Port $Port ` - -Username $Username -Password $Password -SqlCommand $sql -AsSysdba:$AsSysdba -Silent - - return $result -match "CONNECTED" - } - catch { - return $false - } -} - -<# -.SYNOPSIS - Test if connected to a PDB or non-CDB. - -.DESCRIPTION - Checks the current container type to determine if connected to a PDB, - the CDB root, or a non-CDB database. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Username - Username for connection. - -.PARAMETER Password - Password for connection. - -.OUTPUTS - PSCustomObject with ContainerName, IsPDB, IsCDBRoot, IsNonCDB properties. - -.EXAMPLE - $containerInfo = Test-PDB -ServiceName "XEPDB1" -Password "oracle" -#> -function Test-PDB { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $false)] - [string]$DbHost, - - [Parameter(Mandatory = $false)] - [int]$Port = 1521, - - [Parameter(Mandatory = $false)] - [string]$Username = "SYSTEM", - - [Parameter(Mandatory = $true)] - [string]$Password - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - - $sql = @" -SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF -SELECT 'CONTAINER:' || SYS_CONTEXT('USERENV', 'CON_NAME') FROM dual; -SELECT 'CON_ID:' || SYS_CONTEXT('USERENV', 'CON_ID') FROM dual; -EXIT; -"@ - - $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -DbHost $DbHost -Port $Port ` - -Username $Username -Password $Password -SqlCommand $sql -Silent - - $containerInfo = [PSCustomObject]@{ - ContainerName = "" - ConId = 0 - IsPDB = $false - IsCDBRoot = $false - IsNonCDB = $false - } - - foreach ($line in $result -split "`n") { - if ($line -match "^CONTAINER:(.+)$") { - $containerInfo.ContainerName = $Matches[1].Trim() - } - elseif ($line -match "^CON_ID:(.+)$") { - $containerInfo.ConId = [int]$Matches[1].Trim() - } - } - - if ($containerInfo.ConId -eq 0) { - $containerInfo.IsNonCDB = $true - } - elseif ($containerInfo.ConId -eq 1) { - $containerInfo.IsCDBRoot = $true - } - else { - $containerInfo.IsPDB = $true - } - - return $containerInfo -} - -<# -.SYNOPSIS - Execute SQL via SQL*Plus. - -.DESCRIPTION - Runs a SQL command or file using SQL*Plus and returns the output. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Username - Username for connection. - -.PARAMETER Password - Password for connection. - -.PARAMETER SqlCommand - SQL command(s) to execute. - -.PARAMETER SqlFile - Path to SQL file to execute. - -.PARAMETER AsSysdba - Connect as SYSDBA. - -.PARAMETER Silent - Suppress console output. - -.OUTPUTS - String. The SQL*Plus output. - -.EXAMPLE - Invoke-SqlPlus -ServiceName "XEPDB1" -Username "SYSTEM" -Password "oracle" -SqlCommand "SELECT * FROM dual;" - -.EXAMPLE - Invoke-SqlPlus -ServiceName "XEPDB1" -Username "SYS" -Password "oracle" -SqlFile "C:\scripts\setup.sql" -AsSysdba -#> -function Invoke-SqlPlus { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $false)] - [string]$DbHost, - - [Parameter(Mandatory = $false)] - [int]$Port = 1521, - - [Parameter(Mandatory = $false)] - [string]$Username = "SYSTEM", - - [Parameter(Mandatory = $true)] - [string]$Password, - - [Parameter(Mandatory = $false)] - [string]$SqlCommand, - - [Parameter(Mandatory = $false)] - [string]$SqlFile, - - [Parameter(Mandatory = $false)] - [switch]$AsSysdba, - - [Parameter(Mandatory = $false)] - [switch]$Silent - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - $sqlplus = Join-Path $oraHome "bin\sqlplus.exe" - - if (-not (Test-Path -Path $sqlplus)) { - throw "SQL*Plus not found at: $sqlplus" - } - - # Auto-detect host from listener if not provided - if (-not $DbHost) { - $DbHost = Get-ListenerHost -OracleHome $oraHome - } - - # Build connection string (always use EZConnect format for reliability) - $sysdba = if ($AsSysdba) { " as sysdba" } else { "" } - $connString = "$Username/`"$Password`"@${DbHost}:${Port}/${ServiceName}$sysdba" - - $tempFile = $null - - try { - if ($SqlFile) { - # Execute SQL file - if (-not (Test-Path -Path $SqlFile)) { - throw "SQL file not found: $SqlFile" - } - - $arguments = "-S `"$connString`" @`"$SqlFile`"" - } - else { - # Create temp file for SQL command - $tempFile = [System.IO.Path]::GetTempFileName() - $tempFile = [System.IO.Path]::ChangeExtension($tempFile, ".sql") - Set-Content -Path $tempFile -Value $SqlCommand -Encoding ASCII - - $arguments = "-S `"$connString`" @`"$tempFile`"" - } - - # Set Oracle environment - $env:ORACLE_HOME = $oraHome - $env:PATH = "$oraHome\bin;$env:PATH" - $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" - - # Execute SQL*Plus - $processInfo = New-Object System.Diagnostics.ProcessStartInfo - $processInfo.FileName = $sqlplus - $processInfo.Arguments = "-S `"$connString`"" - $processInfo.RedirectStandardInput = $true - $processInfo.RedirectStandardOutput = $true - $processInfo.RedirectStandardError = $true - $processInfo.UseShellExecute = $false - $processInfo.CreateNoWindow = $true - - $process = New-Object System.Diagnostics.Process - $process.StartInfo = $processInfo - $process.Start() | Out-Null - - # Send SQL - if ($SqlFile) { - $process.StandardInput.WriteLine("@`"$SqlFile`"") - } - else { - $process.StandardInput.WriteLine($SqlCommand) - } - $process.StandardInput.Close() - - $output = $process.StandardOutput.ReadToEnd() - $errorOutput = $process.StandardError.ReadToEnd() - $process.WaitForExit() - - if ($errorOutput) { - $output += "`n$errorOutput" - } - - # Check for Oracle errors - if ($output -match "ORA-\d{5}:|SP2-\d{4}:") { - if (-not $Silent) { - Write-LogWarning "SQL*Plus returned errors:" - Write-Host $output -ForegroundColor Yellow - } - } - - return $output - } - finally { - if ($tempFile -and (Test-Path -Path $tempFile)) { - Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue - } - } -} - -<# -.SYNOPSIS - Create Oracle directory object for Data Pump. - -.DESCRIPTION - Creates a directory object in Oracle for Data Pump operations if it doesn't exist. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Password - SYS or SYSTEM password. - -.PARAMETER DirectoryName - Oracle directory object name (default: DMPDIR). - -.PARAMETER DirectoryPath - File system path for the directory (default: C:\DMPDIR). - -.EXAMPLE - New-OracleDirectory -ServiceName "XEPDB1" -Password "oracle" -DirectoryPath "D:\Dumps" -#> -function New-OracleDirectory { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $true)] - [string]$Password, - - [Parameter(Mandatory = $false)] - [string]$DirectoryName = "DMPDIR", - - [Parameter(Mandatory = $false)] - [string]$DirectoryPath = "C:\DMPDIR" - ) - - # Create physical directory if it doesn't exist - if (-not (Test-Path -Path $DirectoryPath)) { - New-Item -ItemType Directory -Path $DirectoryPath -Force | Out-Null - Write-Log "Created directory: $DirectoryPath" - } - - # Always drop and recreate directory to ensure correct path - # This fixes Oracle XE issue where DMPDIR may point to wrong default location - # Using DROP + CREATE instead of CREATE OR REPLACE for reliability - $sql = @" -SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 500 --- Check current directory path -VARIABLE v_path VARCHAR2(500); -VARIABLE v_exists NUMBER; -BEGIN - SELECT COUNT(*), MAX(directory_path) INTO :v_exists, :v_path - FROM dba_directories WHERE directory_name = '$DirectoryName'; -END; -/ -PRINT v_path - --- Drop if exists with different path -BEGIN - IF :v_exists > 0 AND UPPER(TRIM(:v_path)) != UPPER('$DirectoryPath') THEN - EXECUTE IMMEDIATE 'DROP DIRECTORY $DirectoryName'; - DBMS_OUTPUT.PUT_LINE('DROPPED_DIFFERENT_PATH'); - ELSIF :v_exists > 0 THEN - DBMS_OUTPUT.PUT_LINE('PATH_ALREADY_CORRECT'); - END IF; -EXCEPTION - WHEN OTHERS THEN NULL; -END; -/ - --- Create or replace directory -CREATE OR REPLACE DIRECTORY $DirectoryName AS '$DirectoryPath'; -GRANT READ, WRITE ON DIRECTORY $DirectoryName TO PUBLIC; - --- Verify -SELECT 'VERIFIED:' || directory_path FROM dba_directories WHERE directory_name = '$DirectoryName'; -EXIT; -"@ - - $result = Invoke-SqlPlus -OracleHome $OracleHome -ServiceName $ServiceName ` - -Username "SYS" -Password $Password -SqlCommand $sql -AsSysdba -Silent - - # Check verification result - if ($result -match "VERIFIED:(.+)") { - $verifiedPath = $Matches[1].Trim() - if ($verifiedPath.ToUpper() -eq $DirectoryPath.ToUpper()) { - Write-Log "Oracle directory $DirectoryName created pointing to $DirectoryPath" - return $true - } - else { - Write-LogWarning "Directory path mismatch! Expected: $DirectoryPath, Got: $verifiedPath" - return $false - } - } - else { - Write-LogWarning "Could not verify directory creation" - Write-LogWarning "SQL output: $result" - return $false - } -} - -<# -.SYNOPSIS - Get the default datafile path based on Oracle installation. - -.DESCRIPTION - Determines the appropriate datafile location based on Oracle Home and version. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Password - SYSTEM password. - -.OUTPUTS - String. The datafile directory path. - -.EXAMPLE - $datafilePath = Get-DatafilePath -ServiceName "XEPDB1" -Password "oracle" -#> -function Get-DatafilePath { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $true)] - [string]$Password - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - - # Query Oracle for default datafile location - $sql = @" -SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 500 -SELECT 'DATAFILE_PATH:' || SUBSTR(file_name, 1, INSTR(file_name, '\', -1)) -FROM dba_data_files -WHERE tablespace_name = 'SYSTEM' -AND ROWNUM = 1; -EXIT; -"@ - - $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $Password -SqlCommand $sql -Silent - - foreach ($line in $result -split "`n") { - if ($line -match "^DATAFILE_PATH:(.+)$") { - $path = $Matches[1].Trim() - # Remove trailing backslash if present - return $path.TrimEnd('\') - } - } - - # Fallback: derive path from ORACLE_HOME - # Example: C:\app\romfast\product\21c\dbhomeXE -> C:\app\romfast\product\21c\oradata\XE\XEPDB1 - Write-LogWarning "Could not query datafile path from database, using fallback based on ORACLE_HOME" - - $version = Get-OracleVersion -OracleHome $oraHome -ServiceName $ServiceName -Password $Password - - # Try to derive oradata path from ORACLE_HOME - # Pattern: \product\\dbhomeXE -> \product\\oradata\XE\ - if ($oraHome -match "^(.+\\product\\[^\\]+)\\dbhome") { - $baseOradata = "$($Matches[1])\oradata" - if ($version.IsXE) { - if ($version.Version -match "^21") { - return "$baseOradata\XE\XEPDB1" - } - elseif ($version.Version -match "^18") { - return "$baseOradata\XE" - } - } - return $baseOradata - } - - # Legacy fallback for non-standard installations - # Extract base from ORACLE_HOME (e.g., C:\app\romfast from C:\app\romfast\product\21c\dbhomeXE) - if ($oraHome -match "^([A-Z]:\\[^\\]+\\[^\\]+)\\") { - $appBase = $Matches[1] - if ($version.IsXE) { - if ($version.Version -match "^21") { - return "$appBase\oradata\XE\XEPDB1" - } - elseif ($version.Version -match "^18") { - return "$appBase\oradata\XE" - } - } - return "$appBase\oradata" - } - - # Ultimate fallback - this should rarely be reached - Write-LogWarning "Could not derive datafile path from ORACLE_HOME, using hardcoded default" - return "C:\app\oracle\oradata" -} - -<# -.SYNOPSIS - Execute Data Pump import. - -.DESCRIPTION - Runs impdp with specified parameters. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Username - Username for connection (default: SYSTEM). - -.PARAMETER Password - Password for connection. - -.PARAMETER DumpFile - Name of the dump file (in DMPDIR). - -.PARAMETER LogFile - Name of the log file. - -.PARAMETER Schemas - Schema(s) to import. - -.PARAMETER RemapSchema - Remap schema mapping (e.g., "OLD_SCHEMA:NEW_SCHEMA"). - -.PARAMETER DirectoryName - Oracle directory object name (default: DMPDIR). - -.PARAMETER TableExists - Table exists action: SKIP, APPEND, TRUNCATE, REPLACE (default: REPLACE). - -.PARAMETER AdditionalParams - Additional impdp parameters. - -.OUTPUTS - PSCustomObject with Success, LogContent, ErrorMessage properties. - -.EXAMPLE - Invoke-DataPumpImport -ServiceName "XEPDB1" -Password "oracle" -DumpFile "CONTAFIN_ORACLE.dmp" -Schemas "CONTAFIN_ORACLE" -#> -function Invoke-DataPumpImport { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $false)] - [string]$Username = "SYSTEM", - - [Parameter(Mandatory = $true)] - [string]$Password, - - [Parameter(Mandatory = $true)] - [string]$DumpFile, - - [Parameter(Mandatory = $false)] - [string]$LogFile, - - [Parameter(Mandatory = $false)] - [string[]]$Schemas, - - [Parameter(Mandatory = $false)] - [string]$RemapSchema, - - [Parameter(Mandatory = $false)] - [string]$DirectoryName = "DMPDIR", - - [Parameter(Mandatory = $false)] - [ValidateSet('SKIP', 'APPEND', 'TRUNCATE', 'REPLACE')] - [string]$TableExists = "REPLACE", - - [Parameter(Mandatory = $false)] - [string]$AdditionalParams - ) - - $oraHome = Get-OracleHome -OracleHome $OracleHome - $impdp = Join-Path $oraHome "bin\impdp.exe" - - if (-not (Test-Path -Path $impdp)) { - throw "impdp not found at: $impdp" - } - - # Build impdp command - if (-not $LogFile) { - $LogFile = [System.IO.Path]::GetFileNameWithoutExtension($DumpFile) + "_import.log" - } - - $connString = "$Username/`"$Password`"@$ServiceName" - - $params = @( - "`"$connString`"", - "directory=$DirectoryName", - "dumpfile=$DumpFile", - "logfile=$LogFile", - "table_exists_action=$TableExists" - ) - - if ($Schemas) { - $params += "schemas=$($Schemas -join ',')" - } - - if ($RemapSchema) { - $params += "remap_schema=$RemapSchema" - } - - if ($AdditionalParams) { - $params += $AdditionalParams - } - - $arguments = $params -join " " - - Write-Log "Executing: impdp $($params -join ' ' -replace $Password, '****')" - - # Set Oracle environment - $env:ORACLE_HOME = $oraHome - $env:PATH = "$oraHome\bin;$env:PATH" - $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" - - # Execute impdp - $process = Start-Process -FilePath $impdp -ArgumentList $arguments -Wait -NoNewWindow -PassThru ` - -RedirectStandardOutput "$env:TEMP\impdp_out.txt" -RedirectStandardError "$env:TEMP\impdp_err.txt" - - $stdout = Get-Content -Path "$env:TEMP\impdp_out.txt" -Raw -ErrorAction SilentlyContinue - $stderr = Get-Content -Path "$env:TEMP\impdp_err.txt" -Raw -ErrorAction SilentlyContinue - - $result = [PSCustomObject]@{ - Success = $process.ExitCode -eq 0 - ExitCode = $process.ExitCode - Output = $stdout - ErrorOutput = $stderr - LogFile = $LogFile - } - - # Clean up temp files - Remove-Item -Path "$env:TEMP\impdp_out.txt" -Force -ErrorAction SilentlyContinue - Remove-Item -Path "$env:TEMP\impdp_err.txt" -Force -ErrorAction SilentlyContinue - - return $result -} - -<# -.SYNOPSIS - Check if a user/schema exists in the database. - -.DESCRIPTION - Queries DBA_USERS to check if a user exists. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Password - SYSTEM password. - -.PARAMETER SchemaName - Name of the schema/user to check. - -.OUTPUTS - Boolean. True if user exists. - -.EXAMPLE - if (Test-OracleUser -ServiceName "XEPDB1" -Password "oracle" -SchemaName "CONTAFIN_ORACLE") { - Write-Host "User exists" - } -#> -function Test-OracleUser { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $true)] - [string]$Password, - - [Parameter(Mandatory = $true)] - [string]$SchemaName - ) - - $sql = @" -SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF -SELECT 'USER_EXISTS' FROM dba_users WHERE username = UPPER('$SchemaName'); -EXIT; -"@ - - $result = Invoke-SqlPlus -OracleHome $OracleHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $Password -SqlCommand $sql -Silent - - return $result -match "USER_EXISTS" -} - -<# -.SYNOPSIS - Get object count for a schema. - -.DESCRIPTION - Counts objects in a schema grouped by type. - -.PARAMETER OracleHome - Oracle Home directory. - -.PARAMETER ServiceName - Database service name. - -.PARAMETER Password - SYSTEM password. - -.PARAMETER SchemaName - Name of the schema. - -.OUTPUTS - Hashtable with object types and counts. - -.EXAMPLE - $counts = Get-SchemaObjectCount -ServiceName "XEPDB1" -Password "oracle" -SchemaName "CONTAFIN_ORACLE" -#> -function Get-SchemaObjectCount { - [CmdletBinding()] - param( - [Parameter(Mandatory = $false)] - [string]$OracleHome, - - [Parameter(Mandatory = $true)] - [string]$ServiceName, - - [Parameter(Mandatory = $true)] - [string]$Password, - - [Parameter(Mandatory = $true)] - [string]$SchemaName - ) - - $sql = @" -SET PAGESIZE 1000 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 200 -SELECT object_type || ':' || COUNT(*) -FROM dba_objects -WHERE owner = UPPER('$SchemaName') -GROUP BY object_type -ORDER BY object_type; -SELECT 'TOTAL:' || COUNT(*) FROM dba_objects WHERE owner = UPPER('$SchemaName'); -SELECT 'INVALID:' || COUNT(*) FROM dba_objects WHERE owner = UPPER('$SchemaName') AND status = 'INVALID'; -EXIT; -"@ - - $oraHome = Get-OracleHome -OracleHome $OracleHome - - $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` - -Username "SYSTEM" -Password $Password -SqlCommand $sql -Silent - - $counts = @{} - - # Parse result lines for object type counts - $lines = $result -split "`r?`n" - foreach ($line in $lines) { - $trimmed = $line.Trim() - if ($trimmed.Length -gt 0 -and $trimmed -match "^([A-Z][A-Z_ ]*):(\d+)$") { - $key = $Matches[1].Trim() - $value = [int]$Matches[2] - $counts[$key] = $value - } - } - - return $counts -} - -# Note: Functions are available when dot-sourced (. .\oracle-functions.ps1) -# Do NOT use Export-ModuleMember - it only works inside .psm1 modules +#Requires -Version 5.1 + +<# +.SYNOPSIS + Oracle connectivity and operations functions for ROA setup scripts. + +.DESCRIPTION + Provides functions for Oracle database connectivity, SQL*Plus execution, + version detection, and Data Pump operations. + +.NOTES + File Name : oracle-functions.ps1 + Prerequisite : PowerShell 5.1 or higher, Oracle Client installed + Copyright 2024 : ROMFAST +#> + +# Source logging functions +. "$PSScriptRoot\logging-functions.ps1" + +<# +.SYNOPSIS + Get the listener host address. + +.DESCRIPTION + Auto-detects the host address from listener configuration. + Returns the IP address the listener is bound to. + +.PARAMETER OracleHome + Oracle Home directory. + +.OUTPUTS + String. The host address (IP or hostname). + +.EXAMPLE + $host = Get-ListenerHost -OracleHome "C:\app\oracle\product\21c\dbhomeXE" +#> +function Get-ListenerHost { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + $lsnrctl = Join-Path $oraHome "bin\lsnrctl.exe" + + if (-not (Test-Path $lsnrctl)) { + return "localhost" + } + + try { + # Set Oracle environment + $env:ORACLE_HOME = $oraHome + $env:PATH = "$oraHome\bin;$env:PATH" + + $output = & $lsnrctl status 2>&1 | Out-String + + # Parse HOST from listener output - look for TCP endpoint (not TCPS) + # Format: (PROTOCOL=tcp)(HOST=10.0.20.130)(PORT=1521) + # Use simple IP pattern to avoid regex escaping issues + $tcpMatch = [regex]::Match($output, "PROTOCOL=tcp\).*?HOST=([0-9\.]+)") + if ($tcpMatch.Success) { + $listenerHost = $tcpMatch.Groups[1].Value + + # If listener is bound to 0.0.0.0, try to get actual IP + if ($listenerHost -eq "0.0.0.0") { + $ip = (Get-NetIPAddress -AddressFamily IPv4 | + Where-Object { $_.IPAddress -notmatch "^127\." -and $_.PrefixOrigin -ne "WellKnown" } | + Select-Object -First 1).IPAddress + if ($ip) { return $ip } + return "localhost" + } + + # If it's a specific IP (not localhost), return it + if ($listenerHost -notmatch "^127\." -and $listenerHost -ne "localhost") { + return $listenerHost + } + } + + # Fallback: try any HOST= pattern + $anyHostMatch = [regex]::Match($output, "HOST=([0-9\.]+)") + if ($anyHostMatch.Success) { + $listenerHost = $anyHostMatch.Groups[1].Value + if ($listenerHost -notmatch "^127\." -and $listenerHost -ne "0.0.0.0") { + return $listenerHost + } + } + + return "localhost" + } + catch { + return "localhost" + } +} + +<# +.SYNOPSIS + Find Oracle Home directory. + +.DESCRIPTION + Auto-detects Oracle Home from registry, environment variable, or common paths. + +.PARAMETER OracleHome + Optional explicit Oracle Home path. If not specified, auto-detects. + +.OUTPUTS + String. The Oracle Home path. + +.EXAMPLE + $oracleHome = Get-OracleHome +#> +function Get-OracleHome { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome + ) + + # If explicitly provided, validate and return + if ($OracleHome) { + if (Test-Path -Path "$OracleHome\bin\sqlplus.exe") { + return $OracleHome + } + throw "Invalid Oracle Home: sqlplus.exe not found at $OracleHome\bin\sqlplus.exe" + } + + # Try ORACLE_HOME environment variable + if ($env:ORACLE_HOME -and (Test-Path -Path "$env:ORACLE_HOME\bin\sqlplus.exe")) { + return $env:ORACLE_HOME + } + + # Try registry for Oracle XE + $regPaths = @( + 'HKLM:\SOFTWARE\Oracle\KEY_OraDB21Home1', + 'HKLM:\SOFTWARE\Oracle\KEY_OraDB18Home1', + 'HKLM:\SOFTWARE\Oracle\KEY_XE', + 'HKLM:\SOFTWARE\Wow6432Node\Oracle\KEY_OraDB21Home1', + 'HKLM:\SOFTWARE\Wow6432Node\Oracle\KEY_OraDB18Home1' + ) + + foreach ($regPath in $regPaths) { + if (Test-Path -Path $regPath) { + $oraHome = (Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue).ORACLE_HOME + if ($oraHome -and (Test-Path -Path "$oraHome\bin\sqlplus.exe")) { + return $oraHome + } + } + } + + # Try common installation paths (including user-specific paths) + $currentUser = $env:USERNAME + $commonPaths = @( + "C:\app\$currentUser\product\21c\dbhomeXE", + "C:\app\$currentUser\product\21c\dbhome_1", + "C:\app\$currentUser\product\18c\dbhomeXE", + 'C:\app\oracle\product\21c\dbhomeXE', + 'C:\app\oracle\product\21c\dbhome_1', + 'C:\app\oracle\product\18c\dbhomeXE', + 'C:\app\romfast\product\21c\dbhomeXE', + 'C:\app\romfast\product\21c\dbhome_1', + 'C:\oraclexe\app\oracle\product\11.2.0\server', + "D:\app\$currentUser\product\21c\dbhomeXE", + 'D:\app\oracle\product\21c\dbhomeXE', + 'D:\app\oracle\product\18c\dbhomeXE' + ) + + foreach ($path in $commonPaths) { + if (Test-Path -Path "$path\bin\sqlplus.exe") { + return $path + } + } + + throw "Oracle Home not found. Please specify -OracleHome parameter or set ORACLE_HOME environment variable." +} + +<# +.SYNOPSIS + Get Oracle version information. + +.DESCRIPTION + Detects Oracle version (XE vs SE), edition, and whether it's a Container Database (CDB). + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Username + Username for connection (default: SYSTEM). + +.PARAMETER Password + Password for connection. + +.OUTPUTS + PSCustomObject with Version, Edition, IsCDB, IsXE properties. + +.EXAMPLE + $version = Get-OracleVersion -ServiceName "XE" -Password "oracle" +#> +function Get-OracleVersion { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $false)] + [string]$DbHost, + + [Parameter(Mandatory = $false)] + [int]$Port = 1521, + + [Parameter(Mandatory = $false)] + [string]$Username = "SYSTEM", + + [Parameter(Mandatory = $true)] + [string]$Password + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + + $sql = @" +SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF +SELECT 'VERSION:' || version_full FROM v`$instance; +SELECT 'EDITION:' || edition FROM v`$instance; +SELECT 'CDB:' || CDB FROM v`$database; +EXIT; +"@ + + $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -DbHost $DbHost -Port $Port ` + -Username $Username -Password $Password -SqlCommand $sql -Silent + + $versionInfo = [PSCustomObject]@{ + Version = "" + Edition = "" + IsCDB = $false + IsXE = $false + FullInfo = $result + } + + foreach ($line in $result -split "`n") { + if ($line -match "^VERSION:(.+)$") { + $versionInfo.Version = $Matches[1].Trim() + } + elseif ($line -match "^EDITION:(.+)$") { + $versionInfo.Edition = $Matches[1].Trim() + $versionInfo.IsXE = $versionInfo.Edition -match "XE|Express" + } + elseif ($line -match "^CDB:(.+)$") { + $versionInfo.IsCDB = $Matches[1].Trim() -eq "YES" + } + } + + return $versionInfo +} + +<# +.SYNOPSIS + Get the default service name based on Oracle configuration. + +.DESCRIPTION + Auto-detects the appropriate service name. Returns XEPDB1 for Oracle XE CDB, + or ROA for traditional non-CDB installations. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER SystemPassword + SYSTEM user password. + +.OUTPUTS + String. The service name. + +.EXAMPLE + $serviceName = Get-ServiceName -SystemPassword "oracle" +#> +function Get-ServiceName { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $false)] + [string]$DbHost, + + [Parameter(Mandatory = $false)] + [int]$Port = 1521, + + [Parameter(Mandatory = $true)] + [string]$SystemPassword + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + + # First try to connect to XE and check if it's CDB + try { + $version = Get-OracleVersion -OracleHome $oraHome -ServiceName "XE" ` + -DbHost $DbHost -Port $Port ` + -Password $SystemPassword -ErrorAction Stop + + if ($version.IsCDB) { + # For CDB, use XEPDB1 (the default pluggable database) + return "XEPDB1" + } + else { + return "XE" + } + } + catch { + # Try ROA as service name + try { + $null = Test-OracleConnection -OracleHome $oraHome -ServiceName "ROA" ` + -DbHost $DbHost -Port $Port ` + -Username "SYSTEM" -Password $SystemPassword -ErrorAction Stop + return "ROA" + } + catch { + # Default to XEPDB1 for modern XE installations + return "XEPDB1" + } + } +} + +<# +.SYNOPSIS + Test Oracle database connection. + +.DESCRIPTION + Attempts to connect to Oracle and verifies the connection is successful. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Username + Username for connection. + +.PARAMETER Password + Password for connection. + +.OUTPUTS + Boolean. True if connection successful. + +.EXAMPLE + if (Test-OracleConnection -ServiceName "XEPDB1" -Username "SYSTEM" -Password "oracle") { + Write-Host "Connected!" + } +#> +function Test-OracleConnection { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $false)] + [string]$DbHost, + + [Parameter(Mandatory = $false)] + [int]$Port = 1521, + + [Parameter(Mandatory = $false)] + [string]$Username = "SYSTEM", + + [Parameter(Mandatory = $true)] + [string]$Password, + + [Parameter(Mandatory = $false)] + [switch]$AsSysdba + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + + $sql = "SELECT 'CONNECTED' FROM dual; EXIT;" + + try { + $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -DbHost $DbHost -Port $Port ` + -Username $Username -Password $Password -SqlCommand $sql -AsSysdba:$AsSysdba -Silent + + return $result -match "CONNECTED" + } + catch { + return $false + } +} + +<# +.SYNOPSIS + Test if connected to a PDB or non-CDB. + +.DESCRIPTION + Checks the current container type to determine if connected to a PDB, + the CDB root, or a non-CDB database. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Username + Username for connection. + +.PARAMETER Password + Password for connection. + +.OUTPUTS + PSCustomObject with ContainerName, IsPDB, IsCDBRoot, IsNonCDB properties. + +.EXAMPLE + $containerInfo = Test-PDB -ServiceName "XEPDB1" -Password "oracle" +#> +function Test-PDB { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $false)] + [string]$DbHost, + + [Parameter(Mandatory = $false)] + [int]$Port = 1521, + + [Parameter(Mandatory = $false)] + [string]$Username = "SYSTEM", + + [Parameter(Mandatory = $true)] + [string]$Password + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + + $sql = @" +SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF +SELECT 'CONTAINER:' || SYS_CONTEXT('USERENV', 'CON_NAME') FROM dual; +SELECT 'CON_ID:' || SYS_CONTEXT('USERENV', 'CON_ID') FROM dual; +EXIT; +"@ + + $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -DbHost $DbHost -Port $Port ` + -Username $Username -Password $Password -SqlCommand $sql -Silent + + $containerInfo = [PSCustomObject]@{ + ContainerName = "" + ConId = 0 + IsPDB = $false + IsCDBRoot = $false + IsNonCDB = $false + } + + foreach ($line in $result -split "`n") { + if ($line -match "^CONTAINER:(.+)$") { + $containerInfo.ContainerName = $Matches[1].Trim() + } + elseif ($line -match "^CON_ID:(.+)$") { + $containerInfo.ConId = [int]$Matches[1].Trim() + } + } + + if ($containerInfo.ConId -eq 0) { + $containerInfo.IsNonCDB = $true + } + elseif ($containerInfo.ConId -eq 1) { + $containerInfo.IsCDBRoot = $true + } + else { + $containerInfo.IsPDB = $true + } + + return $containerInfo +} + +<# +.SYNOPSIS + Execute SQL via SQL*Plus. + +.DESCRIPTION + Runs a SQL command or file using SQL*Plus and returns the output. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Username + Username for connection. + +.PARAMETER Password + Password for connection. + +.PARAMETER SqlCommand + SQL command(s) to execute. + +.PARAMETER SqlFile + Path to SQL file to execute. + +.PARAMETER AsSysdba + Connect as SYSDBA. + +.PARAMETER Silent + Suppress console output. + +.OUTPUTS + String. The SQL*Plus output. + +.EXAMPLE + Invoke-SqlPlus -ServiceName "XEPDB1" -Username "SYSTEM" -Password "oracle" -SqlCommand "SELECT * FROM dual;" + +.EXAMPLE + Invoke-SqlPlus -ServiceName "XEPDB1" -Username "SYS" -Password "oracle" -SqlFile "C:\scripts\setup.sql" -AsSysdba +#> +function Invoke-SqlPlus { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $false)] + [string]$DbHost, + + [Parameter(Mandatory = $false)] + [int]$Port = 1521, + + [Parameter(Mandatory = $false)] + [string]$Username = "SYSTEM", + + [Parameter(Mandatory = $true)] + [string]$Password, + + [Parameter(Mandatory = $false)] + [string]$SqlCommand, + + [Parameter(Mandatory = $false)] + [string]$SqlFile, + + [Parameter(Mandatory = $false)] + [switch]$AsSysdba, + + [Parameter(Mandatory = $false)] + [switch]$Silent + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + $sqlplus = Join-Path $oraHome "bin\sqlplus.exe" + + if (-not (Test-Path -Path $sqlplus)) { + throw "SQL*Plus not found at: $sqlplus" + } + + # Auto-detect host from listener if not provided + if (-not $DbHost) { + $DbHost = Get-ListenerHost -OracleHome $oraHome + } + + # Build connection string (always use EZConnect format for reliability) + $sysdba = if ($AsSysdba) { " as sysdba" } else { "" } + $connString = "$Username/`"$Password`"@${DbHost}:${Port}/${ServiceName}$sysdba" + + $tempFile = $null + + try { + if ($SqlFile) { + # Execute SQL file + if (-not (Test-Path -Path $SqlFile)) { + throw "SQL file not found: $SqlFile" + } + + $arguments = "-S `"$connString`" @`"$SqlFile`"" + } + else { + # Create temp file for SQL command + $tempFile = [System.IO.Path]::GetTempFileName() + $tempFile = [System.IO.Path]::ChangeExtension($tempFile, ".sql") + Set-Content -Path $tempFile -Value $SqlCommand -Encoding ASCII + + $arguments = "-S `"$connString`" @`"$tempFile`"" + } + + # Set Oracle environment + $env:ORACLE_HOME = $oraHome + $env:PATH = "$oraHome\bin;$env:PATH" + $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" + + # Execute SQL*Plus + $processInfo = New-Object System.Diagnostics.ProcessStartInfo + $processInfo.FileName = $sqlplus + $processInfo.Arguments = "-S `"$connString`"" + $processInfo.RedirectStandardInput = $true + $processInfo.RedirectStandardOutput = $true + $processInfo.RedirectStandardError = $true + $processInfo.UseShellExecute = $false + $processInfo.CreateNoWindow = $true + + $process = New-Object System.Diagnostics.Process + $process.StartInfo = $processInfo + $process.Start() | Out-Null + + # Send SQL + if ($SqlFile) { + $process.StandardInput.WriteLine("@`"$SqlFile`"") + } + else { + $process.StandardInput.WriteLine($SqlCommand) + } + $process.StandardInput.Close() + + $output = $process.StandardOutput.ReadToEnd() + $errorOutput = $process.StandardError.ReadToEnd() + $process.WaitForExit() + + if ($errorOutput) { + $output += "`n$errorOutput" + } + + # Check for Oracle errors + if ($output -match "ORA-\d{5}:|SP2-\d{4}:") { + if (-not $Silent) { + Write-LogWarning "SQL*Plus returned errors:" + Write-Host $output -ForegroundColor Yellow + } + } + + return $output + } + finally { + if ($tempFile -and (Test-Path -Path $tempFile)) { + Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue + } + } +} + +<# +.SYNOPSIS + Create Oracle directory object for Data Pump. + +.DESCRIPTION + Creates a directory object in Oracle for Data Pump operations if it doesn't exist. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Password + SYS or SYSTEM password. + +.PARAMETER DirectoryName + Oracle directory object name (default: DMPDIR). + +.PARAMETER DirectoryPath + File system path for the directory (default: C:\DMPDIR). + +.EXAMPLE + New-OracleDirectory -ServiceName "XEPDB1" -Password "oracle" -DirectoryPath "D:\Dumps" +#> +function New-OracleDirectory { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $true)] + [string]$Password, + + [Parameter(Mandatory = $false)] + [string]$DirectoryName = "DMPDIR", + + [Parameter(Mandatory = $false)] + [string]$DirectoryPath = "C:\DMPDIR" + ) + + # Create physical directory if it doesn't exist + if (-not (Test-Path -Path $DirectoryPath)) { + New-Item -ItemType Directory -Path $DirectoryPath -Force | Out-Null + Write-Log "Created directory: $DirectoryPath" + } + + # Always drop and recreate directory to ensure correct path + # This fixes Oracle XE issue where DMPDIR may point to wrong default location + # Using DROP + CREATE instead of CREATE OR REPLACE for reliability + $sql = @" +SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 500 +-- Check current directory path +VARIABLE v_path VARCHAR2(500); +VARIABLE v_exists NUMBER; +BEGIN + SELECT COUNT(*), MAX(directory_path) INTO :v_exists, :v_path + FROM dba_directories WHERE directory_name = '$DirectoryName'; +END; +/ +PRINT v_path + +-- Drop if exists with different path +BEGIN + IF :v_exists > 0 AND UPPER(TRIM(:v_path)) != UPPER('$DirectoryPath') THEN + EXECUTE IMMEDIATE 'DROP DIRECTORY $DirectoryName'; + DBMS_OUTPUT.PUT_LINE('DROPPED_DIFFERENT_PATH'); + ELSIF :v_exists > 0 THEN + DBMS_OUTPUT.PUT_LINE('PATH_ALREADY_CORRECT'); + END IF; +EXCEPTION + WHEN OTHERS THEN NULL; +END; +/ + +-- Create or replace directory +CREATE OR REPLACE DIRECTORY $DirectoryName AS '$DirectoryPath'; +GRANT READ, WRITE ON DIRECTORY $DirectoryName TO PUBLIC; + +-- Verify +SELECT 'VERIFIED:' || directory_path FROM dba_directories WHERE directory_name = '$DirectoryName'; +EXIT; +"@ + + $result = Invoke-SqlPlus -OracleHome $OracleHome -ServiceName $ServiceName ` + -Username "SYS" -Password $Password -SqlCommand $sql -AsSysdba -Silent + + # Check verification result + if ($result -match "VERIFIED:(.+)") { + $verifiedPath = $Matches[1].Trim() + if ($verifiedPath.ToUpper() -eq $DirectoryPath.ToUpper()) { + Write-Log "Oracle directory $DirectoryName created pointing to $DirectoryPath" + + # Pana aici s-au comparat doar siruri de caractere. Adevarata intrebare + # e daca procesul Oracle poate scrie efectiv acolo, si raspunsul poate fi + # nu chiar cu ACL-uri corecte pe folder: contul virtual al serviciului + # (NT SERVICE\OracleServiceXE) nu are traversare pe radacina lui C:, asa + # ca un DMPDIR pe C:\ pica abia in mijlocul importului, cu un + # ORA-29283 "nonexistent file or path" care nu arata spre cauza. + $testSql = @" +SET PAGESIZE 0 FEEDBACK OFF HEADING OFF VERIFY OFF ECHO OFF SERVEROUTPUT ON +DECLARE + f UTL_FILE.FILE_TYPE; +BEGIN + f := UTL_FILE.FOPEN('$DirectoryName', 'roa_write_test.tmp', 'w'); + UTL_FILE.PUT_LINE(f, 'ok'); + UTL_FILE.FCLOSE(f); + UTL_FILE.FREMOVE('$DirectoryName', 'roa_write_test.tmp'); + DBMS_OUTPUT.PUT_LINE('WRITE_OK'); +EXCEPTION + WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('WRITE_FAIL:' || SQLERRM); +END; +/ +EXIT; +"@ + $testResult = Invoke-SqlPlus -OracleHome $OracleHome -ServiceName $ServiceName ` + -Username "SYS" -Password $Password -SqlCommand $testSql -AsSysdba -Silent + + if ($testResult -notmatch "WRITE_OK") { + Write-LogError "Baza NU poate scrie in $DirectoryPath (directorul Oracle $DirectoryName)." + Write-LogError " $(($testResult -split "`n" | Where-Object { $_ -match 'ORA-|WRITE_FAIL' } | Select-Object -First 2) -join ' ')" + Write-LogError " Serviciul Oracle ruleaza ca NT SERVICE\OracleServiceXE si nu ajunge la calea asta" + Write-LogError " chiar daca ACL-ul pe folder pare corect - de obicei lipseste traversarea pe" + Write-LogError " radacina discului. Pune DMP-urile pe discul unde e Oracle Home si reia cu" + Write-LogError " optiunea /dmpdest: (ex. /dmpdest:D:\DMPDIR)." + return $false + } + Write-Log "Verificat: baza poate scrie in $DirectoryPath" + return $true + } + else { + Write-LogWarning "Directory path mismatch! Expected: $DirectoryPath, Got: $verifiedPath" + return $false + } + } + else { + Write-LogWarning "Could not verify directory creation" + Write-LogWarning "SQL output: $result" + return $false + } +} + +<# +.SYNOPSIS + Get the default datafile path based on Oracle installation. + +.DESCRIPTION + Determines the appropriate datafile location based on Oracle Home and version. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Password + SYSTEM password. + +.OUTPUTS + String. The datafile directory path. + +.EXAMPLE + $datafilePath = Get-DatafilePath -ServiceName "XEPDB1" -Password "oracle" +#> +function Get-DatafilePath { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $true)] + [string]$Password + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + + # Query Oracle for default datafile location + $sql = @" +SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 500 +SELECT 'DATAFILE_PATH:' || SUBSTR(file_name, 1, INSTR(file_name, '\', -1)) +FROM dba_data_files +WHERE tablespace_name = 'SYSTEM' +AND ROWNUM = 1; +EXIT; +"@ + + $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $Password -SqlCommand $sql -Silent + + foreach ($line in $result -split "`n") { + if ($line -match "^DATAFILE_PATH:(.+)$") { + $path = $Matches[1].Trim() + # Remove trailing backslash if present + return $path.TrimEnd('\') + } + } + + # Fallback: derive path from ORACLE_HOME + # Example: C:\app\romfast\product\21c\dbhomeXE -> C:\app\romfast\product\21c\oradata\XE\XEPDB1 + Write-LogWarning "Could not query datafile path from database, using fallback based on ORACLE_HOME" + + $version = Get-OracleVersion -OracleHome $oraHome -ServiceName $ServiceName -Password $Password + + # Try to derive oradata path from ORACLE_HOME + # Pattern: \product\\dbhomeXE -> \product\\oradata\XE\ + if ($oraHome -match "^(.+\\product\\[^\\]+)\\dbhome") { + $baseOradata = "$($Matches[1])\oradata" + if ($version.IsXE) { + if ($version.Version -match "^21") { + return "$baseOradata\XE\XEPDB1" + } + elseif ($version.Version -match "^18") { + return "$baseOradata\XE" + } + } + return $baseOradata + } + + # Legacy fallback for non-standard installations + # Extract base from ORACLE_HOME (e.g., C:\app\romfast from C:\app\romfast\product\21c\dbhomeXE) + if ($oraHome -match "^([A-Z]:\\[^\\]+\\[^\\]+)\\") { + $appBase = $Matches[1] + if ($version.IsXE) { + if ($version.Version -match "^21") { + return "$appBase\oradata\XE\XEPDB1" + } + elseif ($version.Version -match "^18") { + return "$appBase\oradata\XE" + } + } + return "$appBase\oradata" + } + + # Ultimate fallback - this should rarely be reached + Write-LogWarning "Could not derive datafile path from ORACLE_HOME, using hardcoded default" + return "C:\app\oracle\oradata" +} + +<# +.SYNOPSIS + Execute Data Pump import. + +.DESCRIPTION + Runs impdp with specified parameters. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Username + Username for connection (default: SYSTEM). + +.PARAMETER Password + Password for connection. + +.PARAMETER DumpFile + Name of the dump file (in DMPDIR). + +.PARAMETER LogFile + Name of the log file. + +.PARAMETER Schemas + Schema(s) to import. + +.PARAMETER RemapSchema + Remap schema mapping (e.g., "OLD_SCHEMA:NEW_SCHEMA"). + +.PARAMETER DirectoryName + Oracle directory object name (default: DMPDIR). + +.PARAMETER TableExists + Table exists action: SKIP, APPEND, TRUNCATE, REPLACE (default: REPLACE). + +.PARAMETER AdditionalParams + Additional impdp parameters. + +.OUTPUTS + PSCustomObject with Success, LogContent, ErrorMessage properties. + +.EXAMPLE + Invoke-DataPumpImport -ServiceName "XEPDB1" -Password "oracle" -DumpFile "CONTAFIN_ORACLE.dmp" -Schemas "CONTAFIN_ORACLE" +#> +function Invoke-DataPumpImport { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $false)] + [string]$Username = "SYSTEM", + + [Parameter(Mandatory = $true)] + [string]$Password, + + [Parameter(Mandatory = $true)] + [string]$DumpFile, + + [Parameter(Mandatory = $false)] + [string]$LogFile, + + [Parameter(Mandatory = $false)] + [string[]]$Schemas, + + [Parameter(Mandatory = $false)] + [string]$RemapSchema, + + [Parameter(Mandatory = $false)] + [string]$DirectoryName = "DMPDIR", + + [Parameter(Mandatory = $false)] + [ValidateSet('SKIP', 'APPEND', 'TRUNCATE', 'REPLACE')] + [string]$TableExists = "REPLACE", + + [Parameter(Mandatory = $false)] + [string]$AdditionalParams + ) + + $oraHome = Get-OracleHome -OracleHome $OracleHome + $impdp = Join-Path $oraHome "bin\impdp.exe" + + if (-not (Test-Path -Path $impdp)) { + throw "impdp not found at: $impdp" + } + + # Build impdp command + if (-not $LogFile) { + $LogFile = [System.IO.Path]::GetFileNameWithoutExtension($DumpFile) + "_import.log" + } + + $connString = "$Username/`"$Password`"@$ServiceName" + + $params = @( + "`"$connString`"", + "directory=$DirectoryName", + "dumpfile=$DumpFile", + "logfile=$LogFile", + "table_exists_action=$TableExists" + ) + + if ($Schemas) { + $params += "schemas=$($Schemas -join ',')" + } + + if ($RemapSchema) { + $params += "remap_schema=$RemapSchema" + } + + if ($AdditionalParams) { + $params += $AdditionalParams + } + + $arguments = $params -join " " + + Write-Log "Executing: impdp $($params -join ' ' -replace $Password, '****')" + + # Set Oracle environment + $env:ORACLE_HOME = $oraHome + $env:PATH = "$oraHome\bin;$env:PATH" + $env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8" + + # Execute impdp + $process = Start-Process -FilePath $impdp -ArgumentList $arguments -Wait -NoNewWindow -PassThru ` + -RedirectStandardOutput "$env:TEMP\impdp_out.txt" -RedirectStandardError "$env:TEMP\impdp_err.txt" + + $stdout = Get-Content -Path "$env:TEMP\impdp_out.txt" -Raw -ErrorAction SilentlyContinue + $stderr = Get-Content -Path "$env:TEMP\impdp_err.txt" -Raw -ErrorAction SilentlyContinue + + $result = [PSCustomObject]@{ + Success = $process.ExitCode -eq 0 + ExitCode = $process.ExitCode + Output = $stdout + ErrorOutput = $stderr + LogFile = $LogFile + } + + # Clean up temp files + Remove-Item -Path "$env:TEMP\impdp_out.txt" -Force -ErrorAction SilentlyContinue + Remove-Item -Path "$env:TEMP\impdp_err.txt" -Force -ErrorAction SilentlyContinue + + return $result +} + +<# +.SYNOPSIS + Check if a user/schema exists in the database. + +.DESCRIPTION + Queries DBA_USERS to check if a user exists. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Password + SYSTEM password. + +.PARAMETER SchemaName + Name of the schema/user to check. + +.OUTPUTS + Boolean. True if user exists. + +.EXAMPLE + if (Test-OracleUser -ServiceName "XEPDB1" -Password "oracle" -SchemaName "CONTAFIN_ORACLE") { + Write-Host "User exists" + } +#> +function Test-OracleUser { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $true)] + [string]$Password, + + [Parameter(Mandatory = $true)] + [string]$SchemaName + ) + + $sql = @" +SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF +SELECT 'USER_EXISTS' FROM dba_users WHERE username = UPPER('$SchemaName'); +EXIT; +"@ + + $result = Invoke-SqlPlus -OracleHome $OracleHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $Password -SqlCommand $sql -Silent + + return $result -match "USER_EXISTS" +} + +<# +.SYNOPSIS + Get object count for a schema. + +.DESCRIPTION + Counts objects in a schema grouped by type. + +.PARAMETER OracleHome + Oracle Home directory. + +.PARAMETER ServiceName + Database service name. + +.PARAMETER Password + SYSTEM password. + +.PARAMETER SchemaName + Name of the schema. + +.OUTPUTS + Hashtable with object types and counts. + +.EXAMPLE + $counts = Get-SchemaObjectCount -ServiceName "XEPDB1" -Password "oracle" -SchemaName "CONTAFIN_ORACLE" +#> +function Get-SchemaObjectCount { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$OracleHome, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $true)] + [string]$Password, + + [Parameter(Mandatory = $true)] + [string]$SchemaName + ) + + $sql = @" +SET PAGESIZE 1000 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 200 +SELECT object_type || ':' || COUNT(*) +FROM dba_objects +WHERE owner = UPPER('$SchemaName') +GROUP BY object_type +ORDER BY object_type; +SELECT 'TOTAL:' || COUNT(*) FROM dba_objects WHERE owner = UPPER('$SchemaName'); +SELECT 'INVALID:' || COUNT(*) FROM dba_objects WHERE owner = UPPER('$SchemaName') AND status = 'INVALID'; +EXIT; +"@ + + $oraHome = Get-OracleHome -OracleHome $OracleHome + + $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName ` + -Username "SYSTEM" -Password $Password -SqlCommand $sql -Silent + + $counts = @{} + + # Parse result lines for object type counts + $lines = $result -split "`r?`n" + foreach ($line in $lines) { + $trimmed = $line.Trim() + if ($trimmed.Length -gt 0 -and $trimmed -match "^([A-Z][A-Z_ ]*):(\d+)$") { + $key = $Matches[1].Trim() + $value = [int]$Matches[2] + $counts[$key] = $value + } + } + + return $counts +} + +# Note: Functions are available when dot-sourced (. .\oracle-functions.ps1) +# Do NOT use Export-ModuleMember - it only works inside .psm1 modules