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