Skip to content

Instantly share code, notes, and snippets.

@check5004
Last active July 2, 2025 01:32
Show Gist options
  • Save check5004/6f077f901bb7facb686701a582319fd3 to your computer and use it in GitHub Desktop.
Save check5004/6f077f901bb7facb686701a582319fd3 to your computer and use it in GitHub Desktop.
run-stored-procedure.ps1 は、Oracleデータベースに接続し、ストアドプロシージャやパッケージなどのSQLファイルを簡単にコンパイル・実行するためのPowerShellスクリプトです。

run-stored-procedure.ps1 利用ガイド

Gist: run-stored-procedure.ps1

1. 概要

run-stored-procedure.ps1 は、Oracleデータベースに接続し、ストアドプロシージャやパッケージなどのSQLファイルを簡単にコンパイル・実行するためのPowerShellスクリプトです。

主な機能

  • 対話的なSQLファイル選択: 実行履歴からファイルを選んだり、パスを直接入力して実行できます。
  • ドラッグ&ドロップによるパス入力: VSCodeのターミナルにSQLファイルをドラッグ&ドロップするだけで、簡単にファイルパスを指定できます。
  • 詳細なエラーレポート: パッケージのコンパイルエラー発生時に、user_errorsビューを自動で検索し、エラー箇所(行番号、内容)を特定して表示します。
  • 多重起動防止: スクリプトの誤った二重実行を防ぎます。
  • 実行ログの保存: いつ、どのファイルを実行したかのログが自動で保存されます。

2. 入手と配置

  1. スクリプトのダウンロード 以下のリンクから run-stored-procedure.ps1 ファイルをダウンロードします。

    run-stored-procedure.ps1 をダウンロード

  2. スクリプトの配置 ダウンロードした run-stored-procedure.ps1 を、プロジェクトのルートディレクトリに配置してください。

3. 前提条件と初回設定

前提条件

  • Windows PowerShell が利用できること。
  • Oracle Client がインストールされており、コマンドプロンプトやPowerShellから sqlplus コマンドが実行できること。(環境変数の PATH 設定が必要です)

初回設定

スクリプトを初めて使用する前に、お使いのデータベース環境に合わせて接続情報を設定する必要があります。

  1. run-stored-procedure.ps1 ファイルをエディタで開きます。
  2. ファイル冒頭にある $OracleConfig のセクションを、ご自身の環境に合わせて編集・保存してください。
# =============================================================================
# 【接続情報設定】※ここを環境に合わせて変更してください
# =============================================================================
$OracleConfig = @{
    # データベース接続情報
    Host        = "your_oracle_host_ip" # Oracleサーバーのホスト名またはIP
    Port        = "1521"                # ポート番号(通常は1521)
    SID         = "your_sid_or_service" # SID名またはサービス名
    Username    = "your_username"       # ユーザー名
    Password    = "your_password"       # パスワード

    # 接続文字列のタイプ(SID または SERVICE_NAME)
    ConnectionType = "SERVICE_NAME"     # "SID" または "SERVICE_NAME"
}

4. VSCodeでの実行手順

VSCodeの統合ターミナルを使用すると、ファイルのドラッグ&ドロップでパスを入力できるため、非常に効率的に作業を進められます。

graph TD
    A["VSCodeでプロジェクトを開く"] --> B["ターミナルを開く"];
    B --> C["スクリプト実行コマンドを入力"];
    C --> D["SQLファイルを<br>ターミナルへドラッグ&ドロップ"];
    D --> E["ファイルパスが自動入力される"];
    E --> F["Enterキーで実行"];
    F --> G{"実行を確認"};
    G --> H["実行結果が表示される"];
Loading

【推奨】ドラッグ&ドロップでSQLファイルを指定して実行

