2014-12-24 186 views
1

我使用以下代碼將任意二進制數據插入到mysql數據庫MEDIUMBLOB中。我從相同的程序將相同的數據寫入文件。我再從DB內容創建一個文件:將字符插入到數據庫中

select data from table where tag=95 order by date, time into outfile "dbout"; 

我再比較直接寫入文件中DBOUT輸出的輸出。在dbout文件中的某些字節(例如0x00之前)之前有escape(0x5c,'\')字符。這會干擾數據庫的輸出。我的理解是,通過使用MEDIUMBLOB並準備好語句,我可以避免這個問題。最初我使用了帶有普通INSERT的mysql_real_escape_string,並且遇到了問題。似乎沒有什麼解決這個問題的。

void 
insertdb(int16_t *data, size_t size, size_t nmemb) 
{ 
    int16_t *fwbuf; // I have also tried this as char *fwbuf 
    unsigned long i; 
    struct tm *info; 
    time_t rawtime; 
    char dbuf[12]; 
    char tbuf[12]; 

    if(fwinitialized==0){ 
     fwbuf = malloc(CHUNK_SZ); 
     fwinitialized = 1; 
    } 

    if(fwindex + (nmemb*size) + 1 >= CHUNK_SZ || do_exit == 1){ 
     MYSQL_STMT *stmt = mysql_stmt_init(con); 
     MYSQL_BIND param[1]; 

     time(&rawtime); 
     info = localtime(&rawtime); 
     snprintf(dbuf, 16, "%d-%02d-%02d", 1900+info->tm_year, 1+info->tm_mon, info->tm_mday); 
     snprintf(tbuf, 16, "%02d:%02d:%02d", info->tm_hour, info->tm_min, info->tm_sec); 

     char *tmp = "INSERT INTO %s (date, time, tag, data) VALUES ('%s', '%s', %d, ?)"; 
     int len = strlen(tmp)+strlen(db_mon_table)+strlen(dbuf)+strlen(tbuf)+MAX_TAG_LEN+1; 
     char *sql = (char *) malloc(len); 
     int sqllen = snprintf(sql, len, tmp, db_mon_table, dbuf, tbuf, tag); 

     if(mysql_stmt_prepare(stmt, sql, strlen(sql)) != 0){ 
      printf("Unable to create session: mysql_stmt_prepare()\n"); 
      exit(1); 
     } 

     memset(param, 0, sizeof(param)); 
     param[0].buffer_type = MYSQL_TYPE_MEDIUM_BLOB; 
     param[0].buffer = fwbuf; 
     param[0].is_unsigned = 0; 
     param[0].is_null = 0; 
     param[0].length = &fwindex; 

     if(mysql_stmt_bind_param(stmt, param) != 0){ 
      printf("Unable to create session: mysql_stmt_bind_param()\n"); 
      exit(1); 
     } 

     if(mysql_stmt_execute(stmt) != 0){ 
      printf("Unabel to execute session: mysql_stmt_execute()\n"); 
      exit(1); 
     } 

     printf("closing\n"); 
     mysql_stmt_close(stmt); 

     free(sql); 
     fwindex = 0; 

    } else { 
     memcpy((void *) fwbuf+fwindex, (void *) data, nmemb*size); 
     fwindex += (nmemb*size); 
    } 
} 

那麼,爲什麼數據庫中的轉義字符?我已經在程序中以及從msyql創建文件時嘗試了幾個hex/unhex的組合。這似乎也沒有幫助。是不是將一個任意的二進制數據插入到數據庫中是一個定義明確的解決方案的常見事情?

P.S. - 可以準備好打開,插入和關閉這樣的語句,或者準備好通常用於在關閉之前循環和插入一堆數據的語句?

PPS - 也許這是解決問題的重要:當我嘗試使用UNHEX這樣的:

select unhex(data) from table where tag=95 order by date, time into outfile "dbout"; 

輸出很短(不到幾十個字節,截斷由於某種原因)。

+1

你的代碼有很多問題,最不重要的是顯示'tag'是什麼,而最糟糕的是可能的[undefined behavior](http://en.wikipedia.org/wiki/Undefined_behavior)不在字符串中爲字符串終止符分配空間。 –

+0

什麼是標籤的數據類型?注意sizeof(tag)對於數字的字符串長度表示來說不是一個好的估計。如果標籤是一個指針,它將是8或4字節(取決於你是否編譯爲32或64位體系結構),無論存儲的數字是多少,例如數字「1」只有長度1 ..還有一些內存泄漏在你的程序中。特別是在你的錯誤處理代碼中,你不會在退出之前釋放sql或fwbuf。 – Rob

+0

@Rob - 標記只是一個int。另外,如果程序退出,我認爲sql和fwbuf將被操作系統「釋放」。但是,如果我的問題沒有轉義字符存儲任意二進制數據在數據庫中? –

回答

1

由於MEDIUMBLOB可以包含任何字符(即使是ASCII NUL)MySQL通常會轉義輸出,因此您可以知道字段何時結束。你可以使用ESCAPED BY來控制它。該文檔是here。以下是摘錄。根據下面的最後一段(我用粗體表示),你完全可以禁用轉義。我從來沒有嘗試過,因爲最後一句話的原因。

FIELDS ESCAPED BY控制如何寫特殊字符。如果FIELDS ESCAPED BY字符不爲空,則在必要時使用,以避免模糊作爲上輸出之前以下字符前綴:

  • FIELDS ESCAPED BY字符

  • FIELDS [OPTIONALLY] ENCLOSED BY字符

  • FIELDS TERMINATED BYLINES TERMINATED BY的第一個字符值

  • ASCII NUL(零值字節;什麼是實際寫入以下轉義字符爲ASCII "0",而不是一個零值字節)

FIELDS TERMINATED BYENCLOSED BYESCAPED BY,或LINES TERMINATED BY字符必須進行轉義,這樣就可以讀取該文件回可靠。 ASCII NUL被轉義,以便使用某些傳呼機查看時更容易。

生成的文件不必符合SQL語法,所以不需要轉義任何東西。

如果FIELDS ESCAPED BY字符是空的,沒有字符被轉義,並且NULL輸出爲NULL,不\N指定空轉義字符可能不是一個好主意,特別是如果數據中的字段值包含剛纔給出的列表中的任何字符。

一個更好的策略(如果你只需要在輸出文件中一個BLOB)是SELECT INTO ... DUMPFILE,記錄在同一頁上,按照下面:如果您使用INTO DUMPFILE代替INTO OUTFILE

,MySQL的寫只有一行進入文件,沒有任何列或行終止,也沒有執行任何轉義處理。如果要將BLOB值存儲在文件中,這非常有用。

+0

非常感謝您的幫助。 –

相關問題