diff --git a/doc/src/conf.py b/doc/src/conf.py index 1196747d..e4994cd8 100644 --- a/doc/src/conf.py +++ b/doc/src/conf.py @@ -42,7 +42,7 @@ # The short X.Y version. version = '7.2' # The full version, including alpha/beta/rc tags. -release = '7.2.2' +release = '7.2.3' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: diff --git a/doc/src/release_notes.rst b/doc/src/release_notes.rst index d2f98182..f882efe1 100644 --- a/doc/src/release_notes.rst +++ b/doc/src/release_notes.rst @@ -5,6 +5,21 @@ cx_Oracle Release Notes ======================= +Version 7.2.3 (October 2019) +---------------------------- + +#) Updated embedded ODPI-C to `version 3.2.2 + `__. +#) Restored support for setting numeric bind variables with boolean values. +#) Ensured that sharding keys are dedicated to the connection that is acquired + using them in order to avoid possible hangs, crashes or unusual errors. +#) Corrected support for PLS_INTEGER and BINARY_INTEGER types when used in + PL/SQL records + (`ODPI-C issue 112 `__). +#) Improved documentation. + + Version 7.2.2 (August 2019) --------------------------- diff --git a/doc/src/user_guide/batch_statement.rst b/doc/src/user_guide/batch_statement.rst index 9456354f..82d5c0ab 100644 --- a/doc/src/user_guide/batch_statement.rst +++ b/doc/src/user_guide/batch_statement.rst @@ -234,3 +234,53 @@ the maximum size of the strings that will be processed is 20 characters. Since cx_Oracle allocates memory for each row based on this value, it is best not to oversize it. The first parameter of ``None`` tells cx_Oracle that its default processing will be sufficient. + +Loading CSV Files into Oracle Database +====================================== + +The :meth:`Cursor.executemany()` method and `csv module +`__ can be used to +efficiently load CSV (Comma Separated Values) files. For example, consider the +file ``data.csv``:: + + 101,Abel + 154,Baker + 132,Charlie + 199,Delta + . . . + +And the schema: + +.. code-block:: sql + + create table test (id number, name varchar2(25)); + +Instead of looping through each line of the CSV file and inserting it +individually, you can insert batches of records using +:meth:`Cursor.executemany()`: + +.. code-block:: python + + import cx_Oracle + import csv + + . . . + + # Predefine the memory areas to match the table definition + cursor.setinputsizes(None, 25) + + # Adjust the batch size to meet your memory and performance requirements + batch_size = 10000 + + with open('testsp.csv', 'r') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + sql = "insert into test (id,name) values (:1, :2)" + data = [] + for line in csv_reader: + data.append((line[0], line[1])) + if len(data) % batch_size == 0: + cursor.executemany(sql, data) + data = [] + if data: + cursor.executemany(sql, data) + con.commit() diff --git a/doc/src/user_guide/connection_handling.rst b/doc/src/user_guide/connection_handling.rst index 3fa76b55..22fc9615 100644 --- a/doc/src/user_guide/connection_handling.rst +++ b/doc/src/user_guide/connection_handling.rst @@ -435,9 +435,9 @@ same (and `increment` equal to zero). the firewall, `resource manager or user profile `IDLE_TIME `__ should not expire idle sessions. This avoids connection storms which can -decrease throughput. See `About Optimizing Real-World Performance with Static -Connection Pools -`__, +decrease throughput. See `Guideline for Preventing Connection Storms: Use +Static Pools +`__, which contains details about sizing of pools. Session CallBacks for Setting Pooled Connection State diff --git a/doc/src/user_guide/sql_execution.rst b/doc/src/user_guide/sql_execution.rst index 8a789aaa..e72e231e 100644 --- a/doc/src/user_guide/sql_execution.rst +++ b/doc/src/user_guide/sql_execution.rst @@ -48,9 +48,9 @@ optionally :ref:`overridden `. .. IMPORTANT:: Interpolating or concatenating user data with SQL statements, for example - ``sql = 'SELECT * FROM mytab WHERE mycol = ' + myvar``, is a security risk + ``cur.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")``, is a security risk and impacts performance. Use :ref:`bind variables ` instead. For - example, ``sql = 'SELECT * FROM mytab WHERE mycol = :mybv``. + example, ``cur.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)``. .. _fetching: @@ -338,9 +338,11 @@ object that is returned by default. Python types can be changed with Changing Fetched Data Types with Output Type Handlers ----------------------------------------------------- -Sometimes the default conversion from Oracle Database type to Python type must -be changed in order to prevent data loss or to fit the purposes of the Python -application. In such cases, an output type handler can be specified. +Sometimes the default conversion from an Oracle Database type to a Python type +must be changed in order to prevent data loss or to fit the purposes of the +Python application. In such cases, an output type handler can be specified for +queries. Output type handlers do not affect values returned from +:meth:`Cursor.callfunc()` or :meth:`Cursor.callproc()`. Output type handlers can be specified on the :attr:`connection ` or on the :attr:`cursor diff --git a/odpi b/odpi index 21b1888f..f355af15 160000 --- a/odpi +++ b/odpi @@ -1 +1 @@ -Subproject commit 21b1888f6ae1357d039fdbc4882fd537dd1c6130 +Subproject commit f355af1591ee41799af5ed24f5cc26e4528463f8 diff --git a/setup.py b/setup.py index 8743830e..f2ec1a80 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ from distutils.extension import Extension # define build constants -BUILD_VERSION = "7.2.2" +BUILD_VERSION = "7.2.3" # setup extra link and compile args extraLinkArgs = [] diff --git a/src/cxoTransform.c b/src/cxoTransform.c index 0dc2c81a..f02e7af1 100644 --- a/src/cxoTransform.c +++ b/src/cxoTransform.c @@ -274,22 +274,28 @@ int cxoTransform_fromPython(cxoTransformNum transformNum, case CXO_TRANSFORM_INT: case CXO_TRANSFORM_DECIMAL: case CXO_TRANSFORM_FLOAT: - if (!PyFloat_Check(pyValue) && + if (PyBool_Check(pyValue)) { + buffer->ptr = (pyValue == Py_True) ? "1" : "0"; + buffer->size = 1; + buffer->numCharacters = 1; + } else { + if (!PyFloat_Check(pyValue) && #if PY_MAJOR_VERSION < 3 - !PyInt_Check(pyValue) && + !PyInt_Check(pyValue) && #endif - !PyLong_Check(pyValue) && - !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal)) { - PyErr_SetString(PyExc_TypeError, "expecting number"); - return -1; + !PyLong_Check(pyValue) && + !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal)) { + PyErr_SetString(PyExc_TypeError, "expecting number"); + return -1; + } + textValue = PyObject_Str(pyValue); + if (!textValue) + return -1; + status = cxoBuffer_fromObject(buffer, textValue, encoding); + Py_DECREF(textValue); + if (status < 0) + return -1; } - textValue = PyObject_Str(pyValue); - if (!textValue) - return -1; - status = cxoBuffer_fromObject(buffer, textValue, encoding); - Py_DECREF(textValue); - if (status < 0) - return -1; dbValue->asBytes.ptr = (char*) buffer->ptr; dbValue->asBytes.length = buffer->size; return 0; diff --git a/test/NumberVar.py b/test/NumberVar.py index 31de4cab..4be4928b 100644 --- a/test/NumberVar.py +++ b/test/NumberVar.py @@ -50,6 +50,18 @@ def testBindBoolean(self): (True,)) self.assertEqual(result, "TRUE") + def testBindBooleanAsNumber(self): + "test binding in a boolean as a number" + var = self.cursor.var(cx_Oracle.NUMBER) + var.setvalue(0, True) + self.cursor.execute("select :1 from dual", [var]) + result, = self.cursor.fetchone() + self.assertEqual(result, 1) + var.setvalue(0, False) + self.cursor.execute("select :1 from dual", [var]) + result, = self.cursor.fetchone() + self.assertEqual(result, 0) + def testBindDecimal(self): "test binding in a decimal.Decimal" self.cursor.execute("""