#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