Hello community,

here is the log from the commit of package python-pyodbc for openSUSE:Factory 
checked in at 2019-08-13 13:23:37
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyodbc (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyodbc.new.9556 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyodbc"

Tue Aug 13 13:23:37 2019 rev:5 rq:722652 version:4.0.27

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyodbc/python-pyodbc.changes      
2019-03-11 11:17:57.397284479 +0100
+++ /work/SRC/openSUSE:Factory/.python-pyodbc.new.9556/python-pyodbc.changes    
2019-08-13 13:23:38.853380757 +0200
@@ -1,0 +2,13 @@
+Mon Aug  5 18:35:48 UTC 2019 - Dirk Hartmann <[email protected]>
+
+- Update to version 4.0.27: 
+  * Use int instead of bigint when possible (based on size of data) 
+    to work with drivers that don't support bigint at all.
+  * Support SQL Server datetime2 precision. Previously more data 
+    was passed than the column precision causing an error.
+  * Make Informix unit tests work again.
+  * Correct encoding error on big-endian machines for connection 
+    errors. Default to native UTF16 instead of UTF16-LE.
+  * Fix MySQL unit tests.
+
+-------------------------------------------------------------------

Old:
----
  pyodbc-4.0.26.tar.gz

New:
----
  pyodbc-4.0.27.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyodbc.spec ++++++
--- /var/tmp/diff_new_pack.2E6cz4/_old  2019-08-13 13:23:39.285380643 +0200
+++ /var/tmp/diff_new_pack.2E6cz4/_new  2019-08-13 13:23:39.289380642 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-pyodbc
-Version:        4.0.26
+Version:        4.0.27
 Release:        0
 Summary:        Python ODBC API
 License:        MIT

++++++ pyodbc-4.0.26.tar.gz -> pyodbc-4.0.27.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/PKG-INFO new/pyodbc-4.0.27/PKG-INFO
--- old/pyodbc-4.0.26/PKG-INFO  2019-02-23 20:17:56.000000000 +0100
+++ new/pyodbc-4.0.27/PKG-INFO  2019-07-31 05:14:55.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyodbc
-Version: 4.0.26
+Version: 4.0.27
 Summary: DB API Module for ODBC
 Home-page: https://github.com/mkleehammer/pyodbc
 Author: Michael Kleehammer
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/pyodbc.egg-info/PKG-INFO 
new/pyodbc-4.0.27/pyodbc.egg-info/PKG-INFO
--- old/pyodbc-4.0.26/pyodbc.egg-info/PKG-INFO  2019-02-23 20:17:55.000000000 
+0100
+++ new/pyodbc-4.0.27/pyodbc.egg-info/PKG-INFO  2019-07-31 05:14:54.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyodbc
-Version: 4.0.26
+Version: 4.0.27
 Summary: DB API Module for ODBC
 Home-page: https://github.com/mkleehammer/pyodbc
 Author: Michael Kleehammer
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/pyodbc.egg-info/SOURCES.txt 
new/pyodbc-4.0.27/pyodbc.egg-info/SOURCES.txt
--- old/pyodbc-4.0.26/pyodbc.egg-info/SOURCES.txt       2019-02-23 
20:17:56.000000000 +0100
+++ new/pyodbc-4.0.27/pyodbc.egg-info/SOURCES.txt       2019-07-31 
05:14:55.000000000 +0200
@@ -55,6 +55,8 @@
 tests3/accesstests.py
 tests3/dbapi20.py
 tests3/dbapitests.py
+tests3/empty.accdb
+tests3/empty.mdb
 tests3/exceltests.py
 tests3/informixtests.py
 tests3/mysqltests.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/src/errors.cpp 
new/pyodbc-4.0.27/src/errors.cpp
--- old/pyodbc-4.0.26/src/errors.cpp    2019-01-30 05:14:36.000000000 +0100
+++ new/pyodbc-4.0.27/src/errors.cpp    2019-07-31 05:14:16.000000000 +0200
@@ -289,9 +289,9 @@
         // Not always NULL terminated (MS Access)
         sqlstateT[5] = 0;
 
-        // For now, default to UTF-16LE if this is not in the context of a 
connection.
+        // For now, default to UTF-16 if this is not in the context of a 
connection.
         // Note that this will not work if the DM is using a different wide 
encoding (e.g. UTF-32).
-        const char *unicode_enc = conn ? conn->metadata_enc.name : "utf-16-le";
+        const char *unicode_enc = conn ? conn->metadata_enc.name : 
ENCSTR_UTF16NE;
         Object msgStr(PyUnicode_Decode((char*)szMsg, cchMsg * 
sizeof(ODBCCHAR), unicode_enc, "strict"));
 
         if (cchMsg != 0 && msgStr.Get())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/src/params.cpp 
new/pyodbc-4.0.27/src/params.cpp
--- old/pyodbc-4.0.26/src/params.cpp    2019-02-23 19:31:35.000000000 +0100
+++ new/pyodbc-4.0.27/src/params.cpp    2019-07-31 05:14:16.000000000 +0200
@@ -20,6 +20,7 @@
 #include "row.h"
 #include <datetime.h>
 
+
 inline Connection* GetConnection(Cursor* cursor)
 {
     return (Connection*)cursor->cnxn;
@@ -400,7 +401,12 @@
         pts->hour = PyDateTime_DATE_GET_HOUR(cell);
         pts->minute = PyDateTime_DATE_GET_MINUTE(cell);
         pts->second = PyDateTime_DATE_GET_SECOND(cell);
-        pts->fraction = PyDateTime_DATE_GET_MICROSECOND(cell) * 1000;
+
+        // Truncate the fraction according to precision
+        long fast_pow10[] = 
{1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
+        SQLUINTEGER milliseconds = PyDateTime_DATE_GET_MICROSECOND(cell) * 
1000;
+        pts->fraction = milliseconds - (milliseconds % fast_pow10[9 - 
pi->DecimalDigits]);
+
         *outbuf += sizeof(SQL_TIMESTAMP_STRUCT);
         ind = sizeof(SQL_TIMESTAMP_STRUCT);
     }
@@ -634,7 +640,7 @@
     Py_ssize_t cb = PyBytes_GET_SIZE(param);
 
     info.ValueType  = SQL_C_BINARY;
-    info.ColumnSize = isTVP?0:(SQLUINTEGER)max(cb, 1);
+    info.ColumnSize = isTVP ? 0 : (SQLUINTEGER)max(cb, 1);
 
     SQLLEN maxlength = cur->cnxn->GetMaxLength(info.ValueType);
 
@@ -670,7 +676,7 @@
 
     Py_ssize_t cch = PyString_GET_SIZE(param);
 
-    info.ColumnSize = isTVP?0:(SQLUINTEGER)max(cch, 1);
+    info.ColumnSize = isTVP ? 0 : (SQLUINTEGER)max(cch, 1);
 
     Object encoded;
 
@@ -740,20 +746,20 @@
     }
 
     Py_ssize_t cb = PyBytes_GET_SIZE(encoded);
-    
+
     int denom = 1;
-    
-    if(enc.optenc == OPTENC_UTF16)
+
+    if (enc.optenc == OPTENC_UTF16)
     {
         denom = 2;
     }
-    else if(enc.optenc == OPTENC_UTF32)
+    else if (enc.optenc == OPTENC_UTF32)
     {
         denom = 4;
     }
-    
-    info.ColumnSize = isTVP?0:(SQLUINTEGER)max(cb / denom, 1);
-    
+
+    info.ColumnSize = isTVP ? 0 : (SQLUINTEGER)max(cb / denom, 1);
+
     info.pObject = encoded.Detach();
 
     SQLLEN maxlength = cur->cnxn->GetMaxLength(enc.ctype);
@@ -851,85 +857,67 @@
     return true;
 }
 
