currentDb.Execute
とDocmd.RunSQL
をヘルパー関数に置き換えました。いずれかの更新ステートメントにテーブルが1つしか含まれていない場合、SQLステートメントを前処理して変更できます。dual
(単一行、単一列)テーブルが既にあるので、fakeTableオプションを使用しました。
注:これによってクエリオブジェクトが変更されることはありません。VBAを介したSQL実行のみを支援します。If you would like to change your query objects, use FnQueryReplaceSingleTableUpdateStatements and update your sql in each of your querydefs. Shouldn't be a problem either.
これは単なる概念(If it's a single table update modify the sql before execution)
です。必要に応じて調整してください。この方法では、各テーブルの置換クエリは作成されません(これは最も簡単な方法ですが、独自の欠点があります。つまり、パフォーマンスの問題)。
+ポイント: MSがバグを修正しても何も変更されない場合でも、このヘルパーを引き続き使用
できます。将来、別の問題が発生した場合でも、pre-process
SQLを1か所で準備できます。更新方法をアンインストールすることはしませんでした。これは、管理者アクセスが必要であり、すべてのユーザーが正しいバージョンになるまでに時間がかかりすぎるためです+アンインストールしても、一部のエンドユーザーのグループポリシーによって最新の更新が再度インストールされます。同じ問題に戻ります。
ソースコードにアクセスできuse this method
、問題のあるエンドユーザーがいないことを100%確信している場合。
Public Function Execute(Query As String, Optional Options As Variant)
'Direct replacement for currentDb.Execute
If IsBlank(Query) Then Exit Function
'invalid db options remove
If Not IsMissing(Options) Then
If (Options = True) Then
'DoCmd RunSql query,True ' True should fail so transactions can be reverted
'We are only doing this so DoCmd.RunSQL query, true can be directly replaced by helper.Execute query, true.
Options = dbFailOnError
End If
End If
'Preprocessing the sql command to remove single table updates
Query = FnQueryReplaceSingleTableUpdateStatements(Query)
'Execute the command
If ((Not IsMissing(Options)) And (CLng(Options) > 0)) Then
currentDb.Execute Query, Options
Else
currentDb.Execute Query
End If
End Function
Public Function FnQueryReplaceSingleTableUpdateStatements(Query As String) As String
' ON November 2019 Microsoft released a buggy security update that affected single table updates.
'/programming/58832269/getting-error-3340-query-is-corrupt-while-executing-queries-docmd-runsql
Dim singleTableUpdate As String
Dim tableName As String
Const updateWord As String = "update"
Const setWord As String = "set"
If IsBlank(Query) Then Exit Function
'Find the update statement between UPDATE ... SET
singleTableUpdate = FnQueryContainsSingleTableUpdate(Query)
'do we have any match? if any match found, that needs to be preprocessed
If Not (IsBlank(singleTableUpdate)) Then
'Remove UPDATe keyword
If (VBA.Left(singleTableUpdate, Len(updateWord)) = updateWord) Then
tableName = VBA.Right(singleTableUpdate, Len(singleTableUpdate) - Len(updateWord))
End If
'Remove SET keyword
If (VBA.Right(tableName, Len(setWord)) = setWord) Then
tableName = VBA.Left(tableName, Len(tableName) - Len(setWord))
End If
'Decide which method you want to go for. SingleRow table or Select?
'I'm going with a fake/dual table.
'If you are going with update (select * from T) as T, make sure table aliases are correctly assigned.
tableName = gDll.sFormat("UPDATE {0},{1} SET ", tableName, ModTableNames.FakeTableName)
'replace the query with the new statement
Query = vba.Replace(Query, singleTableUpdate, tableName, compare:=vbDatabaseCompare, Count:=1)
End If
FnQueryReplaceSingleTableUpdateStatements = Query
End Function
Public Function FnQueryContainsSingleTableUpdate(Query As String) As String
'Returns the update ... SET statment if it contains only one table.
FnQueryContainsSingleTableUpdate = ""
If IsBlank(Query) Then Exit Function
Dim pattern As String
Dim firstMatch As String
'Get the pattern from your settings repository or hardcode it.
pattern = "(update)+(\w|\s(?!join))*set"
FnQueryContainsSingleTableUpdate = FN_REGEX_GET_FIRST_MATCH(Query, pattern, isGlobal:=True, isMultiline:=True, doIgnoreCase:=True)
End Function
Public Function FN_REGEX_GET_FIRST_MATCH(iText As String, iPattern As String, Optional isGlobal As Boolean = True, Optional isMultiline As Boolean = True, Optional doIgnoreCase As Boolean = True) As String
'Returns first match or ""
If IsBlank(iText) Then Exit Function
If IsBlank(iPattern) Then Exit Function
Dim objRegex As Object
Dim allMatches As Variant
Dim I As Long
FN_REGEX_GET_FIRST_MATCH = ""
On Error GoTo FN_REGEX_GET_FIRST_MATCH_Error
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Multiline = isMultiline
.Global = isGlobal
.IgnoreCase = doIgnoreCase
.pattern = iPattern
If .test(iText) Then
Set allMatches = .Execute(iText)
If allMatches.Count > 0 Then
FN_REGEX_GET_FIRST_MATCH = allMatches.item(0)
End If
End If
End With
Set objRegex = Nothing
On Error GoTo 0
Exit Function
FN_REGEX_GET_FIRST_MATCH_Error:
FN_REGEX_GET_FIRST_MATCH = ""
End Function
今ちょうどCTRL+F
検索と置換するdocmd.RunSQL
とhelper.Execute
検索と置換する[currentdb|dbengine|or your dbobject].execute
とhelper.execute
楽しんで!