2012-05-26 2 views
3

BLOB을 사용하여 SQLite 데이터베이스에 개체를 삽입합니다. 삽입 후 "SQLite Database Browser"를 사용하여 데이터베이스를 탐색 할 때 "TASK_HEAD"행이 "Empty"이지만 "SELECT"문장으로 데이터를 가져올 수 있습니다. 그러나 방금 삽입 한 개체를 파괴하면 "pHead"포인터가 "id"멤버의 내용이 "铪 铪 铪 铪 is"인 주소를 가리키고 올바른 데이터를 더 이상 얻을 수 없습니다铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪 铪   " 디버그 모드에서 VS2008에서 읽을 때. 여기 BLOB 데이터를 SQLite 데이터베이스에 성공적으로 삽입했으나 삽입 된 데이터를 가져올 수 없습니다.

은 예입니다 : 처음에 나는 그것이 sqlite3_bind_blob에 전달 바이트의 문제가 될 거라고 생각

// user-defined data type 
typedef std::string TASK_ID; 
struct TASK_HEAD 
{ 
    TASK_ID id; 
    std::string userData; 

    int Size() 
    { 
     return (id.size() + userData.size()) * sizeof(TCHAR); 
    } 
}; 

// when TEST_INSIDE is defined, pHead is invalid; but if undef it, I can get the "head" I just inserted 
// and if the blob data is a string (when USING_STRING is defined), I can get the string inserted into the db even though the "test" string has been destroyed 
void CDBWriter::WriteTestData() 
{ 
    // open db 
    sqlite3* db = NULL; 
    int nRet = sqlite3_open(DATABASE_NAME.c_str(), &db); 
    if (nRet != SQLITE_OK) 
    { 
     return; 
    } 

    if (db != NULL) 
    { 
     // create a table 
     std::string cmdCreate("CREATE TABLE IF NOT EXISTS testTable (id TEXT NOT NULL, TASK_HEAD BLOB, PRIMARY KEY(id));"); 
     char* errMsg = NULL; 
     nRet = sqlite3_exec(db , cmdCreate.c_str() , 0 , 0 , &errMsg); 
     if(errMsg != NULL) 
     { 
      sqlite3_free(errMsg); 
      errMsg = NULL; 
      return; 
     } 

//#define USING_STRING 
#define TEST_INSIDE 
#ifndef TEST_INSIDE 
     TASK_HEAD head; 
#endif // TEST_INSIDE 

     // insert blob data 
     const TASK_ID newID(NewGUID()); // NewGUID returns string like this: "5811307F-7AA7-4C44-831F-774FC5832627" 
     string query = "INSERT OR REPLACE INTO testTable (id, TASK_HEAD) VALUES ('"; 
     query += newID; 
     query += "', ?1);"; 
     sqlite3_stmt* res = NULL; 
     nRet = sqlite3_prepare_v2(db, query.c_str(), query.length(), &res, 0); 
     { 
#ifdef TEST_INSIDE 
      TASK_HEAD head; 
#endif // TEST_INSIDE 
      head.id = newID; 
#ifdef USING_STRING 
      std::string test("ewsjoafijdoaijeofsafsd"); 
      nRet = sqlite3_bind_blob (res, 1, test.c_str(), test.size(), SQLITE_TRANSIENT); 
#else 
      int nsizeHead = sizeof(head); 
      int nSizeHeadSt = sizeof(TASK_HEAD); 
      int sizeString = sizeof(std::string); 
      size_t nLen = newID.size(); 
      //nRet = sqlite3_bind_blob (res, 1, &head, sizeof(head), SQLITE_TRANSIENT); 
      nRet = sqlite3_bind_blob (res, 1, &head, head.Size(), SQLITE_TRANSIENT); 
#endif // USING_STRING 

      if (SQLITE_OK == nRet) 
      { 
       nRet = sqlite3_step(res); 
      } 
      if (nRet != SQLITE_OK && nRet != SQLITE_DONE) 
      { 
       return; 
      } 
     } 

     // get all columns in the database 
     query = "SELECT * FROM testTable;"; 
     nRet = sqlite3_prepare_v2 (db, query.c_str(), query.length(), &res, 0); 
     if (SQLITE_OK == nRet) 
     { 
      while (SQLITE_ROW == sqlite3_step(res)) 
      { 
#ifdef USING_STRING 
       const char* pHead = (const char*)sqlite3_column_blob(res, 1); 
#else 
       const TASK_HEAD *pHead = (const TASK_HEAD*)sqlite3_column_blob(res, 1); 
#endif // USING_STRING 
       continue; 
      } 
     } 
     sqlite3_finalize(res); 
     sqlite3_close(db); 
    } 
} 

