summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpython/expected/plpython_types.out49
-rw-r--r--src/pl/plpython/expected/plpython_types_3.out49
-rw-r--r--src/pl/plpython/plpy_typeio.c42
-rw-r--r--src/pl/plpython/sql/plpython_types.sql9
4 files changed, 121 insertions, 28 deletions
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index 46413455c8b..edc51423e96 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -213,36 +213,69 @@ CONTEXT: PL/Python function "test_type_conversion_int8"
(1 row)
CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
-plpy.info(x, type(x))
+# print just the class name, not the type, to avoid differences
+# between decimal and cdecimal
+plpy.info(x, x.__class__.__name__)
return x
$$ LANGUAGE plpythonu;
-/* The current implementation converts numeric to float. */
SELECT * FROM test_type_conversion_numeric(100);
-INFO: (100.0, <type 'float'>)
+INFO: (Decimal('100'), 'Decimal')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
- 100.0
+ 100
(1 row)
SELECT * FROM test_type_conversion_numeric(-100);
-INFO: (-100.0, <type 'float'>)
+INFO: (Decimal('-100'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ -100
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(100.0);
+INFO: (Decimal('100.0'), 'Decimal')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
- -100.0
+ 100.0
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(100.00);
+INFO: (Decimal('100.00'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ 100.00
(1 row)
SELECT * FROM test_type_conversion_numeric(5000000000.5);
-INFO: (5000000000.5, <type 'float'>)
+INFO: (Decimal('5000000000.5'), 'Decimal')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
5000000000.5
(1 row)
+SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
+INFO: (Decimal('1234567890.0987654321'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ 1234567890.0987654321
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
+INFO: (Decimal('-1234567890.0987654321'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ -1234567890.0987654321
+(1 row)
+
SELECT * FROM test_type_conversion_numeric(null);
-INFO: (None, <type 'NoneType'>)
+INFO: (None, 'NoneType')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
index 511ef5a4c9a..11c4c478c4f 100644
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ b/src/pl/plpython/expected/plpython_types_3.out
@@ -213,36 +213,69 @@ CONTEXT: PL/Python function "test_type_conversion_int8"
(1 row)
CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
-plpy.info(x, type(x))
+# print just the class name, not the type, to avoid differences
+# between decimal and cdecimal
+plpy.info(x, x.__class__.__name__)
return x
$$ LANGUAGE plpython3u;
-/* The current implementation converts numeric to float. */
SELECT * FROM test_type_conversion_numeric(100);
-INFO: (100.0, <class 'float'>)
+INFO: (Decimal('100'), 'Decimal')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
- 100.0
+ 100
(1 row)
SELECT * FROM test_type_conversion_numeric(-100);
-INFO: (-100.0, <class 'float'>)
+INFO: (Decimal('-100'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ -100
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(100.0);
+INFO: (Decimal('100.0'), 'Decimal')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
- -100.0
+ 100.0
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(100.00);
+INFO: (Decimal('100.00'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ 100.00
(1 row)
SELECT * FROM test_type_conversion_numeric(5000000000.5);
-INFO: (5000000000.5, <class 'float'>)
+INFO: (Decimal('5000000000.5'), 'Decimal')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
5000000000.5
(1 row)
+SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
+INFO: (Decimal('1234567890.0987654321'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ 1234567890.0987654321
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
+INFO: (Decimal('-1234567890.0987654321'), 'Decimal')
+CONTEXT: PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric
+------------------------------
+ -1234567890.0987654321
+(1 row)
+
SELECT * FROM test_type_conversion_numeric(null);
-INFO: (None, <class 'NoneType'>)
+INFO: (None, 'NoneType')
CONTEXT: PL/Python function "test_type_conversion_numeric"
test_type_conversion_numeric
------------------------------
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 6a9a2cb9747..caccbf9b88f 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -16,6 +16,7 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/numeric.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -35,7 +36,7 @@ static void PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup);
static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
static PyObject *PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d);
static PyObject *PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d);
-static PyObject *PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d);
static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
@@ -450,7 +451,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
arg->func = PLyFloat_FromFloat8;
break;
case NUMERICOID:
- arg->func = PLyFloat_FromNumeric;
+ arg->func = PLyDecimal_FromNumeric;
break;
case INT2OID:
arg->func = PLyInt_FromInt16;
@@ -516,16 +517,37 @@ PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d)
}
static PyObject *
-PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d)
+PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d)
{
- /*
- * Numeric is cast to a PyFloat: This results in a loss of precision Would
- * it be better to cast to PyString?
- */
- Datum f = DirectFunctionCall1(numeric_float8, d);
- double x = DatumGetFloat8(f);
+ static PyObject *decimal_constructor;
+ char *str;
+ PyObject *pyvalue;
+
+ /* Try to import cdecimal. If it doesn't exist, fall back to decimal. */
+ if (!decimal_constructor)
+ {
+ PyObject *decimal_module;
+
+ decimal_module = PyImport_ImportModule("cdecimal");
+ if (!decimal_module)
+ {
+ PyErr_Clear();
+ decimal_module = PyImport_ImportModule("decimal");
+ }
+ if (!decimal_module)
+ PLy_elog(ERROR, "could not import a module for Decimal constructor");
+
+ decimal_constructor = PyObject_GetAttrString(decimal_module, "Decimal");
+ if (!decimal_constructor)
+ PLy_elog(ERROR, "no Decimal attribute in module");
+ }
+
+ str = DatumGetCString(DirectFunctionCall1(numeric_out, d));
+ pyvalue = PyObject_CallFunction(decimal_constructor, "s", str);
+ if (!pyvalue)
+ PLy_elog(ERROR, "conversion from numeric to Decimal failed");
- return PyFloat_FromDouble(x);
+ return pyvalue;
}
static PyObject *
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 6a50b4236db..68818807299 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -86,14 +86,19 @@ SELECT * FROM test_type_conversion_int8(null);
CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
-plpy.info(x, type(x))
+# print just the class name, not the type, to avoid differences
+# between decimal and cdecimal
+plpy.info(x, x.__class__.__name__)
return x
$$ LANGUAGE plpythonu;
-/* The current implementation converts numeric to float. */
SELECT * FROM test_type_conversion_numeric(100);
SELECT * FROM test_type_conversion_numeric(-100);
+SELECT * FROM test_type_conversion_numeric(100.0);
+SELECT * FROM test_type_conversion_numeric(100.00);
SELECT * FROM test_type_conversion_numeric(5000000000.5);
+SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
+SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
SELECT * FROM test_type_conversion_numeric(null);