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
449 lines
15 KiB
PowerShell
449 lines
15 KiB
PowerShell
#Requires -Version 5.1
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Batch import company schemas from Data Pump dump files.
|
|
|
|
.DESCRIPTION
|
|
Imports company schemas from dump files. CRITICAL: Reads passwords from
|
|
CONTAFIN_ORACLE.NOM_FIRME table to ensure correct authentication.
|
|
|
|
For each company:
|
|
1. Queries NOM_FIRME for schema name and password
|
|
2. Finds matching DMP file in DumpDirectory
|
|
3. Creates user with password from NOM_FIRME
|
|
4. Runs impdp to import schema
|
|
5. Logs progress
|
|
|
|
.PARAMETER DumpDirectory
|
|
Directory containing company DMP files. If not specified, defaults to C:\DMPDIR.
|
|
|
|
.PARAMETER OracleHome
|
|
Oracle home directory. If not specified, auto-detects.
|
|
|
|
.PARAMETER ServiceName
|
|
Database service name. Default: XEPDB1
|
|
|
|
.PARAMETER SystemPassword
|
|
SYSTEM user password. Default: romfastsoft
|
|
|
|
.PARAMETER ParallelJobs
|
|
Number of parallel import jobs. Default: 2
|
|
|
|
.PARAMETER Companies
|
|
Optional array of specific company names to import. If not specified, imports all.
|
|
|
|
.PARAMETER DryRun
|
|
Show what would be imported without actually importing.
|
|
|
|
.EXAMPLE
|
|
.\05-import-companies.ps1 -DumpDirectory "C:\dumps\companies"
|
|
|
|
.EXAMPLE
|
|
.\05-import-companies.ps1 -DumpDirectory "D:\backups" -Companies "FIRMA1","FIRMA2"
|
|
|
|
.EXAMPLE
|
|
.\05-import-companies.ps1 -DumpDirectory "C:\dumps" -DryRun
|
|
|
|
.NOTES
|
|
File Name : 05-import-companies.ps1
|
|
Prerequisite : Run 04-create-synonyms-grants.ps1 first
|
|
Copyright 2024 : ROMFAST
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$DumpDirectory = "C:\DMPDIR",
|
|
|
|
# Calea fizica a directorului Oracle DMPDIR - de acolo citeste baza.
|
|
# Implicit e aceeasi cu DumpDirectory, deci nu trebuie data decat cand
|
|
# DMP-urile stau in alta parte decat directorul pe care il vede baza.
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$DmpDir,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$OracleHome,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$ServiceName = "XEPDB1",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$SystemPassword = "romfastsoft",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[int]$ParallelJobs = 2,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string[]]$Companies,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$DryRun
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
# Source library functions
|
|
. "$PSScriptRoot\lib\logging-functions.ps1"
|
|
. "$PSScriptRoot\lib\oracle-functions.ps1"
|
|
|
|
# Initialize logging
|
|
$logPath = Join-Path $PSScriptRoot "..\logs\05-import-companies_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
|
|
Initialize-LogFile -LogPath $logPath -ScriptName "05-import-companies.ps1"
|
|
|
|
try {
|
|
Write-LogSection "Batch Import Company Schemas"
|
|
|
|
# Validate DumpDirectory exists
|
|
if (-not (Test-Path $DumpDirectory -PathType Container)) {
|
|
throw "DumpDirectory not found: $DumpDirectory"
|
|
}
|
|
Write-Log "Using dump directory: $DumpDirectory"
|
|
|
|
if ($DryRun) {
|
|
Write-LogWarning "DRY RUN MODE - No changes will be made"
|
|
}
|
|
|
|
# Validate Oracle installation
|
|
$oraHome = Get-OracleHome -OracleHome $OracleHome
|
|
Write-LogSuccess "Oracle Home: $oraHome"
|
|
|
|
# Test connection
|
|
Write-Log "Testing database connection..."
|
|
if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Password $SystemPassword)) {
|
|
throw "Cannot connect to database. Please verify ServiceName and SystemPassword."
|
|
}
|
|
Write-LogSuccess "Database connection successful"
|
|
|
|
# Get company list from NOM_FIRME
|
|
Write-LogSection "Reading Company List from NOM_FIRME"
|
|
|
|
$companySql = @"
|
|
SET PAGESIZE 10000 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF LINESIZE 500
|
|
SELECT schema || '|' || parola
|
|
FROM CONTAFIN_ORACLE.NOM_FIRME
|
|
WHERE sters = 0
|
|
AND schema IS NOT NULL
|
|
ORDER BY schema;
|
|
EXIT;
|
|
"@
|
|
|
|
$companyResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $companySql -Silent
|
|
|
|
# Parse company data
|
|
$companyData = @{}
|
|
foreach ($line in $companyResult -split "`n") {
|
|
$line = $line.Trim()
|
|
if ($line -match "^([A-Z0-9_]+)\|(.*)$") {
|
|
$schema = $Matches[1]
|
|
$password = $Matches[2]
|
|
if (-not $password) { $password = "ROMFASTSOFT" }
|
|
$companyData[$schema] = $password
|
|
}
|
|
}
|
|
|
|
Write-Log "Found $($companyData.Count) companies in NOM_FIRME"
|
|
|
|
if ($companyData.Count -eq 0) {
|
|
Write-LogWarning "No companies found in NOM_FIRME"
|
|
Close-LogFile -Success $true
|
|
exit 0
|
|
}
|
|
|
|
# Filter companies if specified
|
|
if ($Companies) {
|
|
$filteredData = @{}
|
|
foreach ($company in $Companies) {
|
|
$companyUpper = $company.ToUpper()
|
|
if ($companyData.ContainsKey($companyUpper)) {
|
|
$filteredData[$companyUpper] = $companyData[$companyUpper]
|
|
}
|
|
else {
|
|
Write-LogWarning "Company $companyUpper not found in NOM_FIRME"
|
|
}
|
|
}
|
|
$companyData = $filteredData
|
|
Write-Log "Filtered to $($companyData.Count) companies"
|
|
}
|
|
|
|
# Find matching dump files
|
|
Write-LogSection "Scanning Dump Files"
|
|
|
|
$dumpFiles = Get-ChildItem -Path $DumpDirectory -Filter "*.dmp" -File
|
|
Write-Log "Found $($dumpFiles.Count) DMP files in $DumpDirectory"
|
|
|
|
# Match companies with dump files
|
|
$importList = @()
|
|
$notFoundList = @()
|
|
|
|
foreach ($schema in $companyData.Keys) {
|
|
# Numele exact are intaietate. Fara asta, schema DANUBE putea primi
|
|
# DANUBE_20200914.dmp - datele altei firme, fara niciun avertisment.
|
|
$matchingFile = $dumpFiles | Where-Object { $_.BaseName -ieq $schema } | Select-Object -First 1
|
|
|
|
if (-not $matchingFile) {
|
|
$escaped = [regex]::Escape($schema)
|
|
$prefixed = @($dumpFiles | Where-Object { $_.BaseName -imatch "^$escaped[_\.]" })
|
|
|
|
if ($prefixed.Count -eq 1) {
|
|
$matchingFile = $prefixed[0]
|
|
}
|
|
elseif ($prefixed.Count -gt 1) {
|
|
$nume = ($prefixed | ForEach-Object { $_.Name }) -join ', '
|
|
Write-LogWarning " - ${schema}: $($prefixed.Count) DMP-uri ambigue ($nume) - redenumeste-l pe cel bun in $schema.dmp"
|
|
}
|
|
}
|
|
|
|
if ($matchingFile) {
|
|
$importList += [PSCustomObject]@{
|
|
Schema = $schema
|
|
Password = $companyData[$schema]
|
|
DumpFile = $matchingFile.FullName
|
|
DumpSize = [math]::Round($matchingFile.Length / 1MB, 2)
|
|
}
|
|
}
|
|
else {
|
|
$notFoundList += $schema
|
|
}
|
|
}
|
|
|
|
Write-Log "Companies to import: $($importList.Count)"
|
|
if ($notFoundList.Count -gt 0) {
|
|
Write-LogWarning "Companies without matching dump file: $($notFoundList.Count)"
|
|
foreach ($schema in $notFoundList) {
|
|
Write-Log " - $schema (no DMP file found)"
|
|
}
|
|
}
|
|
|
|
if ($importList.Count -eq 0) {
|
|
Write-LogWarning "No companies to import"
|
|
Close-LogFile -Success $true
|
|
exit 0
|
|
}
|
|
|
|
# Display import plan
|
|
Write-LogSection "Import Plan"
|
|
|
|
$totalSize = ($importList | Measure-Object -Property DumpSize -Sum).Sum
|
|
Write-Log "Total data to import: $totalSize MB"
|
|
Write-Log ""
|
|
Write-Log "Companies to import:"
|
|
foreach ($item in $importList) {
|
|
Write-Log " - $($item.Schema): $($item.DumpFile) ($($item.DumpSize) MB)"
|
|
}
|
|
|
|
if ($DryRun) {
|
|
Write-LogSection "Dry Run Complete"
|
|
Write-Log "Would import $($importList.Count) company schemas"
|
|
Close-LogFile -Success $true
|
|
exit 0
|
|
}
|
|
|
|
# Ensure DMPDIR exists
|
|
$dmpDir = if ($DmpDir) { $DmpDir } else { $DumpDirectory }
|
|
if (-not (Test-Path -Path $dmpDir)) {
|
|
New-Item -ItemType Directory -Path $dmpDir -Force | Out-Null
|
|
}
|
|
|
|
# Process each company
|
|
Write-LogSection "Importing Companies"
|
|
|
|
$successCount = 0
|
|
$failCount = 0
|
|
$startTime = Get-Date
|
|
|
|
foreach ($item in $importList) {
|
|
Write-LogSection "Importing $($item.Schema)"
|
|
|
|
try {
|
|
# Check if user already exists
|
|
$userExists = Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Password $SystemPassword -SchemaName $item.Schema
|
|
|
|
if ($userExists) {
|
|
Write-Log "User $($item.Schema) already exists"
|
|
|
|
# Get current object count
|
|
$beforeCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Password $SystemPassword -SchemaName $item.Schema
|
|
|
|
$beforeTotal = if ($beforeCounts['TOTAL']) { $beforeCounts['TOTAL'] } else { 0 }
|
|
Write-Log "Existing objects: $beforeTotal"
|
|
}
|
|
else {
|
|
# Create user
|
|
Write-Log "Creating user $($item.Schema)..."
|
|
|
|
$createUserSql = @"
|
|
SET ECHO OFF FEEDBACK ON VERIFY OFF
|
|
|
|
CREATE USER $($item.Schema)
|
|
IDENTIFIED BY "$($item.Password)"
|
|
DEFAULT TABLESPACE ROA
|
|
TEMPORARY TABLESPACE TEMP
|
|
PROFILE DEFAULT;
|
|
|
|
GRANT CONNECT TO $($item.Schema);
|
|
GRANT RESOURCE TO $($item.Schema);
|
|
GRANT CREATE MATERIALIZED VIEW TO $($item.Schema);
|
|
GRANT CREATE PROCEDURE TO $($item.Schema);
|
|
GRANT CREATE SEQUENCE TO $($item.Schema);
|
|
GRANT CREATE TABLE TO $($item.Schema);
|
|
GRANT CREATE TRIGGER TO $($item.Schema);
|
|
GRANT CREATE VIEW TO $($item.Schema);
|
|
GRANT DEBUG CONNECT SESSION TO $($item.Schema);
|
|
GRANT SELECT ANY TABLE TO $($item.Schema);
|
|
GRANT CREATE TYPE TO $($item.Schema);
|
|
GRANT UNLIMITED TABLESPACE TO $($item.Schema);
|
|
|
|
COMMIT;
|
|
SELECT 'USER_CREATED' FROM dual;
|
|
EXIT;
|
|
"@
|
|
|
|
$createResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $createUserSql -Silent
|
|
|
|
if ($createResult -match "USER_CREATED") {
|
|
Write-LogSuccess "User $($item.Schema) created"
|
|
}
|
|
else {
|
|
throw "Failed to create user $($item.Schema)"
|
|
}
|
|
}
|
|
|
|
# Copy dump file to DMPDIR if not already there
|
|
$dumpFileName = Split-Path -Path $item.DumpFile -Leaf
|
|
$targetDumpPath = Join-Path $dmpDir $dumpFileName
|
|
|
|
if ($item.DumpFile -ne $targetDumpPath) {
|
|
Write-Log "Copying dump file to DMPDIR..."
|
|
Copy-Item -Path $item.DumpFile -Destination $targetDumpPath -Force
|
|
}
|
|
|
|
# Run import
|
|
Write-Log "Running impdp for $($item.Schema)..."
|
|
|
|
$impdpPath = Join-Path $oraHome "bin\impdp.exe"
|
|
$importLogFile = "$($item.Schema)_import_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
|
|
|
|
# Use EZConnect format for impdp (handles listener bound to specific IP)
|
|
$dbHost = Get-ListenerHost -OracleHome $oraHome
|
|
$connString = "SYSTEM/`"$SystemPassword`"@${dbHost}:1521/$ServiceName"
|
|
|
|
# Check if dump has different schema name (remapping needed)
|
|
$remapParam = ""
|
|
$dumpBaseName = [System.IO.Path]::GetFileNameWithoutExtension($dumpFileName).ToUpper()
|
|
if ($dumpBaseName -ne $item.Schema) {
|
|
$remapParam = "remap_schema=$dumpBaseName`:$($item.Schema)"
|
|
Write-Log "Schema remap: $dumpBaseName -> $($item.Schema)"
|
|
}
|
|
|
|
$impdpParams = @(
|
|
"`"$connString`"",
|
|
"directory=DMPDIR",
|
|
"dumpfile=$dumpFileName",
|
|
"logfile=$importLogFile",
|
|
"schemas=$($item.Schema)",
|
|
"table_exists_action=REPLACE",
|
|
"parallel=1" # Required for Oracle XE
|
|
)
|
|
|
|
if ($remapParam) {
|
|
$impdpParams += $remapParam
|
|
}
|
|
|
|
$arguments = $impdpParams -join " "
|
|
|
|
# Set Oracle environment
|
|
$env:ORACLE_HOME = $oraHome
|
|
$env:PATH = "$oraHome\bin;$env:PATH"
|
|
$env:NLS_LANG = "AMERICAN_AMERICA.AL32UTF8"
|
|
|
|
$importStart = Get-Date
|
|
$process = Start-Process -FilePath $impdpPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru
|
|
|
|
$importDuration = (Get-Date) - $importStart
|
|
Write-Log "Import completed in $([math]::Round($importDuration.TotalMinutes, 2)) minutes"
|
|
|
|
# Verify import
|
|
$afterCounts = Get-SchemaObjectCount -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Password $SystemPassword -SchemaName $item.Schema
|
|
|
|
$afterTotal = if ($afterCounts['TOTAL']) { $afterCounts['TOTAL'] } else { 0 }
|
|
$afterInvalid = if ($afterCounts['INVALID']) { $afterCounts['INVALID'] } else { 0 }
|
|
|
|
Write-Log "Objects after import: $afterTotal"
|
|
|
|
if ($afterInvalid -gt 0) {
|
|
Write-LogWarning "Invalid objects: $afterInvalid"
|
|
|
|
# Recompile
|
|
$recompileSql = @"
|
|
BEGIN
|
|
DBMS_UTILITY.COMPILE_SCHEMA('$($item.Schema)', FALSE);
|
|
END;
|
|
/
|
|
EXIT;
|
|
"@
|
|
Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName `
|
|
-Username "SYSTEM" -Password $SystemPassword -SqlCommand $recompileSql -Silent
|
|
}
|
|
|
|
if ($process.ExitCode -eq 0) {
|
|
Write-LogSuccess "$($item.Schema) imported successfully ($afterTotal objects)"
|
|
$successCount++
|
|
}
|
|
else {
|
|
Write-LogWarning "$($item.Schema) import completed with warnings"
|
|
$successCount++
|
|
}
|
|
|
|
# Clean up copied dump file
|
|
if ($item.DumpFile -ne $targetDumpPath -and (Test-Path -Path $targetDumpPath)) {
|
|
Remove-Item -Path $targetDumpPath -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
catch {
|
|
Write-LogError "Failed to import $($item.Schema): $_"
|
|
$failCount++
|
|
}
|
|
}
|
|
|
|
$totalDuration = (Get-Date) - $startTime
|
|
|
|
# Summary
|
|
Write-LogSection "Import Complete"
|
|
|
|
Write-LogSuccess "Company import batch completed!"
|
|
Write-Log ""
|
|
Write-Log "Summary:"
|
|
Write-Log " Total companies: $($importList.Count)"
|
|
Write-Log " Successful: $successCount"
|
|
Write-Log " Failed: $failCount"
|
|
Write-Log " Total duration: $([math]::Round($totalDuration.TotalMinutes, 2)) minutes"
|
|
Write-Log ""
|
|
|
|
if ($notFoundList.Count -gt 0) {
|
|
Write-Log "Companies skipped (no dump file):"
|
|
foreach ($schema in $notFoundList) {
|
|
Write-Log " - $schema"
|
|
}
|
|
}
|
|
|
|
Write-Log ""
|
|
Write-Log "Next steps:"
|
|
Write-Log " 1. Run 07-verify-installation.ps1 to verify all schemas"
|
|
|
|
Close-LogFile -Success ($failCount -eq 0)
|
|
exit $(if ($failCount -eq 0) { 0 } else { 1 })
|
|
}
|
|
catch {
|
|
Write-LogError "Import failed: $_"
|
|
Write-LogError $_.ScriptStackTrace
|
|
Close-LogFile -Success $false
|
|
exit 1
|
|
}
|