Key fixes: - Add Run.cmd/RunAll.cmd wrappers with ExecutionPolicy Bypass - Add Get-ListenerHost() to auto-detect listener IP address - Fix impdp connection using EZConnect format (host:port/service) - Add parallel=1 for Oracle XE compatibility - Fix Write-Log to accept empty strings with [AllowEmptyString()] - Fix Get-SchemaObjectCount regex for Windows line endings (\r\n) - Fix path comparison for DMP file copy operation - Add GRANT EXECUTE ON SYS.AUTH_PACK TO PUBLIC for PACK_DREPTURI - Fix VAUTH_SERII view to use SYN_NOM_PROGRAME (has DENUMIRE column) - Add sections 10-11 to grants-public.sql for SYS object grants Tested on VM 302 (10.0.20.130) with Oracle XE 21c. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1076 lines
29 KiB
PowerShell
1076 lines
29 KiB
PowerShell
#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"
|
|
}
|
|
|
|
$sql = @"
|
|
SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF
|
|
CREATE OR REPLACE DIRECTORY $DirectoryName AS '$DirectoryPath';
|
|
GRANT READ, WRITE ON DIRECTORY $DirectoryName TO PUBLIC;
|
|
COMMIT;
|
|
SELECT 'DIRECTORY_CREATED' FROM dual;
|
|
EXIT;
|
|
"@
|
|
|
|
$result = Invoke-SqlPlus -OracleHome $OracleHome -ServiceName $ServiceName `
|
|
-Username "SYS" -Password $Password -SqlCommand $sql -AsSysdba -Silent
|
|
|
|
if ($result -match "DIRECTORY_CREATED") {
|
|
Write-Log "Oracle directory $DirectoryName created pointing to $DirectoryPath"
|
|
return $true
|
|
}
|
|
else {
|
|
Write-LogWarning "Could not verify directory creation"
|
|
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 to common paths based on Oracle Home
|
|
$version = Get-OracleVersion -OracleHome $oraHome -ServiceName $ServiceName -Password $Password
|
|
|
|
if ($version.IsXE) {
|
|
if ($version.Version -match "^21") {
|
|
return "C:\app\oracle\oradata\XE\XEPDB1"
|
|
}
|
|
elseif ($version.Version -match "^18") {
|
|
return "C:\app\oracle\oradata\XE"
|
|
}
|
|
}
|
|
|
|
# Default fallback
|
|
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
|