ADO.NETとA5:SQL Mk-2の仲良しデバッグ

A5:SQL Mk-2を勝手に応援を勝手に応援シリーズ第二弾です。


普段は『自作の「SQL Server sp_executesql stripper for A5:SQL Mk-2」+SQL Server Profiler』と「A5:SQL Mk-2 スクリプト - 流星墓場」で公開されてる「SetParameterジェネレータ」で事足りてるんだけど、SQLServer以外でもC#/VB.NET上のデバッグ時にいつもの擬似命令付きSQLを取得できるようにとやっつけでコード書いてみました。


対応パラメータ形式は現状名前付き(NamedParameterだっけ?)パラメータのみです。ODBCドライバやOracleの標準プロバイダを選んだ際の

INSERT INTO TBL1 (col1, col2) VALUES (?, ?)

の記法はまだ対応してません。

確かSystem.Data.SqlClient、System.Data.SQLiteOracleの拡張プロバイダ(みたいなのがあった気がする)辺りなら

INSERT INTO TBL1 (col1, col2) VALUES (@col1, @col2)

で書けた筈です。


では早速デバッグ用コードをば…。十分なテストをしてないβ版なので取扱いに注意ください。

    'v0.0.1 手抜きβVer 
    'v0.0.2 2回目βVer
    '  Null値対応してない大ポカを修正
    '  DateTimeとTimeの秒とミリ秒の区切りの修正
    '  末尾にSQL区切り";"の出力を追加(A5:SQL Mk-2上なら";;"とかなってても平気なので)
    'v0.0.3 βVer
    '  時刻は24時間表記する時"HH"と大文字にしないとね…。orz
    ''' <summary>
    ''' 使用例
    '''     Dim cmd As New System.Data.SqlClient.SqlCommand()
    '''     cmd.Connection = connectionObject
    '''     cmd.CommandText = "INSERT INTO TBL1 (col1, col2) VALUES (@col1, @col2)"
    '''     cmd.Parameters.Add("@col1", System.Data.SqlDbType.VarChar).Value = "ABC"
    '''     cmd.Parameters.Add("@col2", System.Data.SqlDbType.Int).Value = 1
    '''
    '''     Debug.Print(a5m2cmdParse(cmd)) '実行直前にデバッグ出力
    '''     cmd.ExecuteNonQuery()
    ''' </summary>
    ''' <param name="dbcmd">System.Data.IDbCommandを実装してる???Commandのインスタンス</param>
    ''' <returns>A5:SQL Mk-2でパラメータ設定付で実行する為の文字列 </returns>
    Public Shared Function a5m2cmdParse(ByVal dbcmd As System.Data.IDbCommand) As String
        Dim sb As New System.Text.StringBuilder()
        sb.Length = 0
        For Each p As System.Data.IDataParameter In dbcmd.Parameters
            Dim a5name As String = p.ParameterName.Substring(1)
            Dim a5val As String = String.Empty
            Dim a5type As String = "String"
            Dim isNullValue As Boolean = False
            If IsNothing(p.Value) OrElse IsDBNull(p.Value) Then
                isNullValue = True
            End If
            Select Case p.DbType
                Case DbType.Int16, DbType.Int32, DbType.Int64
                    a5type = "Integer"
                    a5val = If(isNullValue, "NULL", CInt(p.Value).ToString())
                Case DbType.UInt16, DbType.UInt32
                    a5type = "Integer"
                    a5val = If(isNullValue, "NULL", CInt(p.Value).ToString())
                Case DbType.UInt64
                    'a5type = "Integer"
                    a5type = "Currency" '擬似命令でUint64指定が出来ないのでCurrencyで代替
                    a5val = If(isNullValue, "NULL", CDec(p.Value).ToString())
                Case DbType.Decimal
                    a5type = "Currency"
                    a5val = If(isNullValue, "NULL", CDec(p.Value).ToString())
                Case DbType.Byte, DbType.Byte
                    a5type = "Short"
                    a5val = If(isNullValue, "NULL", CShort(p.Value).ToString())
                Case DbType.Single, DbType.Double
                    a5type = "Float"
                    a5val = If(isNullValue, "NULL", CDbl(p.Value).ToString())
                Case DbType.Date
                    a5type = "Date"
                    a5val = If(isNullValue, "NULL", "'" & CDate(p.Value).ToString("yyyy/MM/dd") & "'")
                Case DbType.DateTime, DbType.DateTime2
                    a5type = "DateTime"
                    a5val = If(isNullValue, "NULL", "'" & CDate(p.Value).ToString("yyyy/MM/dd HH:mm:ss.fff") & "'")
                Case DbType.Time
                    a5type = "Time"
                    a5val = If(isNullValue, "NULL", "'" & CDate(p.Value).ToString("HH:mm:ss.fff") & "'")
                Case DbType.AnsiString, DbType.AnsiStringFixedLength, DbType.String, DbType.StringFixedLength
                    a5val = If(isNullValue, "NULL", "'" & CStr(p.Value).Replace("'", "''") & "'")
                    a5type = "String"

                    'Case DbType.Boolean
                    '    'DB毎に実装違いそう

                    '他はもう面倒
                    'Case DbType.Binary
                    'Case DbType.Guid
                    'Case DbType.Object
                    'Case DbType.VarNumeric
                    'Case DbType.Xml
                Case Else
                    a5val = If(isNullValue, "NULL", "'" & (p.Value.ToString()).Replace("'", "''") & "'")
                    a5type = "String"
            End Select
            sb.AppendLine("--*SetParameter " & a5name & " " & a5val & " " & a5type)
        Next
        sb.AppendLine(dbcmd.CommandText)
        sb.AppendLine(";") '区切り追加
        Return sb.ToString()
    End Function

C#版が欲しいならILSpyでも使って逆コンパイルしてください!)
呼び出し方は

Dim cmd As New System.Data.SqlClient.SqlCommand()
cmd.Connection = connectionObject
cmd.CommandText = "INSERT INTO TBL1 (col1, col2) VALUES (@col1, @col2)"
cmd.Parameters.Add("@col1", System.Data.SqlDbType.VarChar).Value = "ABC"
cmd.Parameters.Add("@col2", System.Data.SqlDbType.Int).Value = 1

Debug.Print(a5m2cmdParse(cmd)) '<-ここ
cmd.ExecuteNonQuery()

の様にSQLもパラメータもセットしおわったSqlCommand等のインスタンス*1を渡して結果文字列を取得するだけです。簡単ですね!
ではデバッグは皆に任せた!

*1: System.Data.IDbCommandインターフェースを実装してる*Commandなら基本何でも可