Continuarea corectiei 3 din ec66377. Aceea rezolva doar jumatatea vizibila a
problemei; jumatatea ascunsa ar fi lovit la primul reboot al serverului, dupa
plecarea de la client. Gasit la VADECO, verificat pe loc.
1. ORA-12514 la toti clientii, cu baza perfect sanatoasa.
TNS_ADMIN de masina nu decide doar de unde se citeste sqlnet.ora: instanta
rezolva de acolo si aliasurile din tnsnames.ora. La Oracle XE LOCAL_LISTENER
e implicit aliasul LISTENER_XE, definit doar in tnsnames.ora din Oracle Home.
Cand serviciul reporneste dupa ce ROAClient a pus TNS_ADMIN pe folderul
instantclient-ului, aliasul nu se mai rezolva, instanta nu se mai
inregistreaza la listener, si atunci: v$instance OPEN, v$pdbs READ WRITE,
listener pornit pe 0.0.0.0:1521, lsnrctl arata doar CLRExtProc, si absolut
orice client primeste ORA-12514.
Capcana e de timp, nu de continut: o instanta pornita inainte ca TNS_ADMIN
sa existe nu il vede, deci instalarea pare impecabila si cade la primul
reboot. Reprodus la VADECO cu un simplu Restart-Service OracleServiceXE.
Pasul 01 scrie acum si tnsnames.ora in TNS_ADMIN (aliasul ROA pentru
ROAClient - sablonul livrat cu el arata spre HOST=SERVER_ROA, inexistent -
plus LISTENER_XE pentru instanta) si, mai important, fixeaza LOCAL_LISTENER
pe adresa literala. Doar asta din urma rezista si daca ROAClient se
instaleaza dupa kit, sau daca folderul instantclient se muta.
2. ORA-01017 desi parola e corecta.
Cu SQLNET.ALLOWED_LOGON_VERSION_SERVER=8 serverul autentifica orice client
pre-12c pe baza verificatorului 10G. Un cont fara 10G in PASSWORD_VERSIONS
nu se mai poate conecta din Instant Client 10/11, si eroarea nu e ORA-28040,
ci ORA-01017 - fix cea care trimite pe pista parolei. Dupa o instalare 21c,
SYSTEM are doar 11G 12C; CONTAFIN_ORACLE scapa doar pentru ca pasul 01 ii
rescrie parola dupa ce a scris sqlnet.ora. Pasul 01 rescrie acum si parola
lui SYSTEM, din CDB$ROOT, cu aceeasi valoare.
Subtilitate: interogat din PDB, PASSWORD_VERSIONS al unui utilizator comun
ramane cel vechi si dupa reparatie, desi autentificarea foloseste definitia
din root si functioneaza. Coloana nu e o dovada pentru SYSTEM; conectarea e.
3. Pasul 07 verifica acum ce nu se vede pe hartie.
Toate cele trei erori trec de verificarile de pana acum: fisierul exista,
parametrul e setat, contul e OPEN, obiectele sunt valide. Sectiunea noua
"Conectivitate client vechi (Instant Client)" verifica unde se citeste
efectiv sqlnet.ora, daca tnsnames.ora din TNS_ADMIN mai e sablonul, daca
LOCAL_LISTENER e alias sau adresa, ce verificatoare de parola au conturile,
si - singurul lucru care le prinde pe toate - face o conectare adevarata cu
Instant Client-ul gasit pe masina. La esec, raportul spune si unde sa te
uiti. Parametri noi: -ContafinPassword, -InstantClientDir.
4. Get-ServerLanIPv4, in biblioteca.
Pe serverele la care intram prin Tailscale, un tnsnames.ora cu 100.x nu duce
nicaieri de pe o statie. Functia alege IP-ul din LAN sarind peste Tailscale
(100.64.0.0/10) si APIPA, dupa ruta implicita cand sunt mai multe placi.
Get-ListenerHost o foloseste si el la fallback-ul pe 0.0.0.0, unde inainte
lua prima adresa nefiltrata.
La VADECO a mai fost nevoie de o corectie manuala, in afara kitului: listener.ora
avea HOST = <nume>.ts.net, deci pornirea listenerului depindea de Tailscale.
Regula generala e in documentatie.
Detaliile, cu dovezi si comenzi de diagnostic: docs/lectii-conectare-client-oracle.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SzF1hf4aFS1tmJWpPMiwGp
1231 lines
34 KiB
PowerShell
1231 lines
34 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
|
|
IP-ul din LAN al serverului - cel pe care il vad statiile de lucru.
|
|
|
|
.DESCRIPTION
|
|
Alege adresa IPv4 a interfetei care are ruta implicita, sarind peste:
|
|
- loopback (127.x) si APIPA (169.254.x);
|
|
- intervalul Tailscale (100.64.0.0/10). Pe serverele la care intram prin
|
|
Tailscale, adresa 100.x exista si e perfect functionala pentru noi, dar
|
|
e inutila intr-un tnsnames.ora dus pe statii: statia nu e in tailnet.
|
|
Daca nu gaseste nimic potrivit, intoarce sirul gol - apelantul decide.
|
|
|
|
.OUTPUTS
|
|
String. Adresa IPv4 din LAN, sau "" daca nu poate fi determinata.
|
|
|
|
.EXAMPLE
|
|
$ip = Get-ServerLanIPv4
|
|
#>
|
|
function Get-ServerLanIPv4 {
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
$eTailscale = { param($ip) $ip -match '^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.' }
|
|
|
|
$adrese = @()
|
|
try {
|
|
$adrese = @(Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop |
|
|
Where-Object {
|
|
$_.IPAddress -notmatch '^(127\.|169\.254\.)' -and
|
|
-not (& $eTailscale $_.IPAddress)
|
|
})
|
|
}
|
|
catch {
|
|
# Masini fara modulul NetTCPIP: cadem pe rezolvarea numelui propriu.
|
|
$ipuri = @([Net.Dns]::GetHostAddresses([Net.Dns]::GetHostName()) |
|
|
Where-Object { $_.AddressFamily -eq 'InterNetwork' } |
|
|
ForEach-Object { $_.IPAddressToString } |
|
|
Where-Object { $_ -notmatch '^(127\.|169\.254\.)' -and -not (& $eTailscale $_) })
|
|
if ($ipuri.Count -gt 0) { return $ipuri[0] }
|
|
return ""
|
|
}
|
|
|
|
if ($adrese.Count -eq 0) { return "" }
|
|
if ($adrese.Count -eq 1) { return $adrese[0].IPAddress }
|
|
|
|
# Mai multe placi: o alegem pe cea cu ruta implicita.
|
|
try {
|
|
$rute = @(Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction Stop |
|
|
Sort-Object RouteMetric, ifMetric)
|
|
foreach ($r in $rute) {
|
|
$potrivire = $adrese | Where-Object { $_.InterfaceIndex -eq $r.ifIndex } | Select-Object -First 1
|
|
if ($potrivire) { return $potrivire.IPAddress }
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return $adrese[0].IPAddress
|
|
}
|
|
|
|
<#
|
|
.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-ServerLanIPv4
|
|
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
|