From f3f0e76220171584f24e2b43e8f005b526e479d6 Mon Sep 17 00:00:00 2001 From: Dirk Butson Date: Tue, 22 Apr 2025 20:18:15 -0400 Subject: [PATCH 1/2] In order to support a type column being returned from engine. A TypeObject is needed instead of returning None. As None is used to verify that the type returned is in the TYPEMAP. cursor.execute('select NULL from DUAL') fails without this change. I don't know how TypeObject is used an rather there is a side-effect by using TypeObject(None) and any use of TypeObject. --- pynuodb/datatype.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynuodb/datatype.py b/pynuodb/datatype.py index 62ad2ac..510382e 100644 --- a/pynuodb/datatype.py +++ b/pynuodb/datatype.py @@ -154,8 +154,9 @@ def __cmp__(self, other): NUMBER = TypeObject(int, decimal.Decimal) DATETIME = TypeObject(Timestamp, Date, Time) ROWID = TypeObject() +NULL = TypeObject(None) -TYPEMAP = {"": None, +TYPEMAP = {"": NULL, "string": STRING, "char": STRING, "varchar": STRING, From 07e593ae11a49e3c9e30505bf697c3acacfb5741 Mon Sep 17 00:00:00 2001 From: Dirk Butson Date: Tue, 22 Apr 2025 21:01:17 -0400 Subject: [PATCH 2/2] A change to the field returned from the TE to use for column_name. column_name does not seem to be what we want, use column_label instead. Or the order of these two fields are incorrect from the order that engine is sending the data. Test case nuodb_description_name_test.py shows failure before this change. --- pynuodb/encodedsession.py | 6 +++--- tests/nuodb_description_name_test.py | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 tests/nuodb_description_name_test.py diff --git a/pynuodb/encodedsession.py b/pynuodb/encodedsession.py index 9aa5c20..5e7db3f 100644 --- a/pynuodb/encodedsession.py +++ b/pynuodb/encodedsession.py @@ -469,8 +469,8 @@ def _parse_result_set_description(self): self.getString() # catalog_name self.getString() # schema_name self.getString() # table_name - column_name = self.getString() - self.getString() # column_label + self.getString() # column_name + column_label = self.getString() # column_label self.getValue() # collation_sequence column_type_name = self.getString() self.getInt() # column_type @@ -481,7 +481,7 @@ def _parse_result_set_description(self): # TODO: type information should be derived from the type # (column_type) not the typename. - description[i] = [column_name, + description[i] = [column_label, datatype.TypeObjectFromNuodb(column_type_name), column_display_size, None, precision, scale, None] diff --git a/tests/nuodb_description_name_test.py b/tests/nuodb_description_name_test.py new file mode 100644 index 0000000..1129486 --- /dev/null +++ b/tests/nuodb_description_name_test.py @@ -0,0 +1,27 @@ +""" +(C) Copyright 2025 Dassault Systemes SE. All Rights Reserved. + +This software is licensed under a BSD 3-Clause License. +See the LICENSE file provided with this software. +""" + +import decimal +import datetime + +from . import nuodb_base + + +class TestNuoDBDescription(nuodb_base.NuoBase): + + def test_description(self): + con = self._connect() + cursor = con.cursor() + + cursor.execute("CREATE TEMPORARY TABLE tmp (v1 INTEGER, v2 STRING)" ) + cursor.execute("INSERT INTO tmp VALUES (1,'a'), (2,'b')") + cursor.execute("SELECT v1 AS c1, concat(v2,'') as c2 FROM tmp") + row = cursor.fetchone() + d = cursor.description + + assert d[0][0].lower() == 'c1' + assert d[1][0].lower() == 'c2'