Gasite la instalarea de productie VADECO (Oracle 21c XE, Windows Server 2019). 1. Pe serverul VADECO procesul Oracle nu poate atinge nicio cale de pe C:, nici macar C:\Windows\Temp, desi ACL-urile sunt corecte si serviciul ruleaza ca NT SERVICE\OracleServiceXE. D: merge cu exact acelasi grant. Calea DMP era insa hardcodata "C:\DMPDIR" in 01, 02, 05, 06 si in biblioteca, deci instalarea nu se putea muta. Acum e parametrul -DmpDir, expus ca /dmpdest: in FAZA1.cmd si FAZA2.cmd si transmis tuturor pasilor. Cel mai perfid era pasul 02: recrea DIRECTORY DMPDIR pe 'C:\DMPDIR' peste valoarea pusa de 01. Chiar cu restul instalarii mutata, SYS.NEWSCHEMA ar fi cautat FIRMANOUA.dmp in alta parte, iar adaugarea unei firme noi ar fi picat peste luni, fara legatura vizibila cu instalarea. 2. New-OracleDirectory compara doar siruri de caractere: verifica ce scrie in dba_directories, nu daca baza chiar poate scrie acolo. Acum face un UTL_FILE round-trip si esueaza pe loc, cu explicatia cauzei. Inainte, directorul inutilizabil trecea drept bun si importul cadea abia in impdp cu ORA-29283, la zeci de linii distanta. Valoarea de retur nici nu era verificata in 01/03/06 - de aici si "True" ratacit in log. 3. Instalarea ROAClient seteaza TNS_ADMIN de masina spre instantclient-ul ei. Serviciul Oracle mosteneste variabilele de masina, deci citea sqlnet.ora de acolo - unde nu exista niciunul - si nu din Oracle Home, unde il scria pasul 01. Compatibilitatea pentru clienti vechi nu se aplica, iar statiile ar fi primit ORA-28040 cu fisierul scris corect, in locul gresit. Pasul 01 scrie acum sqlnet.ora si in TNS_ADMIN cand difera. 4. Pasul 05 lega schemele de DMP-uri cu "^$schema[_\.]", deci schema DANUBE putea primi DANUBE_20200914.dmp - datele altei firme, fara avertisment. Numele exact are acum intaietate, prefixul se accepta doar daca e unic, iar ambiguitatea se raporteaza. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SzF1hf4aFS1tmJWpPMiwGp
1174 lines
32 KiB
PowerShell
1174 lines
32 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"
|
|
}
|
|
|
|
# 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:<cale> (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: <base>\product\<version>\dbhomeXE -> <base>\product\<version>\oradata\XE\<PDB>
|
|
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
|