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

Parameter array operation inserts wrong values in autoincrement field if indicator was specified

Details

    Description

      If we have auto_increment field in the table, and we insert there a few rows using parameter array, and we have indicator array set for the auto_increment field, and for some row indicator value is set to STMT_INDICATOR_NULL or STMT_INDICATOR_IGNORE, for all subsequent rows server insert values like if same indicator value was set for them, too. i.e. it auto-incremented values instead of values specified in bound array.

      I don't think C/C can be here to blame - I traced execution, and things look sane. Besides same error is not exposed with 10.2 server.

      I am not sure if that is "major" issue, as this does not look like very probable scenario, but on other hand c/odbc testcases have it. Besides I don't think it's very hard to fix.

      Below is short program to repeat it

      #include <stdio.h>
      #include <stdarg.h>
      #include <stdlib.h>
      #include <string.h>
      #include <mysql.h>
       
      #ifndef OK
      # define OK 0
      #endif
      #ifndef FAIL
      # define FAIL 1
      #endif
      #ifndef SKIP
      # define SKIP -1
      #endif
      #ifndef FALSE
      # define FALSE 0
      #endif
      #ifndef TRUE
      # define TRUE 1
      #endif
       
      void
      diag(char const *fmt, ...)
      {
        va_list ap;
        va_start(ap, fmt);
        fprintf(stdout, "# ");
        vfprintf(stdout, fmt, ap);
        fprintf(stdout, "\n");
        va_end(ap);
      }
       
      #define check_stmt_rc(_rc, _stmt) \
      if (_rc)\
      {\
        diag("Error: [%s] %s(%d) (%s: %d)", mysql_stmt_sqlstate(_stmt), mysql_stmt_error(_stmt), mysql_stmt_errno(_stmt), __FILE__, __LINE__);\
        return(FAIL);\
      }
       
      #define check_mysql_rc(rc, mysql) \
      if (rc)\
      {\
        diag("Error (%d): %s (%d) in %s line %d", rc, mysql_error(mysql), \
             mysql_errno(mysql), __FILE__, __LINE__);\
        return(FAIL);\
      }
       
      #define FAIL_IF(expr, reason)\
      if (expr)\
      {\
        diag("Error: %s (%s: %d)", (reason) ? reason : "", __FILE__, __LINE__);\
        return FAIL;\
      }
       
       
      int main(int argc, char *argv)
      {
        MYSQL      *ma;
        MYSQL_STMT *stmt;
        MYSQL_BIND bind[1];
        MYSQL_RES  *res;
        MYSQL_ROW  row;
        char       indicator[]= {0, STMT_INDICATOR_NULL, 0/*STMT_INDICATOR_IGNORE*/};
        my_bool   error[1];
        int        i, id[]= {2, 3, 777}, param_count= 1, count= sizeof(id)/sizeof(id[0]);
        my_bool    UpdateMaxLength= 1;
         
        ma = mysql_init(NULL);
        if (!mysql_real_connect(ma, "localhost", "root", "", "test", 3306, NULL, 0))
        {
          printf("Could not connect: %s\n", mysql_error(ma));
          exit(1);
        }
        else
        {
          printf("Server info %s\nClient info: %s\n",
            mysql_get_server_info(ma), mysql_get_client_info());
        }
       
        stmt = mysql_stmt_init(ma);
       
        if (!stmt)
        {
          diag("Could not init stmt handler(s)");
          return FAIL;
        }
       
        check_mysql_rc(mysql_query(ma, "DROP TABLE IF EXISTS ai_field_value"), ma);
        check_mysql_rc(mysql_query(ma, "CREATE TABLE ai_field_value (id int not null primary key auto_increment)"), ma);
        check_stmt_rc(mysql_stmt_prepare(stmt, "INSERT INTO ai_field_value(id) values(?)", -1), stmt);
       
        memset(bind, 0, sizeof(bind));
        bind[0].buffer_type = MYSQL_TYPE_LONG;
        bind[0].buffer = (void *)id;
        bind[0].buffer_length = 0;
        bind[0].is_null = NULL;
        bind[0].length = NULL;
        bind[0].error = error;
        bind[0].u.indicator= indicator;
       
        mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
        check_stmt_rc(mysql_stmt_bind_param(stmt, bind), stmt);
       
        check_stmt_rc(mysql_stmt_execute(stmt), stmt);
       
        mysql_stmt_close(stmt);
       
        check_mysql_rc(mysql_query(ma, "SELECT id FROM ai_field_value"), ma);
       
        res= mysql_store_result(ma);
        
        i= 0;
        while (row= mysql_fetch_row(res))
        {
          diag("id= %s", row[0]);
          FAIL_IF(atoi(row[0]) != id[i++], "Values is differented form the expected");
        }
        check_mysql_rc(mysql_query(ma, "DROP TABLE ai_field_value"), ma);
       
        mysql_close(ma);
        
        exit(0);
      }
      

      Attachments

        Issue Links

          Activity

            Lawrin Lawrin Novitsky created issue -
            Lawrin Lawrin Novitsky made changes -
            Field Original Value New Value
            Fix Version/s 10.3 [ 22126 ]
            serg Sergei Golubchik made changes -
            Description If we have auto_increment field in the table, and we insert there a few rows using parameter array, and we have indicator array set for the auto_increment field, and for some row indicator value is set to STMT_INDICATOR_NULL or STMT_INDICATOR_IGNORE, for all subsequent rows server insert values like if same indicator value was set for them, too. i.e. it auto-incremented values instead of values specified in bound array.

            I don't think C/C can be here to blame - I traced execution, and things look sane. Besides same error is not exposed with 10.2 server.

            I am not sure if that is "major" issue, as this does not look like very probable scenario, but on other hand c/odbc testcases have it. Besides I don't think it's very hard to fix.

            Below is short program to repeat it

            #include <stdio.h>
            #include <stdarg.h>
            #include <stdlib.h>
            #include <string.h>
            #include <mysql.h>

            #ifndef OK
            # define OK 0
            #endif
            #ifndef FAIL
            # define FAIL 1
            #endif
            #ifndef SKIP
            # define SKIP -1
            #endif
            #ifndef FALSE
            # define FALSE 0
            #endif
            #ifndef TRUE
            # define TRUE 1
            #endif

            void
            diag(char const *fmt, ...)
            {
              va_list ap;
              va_start(ap, fmt);
              fprintf(stdout, "# ");
              vfprintf(stdout, fmt, ap);
              fprintf(stdout, "\n");
              va_end(ap);
            }

            #define check_stmt_rc(_rc, _stmt) \
            if (_rc)\
            {\
              diag("Error: [%s] %s(%d) (%s: %d)", mysql_stmt_sqlstate(_stmt), mysql_stmt_error(_stmt), mysql_stmt_errno(_stmt), __FILE__, __LINE__);\
              return(FAIL);\
            }

            #define check_mysql_rc(rc, mysql) \
            if (rc)\
            {\
              diag("Error (%d): %s (%d) in %s line %d", rc, mysql_error(mysql), \
                   mysql_errno(mysql), __FILE__, __LINE__);\
              return(FAIL);\
            }

            #define FAIL_IF(expr, reason)\
            if (expr)\
            {\
              diag("Error: %s (%s: %d)", (reason) ? reason : "", __FILE__, __LINE__);\
              return FAIL;\
            }


            int main(int argc, char *argv)
            {
              MYSQL *ma;
              MYSQL_STMT *stmt;
              MYSQL_BIND bind[1];
              MYSQL_RES *res;
              MYSQL_ROW row;
              char indicator[]= {0, STMT_INDICATOR_NULL, 0/*STMT_INDICATOR_IGNORE*/};
              my_bool error[1];
              int i, id[]= {2, 3, 777}, param_count= 1, count= sizeof(id)/sizeof(id[0]);
              my_bool UpdateMaxLength= 1;
               
              ma = mysql_init(NULL);
              if (!mysql_real_connect(ma, "localhost", "root", "", "test", 3306, NULL, 0))
              {
                printf("Could not connect: %s\n", mysql_error(ma));
                exit(1);
              }
              else
              {
                printf("Server info %s\nClient info: %s\n",
                  mysql_get_server_info(ma), mysql_get_client_info());
              }

              stmt = mysql_stmt_init(ma);
             
              if (!stmt)
              {
                diag("Could not init stmt handler(s)");
                return FAIL;
              }

              check_mysql_rc(mysql_query(ma, "DROP TABLE IF EXISTS ai_field_value"), ma);
              check_mysql_rc(mysql_query(ma, "CREATE TABLE ai_field_value (id int not null primary key auto_increment)"), ma);
              check_stmt_rc(mysql_stmt_prepare(stmt, "INSERT INTO ai_field_value(id) values(?)", -1), stmt);

              memset(bind, 0, sizeof(bind));
              bind[0].buffer_type = MYSQL_TYPE_LONG;
              bind[0].buffer = (void *)id;
              bind[0].buffer_length = 0;
              bind[0].is_null = NULL;
              bind[0].length = NULL;
              bind[0].error = error;
              bind[0].u.indicator= indicator;

              mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
              check_stmt_rc(mysql_stmt_bind_param(stmt, bind), stmt);

              check_stmt_rc(mysql_stmt_execute(stmt), stmt);
             
              mysql_stmt_close(stmt);
             
              check_mysql_rc(mysql_query(ma, "SELECT id FROM ai_field_value"), ma);

              res= mysql_store_result(ma);
              
              i= 0;
              while (row= mysql_fetch_row(res))
              {
                diag("id= %s", row[0]);
                FAIL_IF(atoi(row[0]) != id[i++], "Values is differented form the expected");
              }
              check_mysql_rc(mysql_query(ma, "DROP TABLE ai_field_value"), ma);

              mysql_close(ma);
              
              exit(0);
            }
            If we have auto_increment field in the table, and we insert there a few rows using parameter array, and we have indicator array set for the auto_increment field, and for some row indicator value is set to STMT_INDICATOR_NULL or STMT_INDICATOR_IGNORE, for all subsequent rows server insert values like if same indicator value was set for them, too. i.e. it auto-incremented values instead of values specified in bound array.

            I don't think C/C can be here to blame - I traced execution, and things look sane. Besides same error is not exposed with 10.2 server.

            I am not sure if that is "major" issue, as this does not look like very probable scenario, but on other hand c/odbc testcases have it. Besides I don't think it's very hard to fix.

            Below is short program to repeat it
            {code:c}
            #include <stdio.h>
            #include <stdarg.h>
            #include <stdlib.h>
            #include <string.h>
            #include <mysql.h>

            #ifndef OK
            # define OK 0
            #endif
            #ifndef FAIL
            # define FAIL 1
            #endif
            #ifndef SKIP
            # define SKIP -1
            #endif
            #ifndef FALSE
            # define FALSE 0
            #endif
            #ifndef TRUE
            # define TRUE 1
            #endif

            void
            diag(char const *fmt, ...)
            {
              va_list ap;
              va_start(ap, fmt);
              fprintf(stdout, "# ");
              vfprintf(stdout, fmt, ap);
              fprintf(stdout, "\n");
              va_end(ap);
            }

            #define check_stmt_rc(_rc, _stmt) \
            if (_rc)\
            {\
              diag("Error: [%s] %s(%d) (%s: %d)", mysql_stmt_sqlstate(_stmt), mysql_stmt_error(_stmt), mysql_stmt_errno(_stmt), __FILE__, __LINE__);\
              return(FAIL);\
            }

            #define check_mysql_rc(rc, mysql) \
            if (rc)\
            {\
              diag("Error (%d): %s (%d) in %s line %d", rc, mysql_error(mysql), \
                   mysql_errno(mysql), __FILE__, __LINE__);\
              return(FAIL);\
            }

            #define FAIL_IF(expr, reason)\
            if (expr)\
            {\
              diag("Error: %s (%s: %d)", (reason) ? reason : "", __FILE__, __LINE__);\
              return FAIL;\
            }


            int main(int argc, char *argv)
            {
              MYSQL *ma;
              MYSQL_STMT *stmt;
              MYSQL_BIND bind[1];
              MYSQL_RES *res;
              MYSQL_ROW row;
              char indicator[]= {0, STMT_INDICATOR_NULL, 0/*STMT_INDICATOR_IGNORE*/};
              my_bool error[1];
              int i, id[]= {2, 3, 777}, param_count= 1, count= sizeof(id)/sizeof(id[0]);
              my_bool UpdateMaxLength= 1;
               
              ma = mysql_init(NULL);
              if (!mysql_real_connect(ma, "localhost", "root", "", "test", 3306, NULL, 0))
              {
                printf("Could not connect: %s\n", mysql_error(ma));
                exit(1);
              }
              else
              {
                printf("Server info %s\nClient info: %s\n",
                  mysql_get_server_info(ma), mysql_get_client_info());
              }

              stmt = mysql_stmt_init(ma);
             
              if (!stmt)
              {
                diag("Could not init stmt handler(s)");
                return FAIL;
              }

              check_mysql_rc(mysql_query(ma, "DROP TABLE IF EXISTS ai_field_value"), ma);
              check_mysql_rc(mysql_query(ma, "CREATE TABLE ai_field_value (id int not null primary key auto_increment)"), ma);
              check_stmt_rc(mysql_stmt_prepare(stmt, "INSERT INTO ai_field_value(id) values(?)", -1), stmt);

              memset(bind, 0, sizeof(bind));
              bind[0].buffer_type = MYSQL_TYPE_LONG;
              bind[0].buffer = (void *)id;
              bind[0].buffer_length = 0;
              bind[0].is_null = NULL;
              bind[0].length = NULL;
              bind[0].error = error;
              bind[0].u.indicator= indicator;

              mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
              check_stmt_rc(mysql_stmt_bind_param(stmt, bind), stmt);

              check_stmt_rc(mysql_stmt_execute(stmt), stmt);
             
              mysql_stmt_close(stmt);
             
              check_mysql_rc(mysql_query(ma, "SELECT id FROM ai_field_value"), ma);

              res= mysql_store_result(ma);
              
              i= 0;
              while (row= mysql_fetch_row(res))
              {
                diag("id= %s", row[0]);
                FAIL_IF(atoi(row[0]) != id[i++], "Values is differented form the expected");
              }
              check_mysql_rc(mysql_query(ma, "DROP TABLE ai_field_value"), ma);

              mysql_close(ma);
              
              exit(0);
            }
            {code}
            serg Sergei Golubchik made changes -
            Assignee Oleksandr Byelkin [ sanja ]
            Lawrin Lawrin Novitsky made changes -
            sanja Oleksandr Byelkin made changes -
            Sprint 10.2.13 [ 228 ]
            sanja Oleksandr Byelkin made changes -
            Sprint 10.2.13 [ 228 ]
            Lawrin Lawrin Novitsky added a comment - - edited

            I guess this bug has been fixed, since corresponding ODBC tests do not fail any more against 10.3

            Lawrin Lawrin Novitsky added a comment - - edited I guess this bug has been fixed, since corresponding ODBC tests do not fail any more against 10.3
            sanja Oleksandr Byelkin made changes -
            Affects Version/s 10.2 [ 14601 ]
            sanja Oleksandr Byelkin made changes -
            Fix Version/s 10.2 [ 14601 ]
            Lawrin Lawrin Novitsky made changes -
            Affects Version/s 10.2 [ 14601 ]

            I am just repeating once again here, that the bug was never observed with the 10.2 server, but with 10.3 server only

            Lawrin Lawrin Novitsky added a comment - I am just repeating once again here, that the bug was never observed with the 10.2 server, but with 10.3 server only
            Lawrin Lawrin Novitsky made changes -
            Fix Version/s 10.2 [ 14601 ]
            sanja Oleksandr Byelkin made changes -
            Status Open [ 1 ] In Progress [ 3 ]
            sanja Oleksandr Byelkin made changes -
            Status In Progress [ 3 ] Stalled [ 10000 ]

            It was fixed, but test suite was added

            sanja Oleksandr Byelkin added a comment - It was fixed, but test suite was added
            sanja Oleksandr Byelkin made changes -
            Fix Version/s 10.3.7 [ 23005 ]
            Fix Version/s 10.3 [ 22126 ]
            Resolution Cannot Reproduce [ 5 ]
            Status Stalled [ 10000 ] Closed [ 6 ]
            serg Sergei Golubchik made changes -
            Workflow MariaDB v3 [ 85149 ] MariaDB v4 [ 153654 ]

            People

              sanja Oleksandr Byelkin
              Lawrin Lawrin Novitsky
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

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