この方法が最も簡単で確実です。

  1. VSCodeでプロジェクトのルートフォルダを開きます。
  2. Ctrl + @ キーを押して、画面下部にターミナルを開きます。
  3. ターミナルに、以下のようにコマンドの先頭部分を入力します。(末尾にスペースを空けるのがポイントです)
    ./run-stored-procedure.ps1 -SqlFile
  4. VSCodeの左側にあるエクスプローラーパネルから、実行したいSQLファイル(例: db/package/your_package.sql)を探し、ターミナルパネル上へドラッグ&ドロップします。
  5. ファイルのフルパスが自動で入力されるので、そのまま Enter キーを押します。
    ./run-stored-procedure.ps1 -SqlFile 'C:\path\to\your\project\db\package\your_package.sql'
  6. 実行しますか? (y/N) という確認メッセージが表示されたら、y を入力して Enter キーを押すと、スクリプトが実行されます。

スクリプトを直接実行(対話モード)

  1. ターミナルで以下のコマンドを実行します。
    ./run-stored-procedure.ps1
  2. 最近使用したファイルの履歴リストが表示されます。
    • リストにあるファイルを実行する場合は、対応する番号を入力して Enter
    • リストにないファイルを実行する場合は、SQLファイルのパスを直接入力(または貼り付け)して Enter

5. 実行結果の見方

  • 成功時: ✓ SQLファイルの実行が完了しました! というメッセージが緑色で表示されます。
  • エラー時: ✗ SQLファイルの実行中にエラーが発生しました というメッセージが赤色で表示されます。エラーが発生した場合は、その直前のログに ORA-PLS- から始まるOracleのエラー詳細が出力されるので、原因の特定に役立ちます。

6. その他の機能とオプション

  • 確認プロンプトを省略: -NoConfirm スイッチを付けて実行すると、実行前の確認がスキップされます。
    ./run-stored-procedure.ps1 -SqlFile <path_to_your_sql_file> -NoConfirm
  • ログファイルの場所: スクリプトの実行ログは、C:\Users\<ユーザー名>\.myscript\oracle-...\logs フォルダ内に保存されます。詳細な調査が必要な場合に参照してください。

7. トラブルシューティング

  • 'sqlplus' は、コマンドレット、...として認識されません:
    • 原因: Oracle Clientがインストールされていないか、sqlplus.exe への環境変数 PATH が設定されていません。
    • 対策: Oracle Clientをインストールし、環境変数 PATHsqlplus.exe が存在するディレクトリを追加してください。
  • データベース接続テスト失敗:
    • 原因: run-stored-procedure.ps1 内の $OracleConfig の設定が誤っている可能性があります。
    • 対策: ホスト名、ポート、SID/サービス名、ユーザー名、パスワードが正しいか確認してください。
