Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-34722

C API prepared statement not replication safe

    XMLWordPrintable

Details

    Description

      When using C API prepared statements with a client character set different from the character set used for a table column, the statement can not be replicated.

      Using the Connector/C specifically with these steps:

      • mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "gbk");
      • CREATE TABLE with a VARCHAR COLLATE utf8mb4
      • mysql_stmt_prepare, mysql_stmt_bind_param

      On the primary, the data given with mysql_stmt_bind_param will be interpreted as specified with MYSQL_SET_CHARSET_NAME, but the binary log will contain this:

      SET @@session.character_set_client=gbk,@@session.collation_connection=28,@@session.collation_server=8/*!*/;
      INSERT INTO test_charset SET c=X'2D3E20B8B1B1BE203C2D'
      

      The hexadecimal literal here will be interpreted using the column's character set (which is not valid, because this is not UTF8).

      Full C code:

      #include "stdafx.h"
      #include <assert.h>
      #include "mysql.h"
       
       
      int main(int argc, char* argv[])
      {
          int error_code;
          MYSQL*  mysql = mysql_init(NULL);
          assert(mysql != NULL);
       
          mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "gbk");
          MYSQL*  mysql2 = mysql_real_connect(mysql, "localhost", "root", "", "test", 3307, NULL, 0);
          assert(mysql2 != NULL);
          error_code = mysql_query(mysql, 
              "CREATE TABLE IF NOT EXISTS `test_charset` ("
      	    "`c` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',"
      	    "`t` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()"
              ")");
          assert(error_code == 0);
       
          MYSQL_BIND bind;
          MYSQL_STMT *stmt = mysql_stmt_init(mysql);
          assert(stmt != NULL);
          char insert_statement[] = "INSERT INTO test_charset SET c=?";
      //    char insert_statement[] = "INSERT INTO test_charset SET c=CONVERT(?, CHAR)";
          error_code = mysql_stmt_prepare(stmt, insert_statement, strlen(insert_statement));
          assert(error_code == 0);
          memset(&bind, 0, sizeof(bind));
          bind.buffer_type   = MYSQL_TYPE_STRING;
          bind.buffer        = "-> \xB8\xB1\xB1\xBE <-";
          bind.buffer_length = strlen((char*)bind.buffer);
          bind.length        = &bind.buffer_length;
          error_code = mysql_stmt_bind_param(stmt, &bind);
          assert(error_code == 0);
          error_code = mysql_stmt_execute(stmt);
          assert(error_code == 0);
       
          mysql_close(mysql);
       
      	return 0;
      }
      

      I am honestly not sure where the bug here is.

      • Is my prepared statement wrong, and I should have used CONVERT(?, CHAR) in the first place?
      • Is the Connector/C doing something wrong here?
      • Is the statement recorded in the binary log wrong?
      • Should any binary string when converted to a text string interpreted according to character_set_connection (or character_set_client)?

      Attachments

        Activity

          People

            bar Alexander Barkov
            sweiser Sebastian Weiser
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.