+
+inline bool NeedsBigInt(PyObject* p)
+{
+    // NOTE: Smallest 32-bit int should be -214748368 but the MS compiler 
v.1900 AMD64
+    // says that (10 < -2147483648).  Perhaps I miscalculated the minimum?
+    long long ll = PyLong_AsLongLong(p);
+    return ll < -2147483647 || ll > 2147483647;
+}
+
 #if PY_MAJOR_VERSION < 3
 static bool GetIntInfo(Cursor* cur, Py_ssize_t index, PyObject* param, 
ParamInfo& info, bool isTVP)
 {
-    if(isTVP)
+    if (isTVP || NeedsBigInt(param))
     {
-        PyErr_Clear();
         info.Data.i64 = (INT64)PyLong_AsLongLong(param);
-        if (!PyErr_Occurred())
-        {
-            info.ValueType         = SQL_C_SBIGINT;
-            info.ParameterType     = SQL_BIGINT;
-            info.ParameterValuePtr = &info.Data.i64;
-            info.StrLen_or_Ind     = 8;
-            
-            return true;
-        }
-        
-        return false;
+
+        info.ValueType         = SQL_C_SBIGINT;
+        info.ParameterType     = SQL_BIGINT;
+        info.ParameterValuePtr = &info.Data.i64;
+        info.StrLen_or_Ind     = 8;
+    }
+    else
+    {
+        info.Data.l = PyLong_AsLong(param);
+
+        info.ValueType         = SQL_C_LONG;
+        info.ParameterType     = SQL_INTEGER;
+        info.ParameterValuePtr = &info.Data.l;
+        info.StrLen_or_Ind     = 4;
     }
 
-    info.Data.i64 = (INT64)PyLong_AsLongLong(param);
-    
-#if LONG_BIT == 64
-    info.ValueType     = SQL_C_SBIGINT;
-    // info.ValueType     = SQL_C_LONG;
-    info.ParameterType = SQL_BIGINT;
-    info.StrLen_or_Ind = 8;
-#elif LONG_BIT == 32
-    info.ValueType     = SQL_C_LONG;
-    info.ParameterType = SQL_INTEGER;
-    info.StrLen_or_Ind = 4;
-#else
-    #error Unexpected LONG_BIT value
-#endif
-    info.ParameterValuePtr = &info.Data.l;
     return true;
 }
 #endif
 
 static bool GetLongInfo(Cursor* cur, Py_ssize_t index, PyObject* param, 
ParamInfo& info, bool isTVP)
 {
-    // Try to use integer when possible.  BIGINT is not always supported and 
is a "special
-    // case" for some drivers.
+    // Since some drivers like Access don't support BIGINT, we use INTEGER 
when possible.
+    // Unfortunately this may mean that we end up with two execution plans for 
the same SQL.
+    // We could use SQLDescribeParam but that's kind of expensive.
 
-    // REVIEW: C & C++ now have constants for max sizes, but I'm not sure if 
they will be
-    // available on all platforms Python supports right now.  It would be more 
performant when
-    // a lot of 64-bit values are used since we could avoid the AsLong call.
-
-    // It's not clear what the maximum SQL_INTEGER should be.  The Microsoft 
documentation says
-    // it is a 'long int', but some drivers run into trouble at high values.  
We'll use
-    // SQL_INTEGER as an optimization for smaller values and rely on BIGINT
-
-    INT64 val = (INT64)PyLong_AsLongLong(param);
-    
-    if (!PyErr_Occurred() && !isTVP && (val >= -2147483648 && val <= 
2147483647))
+    if (isTVP || NeedsBigInt(param))
     {
-        info.Data.l = PyLong_AsLong(param);
-        if (!PyErr_Occurred())
-        {
-            info.ValueType         = SQL_C_LONG;
-            info.ParameterType     = SQL_INTEGER;
-            info.ParameterValuePtr = &info.Data.l;
-            info.StrLen_or_Ind     = 4;
-        }
+        info.Data.i64 = (INT64)PyLong_AsLongLong(param);
+
+        info.ValueType         = SQL_C_SBIGINT;
+        info.ParameterType     = SQL_BIGINT;
+        info.ParameterValuePtr = &info.Data.i64;
+        info.StrLen_or_Ind     = 8;
     }
     else
     {
-        PyErr_Clear();
-        info.Data.i64 = (INT64)PyLong_AsLongLong(param);
-        if (!PyErr_Occurred())
-        {
-            info.ValueType         = SQL_C_SBIGINT;
-            info.ParameterType     = SQL_BIGINT;
-            info.ParameterValuePtr = &info.Data.i64;
-            info.StrLen_or_Ind     = 8;
-        }
+        info.Data.l = PyLong_AsLong(param);
+
+        info.ValueType         = SQL_C_LONG;
+        info.ParameterType     = SQL_INTEGER;
+        info.ParameterValuePtr = &info.Data.l;
+        info.StrLen_or_Ind     = 4;
     }
 
-    return !PyErr_Occurred();
+    return true;
 }
 
 static bool GetFloatInfo(Cursor* cur, Py_ssize_t index, PyObject* param, 
ParamInfo& info)
@@ -1153,7 +1141,7 @@
     Py_ssize_t cb = PyByteArray_Size(param);
 
     SQLLEN maxlength = cur->cnxn->GetMaxLength(info.ValueType);
