2017-04-24 188 views
3

我試圖在我的代碼中使用tracing來調試一些SQLite查詢,只是將所發生的一切記錄到控制檯,但似乎幾乎沒有關於它的任何信息 - 谷歌搜索「sqlite3_trace_v2 swift」只返回兩頁結果,除了上面的鏈接外,其中沒有一個是有用的。使用下面的代碼,我能得到它的地步,至少運行跟蹤回調:用於日誌記錄的SQLite跟蹤

func traceSQL (database: OpaquePointer?) { 
    var pointer: OpaquePointer? 
    func traceCallback (mask: UInt32, pointer: UnsafeMutableRawPointer?, query: UnsafeMutableRawPointer?, result: UnsafeMutableRawPointer?) -> Int32 { 
     print("SQLite Trace:") 

     if let query = query?.load(as: UnsafePointer<Int8>.self) { 
      print(String(cString: query)) 
     } else { 
      print("Could not load query.") 
     } 

     if let result = result?.load(as: UnsafePointer<Int8>.self) { 
      print(String(cString: result)) 
     } else { 
      print("Could not load result.") 
     } 

     return 0 
    } 
    sqlite3_trace_v2(database, 15, traceCallback as @convention(c) (UInt32, UnsafeMutableRawPointer?, UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Int32, &pointer) 
} 

,但我無法弄清楚如何處理函數的輸出做 - 目前,它只是打印出一串不可讀的字符,我以前的嘗試甚至沒有管理。我懷疑至少有部分問題是我不知道如何在Swift中使用UnsafeMutableRawPointer(其他東西似乎缺少可用的信息)。

tl; dr:如何從SQLite記錄追蹤結果?

回答

3

在你的代碼的主要錯誤是,你反引用傳遞給回調,而不是的原始指針 重新詮釋(鑄造)他們。 這些指針的含義對於不同的 事件也不同。

下面是一個示例,如何跟蹤各種事件以及如何將 原始指針轉換爲「正確」類型,使用文字閉包 作爲回調。解釋px 參數含義的註釋取自SQL Trace Event Codes

let traceMask = SQLITE_TRACE_STMT|SQLITE_TRACE_PROFILE|SQLITE_TRACE_ROW|SQLITE_TRACE_CLOSE 

sqlite3_trace_v2(database, UInt32(traceMask), { (reason, context, p, x) -> Int32 in 
    switch Int32(reason) { 
    case SQLITE_TRACE_STMT: 
     // The P argument is a pointer to the prepared statement. 
     // The X argument is a pointer to a string which is the unexpanded SQL text 
     guard 
      let pStmt = OpaquePointer(p), 
      let cSql = x?.assumingMemoryBound(to: CChar.self) 
     else { 
      return 0 
     } 

     let sql = String(cString: cSql) // The unexpanded SQL text 
     let expandedSql = String(cString: sqlite3_expanded_sql(pStmt)) // The expanded SQL text 
     print("SQLITE_TRACE_STMT:", expandedSql) 

    case SQLITE_TRACE_PROFILE: 
     // The P argument is a pointer to the prepared statement and the X argument points 
     // to a 64-bit integer which is the estimated of the number of nanosecond that the 
     // prepared statement took to run. 
     guard 
      let pStmt = OpaquePointer(p), 
      let duration = x?.load(as: UInt64.self) 
     else { 
      return 0 
     } 

     let milliSeconds = Double(duration)/Double(NSEC_PER_MSEC) 
     let sql = String(cString: sqlite3_sql(pStmt)) // The unexpanded SQL text 
     print("SQLITE_TRACE_PROFILE:", milliSeconds, "ms for statement:", sql) 

    case SQLITE_TRACE_ROW: 
     // The P argument is a pointer to the prepared statement and the X argument is unused. 
     guard 
      let pStmt = OpaquePointer(p) 
     else { 
      return 0 
     } 

     print("SQLITE_TRACE_ROW") 

    case SQLITE_TRACE_CLOSE: 
     // The P argument is a pointer to the database connection object and the X argument is unused. 
     guard 
      let database = OpaquePointer(p) 
     else { 
      return 0 
     } 

     print("SQLITE_TRACE_CLOSE") 

    default: 
     break 
    } 
    return 0 
}, nil) 

當然你也可以限制跟蹤模式,你 在很有意思的事件,例如

let traceMask = SQLITE_TRACE_STMT 

只跟蹤預準備語句。