Getting a primitive numeric value from "DatumGetNumeric"?

Started by Demitri Munaabout 8 years ago6 messagesgeneral
Jump to latest
#1Demitri Muna
postgresql@demitri.com

Hi,

I’m writing a C extension for PostgreSQL. One possible input datatype for my function is a numeric array, e.g. ARRAY[[1.5,2.5],[3.5,4.5]]. I can use “DatumGetNumeric” to extract a “Numeric” data type from the data, but at some point I need to convert this to a number (e.g. double) so that I can do mathy things with it. How does one convert a “Numeric” to, say, a double?

I have a workaround in that I can pass this to my function:

ARRAY[[1.5,2.5],[3.5,4.5]]::float8[]

but I’d rather have the code do that instead of bothering the user to remember that.

Thanks,
Demitri

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Demitri Muna (#1)
Re: Getting a primitive numeric value from "DatumGetNumeric"?

Demitri Muna <postgresql@demitri.com> writes:

I’m writing a C extension for PostgreSQL. One possible input datatype for my function is a numeric array, e.g. ARRAY[[1.5,2.5],[3.5,4.5]]. I can use “DatumGetNumeric” to extract a “Numeric” data type from the data, but at some point I need to convert this to a number (e.g. double) so that I can do mathy things with it. How does one convert a “Numeric” to, say, a double?

If you want to work with doubles, why don't you declare the function as
taking doubles?

I have a workaround in that I can pass this to my function:
ARRAY[[1.5,2.5],[3.5,4.5]]::float8[]
but I’d rather have the code do that instead of bothering the user to remember that.

Well, the implicit coercions work in your favor in this particular case.
You can just do, eg,

regression=# create function foo(float8[]) returns float8 as
regression-# 'select $1[1]' language sql;
CREATE FUNCTION
regression=# select foo(array[1.1,1.2]);
foo
-----
1.1
(1 row)

or to emphasize that it is doing a conversion:

regression=# select foo(array[1.1,1.2]::numeric[]);
foo
-----
1.1
(1 row)

regards, tom lane

#3Demitri Muna
postgresql@demitri.com
In reply to: Tom Lane (#2)
Re: Getting a primitive numeric value from "DatumGetNumeric"?

Hi Tom,

On Feb 20, 2018, at 10:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Well, the implicit coercions work in your favor in this particular case.

Ah, I wasn’t aware of implicit coercion. Yes, that solves the problem perfectly, thanks.

Out of curiosity, how does one read a numeric type from within a C extension (i.e. get a number value out of the Datum type)? I ask as I was unable to find an example and there are a few open questions on Stack Overflow (e.g. https://stackoverflow.com/questions/12588554/postgres-c-function-passing-returning-numerics).

Thanks,
Demitri

#4Michael Paquier
michael@paquier.xyz
In reply to: Demitri Muna (#3)
Re: Getting a primitive numeric value from "DatumGetNumeric"?

On Thu, Feb 22, 2018 at 08:00:45PM -0500, Demitri Muna wrote:

Ah, I wasn’t aware of implicit coercion. Yes, that solves the problem perfectly, thanks.

Out of curiosity, how does one read a numeric type from within a C
extension (i.e. get a number value out of the Datum type)? I ask as I
was unable to find an example and there are a few open questions on
Stack Overflow
(e.g. https://stackoverflow.com/questions/12588554/postgres-c-function-passing-returning-numerics).

PG_GETARG_NUMERIC(), no? When working on implementing your own data
types or when hacking out functions which manipulate arguments of an
existing datatype, looking at the input and output functions help a
lot. In your case, numeric_in and numeric_out in
src/backend/utils/adt/numeric.c is full of hints.
--
Michael

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Michael Paquier (#4)
Re: Getting a primitive numeric value from "DatumGetNumeric"?

Michael Paquier <michael@paquier.xyz> writes:

On Thu, Feb 22, 2018 at 08:00:45PM -0500, Demitri Muna wrote:

Out of curiosity, how does one read a numeric type from within a C
extension (i.e. get a number value out of the Datum type)?

PG_GETARG_NUMERIC(), no?

I think the core point here is that PG's "numeric" type *isn't* any
primitive C type; it's a variable-length BCD encoding. If you want
to deal with it you can, but you must spend some time reading
src/backend/utils/adt/numeric.c, as Michael suggests. If you just
want a convenient C approximation, then work with float8 and let
the implicit-coercion machinery do the conversion for you.

regards, tom lane

#6Demitri Muna
postgresql@demitri.com
In reply to: Michael Paquier (#4)
Re: Getting a primitive numeric value from "DatumGetNumeric"?

Hi,

On Feb 22, 2018, at 9:31 PM, Michael Paquier <michael@paquier.xyz> wrote:

PG_GETARG_NUMERIC(), no?

That function returns an object of datatype “Numeric” which still requires some (not immediately obvious) conversation to a double (or whatever primitive C type).

When working on implementing your own data
types or when hacking out functions which manipulate arguments of an
existing datatype, looking at the input and output functions help a
lot. In your case, numeric_in and numeric_out in
src/backend/utils/adt/numeric.c is full of hints.

I spent an hour diving into the code out of curiosity. I found useful functions like this:

double numeric_to_double_no_overflow(Numeric n)

They’re available from the PostgreSQL main source code, but not exposed in the public headers. (Maybe I was missing something.) There was enough there where I could see a way to copy/paste or otherwise link to those methods, but as Tom pointed out, implicit coercion handles what I need so I’ll stick with that.

Cheers,
Demitri