MySQL 连接器 C/C API - 带特殊字符的查询

MySQL Connector C/C API - Query with special characters

我是一个 C 程序,我有一个接受域名参数的函数:

void db_domains_query(char *name);

使用 mysql_query() 我测试域名是否存在于数据库中。如果不是这样,我插入新域名:

...
char *query[400];

sprintf(query, "SELECT Id, DomainName FROM domains WHERE domainName LIKE '%s'", name);
if (mysql_query(con, query))
finish_with_error(con);

MYSQL_RES *result = mysql_store_result(con);
    if (result == NULL)
        finish_with_error(con);

    MYSQL_ROW row;
    if ((row = mysql_fetch_row(result)))
        printf("Element exists : %s %s\n", row[0], row[1]);
    else
        printf("Element %s doesn't found\n", name);
        // Then insert the new domain name ...

如果 name 仅包含 "normal characters",则这部分代码可以完美运行。但是,对于包含 "special characters" 的域名,查询似乎不正确,即使这些域名在数据库中,例如:

字段 域名 的排序规则是 utf8_unicode_ci。 那么我如何才能将所有域名传递给 mysql_query,包括 "special" 的域名?

我建议您避免使用 C API,除非您有充分的理由使用它。 C++ API 更有用。

您正在将参数嵌入到查询字符串中。这有很多问题,包括安全风险。如果您坚持采用这种方法,为了防止参数问题干扰您的查询,您需要确保以下几点:

  • 确保您的数据编码与 MySQL 客户端连接的编码匹配(这可能与您的数据库编码不同)。如果您的连接设置为 UTF-8,那么您需要确保特殊字符(例如 ©)在用作 sprintf 函数的输入时也以 UTF-8 编码。
  • 您还需要防止其他 SQL 转义字符(例如 ')。为此,您可以使用 mysql_real_escape_string 函数,如 Efficiently escaping quotes in C before passing to mysql_query.
  • 中所述

但是,您很可能会使用 准备好的语句 来规避这些问题。您仍然需要确保您的输入数据编码与您的客户端连接的编码匹配,但其他一切都将更容易处理。

我粘贴了一个使用 C API 和准备好的语句的参数化查询示例(来自 http://lgallardo.com/2011/06/23/sentencias-preparadas-de-mysql-en-c-ejemplo-completo/ 的示例)。请注意,该示例适用于整数,而不是字符串,您需要适应您的用例。

 sql = "select count(*) from addresses where id = ?";

 // Open Database
 openDB(&conn);

 // Allocate statement handler
 stmt = mysql_stmt_init(conn);

 if (stmt == NULL) {
  print_error(conn, "Could not initialize statement handler");
  return;
 }

 // Prepare the statement
 if (mysql_stmt_prepare(stmt, sql, strlen(sql)) != 0) {
  print_stmt_error(stmt, "Could not prepare statement");
  return;
 }

 // Initialize the result column structures
 memset (param, 0, sizeof (param)); /* zero the structures */
 memset (result, 0, sizeof (result)); /* zero the structures */

 // Init param structure
 // Select
 param[0].buffer_type     = MYSQL_TYPE_LONG;
 param[0].buffer         = (void *) &myId;
 param[0].is_unsigned    = 0;
 param[0].is_null         = 0;
 param[0].length         = 0;

 // Result
 result[0].buffer_type     = MYSQL_TYPE_LONG;
 result[0].buffer         = (void *) &myNumAddresses;
 result[0].is_unsigned    = 0;
 result[0].is_null         = &is_null[0];
 result[0].length         = 0;

 // Bind param structure to statement
 if (mysql_stmt_bind_param(stmt, param) != 0) {
  print_stmt_error(stmt, "Could not bind parameters");
  return;
 }

 // Bind result
 if (mysql_stmt_bind_result(stmt, result) != 0) {
  print_stmt_error(stmt, "Could not bind results");
  return;
 }

 // Set bind parameters
 myId            = id;

 // Execute!!
 if (mysql_stmt_execute(stmt) != 0) {
  print_stmt_error(stmt, "Could not execute statement");
  return;
 }

 if (mysql_stmt_store_result(stmt) != 0) {
  print_stmt_error(stmt, "Could not buffer result set");
  return;
 }

 // Init data
 (*numAddresses) = 0;

 // Fetch
 if(mysql_stmt_fetch (stmt) == 0){
  (*numAddresses) = myNumAddresses;
 }

 // Deallocate result set
 mysql_stmt_free_result(stmt); /* deallocate result set */

 // Close the statement
 mysql_stmt_close(stmt);

 // Close Database
 closeDB(conn);

同样,如果您可以使用其他一些客户端库(如 C++ 客户端),您的代码将会更短且更易读。

我的错,正如@jjmontes 提到的那样,发送的字符串似乎是在 'latin1' 中编码的。

在查询之前使用函数mysql_set_character_set(conn, "utf8")解决了这个问题。 现在,我将尝试使用 准备好的语句 而不是查询字符串。

再次感谢!