【テクニカル・上級編】DAO.QueryDefのSQLプロパティを直接書き換える際の「コンパイルエラー」を未然に防ぐ静的チェック手法 – Access VBA解析バイブル

スポンサーリンク

聖域の静寂:DAO.QueryDefにおけるSQLバリデーションの極致

Access VBAという、長年「レガシー」と揶揄されながらも企業の心臓部を支え続けるプラットフォームにおいて、最も脆弱かつ強力な武器が「動的SQL」である。

多くの凡庸なエンジニアは、文字列結合によって構築された不完全なSQLをそのまま `DAO.QueryDef.SQL` プロパティへと流し込み、実行時のランタイムエラーに右往左往する。しかし、我々シニアアーキテクトに許されるのは「不確実性の完全なる排除」のみである。

今回は、QueryDefを書き換える直前に、そのSQLが構文的に潔白であるかを判定する「静的チェック・サンドボックス」の構築手法について、その深淵を解説する。

—

1. 永続化の罠:なぜ直接代入が危険なのか

Accessの `QueryDef` オブジェクトは、その `SQL` プロパティが書き換えられた瞬間、暗黙的にパーサ(構文解析器)を走らせる。もしここで構文エラー(予約語の欠如、カンマの過不足、不正な識別子)があれば、代入そのものがトラップされ、VBAの実行は中断される。

これが「動的SQLの脆弱性」だ。実行時までエラーが露呈しない。
我々が目指すべきは、「QueryDefを汚染する前に、そのSQLの適格性を無菌状態で検証する」ことにある。

—

2. 秘儀:空文字列QueryDefによる「インメモリ・バリデーション」

DAOには、オブジェクトをデータベースに保存せずにメモリ上だけで評価する手法が存在する。`Database.CreateQueryDef` メソッドの第一引数に空文字列(`””`)を渡す手法だ。

この手法を用いれば、MSACE(Microsoft Access Database Engine)のパーサを強制的に呼び出しつつ、既存のクエリ定義を一切傷つけることなく、SQLの妥当性のみを抽出できる。

実践:SQL静的バリデーター `IsSqlSyntacticallyValid`

以下のコードは、単なるエラーハンドリングではない。オブジェクトのライフサイクルとメモリ管理を徹底した、プロフェッショナル仕様のバリデーション関数である。

Option Compare Database
Option Explicit

‘ ===========================================================================
‘ 関数名: IsSqlSyntacticallyValid
‘ 目的 : SQL文字列がDAOエンジンによって正しくパース可能か、
‘ 永続化を行わずにメモリ上で静的に検証する。
‘ 備考 : 実行時エラー(3129:構文エラー等)を未然に防ぐための防波堤。
‘ ===========================================================================
Public Function IsSqlSyntacticallyValid(ByVal strSql As String, Optional ByRef outErrorMsg As String) As Boolean
On Error GoTo Err_Handler

Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim isValid As Boolean: isValid = False

‘ カレントデータベースへの参照を取得
‘ ※DBEngine(0)(0)はキャッシュ効率が良いが、明示的にSetする
Set db = CurrentDb

‘ ———————————————————
‘ 核心部:第一引数に空文字列を渡すことで、
‘ システムテーブル(MSysObjects)に登録されない「揮発性QueryDef」を生成。
‘ この代入の瞬間にJET/ACEエンジンのSQLパーサが起動する。
‘ ———————————————————
Set qdf = db.CreateQueryDef(“”, strSql)

‘ ここまで到達すれば、構文は「DAOの解釈範囲内」で正常。
isValid = True

Exit_Point:
‘ ライフサイクルの厳格な管理
‘ 揮発性オブジェクトであっても明示的解放は鉄則
On Error Resume Next
If Not qdf Is Nothing Then qdf.Close: Set qdf = Nothing
Set db = Nothing
IsSqlSyntacticallyValid = isValid
Exit Function

Err_Handler:
‘ DAOのエラーコレクションから詳細を取得
If DBEngine.Errors.Count > 0 Then
outErrorMsg = “Error ” & DBEngine.Errors(0).Number & “: ” & DBEngine.Errors(0).Description
Else
outErrorMsg = “Unknown Error: ” & Err.Description
End If
isValid = False
Resume Exit_Point
End Function

—

3. Windows APIによるパフォーマンス計測の統合

大規模なバッチ処理や、複雑なサブクエリを含む動的SQLの生成プロセスでは、バリデーション自体のオーバーヘッドが無視できない場合がある。伝説的なアーキテクトは、常にマイクロ秒単位のコストを意識する。

`QueryPerformanceCounter` を使用し、バリデーションがシステムのボトルネックになっていないかを監視する。

‘ Windows API宣言:高精度タイマー
Private Declare PtrSafe Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long

Public Sub SecureUpdateQuery(ByVal qdfName As String, ByVal newSql As String)
Dim startCount As Currency, endCount As Currency, freq As Currency
Dim errMsg As String

QueryPerformanceFrequency freq
QueryPerformanceCounter startCount

‘ バリデーション実行
If IsSqlSyntacticallyValid(newSql, errMsg) Then
‘ 検証を通過した場合のみ、本番のQueryDefを書き換える
With CurrentDb.QueryDefs(qdfName)
.SQL = newSql
.Close
End With
Else
‘ ログ出力(実際の運用ではイベントログや専用テーブルへ)
Debug.Print “[CRITICAL] SQL Validation Failed: ” & errMsg
Err.Raise 513, “SecureUpdateQuery”, “不正なSQLが検知されました: ” & errMsg
End If

QueryPerformanceCounter endCount
Debug.Print “Validation Time: ” & Format$((endCount – startCount) / freq, “0.000000”) & ” sec”
End Sub

—

4. 継承される知恵:なぜここまでやるのか

我々が保守するシステムは、往々にして10年、20年という歳月を生き抜く。その過程で、SQLの断片を組み合わせてクエリを構築するロジックは複雑化の一途をたどる。

1. メモリのクリーンネス: `CreateQueryDef(“”)` は、MSysObjects(システムカタログ)に書き込みを発生させない。これはデータベースの肥大化(Bloat)を防ぐための重要なプラクティスである。
2. 原子性の保持: 不完全なSQLを `QueryDef.SQL` に代入してエラーが発生した場合、そのオブジェクトの状態が不安定になる(あるいは古いSQLが中途半端に残る)リスクを排除できる。
3. デバッグの迅速化: 実行時の「実行エラー」ではなく、代入前の「論理エラー」として捕捉することで、コールスタックが汚れず、原因の特定が劇的に速くなる。

—

結言:静かなるコードへの昇華

Access VBAは、決して古臭いおもちゃではない。COM(Component Object Model)とDAOの深い理解に基づけば、これほど堅牢で信頼性の高いデータアクセスレイヤーを構築できる言語も稀である。

SQL文字列を直接QueryDefに叩き込むような野蛮な行為はやめよ。
まずは「仮想の揺りかご(In-memory QueryDef)」でその正当性を試し、確信を持ってから実体を書き換える。この一見すると遠回りに見える「静的チェック」こそが、ミッションクリティカルな環境を支えるシニアエンジニアの矜持である。

あなたのコードに、完璧な静寂と信頼が宿ることを願う。

タイトルとURLをコピーしました