├── .vimrc ├── debian ├── pgversions ├── source │ └── format ├── tests │ ├── installcheck │ └── control ├── watch ├── gitlab-ci.yml ├── upstream │ └── metadata ├── rules ├── NEWS ├── copyright ├── control ├── control.in └── changelog ├── sql ├── extension.sql ├── binary.sql ├── unicode.sql ├── prefix.sql ├── language_functions.sql ├── round.sql ├── custom.sql ├── time.sql ├── convert.sql ├── temperature.sql ├── iec.sql ├── tables.sql ├── units.sql ├── compare.sql ├── upgrade.sql ├── crosstab.sql ├── derived.sql ├── functions.sql ├── unit.sql └── aggregate.sql ├── expected ├── extension.out ├── unicode.out ├── round_1.out ├── round.out ├── binary.out ├── language_functions.out ├── custom.out ├── upgrade.out ├── tables.out ├── prefix.out ├── iec.out ├── temperature.out ├── unit.out ├── convert.out ├── aggregate.out ├── compare.out ├── functions.out ├── derived.out └── time.out ├── unit--4--5.sql.in ├── .gitignore ├── unit.control ├── unit--3--4.sql.in ├── dump-units.sh ├── do ├── powers.c ├── float8out_unit.h ├── .github └── workflows │ └── regression.yml ├── unit--5--6.sql.in ├── unit--2--3.sql.in ├── powers.h ├── Makefile ├── unit--1--2.sql ├── defined_units.h ├── NEWS.md ├── unit_prefixes.data ├── unit--6--7.sql.in ├── definitions.unresolved ├── unit.h ├── unitparse.tab.h ├── unitparse.y ├── load-units.pl ├── unitparse.l └── definitions.units.patch /.vimrc: -------------------------------------------------------------------------------- 1 | set ts=4 sw=4 2 | -------------------------------------------------------------------------------- /debian/pgversions: -------------------------------------------------------------------------------- 1 | 9.5+ 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/tests/installcheck: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pg_buildext -i '--locale=C.UTF-8' installcheck 3 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | https://github.com/df7cb/postgresql-unit/tags .*/([0-9.]*).tar.gz 3 | -------------------------------------------------------------------------------- /debian/gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: https://salsa.debian.org/postgresql/postgresql-common/raw/master/gitlab/gitlab-ci.yml 2 | -------------------------------------------------------------------------------- /sql/extension.sql: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | CREATE EXTENSION IF NOT EXISTS unit; 3 | RESET client_min_messages; 4 | -------------------------------------------------------------------------------- /expected/extension.out: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | CREATE EXTENSION IF NOT EXISTS unit; 3 | RESET client_min_messages; 4 | -------------------------------------------------------------------------------- /debian/tests/control: -------------------------------------------------------------------------------- 1 | Depends: 2 | postgresql-common-dev, 3 | @, 4 | Tests: 5 | installcheck, 6 | Restrictions: 7 | allow-stderr, 8 | -------------------------------------------------------------------------------- /unit--4--5.sql.in: -------------------------------------------------------------------------------- 1 | CREATE FUNCTION round(unit) 2 | RETURNS unit 3 | AS '$libdir/unit', 'unit_round' 4 | LANGUAGE C IMMUTABLE STRICT; 5 | -------------------------------------------------------------------------------- /debian/upstream/metadata: -------------------------------------------------------------------------------- 1 | --- 2 | Bug-Database: https://github.com/df7cb/postgresql-unit/issues 3 | Bug-Submit: https://github.com/df7cb/postgresql-unit/issues/new 4 | Repository-Browse: https://github.com/df7cb/postgresql-unit 5 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | override_dh_installdocs: 4 | dh_installdocs --all README.* 5 | 6 | override_dh_pgxs_test: 7 | # defer testing to autopkgtest, the data tables are not in /usr/share/postgresql yet 8 | 9 | %: 10 | dh $@ --with pgxs 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | powers 2 | regression.diffs 3 | regression.out 4 | results/ 5 | tags 6 | unit--*.sql 7 | unit.o 8 | unitparse.tab.c 9 | unitparse.tab.h 10 | unitparse.tab.o 11 | unitparse.yy.c 12 | unitparse.yy.o 13 | unit.so 14 | unittest 15 | unittest.o 16 | *.bc 17 | -------------------------------------------------------------------------------- /unit.control: -------------------------------------------------------------------------------- 1 | default_version = '7' 2 | comment = 'SI units extension' 3 | # the unit_prefixes/unit_units tables can be installed in an arbitrary schema, 4 | # but can not be relocated later: 5 | relocatable = false 6 | # unit_load() is written in plpgsql: 7 | requires = 'plpgsql' 8 | -------------------------------------------------------------------------------- /sql/binary.sql: -------------------------------------------------------------------------------- 1 | SELECT unit_send('0'); 2 | SELECT unit_send(-'0'::unit); 3 | SELECT unit_send('1 m'::unit); 4 | SELECT unit_send('1 kg^-1'::unit); 5 | SELECT unit_send('1 s'::unit); 6 | SELECT unit_send('1 A^-1'::unit); 7 | SELECT unit_send('1 K'::unit); 8 | SELECT unit_send('1 mol^-1'::unit); 9 | SELECT unit_send('1 cd'::unit); 10 | SELECT unit_send('1 B^-1'::unit); 11 | -------------------------------------------------------------------------------- /debian/NEWS: -------------------------------------------------------------------------------- 1 | postgresql-unit (7.5-1) unstable; urgency=medium 2 | 3 | In previous postgresql-unit versions, 'h' used to mean "hour". To mirror the 4 | behavior of GNU units, where we get the unit definitions from, 'h' has been 5 | changed to mean the Planck constant. Use 'hr' or 'hour' for hours. 6 | 7 | -- Christoph Berg Wed, 18 May 2022 16:09:19 +0200 8 | -------------------------------------------------------------------------------- /sql/unicode.sql: -------------------------------------------------------------------------------- 1 | SET unit.output_superscript = on; 2 | 3 | SELECT 'm⁰'::unit; 4 | SELECT 'm¹'::unit; 5 | SELECT 'm²'::unit; 6 | SELECT 'm⁻²'::unit; 7 | SELECT 'm³'::unit; 8 | SELECT 'm⁴'::unit; 9 | SELECT 'm⁵'::unit; 10 | SELECT 'm⁶'::unit; 11 | SELECT 'm⁷'::unit; 12 | SELECT 'm⁸'::unit; 13 | SELECT 'm⁹'::unit; 14 | SELECT 'm³⁴'::unit; 15 | SELECT 'm⁻⁵⁶'::unit; 16 | SELECT 'm⁺⁷⁸'::unit; 17 | -------------------------------------------------------------------------------- /sql/prefix.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | f, meter(f), kilogram(f) 3 | FROM 4 | factors; 5 | 6 | SELECT 7 | p, (p||'m/m')::unit AS factor, 8 | ('0.001 '||p||'m')::unit AS "0.001 m", 9 | (p||'m')::unit AS "meter", 10 | ('1000 '||p||'m')::unit AS "1000 m", 11 | ('0.001 '||p||'g')::unit AS "0.001 g", 12 | (p||'g')::unit AS "gram", 13 | ('1000 '||p||'g')::unit AS "1000 g" 14 | FROM 15 | prefixes; 16 | -------------------------------------------------------------------------------- /sql/language_functions.sql: -------------------------------------------------------------------------------- 1 | select 'sqrt(4 m^2)'::unit; 2 | select 'sqrt(-4 m^2)'::unit; 3 | select 'sqrt(4 m^3)'::unit; 4 | 5 | select 'exp(1)'::unit; 6 | select 'exp(1 A)'::unit; 7 | 8 | select 'ln(2.7)'::unit; 9 | select 'ln(-2.7)'::unit; 10 | select 'ln(2.7 s)'::unit; 11 | 12 | select 'asin(1)'::unit; 13 | select 'asin(2)'::unit; 14 | select 'asin(1 kg)'::unit; 15 | 16 | select 'tan(1)'::unit; 17 | select 'tan(1 mol)'::unit; 18 | -------------------------------------------------------------------------------- /unit--3--4.sql.in: -------------------------------------------------------------------------------- 1 | -- convert @ from (unit, cstring) to (unit, text) 2 | 3 | DROP OPERATOR @ (unit, cstring); 4 | DROP FUNCTION unit_at(unit, cstring); 5 | 6 | CREATE FUNCTION unit_at(unit, text) 7 | RETURNS cstring 8 | SET search_path = @extschema@ 9 | AS '$libdir/unit', 'unit_at_text' 10 | LANGUAGE C IMMUTABLE STRICT; 11 | 12 | CREATE OPERATOR @ ( 13 | leftarg = unit, 14 | rightarg = text, 15 | procedure = unit_at 16 | ); 17 | -------------------------------------------------------------------------------- /dump-units.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # output intentionally not ordered so the initial load order from definitions.units is preserved 4 | psql <<-EOT 5 | SET extra_float_digits = 3; 6 | SET unit.output_base_units = on; 7 | \copy (SELECT prefix, factor, definition, dump FROM unit_prefixes ORDER BY ordering) to 'unit_prefixes.data' 8 | \copy (SELECT name, unit, shift, definition, dump FROM unit_units ORDER BY ordering) to 'unit_units.data' 9 | EOT 10 | -------------------------------------------------------------------------------- /sql/round.sql: -------------------------------------------------------------------------------- 1 | SELECT round(ampere(1.5)); 2 | SELECT round(candela(-0.5)); 3 | SELECT round(meter(1500.5)); 4 | 5 | SET extra_float_digits = 0; 6 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 7 | SET extra_float_digits = 3; 8 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 9 | SET extra_float_digits = -3; 10 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 11 | SET extra_float_digits = -12; 12 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 13 | SET extra_float_digits = -15; 14 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 15 | -------------------------------------------------------------------------------- /do: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eux 4 | 5 | export PGDATABASE=postgres 6 | 7 | for PGVERSION in ${*:-15 14 13 12 11 10 9.6 9.5}; do 8 | echo 9 | echo "### $PGVERSION ###" 10 | PG_CONFIG=/usr/lib/postgresql/$PGVERSION/bin/pg_config 11 | export PGCLUSTER="$PGVERSION/main" 12 | export PGPORT="54${PGVERSION/./}" 13 | [ "$PGVERSION" = "15" ] && unset PGPORT # default version 14 | 15 | make clean 16 | make PG_CONFIG=$PG_CONFIG PROFILE="-Werror" 17 | sudo make install PG_CONFIG=$PG_CONFIG 18 | psql -c "DROP EXTENSION IF EXISTS unit CASCADE" 19 | if ! make installcheck REGRESS_OPTS="--use-existing --dbname=postgres" PG_CONFIG=$PG_CONFIG; then 20 | cat regression.diffs 21 | exit 1 22 | fi 23 | done 24 | -------------------------------------------------------------------------------- /sql/custom.sql: -------------------------------------------------------------------------------- 1 | SELECT '1 foobar'::unit; 2 | SELECT unit_is_hashed('foobar'); 3 | INSERT INTO unit_prefixes VALUES ('foo', 42); 4 | SELECT '1 foobar'::unit; 5 | SELECT unit_is_hashed('foobar'); 6 | SELECT * FROM unit_prefixes WHERE dump; 7 | 8 | INSERT INTO unit_units VALUES ('legobrick', '9.6 mm'); 9 | SELECT unit_is_hashed('legobricks'); 10 | SELECT '1 m'::unit @ 'legobricks' AS one_meter; 11 | SELECT unit_is_hashed('legobricks'); 12 | SELECT * FROM unit_units WHERE dump; 13 | 14 | UPDATE unit_units SET unit = '19.1 mm' WHERE name = 'legobrick'; -- Duplo size 15 | SELECT '2 legobricks'::unit AS old_size; 16 | SELECT unit_reset(); 17 | SELECT unit_is_hashed('legobricks'); 18 | SELECT '2 legobricks'::unit AS new_size; 19 | -------------------------------------------------------------------------------- /powers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define p(n, p, x) printf("#define POWER_%-3d %-22a /* %s %-22a */\n", n, nextafter(x, 0), p, x) 5 | 6 | int main () 7 | { 8 | printf("/* powers-of-10 constants */\n"); 9 | printf("/* constants here are rounded down so >= comparisons work better */\n\n"); 10 | 11 | 12 | p(33, " ", 1e33); 13 | p(30, "Q", 1e30); 14 | p(27, "R", 1e27); 15 | p(24, "Y", 1e24); 16 | p(21, "Z", 1e21); 17 | p(18, "E", 1e18); 18 | p(15, "P", 1e15); 19 | p(12, "T", 1e12); 20 | p(9, "G", 1e9); 21 | p(6, "M", 1e6); 22 | p(3, "k", 1e3); 23 | p(0, " ", 1e0); 24 | p(-3, "m", 1e-3); 25 | p(-6, "µ", 1e-6); 26 | p(-9, "n", 1e-9); 27 | p(-12, "p", 1e-12); 28 | p(-15, "f", 1e-15); 29 | p(-18, "a", 1e-18); 30 | p(-21, "z", 1e-21); 31 | p(-24, "y", 1e-24); 32 | p(-27, "r", 1e-27); 33 | p(-30, "q", 1e-30); 34 | p(-33, " ", 1e-33); 35 | } 36 | -------------------------------------------------------------------------------- /float8out_unit.h: -------------------------------------------------------------------------------- 1 | /* minimal version of PG 9.6's float8out_internal function for use in 9.4 and 9.5 */ 2 | /* With the adaption of ryn in PG 12, it is now used for all versions */ 3 | 4 | #define MAXDOUBLEWIDTH 128 5 | extern int extra_float_digits; 6 | 7 | static char * 8 | float8out_unit(double num) 9 | { 10 | char *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1); 11 | int extradigits = 12 | #if PG_VERSION_NUM >= 120000 13 | extra_float_digits == 1 ? 0 : extra_float_digits; 14 | #else 15 | extra_float_digits; 16 | #endif 17 | int ndig = DBL_DIG + extradigits; 18 | 19 | if (isnan(num)) 20 | return strcpy(ascii, "NaN"); 21 | if (!isfinite(num)) { 22 | if (num > 0) 23 | return strcpy(ascii, "Infinity"); 24 | else 25 | return strcpy(ascii, "-Infinity"); 26 | } 27 | 28 | if (ndig < 1) 29 | ndig = 1; 30 | 31 | snprintf(ascii, MAXDOUBLEWIDTH + 1, "%.*g", ndig, num); 32 | 33 | return ascii; 34 | } 35 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | 3 | Files: * 4 | Copyright: (C) 2016-2018 Christoph Berg 5 | License: GPL-3+ 6 | 7 | Files: definitions.units* 8 | Copyright: (C) 1996-2002, 2004-2017 9 | Free Software Foundation, Inc 10 | License: GPL-3+ 11 | 12 | License: GPL-3+ 13 | This program is free software; you can redistribute it and/or modify 14 | it under the terms of the GNU General Public License as published by 15 | the Free Software Foundation; either version 3 of the License, or 16 | (at your option) any later version. 17 | . 18 | This program is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | GNU General Public License for more details. 22 | . 23 | The full text of the GPL 3 is distributed as in 24 | /usr/share/common-licenses/GPL-3 on Debian systems. 25 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: postgresql-unit 2 | Section: database 3 | Priority: optional 4 | Maintainer: Christoph Berg 5 | Build-Depends: 6 | debhelper-compat (= 13), 7 | architecture-is-64-bit , 8 | bison, 9 | flex, 10 | postgresql-server-dev-all (>= 217~), 11 | Standards-Version: 4.7.2 12 | Vcs-Git: https://github.com/df7cb/postgresql-unit.git 13 | Vcs-Browser: https://github.com/df7cb/postgresql-unit 14 | Homepage: https://github.com/df7cb/postgresql-unit 15 | 16 | Package: postgresql-18-unit 17 | Architecture: any 18 | Depends: 19 | ${misc:Depends}, 20 | ${postgresql:Depends}, 21 | ${shlibs:Depends}, 22 | Breaks: 23 | ${postgresql:Breaks}, 24 | Description: SI Units for PostgreSQL 25 | postgresql-unit implements a PostgreSQL datatype for SI units, plus byte. The 26 | base units can be combined to named and unnamed derived units using operators 27 | defined in the PostgreSQL type system. SI prefixes are used for input and 28 | output, and quantities can be converted to arbitrary scale. 29 | -------------------------------------------------------------------------------- /debian/control.in: -------------------------------------------------------------------------------- 1 | Source: postgresql-unit 2 | Section: database 3 | Priority: optional 4 | Maintainer: Christoph Berg 5 | Build-Depends: 6 | debhelper-compat (= 13), 7 | architecture-is-64-bit , 8 | bison, 9 | flex, 10 | postgresql-server-dev-all (>= 217~), 11 | Standards-Version: 4.7.2 12 | Vcs-Git: https://github.com/df7cb/postgresql-unit.git 13 | Vcs-Browser: https://github.com/df7cb/postgresql-unit 14 | Homepage: https://github.com/df7cb/postgresql-unit 15 | 16 | Package: postgresql-PGVERSION-unit 17 | Architecture: any 18 | Depends: 19 | ${misc:Depends}, 20 | ${postgresql:Depends}, 21 | ${shlibs:Depends}, 22 | Breaks: 23 | ${postgresql:Breaks}, 24 | Description: SI Units for PostgreSQL 25 | postgresql-unit implements a PostgreSQL datatype for SI units, plus byte. The 26 | base units can be combined to named and unnamed derived units using operators 27 | defined in the PostgreSQL type system. SI prefixes are used for input and 28 | output, and quantities can be converted to arbitrary scale. 29 | -------------------------------------------------------------------------------- /expected/unicode.out: -------------------------------------------------------------------------------- 1 | SET unit.output_superscript = on; 2 | SELECT 'm⁰'::unit; 3 | unit 4 | ------ 5 | 1 6 | (1 row) 7 | 8 | SELECT 'm¹'::unit; 9 | unit 10 | ------ 11 | 1 m 12 | (1 row) 13 | 14 | SELECT 'm²'::unit; 15 | unit 16 | ------ 17 | 1 m² 18 | (1 row) 19 | 20 | SELECT 'm⁻²'::unit; 21 | unit 22 | ------- 23 | 1 m⁻² 24 | (1 row) 25 | 26 | SELECT 'm³'::unit; 27 | unit 28 | ------ 29 | 1 m³ 30 | (1 row) 31 | 32 | SELECT 'm⁴'::unit; 33 | unit 34 | ------ 35 | 1 m⁴ 36 | (1 row) 37 | 38 | SELECT 'm⁵'::unit; 39 | unit 40 | ------ 41 | 1 m⁵ 42 | (1 row) 43 | 44 | SELECT 'm⁶'::unit; 45 | unit 46 | ------ 47 | 1 m⁶ 48 | (1 row) 49 | 50 | SELECT 'm⁷'::unit; 51 | unit 52 | ------ 53 | 1 m⁷ 54 | (1 row) 55 | 56 | SELECT 'm⁸'::unit; 57 | unit 58 | ------ 59 | 1 m⁸ 60 | (1 row) 61 | 62 | SELECT 'm⁹'::unit; 63 | unit 64 | ------ 65 | 1 m⁹ 66 | (1 row) 67 | 68 | SELECT 'm³⁴'::unit; 69 | unit 70 | ------- 71 | 1 m³⁴ 72 | (1 row) 73 | 74 | SELECT 'm⁻⁵⁶'::unit; 75 | unit 76 | -------- 77 | 1 m⁻⁵⁶ 78 | (1 row) 79 | 80 | SELECT 'm⁺⁷⁸'::unit; 81 | unit 82 | ------- 83 | 1 m⁷⁸ 84 | (1 row) 85 | 86 | -------------------------------------------------------------------------------- /.github/workflows/regression.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | defaults: 10 | run: 11 | shell: sh 12 | 13 | strategy: 14 | matrix: 15 | pgversion: 16 | - 18 17 | - 17 18 | - 16 19 | - 15 20 | - 14 21 | - 13 22 | - 12 23 | - 11 24 | - 10 25 | - 9.6 26 | - 9.5 27 | 28 | env: 29 | PGVERSION: ${{ matrix.pgversion }} 30 | 31 | steps: 32 | - name: checkout 33 | uses: actions/checkout@v3 34 | 35 | - name: install pg 36 | run: | 37 | sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -v $PGVERSION -p -i 38 | sudo -u postgres createuser -s "$USER" 39 | 40 | - name: build 41 | run: | 42 | make PROFILE="-Werror" 43 | sudo -E make install 44 | 45 | - name: test 46 | run: | 47 | make installcheck 48 | 49 | - name: show regression diffs 50 | if: ${{ failure() }} 51 | run: | 52 | cat regression.diffs 53 | -------------------------------------------------------------------------------- /unit--5--6.sql.in: -------------------------------------------------------------------------------- 1 | GRANT SELECT ON unit_prefixes, unit_units TO PUBLIC; 2 | 3 | CREATE FUNCTION unit_load() 4 | RETURNS VOID 5 | LANGUAGE plpgsql 6 | AS $$BEGIN 7 | DELETE FROM unit_prefixes WHERE dump IS NOT TRUE; 8 | CREATE TEMP TABLE tmp_prefixes (LIKE unit_prefixes) ON COMMIT DROP; 9 | COPY tmp_prefixes (prefix, factor, definition, dump) FROM '@MODULEDIR@/unit_prefixes.data'; 10 | INSERT INTO unit_prefixes 11 | SELECT * FROM tmp_prefixes t WHERE NOT EXISTS 12 | (SELECT prefix FROM unit_prefixes u WHERE u.prefix = t.prefix); 13 | DROP TABLE tmp_prefixes; 14 | 15 | DELETE FROM unit_units WHERE dump IS NOT TRUE; 16 | CREATE TEMP TABLE tmp_units (LIKE unit_units) ON COMMIT DROP; 17 | COPY tmp_units (name, unit, shift, definition, dump) FROM '@MODULEDIR@/unit_units.data'; 18 | INSERT INTO unit_units 19 | SELECT * FROM tmp_units t WHERE NOT EXISTS 20 | (SELECT name FROM unit_units u WHERE u.name = t.name); 21 | DROP TABLE tmp_units; 22 | END;$$; 23 | 24 | COMMENT ON FUNCTION unit_load() IS 'Loads/upgrades the unit_units and unit_prefixes contents from the data files on disk'; 25 | 26 | SELECT unit_load(); 27 | 28 | CREATE FUNCTION unit_at_double(unit, text) 29 | RETURNS double precision 30 | SET search_path = @extschema@ 31 | AS '$libdir/unit', 'unit_at_double' 32 | LANGUAGE C IMMUTABLE STRICT; 33 | 34 | CREATE OPERATOR @@ ( 35 | leftarg = unit, 36 | rightarg = text, 37 | procedure = unit_at_double 38 | ); 39 | -------------------------------------------------------------------------------- /sql/time.sql: -------------------------------------------------------------------------------- 1 | SELECT '1.1 s'::unit; 2 | SELECT '61.1 s'::unit; 3 | SELECT '71.1 s'::unit; 4 | SELECT '601 s'::unit; 5 | SELECT '3601 s'::unit; 6 | SELECT '86400 s'::unit; 7 | SELECT '-86400 s'::unit; 8 | SELECT '86401 s'::unit; 9 | SELECT '-86401 s'::unit; 10 | SELECT '365 d'::unit; 11 | SELECT '-365 d'::unit; 12 | SELECT '3650 d'::unit; 13 | SELECT '3651 d'::unit; 14 | SELECT '3651.5 d'::unit; 15 | 16 | SELECT '00:00:00.1'::unit; 17 | SELECT '00:00:01'::unit; 18 | SELECT '00:01:00'::unit; 19 | SELECT '01:00:00'::unit; 20 | SELECT '0:0:0'::unit; 21 | 22 | SELECT '00:00:01 s'::unit; 23 | SELECT '00:00:01s'::unit; 24 | SELECT '00:00:01 m'::unit; 25 | SELECT '00:01:00 s'::unit; 26 | SELECT '00:01:00.5 s'::unit; 27 | SELECT '01:00:00 s'::unit; 28 | SELECT '1 d + 02:03:04.5 s'::unit; 29 | SELECT '1.1 d'::unit; -- needs ULP clamping 30 | 31 | SELECT name, unit, definition FROM unit_units WHERE dimension(unit) = 'TIME' ORDER BY unit, name COLLATE "C"; 32 | 33 | -- units that differ when pushed through output-input functions 34 | -- (same test as in units.sql, but with time_output_custom = true) 35 | SELECT name, unit, unit::text::unit, definition FROM unit_units WHERE unit::text::unit::text <> unit::text; 36 | 37 | /* custom time format is only used if dimension is time */ 38 | SELECT '1000 s'::unit, '1000 s/m'::unit; 39 | SET unit.time_output_custom = false; 40 | SELECT '1000 s'::unit, '1000 s/m'::unit; 41 | 42 | -- test if 'Gs' is avoided on output 43 | SELECT '1 Gsec'::unit, '1 Gsec/m'::unit; 44 | -------------------------------------------------------------------------------- /sql/convert.sql: -------------------------------------------------------------------------------- 1 | -- length 2 | WITH 3 | l(u) AS (VALUES 4 | ('mm'), 5 | ('m'), 6 | ('km'), 7 | ('in'), 8 | ('ft'), 9 | ('yd'), 10 | ('mi')) 11 | SELECT 12 | l1.u, l2.u, l1.u::unit @ l2.u 13 | FROM 14 | l l1 CROSS JOIN l l2 15 | \crosstabview 16 | 17 | /* revert to pre-12 default */ 18 | SET extra_float_digits = 0; 19 | WITH 20 | l(u) AS (VALUES 21 | ('mm'), 22 | ('m'), 23 | ('km'), 24 | ('in'), 25 | ('ft'), 26 | ('yd'), 27 | ('mi')) 28 | SELECT 29 | l1.u, l2.u, l1.u::unit @@ l2.u 30 | FROM 31 | l l1 CROSS JOIN l l2 32 | \crosstabview 33 | RESET extra_float_digits; 34 | 35 | -- area 36 | WITH 37 | l(u) AS (VALUES 38 | ('mm^2'), 39 | ('m^2'), 40 | ('km^2'), 41 | ('in^2'), 42 | ('ft^2'), 43 | ('yd^2'), 44 | ('mi^2')) 45 | SELECT 46 | l1.u, l2.u, l1.u::unit @ l2.u 47 | FROM 48 | l l1 CROSS JOIN l l2 49 | \crosstabview 50 | 51 | -- volume 52 | WITH 53 | l(u) AS (VALUES 54 | ('mm^3'), 55 | ('l'), 56 | ('m^3'), 57 | ('in^3'), 58 | ('ft^3'), 59 | ('yd^3')) 60 | SELECT 61 | l1.u, l2.u, l1.u::unit @ l2.u 62 | FROM 63 | l l1 CROSS JOIN l l2 64 | \crosstabview 65 | 66 | -- weight 67 | WITH 68 | l(u) AS (VALUES 69 | ('g'), 70 | ('kg'), 71 | ('t'), 72 | ('oz'), 73 | ('lb')) 74 | SELECT 75 | l1.u, l2.u, l1.u::unit @ l2.u 76 | FROM 77 | l l1 CROSS JOIN l l2 78 | \crosstabview 79 | 80 | -- time 81 | WITH 82 | l(u) AS (VALUES 83 | ('s'), 84 | ('min'), 85 | ('hr'), 86 | ('d'), 87 | ('julianyear')) 88 | SELECT 89 | l1.u, l2.u, l1.u::unit @ l2.u 90 | FROM 91 | l l1 CROSS JOIN l l2 92 | \crosstabview 93 | -------------------------------------------------------------------------------- /sql/temperature.sql: -------------------------------------------------------------------------------- 1 | /* revert to pre-12 default */ 2 | SET extra_float_digits = 0; 3 | SELECT * FROM unit_units WHERE name IN ( 4 | 'K', 'kelvin', 5 | '℃', '°C', 'degC', 'degcelsius', 6 | '℉', '°F', 'degF', 'degfahrenheit', 7 | '°R', 'degR', 'degrankine', 'degreerankine', 'degreesrankine', 'tempR', 'temprankine', 8 | 'degreaumur') 9 | ORDER BY name COLLATE "C"; 10 | RESET extra_float_digits; 11 | 12 | -- Kelvin 13 | SELECT '0 K'::unit, 'K'::unit, '1 K'::unit, '273.15 K'::unit; 14 | 15 | -- Celsius 16 | SELECT '-273.15 ℃'::unit, '0 ℃'::unit, '1 ℃'::unit, '℃'::unit; 17 | SELECT '-273.15 °C'::unit, '0 °C'::unit, '1 °C'::unit, '°C'::unit; 18 | SELECT '-273.15 degC'::unit, '0 degC'::unit, '1 degC'::unit, 'degC'::unit; 19 | SELECT '-273.15 degcelsius'::unit, '0 degcelsius'::unit, '1 degcelsius'::unit, 'degcelsius'::unit; 20 | 21 | -- Fahrenheit 22 | SELECT '-459.67 ℉'::unit, '0 ℉'::unit, '1 ℉'::unit, '℉'::unit; 23 | SELECT '-459.67 °F'::unit, '0 °F'::unit, '1 °F'::unit, '°F'::unit; 24 | SELECT '-459.67 degF'::unit, '0 degF'::unit, '1 degF'::unit, 'degF'::unit; 25 | SELECT '-459.67 degfahrenheit'::unit, '0 degfahrenheit'::unit, '1 degfahrenheit'::unit, 'degfahrenheit'::unit; 26 | 27 | -- Rankine 28 | SELECT '491.67 degR'::unit, '0 degR'::unit, '1 degR'::unit, 'degR'::unit; 29 | 30 | -- Réaumur 31 | SELECT '-218.52 degreaumur'::unit, '0 degreaumur'::unit, '1 degreaumur'::unit, 'degreaumur'::unit; 32 | 33 | SELECT '0 K'::unit @ 'K', '0 K'::unit @ '°C', '0 K'::unit @ '°F', '0 K'::unit @ '°R', '0 K'::unit @ 'degreaumur'; 34 | SELECT '273.15 K'::unit @ 'K', '273.15 K'::unit @ '°C', '273.15 K'::unit @ '°F', '273.15 K'::unit @ '°R', '273.15 K'::unit @ 'degreaumur'; 35 | -------------------------------------------------------------------------------- /expected/round_1.out: -------------------------------------------------------------------------------- 1 | SELECT round(ampere(1.5)); 2 | round 3 | ------- 4 | 2 A 5 | (1 row) 6 | 7 | SELECT round(candela(-0.5)); 8 | round 9 | ------- 10 | -1 cd 11 | (1 row) 12 | 13 | SELECT round(meter(1500.5)); 14 | round 15 | ---------- 16 | 1.501 km 17 | (1 row) 18 | 19 | SET extra_float_digits = 0; 20 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 21 | unit | ?column? | ?column? 22 | ---------------------+---------------------+------------------ 23 | 333.333333333333 mm | 82.0209973753281 ft | 82.0209973753281 24 | (1 row) 25 | 26 | SET extra_float_digits = 3; 27 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 28 | unit | ?column? | ?column? 29 | ------------------------+------------------------+------------------- 30 | 333.333333333333314 mm | 82.0209973753280792 ft | 82.02099737532808 31 | (1 row) 32 | 33 | SET extra_float_digits = -3; 34 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 35 | unit | ?column? | ?column? 36 | ------------------+------------------+--------------- 37 | 333.333333333 mm | 82.0209973753 ft | 82.0209973753 38 | (1 row) 39 | 40 | SET extra_float_digits = -12; 41 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 42 | unit | ?column? | ?column? 43 | --------+----------+---------- 44 | 333 mm | 82 ft | 82 45 | (1 row) 46 | 47 | SET extra_float_digits = -15; 48 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 49 | unit | ?column? | ?column? 50 | ----------+----------+---------- 51 | 3e+02 mm | 8e+01 ft | 8e+01 52 | (1 row) 53 | 54 | -------------------------------------------------------------------------------- /expected/round.out: -------------------------------------------------------------------------------- 1 | SELECT round(ampere(1.5)); 2 | round 3 | ------- 4 | 2 A 5 | (1 row) 6 | 7 | SELECT round(candela(-0.5)); 8 | round 9 | ------- 10 | -1 cd 11 | (1 row) 12 | 13 | SELECT round(meter(1500.5)); 14 | round 15 | ---------- 16 | 1.501 km 17 | (1 row) 18 | 19 | SET extra_float_digits = 0; 20 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 21 | unit | ?column? | ?column? 22 | ---------------------+---------------------+------------------ 23 | 333.333333333333 mm | 82.0209973753281 ft | 82.0209973753281 24 | (1 row) 25 | 26 | SET extra_float_digits = 3; 27 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 28 | unit | ?column? | ?column? 29 | ------------------------+------------------------+--------------------- 30 | 333.333333333333314 mm | 82.0209973753280792 ft | 82.0209973753280792 31 | (1 row) 32 | 33 | SET extra_float_digits = -3; 34 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 35 | unit | ?column? | ?column? 36 | ------------------+------------------+--------------- 37 | 333.333333333 mm | 82.0209973753 ft | 82.0209973753 38 | (1 row) 39 | 40 | SET extra_float_digits = -12; 41 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 42 | unit | ?column? | ?column? 43 | --------+----------+---------- 44 | 333 mm | 82 ft | 82 45 | (1 row) 46 | 47 | SET extra_float_digits = -15; 48 | SELECT '1|3m'::unit, '25m'::unit @ 'ft', '25m'::unit @@ 'ft'; 49 | unit | ?column? | ?column? 50 | ----------+----------+---------- 51 | 3e+02 mm | 8e+01 ft | 8e+01 52 | (1 row) 53 | 54 | -------------------------------------------------------------------------------- /sql/iec.sql: -------------------------------------------------------------------------------- 1 | VALUES 2 | ('1 mB'::unit), 3 | ('1 B'::unit), 4 | ('1 KiB'::unit), 5 | ('1 MiB'::unit), 6 | ('1 GiB'::unit), 7 | ('1 TiB'::unit), 8 | ('1 PiB'::unit), 9 | ('1 EiB'::unit), 10 | ('1 ZiB'::unit), 11 | ('1 YiB'::unit), 12 | ('1 RiB'::unit), 13 | ('1 QiB'::unit); 14 | 15 | VALUES 16 | ('1 B'::unit @ 'B'), 17 | ('1 KiB'::unit @ 'KiB'), 18 | ('1 MiB'::unit @ 'MiB'), 19 | ('1 GiB'::unit @ 'GiB'), 20 | ('1 TiB'::unit @ 'TiB'), 21 | ('1 PiB'::unit @ 'PiB'), 22 | ('1 EiB'::unit @ 'EiB'), 23 | ('1 ZiB'::unit @ 'ZiB'), 24 | ('1 YiB'::unit @ 'YiB'), 25 | ('1 RiB'::unit @ 'RiB'), 26 | ('1 QiB'::unit @ 'QiB'); 27 | 28 | VALUES 29 | ('1 B'::unit @ 'B'), 30 | ('1 kB'::unit @ 'KiB'), 31 | ('1 MB'::unit @ 'MiB'), 32 | ('1 GB'::unit @ 'GiB'), 33 | ('1 TB'::unit @ 'TiB'), 34 | ('1 PB'::unit @ 'PiB'), 35 | ('1 EB'::unit @ 'EiB'), 36 | ('1 ZB'::unit @ 'ZiB'), 37 | ('1 YB'::unit @ 'YiB'), 38 | ('1 RB'::unit @ 'RiB'), 39 | ('1 QB'::unit @ 'QiB'); 40 | 41 | -- errors 42 | SELECT '1 kiB'::unit; 43 | SELECT '1 Ki'::unit; 44 | -- used to be an error in v2 45 | SELECT '1 Kim'::unit AS "1024 m"; 46 | 47 | -- binary prefix output 48 | SET unit.byte_output_iec = 'foobar'; 49 | SET unit.byte_output_iec = on; 50 | 51 | VALUES 52 | ('.001 B'::unit, '.001 B'::unit), 53 | ('1 B'::unit, '1 B'::unit), 54 | ('1 KiB'::unit, '1 kB'::unit), 55 | ('1 MiB'::unit, '1 MB'::unit), 56 | ('1 GiB'::unit, '1 GB'::unit), 57 | ('1 TiB'::unit, '1 TB'::unit), 58 | ('1 PiB'::unit, '1 PB'::unit), 59 | ('1 EiB'::unit, '1 EB'::unit), 60 | ('1 ZiB'::unit, '1 ZB'::unit), 61 | ('1 YiB'::unit, '1 YB'::unit), 62 | ('1 RiB'::unit, '1 RB'::unit), 63 | ('1 QiB'::unit, '1 QB'::unit), 64 | ('1024 QiB'::unit, '1000 QB'::unit); 65 | -------------------------------------------------------------------------------- /sql/tables.sql: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | 3 | DROP TABLE IF EXISTS prefixes; 4 | CREATE TABLE prefixes (p text); 5 | DELETE FROM prefixes; 6 | INSERT INTO prefixes VALUES 7 | ('q'),('r'),('y'),('z'),('a'),('f'),('p'),('n'),('µ'),('mu'),('m'),('c'),('d'), 8 | (''), 9 | ('da'),('h'),('k'),('M'),('G'),('T'),('P'),('E'),('Z'),('Y'),('R'),('Q'); 10 | 11 | DROP TABLE IF EXISTS factors; 12 | CREATE TABLE factors (f double precision); 13 | DELETE FROM factors; 14 | INSERT INTO factors VALUES 15 | (1e33), (1e30), (1e27), (1e24), (1e21), (1e18), (1e15), (1e12), (1e9), (1e6), (1e3), 16 | (1), 17 | (1e-3), (1e-6), (1e-9), (1e-12), (1e-15), (1e-18), (1e-21), (1e-24), (1e-27), (1e-30), (1e-33), (1e-36), 18 | (0); 19 | 20 | DROP TABLE IF EXISTS units; 21 | CREATE TABLE units (u text, unit unit, base boolean, coherent boolean, duplicate boolean); 22 | COMMENT ON COLUMN units.duplicate IS 'For coherent units, this unit has the same dimension as some other base or coherent unit'; 23 | DELETE FROM units; 24 | COPY units (u, base, coherent, duplicate) FROM STDIN; 25 | m/m t f f 26 | m t f f 27 | kg t f f 28 | s t f f 29 | A t f f 30 | K t f f 31 | mol t f f 32 | cd t f f 33 | B t f f 34 | sr f t t 35 | Hz f t f 36 | N f t f 37 | Pa f t f 38 | J f t f 39 | W f t f 40 | C f t f 41 | V f t f 42 | F f t f 43 | Ω f t f 44 | ohm f t t 45 | S f t f 46 | Wb f t f 47 | T f t f 48 | H f t f 49 | °C f t t 50 | lm f t t 51 | lx f t f 52 | Bq f t t 53 | Gy f t f 54 | rad f f t 55 | Sv f t t 56 | kat f t f 57 | g f f t 58 | a f f f 59 | l f f f 60 | t f f f 61 | bar f f f 62 | min f f f 63 | hr f f f 64 | d f f f 65 | in f f f 66 | ft f f f 67 | yd f f f 68 | mi f f f 69 | oz f f f 70 | lb f f f 71 | \. 72 | UPDATE units 73 | SET unit = u::unit; 74 | 75 | SELECT * FROM units; 76 | -------------------------------------------------------------------------------- /expected/binary.out: -------------------------------------------------------------------------------- 1 | SELECT unit_send('0'); 2 | unit_send 3 | ------------------------------------ 4 | \x00000000000000000000000000000000 5 | (1 row) 6 | 7 | SELECT unit_send(-'0'::unit); 8 | unit_send 9 | ------------------------------------ 10 | \x80000000000000000000000000000000 11 | (1 row) 12 | 13 | SELECT unit_send('1 m'::unit); 14 | unit_send 15 | ------------------------------------ 16 | \x3ff00000000000000100000000000000 17 | (1 row) 18 | 19 | SELECT unit_send('1 kg^-1'::unit); 20 | unit_send 21 | ------------------------------------ 22 | \x3ff000000000000000ff000000000000 23 | (1 row) 24 | 25 | SELECT unit_send('1 s'::unit); 26 | unit_send 27 | ------------------------------------ 28 | \x3ff00000000000000000010000000000 29 | (1 row) 30 | 31 | SELECT unit_send('1 A^-1'::unit); 32 | unit_send 33 | ------------------------------------ 34 | \x3ff0000000000000000000ff00000000 35 | (1 row) 36 | 37 | SELECT unit_send('1 K'::unit); 38 | unit_send 39 | ------------------------------------ 40 | \x3ff00000000000000000000001000000 41 | (1 row) 42 | 43 | SELECT unit_send('1 mol^-1'::unit); 44 | unit_send 45 | ------------------------------------ 46 | \x3ff00000000000000000000000ff0000 47 | (1 row) 48 | 49 | SELECT unit_send('1 cd'::unit); 50 | unit_send 51 | ------------------------------------ 52 | \x3ff00000000000000000000000000100 53 | (1 row) 54 | 55 | SELECT unit_send('1 B^-1'::unit); 56 | unit_send 57 | ------------------------------------ 58 | \x3ff000000000000000000000000000ff 59 | (1 row) 60 | 61 | -------------------------------------------------------------------------------- /sql/units.sql: -------------------------------------------------------------------------------- 1 | SET unit.time_output_custom = false; 2 | 3 | /* revert to pre-12 default */ 4 | SET extra_float_digits = 0; 5 | SELECT prefix, factor, definition FROM unit_prefixes ORDER BY factor, prefix COLLATE "C"; 6 | RESET extra_float_digits; 7 | 8 | SELECT name, unit, definition FROM unit_units ORDER BY dimension(unit), unit, name COLLATE "C"; 9 | 10 | -- units that do not conform to their original definition 11 | SELECT name, unit, definition, definition::unit AS parsed_definition, unit / definition::unit AS deviation 12 | FROM unit_units WHERE unit <> definition::unit; 13 | 14 | -- units that differ when pushed through output-input functions 15 | SELECT name, unit, unit::text::unit, definition FROM unit_units WHERE unit::text::unit::text <> unit::text; 16 | 17 | -- prefix-unit combinations that are ambiguous 18 | /* 19 | CREATE FUNCTION valid_unit(u text) 20 | RETURNS boolean LANGUAGE plpgsql 21 | AS $$ 22 | BEGIN 23 | PERFORM u::unit; 24 | RETURN true; 25 | EXCEPTION 26 | WHEN OTHERS THEN 27 | RETURN false; 28 | END; 29 | $$; 30 | 31 | -- takes about 5min to run with cold cache, and about 3s with the hash table filled 32 | SELECT prefix||name AS unit, prefix, name 33 | FROM unit_prefixes CROSS JOIN unit_units 34 | WHERE NOT valid_unit(prefix||name) 35 | ORDER BY prefix||name, prefix; 36 | */ 37 | 38 | SELECT 'daA'::unit; -- ambiguous in original definitions.units 39 | SELECT 'dasb'::unit; 40 | SELECT 'dat'::unit; 41 | SELECT 'dau'::unit; 42 | SELECT 'daustbl'::unit; 43 | SELECT 'daustblsp'::unit; 44 | SELECT 'daustbsp'::unit; 45 | SELECT 'daustsp'::unit; 46 | SELECT 'Eint'::unit; 47 | SELECT 'Gint'::unit; 48 | SELECT 'Mint'::unit; 49 | SELECT 'Pint'::unit; 50 | SELECT 'Tint'::unit; 51 | SELECT 'Yint'::unit; 52 | SELECT 'yoctodecillion'::unit; 53 | SELECT 'Zint'::unit; 54 | -------------------------------------------------------------------------------- /unit--2--3.sql.in: -------------------------------------------------------------------------------- 1 | -- type definition 2 | 3 | ALTER FUNCTION unit_in(cstring) 4 | SET search_path = @extschema@; 5 | 6 | -- prefix/unit definition tables 7 | 8 | CREATE TABLE unit_prefixes ( 9 | prefix varchar(32) PRIMARY KEY, 10 | factor double precision NOT NULL, 11 | definition text, -- original definition, informational 12 | dump boolean DEFAULT true 13 | ); 14 | 15 | SELECT pg_catalog.pg_extension_config_dump('unit_prefixes', 'WHERE dump'); 16 | 17 | COPY unit_prefixes (prefix, factor, definition, dump) FROM '@MODULEDIR@/unit_prefixes.data'; 18 | 19 | CREATE TABLE unit_units ( 20 | name varchar(32) PRIMARY KEY, 21 | unit unit NOT NULL, 22 | shift double precision, -- NULL means 0.0 here 23 | definition text, -- original definition, informational 24 | dump boolean DEFAULT true 25 | ); 26 | 27 | SELECT pg_catalog.pg_extension_config_dump('unit_units', 'WHERE dump'); 28 | 29 | COPY unit_units (name, unit, shift, definition, dump) FROM '@MODULEDIR@/unit_units.data'; 30 | 31 | -- operators 32 | 33 | CREATE FUNCTION sqrt(unit) 34 | RETURNS unit 35 | AS '$libdir/unit', 'unit_sqrt' 36 | LANGUAGE C IMMUTABLE STRICT; 37 | 38 | CREATE OPERATOR |/ ( 39 | rightarg = unit, 40 | procedure = sqrt 41 | ); 42 | 43 | CREATE FUNCTION cbrt(unit) 44 | RETURNS unit 45 | AS '$libdir/unit', 'unit_cbrt' 46 | LANGUAGE C IMMUTABLE STRICT; 47 | 48 | CREATE OPERATOR ||/ ( 49 | rightarg = unit, 50 | procedure = cbrt 51 | ); 52 | 53 | ALTER FUNCTION unit_at(unit, cstring) 54 | SET search_path = @extschema@; 55 | 56 | -- internal functions 57 | 58 | CREATE FUNCTION unit_is_hashed(cstring) 59 | RETURNS bool 60 | AS '$libdir/unit' 61 | LANGUAGE C VOLATILE STRICT; 62 | 63 | CREATE FUNCTION unit_reset() 64 | RETURNS void 65 | AS '$libdir/unit' 66 | LANGUAGE C VOLATILE STRICT; 67 | -------------------------------------------------------------------------------- /expected/language_functions.out: -------------------------------------------------------------------------------- 1 | select 'sqrt(4 m^2)'::unit; 2 | unit 3 | ------ 4 | 2 m 5 | (1 row) 6 | 7 | select 'sqrt(-4 m^2)'::unit; 8 | ERROR: cannot take square root of a negative-valued unit 9 | LINE 1: select 'sqrt(-4 m^2)'::unit; 10 | ^ 11 | select 'sqrt(4 m^3)'::unit; 12 | ERROR: cannot take square root of a unit with odd "m" exponent 13 | LINE 1: select 'sqrt(4 m^3)'::unit; 14 | ^ 15 | select 'exp(1)'::unit; 16 | unit 17 | ------------------ 18 | 2.71828182845905 19 | (1 row) 20 | 21 | select 'exp(1 A)'::unit; 22 | ERROR: cannot take base-e exponent of value that is not dimension-less 23 | LINE 1: select 'exp(1 A)'::unit; 24 | ^ 25 | select 'ln(2.7)'::unit; 26 | unit 27 | ------------------- 28 | 0.993251773010283 29 | (1 row) 30 | 31 | select 'ln(-2.7)'::unit; 32 | ERROR: cannot take ln of a negative-valued unit 33 | LINE 1: select 'ln(-2.7)'::unit; 34 | ^ 35 | select 'ln(2.7 s)'::unit; 36 | ERROR: cannot take ln of value that is not dimension-less 37 | LINE 1: select 'ln(2.7 s)'::unit; 38 | ^ 39 | select 'asin(1)'::unit; 40 | unit 41 | ----------------- 42 | 1.5707963267949 43 | (1 row) 44 | 45 | select 'asin(2)'::unit; 46 | ERROR: cannot asin of values outside the range -1 to 1 47 | LINE 1: select 'asin(2)'::unit; 48 | ^ 49 | select 'asin(1 kg)'::unit; 50 | ERROR: cannot take asin of value that is not dimension-less 51 | LINE 1: select 'asin(1 kg)'::unit; 52 | ^ 53 | select 'tan(1)'::unit; 54 | unit 55 | ----------------- 56 | 1.5574077246549 57 | (1 row) 58 | 59 | select 'tan(1 mol)'::unit; 60 | ERROR: cannot take tan of value that is not dimension-less 61 | LINE 1: select 'tan(1 mol)'::unit; 62 | ^ 63 | -------------------------------------------------------------------------------- /expected/custom.out: -------------------------------------------------------------------------------- 1 | SELECT '1 foobar'::unit; 2 | ERROR: unit "foobar" is not known 3 | LINE 1: SELECT '1 foobar'::unit; 4 | ^ 5 | SELECT unit_is_hashed('foobar'); 6 | unit_is_hashed 7 | ---------------- 8 | f 9 | (1 row) 10 | 11 | INSERT INTO unit_prefixes VALUES ('foo', 42); 12 | SELECT '1 foobar'::unit; 13 | unit 14 | --------- 15 | 4.2 MPa 16 | (1 row) 17 | 18 | SELECT unit_is_hashed('foobar'); 19 | unit_is_hashed 20 | ---------------- 21 | t 22 | (1 row) 23 | 24 | SELECT * FROM unit_prefixes WHERE dump; 25 | prefix | factor | definition | dump 26 | --------+--------+------------+------ 27 | foo | 42 | | t 28 | (1 row) 29 | 30 | INSERT INTO unit_units VALUES ('legobrick', '9.6 mm'); 31 | SELECT unit_is_hashed('legobricks'); 32 | unit_is_hashed 33 | ---------------- 34 | f 35 | (1 row) 36 | 37 | SELECT '1 m'::unit @ 'legobricks' AS one_meter; 38 | one_meter 39 | ----------------------------- 40 | 104.166666666667 legobricks 41 | (1 row) 42 | 43 | SELECT unit_is_hashed('legobricks'); 44 | unit_is_hashed 45 | ---------------- 46 | t 47 | (1 row) 48 | 49 | SELECT * FROM unit_units WHERE dump; 50 | name | unit | shift | definition | dump 51 | -----------+--------+-------+------------+------ 52 | legobrick | 9.6 mm | | | t 53 | (1 row) 54 | 55 | UPDATE unit_units SET unit = '19.1 mm' WHERE name = 'legobrick'; -- Duplo size 56 | SELECT '2 legobricks'::unit AS old_size; 57 | old_size 58 | ---------- 59 | 19.2 mm 60 | (1 row) 61 | 62 | SELECT unit_reset(); 63 | unit_reset 64 | ------------ 65 | 66 | (1 row) 67 | 68 | SELECT unit_is_hashed('legobricks'); 69 | unit_is_hashed 70 | ---------------- 71 | f 72 | (1 row) 73 | 74 | SELECT '2 legobricks'::unit AS new_size; 75 | new_size 76 | ---------- 77 | 38.2 mm 78 | (1 row) 79 | 80 | -------------------------------------------------------------------------------- /powers.h: -------------------------------------------------------------------------------- 1 | /* powers-of-10 constants */ 2 | /* constants here are rounded down so >= comparisons work better */ 3 | 4 | #define POWER_33 0x1.8a6e32246c99bp+109 /* 0x1.8a6e32246c99cp+109 */ 5 | #define POWER_30 0x1.93e5939a08ce9p+99 /* Q 0x1.93e5939a08ceap+99 */ 6 | #define POWER_27 0x1.9d971e4fe8401p+89 /* R 0x1.9d971e4fe8402p+89 */ 7 | #define POWER_24 0x1.a784379d99db3p+79 /* Y 0x1.a784379d99db4p+79 */ 8 | #define POWER_21 0x1.b1ae4d6e2ef4fp+69 /* Z 0x1.b1ae4d6e2ef5p+69 */ 9 | #define POWER_18 0x1.bc16d674ec7ffp+59 /* E 0x1.bc16d674ec8p+59 */ 10 | #define POWER_15 0x1.c6bf52633ffffp+49 /* P 0x1.c6bf52634p+49 */ 11 | #define POWER_12 0x1.d1a94a1ffffffp+39 /* T 0x1.d1a94a2p+39 */ 12 | #define POWER_9 0x1.dcd64ffffffffp+29 /* G 0x1.dcd65p+29 */ 13 | #define POWER_6 0x1.e847fffffffffp+19 /* M 0x1.e848p+19 */ 14 | #define POWER_3 0x1.f3fffffffffffp+9 /* k 0x1.f4p+9 */ 15 | #define POWER_0 0x1.fffffffffffffp-1 /* 0x1p+0 */ 16 | #define POWER__3 0x1.0624dd2f1a9fbp-10 /* m 0x1.0624dd2f1a9fcp-10 */ 17 | #define POWER__6 0x1.0c6f7a0b5ed8cp-20 /* µ 0x1.0c6f7a0b5ed8dp-20 */ 18 | #define POWER__9 0x1.12e0be826d694p-30 /* n 0x1.12e0be826d695p-30 */ 19 | #define POWER__12 0x1.19799812dea1p-40 /* p 0x1.19799812dea11p-40 */ 20 | #define POWER__15 0x1.203af9ee75615p-50 /* f 0x1.203af9ee75616p-50 */ 21 | #define POWER__18 0x1.2725dd1d243abp-60 /* a 0x1.2725dd1d243acp-60 */ 22 | #define POWER__21 0x1.2e3b40a0e9b4ep-70 /* z 0x1.2e3b40a0e9b4fp-70 */ 23 | #define POWER__24 0x1.357c299a88ea6p-80 /* y 0x1.357c299a88ea7p-80 */ 24 | #define POWER__27 0x1.3ce9a36f23c0fp-90 /* r 0x1.3ce9a36f23c1p-90 */ 25 | #define POWER__30 0x1.4484bfeebc29fp-100 /* q 0x1.4484bfeebc2ap-100 */ 26 | #define POWER__33 0x1.4c4e977ba1f5bp-110 /* 0x1.4c4e977ba1f5cp-110 */ 27 | -------------------------------------------------------------------------------- /sql/compare.sql: -------------------------------------------------------------------------------- 1 | -- test comparisons 2 | WITH 3 | v(u) AS (VALUES 4 | (NULL), 5 | ('-2'::unit), 6 | ('-1'::unit), 7 | ('-0'::unit), 8 | ('0'::unit), 9 | ('1'::unit), 10 | ('2'::unit), 11 | (meter()), 12 | (meter() * '2'), 13 | (kilogram()), 14 | (kilogram() * '2') 15 | ), 16 | va(a) AS (SELECT * FROM v), 17 | vb(b) AS (SELECT * FROM v) 18 | SELECT 19 | a, b, 20 | CASE WHEN unit_cmp(a, b) < 0 THEN '<' 21 | WHEN unit_cmp(a, b) = 0 THEN '=' 22 | WHEN unit_cmp(a, b) > 0 THEN '>' 23 | END AS cmp, 24 | a < b AS lt, a <= b AS le, 25 | a = b AS eq, a <> b AS ne, 26 | a >= b AS ge, a > b AS gt 27 | FROM 28 | va CROSS JOIN vb; 29 | 30 | -- test btree index 31 | CREATE TEMP TABLE u (u unit); 32 | INSERT INTO u SELECT meter(generate_series(1,10000)::double precision); 33 | INSERT INTO u SELECT generate_series(1,10000)::text::unit * kilogram(); 34 | ANALYZE u; 35 | CREATE INDEX ON u(u); 36 | SELECT * FROM u WHERE u = meter(400); 37 | SELECT * FROM u WHERE u = '300' * kilogram(); 38 | EXPLAIN (COSTS OFF) SELECT * FROM u WHERE u = meter(400); 39 | EXPLAIN (COSTS OFF) SELECT * FROM u WHERE u = '300' * kilogram(); 40 | 41 | -- test strict operators 42 | SELECT '1 m'::unit << '1 m'::unit; 43 | SELECT '1 m'::unit << '1 A'::unit; 44 | SELECT '1 m'::unit <<= '1 m'::unit; 45 | SELECT '1 m'::unit <<= '1 A'::unit; 46 | SELECT '1 m'::unit == '1 m'::unit; 47 | SELECT '1 m'::unit == '1 A'::unit; 48 | SELECT '1 m'::unit <<>> '1 m'::unit; 49 | SELECT '1 m'::unit <<>> '1 A'::unit; 50 | SELECT '1 m'::unit >>= '1 m'::unit; 51 | SELECT '1 m'::unit >>= '1 A'::unit; 52 | SELECT '1 m'::unit >> '1 m'::unit; 53 | SELECT '1 m'::unit >> '1 A'::unit; 54 | 55 | -- test range type 56 | SELECT 'empty'::unitrange; 57 | SELECT '(1 m, 2 m)'::unitrange; 58 | SELECT '(1 m, 2 A)'::unitrange; 59 | SELECT unit_diff('1 A', '2 A'); 60 | SELECT unit_diff('1 A', '2 K'); 61 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE_big = unit 2 | OBJS = unit.o unitparse.yy.o unitparse.tab.o 3 | EXTENSION = unit 4 | DATA = unit_prefixes.data unit_units.data \ 5 | unit--1.sql unit--1--2.sql \ 6 | unit--2.sql 7 | DATA_built = unit--2--3.sql unit--3.sql \ 8 | unit--3--4.sql unit--4.sql \ 9 | unit--4--5.sql unit--5.sql \ 10 | unit--5--6.sql unit--6.sql \ 11 | unit--6--7.sql unit--7.sql 12 | REGRESS = extension tables unit binary unicode prefix units time temperature functions language_functions round derived compare aggregate iec custom 13 | # Jessie's and trusty's bison (3.0.2) is buggy, ship pregenerated .tab files 14 | EXTRA_CLEAN = unitparse.yy.* powers powers.o unit-*.dump # unitparse.tab.* 15 | 16 | # avoid add/mult contraction so '-459.67 °F' is really '0 K' 17 | # problem visible on ppc64el Ubuntu trusty..zesty (Debian unaffected) 18 | PG_CPPFLAGS += -ffp-contract=off 19 | 20 | PG_CONFIG = pg_config 21 | PGXS := $(shell $(PG_CONFIG) --pgxs) 22 | include $(PGXS) 23 | 24 | HAS_CROSSTAB = $(shell [ $(VERSION_NUM) -ge 90600 ] && echo yes) 25 | ifeq ($(HAS_CROSSTAB),yes) 26 | REGRESS += crosstab convert 27 | endif 28 | 29 | # upgrade testing, not enabled by default (needs extension actually installed) 30 | #REGRESS += upgrade 31 | 32 | unit.o: unit.c unit.h defined_units.h float8out_unit.h 33 | 34 | unitparse.yy.c: unitparse.l 35 | flex -o $@ $< 36 | 37 | unitparse.yy.o: unit.h defined_units.h unitparse.tab.c # actually unitparse.tab.h 38 | 39 | unitparse.tab.c unitparse.tab.h: unitparse.y 40 | bison -d $< 41 | 42 | unitparse.tab.o: unit.h defined_units.h 43 | 44 | %.sql: %.sql.in 45 | sed -e "s!@MODULEDIR@!$(datadir)/$(datamoduledir)!g" $< > $@ 46 | 47 | # unit definitions from GNU units 48 | definitions.units.patched: definitions.units definitions.units.patch 49 | cp $< $@ 50 | patch $@ definitions.units.patch 51 | 52 | # powers-of-10 template 53 | powers: powers.c 54 | gcc -o $@ $^ -lm 55 | -------------------------------------------------------------------------------- /sql/upgrade.sql: -------------------------------------------------------------------------------- 1 | -- version 1 2 | SET client_min_messages = warning; 3 | DROP EXTENSION IF EXISTS unit CASCADE; 4 | CREATE TABLE IF NOT EXISTS pg_depend_save (LIKE pg_depend); 5 | RESET client_min_messages; 6 | 7 | \set pg_depend_save 'BEGIN; DELETE FROM pg_depend_save; WITH ext AS (DELETE FROM pg_depend WHERE refobjid = (SELECT oid FROM pg_extension WHERE extname = $$unit$$) RETURNING *) INSERT INTO pg_depend_save SELECT * FROM ext; COMMIT;' 8 | \set pg_depend_restore 'INSERT INTO pg_depend SELECT * FROM pg_depend_save;' 9 | 10 | CREATE EXTENSION unit VERSION "1"; 11 | :pg_depend_save 12 | \! pg_dump -f unit-1.dump -T pg_depend_save 13 | :pg_depend_restore 14 | 15 | -- upgrade to version 2 16 | ALTER EXTENSION unit UPDATE TO "2"; 17 | :pg_depend_save 18 | \! pg_dump -f unit-1-2.dump -T pg_depend_save 19 | :pg_depend_restore 20 | 21 | -- upgrade to version 3 22 | ALTER EXTENSION unit UPDATE TO "3"; 23 | :pg_depend_save 24 | \! pg_dump -f unit-2-3.dump -T pg_depend_save 25 | :pg_depend_restore 26 | 27 | -- upgrade to version 4 28 | ALTER EXTENSION unit UPDATE TO "4"; 29 | :pg_depend_save 30 | \! pg_dump -f unit-3-4.dump -T pg_depend_save 31 | :pg_depend_restore 32 | 33 | -- upgrade to version 5 34 | ALTER EXTENSION unit UPDATE TO "5"; 35 | :pg_depend_save 36 | \! pg_dump -f unit-4-5.dump -T pg_depend_save 37 | :pg_depend_restore 38 | 39 | -- upgrade to version 6 40 | ALTER EXTENSION unit UPDATE TO "6"; 41 | :pg_depend_save 42 | \! pg_dump -f unit-5-6.dump -T pg_depend_save 43 | :pg_depend_restore 44 | 45 | -- upgrade to version 7 46 | ALTER EXTENSION unit UPDATE TO "7"; 47 | :pg_depend_save 48 | \! pg_dump -f unit-6-7.dump -T pg_depend_save 49 | :pg_depend_restore 50 | 51 | -- reinstall latest version 52 | DROP EXTENSION unit CASCADE; 53 | CREATE EXTENSION unit; 54 | :pg_depend_save 55 | \! pg_dump -f unit-create.dump -T pg_depend_save 56 | :pg_depend_restore 57 | 58 | -- different ordering in unit_accum_t expected: 59 | \! diff -u --label unit-update.dump unit-6-7.dump --label unit-create.dump unit-create.dump | sed -e 's/@@.*@@/@@/' 60 | -------------------------------------------------------------------------------- /sql/crosstab.sql: -------------------------------------------------------------------------------- 1 | SET unit.time_output_custom = false; 2 | 3 | -- test multiplication 4 | WITH i(i) AS (VALUES ('-2'::unit), ('-1'), ('0'), ('1'), ('2')) 5 | SELECT 6 | unit, i, unit * i 7 | FROM 8 | units CROSS JOIN i 9 | WHERE 10 | (base OR coherent) AND NOT duplicate 11 | \crosstabview 12 | 13 | SELECT 14 | a.unit AS a, b.unit AS b, a.unit * b.unit AS mul 15 | FROM 16 | units AS a CROSS JOIN units AS b 17 | WHERE 18 | (a.base OR a.coherent) AND NOT a.duplicate AND (b.base OR b.coherent) AND NOT b.duplicate 19 | \crosstabview 20 | 21 | SELECT 22 | f, unit, f * unit 23 | FROM 24 | factors CROSS JOIN units 25 | WHERE 26 | (base OR coherent) AND NOT duplicate 27 | \crosstabview 28 | 29 | -- test division 30 | WITH i(i) AS (VALUES ('-2'::unit), ('-1'), ('1'), ('2')) 31 | SELECT 32 | unit, i, unit / i 33 | FROM 34 | units CROSS JOIN i 35 | WHERE 36 | (base OR coherent) AND NOT duplicate 37 | \crosstabview 38 | 39 | WITH i(i) AS (VALUES ('-2'::unit), ('-1'), ('0'), ('1'), ('2')) 40 | SELECT 41 | i, unit, i / unit 42 | FROM 43 | units CROSS JOIN i 44 | WHERE 45 | (base OR coherent) AND NOT duplicate 46 | \crosstabview 47 | 48 | SELECT 49 | a.unit AS a, b.unit AS b, a.unit / b.unit AS div 50 | FROM 51 | units AS a CROSS JOIN units AS b 52 | WHERE 53 | (a.base OR a.coherent) AND NOT a.duplicate AND (b.base OR b.coherent) AND NOT b.duplicate 54 | \crosstabview 55 | 56 | -- test comparisons 57 | WITH 58 | v(u) AS (VALUES 59 | ('-2'::unit), 60 | ('-1'::unit), 61 | (-kilogram()), 62 | (-meter()), 63 | ('-0'::unit), 64 | ('0'::unit), 65 | ('1'::unit), 66 | (kilogram()), 67 | (meter()), 68 | ('2'::unit), 69 | (kilogram(2)), 70 | (meter(2)) 71 | ), 72 | va(a) AS (SELECT * FROM v), 73 | vb(b) AS (SELECT * FROM v) 74 | SELECT 75 | a, b, 76 | CASE WHEN unit_cmp(a, b) < 0 THEN '<' 77 | WHEN unit_cmp(a, b) = 0 THEN '=' 78 | WHEN unit_cmp(a, b) > 0 THEN '>' 79 | END AS cmp 80 | FROM 81 | va CROSS JOIN vb 82 | \crosstabview 83 | 84 | -- test all prefix/unit combinations 85 | SELECT 86 | u, p, (p||u)::unit 87 | FROM 88 | prefixes CROSS JOIN units 89 | WHERE 90 | u <> 'kg' AND 91 | (p||u) <> 'dat' -- ambiguous 92 | \crosstabview 93 | -------------------------------------------------------------------------------- /expected/upgrade.out: -------------------------------------------------------------------------------- 1 | -- version 1 2 | SET client_min_messages = warning; 3 | DROP EXTENSION IF EXISTS unit CASCADE; 4 | CREATE TABLE IF NOT EXISTS pg_depend_save (LIKE pg_depend); 5 | RESET client_min_messages; 6 | \set pg_depend_save 'BEGIN; DELETE FROM pg_depend_save; WITH ext AS (DELETE FROM pg_depend WHERE refobjid = (SELECT oid FROM pg_extension WHERE extname = $$unit$$) RETURNING *) INSERT INTO pg_depend_save SELECT * FROM ext; COMMIT;' 7 | \set pg_depend_restore 'INSERT INTO pg_depend SELECT * FROM pg_depend_save;' 8 | CREATE EXTENSION unit VERSION "1"; 9 | :pg_depend_save 10 | \! pg_dump -f unit-1.dump -T pg_depend_save 11 | :pg_depend_restore 12 | -- upgrade to version 2 13 | ALTER EXTENSION unit UPDATE TO "2"; 14 | :pg_depend_save 15 | \! pg_dump -f unit-1-2.dump -T pg_depend_save 16 | :pg_depend_restore 17 | -- upgrade to version 3 18 | ALTER EXTENSION unit UPDATE TO "3"; 19 | :pg_depend_save 20 | \! pg_dump -f unit-2-3.dump -T pg_depend_save 21 | :pg_depend_restore 22 | -- upgrade to version 4 23 | ALTER EXTENSION unit UPDATE TO "4"; 24 | :pg_depend_save 25 | \! pg_dump -f unit-3-4.dump -T pg_depend_save 26 | :pg_depend_restore 27 | -- upgrade to version 5 28 | ALTER EXTENSION unit UPDATE TO "5"; 29 | :pg_depend_save 30 | \! pg_dump -f unit-4-5.dump -T pg_depend_save 31 | :pg_depend_restore 32 | -- upgrade to version 6 33 | ALTER EXTENSION unit UPDATE TO "6"; 34 | :pg_depend_save 35 | \! pg_dump -f unit-5-6.dump -T pg_depend_save 36 | :pg_depend_restore 37 | -- upgrade to version 7 38 | ALTER EXTENSION unit UPDATE TO "7"; 39 | :pg_depend_save 40 | \! pg_dump -f unit-6-7.dump -T pg_depend_save 41 | :pg_depend_restore 42 | -- reinstall latest version 43 | DROP EXTENSION unit CASCADE; 44 | CREATE EXTENSION unit; 45 | :pg_depend_save 46 | \! pg_dump -f unit-create.dump -T pg_depend_save 47 | :pg_depend_restore 48 | -- different ordering in unit_accum_t expected: 49 | \! diff -u --label unit-update.dump unit-6-7.dump --label unit-create.dump unit-create.dump | sed -e 's/@@.*@@/@@/' 50 | --- unit-update.dump 51 | +++ unit-create.dump 52 | @@ 53 | 54 | CREATE TYPE public.unit_accum_t AS ( 55 | s public.unit, 56 | - n bigint, 57 | - squares double precision 58 | + squares double precision, 59 | + n bigint 60 | ); 61 | 62 | 63 | -------------------------------------------------------------------------------- /sql/derived.sql: -------------------------------------------------------------------------------- 1 | -- derived units 2 | SELECT radian(); 3 | SELECT steradian(); 4 | SELECT hertz(); 5 | SELECT newton(); 6 | SELECT pascal(); 7 | SELECT joule(); 8 | SELECT watt(); 9 | SELECT coulomb(); 10 | SELECT volt(); 11 | SELECT farad(); 12 | SELECT ohm(); 13 | SELECT siemens(); 14 | SELECT weber(); 15 | SELECT tesla(); 16 | SELECT henry(); 17 | SELECT celsius(); 18 | SELECT lumen(); 19 | SELECT lux(); 20 | SELECT becquerel(); 21 | SELECT gray(); 22 | SELECT sievert(); 23 | SELECT katal(); 24 | 25 | SELECT radian(2); 26 | SELECT steradian(2); 27 | SELECT hertz(2); 28 | SELECT newton(2); 29 | SELECT pascal(2); 30 | SELECT joule(2); 31 | SELECT watt(2); 32 | SELECT coulomb(2); 33 | SELECT volt(2); 34 | SELECT farad(2); 35 | SELECT ohm(2); 36 | SELECT siemens(2); 37 | SELECT weber(2); 38 | SELECT tesla(2); 39 | SELECT henry(2); 40 | SELECT celsius(100); 41 | SELECT lumen(2); 42 | SELECT lux(2); 43 | SELECT becquerel(2); 44 | SELECT gray(2); 45 | SELECT sievert(2); 46 | SELECT katal(2); 47 | 48 | /* revert to pre-12 default for decibel() tests */ 49 | SET extra_float_digits = 0; 50 | 51 | -- Non-SI units accepted for use with the SI 52 | SELECT minute(); 53 | SELECT hour(); 54 | SELECT day(); 55 | SELECT degree_arc(); 56 | SELECT minute_arc(); 57 | SELECT second_arc(); 58 | SELECT hectare(); 59 | SELECT liter(); 60 | SELECT tonne(); 61 | SELECT au(); 62 | SELECT decibel(); 63 | 64 | SELECT minute(60); 65 | SELECT hour(24); 66 | SELECT day(1/24.0); 67 | SELECT degree_arc(360); 68 | SELECT minute_arc(60); 69 | SELECT second_arc(60); 70 | SELECT hectare(1/100.0); 71 | SELECT liter(1000); 72 | SELECT tonne(1/1000.0); 73 | SELECT au(10); 74 | SELECT decibel(-3); 75 | SELECT decibel(3); 76 | SELECT decibel(10); 77 | SELECT decibel(20); 78 | 79 | SET client_min_messages = warning; 80 | DROP TABLE IF EXISTS u; 81 | RESET client_min_messages; 82 | CREATE TABLE u AS 83 | SELECT unit AS u, 2000 * unit AS "2k", unit/5000 AS "200µ", 0.002/unit AS "500th_inverse", (40*unit)^2 AS "1600_square" 84 | FROM units WHERE base OR coherent; 85 | SELECT * FROM u; 86 | SELECT u, u::text::unit AS "text::unit", "2k"::text::unit, "200µ"::text::unit, "500th_inverse"::text::unit, "1600_square"::text::unit FROM u; 87 | 88 | -- test i/o 89 | SELECT 90 | a.u, b.u, (a.u::unit*b.u::unit)::text, (a.u::unit*b.u::unit)::text::unit 91 | FROM 92 | units a CROSS JOIN units b 93 | WHERE (a.u::unit*b.u::unit)::text != (a.u::unit*b.u::unit)::text::unit::text; 94 | -------------------------------------------------------------------------------- /unit--1--2.sql: -------------------------------------------------------------------------------- 1 | ALTER TYPE unit_accum_t 2 | ADD ATTRIBUTE squares double precision; 3 | 4 | CREATE FUNCTION unit_accum(a unit_accum_t, u unit) 5 | RETURNS unit_accum_t 6 | AS $$SELECT (CASE WHEN a.s = '0'::unit THEN u ELSE a.s + u END, a.squares + value(u)^2, a.n + 1)::unit_accum_t$$ 7 | LANGUAGE SQL IMMUTABLE STRICT; 8 | 9 | -- update v1 avg() aggregate 10 | UPDATE pg_aggregate 11 | SET agginitval = '(0,0,0)' 12 | WHERE aggtransfn = 'unit_accum'::regproc AND agginitval = '(0,0)'; 13 | 14 | CREATE FUNCTION unit_var_pop(a unit_accum_t) 15 | RETURNS double precision 16 | AS $$SELECT CASE WHEN a.n > 0 THEN (a.squares - value(a.s)^2 / a.n) / a.n ELSE NULL END$$ 17 | LANGUAGE SQL IMMUTABLE STRICT; 18 | 19 | CREATE AGGREGATE var_pop(unit) 20 | ( 21 | sfunc = unit_accum, 22 | stype = unit_accum_t, 23 | finalfunc = unit_var_pop, 24 | initcond = '(0,0,0)' 25 | ); 26 | 27 | CREATE FUNCTION unit_var_samp(a unit_accum_t) 28 | RETURNS double precision 29 | AS $$SELECT CASE WHEN a.n > 1 THEN (a.squares - value(a.s)^2 / a.n) / (a.n - 1) WHEN a.n = 1 THEN 0 ELSE NULL END$$ 30 | LANGUAGE SQL IMMUTABLE STRICT; 31 | 32 | CREATE AGGREGATE var_samp(unit) 33 | ( 34 | sfunc = unit_accum, 35 | stype = unit_accum_t, 36 | finalfunc = unit_var_samp, 37 | initcond = '(0,0,0)' 38 | ); 39 | 40 | CREATE AGGREGATE variance(unit) 41 | ( 42 | sfunc = unit_accum, 43 | stype = unit_accum_t, 44 | finalfunc = unit_var_samp, 45 | initcond = '(0,0,0)' 46 | ); 47 | 48 | CREATE FUNCTION unit_stddev_pop(a unit_accum_t) 49 | RETURNS unit 50 | AS $$SELECT CASE WHEN a.n > 0 THEN sqrt((a.squares - value(a.s)^2 / a.n) / a.n) * dimension(a.s) ELSE NULL END$$ 51 | LANGUAGE SQL IMMUTABLE STRICT; 52 | 53 | CREATE AGGREGATE stddev_pop(unit) 54 | ( 55 | sfunc = unit_accum, 56 | stype = unit_accum_t, 57 | finalfunc = unit_stddev_pop, 58 | initcond = '(0,0,0)' 59 | ); 60 | 61 | CREATE FUNCTION unit_stddev_samp(a unit_accum_t) 62 | RETURNS unit 63 | AS $$SELECT CASE WHEN a.n > 1 THEN sqrt((a.squares - value(a.s)^2 / a.n) / (a.n - 1)) * dimension(a.s) WHEN a.n = 1 THEN 0 * dimension(a.s) ELSE NULL END$$ 64 | LANGUAGE SQL IMMUTABLE STRICT; 65 | 66 | CREATE AGGREGATE stddev_samp(unit) 67 | ( 68 | sfunc = unit_accum, 69 | stype = unit_accum_t, 70 | finalfunc = unit_stddev_samp, 71 | initcond = '(0,0,0)' 72 | ); 73 | 74 | CREATE AGGREGATE stddev(unit) 75 | ( 76 | sfunc = unit_accum, 77 | stype = unit_accum_t, 78 | finalfunc = unit_stddev_samp, 79 | initcond = '(0,0,0)' 80 | ); 81 | 82 | -------------------------------------------------------------------------------- /defined_units.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEFINED_UNITS_H 2 | #define _DEFINED_UNITS_H 1 3 | 4 | #include /* NULL */ 5 | #include "unit.h" 6 | 7 | const char *base_units[N_UNITS] = { 8 | "m", 9 | "kg", 10 | "s", 11 | "A", 12 | "K", 13 | "mol", 14 | "cd", 15 | "B", 16 | }; 17 | 18 | const struct derived_unit_t si_derived_units[] = { /* https://en.wikipedia.org/wiki/International_System_of_Units#Derived_units */ 19 | /* The second is part of this table so we can easily detect its usage for printing it using the hh:mm:ss syntax */ 20 | { "s", { 0, 0, 1, 0, 0, 0, 0, 0,} }, /* second time s */ 21 | { "Hz", { 0, 0, -1, 0, 0, 0, 0, 0,} }, /* hertz frequency s^-1 */ 22 | { "N", { 1, 1, -2, 0, 0, 0, 0, 0,} }, /* newton force, weight kg·m·s^-2 */ 23 | { "Pa", { -1, 1, -2, 0, 0, 0, 0, 0,} }, /* pascal pressure, stress N/m^2 kg·m^-1·s^-2 */ 24 | { "J", { 2, 1, -2, 0, 0, 0, 0, 0,} }, /* joule energy, work, heat N·m kg·m^2·s^-2 */ 25 | { "W", { 2, 1, -3, 0, 0, 0, 0, 0,} }, /* watt power, radiant flux J/s kg·m^2·s^-3 */ 26 | { "C", { 0, 0, 1, 1, 0, 0, 0, 0,} }, /* coulomb electric charge s·A */ 27 | { "V", { 2, 1, -3, -1, 0, 0, 0, 0,} }, /* volt voltage W/A kg·m^2·s^-3·A^-1 */ 28 | { "F", { -2, -1, 4, 2, 0, 0, 0, 0,} }, /* farad electric capacitance C/V kg^-1·m^-2·s^4·A^2 */ 29 | { "Ω", { 2, 1, -3, -2, 0, 0, 0, 0,} }, /* ohm electric resistance, impedance V/A kg·m^2·s^-3·A^-2 */ 30 | { "S", { -2, -1, 3, 2, 0, 0, 0, 0,} }, /* siemens electrical conductance A/V kg^-1·m^-2·s^3·A^2 */ 31 | { "Wb", { 2, 1, -2, -1, 0, 0, 0, 0,} }, /* weber magnetic flux V·s kg·m^2·s^-2·A^-1 */ 32 | { "T", { 0, 1, -2, -1, 0, 0, 0, 0,} }, /* tesla magnetic flux density Wb/m^2 kg·s^-2·A^-1 */ 33 | { "H", { 2, 1, -2, -2, 0, 0, 0, 0,} }, /* henry inductance Wb/A kg·m^2·s^-2·A^-2 */ 34 | { "lx", { -2, 0, 0, 0, 0, 0, 1, 0,} }, /* lux illuminance lm/m^2 m^-2·cd */ 35 | { "Gy", { 2, 0, -2, 0, 0, 0, 0, 0,} }, /* gray absorbed dose (of ionizing radiation) J/kg m^2·s^-2 */ 36 | { "kat", { 0, 0, -1, 0, 0, 1, 0, 0,} }, /* katal catalytic activity mol·s^-1 */ 37 | { 0 } 38 | }; 39 | 40 | #endif /* _DEFINED_UNITS_H */ 41 | -------------------------------------------------------------------------------- /sql/functions.sql: -------------------------------------------------------------------------------- 1 | -- test functions 2 | SELECT value('2'::unit); 3 | SELECT value(meter(2)); 4 | SELECT dimension('2'::unit); 5 | SELECT dimension(kilogram(2)); 6 | 7 | -- test unit addition/subtraction 8 | SELECT '1'::unit + '2' AS sum; 9 | SELECT '1'::unit - '2' AS difference; 10 | SELECT '3 m'::unit - '1 µm' AS difference; 11 | SELECT '0' + meter() AS error; 12 | SELECT meter() + '0' AS error; 13 | SELECT meter() + kilogram() AS error; 14 | SELECT meter() - kilogram() AS error; 15 | 16 | -- test unit multiplication/division 17 | SELECT - '1'::unit AS negative; 18 | SELECT '2'::unit * '2' AS product; 19 | SELECT '5'::unit / '2' AS fraction; 20 | SELECT '0'::unit / '2' AS zero; 21 | SELECT '5'::unit / '0' AS division_by_zero; 22 | 23 | SELECT - second() AS minus_second; 24 | SELECT meter() * '2' AS two_meters; 25 | SELECT '3' * meter() AS three_meters; 26 | SELECT meter() * meter() AS square_meter; 27 | SELECT meter() * kilogram() AS meter_kilogram; 28 | SELECT '1' / second(.02) AS hertz; 29 | SELECT meter(9.81) / (second() * second()) AS acceleration; 30 | 31 | -- test unit/double operators 32 | SELECT 2 * meter() AS two_meters; 33 | SELECT ampere() * 3 AS three_ampere; 34 | SELECT 4 / second() AS four_hertz; 35 | SELECT kelvin(10) / 5 AS two_kelvin; 36 | SELECT 0 / candela() AS zero_by_candela; 37 | SELECT 1 / mole(0) AS error; 38 | SELECT byte(0) / 2 AS zero_byte; 39 | SELECT kilogram(1) / 0 AS error; 40 | 41 | -- test exponentiation 42 | SELECT '6'::unit ^ 2 AS square; 43 | SELECT meter(100) ^ 2 AS square_meter; 44 | SELECT kilogram(100) ^ 2 AS square_kilogram; 45 | SELECT second(.02) ^ -1 AS hertz; 46 | SELECT ampere(4) ^ 0 AS unity; 47 | SELECT '0'::unit ^ 0 AS unity; 48 | 49 | -- test roots 50 | SELECT sqrt('4'::unit); 51 | SELECT sqrt('4 m^2'::unit); 52 | SELECT |/'16 s^-4'::unit; 53 | SELECT sqrt('-4 m^2'::unit) AS error; 54 | SELECT sqrt('4 m'::unit) AS error; 55 | SELECT cbrt('8'::unit); 56 | SELECT cbrt('8 m^3'::unit); 57 | SELECT ||/'-27 s^-6'::unit; 58 | SELECT cbrt('-4 m'::unit) AS error; 59 | 60 | WITH 61 | v(u) AS (VALUES 62 | (NULL), 63 | ('0'::unit), 64 | ('1'::unit), 65 | ('2'::unit), 66 | (meter(0)), 67 | (meter()), 68 | (meter() * '2'), 69 | (kilogram(0)), 70 | (kilogram()), 71 | (kilogram() * '2') 72 | ), 73 | va(a) AS (SELECT * FROM v), 74 | vb(b) AS (SELECT * FROM v) 75 | SELECT 76 | a, b, 77 | -a AS neg, 78 | a * b AS mul, 79 | CASE WHEN value(b) = 0 THEN NULL ELSE a / b END AS div 80 | FROM 81 | va CROSS JOIN vb; 82 | 83 | WITH 84 | v(a) AS (VALUES 85 | (NULL), 86 | ('1'::unit), 87 | ('2'::unit), 88 | (meter()), 89 | (meter() * '2'), 90 | (kilogram()), 91 | (kilogram() * '2') 92 | ) 93 | SELECT 94 | a, 95 | a ^ -2 AS pow_2, a ^ -1 AS pow_1, a ^ 0 AS pow0, a ^ 1 AS pow1, a ^ 2 AS pow2 96 | FROM 97 | v; 98 | 99 | -- test conversion 100 | SELECT '1m'::unit @ 'mm'; 101 | SELECT '10g'::unit @ 'mg'; 102 | SELECT '5dm^3'::unit @ 'l'; 103 | SELECT '9.81 m/s^2'::unit @ 'N/kg', '9.81 m/s^2'::unit @@ 'N/kg'; 104 | SELECT '16384 B'::unit @ '8192 B', '16384 B'::unit @@ '8192 B'; 105 | SELECT '1 hl'::unit @ '0.5 l'; 106 | SELECT '5MB/min'::unit @ 'GB/d'; 107 | SELECT '5m'::unit @ 's' AS error; 108 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | 7.7: Jan 06, 2023 2 | ----------------- 3 | * Fix printing of values around 1e33 on i386. 4 | 5 | 7.6: Jan 04, 2023 6 | ----------------- 7 | * Import definitions.units September 2022 Version 3.15. 8 | * Add R ronna, Q quetta, r ronto, Q quecto, Ri robi, Qi quebi prefixes. 9 | 10 | 7.4: May 20, 2021 - World Metrology Day 11 | --------------------------------------- 12 | * Fix hash table creation on PostgreSQL 14. 13 | 14 | 7.3: Oct 19, 2020 15 | ----------------- 16 | * Packaging updates. 17 | 18 | 7.2: May 20, 2019 - World Metrology Day 19 | --------------------------------------- 20 | * Rewire PostgreSQL 12's new extra_float_digits=1 default internally to 0 to 21 | keep nice, short decimal representations. 22 | 23 | 7.1: Dec 3, 2018 24 | ---------------- 25 | * Import definitions.unit 2.44 from units 2.18 with 36 new units. 26 | When upgrading from a previous install, use `SELECT unit_load();` to import. 27 | * Adjust powers.h to round "1" down by one ULP. 28 | 29 | 7.0: Sep 28, 2018 30 | ----------------- 31 | * Format time using commonyears + days + hh:mm:ss.sss s. 32 | * Avoid using Gs for Gigaseconds, that's actually gauss. 33 | * Modify @(unit, text) operator to return text instead of cstring. 34 | * Import definitions.units from GNU units 2.17 with 13 new units. 35 | * Add π to known units. 36 | * Remove bison 2 compat mode, but keep pregenerated files for jessie and 37 | trusty. 38 | * Support send/receive via the binary protocol. 39 | * Support Infinity and NaN. 40 | * Add strict comparison operators that error out when dimensions of arguments 41 | do not match: << <<= == <<>> >>= >>. 42 | * Add range type over units: unitrange. 43 | 44 | 6.0: Mar 7, 2018 45 | ---------------- 46 | * Add @@ operator: similar to @, but returns the value of the resulting unit 47 | as double precision number. 48 | * Fix parsing of addition/subtraction in unit values. 49 | * Grant SELECT on unit prefixes and units table to public. 50 | * Add unit_load() function to load/update the data tables. 51 | 52 | 5.0: Jan 10, 2018 53 | ----------------- 54 | * Add rounding function. 55 | * Import definitions.units from GNU units 2.16 with 70 new units. 56 | 57 | 4.0: Jul 7, 2017 58 | ---------------- 59 | * Support exponents written using Unicode superscript characters. 60 | * Report 22P02/invalid_text_representation on invalid unit input. 61 | * Fix crash when unit_reset() runs into an OOM error. Patch by Andreas 62 | Seltenreich, thanks! 63 | * Change @ operator signature to (unit, text). 64 | 65 | 3.0: Mar 22, 2017 66 | ----------------- 67 | * Support defining new units at runtime. 68 | * Import unit definitions from GNU units. 69 | * Add sqrt() and cbrt() functions. 70 | * Extension is not relocatable after installation anymore. 71 | * Bump extension version to 3. 72 | * Bump minimum supported PostgreSQL version to 9.5 so we can use 73 | hash_create(HASH_BLOBS). 74 | * Bump license to GPL-3+ to match GNU units' license. 75 | 76 | 2.0: Jan 9, 2017 77 | ---------------- 78 | * Support IEC binary prefixes for byte. 79 | * Support United States customary units: in, ft, yd, mi, oz, lb. 80 | * Add variance and stddev aggregates 81 | 82 | 1.1: Oct 31, 2016 83 | ----------------- 84 | * Use float8out_internal to format floats on output. 85 | 86 | v1.0: Sep 22, 2016 87 | ------------------ 88 | * Initial release 89 | -------------------------------------------------------------------------------- /sql/unit.sql: -------------------------------------------------------------------------------- 1 | -- test input and output 2 | SELECT '1'::unit; -- unit_in 3 | SELECT unit(1); -- dbl2unit 4 | CREATE TEMP TABLE u (u unit); 5 | INSERT INTO u VALUES ('1'); 6 | SELECT * FROM u; 7 | 8 | -- test constructors 9 | SELECT meter(); 10 | SELECT meter(2.0) AS two_meters; 11 | SELECT kilogram(); 12 | SELECT kilogram(3.0) AS three_kilogram; 13 | SELECT second(); 14 | SELECT second(4.0) AS four_seconds; 15 | SELECT ampere(); 16 | SELECT ampere(5.0) AS five_ampere; 17 | SELECT kelvin(); 18 | SELECT kelvin(6.0) AS six_kelvin; 19 | SELECT mole(); 20 | SELECT mole(7.0) AS seven_mole; 21 | SELECT candela(); 22 | SELECT candela(8.0) AS eight_candela; 23 | SELECT byte(); 24 | SELECT byte(9.0) AS nine_byte; 25 | 26 | -- test dimensionless scales 27 | SELECT unit(0) AS zero, unit(1e-6) AS "1e-6", unit(0.001) AS "0.001", unit(1.0) AS "1", unit(1000.0) AS "1000"; 28 | -- test SI prefixes 29 | SELECT meter(0) AS zero, meter(1e-6) AS "µm", meter(0.001) AS mm, meter(1.0) AS m, meter(1000.0) AS km; 30 | SELECT kilogram(0) AS zero, kilogram(1e-6) AS mg, kilogram(0.001) AS g, kilogram(1.0) AS kg, kilogram(1000.0) AS "Mg"; 31 | -- test combined units (exactly one unit with exponent 1 in numerator) 32 | SELECT meter(0)/second() AS zero, meter(1e-6)/second() AS "µm", meter(0.001)/second() AS mm, meter(1.0)/second() AS m, meter(1000.0)/second() AS km; 33 | SELECT kilogram(0)/second() AS zero, kilogram(1e-6)/second() AS mg, kilogram(0.001)/second() AS g, kilogram(1.0)/second() AS kg, kilogram(1000.0)/second() AS "Mg"; 34 | 35 | -- test parser 36 | SELECT '1 m'::unit; 37 | SELECT '-1 m/s'::unit; 38 | SELECT '10 dm^3'::unit, '10l'::unit; 39 | SELECT '9.81 kg*m/s^2'::unit, '9.81 kg*m/s*s'::unit, '9.81 kg*m/s/s'::unit; 40 | SELECT '1 foobar'::unit AS error; 41 | 42 | -- special values 43 | SELECT '-0'::unit, -'0'::unit, '-0 m'::unit, -'0 m'::unit; 44 | SELECT 'infinity'::unit, 'Infinity m'::unit, 'inf A'::unit; 45 | SELECT '-infinity'::unit, '-Infinity m'::unit, 'Inf A'::unit; 46 | SELECT 'nan'::unit, 'NaN'::unit; 47 | 48 | -- test parser arithmetic 49 | SELECT '1|10'::unit, '1|10 m'::unit; 50 | SELECT '2 m + 3 m - 4 m'::unit, '6 m - 3 m - 2 m'::unit, '6 m - 3 m + 1 m'::unit; 51 | SELECT '2 m * 3 m'::unit, '2 m / 3 m'::unit, '10 m / 5 m / 2 m'::unit; 52 | SELECT '2 m^2 + 3 m * 4 m'::unit, '2 m * 3 m / 4 m * 5 m'::unit; 53 | SELECT '2 m * (1 m + 3 m)'::unit; 54 | SELECT '- m'::unit, '/ m'::unit; 55 | SELECT '4+5'::unit, '4-5'::unit, '4(-5)'::unit, '4*5'::unit, '4/5'::unit; 56 | 57 | -- problematic cases 58 | SELECT '1 cd'::unit AS candela; -- candela vs centiday 59 | SELECT '1 Pa'::unit AS pascal; -- pascal vs petayear 60 | SELECT '1 yg'::unit AS yoctogram, '1.00001 yg'::unit AS actual_yoctogram; -- "yg" had rounding problems in the past 61 | SELECT '1 min'::unit AS minute; -- minute vs milliinch 62 | SELECT '1 ft'::unit AS foot; -- foot vs femtotonne 63 | SELECT '1 yd'::unit AS yard; -- yard vs yoctoday 64 | 65 | -- units whose definition changed between v2 and 3 66 | SELECT 'a'::unit AS are; 67 | SELECT 'da'::unit AS day; 68 | SELECT 'rad'::unit AS radiation; 69 | 70 | -- check which prefix/unit combinations produce other dimensions (parser conflicts) 71 | SELECT 72 | p, u, (p||u)::unit 73 | FROM 74 | prefixes CROSS JOIN units 75 | WHERE 76 | u <> 'kg' AND 77 | dimension(u::unit) != dimension((p||u)::unit) AND 78 | p||u NOT IN ('dat') -- skip ambiguous unit 79 | ORDER BY lower(p) COLLATE "C", lower(u) COLLATE "C"; 80 | -------------------------------------------------------------------------------- /expected/tables.out: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | DROP TABLE IF EXISTS prefixes; 3 | CREATE TABLE prefixes (p text); 4 | DELETE FROM prefixes; 5 | INSERT INTO prefixes VALUES 6 | ('q'),('r'),('y'),('z'),('a'),('f'),('p'),('n'),('µ'),('mu'),('m'),('c'),('d'), 7 | (''), 8 | ('da'),('h'),('k'),('M'),('G'),('T'),('P'),('E'),('Z'),('Y'),('R'),('Q'); 9 | DROP TABLE IF EXISTS factors; 10 | CREATE TABLE factors (f double precision); 11 | DELETE FROM factors; 12 | INSERT INTO factors VALUES 13 | (1e33), (1e30), (1e27), (1e24), (1e21), (1e18), (1e15), (1e12), (1e9), (1e6), (1e3), 14 | (1), 15 | (1e-3), (1e-6), (1e-9), (1e-12), (1e-15), (1e-18), (1e-21), (1e-24), (1e-27), (1e-30), (1e-33), (1e-36), 16 | (0); 17 | DROP TABLE IF EXISTS units; 18 | CREATE TABLE units (u text, unit unit, base boolean, coherent boolean, duplicate boolean); 19 | COMMENT ON COLUMN units.duplicate IS 'For coherent units, this unit has the same dimension as some other base or coherent unit'; 20 | DELETE FROM units; 21 | COPY units (u, base, coherent, duplicate) FROM STDIN; 22 | UPDATE units 23 | SET unit = u::unit; 24 | SELECT * FROM units; 25 | u | unit | base | coherent | duplicate 26 | -----+----------------+------+----------+----------- 27 | m/m | 1 | t | f | f 28 | m | 1 m | t | f | f 29 | kg | 1 kg | t | f | f 30 | s | 1 s | t | f | f 31 | A | 1 A | t | f | f 32 | K | 1 K | t | f | f 33 | mol | 1 mol | t | f | f 34 | cd | 1 cd | t | f | f 35 | B | 1 B | t | f | f 36 | sr | 1 | f | t | t 37 | Hz | 1 Hz | f | t | f 38 | N | 1 N | f | t | f 39 | Pa | 1 Pa | f | t | f 40 | J | 1 J | f | t | f 41 | W | 1 W | f | t | f 42 | C | 1 C | f | t | f 43 | V | 1 V | f | t | f 44 | F | 1 F | f | t | f 45 | Ω | 1 Ω | f | t | f 46 | ohm | 1 Ω | f | t | t 47 | S | 1 S | f | t | f 48 | Wb | 1 Wb | f | t | f 49 | T | 1 T | f | t | f 50 | H | 1 H | f | t | f 51 | °C | 1 K | f | t | t 52 | lm | 1 cd | f | t | t 53 | lx | 1 lx | f | t | f 54 | Bq | 1 Hz | f | t | t 55 | Gy | 1 Gy | f | t | f 56 | rad | 10 mGy | f | f | t 57 | Sv | 1 Gy | f | t | t 58 | kat | 1 kat | f | t | f 59 | g | 1 g | f | f | t 60 | a | 100 m^2 | f | f | f 61 | l | 0.001 m^3 | f | f | f 62 | t | 1 Mg | f | f | f 63 | bar | 100 kPa | f | f | f 64 | min | 00:01:00 s | f | f | f 65 | hr | 01:00:00 s | f | f | f 66 | d | 1 d | f | f | f 67 | in | 25.4 mm | f | f | f 68 | ft | 304.8 mm | f | f | f 69 | yd | 914.4 mm | f | f | f 70 | mi | 1.609344 km | f | f | f 71 | oz | 28.349523125 g | f | f | f 72 | lb | 453.59237 g | f | f | f 73 | (46 rows) 74 | 75 | -------------------------------------------------------------------------------- /unit_prefixes.data: -------------------------------------------------------------------------------- 1 | quetta 1e+30 1e30 \N 2 | ronna 1e+27 1e27 \N 3 | yotta 1e+24 1e24 \N 4 | zetta 1e+21 1e21 \N 5 | exa 1e+18 1e18 \N 6 | peta 1e+15 1e15 \N 7 | tera 1000000000000 1e12 \N 8 | giga 1000000000 1e9 \N 9 | mega 1000000 1e6 \N 10 | myria 10000 1e4 \N 11 | kilo 1000 1e3 \N 12 | hecto 100 1e2 \N 13 | deca 10 1e1 \N 14 | deka 10 deca \N 15 | deci 0.1 1e-1 \N 16 | centi 0.01 1e-2 \N 17 | milli 0.001 1e-3 \N 18 | micro 1e-06 1e-6 \N 19 | nano 1e-09 1e-9 \N 20 | pico 1e-12 1e-12 \N 21 | femto 1e-15 1e-15 \N 22 | atto 1e-18 1e-18 \N 23 | zepto 1e-21 1e-21 \N 24 | yocto 1e-24 1e-24 \N 25 | ronto 1e-27 1e-27 \N 26 | quecto 1e-30 1e-30 \N 27 | quarter 0.25 1|4 \N 28 | semi 0.5 0.5 \N 29 | demi 0.5 0.5 \N 30 | hemi 0.5 0.5 \N 31 | half 0.5 0.5 \N 32 | double 2 2 \N 33 | triple 3 3 \N 34 | treble 3 3 \N 35 | kibi 1024 2^10 \N 36 | mebi 1048576 2^20 \N 37 | gibi 1073741824 2^30 \N 38 | tebi 1099511627776 2^40 \N 39 | pebi 1.125899906842624e+15 2^50 \N 40 | exbi 1.152921504606847e+18 2^60 \N 41 | zebi 1.1805916207174113e+21 2^70 \N 42 | yobi 1.2089258196146292e+24 2^80 \N 43 | robi 1.2379400392853803e+27 2^90 \N 44 | quebi 1.2676506002282294e+30 2^100 \N 45 | Ki 1024 kibi \N 46 | Mi 1048576 mebi \N 47 | Gi 1073741824 gibi \N 48 | Ti 1099511627776 tebi \N 49 | Pi 1.125899906842624e+15 pebi \N 50 | Ei 1.152921504606847e+18 exbi \N 51 | Zi 1.1805916207174113e+21 zebi \N 52 | Yi 1.2089258196146292e+24 yobi \N 53 | Ri 1.2379400392853803e+27 robi \N 54 | Qi 1.2676506002282294e+30 quebi \N 55 | Q 1e+30 quetta \N 56 | R 1e+27 ronna \N 57 | Y 1e+24 yotta \N 58 | Z 1e+21 zetta \N 59 | E 1e+18 exa \N 60 | P 1e+15 peta \N 61 | T 1000000000000 tera \N 62 | G 1000000000 giga \N 63 | M 1000000 mega \N 64 | k 1000 kilo \N 65 | h 100 hecto \N 66 | da 10 deka \N 67 | d 0.1 deci \N 68 | c 0.01 centi \N 69 | m 0.001 milli \N 70 | u 1e-06 micro \N 71 | mu 1e-06 micro \N 72 | n 1e-09 nano \N 73 | p 1e-12 pico \N 74 | f 1e-15 femto \N 75 | a 1e-18 atto \N 76 | z 1e-21 zepto \N 77 | y 1e-24 yocto \N 78 | r 1e-27 ronto \N 79 | q 1e-30 quecto \N 80 | US 1.000002000004 US \N 81 | survey 1.000002000004 US \N 82 | geodetic 1.000002000004 US \N 83 | int 0.999998 int \N 84 | Zena 12 12 \N 85 | Duna 144 12^2 \N 86 | Trina 1728 12^3 \N 87 | Quedra 20736 12^4 \N 88 | Quena 248832 12^5 \N 89 | Hesa 2985984 12^6 \N 90 | Seva 35831808 12^7 \N 91 | Aka 429981696 12^8 \N 92 | Neena 5159780352 12^9 \N 93 | Dexa 61917364224 12^10 \N 94 | Lefa 743008370688 12^11 \N 95 | Zennila 8916100448256 12^12 \N 96 | Zeni 0.08333333333333333 12^-1 \N 97 | Duni 0.006944444444444444 12^-2 \N 98 | Trini 0.0005787037037037037 12^-3 \N 99 | Quedri 4.8225308641975306e-05 12^-4 \N 100 | Queni 4.018775720164609e-06 12^-5 \N 101 | Hesi 3.3489797668038406e-07 12^-6 \N 102 | Sevi 2.790816472336534e-08 12^-7 \N 103 | Aki 2.3256803936137784e-09 12^-8 \N 104 | Neeni 1.9380669946781485e-10 12^-9 \N 105 | Dexi 1.615055828898457e-11 12^-10 \N 106 | Lefi 1.345879857415381e-12 12^-11 \N 107 | Zennili 1.1215665478461509e-13 12^-12 \N 108 | ⅛ 0.125 1|8 \N 109 | ¼ 0.25 1|4 \N 110 | ⅜ 0.375 3|8 \N 111 | ½ 0.5 1|2 \N 112 | ⅝ 0.625 5|8 \N 113 | ¾ 0.75 3|4 \N 114 | ⅞ 0.875 7|8 \N 115 | ⅙ 0.16666666666666666 1|6 \N 116 | ⅓ 0.3333333333333333 1|3 \N 117 | ⅔ 0.6666666666666666 2|3 \N 118 | ⅚ 0.8333333333333334 5|6 \N 119 | ⅕ 0.2 1|5 \N 120 | ⅖ 0.4 2|5 \N 121 | ⅗ 0.6 3|5 \N 122 | ⅘ 0.8 4|5 \N 123 | µ 1e-06 micro \N 124 | μ 1e-06 micro \N 125 | UK 0.9999982611548556 UK \N 126 | british 0.9999982611548556 UK \N 127 | -------------------------------------------------------------------------------- /expected/prefix.out: -------------------------------------------------------------------------------- 1 | SELECT 2 | f, meter(f), kilogram(f) 3 | FROM 4 | factors; 5 | f | meter | kilogram 6 | ---------------+---------+---------- 7 | 1e+33 | 1e+33 m | 1e+33 kg 8 | 1e+30 | 1 Qm | 1e+30 kg 9 | 1e+27 | 1 Rm | 1 Qg 10 | 1e+24 | 1 Ym | 1 Rg 11 | 1e+21 | 1 Zm | 1 Yg 12 | 1e+18 | 1 Em | 1 Zg 13 | 1e+15 | 1 Pm | 1 Eg 14 | 1000000000000 | 1 Tm | 1 Pg 15 | 1000000000 | 1 Gm | 1 Tg 16 | 1000000 | 1 Mm | 1 Gg 17 | 1000 | 1 km | 1 Mg 18 | 1 | 1 m | 1 kg 19 | 0.001 | 1 mm | 1 g 20 | 1e-06 | 1 µm | 1 mg 21 | 1e-09 | 1 nm | 1 µg 22 | 1e-12 | 1 pm | 1 ng 23 | 1e-15 | 1 fm | 1 pg 24 | 1e-18 | 1 am | 1 fg 25 | 1e-21 | 1 zm | 1 ag 26 | 1e-24 | 1 ym | 1 zg 27 | 1e-27 | 1 rm | 1 yg 28 | 1e-30 | 1 qm | 1 rg 29 | 1e-33 | 1e-33 m | 1 qg 30 | 1e-36 | 1e-36 m | 1e-36 kg 31 | 0 | 0 m | 0 kg 32 | (25 rows) 33 | 34 | SELECT 35 | p, (p||'m/m')::unit AS factor, 36 | ('0.001 '||p||'m')::unit AS "0.001 m", 37 | (p||'m')::unit AS "meter", 38 | ('1000 '||p||'m')::unit AS "1000 m", 39 | ('0.001 '||p||'g')::unit AS "0.001 g", 40 | (p||'g')::unit AS "gram", 41 | ('1000 '||p||'g')::unit AS "1000 g" 42 | FROM 43 | prefixes; 44 | p | factor | 0.001 m | meter | 1000 m | 0.001 g | gram | 1000 g 45 | ----+---------------+---------+--------+---------+----------+--------+---------- 46 | q | 1e-30 | 1e-33 m | 1 qm | 1 rm | 1e-36 kg | 1 qg | 1 rg 47 | r | 1e-27 | 1 qm | 1 rm | 1 ym | 1 qg | 1 rg | 1 yg 48 | y | 1e-24 | 1 rm | 1 ym | 1 zm | 1 rg | 1 yg | 1 zg 49 | z | 1e-21 | 1 ym | 1 zm | 1 am | 1 yg | 1 zg | 1 ag 50 | a | 1e-18 | 1 zm | 1 am | 1 fm | 1 zg | 1 ag | 1 fg 51 | f | 1e-15 | 1 am | 1 fm | 1 pm | 1 ag | 1 fg | 1 pg 52 | p | 1e-12 | 1 fm | 1 pm | 1 nm | 1 fg | 1 pg | 1 ng 53 | n | 1e-09 | 1 pm | 1 nm | 1 µm | 1 pg | 1 ng | 1 µg 54 | µ | 1e-06 | 1 nm | 1 µm | 1 mm | 1 ng | 1 µg | 1 mg 55 | mu | 1e-06 | 1 nm | 1 µm | 1 mm | 1 ng | 1 µg | 1 mg 56 | m | 0.001 | 1 µm | 1 mm | 1 m | 1 µg | 1 mg | 1 g 57 | c | 0.01 | 10 µm | 10 mm | 10 m | 10 µg | 10 mg | 10 g 58 | d | 0.1 | 100 µm | 100 mm | 100 m | 100 µg | 100 mg | 100 g 59 | | 1 | 1 mm | 1 m | 1 km | 1 mg | 1 g | 1 kg 60 | da | 10 | 10 mm | 10 m | 10 km | 10 mg | 10 g | 10 kg 61 | h | 100 | 100 mm | 100 m | 100 km | 100 mg | 100 g | 100 kg 62 | k | 1000 | 1 m | 1 km | 1 Mm | 1 g | 1 kg | 1 Mg 63 | M | 1000000 | 1 km | 1 Mm | 1 Gm | 1 kg | 1 Mg | 1 Gg 64 | G | 1000000000 | 1 Mm | 1 Gm | 1 Tm | 1 Mg | 1 Gg | 1 Tg 65 | T | 1000000000000 | 1 Gm | 1 Tm | 1 Pm | 1 Gg | 1 Tg | 1 Pg 66 | P | 1e+15 | 1 Tm | 1 Pm | 1 Em | 1 Tg | 1 Pg | 1 Eg 67 | E | 1e+18 | 1 Pm | 1 Em | 1 Zm | 1 Pg | 1 Eg | 1 Zg 68 | Z | 1e+21 | 1 Em | 1 Zm | 1 Ym | 1 Eg | 1 Zg | 1 Yg 69 | Y | 1e+24 | 1 Zm | 1 Ym | 1 Rm | 1 Zg | 1 Yg | 1 Rg 70 | R | 1e+27 | 1 Ym | 1 Rm | 1 Qm | 1 Yg | 1 Rg | 1 Qg 71 | Q | 1e+30 | 1 Rm | 1 Qm | 1e+33 m | 1 Rg | 1 Qg | 1e+30 kg 72 | (26 rows) 73 | 74 | -------------------------------------------------------------------------------- /expected/iec.out: -------------------------------------------------------------------------------- 1 | VALUES 2 | ('1 mB'::unit), 3 | ('1 B'::unit), 4 | ('1 KiB'::unit), 5 | ('1 MiB'::unit), 6 | ('1 GiB'::unit), 7 | ('1 TiB'::unit), 8 | ('1 PiB'::unit), 9 | ('1 EiB'::unit), 10 | ('1 ZiB'::unit), 11 | ('1 YiB'::unit), 12 | ('1 RiB'::unit), 13 | ('1 QiB'::unit); 14 | column1 15 | --------------------- 16 | 1 mB 17 | 1 B 18 | 1.024 kB 19 | 1.048576 MB 20 | 1.073741824 GB 21 | 1.099511627776 TB 22 | 1.12589990684262 PB 23 | 1.15292150460685 EB 24 | 1.18059162071741 ZB 25 | 1.20892581961463 YB 26 | 1.23794003928538 RB 27 | 1.26765060022823 QB 28 | (12 rows) 29 | 30 | VALUES 31 | ('1 B'::unit @ 'B'), 32 | ('1 KiB'::unit @ 'KiB'), 33 | ('1 MiB'::unit @ 'MiB'), 34 | ('1 GiB'::unit @ 'GiB'), 35 | ('1 TiB'::unit @ 'TiB'), 36 | ('1 PiB'::unit @ 'PiB'), 37 | ('1 EiB'::unit @ 'EiB'), 38 | ('1 ZiB'::unit @ 'ZiB'), 39 | ('1 YiB'::unit @ 'YiB'), 40 | ('1 RiB'::unit @ 'RiB'), 41 | ('1 QiB'::unit @ 'QiB'); 42 | column1 43 | --------- 44 | 1 B 45 | 1 KiB 46 | 1 MiB 47 | 1 GiB 48 | 1 TiB 49 | 1 PiB 50 | 1 EiB 51 | 1 ZiB 52 | 1 YiB 53 | 1 RiB 54 | 1 QiB 55 | (11 rows) 56 | 57 | VALUES 58 | ('1 B'::unit @ 'B'), 59 | ('1 kB'::unit @ 'KiB'), 60 | ('1 MB'::unit @ 'MiB'), 61 | ('1 GB'::unit @ 'GiB'), 62 | ('1 TB'::unit @ 'TiB'), 63 | ('1 PB'::unit @ 'PiB'), 64 | ('1 EB'::unit @ 'EiB'), 65 | ('1 ZB'::unit @ 'ZiB'), 66 | ('1 YB'::unit @ 'YiB'), 67 | ('1 RB'::unit @ 'RiB'), 68 | ('1 QB'::unit @ 'QiB'); 69 | column1 70 | ----------------------- 71 | 1 B 72 | 0.9765625 KiB 73 | 0.95367431640625 MiB 74 | 0.931322574615479 GiB 75 | 0.909494701772928 TiB 76 | 0.888178419700125 PiB 77 | 0.867361737988404 EiB 78 | 0.8470329472543 ZiB 79 | 0.827180612553028 YiB 80 | 0.807793566946316 RiB 81 | 0.788860905221012 QiB 82 | (11 rows) 83 | 84 | -- errors 85 | SELECT '1 kiB'::unit; 86 | ERROR: unit "kiB" is not known 87 | LINE 1: SELECT '1 kiB'::unit; 88 | ^ 89 | SELECT '1 Ki'::unit; 90 | ERROR: unit "Ki" is not known 91 | LINE 1: SELECT '1 Ki'::unit; 92 | ^ 93 | -- used to be an error in v2 94 | SELECT '1 Kim'::unit AS "1024 m"; 95 | 1024 m 96 | ---------- 97 | 1.024 km 98 | (1 row) 99 | 100 | -- binary prefix output 101 | SET unit.byte_output_iec = 'foobar'; 102 | ERROR: parameter "unit.byte_output_iec" requires a Boolean value 103 | SET unit.byte_output_iec = on; 104 | VALUES 105 | ('.001 B'::unit, '.001 B'::unit), 106 | ('1 B'::unit, '1 B'::unit), 107 | ('1 KiB'::unit, '1 kB'::unit), 108 | ('1 MiB'::unit, '1 MB'::unit), 109 | ('1 GiB'::unit, '1 GB'::unit), 110 | ('1 TiB'::unit, '1 TB'::unit), 111 | ('1 PiB'::unit, '1 PB'::unit), 112 | ('1 EiB'::unit, '1 EB'::unit), 113 | ('1 ZiB'::unit, '1 ZB'::unit), 114 | ('1 YiB'::unit, '1 YB'::unit), 115 | ('1 RiB'::unit, '1 RB'::unit), 116 | ('1 QiB'::unit, '1 QB'::unit), 117 | ('1024 QiB'::unit, '1000 QB'::unit); 118 | column1 | column2 119 | ------------------------+---------------------- 120 | 0.001 B | 0.001 B 121 | 1 B | 1 B 122 | 1 KiB | 1000 B 123 | 1 MiB | 976.5625 KiB 124 | 1 GiB | 953.67431640625 MiB 125 | 1 TiB | 931.322574615479 GiB 126 | 1 PiB | 909.494701772928 TiB 127 | 1 EiB | 888.178419700125 PiB 128 | 1 ZiB | 867.361737988404 EiB 129 | 1 YiB | 847.0329472543 ZiB 130 | 1 RiB | 827.180612553028 YiB 131 | 1 QiB | 807.793566946316 RiB 132 | 1.29807421463371e+33 B | 788.860905221012 QiB 133 | (13 rows) 134 | 135 | -------------------------------------------------------------------------------- /sql/aggregate.sql: -------------------------------------------------------------------------------- 1 | -- test aggregates 2 | 3 | /* revert to pre-12 default for stddev(value()) tests */ 4 | SET extra_float_digits = 0; 5 | 6 | CREATE TEMP TABLE u ( 7 | u unit 8 | ); 9 | 10 | -- empty 11 | SELECT sum(u) AS null_sum FROM u; 12 | SELECT min(u) AS null_min FROM u; 13 | SELECT max(u) AS null_max FROM u; 14 | SELECT avg(u) AS null_avg FROM u; 15 | SELECT var_pop(u) AS null_var_pop FROM u; 16 | SELECT var_samp(u) AS null_var_samp FROM u; 17 | SELECT variance(u) AS null_variance FROM u; 18 | SELECT stddev_pop(u) AS null_stddev_pop FROM u; 19 | SELECT stddev_samp(u) AS null_stddev_samp FROM u; 20 | SELECT stddev(u) AS null_stddev FROM u; 21 | 22 | -- one NULL row 23 | INSERT INTO u VALUES (NULL); 24 | SELECT sum(u) AS null_sum FROM u; 25 | SELECT min(u) AS null_min FROM u; 26 | SELECT max(u) AS null_max FROM u; 27 | SELECT avg(u) AS null_avg FROM u; 28 | SELECT var_pop(u) AS null_var_pop FROM u; 29 | SELECT var_samp(u) AS null_var_samp FROM u; 30 | SELECT variance(u) AS null_variance FROM u; 31 | SELECT stddev_pop(u) AS null_stddev_pop FROM u; 32 | SELECT stddev_samp(u) AS null_stddev_samp FROM u; 33 | SELECT stddev(u) AS null_stddev FROM u; 34 | 35 | -- one non-NULL row 36 | DELETE FROM u; 37 | INSERT INTO u VALUES (meter(5)); 38 | SELECT sum(u) AS five_meters FROM u; 39 | SELECT min(u) AS five_meters FROM u; 40 | SELECT max(u) AS five_meters FROM u; 41 | SELECT avg(u) AS five_meters FROM u; 42 | SELECT var_pop(u) AS zero_var_pop FROM u; 43 | SELECT var_samp(u) AS zero_var_samp FROM u; 44 | SELECT variance(u) AS zero_variance FROM u; 45 | SELECT stddev_pop(u) AS zero_stddev_pop FROM u; 46 | SELECT stddev_samp(u) AS zero_stddev_samp FROM u; 47 | SELECT stddev(u) AS zero_stddev FROM u; 48 | 49 | -- two rows, matching dimensions 50 | DELETE FROM u; 51 | INSERT INTO u VALUES (meter()), (meter(2)); 52 | SELECT sum(u) AS three_meters FROM u; 53 | SELECT min(u) AS one_meter FROM u; 54 | SELECT max(u) AS two_meters FROM u; 55 | SELECT avg(u) AS oneandhalf_meters FROM u; 56 | SELECT var_pop(u) FROM u; 57 | SELECT var_samp(u) FROM u; 58 | SELECT variance(u) FROM u; 59 | SELECT stddev_pop(u) FROM u; 60 | SELECT stddev_samp(u) FROM u; 61 | SELECT stddev(u) FROM u; 62 | 63 | DELETE FROM u; 64 | INSERT INTO u VALUES (kilogram(2)), (kilogram()); 65 | SELECT sum(u), sum(value(u)) FROM u; 66 | SELECT min(u), min(value(u)) FROM u; 67 | SELECT max(u), max(value(u)) FROM u; 68 | SELECT avg(u), avg(value(u)) FROM u; 69 | SELECT var_pop(u), var_pop(value(u)) FROM u; 70 | SELECT var_samp(u), var_samp(value(u)) FROM u; 71 | SELECT variance(u), variance(value(u)) FROM u; 72 | SELECT stddev_pop(u), stddev_pop(value(u)) FROM u; 73 | SELECT stddev_samp(u), stddev_samp(value(u)) FROM u; 74 | SELECT stddev(u), stddev(value(u)) FROM u; 75 | 76 | -- three rows, one NULL 77 | INSERT INTO u VALUES (NULL); 78 | SELECT sum(u) AS three_kilogram FROM u; 79 | SELECT min(u) AS one_kilogram FROM u; 80 | SELECT max(u) AS two_kilogram FROM u; 81 | SELECT avg(u) AS oneandhalf_kilogram FROM u; 82 | SELECT var_pop(u) FROM u; 83 | SELECT var_samp(u) FROM u; 84 | SELECT variance(u) FROM u; 85 | SELECT stddev_pop(u) FROM u; 86 | SELECT stddev_samp(u) FROM u; 87 | SELECT stddev(u) FROM u; 88 | 89 | -- two rows, dimension mismatch 90 | DELETE FROM u; 91 | INSERT INTO u VALUES (ampere()), (kilogram(2)); 92 | SELECT sum(u) AS error FROM u; 93 | SELECT min(u) AS error FROM u; 94 | SELECT max(u) AS error FROM u; 95 | SELECT avg(u) AS error FROM u; 96 | SELECT var_pop(u) AS error FROM u; 97 | SELECT var_samp(u) AS error FROM u; 98 | SELECT variance(u) AS error FROM u; 99 | SELECT stddev_pop(u) AS error FROM u; 100 | SELECT stddev_samp(u) AS error FROM u; 101 | SELECT stddev(u) AS error FROM u; 102 | -------------------------------------------------------------------------------- /unit--6--7.sql.in: -------------------------------------------------------------------------------- 1 | -- add recv/send functions 2 | 3 | CREATE FUNCTION unit_recv(internal) 4 | RETURNS unit 5 | AS '$libdir/unit' 6 | LANGUAGE C IMMUTABLE STRICT; 7 | 8 | CREATE FUNCTION unit_send(unit) 9 | RETURNS bytea 10 | AS '$libdir/unit' 11 | LANGUAGE C IMMUTABLE STRICT; 12 | 13 | UPDATE pg_type SET typreceive = 'unit_recv', typsend = 'unit_send' 14 | WHERE typname = 'unit' AND typnamespace = '@extschema@'::regnamespace; 15 | 16 | INSERT INTO pg_depend (classid, objid, objsubid, refclassid, refobjid, refobjsubid, deptype) 17 | VALUES 18 | ('pg_type'::regclass, 'unit'::regtype, 0, 'pg_proc'::regclass, 'unit_recv'::regproc, 0, 'n'), 19 | ('pg_type'::regclass, 'unit'::regtype, 0, 'pg_proc'::regclass, 'unit_send'::regproc, 0, 'n'); 20 | 21 | -- convert @ from (unit, text)->cstring to (unit, text)->text 22 | 23 | DROP OPERATOR @ (unit, text); 24 | DROP FUNCTION unit_at(unit, text); 25 | 26 | CREATE FUNCTION unit_at(unit, text) 27 | RETURNS text 28 | SET search_path = @extschema@ 29 | AS '$libdir/unit', 'unit_at_text2' 30 | LANGUAGE C IMMUTABLE STRICT; 31 | 32 | CREATE OPERATOR @ ( 33 | leftarg = unit, 34 | rightarg = text, 35 | procedure = unit_at 36 | ); 37 | 38 | -- strict comparisons 39 | 40 | CREATE FUNCTION unit_strict_lt(unit, unit) RETURNS bool 41 | AS '$libdir/unit' LANGUAGE C IMMUTABLE STRICT; 42 | CREATE FUNCTION unit_strict_le(unit, unit) RETURNS bool 43 | AS '$libdir/unit' LANGUAGE C IMMUTABLE STRICT; 44 | CREATE FUNCTION unit_strict_eq(unit, unit) RETURNS bool 45 | AS '$libdir/unit' LANGUAGE C IMMUTABLE STRICT; 46 | CREATE FUNCTION unit_strict_ne(unit, unit) RETURNS bool 47 | AS '$libdir/unit' LANGUAGE C IMMUTABLE STRICT; 48 | CREATE FUNCTION unit_strict_ge(unit, unit) RETURNS bool 49 | AS '$libdir/unit' LANGUAGE C IMMUTABLE STRICT; 50 | CREATE FUNCTION unit_strict_gt(unit, unit) RETURNS bool 51 | AS '$libdir/unit' LANGUAGE C IMMUTABLE STRICT; 52 | 53 | CREATE OPERATOR << ( 54 | leftarg = unit, rightarg = unit, procedure = unit_strict_lt, 55 | commutator = >> , negator = >>= , 56 | restrict = scalarltsel, join = scalarltjoinsel 57 | ); 58 | CREATE OPERATOR <<= ( 59 | leftarg = unit, rightarg = unit, procedure = unit_strict_le, 60 | commutator = >>= , negator = >> , 61 | restrict = scalarltsel, join = scalarltjoinsel 62 | ); 63 | CREATE OPERATOR == ( 64 | leftarg = unit, rightarg = unit, procedure = unit_strict_eq, 65 | commutator = == , negator = <<>> , 66 | restrict = eqsel, join = eqjoinsel 67 | ); 68 | CREATE OPERATOR <<>> ( 69 | leftarg = unit, rightarg = unit, procedure = unit_strict_ne, 70 | commutator = <<>> , negator = == , 71 | restrict = neqsel, join = neqjoinsel 72 | ); 73 | CREATE OPERATOR >>= ( 74 | leftarg = unit, rightarg = unit, procedure = unit_strict_ge, 75 | commutator = <<= , negator = << , 76 | restrict = scalargtsel, join = scalargtjoinsel 77 | ); 78 | CREATE OPERATOR >> ( 79 | leftarg = unit, rightarg = unit, procedure = unit_strict_gt, 80 | commutator = << , negator = <<= , 81 | restrict = scalargtsel, join = scalargtjoinsel 82 | ); 83 | 84 | CREATE FUNCTION unit_strict_cmp(unit, unit) 85 | RETURNS int4 86 | AS '$libdir/unit' 87 | LANGUAGE C IMMUTABLE STRICT; 88 | 89 | CREATE OPERATOR CLASS unit_strict_ops 90 | FOR TYPE unit USING btree AS 91 | OPERATOR 1 << , 92 | OPERATOR 2 <<= , 93 | OPERATOR 3 == , 94 | OPERATOR 4 >>= , 95 | OPERATOR 5 >> , 96 | FUNCTION 1 unit_strict_cmp(unit, unit); 97 | 98 | -- range type 99 | 100 | CREATE FUNCTION unit_diff(unit, unit) 101 | RETURNS float8 102 | AS '$libdir/unit' 103 | LANGUAGE C IMMUTABLE STRICT; 104 | 105 | COMMENT ON FUNCTION unit_diff(unit, unit) IS 'returns difference of two units as float8 for use in the unitrange type'; 106 | 107 | CREATE TYPE unitrange AS RANGE ( 108 | SUBTYPE = unit, 109 | SUBTYPE_OPCLASS = unit_strict_ops, 110 | SUBTYPE_DIFF = unit_diff 111 | ); 112 | 113 | -- load prefixes and units tables 114 | SELECT unit_load(); 115 | -------------------------------------------------------------------------------- /definitions.unresolved: -------------------------------------------------------------------------------- 1 | MONEY US$ 2 | sackurtetrodeconstant 5|2 + ln((u k K / 2 pi hbar^2)^(3|2) k K / atm) 3 | normaltemp tempF(70) 4 | normtemp normaltemp 5 | sqrt_cm sqrt_cm 6 | sqrt_centimeter sqrt_cm 7 | sqrt_g sqrt_g 8 | sqrt_gram sqrt_g 9 | S10 SB_degree(10) 10 | buck US$ 11 | fin 5 US$ 12 | sawbuck 10 US$ 13 | usgrand 1000 US$ 14 | greenback US$ 15 | count per pound 16 | diamond_natural_thermal_conductivity 2200 W / m K 17 | diamond_synthetic_thermal_conductivity 3320 W / m K 18 | stainless_304_thermal_conductivity 15.5 W / m K 19 | diamond_synthetic_thermal_diffusivity 1200 mm^2 / s 20 | diamond_natural_thermal_diffusivity 780 mm^2 / s 21 | stainless_304_thermal_diffusivity 4.2 mm^2 / s 22 | ipv4classA ipv4subnetsize(8) 23 | ipv4classB ipv4subnetsize(16) 24 | ipv4classC ipv4subnetsize(24) 25 | semitone octave^(1|12) 26 | wholenote wholenote 27 | MUSICAL_NOTE_LENGTH wholenote 28 | halfnote 1|2 wholenote 29 | quarternote 1|4 wholenote 30 | eighthnote 1|8 wholenote 31 | sixteenthnote 1|16 wholenote 32 | thirtysecondnote 1|32 wholenote 33 | sixtyfourthnote 1|64 wholenote 34 | breve doublewholenote 35 | semibreve wholenote 36 | minimnote halfnote 37 | crotchet quarternote 38 | quaver eighthnote 39 | semiquaver sixteenthnote 40 | demisemiquaver thirtysecondnote 41 | hemidemisemiquaver sixtyfourthnote 42 | semidemisemiquaver hemidemisemiquaver 43 | unitedstatesdollar US$ 44 | usdollar US$ 45 | $ dollar 46 | mark germanymark 47 | peseta spainpeseta 48 | rand southafricarand 49 | escudo portugalescudo 50 | guilder netherlandsguilder 51 | hollandguilder netherlandsguilder 52 | peso mexicopeso 53 | yen japanyen 54 | lira turkeylira 55 | rupee indiarupee 56 | drachma greecedrachma 57 | franc francefranc 58 | markka finlandmarkka 59 | britainpound unitedkingdompound 60 | greatbritainpound unitedkingdompound 61 | unitedkingdompound ukpound 62 | poundsterling britainpound 63 | yuan chinayuan 64 | icelandkróna icelandkrona 65 | polandzłoty polandzloty 66 | tongapa’anga tongapa'anga 67 | vietnamđồng vietnamdong 68 | mongoliatögrög mongoliatugrik 69 | sãotomé&príncipedobra saotome&principedobra 70 | UKP GBP 71 | olddollargold 23.22 grains goldprice 72 | newdollargold 96|7 grains goldprice 73 | dollargold newdollargold 74 | poundgold 113 grains goldprice 75 | goldounce goldprice troyounce 76 | silverounce silverprice troyounce 77 | platinumounce platinumprice troyounce 78 | XAU goldounce 79 | XPT platinumounce 80 | XAG silverounce 81 | USdimeweight US$ 0.10 / (20 US$ / lb) 82 | USquarterweight US$ 0.25 / (20 US$ / lb) 83 | UShalfdollarweight US$ 0.50 / (20 US$ / lb) 84 | quid britainpound 85 | fiver 5 quid 86 | tenner 10 quid 87 | monkey 500 quid 88 | brgrand 1000 quid 89 | bob shilling 90 | shilling 1|20 britainpound 91 | oldpence 1|12 shilling 92 | farthing 1|4 oldpence 93 | guinea 21 shilling 94 | crown 5 shilling 95 | florin 2 shilling 96 | groat 4 oldpence 97 | tanner 6 oldpence 98 | brpenny 0.01 britainpound 99 | pence brpenny 100 | tuppence 2 pence 101 | tuppenny tuppence 102 | ha'penny halfbrpenny 103 | hapenny ha'penny 104 | oldpenny oldpence 105 | oldtuppence 2 oldpence 106 | oldtuppenny oldtuppence 107 | threepence 3 oldpence 108 | threepenny threepence 109 | oldthreepence threepence 110 | oldthreepenny threepence 111 | oldhalfpenny halfoldpenny 112 | oldha'penny oldhalfpenny 113 | oldhapenny oldha'penny 114 | brpony 25 britainpound 115 | loony 1 canadadollar 116 | toony 2 canadadollar 117 | satoshi 1e-8 bitcoin 118 | XBT bitcoin 119 | USCPI_now UScpi_now 120 | USCPI_lastdate UScpi_lastdate 121 | cpi_now UScpi_now 122 | CPI_now UScpi_now 123 | cpi_lastdate UScpi_lastdate 124 | CPI_lastdate UScpi_lastdate 125 | brmgd mega brgallon/day 126 | usmgd mega usgallon/day 127 | percapita per capita 128 | dollar US$ 129 | cent $ 0.01 130 | penny cent 131 | grand usgrand 132 | π pi 133 | ¢ cent 134 | £ britainpound 135 | ¥ japanyen 136 | € euro 137 | ₩ southkoreawon 138 | ₪ israelnewshekel 139 | ₤ lira 140 | ₨ rupee 141 | ฿ thailandbaht 142 | ₡ costaricacolon 143 | ₣ francefranc 144 | ₦ nigerianaira 145 | ₧ spainpeseta 146 | ₫ vietnamdong 147 | ₭ laokip 148 | ₮ mongoliatugrik 149 | ₯ greecedrachma 150 | ₱ philippinepeso 151 | ﷼ iranrial 152 | ﹩ $ 153 | ¢ ¢ 154 | £ £ 155 | ¥ ¥ 156 | ₩ ₩ 157 | -------------------------------------------------------------------------------- /unit.h: -------------------------------------------------------------------------------- 1 | #ifndef _UNIT_H 2 | #define _UNIT_H 1 3 | 4 | #include 5 | #include 6 | 7 | /* indices */ 8 | #define UNIT_m 0 /* meter */ 9 | #define UNIT_kg 1 /* kilogram */ 10 | #define UNIT_s 2 /* second */ 11 | #define UNIT_A 3 /* ampere */ 12 | #define UNIT_K 4 /* kelvin */ 13 | #define UNIT_mol 5 /* mole */ 14 | #define UNIT_cd 6 /* candela */ 15 | #define UNIT_B 7 /* byte */ 16 | 17 | #define N_UNITS 8 18 | 19 | /* functions recognized in parser */ 20 | enum parser_function { 21 | FUNCTION_SQRT, 22 | FUNCTION_EXP, 23 | FUNCTION_LN, 24 | FUNCTION_LOG2, 25 | FUNCTION_ASIN, 26 | FUNCTION_TAN, 27 | }; 28 | 29 | /* defined units */ 30 | 31 | #define UNIT_NAME_LENGTH 32 32 | /* longest unit names (without prefixes) observed in definitions.units: 33 | * specificheat_glass_silica (25 chars) 34 | * hardtranslucentarkansas (23 chars) 35 | * venezuelanbolivarfuerte */ 36 | 37 | #define MIN_PLURAL_LENGTH 3 38 | /* minimum length of input unit to consider stripping a trailing plural 's' 39 | * lbs secs 40 | */ 41 | 42 | #define DBL_DIG 15 43 | #define TIME_MINUTE 60 44 | #define TIME_HOUR (60 * TIME_MINUTE) 45 | #define TIME_DAY (24 * TIME_HOUR) 46 | #define TIME_YEAR (365 * TIME_DAY) 47 | #define TIME_YEAR_NAME "commonyear" 48 | 49 | extern const char *base_units[N_UNITS]; /* names of base units */ 50 | 51 | struct derived_unit_t { 52 | char *name; 53 | signed char units[N_UNITS]; 54 | }; 55 | 56 | extern const struct derived_unit_t si_derived_units[]; 57 | 58 | /* type def */ 59 | 60 | typedef struct Unit { 61 | double value; 62 | signed char units[N_UNITS]; 63 | } Unit; 64 | 65 | typedef struct UnitShift { 66 | Unit unit; 67 | double shift; 68 | } UnitShift; 69 | 70 | /* hash table and regex interface */ 71 | 72 | extern HTAB *unit_names; 73 | 74 | typedef struct unit_names_t { 75 | char name[UNIT_NAME_LENGTH]; 76 | UnitShift unit_shift; 77 | } unit_names_t; 78 | 79 | typedef struct unit_dimensions_t { 80 | char units[N_UNITS]; 81 | char name[UNIT_NAME_LENGTH]; 82 | } unit_dimensions_t; 83 | 84 | /* parser interface */ 85 | 86 | int unit_parse (char *s, UnitShift *unit); /* in unit.y */ 87 | 88 | char *unit_cstring (Unit *unit); 89 | 90 | /* static functions */ 91 | 92 | /* test if two Units have the same dimension */ 93 | static inline void 94 | test_same_dimension (char *op, Unit *a, Unit *b) 95 | { 96 | if (memcmp(a->units, b->units, N_UNITS)) 97 | ereport(ERROR, 98 | (errcode(ERRCODE_DATA_EXCEPTION), 99 | errmsg("dimension mismatch in \"%s\" operation: \"%s\", \"%s\"", 100 | op, unit_cstring(a), unit_cstring(b)))); 101 | } 102 | 103 | static inline void 104 | unit_add_internal (Unit *a, Unit *b, Unit *result) 105 | { 106 | test_same_dimension("+", a, b); 107 | result->value = a->value + b->value; 108 | memcpy(result->units, a->units, N_UNITS); 109 | } 110 | 111 | static inline void 112 | unit_sub_internal (Unit *a, Unit *b, Unit *result) 113 | { 114 | test_same_dimension("-", a, b); 115 | result->value = a->value - b->value; 116 | memcpy(result->units, a->units, N_UNITS); 117 | } 118 | 119 | static inline void 120 | unit_mult_internal (Unit *a, Unit *b, Unit *result) 121 | { 122 | int i; 123 | 124 | result->value = a->value * b->value; 125 | for (i = 0; i < N_UNITS; i++) 126 | result->units[i] = a->units[i] + b->units[i]; 127 | } 128 | 129 | static inline void 130 | unit_div_internal (Unit *a, Unit *b, Unit *result) 131 | { 132 | int i; 133 | 134 | if (b->value == 0) 135 | ereport(ERROR, 136 | (errcode(ERRCODE_DIVISION_BY_ZERO), 137 | errmsg("division by zero-valued unit: \"%s\"", 138 | unit_cstring(b)))); 139 | 140 | result->value = a->value / b->value; 141 | for (i = 0; i < N_UNITS; i++) 142 | result->units[i] = a->units[i] - b->units[i]; 143 | } 144 | 145 | void 146 | unit_sqrt_internal(Unit *a, Unit *result); 147 | 148 | void 149 | unit_exp_internal(Unit *a, Unit *result); 150 | 151 | void 152 | unit_ln_internal(Unit *a, Unit *result); 153 | 154 | void 155 | unit_log2_internal(Unit *a, Unit *result); 156 | 157 | void 158 | unit_asin_internal(Unit *a, Unit *result); 159 | 160 | void 161 | unit_tan_internal(Unit *a, Unit *result); 162 | 163 | #endif /* _UNIT_H */ 164 | -------------------------------------------------------------------------------- /unitparse.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.8.2. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, 6 | Inc. 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . */ 20 | 21 | /* As a special exception, you may create a larger work that contains 22 | part or all of the Bison parser skeleton and distribute that work 23 | under terms of your choice, so long as that work isn't itself a 24 | parser generator using the skeleton or a modified version thereof 25 | as a parser skeleton. Alternatively, if you modify or redistribute 26 | the parser skeleton itself, you may (at your option) remove this 27 | special exception, which will cause the skeleton and the resulting 28 | Bison output files to be licensed under the GNU General Public 29 | License without this special exception. 30 | 31 | This special exception was added by the Free Software Foundation in 32 | version 2.2 of Bison. */ 33 | 34 | /* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, 35 | especially those whose name start with YY_ or yy_. They are 36 | private implementation details that can be changed or removed. */ 37 | 38 | #ifndef YY_YYUNIT_UNITPARSE_TAB_H_INCLUDED 39 | # define YY_YYUNIT_UNITPARSE_TAB_H_INCLUDED 40 | /* Debug traces. */ 41 | #ifndef YYUNITDEBUG 42 | # if defined YYDEBUG 43 | #if YYDEBUG 44 | # define YYUNITDEBUG 1 45 | # else 46 | # define YYUNITDEBUG 0 47 | # endif 48 | # else /* ! defined YYDEBUG */ 49 | # define YYUNITDEBUG 0 50 | # endif /* ! defined YYDEBUG */ 51 | #endif /* ! defined YYUNITDEBUG */ 52 | #if YYUNITDEBUG 53 | extern int yyunitdebug; 54 | #endif 55 | 56 | /* Token kinds. */ 57 | #ifndef YYUNITTOKENTYPE 58 | # define YYUNITTOKENTYPE 59 | enum yyunittokentype 60 | { 61 | YYUNITEMPTY = -2, 62 | YYUNITEOF = 0, /* "end of file" */ 63 | YYUNITerror = 256, /* error */ 64 | YYUNITUNDEF = 257, /* "invalid token" */ 65 | DOUBLE = 258, /* DOUBLE */ 66 | UNIT_SHIFT = 259, /* UNIT_SHIFT */ 67 | EXPONENT = 260, /* EXPONENT */ 68 | SUPER_SIGN = 261, /* SUPER_SIGN */ 69 | SUPER = 262, /* SUPER */ 70 | FUNCTION = 263, /* FUNCTION */ 71 | ERR = 264, /* ERR */ 72 | UMINUS = 265 /* UMINUS */ 73 | }; 74 | typedef enum yyunittokentype yyunittoken_kind_t; 75 | #endif 76 | 77 | /* Value type. */ 78 | #if ! defined YYUNITSTYPE && ! defined YYUNITSTYPE_IS_DECLARED 79 | union YYUNITSTYPE 80 | { 81 | UnitShift UNIT_SHIFT; /* UNIT_SHIFT */ 82 | UnitShift input; /* input */ 83 | UnitShift expr; /* expr */ 84 | UnitShift simple_expr; /* simple_expr */ 85 | double DOUBLE; /* DOUBLE */ 86 | double number; /* number */ 87 | enum parser_function FUNCTION; /* FUNCTION */ 88 | int EXPONENT; /* EXPONENT */ 89 | int SUPER_SIGN; /* SUPER_SIGN */ 90 | int SUPER; /* SUPER */ 91 | int exponent; /* exponent */ 92 | int super; /* super */ 93 | 94 | #line 95 "unitparse.tab.h" 95 | 96 | }; 97 | typedef union YYUNITSTYPE YYUNITSTYPE; 98 | # define YYUNITSTYPE_IS_TRIVIAL 1 99 | # define YYUNITSTYPE_IS_DECLARED 1 100 | #endif 101 | 102 | 103 | extern YYUNITSTYPE yyunitlval; 104 | 105 | 106 | int yyunitparse (void); 107 | 108 | 109 | #endif /* !YY_YYUNIT_UNITPARSE_TAB_H_INCLUDED */ 110 | -------------------------------------------------------------------------------- /unitparse.y: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016-2018 Christoph Berg 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | */ 14 | 15 | %{ 16 | #include /* bzero */ 17 | #include /* pow */ 18 | #include "unit.h" 19 | 20 | /* flex/bison prototypes */ 21 | int yylex (void); 22 | struct yyunit_buffer_state *yyunit_scan_string(char *str); 23 | void yyunit_delete_buffer(struct yyunit_buffer_state *buffer); 24 | void yyerror (char const *s); 25 | 26 | static UnitShift *unit_parse_result; /* parsing result gets stored here */ 27 | %} 28 | 29 | %define parse.error verbose 30 | %define api.prefix {yyunit} 31 | 32 | %define api.value.type union 33 | %token DOUBLE 34 | %token UNIT_SHIFT 35 | %token EXPONENT SUPER_SIGN SUPER 36 | %token FUNCTION 37 | %token ERR 38 | %type input expr simple_expr 39 | %type number 40 | %type exponent super 41 | 42 | %left '+' '-' 43 | %left '/' 44 | %left '*' /* * binds stronger than / */ 45 | %left UMINUS /* unary minus */ 46 | 47 | %% 48 | 49 | input: 50 | expr { 51 | *unit_parse_result = $1; 52 | } 53 | ; 54 | 55 | expr: 56 | simple_expr 57 | /* accept only simple expressions after unary sign to disambiguate 58 | * (-N)N from -(NN): (-273.15)°C is not the same as -(273.15°C) */ 59 | | '+' simple_expr %prec UMINUS { 60 | $$ = $2; 61 | $$.shift = 0.0; 62 | } 63 | | '-' simple_expr %prec UMINUS { 64 | $$ = $2; 65 | $$.unit.value = -$$.unit.value; 66 | $$.shift = 0.0; 67 | } 68 | | expr exponent { 69 | int i; 70 | if ($2 != 1) { 71 | $$.unit.value = pow($1.unit.value, $2); 72 | for (i = 0; i < N_UNITS; i++) 73 | $$.unit.units[i] = $1.unit.units[i] * $2; 74 | } else { 75 | $$ = $1; 76 | } 77 | $$.shift = 0.0; 78 | } 79 | | expr '+' expr { 80 | unit_add_internal(&$1.unit, &$3.unit, &$$.unit); 81 | $$.shift = 0.0; 82 | } 83 | | expr '-' expr { 84 | unit_sub_internal(&$1.unit, &$3.unit, &$$.unit); 85 | $$.shift = 0.0; 86 | } 87 | | expr expr %prec '*' { 88 | unit_mult_internal(&$1.unit, &$2.unit, &$$.unit); 89 | if ($2.shift != 0.0) /* avoid shift to not destroy -0 */ 90 | $$.unit.value += $2.shift; /* shift is evaluated exactly here */ 91 | $$.shift = 0.0; 92 | } 93 | | expr '*' expr { 94 | unit_mult_internal(&$1.unit, &$3.unit, &$$.unit); 95 | $$.shift = 0.0; 96 | } 97 | | expr '/' expr { 98 | unit_div_internal(&$1.unit, &$3.unit, &$$.unit); 99 | $$.shift = 0.0; 100 | } 101 | | '/' expr { 102 | Unit nominator = { 1.0, {0} }; 103 | unit_div_internal(&nominator, &$2.unit, &$$.unit); 104 | $$.shift = 0.0; 105 | } 106 | ; 107 | 108 | simple_expr: 109 | number { 110 | $$.unit.value = $1; 111 | memset(&$$.unit.units, 0, N_UNITS); 112 | $$.shift = 0.0; 113 | } 114 | | UNIT_SHIFT 115 | | FUNCTION '(' expr ')' { 116 | switch ($1) { 117 | case FUNCTION_SQRT: 118 | unit_sqrt_internal(&$3.unit, &$$.unit); 119 | break; 120 | case FUNCTION_EXP: 121 | unit_exp_internal(&$3.unit, &$$.unit); 122 | break; 123 | case FUNCTION_LN: 124 | unit_ln_internal(&$3.unit, &$$.unit); 125 | break; 126 | case FUNCTION_LOG2: 127 | unit_log2_internal(&$3.unit, &$$.unit); 128 | break; 129 | case FUNCTION_ASIN: 130 | unit_asin_internal(&$3.unit, &$$.unit); 131 | break; 132 | case FUNCTION_TAN: 133 | unit_tan_internal(&$3.unit, &$$.unit); 134 | break; 135 | } 136 | } 137 | | '(' expr ')' { 138 | $$ = $2; 139 | $$.shift = 0.0; 140 | } 141 | 142 | number: 143 | DOUBLE 144 | | DOUBLE '|' DOUBLE { 145 | $$ = $1 / $3; 146 | } 147 | 148 | exponent: 149 | EXPONENT 150 | | SUPER_SIGN super { $$ = $1 * $2; } 151 | | super 152 | ; 153 | 154 | super: 155 | SUPER 156 | | SUPER super { $$ = 10 * $1 + $2; } 157 | ; 158 | 159 | %% 160 | 161 | /* parse a given string and return the result via the second argument */ 162 | int 163 | unit_parse (char *s, UnitShift *unit_shift) 164 | { 165 | struct yyunit_buffer_state *buf; 166 | int ret; 167 | 168 | unit_parse_result = unit_shift; 169 | buf = yyunit_scan_string(s); 170 | ret = yyunitparse(); 171 | yyunit_delete_buffer(buf); 172 | return ret; 173 | } 174 | -------------------------------------------------------------------------------- /load-units.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # load definitions.units.patched into the unit_prefixes and unit_units tables 4 | # existing data is wiped! 5 | 6 | use utf8; 7 | use open ":std", ":encoding(UTF-8)"; 8 | use strict; 9 | use warnings; 10 | use DBD::Pg; 11 | 12 | open F, "cat definitions.units.patched elements.units |"; 13 | 14 | my $dbh = DBI->connect("dbi:Pg:", '', '', 15 | {AutoCommit => 1, PrintError => 0, RaiseError => 0} 16 | ) || die "PG connection failed"; 17 | $dbh->do("SET synchronous_commit = off"); 18 | $dbh->do("TRUNCATE unit_prefixes, unit_units"); 19 | $dbh->do("SET client_min_messages = 'error'"); 20 | $dbh->do("ALTER TABLE unit_prefixes ADD COLUMN IF NOT EXISTS ordering serial"); # add temp column to preserve load ordering for dumping 21 | $dbh->do("ALTER TABLE unit_units ADD COLUMN IF NOT EXISTS ordering serial"); 22 | $dbh->do("RESET client_min_messages"); 23 | 24 | my $skip_british = 0; 25 | my @todo; 26 | my $continued = ''; 27 | 28 | while () { 29 | # skip over locale specific parts 30 | $skip_british = 1 if /^!var UNITS_ENGLISH GB/; 31 | $skip_british = 0 if /^!endvar/; 32 | next if ($skip_british); 33 | 34 | if (/\s*(.*)\\$/) { 35 | $continued .= $1; 36 | next; 37 | } elsif ($continued) { 38 | s/^\s*//; 39 | $_ = "$continued$_"; 40 | $continued = ''; 41 | } 42 | 43 | s/#.*//; 44 | s/\s+$//; 45 | next if /^\s*$/; # skip emtpy lines 46 | next if /^!/; # skip pragmas 47 | next if /^\+/; # skip units from non-SI systems 48 | #next if /^[0-9]/; # skip over table contents 49 | #next if /^\s/; # skip over table contents/continued lines 50 | unless (/^(\S+)\s+(.*)/) { 51 | print "unknown line $_\n"; 52 | exit 1; 53 | } 54 | 55 | my ($unit, $def) = ($1, $2); 56 | next if ($unit =~ /[(\[]/); # skip functions and table definitions 57 | 58 | my $is_prefix = ($unit =~ s/-$//); # it's a prefix if it ends with '-' 59 | $def = $unit if ($def eq '!'); # base unit 60 | $def = 1 if ($def eq '!dimensionless'); 61 | 62 | my $u = { unit => $unit, def => $def, is_prefix => $is_prefix }; 63 | $u->{is_base} = ($u->{unit} eq $u->{def}); 64 | 65 | # shifted units 66 | if ($unit =~ /^(℃|°C|degC|degcelsius)$/) { 67 | $u->{shift} = '273.15'; # 0 °C in K 68 | } elsif ($unit =~ /^(℉|°F|degF|degfahrenheit)$/) { 69 | $u->{shift} = '255.3722222222222222'; # 0 °F in K 70 | } elsif ($unit =~ /^(degreaumur)$/) { 71 | $u->{shift} = '273.15'; # 0 °Ré in K 72 | } 73 | 74 | push @todo, $u; 75 | } 76 | 77 | # try repeatedly to insert units, unfortunately the input data contains some 78 | # forward references 79 | 80 | my ($n_todo, $new_n_todo); 81 | do { 82 | $n_todo = @todo; 83 | print "$n_todo units left to try ...\n"; 84 | 85 | my @new_todo; 86 | foreach my $u (@todo) { 87 | my ($unit, $def, $shift, $is_prefix) = ($u->{unit}, $u->{def}, $u->{shift}, $u->{is_prefix}); 88 | if ($is_prefix) { 89 | my $ret = $dbh->do("INSERT INTO unit_prefixes (prefix, factor, definition, dump) VALUES (?, value(?::unit), ?, NULL)", 90 | undef, 91 | $unit, $def, $def); 92 | next if defined $ret; 93 | # see if the prefix is defined in terms of another prefix 94 | # (we can't simply inject all prefixes as units because conflicts exist, e.g. on 'T') 95 | $ret = $dbh->do("INSERT INTO unit_prefixes (prefix, factor, definition, dump) SELECT ?, factor, ?, NULL FROM unit_prefixes WHERE prefix = ?", 96 | undef, 97 | $unit, $def, $def); 98 | next if defined $ret and $ret > 0; 99 | } else { 100 | my ($is_hashed) = $dbh->selectrow_array("SELECT unit_is_hashed(?)", undef, $unit); 101 | if ($is_hashed and not $u->{is_base}) { 102 | # if the unit we are defining now was successfully used before, 103 | # something went wrong. It indicates that the new unit could 104 | # also be parsed as prefix-otherknownunit, e.g. "ft" vs "f-t" 105 | print "Unit $unit has already been used before being defined. Bad.\n"; 106 | exit 1; 107 | } 108 | my $ret = $dbh->do("INSERT INTO unit_units (name, unit, shift, definition, dump) VALUES (?, ?, ?, ?, NULL)", 109 | undef, 110 | $unit, $def, $shift, $def); 111 | next if defined $ret; 112 | $u->{error} = $dbh->errstr; 113 | } 114 | 115 | push @new_todo, $u; 116 | } 117 | 118 | $new_n_todo = @new_todo; 119 | @todo = @new_todo; 120 | } while ($n_todo != $new_n_todo); 121 | 122 | print "$new_n_todo units not inserted:\n"; 123 | open my $unres, '>', "definitions.unresolved"; 124 | foreach my $u (@todo) { 125 | if ($u->{is_prefix}) { 126 | print "Prefix $u->{unit}: $u->{def}\n"; 127 | } else { 128 | print $unres "$u->{unit}\t$u->{def}\n"; 129 | next if ($u->{error} =~ /dollar|euro|pence|quid|shilling/); # skip currencies so we can see the rest better 130 | print "$u->{unit}: $u->{def} ($u->{error})\n"; 131 | } 132 | } 133 | close $unres; 134 | -------------------------------------------------------------------------------- /expected/temperature.out: -------------------------------------------------------------------------------- 1 | /* revert to pre-12 default */ 2 | SET extra_float_digits = 0; 3 | SELECT * FROM unit_units WHERE name IN ( 4 | 'K', 'kelvin', 5 | '℃', '°C', 'degC', 'degcelsius', 6 | '℉', '°F', 'degF', 'degfahrenheit', 7 | '°R', 'degR', 'degrankine', 'degreerankine', 'degreesrankine', 'tempR', 'temprankine', 8 | 'degreaumur') 9 | ORDER BY name COLLATE "C"; 10 | name | unit | shift | definition | dump 11 | ----------------+---------------------+------------------+----------------+------ 12 | K | 1 K | | K | 13 | degC | 1 K | 273.15 | K | 14 | degF | 555.555555555556 mK | 255.372222222222 | 5|9 * degC | 15 | degR | 555.555555555556 mK | | degrankine | 16 | degcelsius | 1 K | 273.15 | K | 17 | degfahrenheit | 555.555555555556 mK | 255.372222222222 | 5|9 * degC | 18 | degrankine | 555.555555555556 mK | | degreesrankine | 19 | degreaumur | 1.25 K | 273.15 | 10|8 * degC | 20 | degreerankine | 555.555555555556 mK | | degF | 21 | degreesrankine | 555.555555555556 mK | | degF | 22 | kelvin | 1 K | | K | 23 | tempR | 555.555555555556 mK | | degrankine | 24 | temprankine | 555.555555555556 mK | | degrankine | 25 | °C | 1 K | 273.15 | degC | 26 | °F | 555.555555555556 mK | 255.372222222222 | degF | 27 | °R | 555.555555555556 mK | | degR | 28 | ℃ | 1 K | 273.15 | degC | 29 | ℉ | 555.555555555556 mK | 255.372222222222 | degF | 30 | (18 rows) 31 | 32 | RESET extra_float_digits; 33 | -- Kelvin 34 | SELECT '0 K'::unit, 'K'::unit, '1 K'::unit, '273.15 K'::unit; 35 | unit | unit | unit | unit 36 | ------+------+------+---------- 37 | 0 K | 1 K | 1 K | 273.15 K 38 | (1 row) 39 | 40 | -- Celsius 41 | SELECT '-273.15 ℃'::unit, '0 ℃'::unit, '1 ℃'::unit, '℃'::unit; 42 | unit | unit | unit | unit 43 | ------+----------+----------+------ 44 | 0 K | 273.15 K | 274.15 K | 1 K 45 | (1 row) 46 | 47 | SELECT '-273.15 °C'::unit, '0 °C'::unit, '1 °C'::unit, '°C'::unit; 48 | unit | unit | unit | unit 49 | ------+----------+----------+------ 50 | 0 K | 273.15 K | 274.15 K | 1 K 51 | (1 row) 52 | 53 | SELECT '-273.15 degC'::unit, '0 degC'::unit, '1 degC'::unit, 'degC'::unit; 54 | unit | unit | unit | unit 55 | ------+----------+----------+------ 56 | 0 K | 273.15 K | 274.15 K | 1 K 57 | (1 row) 58 | 59 | SELECT '-273.15 degcelsius'::unit, '0 degcelsius'::unit, '1 degcelsius'::unit, 'degcelsius'::unit; 60 | unit | unit | unit | unit 61 | ------+----------+----------+------ 62 | 0 K | 273.15 K | 274.15 K | 1 K 63 | (1 row) 64 | 65 | -- Fahrenheit 66 | SELECT '-459.67 ℉'::unit, '0 ℉'::unit, '1 ℉'::unit, '℉'::unit; 67 | unit | unit | unit | unit 68 | ------+--------------------+--------------------+--------------------- 69 | 0 K | 255.372222222222 K | 255.927777777778 K | 555.555555555556 mK 70 | (1 row) 71 | 72 | SELECT '-459.67 °F'::unit, '0 °F'::unit, '1 °F'::unit, '°F'::unit; 73 | unit | unit | unit | unit 74 | ------+--------------------+--------------------+--------------------- 75 | 0 K | 255.372222222222 K | 255.927777777778 K | 555.555555555556 mK 76 | (1 row) 77 | 78 | SELECT '-459.67 degF'::unit, '0 degF'::unit, '1 degF'::unit, 'degF'::unit; 79 | unit | unit | unit | unit 80 | ------+--------------------+--------------------+--------------------- 81 | 0 K | 255.372222222222 K | 255.927777777778 K | 555.555555555556 mK 82 | (1 row) 83 | 84 | SELECT '-459.67 degfahrenheit'::unit, '0 degfahrenheit'::unit, '1 degfahrenheit'::unit, 'degfahrenheit'::unit; 85 | unit | unit | unit | unit 86 | ------+--------------------+--------------------+--------------------- 87 | 0 K | 255.372222222222 K | 255.927777777778 K | 555.555555555556 mK 88 | (1 row) 89 | 90 | -- Rankine 91 | SELECT '491.67 degR'::unit, '0 degR'::unit, '1 degR'::unit, 'degR'::unit; 92 | unit | unit | unit | unit 93 | ----------+------+---------------------+--------------------- 94 | 273.15 K | 0 K | 555.555555555556 mK | 555.555555555556 mK 95 | (1 row) 96 | 97 | -- Réaumur 98 | SELECT '-218.52 degreaumur'::unit, '0 degreaumur'::unit, '1 degreaumur'::unit, 'degreaumur'::unit; 99 | unit | unit | unit | unit 100 | ---------------------+----------+---------+-------- 101 | -56.843418860808 fK | 273.15 K | 274.4 K | 1.25 K 102 | (1 row) 103 | 104 | SELECT '0 K'::unit @ 'K', '0 K'::unit @ '°C', '0 K'::unit @ '°F', '0 K'::unit @ '°R', '0 K'::unit @ 'degreaumur'; 105 | ?column? | ?column? | ?column? | ?column? | ?column? 106 | ----------+------------+------------+----------+-------------------- 107 | 0 K | -273.15 °C | -459.67 °F | 0 °R | -218.52 degreaumur 108 | (1 row) 109 | 110 | SELECT '273.15 K'::unit @ 'K', '273.15 K'::unit @ '°C', '273.15 K'::unit @ '°F', '273.15 K'::unit @ '°R', '273.15 K'::unit @ 'degreaumur'; 111 | ?column? | ?column? | ?column? | ?column? | ?column? 112 | ----------+----------+---------------------+-----------+-------------- 113 | 273.15 K | 0 °C | 31.9999999999999 °F | 491.67 °R | 0 degreaumur 114 | (1 row) 115 | 116 | -------------------------------------------------------------------------------- /expected/unit.out: -------------------------------------------------------------------------------- 1 | -- test input and output 2 | SELECT '1'::unit; -- unit_in 3 | unit 4 | ------ 5 | 1 6 | (1 row) 7 | 8 | SELECT unit(1); -- dbl2unit 9 | unit 10 | ------ 11 | 1 12 | (1 row) 13 | 14 | CREATE TEMP TABLE u (u unit); 15 | INSERT INTO u VALUES ('1'); 16 | SELECT * FROM u; 17 | u 18 | --- 19 | 1 20 | (1 row) 21 | 22 | -- test constructors 23 | SELECT meter(); 24 | meter 25 | ------- 26 | 1 m 27 | (1 row) 28 | 29 | SELECT meter(2.0) AS two_meters; 30 | two_meters 31 | ------------ 32 | 2 m 33 | (1 row) 34 | 35 | SELECT kilogram(); 36 | kilogram 37 | ---------- 38 | 1 kg 39 | (1 row) 40 | 41 | SELECT kilogram(3.0) AS three_kilogram; 42 | three_kilogram 43 | ---------------- 44 | 3 kg 45 | (1 row) 46 | 47 | SELECT second(); 48 | second 49 | -------- 50 | 1 s 51 | (1 row) 52 | 53 | SELECT second(4.0) AS four_seconds; 54 | four_seconds 55 | -------------- 56 | 4 s 57 | (1 row) 58 | 59 | SELECT ampere(); 60 | ampere 61 | -------- 62 | 1 A 63 | (1 row) 64 | 65 | SELECT ampere(5.0) AS five_ampere; 66 | five_ampere 67 | ------------- 68 | 5 A 69 | (1 row) 70 | 71 | SELECT kelvin(); 72 | kelvin 73 | -------- 74 | 1 K 75 | (1 row) 76 | 77 | SELECT kelvin(6.0) AS six_kelvin; 78 | six_kelvin 79 | ------------ 80 | 6 K 81 | (1 row) 82 | 83 | SELECT mole(); 84 | mole 85 | ------- 86 | 1 mol 87 | (1 row) 88 | 89 | SELECT mole(7.0) AS seven_mole; 90 | seven_mole 91 | ------------ 92 | 7 mol 93 | (1 row) 94 | 95 | SELECT candela(); 96 | candela 97 | --------- 98 | 1 cd 99 | (1 row) 100 | 101 | SELECT candela(8.0) AS eight_candela; 102 | eight_candela 103 | --------------- 104 | 8 cd 105 | (1 row) 106 | 107 | SELECT byte(); 108 | byte 109 | ------ 110 | 1 B 111 | (1 row) 112 | 113 | SELECT byte(9.0) AS nine_byte; 114 | nine_byte 115 | ----------- 116 | 9 B 117 | (1 row) 118 | 119 | -- test dimensionless scales 120 | SELECT unit(0) AS zero, unit(1e-6) AS "1e-6", unit(0.001) AS "0.001", unit(1.0) AS "1", unit(1000.0) AS "1000"; 121 | zero | 1e-6 | 0.001 | 1 | 1000 122 | ------+-------+-------+---+------ 123 | 0 | 1e-06 | 0.001 | 1 | 1000 124 | (1 row) 125 | 126 | -- test SI prefixes 127 | SELECT meter(0) AS zero, meter(1e-6) AS "µm", meter(0.001) AS mm, meter(1.0) AS m, meter(1000.0) AS km; 128 | zero | µm | mm | m | km 129 | ------+------+------+-----+------ 130 | 0 m | 1 µm | 1 mm | 1 m | 1 km 131 | (1 row) 132 | 133 | SELECT kilogram(0) AS zero, kilogram(1e-6) AS mg, kilogram(0.001) AS g, kilogram(1.0) AS kg, kilogram(1000.0) AS "Mg"; 134 | zero | mg | g | kg | Mg 135 | ------+------+-----+------+------ 136 | 0 kg | 1 mg | 1 g | 1 kg | 1 Mg 137 | (1 row) 138 | 139 | -- test combined units (exactly one unit with exponent 1 in numerator) 140 | SELECT meter(0)/second() AS zero, meter(1e-6)/second() AS "µm", meter(0.001)/second() AS mm, meter(1.0)/second() AS m, meter(1000.0)/second() AS km; 141 | zero | µm | mm | m | km 142 | -------+--------+--------+-------+-------- 143 | 0 m/s | 1 µm/s | 1 mm/s | 1 m/s | 1 km/s 144 | (1 row) 145 | 146 | SELECT kilogram(0)/second() AS zero, kilogram(1e-6)/second() AS mg, kilogram(0.001)/second() AS g, kilogram(1.0)/second() AS kg, kilogram(1000.0)/second() AS "Mg"; 147 | zero | mg | g | kg | Mg 148 | --------+--------+-------+--------+-------- 149 | 0 kg/s | 1 mg/s | 1 g/s | 1 kg/s | 1 Mg/s 150 | (1 row) 151 | 152 | -- test parser 153 | SELECT '1 m'::unit; 154 | unit 155 | ------ 156 | 1 m 157 | (1 row) 158 | 159 | SELECT '-1 m/s'::unit; 160 | unit 161 | -------- 162 | -1 m/s 163 | (1 row) 164 | 165 | SELECT '10 dm^3'::unit, '10l'::unit; 166 | unit | unit 167 | ----------+---------- 168 | 0.01 m^3 | 0.01 m^3 169 | (1 row) 170 | 171 | SELECT '9.81 kg*m/s^2'::unit, '9.81 kg*m/s*s'::unit, '9.81 kg*m/s/s'::unit; 172 | unit | unit | unit 173 | --------+--------+-------- 174 | 9.81 N | 9.81 N | 9.81 N 175 | (1 row) 176 | 177 | SELECT '1 foobar'::unit AS error; 178 | ERROR: unit "foobar" is not known 179 | LINE 1: SELECT '1 foobar'::unit AS error; 180 | ^ 181 | -- special values 182 | SELECT '-0'::unit, -'0'::unit, '-0 m'::unit, -'0 m'::unit; 183 | unit | ?column? | unit | ?column? 184 | ------+----------+------+---------- 185 | -0 | -0 | -0 m | -0 m 186 | (1 row) 187 | 188 | SELECT 'infinity'::unit, 'Infinity m'::unit, 'inf A'::unit; 189 | unit | unit | unit 190 | ----------+------------+------------ 191 | Infinity | Infinity m | Infinity A 192 | (1 row) 193 | 194 | SELECT '-infinity'::unit, '-Infinity m'::unit, 'Inf A'::unit; 195 | unit | unit | unit 196 | -----------+-------------+------------ 197 | -Infinity | -Infinity m | Infinity A 198 | (1 row) 199 | 200 | SELECT 'nan'::unit, 'NaN'::unit; 201 | unit | unit 202 | ------+------ 203 | NaN | NaN 204 | (1 row) 205 | 206 | -- test parser arithmetic 207 | SELECT '1|10'::unit, '1|10 m'::unit; 208 | unit | unit 209 | ------+-------- 210 | 0.1 | 100 mm 211 | (1 row) 212 | 213 | SELECT '2 m + 3 m - 4 m'::unit, '6 m - 3 m - 2 m'::unit, '6 m - 3 m + 1 m'::unit; 214 | unit | unit | unit 215 | ------+------+------ 216 | 1 m | 1 m | 4 m 217 | (1 row) 218 | 219 | SELECT '2 m * 3 m'::unit, '2 m / 3 m'::unit, '10 m / 5 m / 2 m'::unit; 220 | unit | unit | unit 221 | -------+-------------------+-------- 222 | 6 m^2 | 0.666666666666667 | 1 m^-1 223 | (1 row) 224 | 225 | SELECT '2 m^2 + 3 m * 4 m'::unit, '2 m * 3 m / 4 m * 5 m'::unit; 226 | unit | unit 227 | --------+------ 228 | 14 m^2 | 0.3 229 | (1 row) 230 | 231 | SELECT '2 m * (1 m + 3 m)'::unit; 232 | unit 233 | ------- 234 | 8 m^2 235 | (1 row) 236 | 237 | SELECT '- m'::unit, '/ m'::unit; 238 | unit | unit 239 | ------+-------- 240 | -1 m | 1 m^-1 241 | (1 row) 242 | 243 | SELECT '4+5'::unit, '4-5'::unit, '4(-5)'::unit, '4*5'::unit, '4/5'::unit; 244 | unit | unit | unit | unit | unit 245 | ------+------+------+------+------ 246 | 9 | -1 | -20 | 20 | 0.8 247 | (1 row) 248 | 249 | -- problematic cases 250 | SELECT '1 cd'::unit AS candela; -- candela vs centiday 251 | candela 252 | --------- 253 | 1 cd 254 | (1 row) 255 | 256 | SELECT '1 Pa'::unit AS pascal; -- pascal vs petayear 257 | pascal 258 | -------- 259 | 1 Pa 260 | (1 row) 261 | 262 | SELECT '1 yg'::unit AS yoctogram, '1.00001 yg'::unit AS actual_yoctogram; -- "yg" had rounding problems in the past 263 | yoctogram | actual_yoctogram 264 | -----------+------------------ 265 | 1 yg | 1.00001 yg 266 | (1 row) 267 | 268 | SELECT '1 min'::unit AS minute; -- minute vs milliinch 269 | minute 270 | ------------ 271 | 00:01:00 s 272 | (1 row) 273 | 274 | SELECT '1 ft'::unit AS foot; -- foot vs femtotonne 275 | foot 276 | ---------- 277 | 304.8 mm 278 | (1 row) 279 | 280 | SELECT '1 yd'::unit AS yard; -- yard vs yoctoday 281 | yard 282 | ---------- 283 | 914.4 mm 284 | (1 row) 285 | 286 | -- units whose definition changed between v2 and 3 287 | SELECT 'a'::unit AS are; 288 | are 289 | --------- 290 | 100 m^2 291 | (1 row) 292 | 293 | SELECT 'da'::unit AS day; 294 | day 295 | ----- 296 | 1 d 297 | (1 row) 298 | 299 | SELECT 'rad'::unit AS radiation; 300 | radiation 301 | ----------- 302 | 10 mGy 303 | (1 row) 304 | 305 | -- check which prefix/unit combinations produce other dimensions (parser conflicts) 306 | SELECT 307 | p, u, (p||u)::unit 308 | FROM 309 | prefixes CROSS JOIN units 310 | WHERE 311 | u <> 'kg' AND 312 | dimension(u::unit) != dimension((p||u)::unit) AND 313 | p||u NOT IN ('dat') -- skip ambiguous unit 314 | ORDER BY lower(p) COLLATE "C", lower(u) COLLATE "C"; 315 | p | u | unit 316 | ---+-----+------------------------------- 317 | a | t | 98.0665 kPa 318 | c | d | 1 cd 319 | d | a | 1 d 320 | f | t | 304.8 mm 321 | G | s | 100 µT 322 | h | bar | 1.05457181764616e-34 m^2*kg/s 323 | h | d | 0.238480942392 m^3 324 | k | in | 600 g 325 | m | in | 00:01:00 s 326 | n | t | 1 N 327 | P | a | 1 Pa 328 | p | a | 1 Pa 329 | p | in | 0.020457405 m^3 330 | p | t | 0.000473176473 m^3 331 | q | t | 0.000946352946 m^3 332 | r | d | 5.0292 m 333 | y | d | 914.4 mm 334 | (17 rows) 335 | 336 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | postgresql-unit (7.10-2) unstable; urgency=medium 2 | 3 | * Upload for PostgreSQL 18. (Closes: #1117334) 4 | * Add ${postgresql:Breaks}. 5 | * debian/tests: Depend on postgresql-common-dev instead of 'make'. 6 | 7 | -- Christoph Berg Wed, 08 Oct 2025 23:47:48 +0200 8 | 9 | postgresql-unit (7.10-1) unstable; urgency=medium 10 | 11 | * Import definitions.units November 2024 Version 3.22. 12 | * Also import elements.units, adding some 3800 isotope weights. 13 | 14 | -- Christoph Berg Mon, 09 Dec 2024 12:16:11 +0100 15 | 16 | postgresql-unit (7.9-1) unstable; urgency=medium 17 | 18 | * New upstream version. 19 | * Upload for PostgreSQL 17. 20 | * Restrict to 64-bit architectures. 21 | 22 | -- Christoph Berg Sun, 15 Sep 2024 13:58:15 +0200 23 | 24 | postgresql-unit (7.8-1) unstable; urgency=medium 25 | 26 | * New upstream version. 27 | * Upload for PostgreSQL 16. 28 | * Use ${postgresql:Depends}. 29 | 30 | -- Christoph Berg Mon, 18 Sep 2023 22:22:20 +0200 31 | 32 | postgresql-unit (7.7-1) unstable; urgency=medium 33 | 34 | * Fix printing of values around 1e33 on i386. 35 | 36 | -- Christoph Berg Fri, 06 Jan 2023 17:34:42 +0200 37 | 38 | postgresql-unit (7.6-1) unstable; urgency=medium 39 | 40 | [ Debian Janitor ] 41 | * Remove constraints unnecessary since buster (oldstable): 42 | + Build-Depends: Drop versioned constraint on bison. 43 | * Use secure copyright file specification URI. 44 | * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository-Browse. 45 | 46 | [ Christoph Berg ] 47 | * Import definitions.units September 2022 Version 3.15. 48 | * Add R ronna, Q quetta, r ronto, Q quecto, Ri robi, Qi quebi prefixes. 49 | 50 | -- Christoph Berg Wed, 04 Jan 2023 20:42:16 +0200 51 | 52 | postgresql-unit (7.5-2) unstable; urgency=medium 53 | 54 | * Upload for PostgreSQL 15. 55 | 56 | -- Christoph Berg Mon, 24 Oct 2022 16:48:19 +0200 57 | 58 | postgresql-unit (7.5-1) unstable; urgency=medium 59 | 60 | * Use "create" without "or replace" in extension sql scripts. 61 | Fixes security issue spotted by Sven Klemm, thanks! 62 | * Update definitions.units to September 2020 Version 3.09 from GNU units. 63 | * Breaking change: 'h' is the Planck constant now; use 'hr' for hours. 64 | * Implement functions in input language: sqrt exp ln log2 asin tan. 65 | 66 | -- Christoph Berg Wed, 18 May 2022 16:11:56 +0200 67 | 68 | postgresql-unit (7.4-2) unstable; urgency=medium 69 | 70 | * B-D on postgresql-server-dev-all only instead of postgresql-all since we 71 | are not testing at build-time. 72 | * Upload for PostgreSQL 14. 73 | 74 | -- Christoph Berg Thu, 21 Oct 2021 11:29:43 +0200 75 | 76 | postgresql-unit (7.4-1) experimental; urgency=medium 77 | 78 | * Fix hash table creation on PostgreSQL 14. 79 | 80 | -- Christoph Berg Thu, 20 May 2021 17:27:35 +0200 81 | 82 | postgresql-unit (7.3-1) unstable; urgency=medium 83 | 84 | * Upload for PostgreSQL 13. 85 | * Use dh --with pgxs. 86 | * R³: no. 87 | * DH 13. 88 | * debian/tests: Use 'make' instead of postgresql-server-dev-all. 89 | 90 | -- Christoph Berg Mon, 19 Oct 2020 21:48:27 +0200 91 | 92 | postgresql-unit (7.2-2) unstable; urgency=medium 93 | 94 | * Upload for PostgreSQL 12. 95 | 96 | -- Christoph Berg Thu, 31 Oct 2019 14:33:26 +0100 97 | 98 | postgresql-unit (7.2-1) unstable; urgency=medium 99 | 100 | * Rewire PostgreSQL 12's new extra_float_digits=1 default internally to 0 to 101 | keep nice, short decimal representations. 102 | 103 | -- Christoph Berg Mon, 20 May 2019 13:38:02 +0200 104 | 105 | postgresql-unit (7.1-1) unstable; urgency=medium 106 | 107 | * Import definitions.unit 2.44 from units 2.18 with 36 new units. 108 | * Adjust powers.h to round "1" down by one ULP. 109 | 110 | -- Christoph Berg Mon, 03 Dec 2018 09:51:31 +0100 111 | 112 | postgresql-unit (7.0-2) unstable; urgency=medium 113 | 114 | * Upload for PostgreSQL 11. 115 | * Use source format 3.0 again. 116 | 117 | -- Christoph Berg Thu, 11 Oct 2018 22:58:03 +0200 118 | 119 | postgresql-unit (7.0-1) unstable; urgency=medium 120 | 121 | * Format time using commonyears + days + hh:mm:ss.sss s. 122 | * Avoid using Gs for Gigaseconds, that's actually gauss. 123 | * Modify @(unit, text) operator to return text instead of cstring. 124 | * Import definitions.units from GNU units 2.17 with 13 new units. 125 | * Add π to known units. 126 | * Remove bison 2 compat mode, but keep pregenerated files for jessie and 127 | trusty. 128 | * Support send/receive via the binary protocol. 129 | * Support Infinity and NaN. 130 | * Add strict comparison operators that error out when dimensions of 131 | arguments do not match: << <<= == <<>> >>= >>. 132 | * Add range type over units: unitrange. 133 | 134 | -- Christoph Berg Fri, 28 Sep 2018 15:12:50 +0200 135 | 136 | postgresql-unit (6.0-1) unstable; urgency=medium 137 | 138 | * Add @@ operator: similar to @, but returns the value of the resulting unit 139 | as double precision number. 140 | * Fix parsing of addition/subtraction in unit values. 141 | * Grant SELECT on unit prefixes and units table to public. 142 | * Add unit_load() function to load/update the data tables. 143 | 144 | -- Christoph Berg Wed, 07 Mar 2018 09:30:42 +0100 145 | 146 | postgresql-unit (5.0-1) unstable; urgency=medium 147 | 148 | * Add rounding function. 149 | * Import definitions.units from GNU units 2.16 with 70 new units. 150 | (The file header erroneously says 2.19.) 151 | * debian/watch: Ignore debian/ tags. 152 | 153 | -- Christoph Berg Wed, 10 Jan 2018 12:16:58 +0100 154 | 155 | postgresql-unit (4.0-2) unstable; urgency=medium 156 | 157 | * Upload with PostgreSQL 10 support. 158 | * Mark package as non-native and add watch file. 159 | 160 | -- Christoph Berg Wed, 27 Sep 2017 20:42:41 +0200 161 | 162 | postgresql-unit (4.0) unstable; urgency=medium 163 | 164 | * Support exponents written using Unicode superscript characters. 165 | * Report 22P02/invalid_text_representation on invalid unit input. 166 | * Fix crash when unit_reset() runs into an OOM error. Patch by Andreas 167 | Seltenreich, thanks! 168 | * Change @ operator signature to (unit, text). 169 | 170 | -- Christoph Berg Fri, 07 Jul 2017 21:42:13 +0200 171 | 172 | postgresql-unit (3.1) experimental; urgency=medium 173 | 174 | * Reupload to Debian, the 3.0 tarball uploaded contained generated files 175 | that broke out-of-tree builds. No source changes. 176 | 177 | -- Christoph Berg Thu, 23 Mar 2017 11:25:43 +0100 178 | 179 | postgresql-unit (3.0) experimental; urgency=medium 180 | 181 | * Support defining new units at runtime. 182 | * Import unit definitions from GNU units. 183 | * Add sqrt() and cbrt() functions. 184 | * Extension is not relocatable after installation anymore. 185 | * Bump extension version to 3. 186 | * Bump minimum supported PostgreSQL version to 9.5 so we can use 187 | hash_create(HASH_BLOBS). 188 | * Bump license to GPL-3+ to match GNU units' license. 189 | 190 | -- Christoph Berg Wed, 22 Mar 2017 18:33:22 +0100 191 | 192 | postgresql-unit (2.0) unstable; urgency=medium 193 | 194 | * Support IEC binary prefixes for byte. 195 | * Support United States customary units: in, ft, yd, mi, oz, lb. 196 | * Add variance and stddev aggregates; bump extension version to 2. 197 | 198 | -- Christoph Berg Mon, 09 Jan 2017 22:24:23 +0100 199 | 200 | postgresql-unit (1.1) unstable; urgency=medium 201 | 202 | * Use float8out_internal to format floats on output. 203 | 204 | -- Christoph Berg Mon, 31 Oct 2016 11:55:43 +0100 205 | 206 | postgresql-unit (1.0) unstable; urgency=medium 207 | 208 | * Initial release. 209 | 210 | -- Christoph Berg Thu, 22 Sep 2016 15:42:55 +0200 211 | -------------------------------------------------------------------------------- /expected/convert.out: -------------------------------------------------------------------------------- 1 | -- length 2 | WITH 3 | l(u) AS (VALUES 4 | ('mm'), 5 | ('m'), 6 | ('km'), 7 | ('in'), 8 | ('ft'), 9 | ('yd'), 10 | ('mi')) 11 | SELECT 12 | l1.u, l2.u, l1.u::unit @ l2.u 13 | FROM 14 | l l1 CROSS JOIN l l2 15 | \crosstabview 16 | u | mm | m | km | in | ft | yd | mi 17 | ----+------------+------------+--------------+-----------------------+------------------------+------------------------+------------------------- 18 | mm | 1 mm | 0.001 m | 1e-06 km | 0.0393700787401575 in | 0.00328083989501312 ft | 0.00109361329833771 yd | 6.21371192237334e-07 mi 19 | m | 1000 mm | 1 m | 0.001 km | 39.3700787401575 in | 3.28083989501312 ft | 1.09361329833771 yd | 0.000621371192237334 mi 20 | km | 1000000 mm | 1000 m | 1 km | 39370.0787401575 in | 3280.83989501312 ft | 1093.61329833771 yd | 0.621371192237334 mi 21 | in | 25.4 mm | 0.0254 m | 2.54e-05 km | 1 in | 0.0833333333333333 ft | 0.0277777777777778 yd | 1.57828282828283e-05 mi 22 | ft | 304.8 mm | 0.3048 m | 0.0003048 km | 12 in | 1 ft | 0.333333333333333 yd | 0.000189393939393939 mi 23 | yd | 914.4 mm | 0.9144 m | 0.0009144 km | 36 in | 3 ft | 1 yd | 0.000568181818181818 mi 24 | mi | 1609344 mm | 1609.344 m | 1.609344 km | 63360 in | 5280 ft | 1760 yd | 1 mi 25 | (7 rows) 26 | 27 | /* revert to pre-12 default */ 28 | SET extra_float_digits = 0; 29 | WITH 30 | l(u) AS (VALUES 31 | ('mm'), 32 | ('m'), 33 | ('km'), 34 | ('in'), 35 | ('ft'), 36 | ('yd'), 37 | ('mi')) 38 | SELECT 39 | l1.u, l2.u, l1.u::unit @@ l2.u 40 | FROM 41 | l l1 CROSS JOIN l l2 42 | \crosstabview 43 | u | mm | m | km | in | ft | yd | mi 44 | ----+---------+----------+-----------+--------------------+---------------------+---------------------+---------------------- 45 | mm | 1 | 0.001 | 1e-06 | 0.0393700787401575 | 0.00328083989501312 | 0.00109361329833771 | 6.21371192237334e-07 46 | m | 1000 | 1 | 0.001 | 39.3700787401575 | 3.28083989501312 | 1.09361329833771 | 0.000621371192237334 47 | km | 1000000 | 1000 | 1 | 39370.0787401575 | 3280.83989501312 | 1093.61329833771 | 0.621371192237334 48 | in | 25.4 | 0.0254 | 2.54e-05 | 1 | 0.0833333333333333 | 0.0277777777777778 | 1.57828282828283e-05 49 | ft | 304.8 | 0.3048 | 0.0003048 | 12 | 1 | 0.333333333333333 | 0.000189393939393939 50 | yd | 914.4 | 0.9144 | 0.0009144 | 36 | 3 | 1 | 0.000568181818181818 51 | mi | 1609344 | 1609.344 | 1.609344 | 63360 | 5280 | 1760 | 1 52 | (7 rows) 53 | 54 | RESET extra_float_digits; 55 | -- area 56 | WITH 57 | l(u) AS (VALUES 58 | ('mm^2'), 59 | ('m^2'), 60 | ('km^2'), 61 | ('in^2'), 62 | ('ft^2'), 63 | ('yd^2'), 64 | ('mi^2')) 65 | SELECT 66 | l1.u, l2.u, l1.u::unit @ l2.u 67 | FROM 68 | l l1 CROSS JOIN l l2 69 | \crosstabview 70 | u | mm^2 | m^2 | km^2 | in^2 | ft^2 | yd^2 | mi^2 71 | ------+--------------------+--------------------+---------------------+-------------------------+---------------------------+---------------------------+--------------------------- 72 | mm^2 | 1 mm^2 | 1e-06 m^2 | 1e-12 km^2 | 0.0015500031000062 in^2 | 1.07639104167097e-05 ft^2 | 1.19599004630108e-06 yd^2 | 3.86102158542446e-13 mi^2 73 | m^2 | 1000000 mm^2 | 1 m^2 | 1e-06 km^2 | 1550.0031000062 in^2 | 10.7639104167097 ft^2 | 1.19599004630108 yd^2 | 3.86102158542446e-07 mi^2 74 | km^2 | 1000000000000 mm^2 | 1000000 m^2 | 1 km^2 | 1550003100.0062 in^2 | 10763910.4167097 ft^2 | 1195990.04630108 yd^2 | 0.386102158542446 mi^2 75 | in^2 | 645.16 mm^2 | 0.00064516 m^2 | 6.4516e-10 km^2 | 1 in^2 | 0.00694444444444444 ft^2 | 0.000771604938271605 yd^2 | 2.49097668605244e-10 mi^2 76 | ft^2 | 92903.04 mm^2 | 0.09290304 m^2 | 9.290304e-08 km^2 | 144 in^2 | 1 ft^2 | 0.111111111111111 yd^2 | 3.58700642791552e-08 mi^2 77 | yd^2 | 836127.36 mm^2 | 0.83612736 m^2 | 8.3612736e-07 km^2 | 1296 in^2 | 9 ft^2 | 1 yd^2 | 3.22830578512397e-07 mi^2 78 | mi^2 | 2589988110336 mm^2 | 2589988.110336 m^2 | 2.589988110336 km^2 | 4014489600 in^2 | 27878400 ft^2 | 3097600 yd^2 | 1 mi^2 79 | (7 rows) 80 | 81 | -- volume 82 | WITH 83 | l(u) AS (VALUES 84 | ('mm^3'), 85 | ('l'), 86 | ('m^3'), 87 | ('in^3'), 88 | ('ft^3'), 89 | ('yd^3')) 90 | SELECT 91 | l1.u, l2.u, l1.u::unit @ l2.u 92 | FROM 93 | l l1 CROSS JOIN l l2 94 | \crosstabview 95 | u | mm^3 | l | m^3 | in^3 | ft^3 | yd^3 96 | ------+--------------------+-----------------+--------------------+---------------------------+---------------------------+--------------------------- 97 | mm^3 | 1 mm^3 | 1e-06 l | 1e-09 m^3 | 6.10237440947323e-05 in^3 | 3.53146667214886e-08 ft^3 | 1.30795061931439e-09 yd^3 98 | l | 1000000 mm^3 | 1 l | 0.001 m^3 | 61.0237440947323 in^3 | 0.0353146667214886 ft^3 | 0.00130795061931439 yd^3 99 | m^3 | 1000000000 mm^3 | 1000 l | 1 m^3 | 61023.7440947323 in^3 | 35.3146667214886 ft^3 | 1.30795061931439 yd^3 100 | in^3 | 16387.064 mm^3 | 0.016387064 l | 1.6387064e-05 m^3 | 1 in^3 | 0.000578703703703704 ft^3 | 2.14334705075446e-05 yd^3 101 | ft^3 | 28316846.592 mm^3 | 28.316846592 l | 0.028316846592 m^3 | 1728 in^3 | 1 ft^3 | 0.037037037037037 yd^3 102 | yd^3 | 764554857.984 mm^3 | 764.554857984 l | 0.764554857984 m^3 | 46656 in^3 | 27 ft^3 | 1 yd^3 103 | (6 rows) 104 | 105 | -- weight 106 | WITH 107 | l(u) AS (VALUES 108 | ('g'), 109 | ('kg'), 110 | ('t'), 111 | ('oz'), 112 | ('lb')) 113 | SELECT 114 | l1.u, l2.u, l1.u::unit @ l2.u 115 | FROM 116 | l l1 CROSS JOIN l l2 117 | \crosstabview 118 | u | g | kg | t | oz | lb 119 | ----+----------------+-------------------+--------------------+-----------------------+------------------------ 120 | g | 1 g | 0.001 kg | 1e-06 t | 0.0352739619495804 oz | 0.00220462262184878 lb 121 | kg | 1000 g | 1 kg | 0.001 t | 35.2739619495804 oz | 2.20462262184878 lb 122 | t | 1000000 g | 1000 kg | 1 t | 35273.9619495804 oz | 2204.62262184878 lb 123 | oz | 28.349523125 g | 0.028349523125 kg | 2.8349523125e-05 t | 1 oz | 0.0625 lb 124 | lb | 453.59237 g | 0.45359237 kg | 0.00045359237 t | 16 oz | 1 lb 125 | (5 rows) 126 | 127 | -- time 128 | WITH 129 | l(u) AS (VALUES 130 | ('s'), 131 | ('min'), 132 | ('hr'), 133 | ('d'), 134 | ('julianyear')) 135 | SELECT 136 | l1.u, l2.u, l1.u::unit @ l2.u 137 | FROM 138 | l l1 CROSS JOIN l l2 139 | \crosstabview 140 | u | s | min | hr | d | julianyear 141 | ------------+------------+------------------------+-------------------------+------------------------+--------------------------------- 142 | s | 1 s | 0.0166666666666667 min | 0.000277777777777778 hr | 1.15740740740741e-05 d | 3.16880878140289e-08 julianyear 143 | min | 60 s | 1 min | 0.0166666666666667 hr | 0.000694444444444444 d | 1.90128526884174e-06 julianyear 144 | hr | 3600 s | 60 min | 1 hr | 0.0416666666666667 d | 0.000114077116130504 julianyear 145 | d | 86400 s | 1440 min | 24 hr | 1 d | 0.0027378507871321 julianyear 146 | julianyear | 31557600 s | 525960 min | 8766 hr | 365.25 d | 1 julianyear 147 | (5 rows) 148 | 149 | -------------------------------------------------------------------------------- /expected/aggregate.out: -------------------------------------------------------------------------------- 1 | -- test aggregates 2 | /* revert to pre-12 default for stddev(value()) tests */ 3 | SET extra_float_digits = 0; 4 | CREATE TEMP TABLE u ( 5 | u unit 6 | ); 7 | -- empty 8 | SELECT sum(u) AS null_sum FROM u; 9 | null_sum 10 | ---------- 11 | 12 | (1 row) 13 | 14 | SELECT min(u) AS null_min FROM u; 15 | null_min 16 | ---------- 17 | 18 | (1 row) 19 | 20 | SELECT max(u) AS null_max FROM u; 21 | null_max 22 | ---------- 23 | 24 | (1 row) 25 | 26 | SELECT avg(u) AS null_avg FROM u; 27 | null_avg 28 | ---------- 29 | 30 | (1 row) 31 | 32 | SELECT var_pop(u) AS null_var_pop FROM u; 33 | null_var_pop 34 | -------------- 35 | 36 | (1 row) 37 | 38 | SELECT var_samp(u) AS null_var_samp FROM u; 39 | null_var_samp 40 | --------------- 41 | 42 | (1 row) 43 | 44 | SELECT variance(u) AS null_variance FROM u; 45 | null_variance 46 | --------------- 47 | 48 | (1 row) 49 | 50 | SELECT stddev_pop(u) AS null_stddev_pop FROM u; 51 | null_stddev_pop 52 | ----------------- 53 | 54 | (1 row) 55 | 56 | SELECT stddev_samp(u) AS null_stddev_samp FROM u; 57 | null_stddev_samp 58 | ------------------ 59 | 60 | (1 row) 61 | 62 | SELECT stddev(u) AS null_stddev FROM u; 63 | null_stddev 64 | ------------- 65 | 66 | (1 row) 67 | 68 | -- one NULL row 69 | INSERT INTO u VALUES (NULL); 70 | SELECT sum(u) AS null_sum FROM u; 71 | null_sum 72 | ---------- 73 | 74 | (1 row) 75 | 76 | SELECT min(u) AS null_min FROM u; 77 | null_min 78 | ---------- 79 | 80 | (1 row) 81 | 82 | SELECT max(u) AS null_max FROM u; 83 | null_max 84 | ---------- 85 | 86 | (1 row) 87 | 88 | SELECT avg(u) AS null_avg FROM u; 89 | null_avg 90 | ---------- 91 | 92 | (1 row) 93 | 94 | SELECT var_pop(u) AS null_var_pop FROM u; 95 | null_var_pop 96 | -------------- 97 | 98 | (1 row) 99 | 100 | SELECT var_samp(u) AS null_var_samp FROM u; 101 | null_var_samp 102 | --------------- 103 | 104 | (1 row) 105 | 106 | SELECT variance(u) AS null_variance FROM u; 107 | null_variance 108 | --------------- 109 | 110 | (1 row) 111 | 112 | SELECT stddev_pop(u) AS null_stddev_pop FROM u; 113 | null_stddev_pop 114 | ----------------- 115 | 116 | (1 row) 117 | 118 | SELECT stddev_samp(u) AS null_stddev_samp FROM u; 119 | null_stddev_samp 120 | ------------------ 121 | 122 | (1 row) 123 | 124 | SELECT stddev(u) AS null_stddev FROM u; 125 | null_stddev 126 | ------------- 127 | 128 | (1 row) 129 | 130 | -- one non-NULL row 131 | DELETE FROM u; 132 | INSERT INTO u VALUES (meter(5)); 133 | SELECT sum(u) AS five_meters FROM u; 134 | five_meters 135 | ------------- 136 | 5 m 137 | (1 row) 138 | 139 | SELECT min(u) AS five_meters FROM u; 140 | five_meters 141 | ------------- 142 | 5 m 143 | (1 row) 144 | 145 | SELECT max(u) AS five_meters FROM u; 146 | five_meters 147 | ------------- 148 | 5 m 149 | (1 row) 150 | 151 | SELECT avg(u) AS five_meters FROM u; 152 | five_meters 153 | ------------- 154 | 5 m 155 | (1 row) 156 | 157 | SELECT var_pop(u) AS zero_var_pop FROM u; 158 | zero_var_pop 159 | -------------- 160 | 0 161 | (1 row) 162 | 163 | SELECT var_samp(u) AS zero_var_samp FROM u; 164 | zero_var_samp 165 | --------------- 166 | 0 167 | (1 row) 168 | 169 | SELECT variance(u) AS zero_variance FROM u; 170 | zero_variance 171 | --------------- 172 | 0 173 | (1 row) 174 | 175 | SELECT stddev_pop(u) AS zero_stddev_pop FROM u; 176 | zero_stddev_pop 177 | ----------------- 178 | 0 m 179 | (1 row) 180 | 181 | SELECT stddev_samp(u) AS zero_stddev_samp FROM u; 182 | zero_stddev_samp 183 | ------------------ 184 | 0 m 185 | (1 row) 186 | 187 | SELECT stddev(u) AS zero_stddev FROM u; 188 | zero_stddev 189 | ------------- 190 | 0 m 191 | (1 row) 192 | 193 | -- two rows, matching dimensions 194 | DELETE FROM u; 195 | INSERT INTO u VALUES (meter()), (meter(2)); 196 | SELECT sum(u) AS three_meters FROM u; 197 | three_meters 198 | -------------- 199 | 3 m 200 | (1 row) 201 | 202 | SELECT min(u) AS one_meter FROM u; 203 | one_meter 204 | ----------- 205 | 1 m 206 | (1 row) 207 | 208 | SELECT max(u) AS two_meters FROM u; 209 | two_meters 210 | ------------ 211 | 2 m 212 | (1 row) 213 | 214 | SELECT avg(u) AS oneandhalf_meters FROM u; 215 | oneandhalf_meters 216 | ------------------- 217 | 1.5 m 218 | (1 row) 219 | 220 | SELECT var_pop(u) FROM u; 221 | var_pop 222 | --------- 223 | 0.25 224 | (1 row) 225 | 226 | SELECT var_samp(u) FROM u; 227 | var_samp 228 | ---------- 229 | 0.5 230 | (1 row) 231 | 232 | SELECT variance(u) FROM u; 233 | variance 234 | ---------- 235 | 0.5 236 | (1 row) 237 | 238 | SELECT stddev_pop(u) FROM u; 239 | stddev_pop 240 | ------------ 241 | 500 mm 242 | (1 row) 243 | 244 | SELECT stddev_samp(u) FROM u; 245 | stddev_samp 246 | --------------------- 247 | 707.106781186548 mm 248 | (1 row) 249 | 250 | SELECT stddev(u) FROM u; 251 | stddev 252 | --------------------- 253 | 707.106781186548 mm 254 | (1 row) 255 | 256 | DELETE FROM u; 257 | INSERT INTO u VALUES (kilogram(2)), (kilogram()); 258 | SELECT sum(u), sum(value(u)) FROM u; 259 | sum | sum 260 | ------+----- 261 | 3 kg | 3 262 | (1 row) 263 | 264 | SELECT min(u), min(value(u)) FROM u; 265 | min | min 266 | ------+----- 267 | 1 kg | 1 268 | (1 row) 269 | 270 | SELECT max(u), max(value(u)) FROM u; 271 | max | max 272 | ------+----- 273 | 2 kg | 2 274 | (1 row) 275 | 276 | SELECT avg(u), avg(value(u)) FROM u; 277 | avg | avg 278 | --------+----- 279 | 1.5 kg | 1.5 280 | (1 row) 281 | 282 | SELECT var_pop(u), var_pop(value(u)) FROM u; 283 | var_pop | var_pop 284 | ---------+--------- 285 | 0.25 | 0.25 286 | (1 row) 287 | 288 | SELECT var_samp(u), var_samp(value(u)) FROM u; 289 | var_samp | var_samp 290 | ----------+---------- 291 | 0.5 | 0.5 292 | (1 row) 293 | 294 | SELECT variance(u), variance(value(u)) FROM u; 295 | variance | variance 296 | ----------+---------- 297 | 0.5 | 0.5 298 | (1 row) 299 | 300 | SELECT stddev_pop(u), stddev_pop(value(u)) FROM u; 301 | stddev_pop | stddev_pop 302 | ------------+------------ 303 | 500 g | 0.5 304 | (1 row) 305 | 306 | SELECT stddev_samp(u), stddev_samp(value(u)) FROM u; 307 | stddev_samp | stddev_samp 308 | --------------------+------------------- 309 | 707.106781186548 g | 0.707106781186548 310 | (1 row) 311 | 312 | SELECT stddev(u), stddev(value(u)) FROM u; 313 | stddev | stddev 314 | --------------------+------------------- 315 | 707.106781186548 g | 0.707106781186548 316 | (1 row) 317 | 318 | -- three rows, one NULL 319 | INSERT INTO u VALUES (NULL); 320 | SELECT sum(u) AS three_kilogram FROM u; 321 | three_kilogram 322 | ---------------- 323 | 3 kg 324 | (1 row) 325 | 326 | SELECT min(u) AS one_kilogram FROM u; 327 | one_kilogram 328 | -------------- 329 | 1 kg 330 | (1 row) 331 | 332 | SELECT max(u) AS two_kilogram FROM u; 333 | two_kilogram 334 | -------------- 335 | 2 kg 336 | (1 row) 337 | 338 | SELECT avg(u) AS oneandhalf_kilogram FROM u; 339 | oneandhalf_kilogram 340 | --------------------- 341 | 1.5 kg 342 | (1 row) 343 | 344 | SELECT var_pop(u) FROM u; 345 | var_pop 346 | --------- 347 | 0.25 348 | (1 row) 349 | 350 | SELECT var_samp(u) FROM u; 351 | var_samp 352 | ---------- 353 | 0.5 354 | (1 row) 355 | 356 | SELECT variance(u) FROM u; 357 | variance 358 | ---------- 359 | 0.5 360 | (1 row) 361 | 362 | SELECT stddev_pop(u) FROM u; 363 | stddev_pop 364 | ------------ 365 | 500 g 366 | (1 row) 367 | 368 | SELECT stddev_samp(u) FROM u; 369 | stddev_samp 370 | -------------------- 371 | 707.106781186548 g 372 | (1 row) 373 | 374 | SELECT stddev(u) FROM u; 375 | stddev 376 | -------------------- 377 | 707.106781186548 g 378 | (1 row) 379 | 380 | -- two rows, dimension mismatch 381 | DELETE FROM u; 382 | INSERT INTO u VALUES (ampere()), (kilogram(2)); 383 | SELECT sum(u) AS error FROM u; 384 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 385 | SELECT min(u) AS error FROM u; 386 | ERROR: dimension mismatch in "unit_least" operation: "1 A", "2 kg" 387 | SELECT max(u) AS error FROM u; 388 | ERROR: dimension mismatch in "unit_greatest" operation: "1 A", "2 kg" 389 | SELECT avg(u) AS error FROM u; 390 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 391 | CONTEXT: SQL function "unit_accum" statement 1 392 | SELECT var_pop(u) AS error FROM u; 393 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 394 | CONTEXT: SQL function "unit_accum" statement 1 395 | SELECT var_samp(u) AS error FROM u; 396 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 397 | CONTEXT: SQL function "unit_accum" statement 1 398 | SELECT variance(u) AS error FROM u; 399 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 400 | CONTEXT: SQL function "unit_accum" statement 1 401 | SELECT stddev_pop(u) AS error FROM u; 402 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 403 | CONTEXT: SQL function "unit_accum" statement 1 404 | SELECT stddev_samp(u) AS error FROM u; 405 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 406 | CONTEXT: SQL function "unit_accum" statement 1 407 | SELECT stddev(u) AS error FROM u; 408 | ERROR: dimension mismatch in "+" operation: "1 A", "2 kg" 409 | CONTEXT: SQL function "unit_accum" statement 1 410 | -------------------------------------------------------------------------------- /expected/compare.out: -------------------------------------------------------------------------------- 1 | -- test comparisons 2 | WITH 3 | v(u) AS (VALUES 4 | (NULL), 5 | ('-2'::unit), 6 | ('-1'::unit), 7 | ('-0'::unit), 8 | ('0'::unit), 9 | ('1'::unit), 10 | ('2'::unit), 11 | (meter()), 12 | (meter() * '2'), 13 | (kilogram()), 14 | (kilogram() * '2') 15 | ), 16 | va(a) AS (SELECT * FROM v), 17 | vb(b) AS (SELECT * FROM v) 18 | SELECT 19 | a, b, 20 | CASE WHEN unit_cmp(a, b) < 0 THEN '<' 21 | WHEN unit_cmp(a, b) = 0 THEN '=' 22 | WHEN unit_cmp(a, b) > 0 THEN '>' 23 | END AS cmp, 24 | a < b AS lt, a <= b AS le, 25 | a = b AS eq, a <> b AS ne, 26 | a >= b AS ge, a > b AS gt 27 | FROM 28 | va CROSS JOIN vb; 29 | a | b | cmp | lt | le | eq | ne | ge | gt 30 | ------+------+-----+----+----+----+----+----+---- 31 | | | | | | | | | 32 | | -2 | | | | | | | 33 | | -1 | | | | | | | 34 | | -0 | | | | | | | 35 | | 0 | | | | | | | 36 | | 1 | | | | | | | 37 | | 2 | | | | | | | 38 | | 1 m | | | | | | | 39 | | 2 m | | | | | | | 40 | | 1 kg | | | | | | | 41 | | 2 kg | | | | | | | 42 | -2 | | | | | | | | 43 | -2 | -2 | = | f | t | t | f | t | f 44 | -2 | -1 | < | t | t | f | t | f | f 45 | -2 | -0 | < | t | t | f | t | f | f 46 | -2 | 0 | < | t | t | f | t | f | f 47 | -2 | 1 | < | t | t | f | t | f | f 48 | -2 | 2 | < | t | t | f | t | f | f 49 | -2 | 1 m | < | t | t | f | t | f | f 50 | -2 | 2 m | < | t | t | f | t | f | f 51 | -2 | 1 kg | < | t | t | f | t | f | f 52 | -2 | 2 kg | < | t | t | f | t | f | f 53 | -1 | | | | | | | | 54 | -1 | -2 | > | f | f | f | t | t | t 55 | -1 | -1 | = | f | t | t | f | t | f 56 | -1 | -0 | < | t | t | f | t | f | f 57 | -1 | 0 | < | t | t | f | t | f | f 58 | -1 | 1 | < | t | t | f | t | f | f 59 | -1 | 2 | < | t | t | f | t | f | f 60 | -1 | 1 m | < | t | t | f | t | f | f 61 | -1 | 2 m | < | t | t | f | t | f | f 62 | -1 | 1 kg | < | t | t | f | t | f | f 63 | -1 | 2 kg | < | t | t | f | t | f | f 64 | -0 | | | | | | | | 65 | -0 | -2 | > | f | f | f | t | t | t 66 | -0 | -1 | > | f | f | f | t | t | t 67 | -0 | -0 | = | f | t | t | f | t | f 68 | -0 | 0 | = | f | t | t | f | t | f 69 | -0 | 1 | < | t | t | f | t | f | f 70 | -0 | 2 | < | t | t | f | t | f | f 71 | -0 | 1 m | < | t | t | f | t | f | f 72 | -0 | 2 m | < | t | t | f | t | f | f 73 | -0 | 1 kg | < | t | t | f | t | f | f 74 | -0 | 2 kg | < | t | t | f | t | f | f 75 | 0 | | | | | | | | 76 | 0 | -2 | > | f | f | f | t | t | t 77 | 0 | -1 | > | f | f | f | t | t | t 78 | 0 | -0 | = | f | t | t | f | t | f 79 | 0 | 0 | = | f | t | t | f | t | f 80 | 0 | 1 | < | t | t | f | t | f | f 81 | 0 | 2 | < | t | t | f | t | f | f 82 | 0 | 1 m | < | t | t | f | t | f | f 83 | 0 | 2 m | < | t | t | f | t | f | f 84 | 0 | 1 kg | < | t | t | f | t | f | f 85 | 0 | 2 kg | < | t | t | f | t | f | f 86 | 1 | | | | | | | | 87 | 1 | -2 | > | f | f | f | t | t | t 88 | 1 | -1 | > | f | f | f | t | t | t 89 | 1 | -0 | > | f | f | f | t | t | t 90 | 1 | 0 | > | f | f | f | t | t | t 91 | 1 | 1 | = | f | t | t | f | t | f 92 | 1 | 2 | < | t | t | f | t | f | f 93 | 1 | 1 m | < | t | t | f | t | f | f 94 | 1 | 2 m | < | t | t | f | t | f | f 95 | 1 | 1 kg | < | t | t | f | t | f | f 96 | 1 | 2 kg | < | t | t | f | t | f | f 97 | 2 | | | | | | | | 98 | 2 | -2 | > | f | f | f | t | t | t 99 | 2 | -1 | > | f | f | f | t | t | t 100 | 2 | -0 | > | f | f | f | t | t | t 101 | 2 | 0 | > | f | f | f | t | t | t 102 | 2 | 1 | > | f | f | f | t | t | t 103 | 2 | 2 | = | f | t | t | f | t | f 104 | 2 | 1 m | > | f | f | f | t | t | t 105 | 2 | 2 m | < | t | t | f | t | f | f 106 | 2 | 1 kg | > | f | f | f | t | t | t 107 | 2 | 2 kg | < | t | t | f | t | f | f 108 | 1 m | | | | | | | | 109 | 1 m | -2 | > | f | f | f | t | t | t 110 | 1 m | -1 | > | f | f | f | t | t | t 111 | 1 m | -0 | > | f | f | f | t | t | t 112 | 1 m | 0 | > | f | f | f | t | t | t 113 | 1 m | 1 | > | f | f | f | t | t | t 114 | 1 m | 2 | < | t | t | f | t | f | f 115 | 1 m | 1 m | = | f | t | t | f | t | f 116 | 1 m | 2 m | < | t | t | f | t | f | f 117 | 1 m | 1 kg | > | f | f | f | t | t | t 118 | 1 m | 2 kg | < | t | t | f | t | f | f 119 | 2 m | | | | | | | | 120 | 2 m | -2 | > | f | f | f | t | t | t 121 | 2 m | -1 | > | f | f | f | t | t | t 122 | 2 m | -0 | > | f | f | f | t | t | t 123 | 2 m | 0 | > | f | f | f | t | t | t 124 | 2 m | 1 | > | f | f | f | t | t | t 125 | 2 m | 2 | > | f | f | f | t | t | t 126 | 2 m | 1 m | > | f | f | f | t | t | t 127 | 2 m | 2 m | = | f | t | t | f | t | f 128 | 2 m | 1 kg | > | f | f | f | t | t | t 129 | 2 m | 2 kg | > | f | f | f | t | t | t 130 | 1 kg | | | | | | | | 131 | 1 kg | -2 | > | f | f | f | t | t | t 132 | 1 kg | -1 | > | f | f | f | t | t | t 133 | 1 kg | -0 | > | f | f | f | t | t | t 134 | 1 kg | 0 | > | f | f | f | t | t | t 135 | 1 kg | 1 | > | f | f | f | t | t | t 136 | 1 kg | 2 | < | t | t | f | t | f | f 137 | 1 kg | 1 m | < | t | t | f | t | f | f 138 | 1 kg | 2 m | < | t | t | f | t | f | f 139 | 1 kg | 1 kg | = | f | t | t | f | t | f 140 | 1 kg | 2 kg | < | t | t | f | t | f | f 141 | 2 kg | | | | | | | | 142 | 2 kg | -2 | > | f | f | f | t | t | t 143 | 2 kg | -1 | > | f | f | f | t | t | t 144 | 2 kg | -0 | > | f | f | f | t | t | t 145 | 2 kg | 0 | > | f | f | f | t | t | t 146 | 2 kg | 1 | > | f | f | f | t | t | t 147 | 2 kg | 2 | > | f | f | f | t | t | t 148 | 2 kg | 1 m | > | f | f | f | t | t | t 149 | 2 kg | 2 m | < | t | t | f | t | f | f 150 | 2 kg | 1 kg | > | f | f | f | t | t | t 151 | 2 kg | 2 kg | = | f | t | t | f | t | f 152 | (121 rows) 153 | 154 | -- test btree index 155 | CREATE TEMP TABLE u (u unit); 156 | INSERT INTO u SELECT meter(generate_series(1,10000)::double precision); 157 | INSERT INTO u SELECT generate_series(1,10000)::text::unit * kilogram(); 158 | ANALYZE u; 159 | CREATE INDEX ON u(u); 160 | SELECT * FROM u WHERE u = meter(400); 161 | u 162 | ------- 163 | 400 m 164 | (1 row) 165 | 166 | SELECT * FROM u WHERE u = '300' * kilogram(); 167 | u 168 | -------- 169 | 300 kg 170 | (1 row) 171 | 172 | EXPLAIN (COSTS OFF) SELECT * FROM u WHERE u = meter(400); 173 | QUERY PLAN 174 | ------------------------------------ 175 | Index Only Scan using u_u_idx on u 176 | Index Cond: (u = '400 m'::unit) 177 | (2 rows) 178 | 179 | EXPLAIN (COSTS OFF) SELECT * FROM u WHERE u = '300' * kilogram(); 180 | QUERY PLAN 181 | ------------------------------------ 182 | Index Only Scan using u_u_idx on u 183 | Index Cond: (u = '300 kg'::unit) 184 | (2 rows) 185 | 186 | -- test strict operators 187 | SELECT '1 m'::unit << '1 m'::unit; 188 | ?column? 189 | ---------- 190 | f 191 | (1 row) 192 | 193 | SELECT '1 m'::unit << '1 A'::unit; 194 | ERROR: dimension mismatch in "strict comparison" operation: "1 m", "1 A" 195 | SELECT '1 m'::unit <<= '1 m'::unit; 196 | ?column? 197 | ---------- 198 | t 199 | (1 row) 200 | 201 | SELECT '1 m'::unit <<= '1 A'::unit; 202 | ERROR: dimension mismatch in "strict comparison" operation: "1 m", "1 A" 203 | SELECT '1 m'::unit == '1 m'::unit; 204 | ?column? 205 | ---------- 206 | t 207 | (1 row) 208 | 209 | SELECT '1 m'::unit == '1 A'::unit; 210 | ERROR: dimension mismatch in "strict comparison" operation: "1 m", "1 A" 211 | SELECT '1 m'::unit <<>> '1 m'::unit; 212 | ?column? 213 | ---------- 214 | f 215 | (1 row) 216 | 217 | SELECT '1 m'::unit <<>> '1 A'::unit; 218 | ERROR: dimension mismatch in "strict comparison" operation: "1 m", "1 A" 219 | SELECT '1 m'::unit >>= '1 m'::unit; 220 | ?column? 221 | ---------- 222 | t 223 | (1 row) 224 | 225 | SELECT '1 m'::unit >>= '1 A'::unit; 226 | ERROR: dimension mismatch in "strict comparison" operation: "1 m", "1 A" 227 | SELECT '1 m'::unit >> '1 m'::unit; 228 | ?column? 229 | ---------- 230 | f 231 | (1 row) 232 | 233 | SELECT '1 m'::unit >> '1 A'::unit; 234 | ERROR: dimension mismatch in "strict comparison" operation: "1 m", "1 A" 235 | -- test range type 236 | SELECT 'empty'::unitrange; 237 | unitrange 238 | ----------- 239 | empty 240 | (1 row) 241 | 242 | SELECT '(1 m, 2 m)'::unitrange; 243 | unitrange 244 | --------------- 245 | ("1 m","2 m") 246 | (1 row) 247 | 248 | SELECT '(1 m, 2 A)'::unitrange; 249 | ERROR: dimension mismatch in "strict comparison" operation: "1 m", "2 A" 250 | LINE 1: SELECT '(1 m, 2 A)'::unitrange; 251 | ^ 252 | SELECT unit_diff('1 A', '2 A'); 253 | unit_diff 254 | ----------- 255 | -1 256 | (1 row) 257 | 258 | SELECT unit_diff('1 A', '2 K'); 259 | ERROR: dimension mismatch in "-" operation: "1 A", "2 K" 260 | -------------------------------------------------------------------------------- /expected/functions.out: -------------------------------------------------------------------------------- 1 | -- test functions 2 | SELECT value('2'::unit); 3 | value 4 | ------- 5 | 2 6 | (1 row) 7 | 8 | SELECT value(meter(2)); 9 | value 10 | ------- 11 | 2 12 | (1 row) 13 | 14 | SELECT dimension('2'::unit); 15 | dimension 16 | ----------- 17 | 1 18 | (1 row) 19 | 20 | SELECT dimension(kilogram(2)); 21 | dimension 22 | ----------- 23 | 1 kg 24 | (1 row) 25 | 26 | -- test unit addition/subtraction 27 | SELECT '1'::unit + '2' AS sum; 28 | sum 29 | ----- 30 | 3 31 | (1 row) 32 | 33 | SELECT '1'::unit - '2' AS difference; 34 | difference 35 | ------------ 36 | -1 37 | (1 row) 38 | 39 | SELECT '3 m'::unit - '1 µm' AS difference; 40 | difference 41 | ------------ 42 | 2.999999 m 43 | (1 row) 44 | 45 | SELECT '0' + meter() AS error; 46 | ERROR: dimension mismatch in "+" operation: "0", "1 m" 47 | SELECT meter() + '0' AS error; 48 | ERROR: dimension mismatch in "+" operation: "1 m", "0" 49 | SELECT meter() + kilogram() AS error; 50 | ERROR: dimension mismatch in "+" operation: "1 m", "1 kg" 51 | SELECT meter() - kilogram() AS error; 52 | ERROR: dimension mismatch in "-" operation: "1 m", "1 kg" 53 | -- test unit multiplication/division 54 | SELECT - '1'::unit AS negative; 55 | negative 56 | ---------- 57 | -1 58 | (1 row) 59 | 60 | SELECT '2'::unit * '2' AS product; 61 | product 62 | --------- 63 | 4 64 | (1 row) 65 | 66 | SELECT '5'::unit / '2' AS fraction; 67 | fraction 68 | ---------- 69 | 2.5 70 | (1 row) 71 | 72 | SELECT '0'::unit / '2' AS zero; 73 | zero 74 | ------ 75 | 0 76 | (1 row) 77 | 78 | SELECT '5'::unit / '0' AS division_by_zero; 79 | ERROR: division by zero-valued unit: "0" 80 | SELECT - second() AS minus_second; 81 | minus_second 82 | -------------- 83 | -1 s 84 | (1 row) 85 | 86 | SELECT meter() * '2' AS two_meters; 87 | two_meters 88 | ------------ 89 | 2 m 90 | (1 row) 91 | 92 | SELECT '3' * meter() AS three_meters; 93 | three_meters 94 | -------------- 95 | 3 m 96 | (1 row) 97 | 98 | SELECT meter() * meter() AS square_meter; 99 | square_meter 100 | -------------- 101 | 1 m^2 102 | (1 row) 103 | 104 | SELECT meter() * kilogram() AS meter_kilogram; 105 | meter_kilogram 106 | ---------------- 107 | 1 m*kg 108 | (1 row) 109 | 110 | SELECT '1' / second(.02) AS hertz; 111 | hertz 112 | ------- 113 | 50 Hz 114 | (1 row) 115 | 116 | SELECT meter(9.81) / (second() * second()) AS acceleration; 117 | acceleration 118 | -------------- 119 | 9.81 m/s^2 120 | (1 row) 121 | 122 | -- test unit/double operators 123 | SELECT 2 * meter() AS two_meters; 124 | two_meters 125 | ------------ 126 | 2 m 127 | (1 row) 128 | 129 | SELECT ampere() * 3 AS three_ampere; 130 | three_ampere 131 | -------------- 132 | 3 A 133 | (1 row) 134 | 135 | SELECT 4 / second() AS four_hertz; 136 | four_hertz 137 | ------------ 138 | 4 Hz 139 | (1 row) 140 | 141 | SELECT kelvin(10) / 5 AS two_kelvin; 142 | two_kelvin 143 | ------------ 144 | 2 K 145 | (1 row) 146 | 147 | SELECT 0 / candela() AS zero_by_candela; 148 | zero_by_candela 149 | ----------------- 150 | 0 cd^-1 151 | (1 row) 152 | 153 | SELECT 1 / mole(0) AS error; 154 | ERROR: division by zero-valued unit: "0 mol" 155 | SELECT byte(0) / 2 AS zero_byte; 156 | zero_byte 157 | ----------- 158 | 0 B 159 | (1 row) 160 | 161 | SELECT kilogram(1) / 0 AS error; 162 | ERROR: division of unit by zero 163 | -- test exponentiation 164 | SELECT '6'::unit ^ 2 AS square; 165 | square 166 | -------- 167 | 36 168 | (1 row) 169 | 170 | SELECT meter(100) ^ 2 AS square_meter; 171 | square_meter 172 | -------------- 173 | 10000 m^2 174 | (1 row) 175 | 176 | SELECT kilogram(100) ^ 2 AS square_kilogram; 177 | square_kilogram 178 | ----------------- 179 | 10000 kg^2 180 | (1 row) 181 | 182 | SELECT second(.02) ^ -1 AS hertz; 183 | hertz 184 | ------- 185 | 50 Hz 186 | (1 row) 187 | 188 | SELECT ampere(4) ^ 0 AS unity; 189 | unity 190 | ------- 191 | 1 192 | (1 row) 193 | 194 | SELECT '0'::unit ^ 0 AS unity; 195 | unity 196 | ------- 197 | 1 198 | (1 row) 199 | 200 | -- test roots 201 | SELECT sqrt('4'::unit); 202 | sqrt 203 | ------ 204 | 2 205 | (1 row) 206 | 207 | SELECT sqrt('4 m^2'::unit); 208 | sqrt 209 | ------ 210 | 2 m 211 | (1 row) 212 | 213 | SELECT |/'16 s^-4'::unit; 214 | ?column? 215 | ---------- 216 | 4 s^-2 217 | (1 row) 218 | 219 | SELECT sqrt('-4 m^2'::unit) AS error; 220 | ERROR: cannot take square root of a negative-valued unit 221 | SELECT sqrt('4 m'::unit) AS error; 222 | ERROR: cannot take square root of a unit with odd "m" exponent 223 | SELECT cbrt('8'::unit); 224 | cbrt 225 | ------ 226 | 2 227 | (1 row) 228 | 229 | SELECT cbrt('8 m^3'::unit); 230 | cbrt 231 | ------ 232 | 2 m 233 | (1 row) 234 | 235 | SELECT ||/'-27 s^-6'::unit; 236 | ?column? 237 | ---------- 238 | -3 s^-2 239 | (1 row) 240 | 241 | SELECT cbrt('-4 m'::unit) AS error; 242 | ERROR: cannot take cube root of a unit with "m" exponent not divisible by three 243 | WITH 244 | v(u) AS (VALUES 245 | (NULL), 246 | ('0'::unit), 247 | ('1'::unit), 248 | ('2'::unit), 249 | (meter(0)), 250 | (meter()), 251 | (meter() * '2'), 252 | (kilogram(0)), 253 | (kilogram()), 254 | (kilogram() * '2') 255 | ), 256 | va(a) AS (SELECT * FROM v), 257 | vb(b) AS (SELECT * FROM v) 258 | SELECT 259 | a, b, 260 | -a AS neg, 261 | a * b AS mul, 262 | CASE WHEN value(b) = 0 THEN NULL ELSE a / b END AS div 263 | FROM 264 | va CROSS JOIN vb; 265 | a | b | neg | mul | div 266 | ------+------+-------+--------+----------- 267 | | | | | 268 | | 0 | | | 269 | | 1 | | | 270 | | 2 | | | 271 | | 0 m | | | 272 | | 1 m | | | 273 | | 2 m | | | 274 | | 0 kg | | | 275 | | 1 kg | | | 276 | | 2 kg | | | 277 | 0 | | -0 | | 278 | 0 | 0 | -0 | 0 | 279 | 0 | 1 | -0 | 0 | 0 280 | 0 | 2 | -0 | 0 | 0 281 | 0 | 0 m | -0 | 0 m | 282 | 0 | 1 m | -0 | 0 m | 0 m^-1 283 | 0 | 2 m | -0 | 0 m | 0 m^-1 284 | 0 | 0 kg | -0 | 0 kg | 285 | 0 | 1 kg | -0 | 0 kg | 0 kg^-1 286 | 0 | 2 kg | -0 | 0 kg | 0 kg^-1 287 | 1 | | -1 | | 288 | 1 | 0 | -1 | 0 | 289 | 1 | 1 | -1 | 1 | 1 290 | 1 | 2 | -1 | 2 | 0.5 291 | 1 | 0 m | -1 | 0 m | 292 | 1 | 1 m | -1 | 1 m | 1 m^-1 293 | 1 | 2 m | -1 | 2 m | 0.5 m^-1 294 | 1 | 0 kg | -1 | 0 kg | 295 | 1 | 1 kg | -1 | 1 kg | 1 kg^-1 296 | 1 | 2 kg | -1 | 2 kg | 0.5 kg^-1 297 | 2 | | -2 | | 298 | 2 | 0 | -2 | 0 | 299 | 2 | 1 | -2 | 2 | 2 300 | 2 | 2 | -2 | 4 | 1 301 | 2 | 0 m | -2 | 0 m | 302 | 2 | 1 m | -2 | 2 m | 2 m^-1 303 | 2 | 2 m | -2 | 4 m | 1 m^-1 304 | 2 | 0 kg | -2 | 0 kg | 305 | 2 | 1 kg | -2 | 2 kg | 2 kg^-1 306 | 2 | 2 kg | -2 | 4 kg | 1 kg^-1 307 | 0 m | | -0 m | | 308 | 0 m | 0 | -0 m | 0 m | 309 | 0 m | 1 | -0 m | 0 m | 0 m 310 | 0 m | 2 | -0 m | 0 m | 0 m 311 | 0 m | 0 m | -0 m | 0 m^2 | 312 | 0 m | 1 m | -0 m | 0 m^2 | 0 313 | 0 m | 2 m | -0 m | 0 m^2 | 0 314 | 0 m | 0 kg | -0 m | 0 m*kg | 315 | 0 m | 1 kg | -0 m | 0 m*kg | 0 m/kg 316 | 0 m | 2 kg | -0 m | 0 m*kg | 0 m/kg 317 | 1 m | | -1 m | | 318 | 1 m | 0 | -1 m | 0 m | 319 | 1 m | 1 | -1 m | 1 m | 1 m 320 | 1 m | 2 | -1 m | 2 m | 500 mm 321 | 1 m | 0 m | -1 m | 0 m^2 | 322 | 1 m | 1 m | -1 m | 1 m^2 | 1 323 | 1 m | 2 m | -1 m | 2 m^2 | 0.5 324 | 1 m | 0 kg | -1 m | 0 m*kg | 325 | 1 m | 1 kg | -1 m | 1 m*kg | 1 m/kg 326 | 1 m | 2 kg | -1 m | 2 m*kg | 500 mm/kg 327 | 2 m | | -2 m | | 328 | 2 m | 0 | -2 m | 0 m | 329 | 2 m | 1 | -2 m | 2 m | 2 m 330 | 2 m | 2 | -2 m | 4 m | 1 m 331 | 2 m | 0 m | -2 m | 0 m^2 | 332 | 2 m | 1 m | -2 m | 2 m^2 | 2 333 | 2 m | 2 m | -2 m | 4 m^2 | 1 334 | 2 m | 0 kg | -2 m | 0 m*kg | 335 | 2 m | 1 kg | -2 m | 2 m*kg | 2 m/kg 336 | 2 m | 2 kg | -2 m | 4 m*kg | 1 m/kg 337 | 0 kg | | -0 kg | | 338 | 0 kg | 0 | -0 kg | 0 kg | 339 | 0 kg | 1 | -0 kg | 0 kg | 0 kg 340 | 0 kg | 2 | -0 kg | 0 kg | 0 kg 341 | 0 kg | 0 m | -0 kg | 0 m*kg | 342 | 0 kg | 1 m | -0 kg | 0 m*kg | 0 kg/m 343 | 0 kg | 2 m | -0 kg | 0 m*kg | 0 kg/m 344 | 0 kg | 0 kg | -0 kg | 0 kg^2 | 345 | 0 kg | 1 kg | -0 kg | 0 kg^2 | 0 346 | 0 kg | 2 kg | -0 kg | 0 kg^2 | 0 347 | 1 kg | | -1 kg | | 348 | 1 kg | 0 | -1 kg | 0 kg | 349 | 1 kg | 1 | -1 kg | 1 kg | 1 kg 350 | 1 kg | 2 | -1 kg | 2 kg | 500 g 351 | 1 kg | 0 m | -1 kg | 0 m*kg | 352 | 1 kg | 1 m | -1 kg | 1 m*kg | 1 kg/m 353 | 1 kg | 2 m | -1 kg | 2 m*kg | 500 g/m 354 | 1 kg | 0 kg | -1 kg | 0 kg^2 | 355 | 1 kg | 1 kg | -1 kg | 1 kg^2 | 1 356 | 1 kg | 2 kg | -1 kg | 2 kg^2 | 0.5 357 | 2 kg | | -2 kg | | 358 | 2 kg | 0 | -2 kg | 0 kg | 359 | 2 kg | 1 | -2 kg | 2 kg | 2 kg 360 | 2 kg | 2 | -2 kg | 4 kg | 1 kg 361 | 2 kg | 0 m | -2 kg | 0 m*kg | 362 | 2 kg | 1 m | -2 kg | 2 m*kg | 2 kg/m 363 | 2 kg | 2 m | -2 kg | 4 m*kg | 1 kg/m 364 | 2 kg | 0 kg | -2 kg | 0 kg^2 | 365 | 2 kg | 1 kg | -2 kg | 2 kg^2 | 2 366 | 2 kg | 2 kg | -2 kg | 4 kg^2 | 1 367 | (100 rows) 368 | 369 | WITH 370 | v(a) AS (VALUES 371 | (NULL), 372 | ('1'::unit), 373 | ('2'::unit), 374 | (meter()), 375 | (meter() * '2'), 376 | (kilogram()), 377 | (kilogram() * '2') 378 | ) 379 | SELECT 380 | a, 381 | a ^ -2 AS pow_2, a ^ -1 AS pow_1, a ^ 0 AS pow0, a ^ 1 AS pow1, a ^ 2 AS pow2 382 | FROM 383 | v; 384 | a | pow_2 | pow_1 | pow0 | pow1 | pow2 385 | ------+------------+-----------+------+------+-------- 386 | | | | | | 387 | 1 | 1 | 1 | 1 | 1 | 1 388 | 2 | 0.25 | 0.5 | 1 | 2 | 4 389 | 1 m | 1 m^-2 | 1 m^-1 | 1 | 1 m | 1 m^2 390 | 2 m | 0.25 m^-2 | 0.5 m^-1 | 1 | 2 m | 4 m^2 391 | 1 kg | 1 kg^-2 | 1 kg^-1 | 1 | 1 kg | 1 kg^2 392 | 2 kg | 0.25 kg^-2 | 0.5 kg^-1 | 1 | 2 kg | 4 kg^2 393 | (7 rows) 394 | 395 | -- test conversion 396 | SELECT '1m'::unit @ 'mm'; 397 | ?column? 398 | ---------- 399 | 1000 mm 400 | (1 row) 401 | 402 | SELECT '10g'::unit @ 'mg'; 403 | ?column? 404 | ---------- 405 | 10000 mg 406 | (1 row) 407 | 408 | SELECT '5dm^3'::unit @ 'l'; 409 | ?column? 410 | ---------- 411 | 5 l 412 | (1 row) 413 | 414 | SELECT '9.81 m/s^2'::unit @ 'N/kg', '9.81 m/s^2'::unit @@ 'N/kg'; 415 | ?column? | ?column? 416 | -----------+---------- 417 | 9.81 N/kg | 9.81 418 | (1 row) 419 | 420 | SELECT '16384 B'::unit @ '8192 B', '16384 B'::unit @@ '8192 B'; 421 | ?column? | ?column? 422 | ------------+---------- 423 | 2 * 8192 B | 2 424 | (1 row) 425 | 426 | SELECT '1 hl'::unit @ '0.5 l'; 427 | ?column? 428 | ------------- 429 | 200 * 0.5 l 430 | (1 row) 431 | 432 | SELECT '5MB/min'::unit @ 'GB/d'; 433 | ?column? 434 | ---------- 435 | 7.2 GB/d 436 | (1 row) 437 | 438 | SELECT '5m'::unit @ 's' AS error; 439 | ERROR: dimension mismatch in "@" operation: "5 m", "1 s" 440 | -------------------------------------------------------------------------------- /expected/derived.out: -------------------------------------------------------------------------------- 1 | -- derived units 2 | SELECT radian(); 3 | radian 4 | -------- 5 | 1 6 | (1 row) 7 | 8 | SELECT steradian(); 9 | steradian 10 | ----------- 11 | 1 12 | (1 row) 13 | 14 | SELECT hertz(); 15 | hertz 16 | ------- 17 | 1 Hz 18 | (1 row) 19 | 20 | SELECT newton(); 21 | newton 22 | -------- 23 | 1 N 24 | (1 row) 25 | 26 | SELECT pascal(); 27 | pascal 28 | -------- 29 | 1 Pa 30 | (1 row) 31 | 32 | SELECT joule(); 33 | joule 34 | ------- 35 | 1 J 36 | (1 row) 37 | 38 | SELECT watt(); 39 | watt 40 | ------ 41 | 1 W 42 | (1 row) 43 | 44 | SELECT coulomb(); 45 | coulomb 46 | --------- 47 | 1 C 48 | (1 row) 49 | 50 | SELECT volt(); 51 | volt 52 | ------ 53 | 1 V 54 | (1 row) 55 | 56 | SELECT farad(); 57 | farad 58 | ------- 59 | 1 F 60 | (1 row) 61 | 62 | SELECT ohm(); 63 | ohm 64 | ----- 65 | 1 Ω 66 | (1 row) 67 | 68 | SELECT siemens(); 69 | siemens 70 | --------- 71 | 1 S 72 | (1 row) 73 | 74 | SELECT weber(); 75 | weber 76 | ------- 77 | 1 Wb 78 | (1 row) 79 | 80 | SELECT tesla(); 81 | tesla 82 | ------- 83 | 1 T 84 | (1 row) 85 | 86 | SELECT henry(); 87 | henry 88 | ------- 89 | 1 H 90 | (1 row) 91 | 92 | SELECT celsius(); 93 | celsius 94 | ---------- 95 | 273.15 K 96 | (1 row) 97 | 98 | SELECT lumen(); 99 | lumen 100 | ------- 101 | 1 cd 102 | (1 row) 103 | 104 | SELECT lux(); 105 | lux 106 | ------ 107 | 1 lx 108 | (1 row) 109 | 110 | SELECT becquerel(); 111 | becquerel 112 | ----------- 113 | 1 Hz 114 | (1 row) 115 | 116 | SELECT gray(); 117 | gray 118 | ------ 119 | 1 Gy 120 | (1 row) 121 | 122 | SELECT sievert(); 123 | sievert 124 | --------- 125 | 1 Gy 126 | (1 row) 127 | 128 | SELECT katal(); 129 | katal 130 | ------- 131 | 1 kat 132 | (1 row) 133 | 134 | SELECT radian(2); 135 | radian 136 | -------- 137 | 2 138 | (1 row) 139 | 140 | SELECT steradian(2); 141 | steradian 142 | ----------- 143 | 2 144 | (1 row) 145 | 146 | SELECT hertz(2); 147 | hertz 148 | ------- 149 | 2 Hz 150 | (1 row) 151 | 152 | SELECT newton(2); 153 | newton 154 | -------- 155 | 2 N 156 | (1 row) 157 | 158 | SELECT pascal(2); 159 | pascal 160 | -------- 161 | 2 Pa 162 | (1 row) 163 | 164 | SELECT joule(2); 165 | joule 166 | ------- 167 | 2 J 168 | (1 row) 169 | 170 | SELECT watt(2); 171 | watt 172 | ------ 173 | 2 W 174 | (1 row) 175 | 176 | SELECT coulomb(2); 177 | coulomb 178 | --------- 179 | 2 C 180 | (1 row) 181 | 182 | SELECT volt(2); 183 | volt 184 | ------ 185 | 2 V 186 | (1 row) 187 | 188 | SELECT farad(2); 189 | farad 190 | ------- 191 | 2 F 192 | (1 row) 193 | 194 | SELECT ohm(2); 195 | ohm 196 | ----- 197 | 2 Ω 198 | (1 row) 199 | 200 | SELECT siemens(2); 201 | siemens 202 | --------- 203 | 2 S 204 | (1 row) 205 | 206 | SELECT weber(2); 207 | weber 208 | ------- 209 | 2 Wb 210 | (1 row) 211 | 212 | SELECT tesla(2); 213 | tesla 214 | ------- 215 | 2 T 216 | (1 row) 217 | 218 | SELECT henry(2); 219 | henry 220 | ------- 221 | 2 H 222 | (1 row) 223 | 224 | SELECT celsius(100); 225 | celsius 226 | ---------- 227 | 373.15 K 228 | (1 row) 229 | 230 | SELECT lumen(2); 231 | lumen 232 | ------- 233 | 2 cd 234 | (1 row) 235 | 236 | SELECT lux(2); 237 | lux 238 | ------ 239 | 2 lx 240 | (1 row) 241 | 242 | SELECT becquerel(2); 243 | becquerel 244 | ----------- 245 | 2 Hz 246 | (1 row) 247 | 248 | SELECT gray(2); 249 | gray 250 | ------ 251 | 2 Gy 252 | (1 row) 253 | 254 | SELECT sievert(2); 255 | sievert 256 | --------- 257 | 2 Gy 258 | (1 row) 259 | 260 | SELECT katal(2); 261 | katal 262 | ------- 263 | 2 kat 264 | (1 row) 265 | 266 | /* revert to pre-12 default for decibel() tests */ 267 | SET extra_float_digits = 0; 268 | -- Non-SI units accepted for use with the SI 269 | SELECT minute(); 270 | minute 271 | ------------ 272 | 00:01:00 s 273 | (1 row) 274 | 275 | SELECT hour(); 276 | hour 277 | ------------ 278 | 01:00:00 s 279 | (1 row) 280 | 281 | SELECT day(); 282 | day 283 | ----- 284 | 1 d 285 | (1 row) 286 | 287 | SELECT degree_arc(); 288 | degree_arc 289 | -------------------- 290 | 0.0174532925199433 291 | (1 row) 292 | 293 | SELECT minute_arc(); 294 | minute_arc 295 | ---------------------- 296 | 0.000290888208665722 297 | (1 row) 298 | 299 | SELECT second_arc(); 300 | second_arc 301 | ---------------------- 302 | 4.84813681109536e-06 303 | (1 row) 304 | 305 | SELECT hectare(); 306 | hectare 307 | ----------- 308 | 10000 m^2 309 | (1 row) 310 | 311 | SELECT liter(); 312 | liter 313 | ----------- 314 | 0.001 m^3 315 | (1 row) 316 | 317 | SELECT tonne(); 318 | tonne 319 | ------- 320 | 1 Mg 321 | (1 row) 322 | 323 | SELECT au(); 324 | au 325 | ---------------- 326 | 149.5978707 Gm 327 | (1 row) 328 | 329 | SELECT decibel(); 330 | decibel 331 | --------- 332 | 1 333 | (1 row) 334 | 335 | SELECT minute(60); 336 | minute 337 | ------------ 338 | 01:00:00 s 339 | (1 row) 340 | 341 | SELECT hour(24); 342 | hour 343 | ------ 344 | 1 d 345 | (1 row) 346 | 347 | SELECT day(1/24.0); 348 | day 349 | ------------ 350 | 01:00:00 s 351 | (1 row) 352 | 353 | SELECT degree_arc(360); 354 | degree_arc 355 | ------------------ 356 | 6.28318530717959 357 | (1 row) 358 | 359 | SELECT minute_arc(60); 360 | minute_arc 361 | -------------------- 362 | 0.0174532925199433 363 | (1 row) 364 | 365 | SELECT second_arc(60); 366 | second_arc 367 | ---------------------- 368 | 0.000290888208665722 369 | (1 row) 370 | 371 | SELECT hectare(1/100.0); 372 | hectare 373 | --------- 374 | 100 m^2 375 | (1 row) 376 | 377 | SELECT liter(1000); 378 | liter 379 | ------- 380 | 1 m^3 381 | (1 row) 382 | 383 | SELECT tonne(1/1000.0); 384 | tonne 385 | ------- 386 | 1 kg 387 | (1 row) 388 | 389 | SELECT au(10); 390 | au 391 | ---------------- 392 | 1.495978707 Tm 393 | (1 row) 394 | 395 | SELECT decibel(-3); 396 | decibel 397 | ------------------- 398 | 0.501187233627272 399 | (1 row) 400 | 401 | SELECT decibel(3); 402 | decibel 403 | ------------------ 404 | 1.99526231496888 405 | (1 row) 406 | 407 | SELECT decibel(10); 408 | decibel 409 | --------- 410 | 10 411 | (1 row) 412 | 413 | SELECT decibel(20); 414 | decibel 415 | --------- 416 | 100 417 | (1 row) 418 | 419 | SET client_min_messages = warning; 420 | DROP TABLE IF EXISTS u; 421 | RESET client_min_messages; 422 | CREATE TABLE u AS 423 | SELECT unit AS u, 2000 * unit AS "2k", unit/5000 AS "200µ", 0.002/unit AS "500th_inverse", (40*unit)^2 AS "1600_square" 424 | FROM units WHERE base OR coherent; 425 | SELECT * FROM u; 426 | u | 2k | 200µ | 500th_inverse | 1600_square 427 | -------+------------+----------+----------------------+----------------------- 428 | 1 | 2000 | 0.0002 | 0.002 | 1600 429 | 1 m | 2 km | 200 µm | 0.002 m^-1 | 1600 m^2 430 | 1 kg | 2 Mg | 200 mg | 0.002 kg^-1 | 1600 kg^2 431 | 1 s | 00:33:20 s | 200 µs | 2 mHz | 1600 s^2 432 | 1 A | 2 kA | 200 µA | 0.002 A^-1 | 1600 A^2 433 | 1 K | 2 kK | 200 µK | 0.002 K^-1 | 1600 K^2 434 | 1 mol | 2 kmol | 200 µmol | 0.002 mol^-1 | 1600 mol^2 435 | 1 cd | 2 kcd | 200 µcd | 0.002 cd^-1 | 1600 cd^2 436 | 1 B | 2 kB | 200 µB | 0.002 B^-1 | 1600 B^2 437 | 1 | 2000 | 0.0002 | 0.002 | 1600 438 | 1 Hz | 2 kHz | 200 µHz | 2 ms | 1600 s^-2 439 | 1 N | 2 kN | 200 µN | 0.002 s^2/m*kg | 1600 m^2*kg^2/s^4 440 | 1 Pa | 2 kPa | 200 µPa | 0.002 m*s^2/kg | 1600 kg^2/m^2*s^4 441 | 1 J | 2 kJ | 200 µJ | 0.002 s^2/m^2*kg | 1600 m^4*kg^2/s^4 442 | 1 W | 2 kW | 200 µW | 0.002 s^3/m^2*kg | 1600 m^4*kg^2/s^6 443 | 1 C | 2 kC | 200 µC | 0.002 s^-1*A^-1 | 1600 s^2*A^2 444 | 1 V | 2 kV | 200 µV | 0.002 s^3*A/m^2*kg | 1600 m^4*kg^2/s^6*A^2 445 | 1 F | 2 kF | 200 µF | 0.002 m^2*kg/s^4*A^2 | 1600 s^8*A^4/m^4*kg^2 446 | 1 Ω | 2 kΩ | 200 µΩ | 2 mS | 1600 m^4*kg^2/s^6*A^4 447 | 1 Ω | 2 kΩ | 200 µΩ | 2 mS | 1600 m^4*kg^2/s^6*A^4 448 | 1 S | 2 kS | 200 µS | 2 mΩ | 1600 s^6*A^4/m^4*kg^2 449 | 1 Wb | 2 kWb | 200 µWb | 0.002 s^2*A/m^2*kg | 1600 m^4*kg^2/s^4*A^2 450 | 1 T | 2 kT | 200 µT | 0.002 s^2*A/kg | 1600 kg^2/s^4*A^2 451 | 1 H | 2 kH | 200 µH | 0.002 s^2*A^2/m^2*kg | 1600 m^4*kg^2/s^4*A^4 452 | 1 K | 2 kK | 200 µK | 0.002 K^-1 | 1600 K^2 453 | 1 cd | 2 kcd | 200 µcd | 0.002 cd^-1 | 1600 cd^2 454 | 1 lx | 2 klx | 200 µlx | 0.002 m^2/cd | 1600 cd^2/m^4 455 | 1 Hz | 2 kHz | 200 µHz | 2 ms | 1600 s^-2 456 | 1 Gy | 2 kGy | 200 µGy | 0.002 s^2/m^2 | 1600 m^4/s^4 457 | 1 Gy | 2 kGy | 200 µGy | 0.002 s^2/m^2 | 1600 m^4/s^4 458 | 1 kat | 2 kkat | 200 µkat | 2 ms/mol | 1600 mol^2/s^2 459 | (31 rows) 460 | 461 | SELECT u, u::text::unit AS "text::unit", "2k"::text::unit, "200µ"::text::unit, "500th_inverse"::text::unit, "1600_square"::text::unit FROM u; 462 | u | text::unit | 2k | 200µ | 500th_inverse | 1600_square 463 | -------+------------+------------+----------+----------------------+----------------------- 464 | 1 | 1 | 2000 | 0.0002 | 0.002 | 1600 465 | 1 m | 1 m | 2 km | 200 µm | 0.002 m^-1 | 1600 m^2 466 | 1 kg | 1 kg | 2 Mg | 200 mg | 0.002 kg^-1 | 1600 kg^2 467 | 1 s | 1 s | 00:33:20 s | 200 µs | 2 mHz | 1600 s^2 468 | 1 A | 1 A | 2 kA | 200 µA | 0.002 A^-1 | 1600 A^2 469 | 1 K | 1 K | 2 kK | 200 µK | 0.002 K^-1 | 1600 K^2 470 | 1 mol | 1 mol | 2 kmol | 200 µmol | 0.002 mol^-1 | 1600 mol^2 471 | 1 cd | 1 cd | 2 kcd | 200 µcd | 0.002 cd^-1 | 1600 cd^2 472 | 1 B | 1 B | 2 kB | 200 µB | 0.002 B^-1 | 1600 B^2 473 | 1 | 1 | 2000 | 0.0002 | 0.002 | 1600 474 | 1 Hz | 1 Hz | 2 kHz | 200 µHz | 2 ms | 1600 s^-2 475 | 1 N | 1 N | 2 kN | 200 µN | 0.002 s^2/m*kg | 1600 m^2*kg^2/s^4 476 | 1 Pa | 1 Pa | 2 kPa | 200 µPa | 0.002 m*s^2/kg | 1600 kg^2/m^2*s^4 477 | 1 J | 1 J | 2 kJ | 200 µJ | 0.002 s^2/m^2*kg | 1600 m^4*kg^2/s^4 478 | 1 W | 1 W | 2 kW | 200 µW | 0.002 s^3/m^2*kg | 1600 m^4*kg^2/s^6 479 | 1 C | 1 C | 2 kC | 200 µC | 0.002 s^-1*A^-1 | 1600 s^2*A^2 480 | 1 V | 1 V | 2 kV | 200 µV | 0.002 s^3*A/m^2*kg | 1600 m^4*kg^2/s^6*A^2 481 | 1 F | 1 F | 2 kF | 200 µF | 0.002 m^2*kg/s^4*A^2 | 1600 s^8*A^4/m^4*kg^2 482 | 1 Ω | 1 Ω | 2 kΩ | 200 µΩ | 2 mS | 1600 m^4*kg^2/s^6*A^4 483 | 1 Ω | 1 Ω | 2 kΩ | 200 µΩ | 2 mS | 1600 m^4*kg^2/s^6*A^4 484 | 1 S | 1 S | 2 kS | 200 µS | 2 mΩ | 1600 s^6*A^4/m^4*kg^2 485 | 1 Wb | 1 Wb | 2 kWb | 200 µWb | 0.002 s^2*A/m^2*kg | 1600 m^4*kg^2/s^4*A^2 486 | 1 T | 1 T | 2 kT | 200 µT | 0.002 s^2*A/kg | 1600 kg^2/s^4*A^2 487 | 1 H | 1 H | 2 kH | 200 µH | 0.002 s^2*A^2/m^2*kg | 1600 m^4*kg^2/s^4*A^4 488 | 1 K | 1 K | 2 kK | 200 µK | 0.002 K^-1 | 1600 K^2 489 | 1 cd | 1 cd | 2 kcd | 200 µcd | 0.002 cd^-1 | 1600 cd^2 490 | 1 lx | 1 lx | 2 klx | 200 µlx | 0.002 m^2/cd | 1600 cd^2/m^4 491 | 1 Hz | 1 Hz | 2 kHz | 200 µHz | 2 ms | 1600 s^-2 492 | 1 Gy | 1 Gy | 2 kGy | 200 µGy | 0.002 s^2/m^2 | 1600 m^4/s^4 493 | 1 Gy | 1 Gy | 2 kGy | 200 µGy | 0.002 s^2/m^2 | 1600 m^4/s^4 494 | 1 kat | 1 kat | 2 kkat | 200 µkat | 2 ms/mol | 1600 mol^2/s^2 495 | (31 rows) 496 | 497 | -- test i/o 498 | SELECT 499 | a.u, b.u, (a.u::unit*b.u::unit)::text, (a.u::unit*b.u::unit)::text::unit 500 | FROM 501 | units a CROSS JOIN units b 502 | WHERE (a.u::unit*b.u::unit)::text != (a.u::unit*b.u::unit)::text::unit::text; 503 | u | u | text | unit 504 | ---+---+------+------ 505 | (0 rows) 506 | 507 | -------------------------------------------------------------------------------- /unitparse.l: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016-2018 Christoph Berg 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | */ 14 | 15 | %{ 16 | #include "unit.h" 17 | #include "unitparse.tab.h" 18 | #include 19 | #include /* CStringGetTextDatum */ 20 | #include /* TEXTOID */ 21 | #include /* INFINITY, NAN */ 22 | 23 | #define when(x) if (!strcmp(yytext, x)) 24 | %} 25 | 26 | %option prefix="yyunit" 27 | %option noyywrap 28 | %option nounput 29 | %option noinput 30 | 31 | /* UTF-8 support, see http://stackoverflow.com/questions/9611682/flexlexer-support-for-unicode */ 32 | U [\x80-\xbf] 33 | /* 2-byte UTF-8: \xc2-\xdf excluding SUPER_2, SUPER_3 and SUPER_1 */ 34 | U2_1 \xc2[\x80-\xb1] 35 | U2_2 \xc2[\xb4-\xb8] 36 | U2_3 \xc2[\xba-\xbf] 37 | U2_4 [\xc3-\xdf]{U} 38 | /* 3-byte UTF-8: \xe0-\xef excluding SUPER_0 .. SUPER_MINUS */ 39 | U3_1 [\xe0-\xe1]{U}{U} 40 | U3_2 \xe2\x80{U} 41 | U3_3 \xe2\x81[\x80-\xaf] 42 | U3_4 \xe2\x81[\xbc-\xbf] 43 | U3_5 \xe2[\x82-\xbf]{U} 44 | U3_6 [\xe3-\xef]{U}{U} 45 | /* 4-byte UTF-8: \xf0-\xf4 */ 46 | U4 [\xf0-\xf4]{U}{U}{U} 47 | 48 | UTF8 {U2_1}|{U2_2}|{U2_3}|{U2_4}|{U3_1}|{U3_2}|{U3_3}|{U3_4}|{U3_5}|{U3_6}|{U4} 49 | ALPHA [a-zA-Z$%'"_]|{UTF8} 50 | ALNUM [a-zA-Z$%'"_0-9]|{UTF8} 51 | 52 | DOUBLE_R [0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)? 53 | TIME_R [0-9]+:[0-9]+:[0-9]+(\.[0-9]+)? 54 | UNIT_R {ALPHA}{ALNUM}* 55 | EXP_R \^[\-+]?[0-9]+ 56 | 57 | SUPER_PLUS \xe2\x81\xba 58 | SUPER_MINUS \xe2\x81\xbb 59 | SUPER_0 \xe2\x81\xb0 60 | SUPER_1 \xc2\xb9 61 | SUPER_2 \xc2\xb2 62 | SUPER_3 \xc2\xb3 63 | SUPER_4 \xe2\x81\xb4 64 | SUPER_5 \xe2\x81\xb5 65 | SUPER_6 \xe2\x81\xb6 66 | SUPER_7 \xe2\x81\xb7 67 | SUPER_8 \xe2\x81\xb8 68 | SUPER_9 \xe2\x81\xb9 69 | 70 | %% 71 | 72 | {DOUBLE_R} { 73 | yyunitlval.DOUBLE = atof(yytext); 74 | return DOUBLE; 75 | } 76 | 77 | [Ii][Nn][Ff]([Ii][Nn][Ii][Tt][Yy])? { 78 | yyunitlval.DOUBLE = INFINITY; 79 | return DOUBLE; 80 | } 81 | 82 | [Nn][Aa][Nn] { 83 | yyunitlval.DOUBLE = NAN; 84 | return DOUBLE; 85 | } 86 | 87 | sqrt { 88 | yyunitlval.FUNCTION = FUNCTION_SQRT; 89 | return FUNCTION; 90 | } 91 | 92 | exp { 93 | yyunitlval.FUNCTION = FUNCTION_EXP; 94 | return FUNCTION; 95 | } 96 | 97 | ln { 98 | yyunitlval.FUNCTION = FUNCTION_LN; 99 | return FUNCTION; 100 | } 101 | 102 | log2 { 103 | yyunitlval.FUNCTION = FUNCTION_LOG2; 104 | return FUNCTION; 105 | } 106 | 107 | asin { 108 | yyunitlval.FUNCTION = FUNCTION_ASIN; 109 | return FUNCTION; 110 | } 111 | 112 | tan { 113 | yyunitlval.FUNCTION = FUNCTION_TAN; 114 | return FUNCTION; 115 | } 116 | 117 | {TIME_R} { /* hh:mm:ss[.sss] */ 118 | char *colon; 119 | yyunitlval.DOUBLE = TIME_HOUR * atoi(yytext); /* hh */ 120 | colon = strchr(yytext, ':'); 121 | yyunitlval.DOUBLE += TIME_MINUTE * atoi(colon + 1); /* mm */ 122 | colon = strchr(colon + 1, ':'); 123 | yyunitlval.DOUBLE += atof(colon + 1); /* ss[.sss] */ 124 | return DOUBLE; 125 | } 126 | 127 | {UNIT_R} { 128 | unit_names_t *name; 129 | int ret; 130 | Oid argtypes[1]; 131 | Datum values[1]; 132 | size_t yytext_len; 133 | 134 | /* Check if it's a predefined or previously seen unit */ 135 | name = hash_search(unit_names, yytext, HASH_FIND, NULL); 136 | if (name) 137 | { 138 | elog(DEBUG1, "unit %s found in unit_names hash table", name->name); 139 | yyunitlval.UNIT_SHIFT = name->unit_shift; 140 | return UNIT_SHIFT; 141 | } 142 | 143 | SPI_connect(); 144 | 145 | argtypes[0] = TEXTOID; 146 | values[0] = CStringGetTextDatum(yytext); 147 | 148 | /* look up unit definition without prefix first */ 149 | ret = SPI_execute_with_args("SELECT unit, shift " 150 | "FROM unit_units WHERE " 151 | "name = $1", 152 | 1, /* nargs */ 153 | argtypes, 154 | values, 155 | NULL, /* nulls */ 156 | true, /* read only */ 157 | 0); /* limit */ 158 | if (ret != SPI_OK_SELECT) 159 | elog(ERROR, "internal error determining definition of unit \"%s\"", yytext); 160 | 161 | if (SPI_processed == 1) /* found definition */ 162 | { 163 | Unit *unitp; 164 | Datum shift; 165 | bool is_null; 166 | 167 | unitp = (Unit *) DatumGetPointer(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &is_null)); 168 | if (is_null) 169 | elog(ERROR, "unit \"%s\" definition is NULL", yytext); 170 | yyunitlval.UNIT_SHIFT.unit = *unitp; 171 | 172 | shift = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &is_null); 173 | yyunitlval.UNIT_SHIFT.shift = is_null ? 0.0 : DatumGetFloat8(shift); 174 | 175 | elog(DEBUG1, "unit %s (value %g)", 176 | SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1), 177 | unitp->value); 178 | 179 | goto found; 180 | } 181 | 182 | /* look up definition with prefix */ 183 | ret = SPI_execute_with_args("SELECT prefix, factor, name, unit, shift " 184 | "FROM unit_prefixes CROSS JOIN unit_units WHERE " 185 | "prefix||name = $1 AND " 186 | "$1 LIKE prefix||'%' AND " 187 | "$1 LIKE '%'||name " 188 | "ORDER BY prefix", 189 | 1, /* nargs */ 190 | argtypes, 191 | values, 192 | NULL, /* nulls */ 193 | true, /* read only */ 194 | 0); /* limit */ 195 | if (ret != SPI_OK_SELECT) 196 | elog(ERROR, "internal error determining definition of unit \"%s\"", yytext); 197 | 198 | if (SPI_processed > 2) 199 | { 200 | long processed = SPI_processed; /* SPI_processed was uint32 before 9.6 */ 201 | ereport(ERROR, 202 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 203 | errmsg("unit \"%s\" is ambiguous, %lu prefix/name combinations found", 204 | yytext, processed))); 205 | } 206 | if (SPI_processed == 2) 207 | ereport(ERROR, 208 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 209 | errmsg("unit \"%s\" is ambiguous, \"%s-%s\" vs. \"%s-%s\"", yytext, 210 | SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1), 211 | SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3), 212 | SPI_getvalue(SPI_tuptable->vals[1], SPI_tuptable->tupdesc, 1), 213 | SPI_getvalue(SPI_tuptable->vals[1], SPI_tuptable->tupdesc, 3)))); 214 | 215 | if (SPI_processed == 1) /* found definition */ 216 | { 217 | double factor; 218 | Unit *unitp; 219 | Datum shift; 220 | bool is_null; 221 | 222 | factor = DatumGetFloat8(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &is_null)); /* we discard is_null here */ 223 | unitp = (Unit *) DatumGetPointer(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 4, &is_null)); 224 | if (is_null) 225 | elog(ERROR, "unit \"%s\" definition is NULL", yytext); 226 | 227 | yyunitlval.UNIT_SHIFT.unit = *unitp; 228 | yyunitlval.UNIT_SHIFT.unit.value *= factor; 229 | 230 | shift = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 5, &is_null); 231 | yyunitlval.UNIT_SHIFT.shift = is_null ? 0.0 : DatumGetFloat8(shift); 232 | 233 | goto found; 234 | } 235 | 236 | /* if unit is long enough, look up unit definition with plural 's' removed */ 237 | yytext_len = strlen(yytext); 238 | if (yytext_len >= MIN_PLURAL_LENGTH && yytext[yytext_len - 1] == 's') { 239 | ret = SPI_execute_with_args("SELECT unit, shift " 240 | "FROM unit_units WHERE " 241 | "name = substring($1 for length($1) - 1)", 242 | 1, /* nargs */ 243 | argtypes, 244 | values, 245 | NULL, /* nulls */ 246 | true, /* read only */ 247 | 0); /* limit */ 248 | if (ret != SPI_OK_SELECT) 249 | elog(ERROR, "internal error determining definition of unit \"%s\"", yytext); 250 | 251 | if (SPI_processed == 1) /* found definition */ 252 | { 253 | Unit *unitp; 254 | Datum shift; 255 | bool is_null; 256 | 257 | unitp = (Unit *) DatumGetPointer(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &is_null)); 258 | if (is_null) 259 | elog(ERROR, "unit \"%s\" definition is NULL", yytext); 260 | yyunitlval.UNIT_SHIFT.unit = *unitp; 261 | 262 | shift = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &is_null); 263 | yyunitlval.UNIT_SHIFT.shift = is_null ? 0.0 : DatumGetFloat8(shift); 264 | 265 | elog(DEBUG1, "unit %s (value %g)", 266 | SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1), 267 | unitp->value); 268 | 269 | goto found; 270 | } 271 | 272 | /* look up definition with prefix */ 273 | ret = SPI_execute_with_args("SELECT prefix, factor, name, unit, shift " 274 | "FROM unit_prefixes CROSS JOIN unit_units WHERE " 275 | "prefix||name = substring($1 for length($1) - 1) AND " 276 | "substring($1 for length($1) - 1) LIKE prefix||'%' AND " 277 | "substring($1 for length($1) - 1) LIKE '%'||name " 278 | "ORDER BY prefix", 279 | 1, /* nargs */ 280 | argtypes, 281 | values, 282 | NULL, /* nulls */ 283 | true, /* read only */ 284 | 0); /* limit */ 285 | if (ret != SPI_OK_SELECT) 286 | elog(ERROR, "internal error determining definition of unit \"%s\"", yytext); 287 | 288 | if (SPI_processed > 2) 289 | { 290 | long processed = SPI_processed; /* SPI_processed was uint32 before 9.6 */ 291 | ereport(ERROR, 292 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 293 | errmsg("unit \"%s\" is ambiguous, %lu prefix/name combinations found", 294 | yytext, processed))); 295 | } 296 | if (SPI_processed == 2) 297 | ereport(ERROR, 298 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 299 | errmsg("unit \"%s\" is ambiguous, \"%s-%s\" vs. \"%s-%s\"", yytext, 300 | SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1), 301 | SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3), 302 | SPI_getvalue(SPI_tuptable->vals[1], SPI_tuptable->tupdesc, 1), 303 | SPI_getvalue(SPI_tuptable->vals[1], SPI_tuptable->tupdesc, 3)))); 304 | 305 | if (SPI_processed == 1) /* found definition */ 306 | { 307 | double factor; 308 | Unit *unitp; 309 | Datum shift; 310 | bool is_null; 311 | 312 | factor = DatumGetFloat8(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &is_null)); /* we discard is_null here */ 313 | unitp = (Unit *) DatumGetPointer(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 4, &is_null)); 314 | if (is_null) 315 | elog(ERROR, "unit \"%s\" definition is NULL", yytext); 316 | yyunitlval.UNIT_SHIFT.unit = *unitp; 317 | yyunitlval.UNIT_SHIFT.unit.value *= factor; 318 | 319 | shift = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 5, &is_null); 320 | yyunitlval.UNIT_SHIFT.shift = is_null ? 0.0 : DatumGetFloat8(shift); 321 | 322 | goto found; 323 | } 324 | } 325 | 326 | /* nothing found, error out */ 327 | ereport(ERROR, 328 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 329 | errmsg("unit \"%s\" is not known", yytext))); 330 | 331 | found: 332 | 333 | SPI_finish(); 334 | 335 | /* store computed unit in hash table */ 336 | name = hash_search(unit_names, yytext, HASH_ENTER, NULL); 337 | strlcpy(name->name, yytext, UNIT_NAME_LENGTH); 338 | name->unit_shift = yyunitlval.UNIT_SHIFT; 339 | 340 | return UNIT_SHIFT; 341 | } 342 | 343 | {EXP_R} { 344 | yyunitlval.EXPONENT = atoi(yytext+1); 345 | return EXPONENT; 346 | } 347 | 348 | {SUPER_PLUS} { yyunitlval.SUPER_SIGN = 1; return SUPER_SIGN; } 349 | {SUPER_MINUS} { yyunitlval.SUPER_SIGN = -1; return SUPER_SIGN; } 350 | {SUPER_0} { yyunitlval.SUPER = 0; return SUPER; } 351 | {SUPER_1} { yyunitlval.SUPER = 1; return SUPER; } 352 | {SUPER_2} { yyunitlval.SUPER = 2; return SUPER; } 353 | {SUPER_3} { yyunitlval.SUPER = 3; return SUPER; } 354 | {SUPER_4} { yyunitlval.SUPER = 4; return SUPER; } 355 | {SUPER_5} { yyunitlval.SUPER = 5; return SUPER; } 356 | {SUPER_6} { yyunitlval.SUPER = 6; return SUPER; } 357 | {SUPER_7} { yyunitlval.SUPER = 7; return SUPER; } 358 | {SUPER_8} { yyunitlval.SUPER = 8; return SUPER; } 359 | {SUPER_9} { yyunitlval.SUPER = 9; return SUPER; } 360 | 361 | \+ return '+'; 362 | - return '-'; 363 | \/ return '/'; 364 | \* return '*'; 365 | \| return '|'; 366 | 367 | \( return '('; 368 | \) return ')'; 369 | 370 | [ \t\n]* /* eat whitespace */ 371 | 372 | . return ERR; 373 | -------------------------------------------------------------------------------- /definitions.units.patch: -------------------------------------------------------------------------------- 1 | --- definitions.units 2023-01-04 18:22:06.602713513 +0200 2 | +++ definitions.units.patched 2023-01-04 19:54:54.681679346 +0200 3 | @@ -230,7 +230,7 @@ 4 | # equator to a pole. 5 | 6 | h_SI 6.62607015e-34 7 | -h 6.62607015e-34 J s # Planck constant (exact) 8 | +h 6.62607015e-34 (m^2*kg/s^2) s # Planck constant (exact) 9 | 10 | kg ! # The kilogram, symbol kg, is the SI unit of mass. It is 11 | kilogram kg # defined by taking the fixed numerical value of the Planck 12 | @@ -279,7 +279,7 @@ 13 | # https://en.wikipedia.org/wiki/Kibble_balance 14 | 15 | k_SI 1.380649e-23 16 | -boltzmann 1.380649e-23 J/K # Boltzmann constant (exact) 17 | +boltzmann 1.380649e-23 (m^2*kg/s^2)/K # Boltzmann constant (exact) 18 | k boltzmann 19 | 20 | K ! # The kelvin, symbol K, is the SI unit of thermodynamic 21 | @@ -322,7 +322,7 @@ 22 | # depend directly on temperature. 23 | 24 | e_SI 1.602176634e-19 25 | -e 1.602176634e-19 C # electron charge (exact) 26 | +e 1.602176634e-19 A s # electron charge (exact) 27 | 28 | A ! # The ampere, symbol A, is the SI unit of electric current. 29 | ampere A # It is defined by taking the fixed numerical value of the 30 | @@ -425,7 +425,9 @@ 31 | # A primitive non-SI unit 32 | # 33 | 34 | -bit ! # Basic unit of information (entropy). The entropy in bits 35 | +B ! 36 | +byte B 37 | +bit 1|8 B # Basic unit of information (entropy). The entropy in bits 38 | # of a random variable over a finite alphabet is defined 39 | # to be the sum of -p(i)*log2(p(i)) over the alphabet where 40 | # p(i) is the probability that the random variable takes 41 | @@ -442,6 +444,8 @@ 42 | # # 43 | ########################################################################### 44 | 45 | +quetta- 1e30 46 | +ronna- 1e27 47 | yotta- 1e24 # Greek or Latin octo, "eight" 48 | zetta- 1e21 # Latin septem, "seven" 49 | exa- 1e18 # Greek hex, "six" 50 | @@ -464,6 +468,8 @@ 51 | atto- 1e-18 # Danish-Norwegian atten, "eighteen" 52 | zepto- 1e-21 # Latin septem, "seven" 53 | yocto- 1e-24 # Greek or Latin octo, "eight" 54 | +ronto- 1e-27 55 | +quecto- 1e-30 56 | 57 | quarter- 1|4 58 | semi- 0.5 59 | @@ -482,6 +488,8 @@ 60 | exbi- 2^60 61 | zebi- 2^70 # Zebi- and yobi- were added in the 2005 ed., 62 | yobi- 2^80 # later superseded by ISO/IEC 80000-13:2008. 63 | +robi- 2^90 64 | +quebi- 2^100 65 | Ki- kibi 66 | Mi- mebi 67 | Gi- gibi 68 | @@ -490,7 +498,11 @@ 69 | Ei- exbi 70 | Zi- zebi 71 | Yi- yobi 72 | +Ri- robi 73 | +Qi- quebi 74 | 75 | +Q- quetta 76 | +R- ronna 77 | Y- yotta 78 | Z- zetta 79 | E- exa 80 | @@ -505,12 +517,15 @@ 81 | c- centi 82 | m- milli 83 | u- micro # it should be a mu but u is easy to type 84 | +mu- micro 85 | n- nano 86 | p- pico 87 | f- femto 88 | a- atto 89 | z- zepto 90 | y- yocto 91 | +r- ronto 92 | +q- quecto 93 | 94 | # 95 | # Names of some numbers 96 | @@ -683,6 +698,14 @@ 97 | padm 1e15 98 | shankh 1e17 99 | 100 | +# postgresql-unit: Define some units before they are used elsewhere before their original definition 101 | +pi 3.14159265358979323846 102 | +π pi 103 | +astronomicalunit 149597870700 m # IAU definition from 2012, exact 104 | +au astronomicalunit # ephemeris for the above described 105 | +m2 m^2 106 | + 107 | + 108 | ############################################################################# 109 | # # 110 | # Derived units which can be reduced to the primitive units # 111 | @@ -962,7 +985,7 @@ 112 | pointangle 1|32 circle # Used for reporting compass readings 113 | centrad 0.01 radian # Used for angular deviation of light 114 | # through a prism. 115 | -mas milli arcsec # Used by astronomers 116 | +mas milliarcsec # Used by astronomers 117 | seclongitude circle (seconds/day) # Astronomers measure longitude 118 | # (which they call right ascension) in 119 | # time units by dividing the equator into 120 | @@ -1083,8 +1106,8 @@ 121 | tempF(x) units=[1;K] domain=[-459.67,) range=[0,) \ 122 | (x+(-32)) degF + stdtemp ; (tempF+(-stdtemp))/degF + 32 123 | tempfahrenheit() tempF 124 | -degfahrenheit 5|9 degC 125 | -degF 5|9 degC 126 | +degfahrenheit 5|9 * degC 127 | +degF 5|9 * degC 128 | 129 | 130 | degreesrankine degF # The Rankine scale has the 131 | @@ -1096,7 +1119,7 @@ 132 | 133 | tempreaumur(x) units=[1;K] domain=[-218.52,) range=[0,) \ 134 | x degreaumur+stdtemp ; (tempreaumur+(-stdtemp))/degreaumur 135 | -degreaumur 10|8 degC # The Reaumur scale was used in Europe and 136 | +degreaumur 10|8 * degC # The Reaumur scale was used in Europe and 137 | # particularly in France. It is defined 138 | # to be 0 at the freezing point of water 139 | # and 80 at the boiling point. Reaumur 140 | @@ -1184,7 +1207,7 @@ 141 | 142 | # Basic constants 143 | 144 | -pi 3.14159265358979323846 145 | +#pi 3.14159265358979323846 146 | tau 2 pi 147 | phi (sqrt(5)+1)/2 148 | light c 149 | @@ -1341,6 +1364,15 @@ 150 | H2O50C 0.98807 force gram / cm^3 151 | H2O100C 0.95838 force gram / cm^3 152 | 153 | +# moved because ft is used before being defined (and otherwise parsed as femtotonne) 154 | +inch 2.54 cm 155 | +in inch 156 | +inches inch 157 | +foot 12 inch 158 | +feet foot 159 | +ft foot 160 | +ft3 ft^3 161 | + 162 | # Atomic constants 163 | 164 | hartree 4.3597447222071e-18 J # Approximate electric potential energy 165 | @@ -1715,7 +1747,7 @@ 166 | 167 | abampere 10 A # Current which produces a force of 168 | abamp abampere # 2 dyne/cm between two infinitely 169 | -aA abampere # long wires that are 1 cm apart 170 | +#aA abampere # long wires that are 1 cm apart 171 | abA abampere 172 | biot abampere 173 | Bi biot 174 | @@ -2668,8 +2700,8 @@ 175 | # gravitational constant. This is a 176 | # fictional year, and doesn't 177 | # correspond to any celestial event. 178 | -astronomicalunit 149597870700 m # IAU definition from 2012, exact 179 | -au astronomicalunit # ephemeris for the above described 180 | +#astronomicalunit 149597870700 m # IAU definition from 2012, exact 181 | +#au astronomicalunit # ephemeris for the above described 182 | # astronomical unit. (See the NASA 183 | # site listed above.) 184 | GMsun 132712440041.279419 km^3 / s^2 # heliocentric gravitational constant 185 | @@ -3003,11 +3035,6 @@ 186 | int 3937|1200 ft/m # Convert US Survey measures to 187 | int- int # international measures 188 | 189 | -inch 2.54 cm 190 | -in inch 191 | -foot 12 inch 192 | -feet foot 193 | -ft foot 194 | yard 3 ft 195 | yd yard 196 | mile 5280 ft # The mile was enlarged from 5000 ft 197 | @@ -3155,6 +3182,7 @@ 198 | # Liquid measure 199 | 200 | usgallon 231 in^3 # US liquid measure is derived from 201 | +gallon usgallon 202 | gal gallon # the British wine gallon of 1707. 203 | quart 1|4 gallon # See the "winegallon" entry below 204 | pint 1|2 quart # more historical information. 205 | @@ -3656,7 +3684,7 @@ 206 | 207 | number1can 10 usfloz 208 | number2can 19 usfloz 209 | -number2.5can 3.5 uscups 210 | +number2_5can 3.5 uscups 211 | number3can 4 uscups 212 | number5can 7 uscups 213 | number10can 105 usfloz 214 | @@ -4618,10 +4646,10 @@ 215 | 216 | btu btu_IT # International Table BTU is the default 217 | britishthermalunit btu 218 | -btu_IT cal_IT lb degF / gram K 219 | -btu_th cal_th lb degF / gram K 220 | -btu_mean cal_mean lb degF / gram K 221 | -btu_15 cal_15 lb degF / gram K 222 | +btu_IT cal_IT lb (degF) / gram K 223 | +btu_th cal_th lb (degF) / gram K 224 | +btu_mean cal_mean lb (degF) / gram K 225 | +btu_15 cal_15 lb (degF) / gram K 226 | btu_ISO 1055.06 J # Exact, rounded ISO definition based 227 | # on the IT calorie 228 | quad quadrillion btu 229 | @@ -4784,7 +4812,7 @@ 230 | 231 | # Celsius heat unit: energy to raise a pound of water 1 degC 232 | 233 | -celsiusheatunit cal lb degC / gram K 234 | +celsiusheatunit cal lb (degC) / gram K 235 | chu celsiusheatunit 236 | 237 | # "Apparent" average power in an AC circuit, the product of rms voltage 238 | @@ -4842,13 +4870,13 @@ 239 | Uvalue 1/Rvalue 240 | europeanUvalue watt / m^2 K 241 | RSI degC m^2 / W 242 | -clo 0.155 degC m^2 / W # Supposed to be the insulance 243 | +clo 0.155 * degC m^2 / W # Supposed to be the insulance 244 | # required to keep a resting person 245 | # comfortable indoors. The value 246 | # given is from NIST and the CRC, 247 | # but [5] gives a slightly different 248 | # value of 0.875 ft^2 degF hr / btu. 249 | -tog 0.1 degC m^2 / W # Also used for clothing. 250 | +tog 0.1 * degC m^2 / W # Also used for clothing. 251 | 252 | 253 | # Thermal Conductivity of a few materials 254 | @@ -5352,8 +5380,8 @@ 255 | # to symbols per second. Modern 256 | # modems transmit several bits 257 | # per symbol. 258 | -byte 8 bit # Not all machines had 8 bit 259 | -B byte # bytes, but these days most of 260 | +#byte 8 bit # Not all machines had 8 bit 261 | +#B byte # bytes, but these days most of 262 | # them do. But beware: for 263 | # transmission over modems, a 264 | # few extra bits are used so 265 | @@ -6800,7 +6828,7 @@ 266 | 267 | pa Pa 268 | ev eV 269 | -hg Hg 270 | +#hg Hg 271 | oe Oe 272 | mh mH 273 | rd rod 274 | @@ -7182,7 +7210,8 @@ 275 | Mag Maz gravity # force 276 | Maz Volm kg / oldliter # mass based on water 277 | 278 | -Tm Tim # Abbreviations 279 | +#gross conflict with terameter 280 | +#Tm Tim # Abbreviations 281 | Gf Grafut 282 | Sf Surf 283 | Vm Volm 284 | @@ -7972,7 +8001,7 @@ 285 | ton uston 286 | scruple apscruple 287 | fluidounce usfluidounce 288 | -gallon usgallon 289 | +#gallon usgallon 290 | bushel usbushel 291 | quarter quarterweight 292 | cup uscup 293 | -------------------------------------------------------------------------------- /expected/time.out: -------------------------------------------------------------------------------- 1 | SELECT '1.1 s'::unit; 2 | unit 3 | ------- 4 | 1.1 s 5 | (1 row) 6 | 7 | SELECT '61.1 s'::unit; 8 | unit 9 | -------------- 10 | 00:01:01.1 s 11 | (1 row) 12 | 13 | SELECT '71.1 s'::unit; 14 | unit 15 | -------------- 16 | 00:01:11.1 s 17 | (1 row) 18 | 19 | SELECT '601 s'::unit; 20 | unit 21 | ------------ 22 | 00:10:01 s 23 | (1 row) 24 | 25 | SELECT '3601 s'::unit; 26 | unit 27 | ------------ 28 | 01:00:01 s 29 | (1 row) 30 | 31 | SELECT '86400 s'::unit; 32 | unit 33 | ------ 34 | 1 d 35 | (1 row) 36 | 37 | SELECT '-86400 s'::unit; 38 | unit 39 | ------ 40 | -1 d 41 | (1 row) 42 | 43 | SELECT '86401 s'::unit; 44 | unit 45 | ------------------ 46 | 1 d + 00:00:01 s 47 | (1 row) 48 | 49 | SELECT '-86401 s'::unit; 50 | unit 51 | ------------------- 52 | -1 d - 00:00:01 s 53 | (1 row) 54 | 55 | SELECT '365 d'::unit; 56 | unit 57 | -------------- 58 | 1 commonyear 59 | (1 row) 60 | 61 | SELECT '-365 d'::unit; 62 | unit 63 | --------------- 64 | -1 commonyear 65 | (1 row) 66 | 67 | SELECT '3650 d'::unit; 68 | unit 69 | --------------- 70 | 10 commonyear 71 | (1 row) 72 | 73 | SELECT '3651 d'::unit; 74 | unit 75 | --------------------- 76 | 10 commonyear + 1 d 77 | (1 row) 78 | 79 | SELECT '3651.5 d'::unit; 80 | unit 81 | ---------------------------------- 82 | 10 commonyear + 1 d + 12:00:00 s 83 | (1 row) 84 | 85 | SELECT '00:00:00.1'::unit; 86 | unit 87 | ------ 88 | 0.1 89 | (1 row) 90 | 91 | SELECT '00:00:01'::unit; 92 | unit 93 | ------ 94 | 1 95 | (1 row) 96 | 97 | SELECT '00:01:00'::unit; 98 | unit 99 | ------ 100 | 60 101 | (1 row) 102 | 103 | SELECT '01:00:00'::unit; 104 | unit 105 | ------ 106 | 3600 107 | (1 row) 108 | 109 | SELECT '0:0:0'::unit; 110 | unit 111 | ------ 112 | 0 113 | (1 row) 114 | 115 | SELECT '00:00:01 s'::unit; 116 | unit 117 | ------ 118 | 1 s 119 | (1 row) 120 | 121 | SELECT '00:00:01s'::unit; 122 | unit 123 | ------ 124 | 1 s 125 | (1 row) 126 | 127 | SELECT '00:00:01 m'::unit; 128 | unit 129 | ------ 130 | 1 m 131 | (1 row) 132 | 133 | SELECT '00:01:00 s'::unit; 134 | unit 135 | ------------ 136 | 00:01:00 s 137 | (1 row) 138 | 139 | SELECT '00:01:00.5 s'::unit; 140 | unit 141 | -------------- 142 | 00:01:00.5 s 143 | (1 row) 144 | 145 | SELECT '01:00:00 s'::unit; 146 | unit 147 | ------------ 148 | 01:00:00 s 149 | (1 row) 150 | 151 | SELECT '1 d + 02:03:04.5 s'::unit; 152 | unit 153 | -------------------- 154 | 1 d + 02:03:04.5 s 155 | (1 row) 156 | 157 | SELECT '1.1 d'::unit; -- needs ULP clamping 158 | unit 159 | ------------------ 160 | 1 d + 02:24:00 s 161 | (1 row) 162 | 163 | SELECT name, unit, definition FROM unit_units WHERE dimension(unit) = 'TIME' ORDER BY unit, name COLLATE "C"; 164 | name | unit | definition 165 | ---------------------+-----------------------------------------------+----------------------------------------------- 166 | plancktime | 5.3912464483136e-44 s | hbar / planckenergy 167 | t_P | 5.3912464483136e-44 s | plancktime 168 | plancktime_red | 2.70277015656937e-43 s | hbar / planckenergy_red 169 | atomictime | 24.1888432658633 as | atomicaction / atomicenergy 170 | natural_time | 658.211956950907 as | natural_action / natural_energy 171 | svedberg | 100 fs | 1e-13 s 172 | ㎰ | 1 ps | ps 173 | ㎱ | 1 ns | ns 174 | shake | 10 ns | 1e-8 sec 175 | ㎲ | 1 µs | µs 176 | ㎳ | 1 ms | ms 177 | jiffies | 10 ms | jiffy 178 | jiffy | 10 ms | 0.01 sec 179 | timeatom | 159.574468085106 ms | 1|47 timeounce 180 | Tim | 173.611111111111 ms | 12^-4 hour 181 | blink | 864 ms | 1e-5 day 182 | decimalsecond | 864 ms | 1|100 decimalminute 183 | siderealsecond | 997.269566435185 ms | 1|60 siderealminute 184 | TIME | 1 s | second 185 | s | 1 s | s 186 | sec | 1 s | s 187 | second | 1 s | s 188 | timeounce | 7.5 s | 1|8 timeostent 189 | siderealminute | 59.8361739861111 s | 1|60 siderealhour 190 | min | 00:01:00 s | minute 191 | minute | 00:01:00 s | 60 s 192 | timeostent | 00:01:00 s | 1|60 hour 193 | beat | 00:01:26.4 s | decimalminute 194 | decimalminute | 00:01:26.4 s | 1|100 decimalhour 195 | timeminute | 00:06:00 s | 1|10 hour 196 | timepoint | 00:12:00 s | 1|5 hour 197 | ce | 00:14:24 s | 1e-2 day 198 | bell | 00:30:00 s | 1|8 watch 199 | lunour | 00:59:03.67055555555 s | 1|24 lune 200 | siderealhour | 00:59:50.17043916667 s | 1|24 siderealday 201 | hour | 01:00:00 s | 60 min 202 | hr | 01:00:00 s | hour 203 | decimalhour | 02:24:00 s | 1|10 day 204 | watch | 04:00:00 s | 4 hours 205 | jupiterday | 09:55:30 s | jupiterday_sidereal 206 | jupiterday_sidereal | 09:55:30 s | 9.9250 hr 207 | jupiterday_solar | 09:55:33.24 s | 9.9259 hr 208 | saturnday | 10:39:21.6 s | saturnday_sidereal 209 | saturnday_sidereal | 10:39:21.6 s | 10.656 hr 210 | saturnday_solar | 10:39:21.6 s | 10.656 hr 211 | neptuneday | 16:06:36 s | neptuneday_sidereal 212 | neptuneday_sidereal | 16:06:36 s | 16.11 hr 213 | neptuneday_solar | 16:06:36 s | 16.11 hr 214 | uranusday | 17:14:24 s | uranusday_sidereal 215 | uranusday_sidereal | 17:14:24 s | 17.24 hr 216 | uranusday_solar | 17:14:24 s | 17.24 hr 217 | lune | 23:37:28.0933333333 s | 1|30 lunation 218 | earthday | 23:56:04.09054 s | earthday_sidereal 219 | earthday_sidereal | 23:56:04.09054 s | siderealday 220 | siderealday | 23:56:04.09054 s | 86164.09054 s 221 | d | 1 d | day 222 | da | 1 d | day 223 | day | 1 d | 24 hr 224 | earthday_solar | 1 d | 24 hr 225 | solarday | 1 d | day 226 | ㍲ | 1 d | da 227 | marsday | 1 d + 00:37:22.44 s | marsday_sidereal 228 | marsday_sidereal | 1 d + 00:37:22.44 s | 24.6229 hr 229 | marsday_solar | 1 d + 00:39:34.92 s | 24.6597 hr 230 | plutoday_solar | 6 d + 09:16:55.2 s | 153.2820 hr 231 | plutoday | 6 d + 09:17:34.08 s | plutoday_sidereal 232 | plutoday_sidereal | 6 d + 09:17:34.08 s | 153.2928 hr 233 | sennight | 7 d | 7 day 234 | week | 7 d | 7 day 235 | wk | 7 d | week 236 | fortnight | 14 d | 14 day 237 | draconicmonth | 27 d + 05:05:35.79936 s | nodicalmonth 238 | draconiticmonth | 27 d + 05:05:35.79936 s | nodicalmonth 239 | nodicalmonth | 27 d + 05:05:35.79936 s | 27.2122199 day 240 | siderealmonth | 27 d + 07:43:11.5104 s | 27.321661 day 241 | anomalisticmonth | 27 d + 13:18:33.100128 s | 27.55454977 day 242 | islamicmonth | 29 d + 12:00:00 s | 1|12 islamicyear 243 | lunarmonth | 29 d + 12:44:02.8 s | 29 days + 12 hours + 44 minutes + 2.8 seconds 244 | lunation | 29 d + 12:44:02.8 s | synodicmonth 245 | synodicmonth | 29 d + 12:44:02.8 s | lunarmonth 246 | mo | 30 d + 10:29:03.8312232 s | month 247 | month | 30 d + 10:29:03.8312232 s | 1|12 year 248 | mercuryday | 58 d + 15:36:00 s | mercuryday_sidereal 249 | mercuryday_sidereal | 58 d + 15:36:00 s | 1407.6 hr 250 | mercuryyear | 87 d + 23:15:21.6 s | 87.969 day 251 | venusday_solar | 116 d + 18:00:00 s | 2802.0 hr 252 | mercuryday_solar | 175 d + 22:36:00 s | 4222.6 hr 253 | venusyear | 224 d + 16:49:26.4 s | 224.701 day 254 | venusday | 243 d + 00:36:00 s | venusday_sidereal 255 | venusday_sidereal | 243 d + 00:36:00 s | 5832.6 hr 256 | eclipseyear | 346 d + 14:52:48 s | 346.62 days 257 | islamicyear | 354 d | 354 day 258 | lunaryear | 354 d + 08:48:33.6 s | 12 lunarmonth 259 | islamicleapyear | 355 d | 355 day 260 | calendaryear | 1 commonyear | 365 day 261 | commonyear | 1 commonyear | 365 day 262 | solaryear | 1 commonyear + 05:48:45.9746784 s | year 263 | tropicalyear | 1 commonyear + 05:48:45.9746784 s | 365.242198781 day 264 | year | 1 commonyear + 05:48:45.9746784 s | tropicalyear 265 | yr | 1 commonyear + 05:48:45.9746784 s | year 266 | gregorianyear | 1 commonyear + 05:49:12 s | 365.2425 days 267 | julianyear | 1 commonyear + 06:00:00 s | 365.25 days 268 | earthyear | 1 commonyear + 06:09:09.5400288 s | siderealyear 269 | siderealyear | 1 commonyear + 06:09:09.5400288 s | 365.256360417 day 270 | gaussianyear | 1 commonyear + 06:09:56.0153947 s | (2 pi / gauss_k) days 271 | anomalisticyear | 1 commonyear + 06:13:49.44 s | 365.2596 days 272 | leapyear | 1 commonyear + 1 d | 366 day 273 | marsyear | 1 commonyear + 321 d + 23:31:12 s | 686.980 day 274 | lustrum | 5 commonyear + 1 d + 05:03:49.873392 s | 5 years 275 | decade | 10 commonyear + 2 d + 10:07:39.746784 s | 10 years 276 | jupiteryear | 11 commonyear + 317 d + 14:08:09.6 s | 4332.589 day 277 | saros | 18 commonyear + 15 d + 07:42:24.4 s | 223 synodicmonth 278 | saturnyear | 29 commonyear + 174 d + 05:16:48 s | 10759.22 day 279 | uranusyear | 84 commonyear + 25 d + 09:36:00 s | 30685.4 day 280 | century | 100 commonyear + 24 d + 05:16:37.46784 s | 100 years 281 | neptuneyear | 164 commonyear + 329 d | 60189 day 282 | plutoyear | 248 commonyear + 40 d | 90560 day 283 | millennia | 1000 commonyear + 242 d + 04:46:14.6784 s | millennium 284 | millennium | 1000 commonyear + 242 d + 04:46:14.6784 s | 1000 years 285 | cron | 1.00066e+06 commonyear + 203 d + 18:44:38.4 s | 1e6 years 286 | (120 rows) 287 | 288 | -- units that differ when pushed through output-input functions 289 | -- (same test as in units.sql, but with time_output_custom = true) 290 | SELECT name, unit, unit::text::unit, definition FROM unit_units WHERE unit::text::unit::text <> unit::text; 291 | name | unit | unit | definition 292 | ------+------+------+------------ 293 | (0 rows) 294 | 295 | /* custom time format is only used if dimension is time */ 296 | SELECT '1000 s'::unit, '1000 s/m'::unit; 297 | unit | unit 298 | ------------+-------- 299 | 00:16:40 s | 1 ks/m 300 | (1 row) 301 | 302 | SET unit.time_output_custom = false; 303 | SELECT '1000 s'::unit, '1000 s/m'::unit; 304 | unit | unit 305 | ------+-------- 306 | 1 ks | 1 ks/m 307 | (1 row) 308 | 309 | -- test if 'Gs' is avoided on output 310 | SELECT '1 Gsec'::unit, '1 Gsec/m'::unit; 311 | unit | unit 312 | --------+---------- 313 | 1 Gsec | 1 Gsec/m 314 | (1 row) 315 | 316 | --------------------------------------------------------------------------------