-    
+
     if (maxlength == 0 || cb <= maxlength || isTVP)
     {
         info.ParameterType     = SQL_VARBINARY;
@@ -1299,40 +1287,40 @@
 
 static bool getObjectValue(PyObject *pObject, long& nValue)
 {
-       if (pObject == NULL)
-               return false;
+  if (pObject == NULL)
+    return false;
 
 #if PY_MAJOR_VERSION < 3
-       if (PyInt_Check(pObject))
-       {
-               nValue = PyInt_AS_LONG(pObject);
-               return true;
-       }
+  if (PyInt_Check(pObject))
+  {
+    nValue = PyInt_AS_LONG(pObject);
+    return true;
+  }
 
 #endif
-       if (PyLong_Check(pObject))
-       {
-               nValue = PyLong_AsLong(pObject);
-               return true;
-       }
+  if (PyLong_Check(pObject))
+  {
+    nValue = PyLong_AsLong(pObject);
+    return true;
+  }
 
-       return false;
+  return false;
 }
 
 static long getSequenceValue(PyObject *pSequence, Py_ssize_t nIndex, long 
nDefault, bool &bChanged)
 {
-       PyObject *obj;
-       long v = nDefault;
+  PyObject *obj;
+  long v = nDefault;
 
-       obj = PySequence_GetItem(pSequence, nIndex);
-       if (obj != NULL)
-       {
-               if (getObjectValue(obj, v))
-                       bChanged = true;
-       }
-       Py_CLEAR(obj);
+  obj = PySequence_GetItem(pSequence, nIndex);
+  if (obj != NULL)
+  {
+    if (getObjectValue(obj, v))
+      bChanged = true;
+  }
+  Py_CLEAR(obj);
 
-       return v;
+  return v;
 }
 
 /**
@@ -1344,47 +1332,47 @@
  */
 static bool UpdateParamInfo(Cursor* pCursor, Py_ssize_t nIndex, ParamInfo 
*pInfo)
 {
-       if (pCursor->inputsizes == NULL || nIndex >= 
PySequence_Length(pCursor->inputsizes))
-               return false;
+  if (pCursor->inputsizes == NULL || nIndex >= 
PySequence_Length(pCursor->inputsizes))
+    return false;
+
+  PyObject *desc = PySequence_GetItem(pCursor->inputsizes, nIndex);
+  if (desc == NULL)
+    return false;
 
-       PyObject *desc = PySequence_GetItem(pCursor->inputsizes, nIndex);
-       if (desc == NULL)
-               return false;
-
-       bool rc = false;
-       long v;
-       bool clearError = true;
-
-       // If the error was already set before we entered here, it is not from 
us, so we leave it alone.
-       if (PyErr_Occurred())
-               clearError = false;
-
-       // integer - sets colsize
-       // type object - sets sqltype (mapping between Python and SQL types is 
not 1:1 so it may not always work)
-       // Consider: sequence of (colsize, sqltype, scale)
-       if (getObjectValue(desc, v))
-       {
-               pInfo->ColumnSize = (SQLULEN)v;
-               rc = true;
-       }
-       else if (PySequence_Check(desc))
-       {
-               pInfo->ParameterType = (SQLSMALLINT)getSequenceValue(desc, 0, 
(long)pInfo->ParameterType, rc);
-               pInfo->ColumnSize = (SQLUINTEGER)getSequenceValue(desc, 1, 
(long)pInfo->ColumnSize, rc);
-               pInfo->DecimalDigits = (SQLSMALLINT)getSequenceValue(desc, 2, 
(long)pInfo->ColumnSize, rc);
-       }
-
-       Py_CLEAR(desc);
-
-       // If the user didn't provide the full array (in case he gave us an 
array), the above code would
-       // set an internal error on the cursor object, as we try to read three 
values from an array
-       // which may not have as many. This is ok, because we don't really care 
if the array is not completly
-       // specified, so we clear the error in case it comes from this. If the 
error was already present before that
-       // we keep it, so the user can handle it.
-       if (clearError)
-               PyErr_Clear();
+  bool rc = false;
+  long v;
+  bool clearError = true;
+
+  // If the error was already set before we entered here, it is not from us, 
so we leave it alone.
+  if (PyErr_Occurred())
+    clearError = false;
+
+  // integer - sets colsize
+  // type object - sets sqltype (mapping between Python and SQL types is not 
1:1 so it may not always work)
+  // Consider: sequence of (colsize, sqltype, scale)
+  if (getObjectValue(desc, v))
+  {
+    pInfo->ColumnSize = (SQLULEN)v;
+    rc = true;
+  }
+  else if (PySequence_Check(desc))
+  {
+    pInfo->ParameterType = (SQLSMALLINT)getSequenceValue(desc, 0, 
(long)pInfo->ParameterType, rc);
+    pInfo->ColumnSize = (SQLUINTEGER)getSequenceValue(desc, 1, 
(long)pInfo->ColumnSize, rc);
+    pInfo->DecimalDigits = (SQLSMALLINT)getSequenceValue(desc, 2, 
(long)pInfo->ColumnSize, rc);
+  }
+
+  Py_CLEAR(desc);
+
+  // If the user didn't provide the full array (in case he gave us an array), 
the above code would
+  // set an internal error on the cursor object, as we try to read three 
values from an array
+  // which may not have as many. This is ok, because we don't really care if 
the array is not completly
+  // specified, so we clear the error in case it comes from this. If the error 
was already present before that
+  // we keep it, so the user can handle it.
+  if (clearError)
+    PyErr_Clear();
 