# =============================================================================
# Oracle ストアドプロシージャ実行スクリプト
# =============================================================================
# 使用方法:
# .\run-stored-procedure.ps1
# .\run-stored-procedure.ps1 -SqlFile "db/package/PKG_COMMON.sql"
# .\run-stored-procedure.ps1 -SqlFile "db/package/PKG_COMMON.sql" -NoConfirm
# =============================================================================
param(
[string]$SqlFile = "",
[switch]$NoConfirm = $false,
[switch]$TestConnection = $false
)
# =============================================================================
# 【接続情報設定】※ここを環境に合わせて変更してください
# =============================================================================
$OracleConfig = @{
# データベース接続情報
Host = "10.000.000.000" # Oracleサーバーのホスト名またはIP
Port = "1521" # ポート番号(通常は1521)
SID = "orcl" # SID名またはサービス名
Username = "USERNAME" # ユーザー名
Password = "PASSWORD" # パスワード
# 接続文字列のタイプ(SID または SERVICE_NAME)
ConnectionType = "SERVICE_NAME" # "SID" または "SERVICE_NAME"
}
# =============================================================================
# 関数定義
# =============================================================================
function Get-UserDataPath {
param([hashtable]$Config)
# 接続情報からフォルダ名を生成(特殊文字をサニタイズ)
$hostPart = $Config.Host
$portPart = $Config.Port
$sidPart = $Config.SID -replace '[\\/:*?"<>|]', '-'
$folderName = "oracle-${hostPart}-${portPart}-${sidPart}"
$userDataDir = "$env:USERPROFILE\.myscript\$folderName"
return $userDataDir
}
# ユーザーデータディレクトリの取得
$UserDataDir = Get-UserDataPath -Config $OracleConfig
# ロックファイルの設定
$LockFile = "$UserDataDir\run-stored-procedure.lock"
# クリーンアップ状態管理
$script:CleanupExecuted = $false
$script:CancelHandler = $null
$script:ExitHandler = $null
# 多重起動チェック関数
function Test-ScriptLock {
param([string]$LockFilePath)
if (Test-Path $LockFilePath) {
try {
$lockInfo = Get-Content $LockFilePath | ConvertFrom-Json
$lockPid = $lockInfo.ProcessId
$lockTime = [DateTime]::Parse($lockInfo.StartTime)
# プロセスが実行中か確認
$process = Get-Process -Id $lockPid -ErrorAction SilentlyContinue
if ($process) {
$timeDiff = (Get-Date) - $lockTime
Write-Host "`n警告: スクリプトは既に実行中です" -ForegroundColor Yellow
Write-Host " プロセスID: $lockPid" -ForegroundColor Yellow
Write-Host " 開始時刻: $($lockTime.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Yellow
Write-Host " 経過時間: $($timeDiff.TotalMinutes.ToString('0.0'))分" -ForegroundColor Yellow
Write-Host ""
Write-Host "選択してください:" -ForegroundColor Cyan
Write-Host " [1] 終了する(デフォルト)" -ForegroundColor White
Write-Host " [2] ロックを強制解除して続行する" -ForegroundColor White
$choice = Read-Host "選択 (1-2)"
switch ($choice) {
"2" {
Write-Host "ロックを強制解除します..." -ForegroundColor Yellow
return "locked_force"
}
default {
Write-Host "終了します。" -ForegroundColor Yellow
return "locked_exit"
}
}
}
else {
# プロセスが存在しない場合、古いロックファイルを削除
Remove-Item $LockFilePath -Force
return "unlocked"
}
}
catch {
# ロックファイルが壊れている場合は削除
Remove-Item $LockFilePath -Force -ErrorAction SilentlyContinue
return "unlocked"
}
}
return "unlocked"
}
# ロックファイル作成関数
function New-ScriptLock {
param([string]$LockFilePath)
$lockInfo = @{
ProcessId = $PID
StartTime = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss")
ScriptPath = $PSCommandPath
}
$lockInfo | ConvertTo-Json | Out-File -FilePath $LockFilePath -Encoding UTF8 -Force
}
# ロックファイル削除関数
function Remove-ScriptLock {
param([string]$LockFilePath)
if (Test-Path $LockFilePath) {
Remove-Item $LockFilePath -Force -ErrorAction SilentlyContinue
}
}
# 包括的なクリーンアップ関数
function Invoke-ScriptCleanup {
param([string]$Reason = "不明")
# 重複実行防止
if ($script:CleanupExecuted) {
return
}
$script:CleanupExecuted = $true
Write-Host "`nクリーンアップ実行中... (理由: $Reason)" -ForegroundColor Yellow
# ログバッファをフラッシュ
try {
Flush-LogBuffer
}
catch {
# エラーは無視
}
# ロックファイル削除
try {
Remove-ScriptLock $LockFile
Write-Host "ロックファイルを削除しました" -ForegroundColor Green
}
catch {
Write-Host "ロックファイル削除に失敗: $($_.Exception.Message)" -ForegroundColor Red
}
# イベントハンドラーの解除
try {
if ($script:CancelHandler) {
[Console]::CancelKeyPress -= $script:CancelHandler
}
if ($script:ExitHandler) {
Unregister-Event -SourceIdentifier "ScriptExit" -ErrorAction SilentlyContinue
}
}
catch {
# エラーは無視
}
}
# Ctrl+C および終了イベントハンドラーの登録
function Register-ExitHandlers {
try {
# Ctrl+C ハンドラー
$script:CancelHandler = {
param($sender, $e)
Invoke-ScriptCleanup -Reason "Ctrl+C"
$e.Cancel = $false # プロセス終了を許可
}
[Console]::CancelKeyPress += $script:CancelHandler
# PowerShell終了ハンドラー
$script:ExitHandler = Register-EngineEvent -SourceIdentifier "PowerShell.Exiting" -Action {
Invoke-ScriptCleanup -Reason "PowerShell終了"
}
Write-Log "終了イベントハンドラーを登録しました"
}
catch {
Write-Log "終了イベントハンドラーの登録に失敗しました: $($_.Exception.Message)" "WARN"
}
}
# ログ設定
$LogDir = "$UserDataDir\logs"
$ProcessId = [System.Diagnostics.Process]::GetCurrentProcess().Id
$Timestamp = Get-Date -Format 'yyyyMMdd_HHmmss_fff'
$LogFile = "$LogDir\run-stored-procedure_${Timestamp}_${ProcessId}.log"
# 履歴設定
$HistoryFile = "$UserDataDir\sql-history.json"
# ログバッファ設定
$script:LogBuffer = New-Object System.Collections.ArrayList
$script:LogBufferSize = 50 # バッファサイズ(行数)
$script:LastFlushTime = Get-Date
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO",
[switch]$NoBuffer = $false
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] [$Level] $Message"
Write-Host $logMessage
# ログメッセージをバッファに追加
[void]$script:LogBuffer.Add($logMessage)
# バッファフラッシュ条件をチェック
$shouldFlush = $false
# 条件1: バッファサイズを超えた
if ($script:LogBuffer.Count -ge $script:LogBufferSize) {
$shouldFlush = $true
}
# 条件2: 重要なログ(ERROR, WARN)または明示的なフラッシュ要求
if ($Level -eq "ERROR" -or $Level -eq "WARN" -or $NoBuffer) {
$shouldFlush = $true
}
# 条件3: 前回のフラッシュから5秒以上経過
if (((Get-Date) - $script:LastFlushTime).TotalSeconds -ge 5) {
$shouldFlush = $true
}
# バッファをフラッシュ
if ($shouldFlush -and $script:LogBuffer.Count -gt 0) {
Flush-LogBuffer
}
}
function Flush-LogBuffer {
if ($script:LogBuffer.Count -eq 0) {
return
}
# ログファイルへの書き込み(排他制御付き)
if (Test-Path $LogDir) {
$retryCount = 0
$maxRetries = 3
do {
try {
# バッファの内容を一度に書き込む
$script:LogBuffer | Out-File -FilePath $LogFile -Append -Encoding UTF8
$script:LogBuffer.Clear()
$script:LastFlushTime = Get-Date
break
}
catch {
$retryCount++
if ($retryCount -ge $maxRetries) {
Write-Host "警告: ログファイルへの書き込みに失敗しました: $($_.Exception.Message)" -ForegroundColor Yellow
$script:LogBuffer.Clear() # 失敗してもバッファをクリア
break
}
Start-Sleep -Milliseconds (100 * $retryCount)
}
} while ($retryCount -lt $maxRetries)
}
}
function Test-SqlPlus {
try {
$sqlplusVersion = & sqlplus -v 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log "SQL*Plus が見つかりました"
return $true
}
}
catch {
Write-Log "SQL*Plus が見つかりません。Oracleクライアントがインストールされていることを確認してください。" "ERROR"
return $false
}
return $false
}
function Get-ConnectionString {
if ($OracleConfig.ConnectionType -eq "SERVICE_NAME") {
return "$($OracleConfig.Username)/$($OracleConfig.Password)@$($OracleConfig.Host):$($OracleConfig.Port)/$($OracleConfig.SID)"
}
else {
return "$($OracleConfig.Username)/$($OracleConfig.Password)@$($OracleConfig.Host):$($OracleConfig.Port):$($OracleConfig.SID)"
}
}
function Test-OracleConnection {
Write-Log "データベース接続をテストしています..."
$connectionString = Get-ConnectionString
$testSql = @"
whenever sqlerror exit sql.sqlcode
set pagesize 0
set feedback off
set heading off
select 'CONNECTION_OK' from dual;
exit;
"@
$tempFile = [System.IO.Path]::GetTempFileName()
$testSql | Out-File -FilePath $tempFile -Encoding ASCII
try {
$result = & sqlplus -s $connectionString "@$tempFile" 2>&1
if ($result -like "*CONNECTION_OK*" -and $LASTEXITCODE -eq 0) {
Write-Log "データベース接続テスト成功" "SUCCESS"
return $true
}
else {
Write-Log "データベース接続テスト失敗: $result" "ERROR"
return $false
}
}
catch {
Write-Log "データベース接続テスト中にエラーが発生しました: $($_.Exception.Message)" "ERROR"
return $false
}
finally {
if (Test-Path $tempFile) {
Remove-Item $tempFile -Force
}
}
}
function Get-SqlHistory {
if (!(Test-Path $HistoryFile)) {
return @()
}
try {
$fileContent = Get-Content $HistoryFile -Raw
if ([string]::IsNullOrWhiteSpace($fileContent)) {
return @()
}
$historyData = $fileContent | ConvertFrom-Json
$rawHistory = $historyData.recentFiles
# 履歴を正規化(絶対パスに変換し、重複を削除)
$normalizedHistory = @()
$seenPaths = @{}
foreach ($item in $rawHistory) {
try {
$absolutePath = [System.IO.Path]::GetFullPath($item.path)
# 重複チェック(絶対パスで)
if (-not $seenPaths.ContainsKey($absolutePath)) {
$seenPaths[$absolutePath] = $true
$normalizedHistory += @{
path = $absolutePath
lastUsed = $item.lastUsed
}
}
}
catch {
# パス変換に失敗した場合は元のパスをそのまま使用
if (-not $seenPaths.ContainsKey($item.path)) {
$seenPaths[$item.path] = $true
$normalizedHistory += $item
}
}
}
# 正規化した履歴が元の履歴と異なる場合は保存
if ($normalizedHistory.Count -ne $rawHistory.Count -or
($normalizedHistory | ConvertTo-Json -Depth 2) -ne ($rawHistory | ConvertTo-Json -Depth 2)) {
$normalizedHistoryData = @{ recentFiles = $normalizedHistory }
try {
$normalizedHistoryData | ConvertTo-Json -Depth 2 | Out-File -FilePath $HistoryFile -Encoding UTF8 -Force
Write-Log "履歴を正規化しました(重複削除・絶対パス変換)"
}
catch {
Write-Log "履歴の正規化保存に失敗しました: $($_.Exception.Message)" "WARN"
}
}
return $normalizedHistory
}
catch {
Write-Log "履歴ファイルの読み込みに失敗しました: $($_.Exception.Message)" "WARN"
try {
Remove-Item $HistoryFile -Force
}
catch {
# ファイル削除失敗は無視
}
return @()
}
}
function Add-SqlHistory {
param([string]$FilePath)
# ユーザーデータディレクトリの作成
if (!(Test-Path $UserDataDir)) {
New-Item -ItemType Directory -Path $UserDataDir -Force | Out-Null
}
# 現在の履歴を取得
$history = Get-SqlHistory
# パスを絶対パスに変換
try {
$absolutePath = [System.IO.Path]::GetFullPath($FilePath)
}
catch {
Write-Log "パスの絶対パス変換に失敗しました: $FilePath - $($_.Exception.Message)" "WARN"
$absolutePath = $FilePath
}
# 新しいエントリを作成
$newEntry = @{
path = $absolutePath
lastUsed = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss")
}
# 既存のエントリを削除(重複回避)- 絶対パスで比較
$history = $history | Where-Object {
try {
$existingAbsolutePath = [System.IO.Path]::GetFullPath($_.path)
$existingAbsolutePath -ne $absolutePath
}
catch {
# パス変換に失敗した場合は文字列比較
$_.path -ne $absolutePath
}
}
# 新しいエントリを先頭に追加
$history = @($newEntry) + $history
# 最大10件まで保持
if ($history.Count -gt 10) {
$history = $history[0..9]
}
# 履歴を保存
$historyData = @{ recentFiles = $history }
try {
$historyData | ConvertTo-Json -Depth 2 | Out-File -FilePath $HistoryFile -Encoding UTF8 -Force
Write-Log "履歴を更新しました: $absolutePath"
}
catch {
Write-Log "履歴の保存に失敗しました: $($_.Exception.Message)" "WARN"
}
}
function Select-SqlFile {
Write-Host "`nSQLファイルの選択:" -ForegroundColor Yellow
Write-Host "===========================================" -ForegroundColor Yellow
# 履歴を取得
$history = Get-SqlHistory
# 履歴が配列でない場合の対処
if ($history -and $history -isnot [array]) {
$history = @($history)
}
if ($history -and $history.Count -gt 0) {
Write-Host "最近使用したSQLファイル:" -ForegroundColor Green
for ($i = 0; $i -lt $history.Count; $i++) {
$lastUsed = [DateTime]::Parse($history[$i].lastUsed).ToString("MM-dd HH:mm")
Write-Host " [$($i + 1)] $($history[$i].path) ($lastUsed)" -ForegroundColor White
}
Write-Host ""
}
else {
Write-Host "履歴がありません。SQLファイルのパスを直接入力してください。" -ForegroundColor Cyan
Write-Host ""
}
# ユーザー入力
do {
if ($history -and $history.Count -gt 0) {
$maxCount = [Math]::Min($history.Count, 10)
$input = Read-Host "番号(1-$maxCount)またはファイルパスを入力"
}
else {
$input = Read-Host "SQLファイルのパスを入力"
}
if ([string]::IsNullOrWhiteSpace($input)) {
Write-Host "入力が空です。再度入力してください。" -ForegroundColor Red
continue
}
# 数字かどうか判定
if ($input -match '^\d+$' -and $history -and $history.Count -gt 0) {
$index = [int]$input - 1
$maxCount = [Math]::Min($history.Count, 10)
if ($index -ge 0 -and $index -lt $maxCount) {
$selectedPath = $history[$index].path
# 番号選択時も履歴を更新(最新順維持のため)
Add-SqlHistory -FilePath $selectedPath
return $selectedPath
}
else {
Write-Host "無効な番号です。1-$maxCount の範囲で入力してください。" -ForegroundColor Red
continue
}
}
else {
# パスとして扱う
return $input.Trim()
}
} while ($true)
}
function Get-OracleErrors {
param(
[string]$ConnectionString,
[string]$SqlFilePath
)
Write-Log "実行したSQLファイルのエラー詳細を取得しています..."
# SQLファイルからオブジェクト名を抽出
$objectNames = @()
if (Test-Path $SqlFilePath) {
try {
$sqlContent = Get-Content $SqlFilePath -Raw
# パッケージ、プロシージャ、ファンクション名を抽出
$patterns = @(
'CREATE\s+OR\s+REPLACE\s+PACKAGE\s+BODY\s+([A-Za-z_][A-Za-z0-9_]*)',
'CREATE\s+OR\s+REPLACE\s+PACKAGE\s+([A-Za-z_][A-Za-z0-9_]*)',
'CREATE\s+OR\s+REPLACE\s+PROCEDURE\s+([A-Za-z_][A-Za-z0-9_]*)',
'CREATE\s+OR\s+REPLACE\s+FUNCTION\s+([A-Za-z_][A-Za-z0-9_]*)'
)
foreach ($pattern in $patterns) {
$matches = [regex]::Matches($sqlContent, $pattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
foreach ($match in $matches) {
$objectName = $match.Groups[1].Value.ToUpper()
if ($objectNames -notcontains $objectName) {
$objectNames += $objectName
}
}
}
}
catch {
Write-Log "SQLファイルの解析に失敗: $($_.Exception.Message)" "WARN"
}
}
if ($objectNames.Count -eq 0) {
Write-Log "実行したSQLファイルからオブジェクト名を特定できませんでした" "WARN"
return
}
# 特定されたオブジェクトのエラーのみを取得
$objectNamesList = "'" + ($objectNames -join "','") + "'"
$errorSql = @"
whenever sqlerror continue
set pagesize 0
set feedback off
set heading off
set linesize 1000
select 'COMPILATION_ERRORS_FOR_EXECUTED_OBJECTS:' from dual;
select name||' ('||type||') Line:'||line||' Position:'||position||' - '||text as error_detail
from user_errors
where name in ($objectNamesList)
order by name, sequence;
exit;
"@
$tempFile = [System.IO.Path]::GetTempFileName()
$errorSql | Out-File -FilePath $tempFile -Encoding ASCII
try {
$errorResult = & sqlplus -s $ConnectionString "@$tempFile" 2>&1
if ($errorResult) {
$hasErrors = $false
Write-Log "=== 実行したオブジェクトのコンパイルエラー ===" "ERROR"
$errorResult | ForEach-Object {
$line = $_.ToString().Trim()
if ($line -ne "" -and $line -ne "COMPILATION_ERRORS_FOR_EXECUTED_OBJECTS:") {
$hasErrors = $true
Write-Log $line "ERROR"
}
}
if (-not $hasErrors) {
Write-Log "実行したオブジェクト ($($objectNames -join ', ')) にコンパイルエラーはありません"
}
Write-Log "=============================================" "ERROR"
}
}
catch {
Write-Log "エラー詳細取得中に例外が発生しました: $($_.Exception.Message)" "ERROR"
}
finally {
if (Test-Path $tempFile) {
Remove-Item $tempFile -Force
}
}
}
function Execute-SqlFile {
param([string]$FilePath)
Write-Log "SQLファイルを実行しています: $FilePath"
$connectionString = Get-ConnectionString
# オプション: SQL実行ログを別ディレクトリに保存する場合
# $sqlLogDir = "$LogDir\sql-execution"
# if (!(Test-Path $sqlLogDir)) {
# New-Item -ItemType Directory -Path $sqlLogDir -Force | Out-Null
# }
# $sqlLogFile = "$sqlLogDir\sql-execution_${Timestamp}_${ProcessId}.log"
# sqlplusのスクリプトを作成(spoolを削除してメインログに統合)
$sqlScript = @"
whenever sqlerror continue
set serveroutput on size unlimited
set feedback on
set echo on
set linesize 1000
set pagesize 0
@$FilePath
show errors;
exit;
"@
# オプション: SQL実行ログを別ファイルに出力する場合は以下を有効化
# $sqlScript = @"
# whenever sqlerror continue
# set serveroutput on size unlimited
# set feedback on
# set echo on
# set linesize 1000
# set pagesize 0
# spool $sqlLogFile
# @$FilePath
# spool off
# show errors;
# "@
$tempFile = [System.IO.Path]::GetTempFileName()
$sqlScript | Out-File -FilePath $tempFile -Encoding ASCII
try {
Write-Log "sqlplusを実行中..."
$result = & sqlplus $connectionString "@$tempFile" 2>&1
# 実行結果をログに記録
Write-Log "=== SQL実行結果 ==="
$result | ForEach-Object {
if ($_ -and $_.ToString().Trim() -ne "") {
Write-Log $_
}
}
Write-Log "=================="
# エラーが発生した場合の詳細取得
if ($LASTEXITCODE -ne 0 -or ($result -join "`n") -match "(ORA-|PLS-|SP2-)") {
Write-Log "エラーが検出されました。詳細を取得中..." "ERROR"
Get-OracleErrors -ConnectionString $connectionString -SqlFilePath $FilePath
Write-Log "SQLファイルの実行中にエラーが発生しました (終了コード: $LASTEXITCODE)" "ERROR"
return $false
}
else {
Write-Log "SQLファイルの実行が完了しました" "SUCCESS"
return $true
}
}
catch {
Write-Log "SQLファイル実行中に例外が発生しました: $($_.Exception.Message)" "ERROR"
return $false
}
finally {
if (Test-Path $tempFile) {
Remove-Item $tempFile -Force
}
}
}
# =============================================================================
# メイン処理
# =============================================================================
Write-Host "==============================================================================" -ForegroundColor Cyan
Write-Host "Oracle ストアドプロシージャ実行スクリプト" -ForegroundColor Cyan
Write-Host "==============================================================================" -ForegroundColor Cyan
# ユーザーデータディレクトリとログディレクトリ作成
if (!(Test-Path $UserDataDir)) {
New-Item -ItemType Directory -Path $UserDataDir -Force | Out-Null
}
if (!(Test-Path $LogDir)) {
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
}
Write-Log "スクリプト開始"
# スクリプト終了時の処理を確実に行うためのtrap設定
trap {
Write-Log "スクリプトが異常終了しました: $($_.Exception.Message)" "ERROR"
Invoke-ScriptCleanup -Reason "異常終了"
exit 1
}
# SQLファイルの決定
if ([string]::IsNullOrEmpty($SqlFile)) {
Write-Log "SQLファイルが指定されていません。ファイルを選択してください。"
$SqlFile = Select-SqlFile
if ([string]::IsNullOrEmpty($SqlFile)) {
Write-Log "SQLファイルが選択されませんでした。" "ERROR"
exit 1
}
}
# SQLファイルの存在確認
if (!(Test-Path $SqlFile)) {
Write-Log "SQLファイルが見つかりません: $SqlFile" "ERROR"
exit 1
}
Write-Log "使用するSQLファイル: $SqlFile"
# 履歴に追加(SQL実行結果に関係なくパス確定時点で保存)
Add-SqlHistory -FilePath $SqlFile
# 多重起動チェック(SQLファイル確定後に実行)
$lockResult = Test-ScriptLock $LockFile
switch ($lockResult) {
"unlocked" {
# ロックファイル作成
try {
New-ScriptLock $LockFile
Write-Log "ロックファイルを作成しました: $LockFile"
# 終了イベントハンドラーを登録
Register-ExitHandlers
}
catch {
Write-Log "ロックファイルの作成に失敗しました: $($_.Exception.Message)" "ERROR"
exit 1
}
}
"locked_exit" {
Write-Log "ユーザーによりキャンセルされました" "WARN"
exit 0
}
"locked_force" {
Write-Log "ロックを強制解除してスクリプトを続行します" "WARN"
Remove-ScriptLock $LockFile
try {
New-ScriptLock $LockFile
Write-Log "新しいロックファイルを作成しました: $LockFile"
# 終了イベントハンドラーを登録
Register-ExitHandlers
}
catch {
Write-Log "ロックファイルの作成に失敗しました: $($_.Exception.Message)" "ERROR"
exit 1
}
}
}
# 設定情報の表示
Write-Host "`n接続情報:" -ForegroundColor Yellow
Write-Host " ホスト: $($OracleConfig.Host):$($OracleConfig.Port)" -ForegroundColor White
Write-Host " $($OracleConfig.ConnectionType): $($OracleConfig.SID)" -ForegroundColor White
Write-Host " ユーザー: $($OracleConfig.Username)" -ForegroundColor White
Write-Host " SQLファイル: $SqlFile" -ForegroundColor White
# SQL*Plusの確認
if (!(Test-SqlPlus)) {
exit 1
}
# 接続テスト(オプション)
if ($TestConnection) {
if (!(Test-OracleConnection)) {
Write-Log "接続テストに失敗しました。設定を確認してください。" "ERROR"
exit 1
}
}
# 実行確認
if (!$NoConfirm) {
Write-Host "`n実行確認:" -ForegroundColor Yellow
$confirm = Read-Host "上記の設定でSQLファイルを実行しますか? (y/N)"
if ($confirm -ne "y" -and $confirm -ne "Y") {
Write-Log "ユーザーによりキャンセルされました"
Invoke-ScriptCleanup -Reason "ユーザーキャンセル"
exit 0
}
}
# SQLファイル実行
Write-Host "`nSQLファイルを実行中..." -ForegroundColor Green
$success = Execute-SqlFile -FilePath $SqlFile
if ($success) {
Write-Host "`n✓ SQLファイルの実行が完了しました!" -ForegroundColor Green
Write-Log "スクリプト正常終了"
# 正常終了時のクリーンアップ
Invoke-ScriptCleanup -Reason "正常終了"
}
else {
Write-Host "`n✗ SQLファイルの実行中にエラーが発生しました" -ForegroundColor Red
Write-Log "スクリプト異常終了" "ERROR"
# 異常終了時のクリーンアップ
Invoke-ScriptCleanup -Reason "SQL実行エラー"
exit 1
}
Write-Host "`nログファイル: $LogFile" -ForegroundColor Cyan
Write-Host "==============================================================================" -ForegroundColor Cyan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment