├── LICENCE ├── META.json ├── Makefile ├── README.decnumber ├── README.md ├── README.pavel ├── decimal--2.0.sql ├── decimal.c ├── decimal.control ├── decimal.h ├── decimal128.c └── decimal64.c /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Pavel Stehule 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | The views and conclusions contained in the software and documentation are those 27 | of the authors and should not be interpreted as representing official policies, 28 | either expressed or implied, of the FreeBSD Project. -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pgDecimal2", 3 | "abstract": "decimal64 and decimal128 data types", 4 | "description": "decimal64/128 using IEEE decimal floating point types", 5 | "version": "2.0.0", 6 | "maintainer": [ 7 | "Feng Tian " 8 | ], 9 | "license": { 10 | "PostgresSQL": "http://www.postgresql.org/about/license" 11 | }, 12 | "provides": { 13 | "pgDecimal2": { 14 | "abstract": "8 and 16 bytes IEEE decimal floating point types", 15 | "file": "decimal--2.0.sql", 16 | "version": "2.0.0" 17 | } 18 | }, 19 | "resources": { 20 | "homepage": "http://github.com/vitesse-ftian/pgdecimal/", 21 | "repository": { 22 | "url": "http://github.com/vitesse-ftian/pgdecimal.git", 23 | "web": "http://github.com/vitesse-ftian/pgdecimal/", 24 | "type": "git" 25 | } 26 | }, 27 | "generated_by": "Feng Tian", 28 | "meta-spec": { 29 | "version": "1.0.0", 30 | "url": "http://pgxn.org/meta/spec.txt" 31 | }, 32 | "tags": [ 33 | "type", 34 | "numeric", 35 | "fix length" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # contrib/decimal/Makefile 2 | 3 | MODULE_big = decimal 4 | OBJS = decimal.o decimal64.o decimal128.o 5 | PG_CPPFLAGS = -I$(TOOLCHAIN_DIR)/installed/include/decnumber 6 | SHLIB_LINK = -L$(TOOLCHAIN_DIR)/installed/lib -ldecnumber 7 | 8 | EXTENSION = decimal 9 | DATA = decimal--2.0.sql 10 | 11 | REGRESS = decimal 12 | 13 | ifdef USE_PGXS 14 | PG_CONFIG = pg_config 15 | PGXS := $(shell $(PG_CONFIG) --pgxs) 16 | include $(PGXS) 17 | else 18 | subdir = contrib/decimal 19 | top_builddir = ../.. 20 | include $(top_builddir)/src/Makefile.global 21 | include $(top_srcdir)/contrib/contrib-global.mk 22 | endif 23 | -------------------------------------------------------------------------------- /README.decnumber: -------------------------------------------------------------------------------- 1 | Backend 2 | ======= 3 | 4 | There are two well-known decimal libraries, the decNumber we used here, and one from Intel. 5 | I picked decnumber because GCC picked it, more portable (not limited to Intel). 6 | Question: How about Intel ADX? Does it help? Does decNumber generate ADX code? 7 | 8 | See http://speleotrove.com/decimal/dpintro.html for performance comparison. Overall, decnumber 9 | and Intel number are very close, and decimal64/128 are many times faster than an arbitrary 10 | precision implementation. I did not compare decNumber against numeric of postgres, but I trust 11 | both are high quality implementations and assume performance wise they are similar. 12 | 13 | INTEL library has conversion functions for decimal64/128 from/to int32/64, float/double. This 14 | native implementations could be much faster than the conversion we implemented -- print to 15 | string and parse. 16 | 17 | DecNumber Build Instructions 18 | =============================== 19 | 20 | The backend we used to implement decimal floating point is taken from 21 | http://speleotrove.com/decimal/. This site contains a huge collection of information on 22 | IEEE 754 decimal floating point format and arithmatics. 23 | 24 | After downloading, one need to build decnumber library. The following simple Makefile will do, 25 | 26 | ------------- BEGIN Makefile for decNumber --------------- 27 | OBJS = decSingle.o decDouble.o decQuad.o decNumber.o decContext.o 28 | 29 | CFLAGS = -Wall -g -O2 -fPIC 30 | 31 | libdecnumber.a: $(OBJS) 32 | ar -rcs libdecnumber.a $(OBJS) 33 | 34 | clean: 35 | rm -f libdecnumber.a $(OBJS) 36 | 37 | install: 38 | install -D *.h ../installed/include/decnumber 39 | install -D libdecnumber.a ../installed/lib 40 | ------------- END Makefile for decNumber ----------------- 41 | 42 | Then, you need to update the Makefile of decimal, pointing include dir and libdir to the right 43 | install path of decNumber, dance the usual make, make install, create extension dance. 44 | 45 | INTEL BID Library 46 | ================= 47 | 48 | NYI. Next. 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pgDecimal 2 | ========= 3 | 4 | PostgreSQL for decimal64 and decimal128. Decimal64 will have 16 precision digits 5 | and decimal128 has 34. 34 is huge. This work is inspired by the pgDecimal package 6 | by Pavel Stehule. See README.pavel for the documents from original author. 7 | It went through significant changes. 8 | 9 | We implemented decimal64 and decimal128. The backend is the decNumber library from 10 | http://speleotrove.com/decimal/. See README.decnumber for build instructions. 11 | 12 | We did not use Decimal32/64 as the original pgDecimal, because we want to access some API in 13 | decDouble/Quad. Also, decimal32 is not implemented, because it only got 7 precision digits 14 | which is too small to be useful. (The real reason, is that decSingle does not implement arithmatics :-) 15 | 16 | decimal64 is stored like INT64 and decimal128 is stored like interval type. There is 17 | no palloc in decimal64 arithmatics and exactly one palloc in decimal128 arithmatics. 18 | 19 | TODO 20 | ==== 21 | 22 | The current implementation of conversion between differnt numeric types is not efficient. 23 | We first convert the input type to cstring then cstring to decimal64/128. Type conversion/cast 24 | is quite tricky. One need to be very careful, for example, it is easy to comeup with a 25 | query that numeric type is actually faster than decimal64 because the unexpect casting cost. 26 | Also watch out sometimes actual execution on numeric type actually casted to double precision. 27 | Anyway, measure, before deploy in real system. 28 | 29 | The INTEL library has necessary converting functions for decimal64/128 from/to int64/float/double. 30 | This alone, could be the reason to use INTEL BID lib when possilbe. Will investigate/do, next. 31 | 32 | Usage Example 33 | ============= 34 | The following was on an Intel NUC, core i5 5252U @ 1.6GHz, 16GB memory, SSD. Only CPU matters. 35 | 36 | ``` 37 | ftian=# set vitesse.enable = 0; 38 | SET 39 | ftian=# \timing 40 | Timing is on. 41 | 42 | ftian=# create table tt(ii bigint, d double precision, d64 decimal64, d128 decimal128, n numeric(15, 3)); 43 | CREATE TABLE 44 | Time: 9.628 ms 45 | 46 | ftian=# insert into tt select i, i + 0.123, i + 0.123, i + 0.123, i + 0.123 from generate_series(1, 1000000) i; 47 | INSERT 0 1000000 48 | Time: 2861.325 ms 49 | 50 | ftian=# select * from tt limit 2; 51 | ii | d | d64 | d128 | n 52 | ----+-------+-------+-------+------- 53 | 1 | 1.123 | 1.123 | 1.123 | 1.123 54 | 2 | 2.123 | 2.123 | 2.123 | 2.123 55 | (2 rows) 56 | 57 | Time: 0.288 ms 58 | 59 | -- 60 | -- Simple predicates 61 | -- 62 | 63 | ftian=# set vitesse.enable = 0; 64 | SET 65 | Time: 0.112 ms 66 | ftian=# select count(*) from tt where (d + d*d + d*d*d + d*d*d*d) > 10000000; 67 | count 68 | -------- 69 | 999945 70 | (1 row) 71 | 72 | Time: 432.331 ms 73 | ftian=# select count(*) from tt where (n + n*n + n*n*n + n*n*n*n) > 10000000; 74 | count 75 | -------- 76 | 999945 77 | (1 row) 78 | 79 | Time: 1763.774 ms 80 | ftian=# select count(*) from tt where (d64 + d64*d64 + d64*d64*d64 + d64*d64*d64*d64) > 10000000; 81 | count 82 | -------- 83 | 999945 84 | (1 row) 85 | 86 | Time: 1090.795 ms 87 | ftian=# select count(*) from tt where (d128 + d128*d128 + d128*d128*d128 + d128*d128*d128*d128) > 10000000; 88 | count 89 | -------- 90 | 999945 91 | (1 row) 92 | 93 | Time: 1387.014 ms 94 | 95 | 96 | -- 97 | -- sort 98 | -- 99 | 100 | ftian=# select * from tt order by d limit 2 offset 999000; 101 | ii | d | d64 | d128 | n 102 | --------+------------+------------+------------+------------ 103 | 999001 | 999001.123 | 999001.123 | 999001.123 | 999001.123 104 | 999002 | 999002.123 | 999002.123 | 999002.123 | 999002.123 105 | (2 rows) 106 | 107 | Time: 749.732 ms 108 | ftian=# select * from tt order by n limit 2 offset 999000; 109 | ii | d | d64 | d128 | n 110 | --------+------------+------------+------------+------------ 111 | 999001 | 999001.123 | 999001.123 | 999001.123 | 999001.123 112 | 999002 | 999002.123 | 999002.123 | 999002.123 | 999002.123 113 | (2 rows) 114 | 115 | Time: 1944.858 ms 116 | ftian=# select * from tt order by d64 limit 2 offset 999000; 117 | ii | d | d64 | d128 | n 118 | --------+------------+------------+------------+------------ 119 | 999001 | 999001.123 | 999001.123 | 999001.123 | 999001.123 120 | 999002 | 999002.123 | 999002.123 | 999002.123 | 999002.123 121 | (2 rows) 122 | 123 | Time: 1579.657 ms 124 | ftian=# select * from tt order by d128 limit 2 offset 999000; 125 | ii | d | d64 | d128 | n 126 | --------+------------+------------+------------+------------ 127 | 999001 | 999001.123 | 999001.123 | 999001.123 | 999001.123 128 | 999002 | 999002.123 | 999002.123 | 999002.123 | 999002.123 129 | (2 rows) 130 | 131 | Time: 1752.646 ms 132 | 133 | -- 134 | -- Hash joinable 135 | -- 136 | 137 | ftian=# explain select count(*) from tt t1 join tt t2 on t1.d64 * t1.d64 + t1.d64 = t2.d64 + t2.d64 * t2.d64; 138 | QUERY PLAN 139 | ---------------------------------------------------------------------------------- 140 | Aggregate (cost=6875071228.00..6875071228.01 rows=1 width=0) 141 | -> Hash Join (cost=36707.00..5625071228.00 rows=500000000000 width=0) 142 | Hash Cond: (((t1.d64 * t1.d64) + t1.d64) = (t2.d64 + (t2.d64 * t2.d64))) 143 | -> Seq Scan on tt t1 (cost=0.00..20300.00 rows=1000000 width=8) 144 | -> Hash (cost=20300.00..20300.00 rows=1000000 width=8) 145 | -> Seq Scan on tt t2 (cost=0.00..20300.00 rows=1000000 width=8) 146 | (6 rows) 147 | 148 | Time: 0.754 ms 149 | 150 | ftian=# select count(*) from tt t1 join tt t2 on t1.d64 * t1.d64 + t1.d64 = t2.d64 + t2.d64 * t2.d64; 151 | count 152 | --------- 153 | 1000000 154 | (1 row) 155 | 156 | Time: 1659.609 ms 157 | ftian=# select count(*) from tt t1 join tt t2 on t1.n * t1.n + t1.n = t2.n + t2.n * t2.n; 158 | count 159 | --------- 160 | 1000000 161 | (1 row) 162 | 163 | Time: 2212.401 ms 164 | 165 | -- 166 | -- nestedloop, just for fun 167 | -- 168 | ftian=# select count(*) from tt t1, tt t2 where t1.ii < 10000 and t2.ii < 10000 and t1.d * t1.d + t1.d > t2.d + t2.d * t2.d; 169 | count 170 | ---------- 171 | 49985001 172 | (1 row) 173 | 174 | Time: 20558.837 ms 175 | 176 | ftian=# select count(*) from tt t1, tt t2 where t1.ii < 10000 and t2.ii < 10000 and t1.n * t1.n + t1.n > t2.n + t2.n * t2.n; 177 | count 178 | ---------- 179 | 49985001 180 | (1 row) 181 | 182 | Time: 70237.267 ms 183 | ftian=# select count(*) from tt t1, tt t2 where t1.ii < 10000 and t2.ii < 10000 and t1.d64 * t1.d64 + t1.d64 > t2.d64 + t2.d64 * t2.d64; 184 | count 185 | ---------- 186 | 49985001 187 | (1 row) 188 | 189 | Time: 48016.060 ms 190 | ftian=# select count(*) from tt t1, tt t2 where t1.ii < 10000 and t2.ii < 10000 and t1.d128 * t1.d128 + t1.d128 > t2.d128 + t2.d128 * t2.d128; 191 | count 192 | ---------- 193 | 49985001 194 | (1 row) 195 | 196 | Time: 60443.629 ms 197 | ``` 198 | -------------------------------------------------------------------------------- /README.pavel: -------------------------------------------------------------------------------- 1 | pgDecimal 2 | ========= 3 | 4 | PostgreSQL types based on _Decimal32 and _Decimal64 types. 5 | 6 | I wrote this library due performance comparation with PostgreSQL's Numeric type. I would to know how much 7 | big impact has fact, so Numeric is varlena type. This initial implementation is based on basic _Decimal 8 | support, that is not complete (and check of overflow is slower, than could be with better library). This library 9 | contains some basic arithmetic operators and basic casts. 10 | 11 | postgres=# CREATE EXTENSION decimal; 12 | CREATE EXTENSION 13 | postgres=# SELECT 3.14::decimal32; 14 | decimal32 15 | ─────────── 16 | 3.14 17 | (1 row) 18 | 19 | postgres=# SELECT 3.14::decimal64; 20 | decimal64 21 | ─────────── 22 | 3.14 23 | (1 row) 24 | 25 | 26 | Contact: pavel.stehule@gmail.com 27 | 28 | -------------------------------------------------------------------------------- /decimal--2.0.sql: -------------------------------------------------------------------------------- 1 | /* contrib/decimal/decimal--1.0.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | -- \echo Use "CREATE EXTENSION decimal" to load this file. \quit 5 | 6 | -- 7 | -- PostgreSQL code for Decimal64/128. 8 | -- 9 | 10 | -- 11 | -- Shell type to keep things a bit quieter. 12 | -- 13 | 14 | CREATE TYPE decimal128; 15 | 16 | -- 17 | -- Input and output functions. 18 | -- 19 | CREATE FUNCTION decimal128_in(cstring) 20 | RETURNS decimal128 21 | AS 'MODULE_PATHNAME' 22 | LANGUAGE C IMMUTABLE STRICT; 23 | 24 | CREATE FUNCTION decimal128_out(decimal128) 25 | RETURNS cstring 26 | AS 'MODULE_PATHNAME' 27 | LANGUAGE C IMMUTABLE STRICT; 28 | 29 | CREATE TYPE decimal128 ( 30 | INPUT = decimal128_in, 31 | OUTPUT = decimal128_out, 32 | LIKE = pg_catalog.interval 33 | ); 34 | 35 | -- 36 | -- decimal128 functions. 37 | -- 38 | 39 | -- group 1: arithmatics, 40 | CREATE FUNCTION decimal128_div(decimal128, decimal128) 41 | RETURNS decimal128 42 | AS 'MODULE_PATHNAME' 43 | LANGUAGE C IMMUTABLE STRICT; 44 | 45 | CREATE FUNCTION decimal128_mul(decimal128, decimal128) 46 | RETURNS decimal128 47 | AS 'MODULE_PATHNAME' 48 | LANGUAGE C IMMUTABLE STRICT; 49 | 50 | CREATE FUNCTION decimal128_add(decimal128, decimal128) 51 | RETURNS decimal128 52 | AS 'MODULE_PATHNAME' 53 | LANGUAGE C IMMUTABLE STRICT; 54 | 55 | CREATE FUNCTION decimal128_sub(decimal128, decimal128) 56 | RETURNS decimal128 57 | AS 'MODULE_PATHNAME' 58 | LANGUAGE C IMMUTABLE STRICT; 59 | 60 | CREATE FUNCTION decimal128_uminus(decimal128) 61 | RETURNS decimal128 62 | AS 'MODULE_PATHNAME' 63 | LANGUAGE C IMMUTABLE STRICT; 64 | 65 | CREATE FUNCTION decimal128_uplus(decimal128) 66 | RETURNS decimal128 67 | AS 'MODULE_PATHNAME' 68 | LANGUAGE C IMMUTABLE STRICT; 69 | 70 | CREATE FUNCTION decimal128_abs(decimal128) 71 | RETURNS decimal128 72 | AS 'MODULE_PATHNAME' 73 | LANGUAGE C IMMUTABLE STRICT; 74 | 75 | -- group 2: compares. 76 | CREATE FUNCTION decimal128_eq(decimal128, decimal128) 77 | RETURNS boolean 78 | AS 'MODULE_PATHNAME' 79 | LANGUAGE C IMMUTABLE STRICT; 80 | 81 | CREATE FUNCTION decimal128_ne(decimal128, decimal128) 82 | RETURNS boolean 83 | AS 'MODULE_PATHNAME' 84 | LANGUAGE C IMMUTABLE STRICT; 85 | 86 | CREATE FUNCTION decimal128_lt(decimal128, decimal128) 87 | RETURNS boolean 88 | AS 'MODULE_PATHNAME' 89 | LANGUAGE C IMMUTABLE STRICT; 90 | 91 | CREATE FUNCTION decimal128_le(decimal128, decimal128) 92 | RETURNS boolean 93 | AS 'MODULE_PATHNAME' 94 | LANGUAGE C IMMUTABLE STRICT; 95 | 96 | CREATE FUNCTION decimal128_gt(decimal128, decimal128) 97 | RETURNS boolean 98 | AS 'MODULE_PATHNAME' 99 | LANGUAGE C IMMUTABLE STRICT; 100 | 101 | CREATE FUNCTION decimal128_ge(decimal128, decimal128) 102 | RETURNS boolean 103 | AS 'MODULE_PATHNAME' 104 | LANGUAGE C IMMUTABLE STRICT; 105 | 106 | -- group 3: hash and cmp 107 | CREATE FUNCTION decimal128_hash(decimal128) 108 | RETURNS integer 109 | AS 'MODULE_PATHNAME' 110 | LANGUAGE C IMMUTABLE STRICT; 111 | 112 | CREATE FUNCTION decimal128_cmp(decimal128, decimal128) 113 | RETURNS integer 114 | AS 'MODULE_PATHNAME' 115 | LANGUAGE C IMMUTABLE STRICT; 116 | 117 | -- group 4: misc 118 | CREATE FUNCTION decimal128_inc(decimal128) 119 | RETURNS decimal128 120 | AS 'MODULE_PATHNAME' 121 | LANGUAGE C IMMUTABLE STRICT; 122 | 123 | CREATE FUNCTION decimal128_smaller(decimal128, decimal128) 124 | RETURNS decimal128 125 | AS 'MODULE_PATHNAME' 126 | LANGUAGE C IMMUTABLE STRICT; 127 | 128 | CREATE FUNCTION decimal128_larger(decimal128, decimal128) 129 | RETURNS decimal128 130 | AS 'MODULE_PATHNAME' 131 | LANGUAGE C IMMUTABLE STRICT; 132 | 133 | CREATE FUNCTION round(decimal128, int) 134 | RETURNS decimal128 135 | AS 'MODULE_PATHNAME','decimal128_round' 136 | LANGUAGE C IMMUTABLE STRICT; 137 | 138 | -- NYI: 139 | -- mod, sqrt, pow, log, ln, fac, sign, trunc, ceil, floor 140 | -- exp, div_trunc, 141 | 142 | -- decimal aggregates 143 | -- NYI: higher momentum stats functions, stddev, var, etc. 144 | CREATE AGGREGATE sum(decimal128) 145 | ( 146 | sfunc = decimal128_add, 147 | stype = decimal128 148 | ); 149 | 150 | CREATE AGGREGATE min(decimal128) 151 | ( 152 | sfunc = decimal128_smaller, 153 | stype = decimal128 154 | ); 155 | 156 | CREATE AGGREGATE max(decimal128) 157 | ( 158 | sfunc = decimal128_larger, 159 | stype = decimal128 160 | ); 161 | 162 | -- 163 | -- operators 164 | -- 165 | CREATE OPERATOR / ( 166 | PROCEDURE = decimal128_div, 167 | LEFTARG = decimal128, RIGHTARG = decimal128 168 | ); 169 | 170 | CREATE OPERATOR * ( 171 | PROCEDURE = decimal128_mul, 172 | LEFTARG = decimal128, RIGHTARG = decimal128 173 | ); 174 | 175 | CREATE OPERATOR + ( 176 | PROCEDURE = decimal128_add, 177 | LEFTARG = decimal128, RIGHTARG = decimal128 178 | ); 179 | 180 | CREATE OPERATOR - ( 181 | PROCEDURE = decimal128_sub, 182 | LEFTARG = decimal128, RIGHTARG = decimal128 183 | ); 184 | 185 | CREATE OPERATOR + ( 186 | PROCEDURE = decimal128_uplus, 187 | RIGHTARG = decimal128 188 | ); 189 | 190 | CREATE OPERATOR - ( 191 | PROCEDURE = decimal128_uminus, 192 | RIGHTARG = decimal128 193 | ); 194 | 195 | CREATE OPERATOR < ( 196 | PROCEDURE = decimal128_lt, 197 | LEFTARG = decimal128, RIGHTARG = decimal128, 198 | COMMUTATOR = >, 199 | NEGATOR = >= 200 | ); 201 | 202 | CREATE OPERATOR <= ( 203 | PROCEDURE = decimal128_le, 204 | LEFTARG = decimal128, RIGHTARG = decimal128, 205 | COMMUTATOR = >=, 206 | NEGATOR = > 207 | ); 208 | 209 | CREATE OPERATOR > ( 210 | PROCEDURE = decimal128_gt, 211 | LEFTARG = decimal128, RIGHTARG = decimal128, 212 | COMMUTATOR = <, 213 | NEGATOR = <= 214 | ); 215 | 216 | CREATE OPERATOR >= ( 217 | PROCEDURE = decimal128_ge, 218 | LEFTARG = decimal128, RIGHTARG = decimal128, 219 | COMMUTATOR = <=, 220 | NEGATOR = < 221 | ); 222 | 223 | CREATE OPERATOR = ( 224 | PROCEDURE = decimal128_eq, 225 | LEFTARG = decimal128, RIGHTARG = decimal128, 226 | COMMUTATOR = =, 227 | NEGATOR = <>, 228 | MERGES, HASHES 229 | ); 230 | 231 | CREATE OPERATOR <> ( 232 | PROCEDURE = decimal128_ne, 233 | LEFTARG = decimal128, RIGHTARG = decimal128, 234 | COMMUTATOR = <>, 235 | NEGATOR = = 236 | ); 237 | 238 | CREATE OPERATOR FAMILY decimal128_ops USING btree; 239 | CREATE OPERATOR FAMILY decimal128_ops USING hash; 240 | 241 | CREATE OPERATOR CLASS decimal128_op_cls DEFAULT 242 | FOR TYPE decimal128 USING btree FAMILY decimal128_ops AS 243 | OPERATOR 1 <, 244 | OPERATOR 2 <=, 245 | OPERATOR 3 =, 246 | OPERATOR 4 >=, 247 | OPERATOR 5 >, 248 | FUNCTION 1 decimal128_cmp(decimal128, decimal128); 249 | 250 | CREATE OPERATOR CLASS decimal128_op_cls DEFAULT 251 | FOR TYPE decimal128 USING hash FAMILY decimal128_ops AS 252 | OPERATOR 1 =, 253 | FUNCTION 1 decimal128_hash(decimal128); 254 | 255 | 256 | -- 257 | -- Shell type to keep things a bit quieter. 258 | -- 259 | 260 | CREATE TYPE decimal64; 261 | 262 | -- 263 | -- Input and output functions. 264 | -- 265 | CREATE FUNCTION decimal64_in(cstring) 266 | RETURNS decimal64 267 | AS 'MODULE_PATHNAME' 268 | LANGUAGE C IMMUTABLE STRICT; 269 | 270 | CREATE FUNCTION decimal64_out(decimal64) 271 | RETURNS cstring 272 | AS 'MODULE_PATHNAME' 273 | LANGUAGE C IMMUTABLE STRICT; 274 | 275 | CREATE TYPE decimal64 ( 276 | INPUT = decimal64_in, 277 | OUTPUT = decimal64_out, 278 | LIKE = pg_catalog.int8 279 | ); 280 | 281 | -- 282 | -- decimal64 functions. 283 | -- 284 | 285 | -- group 1: arithmatics, 286 | CREATE FUNCTION decimal64_div(decimal64, decimal64) 287 | RETURNS decimal64 288 | AS 'MODULE_PATHNAME' 289 | LANGUAGE C IMMUTABLE STRICT; 290 | 291 | CREATE FUNCTION decimal64_mul(decimal64, decimal64) 292 | RETURNS decimal64 293 | AS 'MODULE_PATHNAME' 294 | LANGUAGE C IMMUTABLE STRICT; 295 | 296 | CREATE FUNCTION decimal64_add(decimal64, decimal64) 297 | RETURNS decimal64 298 | AS 'MODULE_PATHNAME' 299 | LANGUAGE C IMMUTABLE STRICT; 300 | 301 | CREATE FUNCTION decimal64_sub(decimal64, decimal64) 302 | RETURNS decimal64 303 | AS 'MODULE_PATHNAME' 304 | LANGUAGE C IMMUTABLE STRICT; 305 | 306 | CREATE FUNCTION decimal64_uminus(decimal64) 307 | RETURNS decimal64 308 | AS 'MODULE_PATHNAME' 309 | LANGUAGE C IMMUTABLE STRICT; 310 | 311 | CREATE FUNCTION decimal64_uplus(decimal64) 312 | RETURNS decimal64 313 | AS 'MODULE_PATHNAME' 314 | LANGUAGE C IMMUTABLE STRICT; 315 | 316 | CREATE FUNCTION decimal64_abs(decimal64) 317 | RETURNS decimal64 318 | AS 'MODULE_PATHNAME' 319 | LANGUAGE C IMMUTABLE STRICT; 320 | 321 | -- group 2: compares. 322 | CREATE FUNCTION decimal64_eq(decimal64, decimal64) 323 | RETURNS boolean 324 | AS 'MODULE_PATHNAME' 325 | LANGUAGE C IMMUTABLE STRICT; 326 | 327 | CREATE FUNCTION decimal64_ne(decimal64, decimal64) 328 | RETURNS boolean 329 | AS 'MODULE_PATHNAME' 330 | LANGUAGE C IMMUTABLE STRICT; 331 | 332 | CREATE FUNCTION decimal64_lt(decimal64, decimal64) 333 | RETURNS boolean 334 | AS 'MODULE_PATHNAME' 335 | LANGUAGE C IMMUTABLE STRICT; 336 | 337 | CREATE FUNCTION decimal64_le(decimal64, decimal64) 338 | RETURNS boolean 339 | AS 'MODULE_PATHNAME' 340 | LANGUAGE C IMMUTABLE STRICT; 341 | 342 | CREATE FUNCTION decimal64_gt(decimal64, decimal64) 343 | RETURNS boolean 344 | AS 'MODULE_PATHNAME' 345 | LANGUAGE C IMMUTABLE STRICT; 346 | 347 | CREATE FUNCTION decimal64_ge(decimal64, decimal64) 348 | RETURNS boolean 349 | AS 'MODULE_PATHNAME' 350 | LANGUAGE C IMMUTABLE STRICT; 351 | 352 | -- group 3: hash and cmp 353 | CREATE FUNCTION decimal64_hash(decimal64) 354 | RETURNS integer 355 | AS 'MODULE_PATHNAME' 356 | LANGUAGE C IMMUTABLE STRICT; 357 | 358 | CREATE FUNCTION decimal64_cmp(decimal64, decimal64) 359 | RETURNS integer 360 | AS 'MODULE_PATHNAME' 361 | LANGUAGE C IMMUTABLE STRICT; 362 | 363 | -- group 4: misc 364 | CREATE FUNCTION decimal64_inc(decimal64) 365 | RETURNS decimal64 366 | AS 'MODULE_PATHNAME' 367 | LANGUAGE C IMMUTABLE STRICT; 368 | 369 | CREATE FUNCTION decimal64_smaller(decimal64, decimal64) 370 | RETURNS decimal64 371 | AS 'MODULE_PATHNAME' 372 | LANGUAGE C IMMUTABLE STRICT; 373 | 374 | CREATE FUNCTION decimal64_larger(decimal64, decimal64) 375 | RETURNS decimal64 376 | AS 'MODULE_PATHNAME' 377 | LANGUAGE C IMMUTABLE STRICT; 378 | 379 | CREATE FUNCTION round(decimal64, int) 380 | RETURNS decimal64 381 | AS 'MODULE_PATHNAME','decimal64_round' 382 | LANGUAGE C IMMUTABLE STRICT; 383 | 384 | -- NYI: 385 | -- mod, sqrt, pow, log, ln, fac, sign, trunc, ceil, floor 386 | -- exp, div_trunc, 387 | 388 | -- decimal aggregates 389 | -- NYI: higher momentum stats functions, stddev, var, etc. 390 | CREATE AGGREGATE sum(decimal64) 391 | ( 392 | sfunc = decimal64_add, 393 | stype = decimal64 394 | ); 395 | 396 | CREATE AGGREGATE min(decimal64) 397 | ( 398 | sfunc = decimal64_smaller, 399 | stype = decimal64 400 | ); 401 | 402 | CREATE AGGREGATE max(decimal64) 403 | ( 404 | sfunc = decimal64_larger, 405 | stype = decimal64 406 | ); 407 | 408 | -- 409 | -- operators 410 | -- 411 | CREATE OPERATOR / ( 412 | PROCEDURE = decimal64_div, 413 | LEFTARG = decimal64, RIGHTARG = decimal64 414 | ); 415 | 416 | CREATE OPERATOR * ( 417 | PROCEDURE = decimal64_mul, 418 | LEFTARG = decimal64, RIGHTARG = decimal64 419 | ); 420 | 421 | CREATE OPERATOR + ( 422 | PROCEDURE = decimal64_add, 423 | LEFTARG = decimal64, RIGHTARG = decimal64 424 | ); 425 | 426 | CREATE OPERATOR - ( 427 | PROCEDURE = decimal64_sub, 428 | LEFTARG = decimal64, RIGHTARG = decimal64 429 | ); 430 | 431 | CREATE OPERATOR + ( 432 | PROCEDURE = decimal64_uplus, 433 | RIGHTARG = decimal64 434 | ); 435 | 436 | CREATE OPERATOR - ( 437 | PROCEDURE = decimal64_uminus, 438 | RIGHTARG = decimal64 439 | ); 440 | 441 | CREATE OPERATOR < ( 442 | PROCEDURE = decimal64_lt, 443 | LEFTARG = decimal64, RIGHTARG = decimal64, 444 | COMMUTATOR = >, 445 | NEGATOR = >= 446 | ); 447 | 448 | CREATE OPERATOR <= ( 449 | PROCEDURE = decimal64_le, 450 | LEFTARG = decimal64, RIGHTARG = decimal64, 451 | COMMUTATOR = >=, 452 | NEGATOR = > 453 | ); 454 | 455 | CREATE OPERATOR > ( 456 | PROCEDURE = decimal64_gt, 457 | LEFTARG = decimal64, RIGHTARG = decimal64, 458 | COMMUTATOR = <, 459 | NEGATOR = <= 460 | ); 461 | 462 | CREATE OPERATOR >= ( 463 | PROCEDURE = decimal64_ge, 464 | LEFTARG = decimal64, RIGHTARG = decimal64, 465 | COMMUTATOR = <=, 466 | NEGATOR = < 467 | ); 468 | 469 | CREATE OPERATOR = ( 470 | PROCEDURE = decimal64_eq, 471 | LEFTARG = decimal64, RIGHTARG = decimal64, 472 | COMMUTATOR = =, 473 | NEGATOR = <>, 474 | MERGES, HASHES 475 | ); 476 | 477 | CREATE OPERATOR <> ( 478 | PROCEDURE = decimal64_ne, 479 | LEFTARG = decimal64, RIGHTARG = decimal64, 480 | COMMUTATOR = <>, 481 | NEGATOR = = 482 | ); 483 | 484 | CREATE OPERATOR FAMILY decimal64_ops USING btree; 485 | CREATE OPERATOR FAMILY decimal64_ops USING hash; 486 | 487 | CREATE OPERATOR CLASS decimal64_op_cls DEFAULT 488 | FOR TYPE decimal64 USING btree FAMILY decimal64_ops AS 489 | OPERATOR 1 <, 490 | OPERATOR 2 <=, 491 | OPERATOR 3 =, 492 | OPERATOR 4 >=, 493 | OPERATOR 5 >, 494 | FUNCTION 1 decimal64_cmp(decimal64, decimal64); 495 | 496 | CREATE OPERATOR CLASS decimal64_op_cls DEFAULT 497 | FOR TYPE decimal64 USING hash FAMILY decimal64_ops AS 498 | OPERATOR 1 =, 499 | FUNCTION 1 decimal64_hash(decimal64); 500 | 501 | /* 502 | * Casts functions 503 | */ 504 | CREATE FUNCTION decimal128_decimal64(decimal128) 505 | RETURNS decimal64 506 | AS 'MODULE_PATHNAME' 507 | LANGUAGE C IMMUTABLE STRICT; 508 | 509 | CREATE FUNCTION decimal64_decimal128(decimal64) 510 | RETURNS decimal128 511 | AS 'MODULE_PATHNAME' 512 | LANGUAGE C IMMUTABLE STRICT; 513 | 514 | CREATE FUNCTION float4_decimal64(float4) 515 | RETURNS decimal64 516 | AS 'MODULE_PATHNAME' 517 | LANGUAGE C IMMUTABLE STRICT; 518 | 519 | CREATE FUNCTION float4_decimal128(float4) 520 | RETURNS decimal128 521 | AS 'MODULE_PATHNAME' 522 | LANGUAGE C IMMUTABLE STRICT; 523 | 524 | CREATE FUNCTION decimal64_float4(decimal64) 525 | RETURNS float4 526 | AS 'MODULE_PATHNAME' 527 | LANGUAGE C IMMUTABLE STRICT; 528 | 529 | CREATE FUNCTION decimal128_float4(decimal128) 530 | RETURNS float4 531 | AS 'MODULE_PATHNAME' 532 | LANGUAGE C IMMUTABLE STRICT; 533 | 534 | CREATE FUNCTION float8_decimal64(float8) 535 | RETURNS decimal64 536 | AS 'MODULE_PATHNAME' 537 | LANGUAGE C IMMUTABLE STRICT; 538 | 539 | CREATE FUNCTION float8_decimal128(float8) 540 | RETURNS decimal128 541 | AS 'MODULE_PATHNAME' 542 | LANGUAGE C IMMUTABLE STRICT; 543 | 544 | CREATE FUNCTION decimal64_float8(decimal64) 545 | RETURNS float8 546 | AS 'MODULE_PATHNAME' 547 | LANGUAGE C IMMUTABLE STRICT; 548 | 549 | CREATE FUNCTION decimal128_float8(decimal128) 550 | RETURNS float8 551 | AS 'MODULE_PATHNAME' 552 | LANGUAGE C IMMUTABLE STRICT; 553 | 554 | CREATE FUNCTION numeric_decimal64(numeric) 555 | RETURNS decimal64 556 | AS 'MODULE_PATHNAME' 557 | LANGUAGE C IMMUTABLE STRICT; 558 | 559 | CREATE FUNCTION numeric_decimal128(numeric) 560 | RETURNS decimal128 561 | AS 'MODULE_PATHNAME' 562 | LANGUAGE C IMMUTABLE STRICT; 563 | 564 | CREATE FUNCTION decimal64_numeric(decimal64) 565 | RETURNS numeric 566 | AS 'MODULE_PATHNAME' 567 | LANGUAGE C IMMUTABLE STRICT; 568 | 569 | CREATE FUNCTION decimal128_numeric(decimal128) 570 | RETURNS numeric 571 | AS 'MODULE_PATHNAME' 572 | LANGUAGE C IMMUTABLE STRICT; 573 | 574 | CREATE FUNCTION int4_decimal64(int4) 575 | RETURNS decimal64 576 | AS 'MODULE_PATHNAME' 577 | LANGUAGE C IMMUTABLE STRICT; 578 | 579 | CREATE FUNCTION int4_decimal128(int4) 580 | RETURNS decimal128 581 | AS 'MODULE_PATHNAME' 582 | LANGUAGE C IMMUTABLE STRICT; 583 | 584 | CREATE FUNCTION int8_decimal64(int8) 585 | RETURNS decimal64 586 | AS 'MODULE_PATHNAME' 587 | LANGUAGE C IMMUTABLE STRICT; 588 | 589 | CREATE FUNCTION int8_decimal128(int8) 590 | RETURNS decimal128 591 | AS 'MODULE_PATHNAME' 592 | LANGUAGE C IMMUTABLE STRICT; 593 | 594 | CREATE CAST(decimal128 AS decimal64) 595 | WITH FUNCTION decimal128_decimal64(decimal128); 596 | 597 | CREATE CAST(decimal64 AS decimal128) 598 | WITH FUNCTION decimal64_decimal128(decimal64) 599 | AS IMPLICIT; 600 | 601 | CREATE CAST(float4 AS decimal64) 602 | WITH FUNCTION float4_decimal64(float4) 603 | AS IMPLICIT; 604 | 605 | CREATE CAST(float4 AS decimal128) 606 | WITH FUNCTION float4_decimal128(float4) 607 | AS IMPLICIT; 608 | 609 | CREATE CAST(decimal64 AS float4) 610 | WITH FUNCTION decimal64_float4(decimal64); 611 | 612 | CREATE CAST(decimal128 AS float4) 613 | WITH FUNCTION decimal128_float4(decimal128); 614 | 615 | CREATE CAST(float8 AS decimal64) 616 | WITH FUNCTION float8_decimal64(float8); 617 | 618 | CREATE CAST(float8 AS decimal128) 619 | WITH FUNCTION float8_decimal128(float8) 620 | AS IMPLICIT; 621 | 622 | CREATE CAST(decimal64 AS float8) 623 | WITH FUNCTION decimal64_float8(decimal64); 624 | 625 | CREATE CAST(decimal128 AS float8) 626 | WITH FUNCTION decimal128_float8(decimal128); 627 | 628 | CREATE CAST(numeric AS decimal64) 629 | WITH FUNCTION numeric_decimal64(numeric); 630 | 631 | CREATE CAST(numeric AS decimal128) 632 | WITH FUNCTION numeric_decimal128(numeric); 633 | 634 | CREATE CAST(decimal64 AS numeric) 635 | WITH FUNCTION decimal64_numeric(decimal64) 636 | AS IMPLICIT; 637 | 638 | CREATE CAST(decimal128 AS numeric) 639 | WITH FUNCTION decimal128_numeric(decimal128) 640 | AS IMPLICIT; 641 | 642 | CREATE CAST(int4 AS decimal64) 643 | WITH FUNCTION int4_decimal64(int4) 644 | AS IMPLICIT; 645 | 646 | CREATE CAST(int4 AS decimal128) 647 | WITH FUNCTION int4_decimal128(int4) 648 | AS IMPLICIT; 649 | 650 | CREATE CAST(int8 AS decimal64) 651 | WITH FUNCTION int8_decimal64(int8); 652 | 653 | CREATE CAST(int8 AS decimal128) 654 | WITH FUNCTION int8_decimal128(int8) 655 | AS IMPLICIT; 656 | 657 | -------------------------------------------------------------------------------- /decimal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * contrib/decimal/decimal.c 3 | */ 4 | #include "decimal.h" 5 | #ifdef PG_MODULE_MAGIC 6 | PG_MODULE_MAGIC; 7 | #endif 8 | 9 | #ifndef USE_FLOAT8_BYVAL 10 | #error pgdecimal uses 64 bit datum. 11 | #endif 12 | 13 | Datum 14 | Decimal64GetDatum(PGDecimal64 X) 15 | { 16 | union 17 | { 18 | PGDecimal64 value; 19 | int64 retval; 20 | } myunion; 21 | 22 | myunion.value = X; 23 | return SET_8_BYTES(myunion.retval); 24 | } 25 | 26 | PGDecimal64 27 | DatumGetDecimal64(Datum X) 28 | { 29 | union 30 | { 31 | int64 value; 32 | PGDecimal64 retval; 33 | } myunion; 34 | 35 | myunion.value = GET_8_BYTES(X); 36 | return myunion.retval; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /decimal.control: -------------------------------------------------------------------------------- 1 | # decimal 2 | comment = 'decimal32 and decimal64 support' 3 | default_version = '1.0' 4 | module_pathname = '$libdir/decimal' 5 | relocatable = true 6 | -------------------------------------------------------------------------------- /decimal.h: -------------------------------------------------------------------------------- 1 | #ifndef _PG_DECIMAL_H_ 2 | #define _PG_DECIMAL_H_ 3 | 4 | #include "postgres.h" 5 | 6 | #include "utils/builtins.h" 7 | #include "utils/formatting.h" 8 | 9 | #include "decSingle.h" 10 | #include "decDouble.h" 11 | #include "decQuad.h" 12 | 13 | #include 14 | 15 | typedef decDouble PGDecimal64; 16 | typedef decQuad PGDecimal128; 17 | 18 | PGDecimal64 DatumGetDecimal64(Datum d); 19 | Datum Decimal64GetDatum(PGDecimal64 d); 20 | 21 | #define PG_GETARG_DECIMAL64(n) DatumGetDecimal64(PG_GETARG_DATUM(n)) 22 | #define PG_GETARG_P_DECIMAL128(n) ((PGDecimal128 *) DatumGetPointer(PG_GETARG_DATUM(n))) 23 | 24 | #define PG_RETURN_DECIMAL64(d) return Decimal64GetDatum(d) 25 | #define PG_RETURN_P_DECIMAL128(d) return PointerGetDatum(d) 26 | 27 | #define DECLARE_PRET_128(P) \ 28 | PGDecimal128 *P = (PGDecimal128 *) palloc(sizeof(PGDecimal128)); \ 29 | if (!P) { \ 30 | elog(ERROR, "OOM"); \ 31 | } else (void) 0 32 | 33 | #define DECLARE_DEC_CTXT(X) \ 34 | decContext dc; \ 35 | decContextDefault(&dc, X) 36 | 37 | #define DECLARE_PRET_128_WITH_CTXT(P, X) \ 38 | decContext dc; \ 39 | PGDecimal128 *P; \ 40 | decContextDefault(&dc, X); \ 41 | P = (PGDecimal128 *) palloc(sizeof(PGDecimal128)); \ 42 | if (!X) { \ 43 | elog(ERROR, "OOM"); \ 44 | } else (void) 0 45 | 46 | 47 | #define DEC_CHK_STATUS(dc, chk_status, ...) \ 48 | if (1) { \ 49 | uint32_t dc_status = decContextGetStatus(&dc); \ 50 | if ((dc_status & chk_status) != 0) { \ 51 | elog(ERROR, __VA_ARGS__); \ 52 | } \ 53 | } else (void) 0 54 | 55 | #define DEC_STATUS_OFUF (DEC_Overflow | DEC_Underflow) 56 | #define DEC_STATUS_DIV (DEC_Division_by_zero | DEC_Division_impossible | DEC_Division_undefined | DEC_STATUS_OFUF) 57 | #define DEC_STATUS_ALL (0xFFFF) 58 | 59 | extern Datum hash_any(register const unsigned char* p, register int len); 60 | 61 | #endif /* _PG_DECIMAL_H_ */ 62 | -------------------------------------------------------------------------------- /decimal128.c: -------------------------------------------------------------------------------- 1 | #include "decimal.h" 2 | 3 | // 4 | // Forward declarations. 5 | // 6 | Datum decimal128_in(PG_FUNCTION_ARGS); 7 | Datum decimal128_out(PG_FUNCTION_ARGS); 8 | Datum decimal128_add(PG_FUNCTION_ARGS); 9 | Datum decimal128_sub(PG_FUNCTION_ARGS); 10 | Datum decimal128_mul(PG_FUNCTION_ARGS); 11 | Datum decimal128_div(PG_FUNCTION_ARGS); 12 | Datum decimal128_uplus(PG_FUNCTION_ARGS); 13 | Datum decimal128_uminus(PG_FUNCTION_ARGS); 14 | Datum decimal128_abs(PG_FUNCTION_ARGS); 15 | Datum decimal128_lt(PG_FUNCTION_ARGS); 16 | Datum decimal128_le(PG_FUNCTION_ARGS); 17 | Datum decimal128_gt(PG_FUNCTION_ARGS); 18 | Datum decimal128_ge(PG_FUNCTION_ARGS); 19 | Datum decimal128_eq(PG_FUNCTION_ARGS); 20 | Datum decimal128_ne(PG_FUNCTION_ARGS); 21 | Datum decimal128_cmp(PG_FUNCTION_ARGS); 22 | Datum decimal128_hash(PG_FUNCTION_ARGS); 23 | Datum decimal128_smaller(PG_FUNCTION_ARGS); 24 | Datum decimal128_larger(PG_FUNCTION_ARGS); 25 | Datum decimal128_inc(PG_FUNCTION_ARGS); 26 | Datum decimal128_round(PG_FUNCTION_ARGS); 27 | Datum decimal128_float4(PG_FUNCTION_ARGS); 28 | Datum float4_decimal128(PG_FUNCTION_ARGS); 29 | Datum decimal128_float8(PG_FUNCTION_ARGS); 30 | Datum float8_decimal128(PG_FUNCTION_ARGS);; 31 | Datum int4_decimal128(PG_FUNCTION_ARGS); 32 | Datum int8_decimal128(PG_FUNCTION_ARGS); 33 | Datum decimal128_numeric(PG_FUNCTION_ARGS); 34 | Datum numeric_decimal128(PG_FUNCTION_ARGS); 35 | 36 | PG_FUNCTION_INFO_V1(decimal128_in); 37 | Datum decimal128_in(PG_FUNCTION_ARGS) 38 | { 39 | char *s = PG_GETARG_CSTRING(0); 40 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 41 | 42 | decQuadFromString(pret, s, &dc); 43 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal128 syntax:\"%s\"", s); 44 | PG_RETURN_P_DECIMAL128(pret); 45 | } 46 | 47 | PG_FUNCTION_INFO_V1(decimal128_out); 48 | Datum decimal128_out(PG_FUNCTION_ARGS) 49 | { 50 | PGDecimal128 *pd = PG_GETARG_P_DECIMAL128(0); 51 | char *s = (char *) palloc(DECQUAD_String); 52 | if (!s) { 53 | elog(ERROR, "OOM"); 54 | } 55 | 56 | decQuadToString(pd, s); 57 | PG_RETURN_CSTRING(s); 58 | } 59 | 60 | PG_FUNCTION_INFO_V1(decimal128_add); 61 | Datum decimal128_add(PG_FUNCTION_ARGS) 62 | { 63 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 64 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 65 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 66 | 67 | decQuadAdd(pret, pa, pb, &dc); 68 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal128 overflow"); 69 | PG_RETURN_P_DECIMAL128(pret); 70 | } 71 | 72 | PG_FUNCTION_INFO_V1(decimal128_sub); 73 | Datum decimal128_sub(PG_FUNCTION_ARGS) 74 | { 75 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 76 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 77 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 78 | 79 | decQuadSubtract(pret, pa, pb, &dc); 80 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal128 overflow"); 81 | PG_RETURN_P_DECIMAL128(pret); 82 | } 83 | 84 | PG_FUNCTION_INFO_V1(decimal128_mul); 85 | Datum decimal128_mul(PG_FUNCTION_ARGS) 86 | { 87 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 88 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 89 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 90 | 91 | decQuadMultiply(pret, pa, pb, &dc); 92 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal128 overflow"); 93 | PG_RETURN_P_DECIMAL128(pret); 94 | } 95 | 96 | PG_FUNCTION_INFO_V1(decimal128_div); 97 | Datum decimal128_div(PG_FUNCTION_ARGS) 98 | { 99 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 100 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 101 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 102 | 103 | decQuadDivide(pret, pa, pb, &dc); 104 | DEC_CHK_STATUS(dc, DEC_STATUS_DIV, "decimal128 division error"); 105 | PG_RETURN_P_DECIMAL128(pret); 106 | } 107 | 108 | PG_FUNCTION_INFO_V1(decimal128_uplus); 109 | Datum decimal128_uplus(PG_FUNCTION_ARGS) 110 | { 111 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 112 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 113 | 114 | decQuadPlus(pret, pa, &dc); 115 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal128 overflow"); 116 | PG_RETURN_P_DECIMAL128(pret); 117 | } 118 | 119 | PG_FUNCTION_INFO_V1(decimal128_uminus); 120 | Datum decimal128_uminus(PG_FUNCTION_ARGS) 121 | { 122 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 123 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 124 | 125 | decQuadMinus(pret, pa, &dc); 126 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal128 overflow"); 127 | PG_RETURN_P_DECIMAL128(pret); 128 | } 129 | 130 | PG_FUNCTION_INFO_V1(decimal128_abs); 131 | Datum decimal128_abs(PG_FUNCTION_ARGS) 132 | { 133 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 134 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 135 | 136 | decQuadAbs(pret, pa, &dc); 137 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal128 overflow"); 138 | PG_RETURN_P_DECIMAL128(pret); 139 | } 140 | 141 | 142 | static int decimal128_cmp_internal(const PGDecimal128* a, const PGDecimal128* b) 143 | { 144 | PGDecimal128 result; 145 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL128); 146 | decQuadCompare(&result, a, b, &dc); 147 | 148 | if (decQuadIsPositive(&result)) { 149 | return 1; 150 | } else if (decQuadIsZero(&result)) { 151 | return 0; 152 | } else if (decQuadIsNegative(&result)) { 153 | return -1; 154 | } else { 155 | elog(ERROR, "invalid decimal128 in compare"); 156 | } 157 | } 158 | 159 | PG_FUNCTION_INFO_V1(decimal128_lt); 160 | Datum decimal128_lt(PG_FUNCTION_ARGS) 161 | { 162 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 163 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 164 | int ret = decimal128_cmp_internal(pa, pb); 165 | PG_RETURN_BOOL(ret < 0); 166 | } 167 | 168 | PG_FUNCTION_INFO_V1(decimal128_le); 169 | Datum decimal128_le(PG_FUNCTION_ARGS) 170 | { 171 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 172 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 173 | int ret = decimal128_cmp_internal(pa, pb); 174 | PG_RETURN_BOOL(ret <= 0); 175 | } 176 | 177 | PG_FUNCTION_INFO_V1(decimal128_gt); 178 | Datum decimal128_gt(PG_FUNCTION_ARGS) 179 | { 180 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 181 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 182 | int ret = decimal128_cmp_internal(pa, pb); 183 | PG_RETURN_BOOL(ret > 0); 184 | } 185 | 186 | PG_FUNCTION_INFO_V1(decimal128_ge); 187 | Datum decimal128_ge(PG_FUNCTION_ARGS) 188 | { 189 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 190 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 191 | int ret = decimal128_cmp_internal(pa, pb); 192 | PG_RETURN_BOOL(ret >= 0); 193 | } 194 | 195 | PG_FUNCTION_INFO_V1(decimal128_eq); 196 | Datum decimal128_eq(PG_FUNCTION_ARGS) 197 | { 198 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 199 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 200 | int ret = decimal128_cmp_internal(pa, pb); 201 | PG_RETURN_BOOL(ret == 0); 202 | } 203 | 204 | PG_FUNCTION_INFO_V1(decimal128_ne); 205 | Datum decimal128_ne(PG_FUNCTION_ARGS) 206 | { 207 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 208 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 209 | int ret = decimal128_cmp_internal(pa, pb); 210 | PG_RETURN_BOOL(ret != 0); 211 | } 212 | 213 | PG_FUNCTION_INFO_V1(decimal128_cmp); 214 | Datum decimal128_cmp(PG_FUNCTION_ARGS) 215 | { 216 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 217 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 218 | int ret = decimal128_cmp_internal(pa, pb); 219 | PG_RETURN_INT32(ret); 220 | } 221 | 222 | PG_FUNCTION_INFO_V1(decimal128_hash); 223 | Datum decimal128_hash(PG_FUNCTION_ARGS) 224 | { 225 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 226 | PGDecimal128 n; 227 | Datum ret; 228 | 229 | if (decQuadIsZero(pa)) { 230 | decQuadZero(&n); 231 | } else { 232 | decQuadCanonical(&n, pa); 233 | } 234 | 235 | ret = hash_any((unsigned char *)&n, sizeof(n)); 236 | return ret; 237 | } 238 | 239 | PG_FUNCTION_INFO_V1(decimal128_smaller); 240 | Datum decimal128_smaller(PG_FUNCTION_ARGS) 241 | { 242 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 243 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 244 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 245 | 246 | decQuadMin(pret, pa, pb, &dc); 247 | PG_RETURN_P_DECIMAL128(pret); 248 | } 249 | 250 | PG_FUNCTION_INFO_V1(decimal128_larger); 251 | Datum decimal128_larger(PG_FUNCTION_ARGS) 252 | { 253 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 254 | PGDecimal128* pb = PG_GETARG_P_DECIMAL128(1); 255 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 256 | 257 | decQuadMax(pret, pa, pb, &dc); 258 | PG_RETURN_P_DECIMAL128(pret); 259 | } 260 | 261 | PG_FUNCTION_INFO_V1(decimal128_inc); 262 | Datum decimal128_inc(PG_FUNCTION_ARGS) 263 | { 264 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 265 | PGDecimal128 one; 266 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 267 | 268 | decQuadFromInt32(&one, 1); 269 | 270 | decQuadMax(pret, pa, &one, &dc); 271 | PG_RETURN_P_DECIMAL128(pret); 272 | } 273 | 274 | PG_FUNCTION_INFO_V1(decimal128_round); 275 | Datum decimal128_round(PG_FUNCTION_ARGS) 276 | { 277 | static PGDecimal128* pquan; 278 | static PGDecimal128 quan[33]; 279 | 280 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 281 | int32_t scale = PG_GETARG_INT32(1); 282 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 283 | 284 | if (pquan == 0) { 285 | int i; 286 | PGDecimal128 ten; 287 | decQuadFromInt32(&ten, 10); 288 | decQuadFromInt32(&quan[0], 1); 289 | for (i = 1; i < 33; i++) { 290 | decQuadDivide(&quan[i], &quan[i-1], &ten, &dc); 291 | } 292 | pquan = &quan[0]; 293 | } 294 | 295 | if (scale < 0 || scale > 32) { 296 | elog(ERROR, "decimal32 rounding to an invalid scale"); 297 | } 298 | 299 | decQuadQuantize(pret, pa, &quan[scale], &dc); 300 | PG_RETURN_P_DECIMAL128(pret); 301 | } 302 | 303 | PG_FUNCTION_INFO_V1(decimal128_float4); 304 | Datum decimal128_float4(PG_FUNCTION_ARGS) 305 | { 306 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 307 | char s[DECQUAD_String]; 308 | char *endp = 0; 309 | float f; 310 | 311 | decQuadToString(pa, s); 312 | 313 | errno = 0; 314 | f = strtof(s, &endp); 315 | 316 | if (endp == s || errno != 0) { 317 | elog(ERROR, "cannot convert decimal128 to float4"); 318 | } 319 | 320 | PG_RETURN_FLOAT4(f); 321 | } 322 | 323 | PG_FUNCTION_INFO_V1(float4_decimal128); 324 | Datum float4_decimal128(PG_FUNCTION_ARGS) 325 | { 326 | float f = PG_GETARG_FLOAT4(0); 327 | char s[128]; 328 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 329 | 330 | sprintf(s, "%.*g", FLT_DIG, f); 331 | decQuadFromString(pret, s, &dc); 332 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal128 syntax:\"%s\"", s); 333 | PG_RETURN_P_DECIMAL128(pret); 334 | } 335 | 336 | PG_FUNCTION_INFO_V1(decimal128_float8); 337 | Datum decimal128_float8(PG_FUNCTION_ARGS) 338 | { 339 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 340 | char s[DECQUAD_String]; 341 | char *endp = 0; 342 | double d; 343 | 344 | decQuadToString(pa, s); 345 | 346 | errno = 0; 347 | d = strtod(s, &endp); 348 | 349 | if (endp == s || errno != 0) { 350 | elog(ERROR, "cannot convert decimal128 to float8"); 351 | } 352 | 353 | PG_RETURN_FLOAT8(d); 354 | } 355 | 356 | PG_FUNCTION_INFO_V1(float8_decimal128); 357 | Datum float8_decimal128(PG_FUNCTION_ARGS) 358 | { 359 | double d = PG_GETARG_FLOAT8(0); 360 | char s[128]; 361 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 362 | 363 | sprintf(s, "%.*g", DBL_DIG, d); 364 | decQuadFromString(pret, s, &dc); 365 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal128 syntax:\"%s\"", s); 366 | PG_RETURN_P_DECIMAL128(pret); 367 | } 368 | 369 | PG_FUNCTION_INFO_V1(int4_decimal128); 370 | Datum int4_decimal128(PG_FUNCTION_ARGS) 371 | { 372 | int32_t a = PG_GETARG_INT32(0); 373 | DECLARE_PRET_128(pret); 374 | 375 | decQuadFromInt32(pret, a); 376 | PG_RETURN_P_DECIMAL128(pret); 377 | } 378 | 379 | PG_FUNCTION_INFO_V1(int8_decimal128); 380 | Datum int8_decimal128(PG_FUNCTION_ARGS) 381 | { 382 | int64_t a = PG_GETARG_INT64(0); 383 | char s[128]; 384 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 385 | 386 | sprintf(s, INT64_FORMAT, a); 387 | decQuadFromString(pret, s, &dc); 388 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal128 syntax:\"%s\"", s); 389 | PG_RETURN_P_DECIMAL128(pret); 390 | } 391 | 392 | PG_FUNCTION_INFO_V1(decimal128_numeric); 393 | Datum decimal128_numeric(PG_FUNCTION_ARGS) 394 | { 395 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 396 | Datum ret; 397 | char s[DECQUAD_String]; 398 | 399 | decQuadToString(pa, s); 400 | ret = DirectFunctionCall3(numeric_in, 401 | CStringGetDatum(s), Int32GetDatum(0), Int32GetDatum(-1)); 402 | PG_RETURN_DATUM(ret); 403 | } 404 | 405 | PG_FUNCTION_INFO_V1(numeric_decimal128); 406 | Datum numeric_decimal128(PG_FUNCTION_ARGS) 407 | { 408 | char *buf; 409 | DECLARE_PRET_128_WITH_CTXT(pret, DEC_INIT_DECIMAL128); 410 | 411 | buf = DatumGetCString(DirectFunctionCall1(numeric_out, PG_GETARG_DATUM(0))); 412 | decQuadFromString(pret, buf, &dc); 413 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal128:\"%s\"", buf); 414 | pfree(buf); 415 | PG_RETURN_P_DECIMAL128(pret); 416 | } 417 | 418 | -------------------------------------------------------------------------------- /decimal64.c: -------------------------------------------------------------------------------- 1 | #include "decimal.h" 2 | 3 | // 4 | // Forward declaritions. 5 | // 6 | Datum decimal64_in(PG_FUNCTION_ARGS); 7 | Datum decimal64_out(PG_FUNCTION_ARGS); 8 | Datum decimal64_add(PG_FUNCTION_ARGS); 9 | Datum decimal64_sub(PG_FUNCTION_ARGS); 10 | Datum decimal64_mul(PG_FUNCTION_ARGS); 11 | Datum decimal64_div(PG_FUNCTION_ARGS); 12 | Datum decimal64_uplus(PG_FUNCTION_ARGS); 13 | Datum decimal64_uminus(PG_FUNCTION_ARGS); 14 | Datum decimal64_abs(PG_FUNCTION_ARGS); 15 | Datum decimal64_lt(PG_FUNCTION_ARGS); 16 | Datum decimal64_le(PG_FUNCTION_ARGS); 17 | Datum decimal64_gt(PG_FUNCTION_ARGS); 18 | Datum decimal64_ge(PG_FUNCTION_ARGS); 19 | Datum decimal64_eq(PG_FUNCTION_ARGS); 20 | Datum decimal64_ne(PG_FUNCTION_ARGS); 21 | Datum decimal64_cmp(PG_FUNCTION_ARGS); 22 | Datum decimal64_hash(PG_FUNCTION_ARGS); 23 | Datum decimal64_smaller(PG_FUNCTION_ARGS); 24 | Datum decimal64_larger(PG_FUNCTION_ARGS); 25 | Datum decimal64_inc(PG_FUNCTION_ARGS); 26 | Datum decimal64_round(PG_FUNCTION_ARGS); 27 | Datum decimal64_decimal128(PG_FUNCTION_ARGS); 28 | Datum decimal128_decimal64(PG_FUNCTION_ARGS); 29 | Datum decimal64_float4(PG_FUNCTION_ARGS); 30 | Datum float4_decimal64(PG_FUNCTION_ARGS); 31 | Datum decimal64_float8(PG_FUNCTION_ARGS); 32 | Datum float8_decimal64(PG_FUNCTION_ARGS); 33 | Datum int4_decimal64(PG_FUNCTION_ARGS); 34 | Datum int8_decimal64(PG_FUNCTION_ARGS); 35 | Datum decimal64_numeric(PG_FUNCTION_ARGS); 36 | Datum numeric_decimal64(PG_FUNCTION_ARGS); 37 | 38 | PG_FUNCTION_INFO_V1(decimal64_in); 39 | Datum decimal64_in(PG_FUNCTION_ARGS) 40 | { 41 | char *s = PG_GETARG_CSTRING(0); 42 | PGDecimal64 ret; 43 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 44 | 45 | decDoubleFromString(&ret, s, &dc); 46 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal64 syntax:%s", s); 47 | 48 | PG_RETURN_DECIMAL64(ret); 49 | } 50 | 51 | PG_FUNCTION_INFO_V1(decimal64_out); 52 | Datum decimal64_out(PG_FUNCTION_ARGS) 53 | { 54 | PGDecimal64 d = PG_GETARG_DECIMAL64(0); 55 | char *s = (char *) palloc(DECDOUBLE_String); 56 | if (!s) { 57 | elog(ERROR, "OOM"); 58 | } 59 | 60 | decDoubleToString(&d, s); 61 | PG_RETURN_CSTRING(s); 62 | } 63 | 64 | PG_FUNCTION_INFO_V1(decimal64_add); 65 | Datum decimal64_add(PG_FUNCTION_ARGS) 66 | { 67 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 68 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 69 | PGDecimal64 ret; 70 | 71 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 72 | decDoubleAdd(&ret, &a, &b, &dc); 73 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal64 overflow"); 74 | 75 | PG_RETURN_DECIMAL64(ret); 76 | } 77 | 78 | PG_FUNCTION_INFO_V1(decimal64_sub); 79 | Datum decimal64_sub(PG_FUNCTION_ARGS) 80 | { 81 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 82 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 83 | PGDecimal64 ret; 84 | 85 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 86 | decDoubleSubtract(&ret, &a, &b, &dc); 87 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal64 overflow"); 88 | 89 | PG_RETURN_DECIMAL64(ret); 90 | } 91 | 92 | PG_FUNCTION_INFO_V1(decimal64_mul); 93 | Datum decimal64_mul(PG_FUNCTION_ARGS) 94 | { 95 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 96 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 97 | PGDecimal64 ret; 98 | 99 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 100 | decDoubleMultiply(&ret, &a, &b, &dc); 101 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal64 overflow"); 102 | 103 | PG_RETURN_DECIMAL64(ret); 104 | } 105 | 106 | PG_FUNCTION_INFO_V1(decimal64_div); 107 | Datum decimal64_div(PG_FUNCTION_ARGS) 108 | { 109 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 110 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 111 | PGDecimal64 ret; 112 | 113 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 114 | decDoubleDivide(&ret, &a, &b, &dc); 115 | DEC_CHK_STATUS(dc, DEC_STATUS_DIV, "decimal64 division error"); 116 | 117 | PG_RETURN_DECIMAL64(ret); 118 | } 119 | 120 | PG_FUNCTION_INFO_V1(decimal64_uplus); 121 | Datum decimal64_uplus(PG_FUNCTION_ARGS) 122 | { 123 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 124 | PGDecimal64 ret; 125 | 126 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 127 | decDoublePlus(&ret, &a, &dc); 128 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal64 overflow"); 129 | 130 | PG_RETURN_DECIMAL64(ret); 131 | } 132 | 133 | PG_FUNCTION_INFO_V1(decimal64_uminus); 134 | Datum decimal64_uminus(PG_FUNCTION_ARGS) 135 | { 136 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 137 | PGDecimal64 ret; 138 | 139 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 140 | decDoubleMinus(&ret, &a, &dc); 141 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal64 overflow"); 142 | 143 | PG_RETURN_DECIMAL64(ret); 144 | } 145 | 146 | PG_FUNCTION_INFO_V1(decimal64_abs); 147 | Datum decimal64_abs(PG_FUNCTION_ARGS) 148 | { 149 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 150 | PGDecimal64 ret; 151 | 152 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 153 | decDoubleAbs(&ret, &a, &dc); 154 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal64 overflow"); 155 | 156 | PG_RETURN_DECIMAL64(ret); 157 | } 158 | 159 | 160 | static int decimal64_cmp_internal(const PGDecimal64* a, const PGDecimal64* b) 161 | { 162 | PGDecimal64 result; 163 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 164 | decDoubleCompare(&result, a, b, &dc); 165 | 166 | if (decDoubleIsPositive(&result)) { 167 | return 1; 168 | } else if (decDoubleIsZero(&result)) { 169 | return 0; 170 | } else if (decDoubleIsNegative(&result)) { 171 | return -1; 172 | } else { 173 | elog(ERROR, "invalid decimal64 in compare"); 174 | } 175 | } 176 | 177 | PG_FUNCTION_INFO_V1(decimal64_lt); 178 | Datum decimal64_lt(PG_FUNCTION_ARGS) 179 | { 180 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 181 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 182 | int ret = decimal64_cmp_internal(&a, &b); 183 | PG_RETURN_BOOL(ret < 0); 184 | } 185 | 186 | PG_FUNCTION_INFO_V1(decimal64_le); 187 | Datum decimal64_le(PG_FUNCTION_ARGS) 188 | { 189 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 190 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 191 | int ret = decimal64_cmp_internal(&a, &b); 192 | PG_RETURN_BOOL(ret <= 0); 193 | } 194 | 195 | PG_FUNCTION_INFO_V1(decimal64_gt); 196 | Datum decimal64_gt(PG_FUNCTION_ARGS) 197 | { 198 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 199 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 200 | int ret = decimal64_cmp_internal(&a, &b); 201 | PG_RETURN_BOOL(ret > 0); 202 | } 203 | 204 | PG_FUNCTION_INFO_V1(decimal64_ge); 205 | Datum decimal64_ge(PG_FUNCTION_ARGS) 206 | { 207 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 208 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 209 | int ret = decimal64_cmp_internal(&a, &b); 210 | PG_RETURN_BOOL(ret >= 0); 211 | } 212 | 213 | PG_FUNCTION_INFO_V1(decimal64_eq); 214 | Datum decimal64_eq(PG_FUNCTION_ARGS) 215 | { 216 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 217 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 218 | int ret = decimal64_cmp_internal(&a, &b); 219 | PG_RETURN_BOOL(ret == 0); 220 | } 221 | 222 | PG_FUNCTION_INFO_V1(decimal64_ne); 223 | Datum decimal64_ne(PG_FUNCTION_ARGS) 224 | { 225 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 226 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 227 | int ret = decimal64_cmp_internal(&a, &b); 228 | PG_RETURN_BOOL(ret != 0); 229 | } 230 | 231 | PG_FUNCTION_INFO_V1(decimal64_cmp); 232 | Datum decimal64_cmp(PG_FUNCTION_ARGS) 233 | { 234 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 235 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 236 | int ret = decimal64_cmp_internal(&a, &b); 237 | PG_RETURN_INT32(ret); 238 | } 239 | 240 | PG_FUNCTION_INFO_V1(decimal64_hash); 241 | Datum decimal64_hash(PG_FUNCTION_ARGS) 242 | { 243 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 244 | PGDecimal64 n; 245 | Datum ret; 246 | 247 | if (decDoubleIsZero(&a)) { 248 | decDoubleZero(&n); 249 | } else { 250 | decDoubleCanonical(&n, &a); 251 | } 252 | 253 | ret = hash_any((unsigned char *)&n, sizeof(n)); 254 | return ret; 255 | } 256 | 257 | PG_FUNCTION_INFO_V1(decimal64_smaller); 258 | Datum decimal64_smaller(PG_FUNCTION_ARGS) 259 | { 260 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 261 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 262 | PGDecimal64 ret; 263 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 264 | 265 | decDoubleMin(&ret, &a, &b, &dc); 266 | PG_RETURN_DECIMAL64(ret); 267 | } 268 | 269 | PG_FUNCTION_INFO_V1(decimal64_larger); 270 | Datum decimal64_larger(PG_FUNCTION_ARGS) 271 | { 272 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 273 | PGDecimal64 b = PG_GETARG_DECIMAL64(1); 274 | PGDecimal64 ret; 275 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 276 | 277 | decDoubleMax(&ret, &a, &b, &dc); 278 | PG_RETURN_DECIMAL64(ret); 279 | } 280 | 281 | PG_FUNCTION_INFO_V1(decimal64_inc); 282 | Datum decimal64_inc(PG_FUNCTION_ARGS) 283 | { 284 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 285 | PGDecimal64 one; 286 | PGDecimal64 ret; 287 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 288 | 289 | decDoubleFromInt32(&one, 1); 290 | decDoubleAdd(&ret, &a, &one, &dc); 291 | PG_RETURN_DECIMAL64(ret); 292 | } 293 | 294 | PG_FUNCTION_INFO_V1(decimal64_round); 295 | Datum decimal64_round(PG_FUNCTION_ARGS) 296 | { 297 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 298 | int32_t scale = PG_GETARG_INT32(1); 299 | PGDecimal64 ret; 300 | 301 | static PGDecimal64* pquan; 302 | static PGDecimal64 quan[16]; 303 | 304 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 305 | 306 | if (pquan == 0) { 307 | int i; 308 | PGDecimal64 ten; 309 | decDoubleFromInt32(&ten, 10); 310 | decDoubleFromInt32(&quan[0], 1); 311 | for (i = 1; i < 16; i++) { 312 | decDoubleDivide(&quan[i], &quan[i-1], &ten, &dc); 313 | } 314 | pquan = &quan[0]; 315 | } 316 | 317 | if (scale < 0 || scale > 15) { 318 | elog(ERROR, "decimal32 rounding to an invalid scale"); 319 | } 320 | 321 | decDoubleQuantize(&ret, &a, &quan[scale], &dc); 322 | PG_RETURN_DECIMAL64(ret); 323 | } 324 | 325 | PG_FUNCTION_INFO_V1(decimal64_decimal128); 326 | Datum decimal64_decimal128(PG_FUNCTION_ARGS) 327 | { 328 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 329 | DECLARE_PRET_128(pret); 330 | 331 | decDoubleToWider(&a, pret); 332 | PG_RETURN_P_DECIMAL128(pret); 333 | } 334 | 335 | PG_FUNCTION_INFO_V1(decimal128_decimal64); 336 | Datum decimal128_decimal64(PG_FUNCTION_ARGS) 337 | { 338 | PGDecimal128* pa = PG_GETARG_P_DECIMAL128(0); 339 | PGDecimal64 ret; 340 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 341 | 342 | decDoubleFromWider(&ret, pa, &dc); 343 | DEC_CHK_STATUS(dc, DEC_STATUS_OFUF, "decimal128 to decimal64 over/under flow"); 344 | PG_RETURN_DECIMAL64(ret); 345 | } 346 | 347 | PG_FUNCTION_INFO_V1(decimal64_float4); 348 | Datum decimal64_float4(PG_FUNCTION_ARGS) 349 | { 350 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 351 | char s[DECDOUBLE_String]; 352 | char *endp = 0; 353 | float f; 354 | 355 | decDoubleToString(&a, s); 356 | 357 | errno = 0; 358 | f = strtof(s, &endp); 359 | 360 | if (endp == s || errno != 0) { 361 | elog(ERROR, "cannot convert decimal64 to float4"); 362 | } 363 | 364 | PG_RETURN_FLOAT4(f); 365 | } 366 | 367 | PG_FUNCTION_INFO_V1(float4_decimal64); 368 | Datum float4_decimal64(PG_FUNCTION_ARGS) 369 | { 370 | float f = PG_GETARG_FLOAT4(0); 371 | PGDecimal64 ret; 372 | char s[128]; 373 | 374 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 375 | 376 | sprintf(s, "%.*g", FLT_DIG, f); 377 | decDoubleFromString(&ret, s, &dc); 378 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal64 syntax:\"%s\"", s); 379 | 380 | PG_RETURN_DECIMAL64(ret); 381 | } 382 | 383 | PG_FUNCTION_INFO_V1(decimal64_float8); 384 | Datum decimal64_float8(PG_FUNCTION_ARGS) 385 | { 386 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 387 | char s[DECDOUBLE_String]; 388 | char *endp = 0; 389 | double d; 390 | 391 | decDoubleToString(&a, s); 392 | 393 | errno = 0; 394 | d = strtod(s, &endp); 395 | 396 | if (endp == s || errno != 0) { 397 | elog(ERROR, "cannot convert decimal64 to float8"); 398 | } 399 | 400 | PG_RETURN_FLOAT8(d); 401 | } 402 | 403 | PG_FUNCTION_INFO_V1(float8_decimal64); 404 | Datum float8_decimal64(PG_FUNCTION_ARGS) 405 | { 406 | double d = PG_GETARG_FLOAT8(0); 407 | PGDecimal64 ret; 408 | char s[128]; 409 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 410 | 411 | sprintf(s, "%.*g", DBL_DIG, d); 412 | decDoubleFromString(&ret, s, &dc); 413 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal64 syntax:\"%s\"", s); 414 | 415 | PG_RETURN_DECIMAL64(ret); 416 | } 417 | 418 | PG_FUNCTION_INFO_V1(int4_decimal64); 419 | Datum int4_decimal64(PG_FUNCTION_ARGS) 420 | { 421 | int32_t a = PG_GETARG_INT32(0); 422 | PGDecimal64 ret; 423 | 424 | decDoubleFromInt32(&ret, a); 425 | PG_RETURN_DECIMAL64(ret); 426 | } 427 | 428 | PG_FUNCTION_INFO_V1(int8_decimal64); 429 | Datum int8_decimal64(PG_FUNCTION_ARGS) 430 | { 431 | int64_t a = PG_GETARG_INT64(0); 432 | PGDecimal64 ret; 433 | char s[128]; 434 | 435 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 436 | sprintf(s, INT64_FORMAT, a); 437 | decDoubleFromString(&ret, s, &dc); 438 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal64 syntax:\"%s\"", s); 439 | PG_RETURN_DECIMAL64(ret); 440 | } 441 | 442 | PG_FUNCTION_INFO_V1(decimal64_numeric); 443 | Datum decimal64_numeric(PG_FUNCTION_ARGS) 444 | { 445 | PGDecimal64 a = PG_GETARG_DECIMAL64(0); 446 | Datum ret; 447 | char s[DECDOUBLE_String]; 448 | 449 | decDoubleToString(&a, s); 450 | ret = DirectFunctionCall3(numeric_in, 451 | CStringGetDatum(s), Int32GetDatum(0), Int32GetDatum(-1)); 452 | PG_RETURN_DATUM(ret); 453 | } 454 | 455 | PG_FUNCTION_INFO_V1(numeric_decimal64); 456 | Datum numeric_decimal64(PG_FUNCTION_ARGS) 457 | { 458 | char *buf; 459 | PGDecimal64 ret; 460 | DECLARE_DEC_CTXT(DEC_INIT_DECIMAL64); 461 | 462 | buf = DatumGetCString(DirectFunctionCall1(numeric_out, PG_GETARG_DATUM(0))); 463 | decDoubleFromString(&ret, buf, &dc); 464 | DEC_CHK_STATUS(dc, DEC_STATUS_ALL, "invalid decimal64:\"%s\"", buf); 465 | pfree(buf); 466 | PG_RETURN_DECIMAL64(ret); 467 | } 468 | 469 | --------------------------------------------------------------------------------