-       return rc;
+  return rc;
 }
 
 bool BindParameter(Cursor* cur, Py_ssize_t index, ParamInfo& info)
@@ -1395,13 +1383,13 @@
 
     if (UpdateParamInfo(cur, index, &info))
     {
-               // Reload in case it has changed.
-               colsize = info.ColumnSize;
-               sqltype = info.ParameterType;
-               scale = info.DecimalDigits;
+    // Reload in case it has changed.
+    colsize = info.ColumnSize;
+    sqltype = info.ParameterType;
+    scale = info.DecimalDigits;
     }
 
-       TRACE("BIND: param=%ld ValueType=%d (%s) ParameterType=%d (%s) 
ColumnSize=%ld DecimalDigits=%d BufferLength=%ld *pcb=%ld\n",
+  TRACE("BIND: param=%ld ValueType=%d (%s) ParameterType=%d (%s) 
ColumnSize=%ld DecimalDigits=%d BufferLength=%ld *pcb=%ld\n",
           (index+1), info.ValueType, CTypeName(info.ValueType), sqltype, 
SqlTypeName(sqltype), colsize,
           scale, info.BufferLength, info.StrLen_or_Ind);
 
@@ -1451,7 +1439,7 @@
                 err = 1;
                 break;
             }
-            if(ncols && ncols != PySequence_Size(row))
+            if (ncols && ncols != PySequence_Size(row))
             {
                 RaiseErrorV(0, ProgrammingError, "A TVP's rows must all be the 
same size.");
                 err = 1;
@@ -1699,11 +1687,11 @@
     }
     memset(cur->paramInfos, 0, sizeof(ParamInfo) * cur->paramcount);
 
-       // Describe each parameter (SQL type) in preparation for allocation of 
paramset array
+  // Describe each parameter (SQL type) in preparation for allocation of 
paramset array
     for (Py_ssize_t i = 0; i < cur->paramcount; i++)
     {
-               SQLSMALLINT nullable;
-        if(!SQL_SUCCEEDED(SQLDescribeParam(cur->hstmt, i + 1, 
&(cur->paramInfos[i].ParameterType),
+        SQLSMALLINT nullable;
+        if (!SQL_SUCCEEDED(SQLDescribeParam(cur->hstmt, i + 1, 
&(cur->paramInfos[i].ParameterType),
             &cur->paramInfos[i].ColumnSize, &cur->paramInfos[i].DecimalDigits,
             &nullable)))
         {
@@ -1713,11 +1701,11 @@
             cur->paramInfos[i].DecimalDigits = 0;
         }
 
-               // This supports overriding of input sizes via setinputsizes
-               // See issue 380
-               // The logic is duplicated from BindParameter
-               UpdateParamInfo(cur, i, &cur->paramInfos[i]);
-       }
+    // This supports overriding of input sizes via setinputsizes
+    // See issue 380
+    // The logic is duplicated from BindParameter
+    UpdateParamInfo(cur, i, &cur->paramInfos[i]);
+  }
 
     PyObject *rowseq = PySequence_Fast(paramArrayObj, "Parameter array must be 
a sequence.");
     if (!rowseq)
@@ -2030,7 +2018,7 @@
 
     Py_XDECREF(rowseq);
     FreeParameterData(cur);
-       return ret;
+  return ret;
 }
 
 static bool GetParamType(Cursor* cur, Py_ssize_t index, SQLSMALLINT& type)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/tests2/accesstests.py 
new/pyodbc-4.0.27/tests2/accesstests.py
--- old/pyodbc-4.0.26/tests2/accesstests.py     2019-01-30 05:14:36.000000000 
+0100
+++ new/pyodbc-4.0.27/tests2/accesstests.py     2019-07-31 05:14:16.000000000 
+0200
@@ -9,25 +9,25 @@
 installed into the Python directories.  You must run python setup.py build
 before running the tests.
 
-To run, pass the filename of an Access database on the command line:
+To run, pass the file EXTENSION of an Access database on the command line:
 
-  accesstests test.accdb
+  accesstests accdb
 
-An empty Access 2000 database (empty.mdb) and an empty Access 2007 database
-(empty.accdb), are provided.
+An empty Access 2000 database (empty.mdb) or an empty Access 2007 database
+(empty.accdb), are automatically created for the tests.
 
 To run a single test, use the -t option:
 
-  accesstests test.accdb -t unicode_null
+  accesstests -t unicode_null accdb
 
 If you want to report an error, it would be helpful to include the driver 
information
 by using the verbose flag and redirecting the output to a file:
 
- accesstests test.accdb -v >& results.txt
+ accesstests -v accdb >& results.txt
 
 You can pass the verbose flag twice for more verbose output:
 
- accesstests test.accdb -vv
+ accesstests -vv accdb
 """
 
 # Access SQL data types: http://msdn2.microsoft.com/en-us/library/bb208866.aspx
@@ -36,7 +36,8 @@
 import unittest
 from decimal import Decimal
 from datetime import datetime, date, time
-from os.path import abspath
+from os.path import abspath, dirname, join
+import shutil
 from testutils import *
 
 CNXNSTRING = None
@@ -77,6 +78,11 @@
         self.cnxn   = pyodbc.connect(CNXNSTRING)
         self.cursor = self.cnxn.cursor()
 
+        # 
https://docs.microsoft.com/en-us/sql/odbc/microsoft/desktop-database-driver-performance-issues?view=sql-server-2017
+        #
+        # As of the 4.0 drivers, you have to send as Unicode?
+        self.cnxn.setencoding(str, encoding='utf-16le')
+
         for i in range(3):
             try:
                 self.cursor.execute("drop table t%d" % i)
@@ -428,10 +434,7 @@
         self.assertEqual(False, result)
 
     def test_guid(self):
-        # REVIEW: Python doesn't (yet) have a UUID type so the value is 
returned as a string.  Access, however, only
-        # really supports Unicode.  For now, we'll have to live with this 
difference.  All strings in Python 3.x will
-        # be Unicode -- pyodbc 3.x will have different defaults.
-        value = "de2ac9c6-8676-4b0b-b8a6-217a8580cbee"
+        value = u"de2ac9c6-8676-4b0b-b8a6-217a8580cbee"
         self.cursor.execute("create table t1(g1 uniqueidentifier)")
         self.cursor.execute("insert into t1 values (?)", value)
         v = self.cursor.execute("select * from t1").fetchone()[0]
@@ -620,32 +623,36 @@
 
 
 def main():
-    from optparse import OptionParser
-    parser = OptionParser(usage=usage)
-    parser.add_option("-v", "--verbose", default=0, action="count", 
help="Increment test verbosity (can be used multiple times)")
-    parser.add_option("-d", "--debug", action="store_true", default=False, 
help="Print debugging items")
-    parser.add_option("-t", "--test", help="Run only the named test")
-
-    (options, args) = parser.parse_args()
-
-    if len(args) != 1:
-        parser.error('dbfile argument required')
-
-    if args[0].endswith('.accdb'):
-        driver = 'Microsoft Access Driver (*.mdb, *.accdb)'
-    else:
-        driver = 'Microsoft Access Driver (*.mdb)'
+    from argparse import ArgumentParser
+    parser = ArgumentParser(usage=usage)
+    parser.add_argument("-v", "--verbose", default=0, action="count", 
help="Increment test verbosity (can be used multiple times)")
+    parser.add_argument("-d", "--debug", action="store_true", default=False, 
help="Print debugging items")
+    parser.add_argument("-t", "--test", help="Run only the named test")
+    parser.add_argument('type', choices=['accdb', 'mdb'], help='Which type of 
file to test')
+
+    args = parser.parse_args()
+
+    DRIVERS = {
+        'accdb': 'Microsoft Access Driver (*.mdb, *.accdb)',
+        'mdb': 'Microsoft Access Driver (*.mdb)'
+    }
+
+    here = dirname(abspath(__file__))
+    src = join(here, 'empty.' + args.type)
+    dest = join(here, 'test.' + args.type)
+    shutil.copy(src, dest)
 
     global CNXNSTRING
-    CNXNSTRING = 'DRIVER={%s};DBQ=%s;ExtendedAnsiSQL=1' % (driver, 
abspath(args[0]))
+    CNXNSTRING = 'DRIVER={%s};DBQ=%s;ExtendedAnsiSQL=1' % (DRIVERS[args.type], 
dest)
+    print(CNXNSTRING)
 
     cnxn = pyodbc.connect(CNXNSTRING)
     print_library_info(cnxn)
     cnxn.close()
 
-    suite = load_tests(AccessTestCase, options.test)
+    suite = load_tests(AccessTestCase, args.test)
 
-    testRunner = unittest.TextTestRunner(verbosity=options.verbose)
+    testRunner = unittest.TextTestRunner(verbosity=args.verbose)
     result = testRunner.run(suite)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/tests2/mysqltests.py 
new/pyodbc-4.0.27/tests2/mysqltests.py
--- old/pyodbc-4.0.26/tests2/mysqltests.py      2019-01-30 05:14:36.000000000 
+0100
+++ new/pyodbc-4.0.27/tests2/mysqltests.py      2019-07-31 05:14:16.000000000 
+0200
@@ -4,14 +4,18 @@
 usage = """\
 usage: %prog [options] connection_string
 
-Unit tests for MySQL.  To use, pass a connection string as the parameter.  The 
tests will create and drop tables t1 and
-t2 as necessary.  The default installation of mysql allows you to connect 
locally with no password and already contains
-a 'test' database, so you can probably use the following.  (Update the driver 
name as appropriate.)
-
-  ./mysqltests DRIVER={MySQL};DATABASE=test
+Unit tests for MySQL.  To use, pass a connection string as the parameter.
+The tests will create and drop tables t1 and t2 as necessary.
 
 These tests use the pyodbc library from the build directory, not the version 
installed in your
 Python directories.  You must run `python setup.py build` before running these 
tests.
+
+You can also put the connection string into a tmp/setup.cfg file like so:
+
+  [mysqltests]
+  connection-string=DRIVER=MySQL ODBC 8.0 Unicode 
Driver;charset=utf8mb4;SERVER=localhost;DATABASE=pyodbc;UID=root;PWD=rootpw
+
+Note: Include charset=utf8mb4 in the connection string so the high-Unicode 
tests won't fail.
 """
 
 import sys, os, re
@@ -689,9 +693,9 @@
         #
         # http://www.fileformat.info/info/unicode/char/1f31c/index.htm
 
-        v = "x \U0001F31C z"
+        v = u"x \U0001F31C z"
 
-        self.cursor.execute("create table t1(s varchar(100))")
+        self.cursor.execute("CREATE TABLE t1(s varchar(100)) DEFAULT 
CHARSET=utf8mb4")
         self.cursor.execute("insert into t1 values (?)", v)
 
         result = self.cursor.execute("select s from t1").fetchone()[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/tests2/sqlservertests.py 
new/pyodbc-4.0.27/tests2/sqlservertests.py
--- old/pyodbc-4.0.26/tests2/sqlservertests.py  2019-02-23 19:31:35.000000000 
+0100
+++ new/pyodbc-4.0.27/tests2/sqlservertests.py  2019-07-31 05:14:16.000000000 
+0200
@@ -458,6 +458,18 @@
         self.cursor.executemany(sql, params)
         self.assertEqual(self.cursor.execute("SELECT txt FROM 
#issue295").fetchval(), v)
 
+    def test_fast_executemany_to_datetime2(self):
+        if self.handle_known_issues_for('freetds', print_reminder=True):
+            warn('FREETDS_KNOWN_ISSUE - test_fast_executemany_to_datetime2: 
test cancelled.')
+            return
+        v = datetime(2019, 3, 12, 10, 0, 0, 123456)
+        self.cursor.execute("CREATE TABLE ##issue540 (dt2 DATETIME2(2))")
+        sql = "INSERT INTO ##issue540 (dt2) VALUES (?)"
+        params = [(v,)]
+        self.cursor.fast_executemany = True
+        self.cursor.executemany(sql, params)
+        self.assertEqual(self.cursor.execute("SELECT CAST(dt2 AS VARCHAR) FROM 
##issue540").fetchval(), '2019-03-12 10:00:00.12')
+
     #
     # binary
     #
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/tests2/testutils.py 
new/pyodbc-4.0.27/tests2/testutils.py
--- old/pyodbc-4.0.26/tests2/testutils.py       2017-08-12 22:24:51.000000000 
+0200
+++ new/pyodbc-4.0.27/tests2/testutils.py       2019-07-31 05:14:16.000000000 
+0200
@@ -50,9 +50,13 @@
     cursor = cnxn.cursor()
     for typename in ['VARCHAR', 'WVARCHAR', 'BINARY']:
         t = getattr(pyodbc, 'SQL_' + typename)
-        cursor.getTypeInfo(t)
-        row = cursor.fetchone()
-        print('Max %s = %s' % (typename, row and row[2] or '(not supported)'))
+        try:
+            cursor.getTypeInfo(t)
+        except pyodbc.Error as e:
+            print('Max %s = (not supported)' % (typename, ))
+        else:
+            row = cursor.fetchone()
+            print('Max %s = %s' % (typename, row and row[2] or '(not 
supported)'))
 
     if platform.system() == 'Windows':
         print('         %s' % ' '.join([s for s in platform.win32_ver() if s]))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/tests3/accesstests.py 
new/pyodbc-4.0.27/tests3/accesstests.py
--- old/pyodbc-4.0.26/tests3/accesstests.py     2019-01-30 05:14:36.000000000 
+0100
+++ new/pyodbc-4.0.27/tests3/accesstests.py     2019-07-31 05:14:16.000000000 
+0200
@@ -9,25 +9,25 @@
 installed into the Python directories.  You must run python setup.py build
 before running the tests.
 
-To run, pass the filename of an Access database on the command line:
+To run, pass the file EXTENSION of an Access database on the command line:
 
-  accesstests test.accdb
+  accesstests accdb
 
-An empty Access 2000 database (empty.mdb) and an empty Access 2007 database
-(empty.accdb), are provided.
+An empty Access 2000 database (empty.mdb) or an empty Access 2007 database
+(empty.accdb), are automatically created for the tests.
 
 To run a single test, use the -t option:
 
-  accesstests test.accdb -t unicode_null
+  accesstests -t unicode_null accdb
 
 If you want to report an error, it would be helpful to include the driver 
information
 by using the verbose flag and redirecting the output to a file:
 
- accesstests test.accdb -v >& results.txt
+ accesstests -v accdb >& results.txt
 
 You can pass the verbose flag twice for more verbose output:
 
- accesstests test.accdb -vv
+ accesstests -vv accdb
 """
 
 # Access SQL data types: http://msdn2.microsoft.com/en-us/library/bb208866.aspx
@@ -36,7 +36,8 @@
 import unittest
 from decimal import Decimal
 from datetime import datetime, date, time
-from os.path import abspath
+from os.path import abspath, dirname, join
+import shutil
 from testutils import *
 
 CNXNSTRING = None
@@ -394,10 +395,7 @@
         self.assertEqual(False, result)
 
     def test_guid(self):
-        # REVIEW: Python doesn't (yet) have a UUID type so the value is 
returned as a string.  Access, however, only
-        # really supports Unicode.  For now, we'll have to live with this 
difference.  All strings in Python 3.x will
-        # be Unicode -- pyodbc 3.x will have different defaults.
-        value = "de2ac9c6-8676-4b0b-b8a6-217a8580cbee"
+        value = u"de2ac9c6-8676-4b0b-b8a6-217a8580cbee"
         self.cursor.execute("create table t1(g1 uniqueidentifier)")
         self.cursor.execute("insert into t1 values (?)", value)
         v = self.cursor.execute("select * from t1").fetchone()[0]
@@ -586,35 +584,36 @@
 
 
 def main():
-    from optparse import OptionParser
-    parser = OptionParser(usage=usage)
-    parser.add_option("-v", "--verbose", default=0, action="count", 
help="Increment test verbosity (can be used multiple times)")
-    parser.add_option("-d", "--debug", action="store_true", default=False, 
help="Print debugging items")
-    parser.add_option("-t", "--test", help="Run only the named test")
-
-    (options, args) = parser.parse_args()
-
-    if len(args) != 1:
-        parser.error('dbfile argument required')
-
-    if args[0].endswith('.accdb'):
-        driver = 'Microsoft Access Driver (*.mdb, *.accdb)'
-    else:
-        driver = 'Microsoft Access Driver (*.mdb, *.accdb)'
-        # driver = 'Microsoft Access Driver (*.mdb)'
+    from argparse import ArgumentParser
+    parser = ArgumentParser(usage=usage)
+    parser.add_argument("-v", "--verbose", default=0, action="count", 
help="Increment test verbosity (can be used multiple times)")
+    parser.add_argument("-d", "--debug", action="store_true", default=False, 
help="Print debugging items")
+    parser.add_argument("-t", "--test", help="Run only the named test")
+    parser.add_argument('type', choices=['accdb', 'mdb'], help='Which type of 
file to test')
+
+    args = parser.parse_args()
+
+    DRIVERS = {
+        'accdb': 'Microsoft Access Driver (*.mdb, *.accdb)',
+        'mdb': 'Microsoft Access Driver (*.mdb)'
+    }
+
+    here = dirname(abspath(__file__))
+    src = join(here, 'empty.' + args.type)
+    dest = join(here, 'test.' + args.type)
+    shutil.copy(src, dest)
 
     global CNXNSTRING
-    CNXNSTRING = 'DRIVER={%s};DBQ=%s;ExtendedAnsiSQL=1' % (driver, 
abspath(args[0]))
-
+    CNXNSTRING = 'DRIVER={%s};DBQ=%s;ExtendedAnsiSQL=1' % (DRIVERS[args.type], 
dest)
     print(CNXNSTRING)
 
     cnxn = pyodbc.connect(CNXNSTRING)
     print_library_info(cnxn)
     cnxn.close()
 
-    suite = load_tests(AccessTestCase, options.test)
+    suite = load_tests(AccessTestCase, args.test)
 
-    testRunner = unittest.TextTestRunner(verbosity=options.verbose)
+    testRunner = unittest.TextTestRunner(verbosity=args.verbose)
     result = testRunner.run(suite)
 
 
Binary files old/pyodbc-4.0.26/tests3/empty.accdb and 
new/pyodbc-4.0.27/tests3/empty.accdb differ
Binary files old/pyodbc-4.0.26/tests3/empty.mdb and 
new/pyodbc-4.0.27/tests3/empty.mdb differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/tests3/mysqltests.py 
new/pyodbc-4.0.27/tests3/mysqltests.py
--- old/pyodbc-4.0.26/tests3/mysqltests.py      2019-01-30 05:14:36.000000000 
+0100
+++ new/pyodbc-4.0.27/tests3/mysqltests.py      2019-07-31 05:14:16.000000000 
+0200
@@ -4,11 +4,8 @@
 usage = """\
 usage: %prog [options] connection_string
 
-Unit tests for MySQL.  To use, pass a connection string as the parameter.  The 
tests will create and drop tables t1 and
-t2 as necessary.  The default installation of mysql allows you to connect 
locally with no password and already contains
-a 'test' database, so you can probably use the following.  (Update the driver 
name as appropriate.)
-
-  ./mysqltests DRIVER={MySQL};DATABASE=test
+Unit tests for MySQL.  To use, pass a connection string as the parameter.
+The tests will create and drop tables t1 and t2 as necessary.
 
 These tests use the pyodbc library from the build directory, not the version 
installed in your
 Python directories.  You must run `python setup.py build` before running these 
tests.
@@ -16,7 +13,9 @@
 You can also put the connection string into a tmp/setup.cfg file like so:
 
   [mysqltests]
-  connection-string=DRIVER={MySQL};SERVER=localhost;UID=uid;PWD=pwd;DATABASE=db
+  connection-string=DRIVER=MySQL ODBC 8.0 Unicode 
Driver;charset=utf8mb4;SERVER=localhost;DATABASE=pyodbc;UID=root;PWD=rootpw
+
+Note: Include charset=utf8mb4 in the connection string so the high-Unicode 
tests won't fail.
 """
 
 import sys, os, re
@@ -722,7 +721,7 @@
 
         v = "x \U0001F31C z"
 
-        self.cursor.execute("create table t1(s varchar(100))")
+        self.cursor.execute("CREATE TABLE t1(s varchar(100)) DEFAULT 
CHARSET=utf8mb4")
         self.cursor.execute("insert into t1 values (?)", v)
 
         result = self.cursor.execute("select s from t1").fetchone()[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyodbc-4.0.26/tests3/sqlservertests.py 
new/pyodbc-4.0.27/tests3/sqlservertests.py
--- old/pyodbc-4.0.26/tests3/sqlservertests.py  2019-02-23 19:55:23.000000000 
+0100
+++ new/pyodbc-4.0.27/tests3/sqlservertests.py  2019-07-31 05:14:16.000000000 
+0200
@@ -450,6 +450,18 @@
         self.cursor.executemany(sql, params)
         self.assertEqual(self.cursor.execute("SELECT txt FROM 
#issue295").fetchval(), v)
 
+    def test_fast_executemany_to_datetime2(self):
+        if self.handle_known_issues_for('freetds', print_reminder=True):
+            warn('FREETDS_KNOWN_ISSUE - test_fast_executemany_to_datetime2: 
test cancelled.')
+            return
+        v = datetime(2019, 3, 12, 10, 0, 0, 123456)
+        self.cursor.execute("CREATE TABLE ##issue540 (dt2 DATETIME2(2))")
+        sql = "INSERT INTO ##issue540 (dt2) VALUES (?)"
+        params = [(v,)]
+        self.cursor.fast_executemany = True
+        self.cursor.executemany(sql, params)
+        self.assertEqual(self.cursor.execute("SELECT CAST(dt2 AS VARCHAR) FROM 
##issue540").fetchval(), '2019-03-12 10:00:00.12')
+
     #
     # binary
     #


Reply via email to