#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 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" } # Build connection string (EZConnect format if DbHost is provided) $sysdba = if ($AsSysdba) { " as sysdba" } else { "" } if ($DbHost) { $connString = "$Username/`"$Password`"@${DbHost}:${Port}/${ServiceName}$sysdba" } else { $connString = "$Username/`"$Password`"@$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; "@ $result = Invoke-SqlPlus -OracleHome $OracleHome -ServiceName $ServiceName ` -Username "SYSTEM" -Password $Password -SqlCommand $sql -Silent $counts = @{} foreach ($line in $result -split "`n") { if ($line -match "^([A-Z_ ]+):(\d+)$") { $counts[$Matches[1].Trim()] = [int]$Matches[2] } } return $counts } # Note: Functions are available when dot-sourced (. .\oracle-functions.ps1) # Do NOT use Export-ModuleMember - it only works inside .psm1 modules