尝试通过 ADODB/SQLiteODBC 更改 SQLite 数据库的日志模式时,Excel/VBA 挂起

Posted

技术标签:

【中文标题】尝试通过 ADODB/SQLiteODBC 更改 SQLite 数据库的日志模式时,Excel/VBA 挂起【英文标题】:Excel/VBA hangs when trying to change journal mode of an SQLite database via ADODB/SQLiteODBC 【发布时间】:2022-01-12 16:17:24 【问题描述】:

下面的代码在 Temp 文件夹中创建一个新的空白 SQLite 数据库,创建一个表,填充它,然后暂停。然后在DB Browser for SQLite中交互式地打开数据库,并更改一个值,但未提交更改。恢复执行,Excel/VBA 挂起约 1.5 分钟,然后在尝试通过 ADODB/SQLiteODBC 更改日志模式时引发“数据库已锁定”错误。

为了澄清,此代码专门用于重现问题。很清楚为什么会引发错误。问题在于所需的等待时间。

Private Sub ConnectSQLiteAdoCommandSource()
    Dim Driver As String
    Driver = "SQLite3 ODBC Driver"
    Dim Database As String
    Database = Environ("Temp") & "\" & CStr(Format(Now, "yyyy-mm-dd_hh-mm-ss.")) _
        & CStr((Timer * 10000) Mod 10000) & CStr(Round(Rnd * 10000, 0)) & ".db"
    Debug.Print Database
    Dim Options As String
    Options = "JournalMode=DELETE;SyncPragma=NORMAL;FKSupport=True;"

    Dim AdoConnStr As String
    AdoConnStr = "Driver=" & Driver & ";" & "Database=" & Database & ";" & Options
    
    Dim SQLQuery As String
    Dim RecordsAffected As Long
    Dim AdoCommand As ADODB.Command
    Set AdoCommand = New ADODB.Command
    With AdoCommand
        .CommandType = adCmdText
        .ActiveConnection = AdoConnStr
        .ActiveConnection.CursorLocation = adUseClient
    End With
    
    '''' ===== Create Functions table ===== ''''
    SQLQuery = Join(Array( _
        "CREATE TABLE functions(", _
        "    name    TEXT COLLATE NOCASE NOT NULL,", _
        "    builtin INTEGER             NOT NULL,", _
        "    type    TEXT COLLATE NOCASE NOT NULL,", _
        "    enc     TEXT COLLATE NOCASE NOT NULL,", _
        "    narg    INTEGER             NOT NULL,", _
        "    flags   INTEGER             NOT NULL", _
        ")" _
    ), vbLf)
    With AdoCommand
        .CommandText = SQLQuery
        .Execute RecordsAffected, Options:=adExecuteNoRecords
    End With
    
    '''' ===== Insert rows into Functions table ===== ''''
    SQLQuery = Join(Array( _
        "INSERT INTO functions", _
        "SELECT * FROM pragma_function_list" _
    ), vbLf)
    With AdoCommand
        .CommandText = SQLQuery
        .Execute RecordsAffected, Options:=adExecuteNoRecords
    End With
    
    '@Ignore StopKeyword
    Stop '''' Lock Db. For example, open in GUI admin tool and start a transaction
    '''' ===== Try changing journal mode ===== ''''
    On Error Resume Next
    With AdoCommand
        .CommandText = "PRAGMA journal_mode = 'WAL'"
        .Execute RecordsAffected, Options:=adExecuteNoRecords
    End With
    If Err.Number <> 0 Then
        Debug.Print "Error: #" & CStr(Err.Number) & ". " & vbNewLine & _
                    "Error description: " & Err.Description
    End If
    On Error GoTo 0
    
    AdoCommand.ActiveConnection.Close
End Sub

【问题讨论】:

您发布的代码未运行然后在 DB Browser for SQLite 中打开数据库。如果在处理此 VBA 子期间发生这种情况,则应引发 数据库已锁定。运行像PRAGMA 这样的系统级调用应该在独占访问模式下完成,而不是在读/写操作期间。 @Parfait,不,它没有。 DB Browser for SQLite 是一个交互式使用的 GUI 管理工具。我知道为什么会引发“数据库被锁定”。创建此代码是为了以交互方式重现问题。错误不是问题。超时的长度是。它不应将 Excel/VBA 挂起 1.5 分钟左右。 知道了。尝试 single error handling 在 sub 末尾使用块而不是 On Error Resume NextOn Error Goto 0。其他进程是否在 CPU 上运行? 1.5 分钟。可能只是你的环境。可以在其他地方测试吗? 【参考方案1】:

ADODB 连接和命令有超时设置,默认为 30 秒。更改此默认值不会产生明显的影响。实际超时时间为 100 秒,这个数字来自 SQLiteODBC 源。驱动程序必须有一个错误。它具有默认超时值,从连接字符串中获取自定义值,但忽略通过 ADODB 库设置的值。因此,添加“超时=XXX;”连接字符串选项可以完成这项工作。似乎将其设置为 0 意味着无限等待,但将其设置为 1 ms 可以解决超时问题。另一个默认关闭的选项 - StepAPI - 应该默认使用。这是另一个驱动程序错误或限制:启用 StepAPI 时,未设置忙处理程序,并且超时问题也消失了,无论“超时 = XXX;”如何选项。

【讨论】:

以上是关于尝试通过 ADODB/SQLiteODBC 更改 SQLite 数据库的日志模式时,Excel/VBA 挂起的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试通过更改 `extra.symfony.require` 来升级 Symfony 时,为啥会出现此错误?

尝试通过部署后脚本添加列(更改表)并尝试将数据添加到新创建的列中抛出错误

尝试通过使用rest api从TFS票证中读取缺陷历史来获取TFS错误状态更改日期

消息:错误:轮询更改失败:通过 Selenium 和 FirefoxProfile 下载文件时尝试获取资源时出现 NetworkError

通过代码从 xcassets 更改图像的内容

尝试通过 liquibase、postgresql、spring boot 创建新数据库。结果->“数据库更改日志”不存在