, 그래서 당신은 여기에서 볼 수 있듯이 나는 바보 같은 방법으로 객체의 바이트를 얻을 (TASK_HEAD의 size() 함수). 그러나 그것은 도움이되지 않습니다. 그런 다음 SQLITE_TRANSIENT 대신 SQLITE_STATIC을 사용하려고했지만 여전히 작동하지 않습니다. 무엇이 잘못 되었나요?

Ps : db에 객체를 삽입하는 것은 좋지 않은 해결책이며, 왜 db에 삽입 된 데이터를 다시 읽을 수 없는지 알고 싶습니다.

답변

1

나는에 문제가 생각합니다. blob을 만들기 위해서는 포인터와 std :: string 객체와 같은 동적 버퍼가없는 평면 데이터가 필요합니다.

바인딩 작업 전에 버퍼의 TASK_HEAD 구조체를 직렬화해야합니다. 예를 들어 :

struct TASK_HEAD 
{ 
    TASK_ID id; 
    std::string userData; 

    std::string Data() 
    { 
     return id+userData; 
    } 

    int Size() 
    { 
     return (id.size() + userData.size()) * sizeof(TCHAR); 
    } 
}; 

과 :

nRet = sqlite3_bind_blob (res, 1, head.Data().c_str(), head.Size(), SQLITE_TRANSIENT); 

위 (이 형식은 직렬화를 할 수 없기 때문에) 매우 가난으로 직렬화 할 필드를 추가 있습니다. blob을 처리하려면 좋은 직렬화 라이브러리 또는 형식 (프로토콜 버퍼, 메시지 팩, JSON 등)을 찾거나 자신을 롤업해야합니다. 이 비슷한 이유로 작동하지 않습니다

const TASK_HEAD *pHead = (const TASK_HEAD*)sqlite3_column_blob(res, 1); 

:

은 코드에서 두 번째 문제가있다.

+0

대단히 감사합니다! 그것은 많은 도움이됩니다.BTW, BLOB 데이터에 대한 소개가 추가되었습니다 (포인터와 동적 버퍼로 빌드 할 수없는 이유는 무엇입니까?). – YoungLearner

+0

http://en.wikipedia.org/wiki/Serialization –

+0

감사합니다. 도움이됩니다. – YoungLearner

1

userData의 내용은 힙에 저장 될 가능성이 큽니다. std::string (SSO 용)에도 저장되어 있어도 내부적으로 포인터를 사용할 수 있으므로 비트 단위로 메모리의 다른 위치로 복사하면 작동하지 않습니다 (수행중인 작업은 memcpy과 같습니다) .

그러나 정확하게 작동하지 않는 이유는 중요하지 않습니다. 단지 동작이 정의되지 않았기 때문에입니다. 그냥 "db에 개체를 삽입"하지 마십시오. 일부 직렬화 라이브러리를 사용하여 직렬화하거나 을 삽입 한 다음을 삽입하거나 테이블에서 두 열을 사용하십시오. 하나는 id이고 다른 하나는 userData입니다.

nRet = sqlite3_bind_blob (res, 1, &head, head.Size(), SQLITE_TRANSIENT); 

당신은 TASK_HEAD 구조의 주소를 얻기과 같이 sqlite를 그것을 전달할 수 없습니다 :

+0

설명해 주셔서 감사합니다. STL 라이브러리에 대해 더 자세히 읽어 보시기 바랍니다. 조언 해 주셔서 감사합니다. – YoungLearner

관련 문제