Uploaded image for project: 'MariaDB Connector/ODBC'
  1. MariaDB Connector/ODBC
  2. ODBC-492

Wrong COLUMN_SIZE reported for datetime columns

    XMLWordPrintable

Details

    • Bug
    • Status: Closed (View Workflow)
    • Major
    • Resolution: Fixed
    • 3.2.8
    • 3.1.23, 3.2.9
    • General
    • None
    • macOS Tahoe 26.4.1

    Description

      Summary

      According to the ODBC documentation for the COLUMN_SIZE column returned by SQLGetTypeInfo: "For datetime data types, this is the length incharacters of the string representation (assuming the maximum allowed precision of the fractional seconds component)." However, with the MariaDB ODBC connector:

      TYPE_NAME                         COLUMN_SIZE
      --------------------------------  -----------
      DATETIME                          16
      TIMESTAMP                         16
      

      This causes sub-second precision to be discarded for datetime objects passed as parameters using pyodbc.

      Repro

      To verify this behavior, compile and run this code, providing the connection string as the command-line argument.

      #include <stdio.h>
      #include <stdlib.h>
      #include <sql.h>
      #include <sqlext.h>
       
      static void check(SQLRETURN ret, SQLSMALLINT htype, SQLHANDLE h, const
      char* msg)
      {
          if (SQL_SUCCEEDED(ret))
              return;
          SQLCHAR state[6], text[256];
          SQLINTEGER native;
          SQLSMALLINT len;
          SQLGetDiagRec(htype, h, 1, state, &native, text, sizeof(text),
      &len);
          fprintf(stderr, "ERROR %s: %s (%s, native=%d)\n", msg, text, state,
      native);
          exit(1);
      }
       
      int main(int argc, char* argv[])
      {
          if (argc < 2) {
              fprintf(stderr, "Usage: %s <connection-string>\n", argv[0]);
              return 1;
          }
       
          printf("sizeof(SQL_TIMESTAMP_STRUCT) = %zu\n",
      sizeof(SQL_TIMESTAMP_STRUCT));
       
          SQLHENV  henv  = SQL_NULL_HANDLE;
          SQLHDBC  hdbc  = SQL_NULL_HANDLE;
          SQLHSTMT hstmt = SQL_NULL_HANDLE;
          SQLRETURN ret;
       
          ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
          check(ret, SQL_HANDLE_ENV, henv, "SQLAllocHandle(ENV)");
       
          ret = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
      (SQLPOINTER)SQL_OV_ODBC3, 0);
          check(ret, SQL_HANDLE_ENV, henv, "SQLSetEnvAttr");
       
          ret = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
          check(ret, SQL_HANDLE_ENV, henv, "SQLAllocHandle(DBC)");
       
          ret = SQLDriverConnect(hdbc, NULL, (SQLCHAR*)argv[1], SQL_NTS,
                                 NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
          check(ret, SQL_HANDLE_DBC, hdbc, "SQLDriverConnect");
       
          ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
          check(ret, SQL_HANDLE_DBC, hdbc, "SQLAllocHandle(STMT)");
       
          ret = SQLGetTypeInfo(hstmt, SQL_TYPE_TIMESTAMP);
          check(ret, SQL_HANDLE_STMT, hstmt, "SQLGetTypeInfo");
       
          printf("%-32s  %s\n", "TYPE_NAME", "COLUMN_SIZE");
          printf("%-32s  %s\n", "--------------------------------",
      "-----------");
       
          while (SQL_SUCCEEDED(SQLFetch(hstmt)))
          {
              SQLCHAR  type_name[128];
              SQLLEN   type_name_ind;
              SQLINTEGER column_size;
              SQLLEN   column_size_ind;
       
              ret = SQLGetData(hstmt, 1, SQL_C_CHAR, type_name,
      sizeof(type_name), &type_name_ind);
              check(ret, SQL_HANDLE_STMT, hstmt, "SQLGetData(TYPE_NAME)");
       
              ret = SQLGetData(hstmt, 3, SQL_C_SLONG, &column_size,
      sizeof(column_size), &column_size_ind);
              check(ret, SQL_HANDLE_STMT, hstmt, "SQLGetData(COLUMN_SIZE)");
       
              if (column_size_ind == SQL_NULL_DATA)
                  printf("%-32s  NULL\n", type_name);
              else
                  printf("%-32s  %d\n", type_name, column_size);
          }
       
          SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
          SQLDisconnect(hdbc);
          SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
          SQLFreeHandle(SQL_HANDLE_ENV, henv);
          return 0;
      }
      

      You can also see the effect of the bug with this Python script:

      import argparse
      import datetime
      import pyodbc
       
      VALUE = datetime.datetime(2000, 1, 2, 3, 4, 5, 123456)
       
      parser = argparse.ArgumentParser()
      parser.add_argument("conn")
      args = parser.parse_args()
      conn = pyodbc.connect(args.conn)
      cursor = conn.cursor()
      cursor.execute("CREATE TEMPORARY TABLE truncated_datetime_repro (dt
      datetime(6))")
      cursor.execute("INSERT INTO truncated_datetime_repro VALUES (?)",
      VALUE)
      cursor.execute("SELECT dt FROM truncated_datetime_repro")
      print("expected:", VALUE)
      print("     got:", cursor.fetchval())
      

      Output

      expected: 2000-01-02 03:04:05.123456
           got: 2000-01-02 03:04:05
      

      Recommendation

      Return 26 for the COLUMN_SIZE, reflecting the MariaDB documentation which says that

      MariaDB displays DATETIME values in 'YYYY-MM-DD HH:MM:SS.ffffff' format.

      Attachments

        Issue Links

          Activity

            People

              Lawrin Lawrin Novitsky
              bkline Bob Kline
              Votes:
              0 Vote for this issue
              Watchers:
              2 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.