我使用下面的代码将任意二进制数据插入到mysql数据库MEDIUMBLOB中。我将相同的数据写入同一程序的文件中。然后我从DB内容创建一个文件:
select data from table where tag=95 order by date, time into outfile "dbout";
然后我将直接写入文件的输出与dbout中的输出进行比较。在dbout文件中的某些字节之前有转义(0x5c,' \')字符(例如在0x00之前)。这会使数据库的输出变得混乱。我的理解是,通过使用MEDIUMBLOB和准备好的语句,我可以避免这个问题。最初我使用mysql_real_escape_string与常规INSERT,并遇到问题。似乎没有什么能解决这个问题。
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";
输出非常短(少于十几个字节,由于某种原因被截断)。
答案 0 :(得分:1)
由于MEDIUMBLOB
可以包含任何字符(甚至是ASCII NUL
),MySQL通常会转义输出,因此您可以判断字段何时结束。您可以使用ESCAPED BY
控制此操作。文档是here。以下是摘录。根据下面的最后一段(我以粗体显示),您可以完全禁用转义。由于最后一句话的原因,我从未尝试过。
FIELDS ESCAPED BY
控制如何编写特殊字符。如果FIELDS ESCAPED BY
字符不为空,则在必要时使用它以避免歧义作为输出后面字符之前的前缀:
FIELDS ESCAPED BY
字符
FIELDS [OPTIONALLY] ENCLOSED BY
字符
FIELDS TERMINATED BY
和LINES TERMINATED BY
值的第一个字符
ASCII NUL
(零值字节;转义字符后面实际写的是ASCII "0"
,而不是零值字节)必须转义
FIELDS TERMINATED BY
,ENCLOSED BY
,ESCAPED BY
或LINES TERMINATED BY
个字符,以便您可以可靠地读取该文件。转义ASCIINUL
,以便使用某些寻呼机更容易查看。生成的文件不必符合SQL语法,因此不需要转义任何其他内容。
如果
FIELDS ESCAPED BY
字符为空,则不转义字符,NULL
输出为NULL
,而不是\N
。可能是指定空的转义字符不是一个好主意,特别是如果数据中的字段值包含刚刚给出的列表中的任何字符。
更好的策略(如果输出文件中只需要一个blob)是SELECT INTO ... DUMPFILE
,记录在同一页面上,如下所示:
如果使用
INTO DUMPFILE
而不是INTO OUTFILE
,MySQL只会在文件中写入一行,不会有任何列或行终止,也不会执行任何转义处理。如果要将BLOB值存储在文件中,这非常有用。