├── .gitignore ├── LICENSE ├── META.json ├── Makefile ├── README.md ├── expected ├── setup.out ├── vec_add.out ├── vec_div.out ├── vec_elements.out ├── vec_mul.out ├── vec_pow.out └── vec_sub.out ├── floatvec--1.0.1--1.1.0.sql ├── floatvec--1.0.1.sql ├── floatvec--1.1.0--1.1.1.sql ├── floatvec--1.1.0.sql ├── floatvec--1.1.1.sql ├── floatvec.c ├── floatvec.control ├── sql ├── setup.sql ├── vec_add.sql ├── vec_div.sql ├── vec_elements.sql ├── vec_mul.sql ├── vec_pow.sql └── vec_sub.sql ├── util.c ├── vec_add.c ├── vec_div.c ├── vec_elements.c ├── vec_mul.c ├── vec_pow.c └── vec_sub.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | .*.swp 4 | .*.swo 5 | /floatvec-*.zip 6 | /results 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Paul A. Jungwirth 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "floatvec", 3 | "abstract": "Functions for arithmetric on numeric arrays", 4 | "version": "1.1.1", 5 | "maintainer": "Paul A. Jungwirth ", 6 | "license": "mit", 7 | "provides": { 8 | "floatvec": { 9 | "abstract": "Functions for arithmetric on numeric arrays", 10 | "file": "floatvec--1.1.1.sql", 11 | "docfile": "README.md", 12 | "version": "1.1.1" 13 | } 14 | }, 15 | "resources": { 16 | "repository": { 17 | "url": "git://github.com/pjungwir/floatvec.git", 18 | "web": "https://github.com/pjungwir/floatvec", 19 | "type": "git" 20 | } 21 | }, 22 | "generated_by": "Paul A. Jungwirth", 23 | "meta-spec": { 24 | "version": "1.0.0", 25 | "url": "http://pgxn.org/meta/spec.txt" 26 | }, 27 | "tags": [ 28 | "math", 29 | "arithmetic", 30 | "arrays", 31 | "vectors" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULES = floatvec 2 | EXTENSION = floatvec 3 | EXTENSION_VERSION = 1.1.1 4 | DATA = floatvec--$(EXTENSION_VERSION).sql floatvec--1.0.1--1.1.0.sql floatvec--1.1.0--1.1.1.sql 5 | 6 | REGRESS = setup \ 7 | vec_add \ 8 | vec_div \ 9 | vec_elements \ 10 | vec_mul \ 11 | vec_pow \ 12 | vec_sub 13 | 14 | PG_CONFIG = pg_config 15 | PGXS := $(shell $(PG_CONFIG) --pgxs) 16 | include $(PGXS) 17 | REGRESS_OPTS = --dbname=$(EXTENSION)_regression # This must come *after* the include since we override the build-in --dbname. 18 | 19 | test: 20 | echo "Run make installcheck to run tests" 21 | exit 1 22 | 23 | release: 24 | git tag v$(EXTENSION_VERSION) 25 | git archive --format zip --prefix=$(EXTENSION)-$(EXTENSION_VERSION)/ --output $(EXTENSION)-$(EXTENSION_VERSION).zip master 26 | 27 | .PHONY: test release 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `floatvec` 2 | ========== 3 | 4 | This Postgres extension provides basic arithmetic functions 5 | for operating on arrays (treated as vectors). 6 | It supports addition, subtraction, multiplication, division, 7 | and raising to a power. 8 | Everything (so far) is element-by-element, 9 | so no dot products or other matrixy stuff. 10 | Every function lets you pass either two arrays of the same length, 11 | or an array and a scalar, 12 | or a scalar and an array. 13 | 14 | Despite the name, we support arguments of any numeric type 15 | (`SMALLINT`, `INTEGER`, `BIGINT`, `REAL`, and `FLOAT` aka `DOUBLE PRECISION`). 16 | The two arguments must be of the same type, 17 | and the result will be of that type too. 18 | 19 | In general if an array argument is `NULL`, 20 | then the whole result will be `NULL`. 21 | If an array *element* is `NULL`, 22 | then the corresponding result element will be `NULL`. 23 | If a scalar argument is `NULL`, 24 | then you'll get an array of the same length as the input, 25 | but all elements will be `NULL`. 26 | 27 | In theory we could define custom operators 28 | so instead of `vec_add(a, b)` you could say `a + b`, 29 | but I'm reluctant to risk conflicts with the widely-used [intarray extension](https://www.postgresql.org/docs/current/static/intarray.html). 30 | If you really want this feature let me know. 31 | 32 | If you like this extension 33 | you might also like [`aggs_for_vecs`](https://github.com/pjungwir/aggs_for_vecs) 34 | and [`aggs_for_arrays`](https://github.com/pjungwir/aggs_for_arrays), 35 | which support more stats-focused activity. 36 | 37 | The functions are: 38 | 39 | `anyarray vec_add(anyarray, anyarray)` 40 | 41 | `anyarray vec_add(anyarray, anyelement)` 42 | 43 | `anyarray vec_add(anyelement, anyarray)` 44 | 45 | `anyarray vec_sub(anyarray, anyarray)` 46 | 47 | `anyarray vec_sub(anyarray, anyelement)` 48 | 49 | `anyarray vec_sub(anyelement, anyarray)` 50 | 51 | `anyarray vec_mul(anyarray, anyarray)` 52 | 53 | `anyarray vec_mul(anyarray, anyelement)` 54 | 55 | `anyarray vec_mul(anyelement, anyarray)` 56 | 57 | `anyarray vec_div(anyarray, anyarray)` 58 | 59 | `anyarray vec_div(anyarray, anyelement)` 60 | 61 | `anyarray vec_div(anyelement, anyarray)` 62 | 63 | `anyarray vec_pow(anyarray, anyarray)` 64 | 65 | `anyarray vec_pow(anyarray, anyelement)` 66 | 67 | `anyarray vec_pow(anyelement, anyarray)` 68 | 69 | ## Author 70 | 71 | Copyright 2017 Paul A. Jungwirth 72 | 73 | -------------------------------------------------------------------------------- /expected/setup.out: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS measurements; 2 | NOTICE: table "measurements" does not exist, skipping 3 | CREATE TABLE measurements ( 4 | sensor_id INTEGER PRIMARY KEY, 5 | floats FLOAT[], 6 | ints INT[] 7 | ); 8 | CREATE INDEX idx_measurements_sensor_id ON measurements (sensor_id); 9 | INSERT INTO measurements 10 | VALUES 11 | (1, NULL, NULL), 12 | (2, ARRAY[NULL, NULL, NULL]::float[], ARRAY[NULL, NULL, NULL]::int[]), 13 | (3, ARRAY[1, 2, 3], ARRAY[1, 2, 3]), 14 | (4, ARRAY[1, NULL, 2], ARRAY[1, NULL, 2]) 15 | ; 16 | CREATE EXTENSION floatvec; 17 | -------------------------------------------------------------------------------- /expected/vec_add.out: -------------------------------------------------------------------------------- 1 | -- float add to scalar lhs 2 | SELECT vec_add(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | vec_add 4 | ------------ 5 | {6,NULL,7} 6 | (1 row) 7 | 8 | -- float add to scalar rhs 9 | SELECT vec_add(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 10 | vec_add 11 | ------------ 12 | {6,NULL,7} 13 | (1 row) 14 | 15 | -- float add to array 16 | SELECT vec_add(floats, floats) FROM measurements WHERE sensor_id IN (4); 17 | vec_add 18 | ------------ 19 | {2,NULL,4} 20 | (1 row) 21 | 22 | -- float add to null scalar lhs 23 | SELECT vec_add(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 24 | vec_add 25 | ------------------ 26 | {NULL,NULL,NULL} 27 | (1 row) 28 | 29 | -- float add to null scalar rhs 30 | SELECT vec_add(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 31 | vec_add 32 | ------------------ 33 | {NULL,NULL,NULL} 34 | (1 row) 35 | 36 | -- float add scalar to null array lhs 37 | SELECT vec_add(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 38 | vec_add 39 | --------- 40 | 41 | (1 row) 42 | 43 | -- float add scalar to null array rhs 44 | SELECT vec_add(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 45 | vec_add 46 | --------- 47 | 48 | (1 row) 49 | 50 | -- float add to null array lhs 51 | SELECT vec_add(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 52 | vec_add 53 | --------- 54 | 55 | (1 row) 56 | 57 | -- float add to null array rhs 58 | SELECT vec_add(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 59 | vec_add 60 | --------- 61 | 62 | (1 row) 63 | 64 | -- null float add to null scalar lhs 65 | SELECT vec_add(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 66 | vec_add 67 | --------- 68 | 69 | (1 row) 70 | 71 | -- null float add to null scalar rhs 72 | SELECT vec_add(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 73 | vec_add 74 | --------- 75 | 76 | (1 row) 77 | 78 | -- null float add to null array 79 | SELECT vec_add(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 80 | vec_add 81 | --------- 82 | 83 | (1 row) 84 | 85 | -- int add to scalar lhs 86 | SELECT vec_add(5::int, ints) FROM measurements WHERE sensor_id IN (4); 87 | vec_add 88 | ------------ 89 | {6,NULL,7} 90 | (1 row) 91 | 92 | -- int add to scalar rhs 93 | SELECT vec_add(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 94 | vec_add 95 | ------------ 96 | {6,NULL,7} 97 | (1 row) 98 | 99 | -- int add to array 100 | SELECT vec_add(ints, ints) FROM measurements WHERE sensor_id IN (4); 101 | vec_add 102 | ------------ 103 | {2,NULL,4} 104 | (1 row) 105 | 106 | -- int add to null scalar lhs 107 | SELECT vec_add(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 108 | vec_add 109 | ------------------ 110 | {NULL,NULL,NULL} 111 | (1 row) 112 | 113 | -- int add to null scalar rhs 114 | SELECT vec_add(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 115 | vec_add 116 | ------------------ 117 | {NULL,NULL,NULL} 118 | (1 row) 119 | 120 | -- int add scalar to null array lhs 121 | SELECT vec_add(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 122 | vec_add 123 | --------- 124 | 125 | (1 row) 126 | 127 | -- int add scalar to null array rhs 128 | SELECT vec_add(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 129 | vec_add 130 | --------- 131 | 132 | (1 row) 133 | 134 | -- int add to null array lhs 135 | SELECT vec_add(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 136 | vec_add 137 | --------- 138 | 139 | (1 row) 140 | 141 | -- int add to null array rhs 142 | SELECT vec_add(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 143 | vec_add 144 | --------- 145 | 146 | (1 row) 147 | 148 | -- null int add to null scalar lhs 149 | SELECT vec_add(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 150 | vec_add 151 | --------- 152 | 153 | (1 row) 154 | 155 | -- null int add to null scalar rhs 156 | SELECT vec_add(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 157 | vec_add 158 | --------- 159 | 160 | (1 row) 161 | 162 | -- null int add to null array 163 | SELECT vec_add(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 164 | vec_add 165 | --------- 166 | 167 | (1 row) 168 | 169 | -------------------------------------------------------------------------------- /expected/vec_div.out: -------------------------------------------------------------------------------- 1 | -- float div to scalar lhs 2 | SELECT vec_div(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | vec_div 4 | -------------- 5 | {5,NULL,2.5} 6 | (1 row) 7 | 8 | -- float div to scalar rhs 9 | SELECT vec_div(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 10 | vec_div 11 | ---------------- 12 | {0.2,NULL,0.4} 13 | (1 row) 14 | 15 | -- float div to array 16 | SELECT vec_div(floats, floats) FROM measurements WHERE sensor_id IN (4); 17 | vec_div 18 | ------------ 19 | {1,NULL,1} 20 | (1 row) 21 | 22 | -- float div to null scalar lhs 23 | SELECT vec_div(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 24 | vec_div 25 | ------------------ 26 | {NULL,NULL,NULL} 27 | (1 row) 28 | 29 | -- float div to null scalar rhs 30 | SELECT vec_div(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 31 | vec_div 32 | ------------------ 33 | {NULL,NULL,NULL} 34 | (1 row) 35 | 36 | -- float div scalar to null array lhs 37 | SELECT vec_div(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 38 | vec_div 39 | --------- 40 | 41 | (1 row) 42 | 43 | -- float div scalar to null array rhs 44 | SELECT vec_div(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 45 | vec_div 46 | --------- 47 | 48 | (1 row) 49 | 50 | -- float div to null array lhs 51 | SELECT vec_div(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 52 | vec_div 53 | --------- 54 | 55 | (1 row) 56 | 57 | -- float div to null array rhs 58 | SELECT vec_div(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 59 | vec_div 60 | --------- 61 | 62 | (1 row) 63 | 64 | -- null float div to null scalar lhs 65 | SELECT vec_div(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 66 | vec_div 67 | --------- 68 | 69 | (1 row) 70 | 71 | -- null float div to null scalar rhs 72 | SELECT vec_div(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 73 | vec_div 74 | --------- 75 | 76 | (1 row) 77 | 78 | -- null float div to null array 79 | SELECT vec_div(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 80 | vec_div 81 | --------- 82 | 83 | (1 row) 84 | 85 | -- int div to scalar lhs 86 | SELECT vec_div(5::int, ints) FROM measurements WHERE sensor_id IN (4); 87 | vec_div 88 | ------------ 89 | {5,NULL,2} 90 | (1 row) 91 | 92 | -- int div to scalar rhs 93 | SELECT vec_div(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 94 | vec_div 95 | ------------ 96 | {0,NULL,0} 97 | (1 row) 98 | 99 | -- int div to array 100 | SELECT vec_div(ints, ints) FROM measurements WHERE sensor_id IN (4); 101 | vec_div 102 | ------------ 103 | {1,NULL,1} 104 | (1 row) 105 | 106 | -- int div to null scalar lhs 107 | SELECT vec_div(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 108 | vec_div 109 | ------------------ 110 | {NULL,NULL,NULL} 111 | (1 row) 112 | 113 | -- int div to null scalar rhs 114 | SELECT vec_div(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 115 | vec_div 116 | ------------------ 117 | {NULL,NULL,NULL} 118 | (1 row) 119 | 120 | -- int div scalar to null array lhs 121 | SELECT vec_div(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 122 | vec_div 123 | --------- 124 | 125 | (1 row) 126 | 127 | -- int div scalar to null array rhs 128 | SELECT vec_div(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 129 | vec_div 130 | --------- 131 | 132 | (1 row) 133 | 134 | -- int div to null array lhs 135 | SELECT vec_div(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 136 | vec_div 137 | --------- 138 | 139 | (1 row) 140 | 141 | -- int div to null array rhs 142 | SELECT vec_div(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 143 | vec_div 144 | --------- 145 | 146 | (1 row) 147 | 148 | -- null int div to null scalar lhs 149 | SELECT vec_div(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 150 | vec_div 151 | --------- 152 | 153 | (1 row) 154 | 155 | -- null int div to null scalar rhs 156 | SELECT vec_div(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 157 | vec_div 158 | --------- 159 | 160 | (1 row) 161 | 162 | -- null int div to null array 163 | SELECT vec_div(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 164 | vec_div 165 | --------- 166 | 167 | (1 row) 168 | 169 | -------------------------------------------------------------------------------- /expected/vec_elements.out: -------------------------------------------------------------------------------- 1 | -- vec_elements with null LHS 2 | SELECT vec_elements(NULL::text[], '{1,2,3}'::int[]); 3 | vec_elements 4 | -------------- 5 | 6 | (1 row) 7 | 8 | -- vec_elements with null RHS 9 | SELECT vec_elements('{a,b,c}'::text[], NULL); 10 | vec_elements 11 | -------------- 12 | 13 | (1 row) 14 | 15 | -- vec_elements with both null 16 | SELECT vec_elements(NULL::text[], NULL); 17 | vec_elements 18 | -------------- 19 | 20 | (1 row) 21 | 22 | -- vec_elements with negative index 23 | SELECT vec_elements('{a,b,c}'::text[], '{-1,2,3}'::int[]); 24 | ERROR: vec_elements indices can't be less than 1, but got -1 25 | -- vec_elements with zero index 26 | SELECT vec_elements('{a,b,c}'::text[], '{0,2,3}'::int[]); 27 | ERROR: vec_elements indices can't be less than 1, but got 0 28 | -- vec_elements with too-large index 29 | SELECT vec_elements('{a,b,c}'::text[], '{1,2,4}'::int[]); 30 | vec_elements 31 | -------------- 32 | {a,b,NULL} 33 | (1 row) 34 | 35 | -- vec_elements with same index twice 36 | SELECT vec_elements('{a,b,c}'::text[], '{1,2,2}'::int[]); 37 | vec_elements 38 | -------------- 39 | {a,b,b} 40 | (1 row) 41 | 42 | -- vec_elements with normal indexes 43 | SELECT vec_elements('{a,b,c}'::text[], '{1,2,3}'::int[]); 44 | vec_elements 45 | -------------- 46 | {a,b,c} 47 | (1 row) 48 | 49 | -- vec_elements with normal indexes backwards 50 | SELECT vec_elements('{a,b,c}'::text[], '{3,2,1}'::int[]); 51 | vec_elements 52 | -------------- 53 | {c,b,a} 54 | (1 row) 55 | 56 | -- vec_elements with empty array on LHS 57 | SELECT vec_elements('{}'::text[], '{1,2,3}'::int[]); 58 | vec_elements 59 | ------------------ 60 | {NULL,NULL,NULL} 61 | (1 row) 62 | 63 | -- vec_elements with empty array on RHS 64 | SELECT vec_elements('{a,b,c}'::text[], '{}'::int[]); 65 | vec_elements 66 | -------------- 67 | {} 68 | (1 row) 69 | 70 | -------------------------------------------------------------------------------- /expected/vec_mul.out: -------------------------------------------------------------------------------- 1 | -- float mul to scalar lhs 2 | SELECT vec_mul(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | vec_mul 4 | ------------- 5 | {5,NULL,10} 6 | (1 row) 7 | 8 | -- float mul to scalar rhs 9 | SELECT vec_mul(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 10 | vec_mul 11 | ------------- 12 | {5,NULL,10} 13 | (1 row) 14 | 15 | -- float mul to array 16 | SELECT vec_mul(floats, floats) FROM measurements WHERE sensor_id IN (4); 17 | vec_mul 18 | ------------ 19 | {1,NULL,4} 20 | (1 row) 21 | 22 | -- float mul to null scalar lhs 23 | SELECT vec_mul(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 24 | vec_mul 25 | ------------------ 26 | {NULL,NULL,NULL} 27 | (1 row) 28 | 29 | -- float mul to null scalar rhs 30 | SELECT vec_mul(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 31 | vec_mul 32 | ------------------ 33 | {NULL,NULL,NULL} 34 | (1 row) 35 | 36 | -- float mul scalar to null array lhs 37 | SELECT vec_mul(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 38 | vec_mul 39 | --------- 40 | 41 | (1 row) 42 | 43 | -- float mul scalar to null array rhs 44 | SELECT vec_mul(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 45 | vec_mul 46 | --------- 47 | 48 | (1 row) 49 | 50 | -- float mul to null array lhs 51 | SELECT vec_mul(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 52 | vec_mul 53 | --------- 54 | 55 | (1 row) 56 | 57 | -- float mul to null array rhs 58 | SELECT vec_mul(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 59 | vec_mul 60 | --------- 61 | 62 | (1 row) 63 | 64 | -- null float mul to null scalar lhs 65 | SELECT vec_mul(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 66 | vec_mul 67 | --------- 68 | 69 | (1 row) 70 | 71 | -- null float mul to null scalar rhs 72 | SELECT vec_mul(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 73 | vec_mul 74 | --------- 75 | 76 | (1 row) 77 | 78 | -- null float mul to null array 79 | SELECT vec_mul(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 80 | vec_mul 81 | --------- 82 | 83 | (1 row) 84 | 85 | -- int mul to scalar lhs 86 | SELECT vec_mul(5::int, ints) FROM measurements WHERE sensor_id IN (4); 87 | vec_mul 88 | ------------- 89 | {5,NULL,10} 90 | (1 row) 91 | 92 | -- int mul to scalar rhs 93 | SELECT vec_mul(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 94 | vec_mul 95 | ------------- 96 | {5,NULL,10} 97 | (1 row) 98 | 99 | -- int mul to array 100 | SELECT vec_mul(ints, ints) FROM measurements WHERE sensor_id IN (4); 101 | vec_mul 102 | ------------ 103 | {1,NULL,4} 104 | (1 row) 105 | 106 | -- int mul to null scalar lhs 107 | SELECT vec_mul(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 108 | vec_mul 109 | ------------------ 110 | {NULL,NULL,NULL} 111 | (1 row) 112 | 113 | -- int mul to null scalar rhs 114 | SELECT vec_mul(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 115 | vec_mul 116 | ------------------ 117 | {NULL,NULL,NULL} 118 | (1 row) 119 | 120 | -- int mul scalar to null array lhs 121 | SELECT vec_mul(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 122 | vec_mul 123 | --------- 124 | 125 | (1 row) 126 | 127 | -- int mul scalar to null array rhs 128 | SELECT vec_mul(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 129 | vec_mul 130 | --------- 131 | 132 | (1 row) 133 | 134 | -- int mul to null array lhs 135 | SELECT vec_mul(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 136 | vec_mul 137 | --------- 138 | 139 | (1 row) 140 | 141 | -- int mul to null array rhs 142 | SELECT vec_mul(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 143 | vec_mul 144 | --------- 145 | 146 | (1 row) 147 | 148 | -- null int mul to null scalar lhs 149 | SELECT vec_mul(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 150 | vec_mul 151 | --------- 152 | 153 | (1 row) 154 | 155 | -- null int mul to null scalar rhs 156 | SELECT vec_mul(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 157 | vec_mul 158 | --------- 159 | 160 | (1 row) 161 | 162 | -- null int mul to null array 163 | SELECT vec_mul(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 164 | vec_mul 165 | --------- 166 | 167 | (1 row) 168 | 169 | -------------------------------------------------------------------------------- /expected/vec_pow.out: -------------------------------------------------------------------------------- 1 | -- float pow to scalar lhs 2 | SELECT vec_pow(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | vec_pow 4 | ------------- 5 | {5,NULL,25} 6 | (1 row) 7 | 8 | -- float pow to scalar rhs 9 | SELECT vec_pow(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 10 | vec_pow 11 | ------------- 12 | {1,NULL,32} 13 | (1 row) 14 | 15 | -- float pow to array 16 | SELECT vec_pow(floats, floats) FROM measurements WHERE sensor_id IN (4); 17 | vec_pow 18 | ------------ 19 | {1,NULL,4} 20 | (1 row) 21 | 22 | -- float pow to null scalar lhs 23 | SELECT vec_pow(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 24 | vec_pow 25 | ------------------ 26 | {NULL,NULL,NULL} 27 | (1 row) 28 | 29 | -- float pow to null scalar rhs 30 | SELECT vec_pow(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 31 | vec_pow 32 | ------------------ 33 | {NULL,NULL,NULL} 34 | (1 row) 35 | 36 | -- float pow scalar to null array lhs 37 | SELECT vec_pow(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 38 | vec_pow 39 | --------- 40 | 41 | (1 row) 42 | 43 | -- float pow scalar to null array rhs 44 | SELECT vec_pow(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 45 | vec_pow 46 | --------- 47 | 48 | (1 row) 49 | 50 | -- float pow to null array lhs 51 | SELECT vec_pow(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 52 | vec_pow 53 | --------- 54 | 55 | (1 row) 56 | 57 | -- float pow to null array rhs 58 | SELECT vec_pow(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 59 | vec_pow 60 | --------- 61 | 62 | (1 row) 63 | 64 | -- null float pow to null scalar lhs 65 | SELECT vec_pow(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 66 | vec_pow 67 | --------- 68 | 69 | (1 row) 70 | 71 | -- null float pow to null scalar rhs 72 | SELECT vec_pow(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 73 | vec_pow 74 | --------- 75 | 76 | (1 row) 77 | 78 | -- null float pow to null array 79 | SELECT vec_pow(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 80 | vec_pow 81 | --------- 82 | 83 | (1 row) 84 | 85 | -- int pow to scalar lhs 86 | SELECT vec_pow(5::int, ints) FROM measurements WHERE sensor_id IN (4); 87 | vec_pow 88 | ------------- 89 | {5,NULL,25} 90 | (1 row) 91 | 92 | -- int pow to scalar rhs 93 | SELECT vec_pow(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 94 | vec_pow 95 | ------------- 96 | {1,NULL,32} 97 | (1 row) 98 | 99 | -- int pow to array 100 | SELECT vec_pow(ints, ints) FROM measurements WHERE sensor_id IN (4); 101 | vec_pow 102 | ------------ 103 | {1,NULL,4} 104 | (1 row) 105 | 106 | -- int pow to null scalar lhs 107 | SELECT vec_pow(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 108 | vec_pow 109 | ------------------ 110 | {NULL,NULL,NULL} 111 | (1 row) 112 | 113 | -- int pow to null scalar rhs 114 | SELECT vec_pow(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 115 | vec_pow 116 | ------------------ 117 | {NULL,NULL,NULL} 118 | (1 row) 119 | 120 | -- int pow scalar to null array lhs 121 | SELECT vec_pow(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 122 | vec_pow 123 | --------- 124 | 125 | (1 row) 126 | 127 | -- int pow scalar to null array rhs 128 | SELECT vec_pow(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 129 | vec_pow 130 | --------- 131 | 132 | (1 row) 133 | 134 | -- int pow to null array lhs 135 | SELECT vec_pow(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 136 | vec_pow 137 | --------- 138 | 139 | (1 row) 140 | 141 | -- int pow to null array rhs 142 | SELECT vec_pow(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 143 | vec_pow 144 | --------- 145 | 146 | (1 row) 147 | 148 | -- null int pow to null scalar lhs 149 | SELECT vec_pow(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 150 | vec_pow 151 | --------- 152 | 153 | (1 row) 154 | 155 | -- null int pow to null scalar rhs 156 | SELECT vec_pow(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 157 | vec_pow 158 | --------- 159 | 160 | (1 row) 161 | 162 | -- null int pow to null array 163 | SELECT vec_pow(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 164 | vec_pow 165 | --------- 166 | 167 | (1 row) 168 | 169 | -------------------------------------------------------------------------------- /expected/vec_sub.out: -------------------------------------------------------------------------------- 1 | -- float sub to scalar lhs 2 | SELECT vec_sub(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | vec_sub 4 | ------------ 5 | {4,NULL,3} 6 | (1 row) 7 | 8 | -- float sub to scalar rhs 9 | SELECT vec_sub(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 10 | vec_sub 11 | -------------- 12 | {-4,NULL,-3} 13 | (1 row) 14 | 15 | -- float sub to array 16 | SELECT vec_sub(floats, floats) FROM measurements WHERE sensor_id IN (4); 17 | vec_sub 18 | ------------ 19 | {0,NULL,0} 20 | (1 row) 21 | 22 | -- float sub to null scalar lhs 23 | SELECT vec_sub(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 24 | vec_sub 25 | ------------------ 26 | {NULL,NULL,NULL} 27 | (1 row) 28 | 29 | -- float sub to null scalar rhs 30 | SELECT vec_sub(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 31 | vec_sub 32 | ------------------ 33 | {NULL,NULL,NULL} 34 | (1 row) 35 | 36 | -- float sub scalar to null array lhs 37 | SELECT vec_sub(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 38 | vec_sub 39 | --------- 40 | 41 | (1 row) 42 | 43 | -- float sub scalar to null array rhs 44 | SELECT vec_sub(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 45 | vec_sub 46 | --------- 47 | 48 | (1 row) 49 | 50 | -- float sub to null array lhs 51 | SELECT vec_sub(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 52 | vec_sub 53 | --------- 54 | 55 | (1 row) 56 | 57 | -- float sub to null array rhs 58 | SELECT vec_sub(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 59 | vec_sub 60 | --------- 61 | 62 | (1 row) 63 | 64 | -- null float sub to null scalar lhs 65 | SELECT vec_sub(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 66 | vec_sub 67 | --------- 68 | 69 | (1 row) 70 | 71 | -- null float sub to null scalar rhs 72 | SELECT vec_sub(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 73 | vec_sub 74 | --------- 75 | 76 | (1 row) 77 | 78 | -- null float sub to null array 79 | SELECT vec_sub(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 80 | vec_sub 81 | --------- 82 | 83 | (1 row) 84 | 85 | -- int sub to scalar lhs 86 | SELECT vec_sub(5::int, ints) FROM measurements WHERE sensor_id IN (4); 87 | vec_sub 88 | ------------ 89 | {4,NULL,3} 90 | (1 row) 91 | 92 | -- int sub to scalar rhs 93 | SELECT vec_sub(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 94 | vec_sub 95 | -------------- 96 | {-4,NULL,-3} 97 | (1 row) 98 | 99 | -- int sub to array 100 | SELECT vec_sub(ints, ints) FROM measurements WHERE sensor_id IN (4); 101 | vec_sub 102 | ------------ 103 | {0,NULL,0} 104 | (1 row) 105 | 106 | -- int sub to null scalar lhs 107 | SELECT vec_sub(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 108 | vec_sub 109 | ------------------ 110 | {NULL,NULL,NULL} 111 | (1 row) 112 | 113 | -- int sub to null scalar rhs 114 | SELECT vec_sub(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 115 | vec_sub 116 | ------------------ 117 | {NULL,NULL,NULL} 118 | (1 row) 119 | 120 | -- int sub scalar to null array lhs 121 | SELECT vec_sub(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 122 | vec_sub 123 | --------- 124 | 125 | (1 row) 126 | 127 | -- int sub scalar to null array rhs 128 | SELECT vec_sub(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 129 | vec_sub 130 | --------- 131 | 132 | (1 row) 133 | 134 | -- int sub to null array lhs 135 | SELECT vec_sub(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 136 | vec_sub 137 | --------- 138 | 139 | (1 row) 140 | 141 | -- int sub to null array rhs 142 | SELECT vec_sub(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 143 | vec_sub 144 | --------- 145 | 146 | (1 row) 147 | 148 | -- null int sub to null scalar lhs 149 | SELECT vec_sub(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 150 | vec_sub 151 | --------- 152 | 153 | (1 row) 154 | 155 | -- null int sub to null scalar rhs 156 | SELECT vec_sub(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 157 | vec_sub 158 | --------- 159 | 160 | (1 row) 161 | 162 | -- null int sub to null array 163 | SELECT vec_sub(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 164 | vec_sub 165 | --------- 166 | 167 | (1 row) 168 | 169 | -------------------------------------------------------------------------------- /floatvec--1.0.1--1.1.0.sql: -------------------------------------------------------------------------------- 1 | /* floatvec--1.0.1--1.1.0.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION floatvec" to load this file. \quit 5 | 6 | -- elements 7 | 8 | CREATE OR REPLACE FUNCTION 9 | vec_elements(anyarray, integer[]) 10 | RETURNS anyarray 11 | AS 'floatvec', 'vec_elements_from_int' 12 | LANGUAGE c; 13 | -------------------------------------------------------------------------------- /floatvec--1.0.1.sql: -------------------------------------------------------------------------------- 1 | /* floatvec--1.0.1.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION floatvec" to load this file. \quit 5 | 6 | 7 | -- add 8 | 9 | CREATE OR REPLACE FUNCTION 10 | vec_add(anyarray, anyarray) 11 | RETURNS anyarray 12 | AS 'floatvec', 'vec_add_with_vec' 13 | LANGUAGE c; 14 | 15 | CREATE OR REPLACE FUNCTION 16 | vec_add(anyarray, anyelement) 17 | RETURNS anyarray 18 | AS 'floatvec', 'vec_add_with_scalar' 19 | LANGUAGE c; 20 | 21 | CREATE OR REPLACE FUNCTION 22 | vec_add(anyelement, anyarray) 23 | RETURNS anyarray 24 | AS 'floatvec', 'vec_add_with_scalar' 25 | LANGUAGE c; 26 | 27 | -- sub 28 | 29 | CREATE OR REPLACE FUNCTION 30 | vec_sub(anyarray, anyarray) 31 | RETURNS anyarray 32 | AS 'floatvec', 'vec_sub_with_vec' 33 | LANGUAGE c; 34 | 35 | CREATE OR REPLACE FUNCTION 36 | vec_sub(anyarray, anyelement) 37 | RETURNS anyarray 38 | AS 'floatvec', 'vec_sub_with_scalar' 39 | LANGUAGE c; 40 | 41 | CREATE OR REPLACE FUNCTION 42 | vec_sub(anyelement, anyarray) 43 | RETURNS anyarray 44 | AS 'floatvec', 'vec_sub_with_scalar' 45 | LANGUAGE c; 46 | 47 | -- mul 48 | 49 | CREATE OR REPLACE FUNCTION 50 | vec_mul(anyarray, anyarray) 51 | RETURNS anyarray 52 | AS 'floatvec', 'vec_mul_with_vec' 53 | LANGUAGE c; 54 | 55 | CREATE OR REPLACE FUNCTION 56 | vec_mul(anyarray, anyelement) 57 | RETURNS anyarray 58 | AS 'floatvec', 'vec_mul_with_scalar' 59 | LANGUAGE c; 60 | 61 | CREATE OR REPLACE FUNCTION 62 | vec_mul(anyelement, anyarray) 63 | RETURNS anyarray 64 | AS 'floatvec', 'vec_mul_with_scalar' 65 | LANGUAGE c; 66 | 67 | -- div 68 | 69 | CREATE OR REPLACE FUNCTION 70 | vec_div(anyarray, anyarray) 71 | RETURNS anyarray 72 | AS 'floatvec', 'vec_div_with_vec' 73 | LANGUAGE c; 74 | 75 | CREATE OR REPLACE FUNCTION 76 | vec_div(anyarray, anyelement) 77 | RETURNS anyarray 78 | AS 'floatvec', 'vec_div_with_scalar' 79 | LANGUAGE c; 80 | 81 | CREATE OR REPLACE FUNCTION 82 | vec_div(anyelement, anyarray) 83 | RETURNS anyarray 84 | AS 'floatvec', 'vec_div_with_scalar' 85 | LANGUAGE c; 86 | 87 | -- pow 88 | 89 | CREATE OR REPLACE FUNCTION 90 | vec_pow(anyarray, anyarray) 91 | RETURNS anyarray 92 | AS 'floatvec', 'vec_pow_with_vec' 93 | LANGUAGE c; 94 | 95 | CREATE OR REPLACE FUNCTION 96 | vec_pow(anyarray, anyelement) 97 | RETURNS anyarray 98 | AS 'floatvec', 'vec_pow_with_scalar' 99 | LANGUAGE c; 100 | 101 | CREATE OR REPLACE FUNCTION 102 | vec_pow(anyelement, anyarray) 103 | RETURNS anyarray 104 | AS 'floatvec', 'vec_pow_with_scalar' 105 | LANGUAGE c; 106 | -------------------------------------------------------------------------------- /floatvec--1.1.0--1.1.1.sql: -------------------------------------------------------------------------------- 1 | /* floatvec--1.1.0--1.1.1.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION floatvec" to load this file. \quit 5 | -------------------------------------------------------------------------------- /floatvec--1.1.0.sql: -------------------------------------------------------------------------------- 1 | /* floatvec--1.1.0.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION floatvec" to load this file. \quit 5 | 6 | 7 | -- add 8 | 9 | CREATE OR REPLACE FUNCTION 10 | vec_add(anyarray, anyarray) 11 | RETURNS anyarray 12 | AS 'floatvec', 'vec_add_with_vec' 13 | LANGUAGE c; 14 | 15 | CREATE OR REPLACE FUNCTION 16 | vec_add(anyarray, anyelement) 17 | RETURNS anyarray 18 | AS 'floatvec', 'vec_add_with_scalar' 19 | LANGUAGE c; 20 | 21 | CREATE OR REPLACE FUNCTION 22 | vec_add(anyelement, anyarray) 23 | RETURNS anyarray 24 | AS 'floatvec', 'vec_add_with_scalar' 25 | LANGUAGE c; 26 | 27 | -- sub 28 | 29 | CREATE OR REPLACE FUNCTION 30 | vec_sub(anyarray, anyarray) 31 | RETURNS anyarray 32 | AS 'floatvec', 'vec_sub_with_vec' 33 | LANGUAGE c; 34 | 35 | CREATE OR REPLACE FUNCTION 36 | vec_sub(anyarray, anyelement) 37 | RETURNS anyarray 38 | AS 'floatvec', 'vec_sub_with_scalar' 39 | LANGUAGE c; 40 | 41 | CREATE OR REPLACE FUNCTION 42 | vec_sub(anyelement, anyarray) 43 | RETURNS anyarray 44 | AS 'floatvec', 'vec_sub_with_scalar' 45 | LANGUAGE c; 46 | 47 | -- mul 48 | 49 | CREATE OR REPLACE FUNCTION 50 | vec_mul(anyarray, anyarray) 51 | RETURNS anyarray 52 | AS 'floatvec', 'vec_mul_with_vec' 53 | LANGUAGE c; 54 | 55 | CREATE OR REPLACE FUNCTION 56 | vec_mul(anyarray, anyelement) 57 | RETURNS anyarray 58 | AS 'floatvec', 'vec_mul_with_scalar' 59 | LANGUAGE c; 60 | 61 | CREATE OR REPLACE FUNCTION 62 | vec_mul(anyelement, anyarray) 63 | RETURNS anyarray 64 | AS 'floatvec', 'vec_mul_with_scalar' 65 | LANGUAGE c; 66 | 67 | -- div 68 | 69 | CREATE OR REPLACE FUNCTION 70 | vec_div(anyarray, anyarray) 71 | RETURNS anyarray 72 | AS 'floatvec', 'vec_div_with_vec' 73 | LANGUAGE c; 74 | 75 | CREATE OR REPLACE FUNCTION 76 | vec_div(anyarray, anyelement) 77 | RETURNS anyarray 78 | AS 'floatvec', 'vec_div_with_scalar' 79 | LANGUAGE c; 80 | 81 | CREATE OR REPLACE FUNCTION 82 | vec_div(anyelement, anyarray) 83 | RETURNS anyarray 84 | AS 'floatvec', 'vec_div_with_scalar' 85 | LANGUAGE c; 86 | 87 | -- pow 88 | 89 | CREATE OR REPLACE FUNCTION 90 | vec_pow(anyarray, anyarray) 91 | RETURNS anyarray 92 | AS 'floatvec', 'vec_pow_with_vec' 93 | LANGUAGE c; 94 | 95 | CREATE OR REPLACE FUNCTION 96 | vec_pow(anyarray, anyelement) 97 | RETURNS anyarray 98 | AS 'floatvec', 'vec_pow_with_scalar' 99 | LANGUAGE c; 100 | 101 | CREATE OR REPLACE FUNCTION 102 | vec_pow(anyelement, anyarray) 103 | RETURNS anyarray 104 | AS 'floatvec', 'vec_pow_with_scalar' 105 | LANGUAGE c; 106 | 107 | -- elements 108 | 109 | CREATE OR REPLACE FUNCTION 110 | vec_elements(anyarray, integer[]) 111 | RETURNS anyarray 112 | AS 'floatvec', 'vec_elements_from_int' 113 | LANGUAGE c; 114 | -------------------------------------------------------------------------------- /floatvec--1.1.1.sql: -------------------------------------------------------------------------------- 1 | /* floatvec--1.1.1.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION floatvec" to load this file. \quit 5 | 6 | 7 | -- add 8 | 9 | CREATE OR REPLACE FUNCTION 10 | vec_add(anyarray, anyarray) 11 | RETURNS anyarray 12 | AS 'floatvec', 'vec_add_with_vec' 13 | LANGUAGE c; 14 | 15 | CREATE OR REPLACE FUNCTION 16 | vec_add(anyarray, anyelement) 17 | RETURNS anyarray 18 | AS 'floatvec', 'vec_add_with_scalar' 19 | LANGUAGE c; 20 | 21 | CREATE OR REPLACE FUNCTION 22 | vec_add(anyelement, anyarray) 23 | RETURNS anyarray 24 | AS 'floatvec', 'vec_add_with_scalar' 25 | LANGUAGE c; 26 | 27 | -- sub 28 | 29 | CREATE OR REPLACE FUNCTION 30 | vec_sub(anyarray, anyarray) 31 | RETURNS anyarray 32 | AS 'floatvec', 'vec_sub_with_vec' 33 | LANGUAGE c; 34 | 35 | CREATE OR REPLACE FUNCTION 36 | vec_sub(anyarray, anyelement) 37 | RETURNS anyarray 38 | AS 'floatvec', 'vec_sub_with_scalar' 39 | LANGUAGE c; 40 | 41 | CREATE OR REPLACE FUNCTION 42 | vec_sub(anyelement, anyarray) 43 | RETURNS anyarray 44 | AS 'floatvec', 'vec_sub_with_scalar' 45 | LANGUAGE c; 46 | 47 | -- mul 48 | 49 | CREATE OR REPLACE FUNCTION 50 | vec_mul(anyarray, anyarray) 51 | RETURNS anyarray 52 | AS 'floatvec', 'vec_mul_with_vec' 53 | LANGUAGE c; 54 | 55 | CREATE OR REPLACE FUNCTION 56 | vec_mul(anyarray, anyelement) 57 | RETURNS anyarray 58 | AS 'floatvec', 'vec_mul_with_scalar' 59 | LANGUAGE c; 60 | 61 | CREATE OR REPLACE FUNCTION 62 | vec_mul(anyelement, anyarray) 63 | RETURNS anyarray 64 | AS 'floatvec', 'vec_mul_with_scalar' 65 | LANGUAGE c; 66 | 67 | -- div 68 | 69 | CREATE OR REPLACE FUNCTION 70 | vec_div(anyarray, anyarray) 71 | RETURNS anyarray 72 | AS 'floatvec', 'vec_div_with_vec' 73 | LANGUAGE c; 74 | 75 | CREATE OR REPLACE FUNCTION 76 | vec_div(anyarray, anyelement) 77 | RETURNS anyarray 78 | AS 'floatvec', 'vec_div_with_scalar' 79 | LANGUAGE c; 80 | 81 | CREATE OR REPLACE FUNCTION 82 | vec_div(anyelement, anyarray) 83 | RETURNS anyarray 84 | AS 'floatvec', 'vec_div_with_scalar' 85 | LANGUAGE c; 86 | 87 | -- pow 88 | 89 | CREATE OR REPLACE FUNCTION 90 | vec_pow(anyarray, anyarray) 91 | RETURNS anyarray 92 | AS 'floatvec', 'vec_pow_with_vec' 93 | LANGUAGE c; 94 | 95 | CREATE OR REPLACE FUNCTION 96 | vec_pow(anyarray, anyelement) 97 | RETURNS anyarray 98 | AS 'floatvec', 'vec_pow_with_scalar' 99 | LANGUAGE c; 100 | 101 | CREATE OR REPLACE FUNCTION 102 | vec_pow(anyelement, anyarray) 103 | RETURNS anyarray 104 | AS 'floatvec', 'vec_pow_with_scalar' 105 | LANGUAGE c; 106 | 107 | -- elements 108 | 109 | CREATE OR REPLACE FUNCTION 110 | vec_elements(anyarray, integer[]) 111 | RETURNS anyarray 112 | AS 'floatvec', 'vec_elements_from_int' 113 | LANGUAGE c; 114 | -------------------------------------------------------------------------------- /floatvec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | PG_MODULE_MAGIC; 9 | 10 | #include "util.c" 11 | #include "vec_add.c" 12 | #include "vec_sub.c" 13 | #include "vec_mul.c" 14 | #include "vec_div.c" 15 | #include "vec_pow.c" 16 | #include "vec_elements.c" 17 | -------------------------------------------------------------------------------- /floatvec.control: -------------------------------------------------------------------------------- 1 | comment = 'Math for vectors (arrays) of numbers' 2 | default_version = '1.1.1' 3 | module_pathname = '$libdir/floatvec' 4 | relocatable = true 5 | -------------------------------------------------------------------------------- /sql/setup.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS measurements; 2 | CREATE TABLE measurements ( 3 | sensor_id INTEGER PRIMARY KEY, 4 | floats FLOAT[], 5 | ints INT[] 6 | ); 7 | CREATE INDEX idx_measurements_sensor_id ON measurements (sensor_id); 8 | 9 | INSERT INTO measurements 10 | VALUES 11 | (1, NULL, NULL), 12 | (2, ARRAY[NULL, NULL, NULL]::float[], ARRAY[NULL, NULL, NULL]::int[]), 13 | (3, ARRAY[1, 2, 3], ARRAY[1, 2, 3]), 14 | (4, ARRAY[1, NULL, 2], ARRAY[1, NULL, 2]) 15 | ; 16 | 17 | CREATE EXTENSION floatvec; 18 | -------------------------------------------------------------------------------- /sql/vec_add.sql: -------------------------------------------------------------------------------- 1 | -- float add to scalar lhs 2 | SELECT vec_add(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | 4 | -- float add to scalar rhs 5 | SELECT vec_add(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 6 | 7 | -- float add to array 8 | SELECT vec_add(floats, floats) FROM measurements WHERE sensor_id IN (4); 9 | 10 | -- float add to null scalar lhs 11 | SELECT vec_add(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 12 | 13 | -- float add to null scalar rhs 14 | SELECT vec_add(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 15 | 16 | -- float add scalar to null array lhs 17 | SELECT vec_add(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 18 | 19 | -- float add scalar to null array rhs 20 | SELECT vec_add(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 21 | 22 | -- float add to null array lhs 23 | SELECT vec_add(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 24 | 25 | -- float add to null array rhs 26 | SELECT vec_add(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 27 | 28 | -- null float add to null scalar lhs 29 | SELECT vec_add(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 30 | 31 | -- null float add to null scalar rhs 32 | SELECT vec_add(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 33 | 34 | -- null float add to null array 35 | SELECT vec_add(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 36 | 37 | -- int add to scalar lhs 38 | SELECT vec_add(5::int, ints) FROM measurements WHERE sensor_id IN (4); 39 | 40 | -- int add to scalar rhs 41 | SELECT vec_add(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 42 | 43 | -- int add to array 44 | SELECT vec_add(ints, ints) FROM measurements WHERE sensor_id IN (4); 45 | 46 | -- int add to null scalar lhs 47 | SELECT vec_add(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 48 | 49 | -- int add to null scalar rhs 50 | SELECT vec_add(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 51 | 52 | -- int add scalar to null array lhs 53 | SELECT vec_add(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 54 | 55 | -- int add scalar to null array rhs 56 | SELECT vec_add(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 57 | 58 | -- int add to null array lhs 59 | SELECT vec_add(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 60 | 61 | -- int add to null array rhs 62 | SELECT vec_add(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 63 | 64 | -- null int add to null scalar lhs 65 | SELECT vec_add(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 66 | 67 | -- null int add to null scalar rhs 68 | SELECT vec_add(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 69 | 70 | -- null int add to null array 71 | SELECT vec_add(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 72 | -------------------------------------------------------------------------------- /sql/vec_div.sql: -------------------------------------------------------------------------------- 1 | -- float div to scalar lhs 2 | SELECT vec_div(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | 4 | -- float div to scalar rhs 5 | SELECT vec_div(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 6 | 7 | -- float div to array 8 | SELECT vec_div(floats, floats) FROM measurements WHERE sensor_id IN (4); 9 | 10 | -- float div to null scalar lhs 11 | SELECT vec_div(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 12 | 13 | -- float div to null scalar rhs 14 | SELECT vec_div(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 15 | 16 | -- float div scalar to null array lhs 17 | SELECT vec_div(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 18 | 19 | -- float div scalar to null array rhs 20 | SELECT vec_div(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 21 | 22 | -- float div to null array lhs 23 | SELECT vec_div(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 24 | 25 | -- float div to null array rhs 26 | SELECT vec_div(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 27 | 28 | -- null float div to null scalar lhs 29 | SELECT vec_div(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 30 | 31 | -- null float div to null scalar rhs 32 | SELECT vec_div(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 33 | 34 | -- null float div to null array 35 | SELECT vec_div(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 36 | 37 | -- int div to scalar lhs 38 | SELECT vec_div(5::int, ints) FROM measurements WHERE sensor_id IN (4); 39 | 40 | -- int div to scalar rhs 41 | SELECT vec_div(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 42 | 43 | -- int div to array 44 | SELECT vec_div(ints, ints) FROM measurements WHERE sensor_id IN (4); 45 | 46 | -- int div to null scalar lhs 47 | SELECT vec_div(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 48 | 49 | -- int div to null scalar rhs 50 | SELECT vec_div(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 51 | 52 | -- int div scalar to null array lhs 53 | SELECT vec_div(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 54 | 55 | -- int div scalar to null array rhs 56 | SELECT vec_div(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 57 | 58 | -- int div to null array lhs 59 | SELECT vec_div(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 60 | 61 | -- int div to null array rhs 62 | SELECT vec_div(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 63 | 64 | -- null int div to null scalar lhs 65 | SELECT vec_div(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 66 | 67 | -- null int div to null scalar rhs 68 | SELECT vec_div(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 69 | 70 | -- null int div to null array 71 | SELECT vec_div(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 72 | -------------------------------------------------------------------------------- /sql/vec_elements.sql: -------------------------------------------------------------------------------- 1 | -- vec_elements with null LHS 2 | SELECT vec_elements(NULL::text[], '{1,2,3}'::int[]); 3 | 4 | -- vec_elements with null RHS 5 | SELECT vec_elements('{a,b,c}'::text[], NULL); 6 | 7 | -- vec_elements with both null 8 | SELECT vec_elements(NULL::text[], NULL); 9 | 10 | -- vec_elements with negative index 11 | SELECT vec_elements('{a,b,c}'::text[], '{-1,2,3}'::int[]); 12 | 13 | -- vec_elements with zero index 14 | SELECT vec_elements('{a,b,c}'::text[], '{0,2,3}'::int[]); 15 | 16 | -- vec_elements with too-large index 17 | SELECT vec_elements('{a,b,c}'::text[], '{1,2,4}'::int[]); 18 | 19 | -- vec_elements with same index twice 20 | SELECT vec_elements('{a,b,c}'::text[], '{1,2,2}'::int[]); 21 | 22 | -- vec_elements with normal indexes 23 | SELECT vec_elements('{a,b,c}'::text[], '{1,2,3}'::int[]); 24 | 25 | -- vec_elements with normal indexes backwards 26 | SELECT vec_elements('{a,b,c}'::text[], '{3,2,1}'::int[]); 27 | 28 | -- vec_elements with empty array on LHS 29 | SELECT vec_elements('{}'::text[], '{1,2,3}'::int[]); 30 | 31 | -- vec_elements with empty array on RHS 32 | SELECT vec_elements('{a,b,c}'::text[], '{}'::int[]); 33 | -------------------------------------------------------------------------------- /sql/vec_mul.sql: -------------------------------------------------------------------------------- 1 | -- float mul to scalar lhs 2 | SELECT vec_mul(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | 4 | -- float mul to scalar rhs 5 | SELECT vec_mul(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 6 | 7 | -- float mul to array 8 | SELECT vec_mul(floats, floats) FROM measurements WHERE sensor_id IN (4); 9 | 10 | -- float mul to null scalar lhs 11 | SELECT vec_mul(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 12 | 13 | -- float mul to null scalar rhs 14 | SELECT vec_mul(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 15 | 16 | -- float mul scalar to null array lhs 17 | SELECT vec_mul(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 18 | 19 | -- float mul scalar to null array rhs 20 | SELECT vec_mul(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 21 | 22 | -- float mul to null array lhs 23 | SELECT vec_mul(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 24 | 25 | -- float mul to null array rhs 26 | SELECT vec_mul(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 27 | 28 | -- null float mul to null scalar lhs 29 | SELECT vec_mul(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 30 | 31 | -- null float mul to null scalar rhs 32 | SELECT vec_mul(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 33 | 34 | -- null float mul to null array 35 | SELECT vec_mul(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 36 | 37 | -- int mul to scalar lhs 38 | SELECT vec_mul(5::int, ints) FROM measurements WHERE sensor_id IN (4); 39 | 40 | -- int mul to scalar rhs 41 | SELECT vec_mul(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 42 | 43 | -- int mul to array 44 | SELECT vec_mul(ints, ints) FROM measurements WHERE sensor_id IN (4); 45 | 46 | -- int mul to null scalar lhs 47 | SELECT vec_mul(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 48 | 49 | -- int mul to null scalar rhs 50 | SELECT vec_mul(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 51 | 52 | -- int mul scalar to null array lhs 53 | SELECT vec_mul(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 54 | 55 | -- int mul scalar to null array rhs 56 | SELECT vec_mul(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 57 | 58 | -- int mul to null array lhs 59 | SELECT vec_mul(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 60 | 61 | -- int mul to null array rhs 62 | SELECT vec_mul(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 63 | 64 | -- null int mul to null scalar lhs 65 | SELECT vec_mul(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 66 | 67 | -- null int mul to null scalar rhs 68 | SELECT vec_mul(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 69 | 70 | -- null int mul to null array 71 | SELECT vec_mul(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 72 | -------------------------------------------------------------------------------- /sql/vec_pow.sql: -------------------------------------------------------------------------------- 1 | -- float pow to scalar lhs 2 | SELECT vec_pow(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | 4 | -- float pow to scalar rhs 5 | SELECT vec_pow(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 6 | 7 | -- float pow to array 8 | SELECT vec_pow(floats, floats) FROM measurements WHERE sensor_id IN (4); 9 | 10 | -- float pow to null scalar lhs 11 | SELECT vec_pow(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 12 | 13 | -- float pow to null scalar rhs 14 | SELECT vec_pow(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 15 | 16 | -- float pow scalar to null array lhs 17 | SELECT vec_pow(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 18 | 19 | -- float pow scalar to null array rhs 20 | SELECT vec_pow(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 21 | 22 | -- float pow to null array lhs 23 | SELECT vec_pow(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 24 | 25 | -- float pow to null array rhs 26 | SELECT vec_pow(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 27 | 28 | -- null float pow to null scalar lhs 29 | SELECT vec_pow(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 30 | 31 | -- null float pow to null scalar rhs 32 | SELECT vec_pow(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 33 | 34 | -- null float pow to null array 35 | SELECT vec_pow(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 36 | 37 | -- int pow to scalar lhs 38 | SELECT vec_pow(5::int, ints) FROM measurements WHERE sensor_id IN (4); 39 | 40 | -- int pow to scalar rhs 41 | SELECT vec_pow(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 42 | 43 | -- int pow to array 44 | SELECT vec_pow(ints, ints) FROM measurements WHERE sensor_id IN (4); 45 | 46 | -- int pow to null scalar lhs 47 | SELECT vec_pow(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 48 | 49 | -- int pow to null scalar rhs 50 | SELECT vec_pow(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 51 | 52 | -- int pow scalar to null array lhs 53 | SELECT vec_pow(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 54 | 55 | -- int pow scalar to null array rhs 56 | SELECT vec_pow(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 57 | 58 | -- int pow to null array lhs 59 | SELECT vec_pow(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 60 | 61 | -- int pow to null array rhs 62 | SELECT vec_pow(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 63 | 64 | -- null int pow to null scalar lhs 65 | SELECT vec_pow(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 66 | 67 | -- null int pow to null scalar rhs 68 | SELECT vec_pow(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 69 | 70 | -- null int pow to null array 71 | SELECT vec_pow(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 72 | -------------------------------------------------------------------------------- /sql/vec_sub.sql: -------------------------------------------------------------------------------- 1 | -- float sub to scalar lhs 2 | SELECT vec_sub(5::float, floats) FROM measurements WHERE sensor_id IN (4); 3 | 4 | -- float sub to scalar rhs 5 | SELECT vec_sub(floats, 5::float) FROM measurements WHERE sensor_id IN (4); 6 | 7 | -- float sub to array 8 | SELECT vec_sub(floats, floats) FROM measurements WHERE sensor_id IN (4); 9 | 10 | -- float sub to null scalar lhs 11 | SELECT vec_sub(NULL::float, floats) FROM measurements WHERE sensor_id IN (4); 12 | 13 | -- float sub to null scalar rhs 14 | SELECT vec_sub(floats, NULL::float) FROM measurements WHERE sensor_id IN (4); 15 | 16 | -- float sub scalar to null array lhs 17 | SELECT vec_sub(5::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 18 | 19 | -- float sub scalar to null array rhs 20 | SELECT vec_sub(NULL::float[], 5::float) FROM measurements WHERE sensor_id IN (4); 21 | 22 | -- float sub to null array lhs 23 | SELECT vec_sub(floats, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 24 | 25 | -- float sub to null array rhs 26 | SELECT vec_sub(NULL::float[], floats) FROM measurements WHERE sensor_id IN (4); 27 | 28 | -- null float sub to null scalar lhs 29 | SELECT vec_sub(NULL::float, NULL::float[]) FROM measurements WHERE sensor_id IN (4); 30 | 31 | -- null float sub to null scalar rhs 32 | SELECT vec_sub(NULL::float[], NULL::float) FROM measurements WHERE sensor_id IN (4); 33 | 34 | -- null float sub to null array 35 | SELECT vec_sub(NULL::float[], NULL::float[]) FROM measurements WHERE sensor_id IN (4); 36 | 37 | -- int sub to scalar lhs 38 | SELECT vec_sub(5::int, ints) FROM measurements WHERE sensor_id IN (4); 39 | 40 | -- int sub to scalar rhs 41 | SELECT vec_sub(ints, 5::int) FROM measurements WHERE sensor_id IN (4); 42 | 43 | -- int sub to array 44 | SELECT vec_sub(ints, ints) FROM measurements WHERE sensor_id IN (4); 45 | 46 | -- int sub to null scalar lhs 47 | SELECT vec_sub(NULL::int, ints) FROM measurements WHERE sensor_id IN (4); 48 | 49 | -- int sub to null scalar rhs 50 | SELECT vec_sub(ints, NULL::int) FROM measurements WHERE sensor_id IN (4); 51 | 52 | -- int sub scalar to null array lhs 53 | SELECT vec_sub(5::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 54 | 55 | -- int sub scalar to null array rhs 56 | SELECT vec_sub(NULL::int[], 5::int) FROM measurements WHERE sensor_id IN (4); 57 | 58 | -- int sub to null array lhs 59 | SELECT vec_sub(ints, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 60 | 61 | -- int sub to null array rhs 62 | SELECT vec_sub(NULL::int[], ints) FROM measurements WHERE sensor_id IN (4); 63 | 64 | -- null int sub to null scalar lhs 65 | SELECT vec_sub(NULL::int, NULL::int[]) FROM measurements WHERE sensor_id IN (4); 66 | 67 | -- null int sub to null scalar rhs 68 | SELECT vec_sub(NULL::int[], NULL::int) FROM measurements WHERE sensor_id IN (4); 69 | 70 | -- null int sub to null array 71 | SELECT vec_sub(NULL::int[], NULL::int[]) FROM measurements WHERE sensor_id IN (4); 72 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * check to see if a float4/8 val has underflowed or overflowed 3 | * (copied from backend/utils/adt/float.c) 4 | */ 5 | #define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \ 6 | do { \ 7 | if (isinf(val) && !(inf_is_valid)) \ 8 | ereport(ERROR, \ 9 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \ 10 | errmsg("value out of range: overflow"))); \ 11 | \ 12 | if ((val) == 0.0 && !(zero_is_valid)) \ 13 | ereport(ERROR, \ 14 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \ 15 | errmsg("value out of range: underflow"))); \ 16 | } while(0) 17 | 18 | typedef union pgnum { 19 | int16 i16; 20 | int32 i32; 21 | int64 i64; 22 | float4 f4; 23 | float8 f8; 24 | } pgnum; 25 | -------------------------------------------------------------------------------- /vec_add.c: -------------------------------------------------------------------------------- 1 | Datum vec_add_with_vec(PG_FUNCTION_ARGS); 2 | PG_FUNCTION_INFO_V1(vec_add_with_vec); 3 | 4 | /** 5 | * Does element-wise addition between two vectors. 6 | * 7 | * Both vectors must be the same length, 8 | * and returns a vector of that length. 9 | * 10 | * If either vector contains a NULL, 11 | * the result is NULL for that position. 12 | * If either input is NULL itself, 13 | * the result is NULL. 14 | * 15 | * by Paul A. Jungwirth 16 | */ 17 | Datum 18 | vec_add_with_vec(PG_FUNCTION_ARGS) 19 | { 20 | Oid elemTypeId; 21 | int16 elemTypeWidth; 22 | bool elemTypeByValue; 23 | char elemTypeAlignmentCode; 24 | int lhsLength; 25 | ArrayType *lhsArray, *rhsArray, *retArray; 26 | Datum *lhsContent, *rhsContent, *retContent; 27 | bool *lhsNulls, *rhsNulls, *retNulls; 28 | int i; 29 | int dims[1]; 30 | int lbs[1]; 31 | 32 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 33 | PG_RETURN_NULL(); 34 | } 35 | 36 | lhsArray = PG_GETARG_ARRAYTYPE_P(0); 37 | rhsArray = PG_GETARG_ARRAYTYPE_P(1); 38 | 39 | if (ARR_NDIM(lhsArray) == 0 || (ARR_NDIM(rhsArray) == 0)) { 40 | PG_RETURN_NULL(); 41 | } 42 | if (ARR_NDIM(lhsArray) > 1 || (ARR_NDIM(rhsArray) > 1)) { 43 | ereport(ERROR, (errmsg("vec_add: one-dimensional arrays are required"))); 44 | } 45 | 46 | elemTypeId = ARR_ELEMTYPE(lhsArray); 47 | 48 | if (elemTypeId != INT2OID && 49 | elemTypeId != INT4OID && 50 | elemTypeId != INT8OID && 51 | elemTypeId != FLOAT4OID && 52 | elemTypeId != FLOAT8OID) { 53 | ereport(ERROR, (errmsg("vec_add input must be array of SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION"))); 54 | } 55 | if (elemTypeId != ARR_ELEMTYPE(rhsArray)) { 56 | ereport(ERROR, (errmsg("vec_add input arrays must be the same type"))); 57 | } 58 | 59 | get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 60 | 61 | deconstruct_array(lhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 62 | &lhsContent, &lhsNulls, &lhsLength); 63 | deconstruct_array(rhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 64 | &rhsContent, &rhsNulls, &lhsLength); 65 | 66 | retContent = palloc0(sizeof(Datum) * lhsLength); 67 | retNulls = palloc0(sizeof(bool) * lhsLength); 68 | 69 | for (i = 0; i < lhsLength; i++) { 70 | if (lhsNulls[i] || rhsNulls[i]) { 71 | retNulls[i] = true; 72 | continue; 73 | } 74 | retNulls[i] = false; 75 | switch(elemTypeId) { 76 | case INT2OID: 77 | retContent[i] = Int16GetDatum(DatumGetInt16(lhsContent[i]) + DatumGetInt16(rhsContent[i])); 78 | break; 79 | case INT4OID: 80 | retContent[i] = Int32GetDatum(DatumGetInt32(lhsContent[i]) + DatumGetInt32(rhsContent[i])); 81 | break; 82 | case INT8OID: 83 | retContent[i] = Int64GetDatum(DatumGetInt64(lhsContent[i]) + DatumGetInt64(rhsContent[i])); 84 | break; 85 | case FLOAT4OID: 86 | retContent[i] = Float4GetDatum(DatumGetFloat4(lhsContent[i]) + DatumGetFloat4(rhsContent[i])); 87 | break; 88 | case FLOAT8OID: 89 | retContent[i] = Float8GetDatum(DatumGetFloat8(lhsContent[i]) + DatumGetFloat8(rhsContent[i])); 90 | break; 91 | } 92 | } 93 | 94 | dims[0] = lhsLength; 95 | lbs[0] = 1; 96 | 97 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 98 | elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 99 | 100 | PG_RETURN_ARRAYTYPE_P(retArray); 101 | } 102 | 103 | 104 | 105 | Datum vec_add_with_scalar(PG_FUNCTION_ARGS); 106 | PG_FUNCTION_INFO_V1(vec_add_with_scalar); 107 | 108 | /** 109 | * Adds a scalar to all elements of a given vector. 110 | * 111 | * If the vector contains a NULL, 112 | * the result is NULL for that position. 113 | * 114 | * If the vector itself is NULL, 115 | * the result is NULL. 116 | * 117 | * If the scalar is NULL, 118 | * then all elements of the resulting vector are NULL. 119 | * 120 | * by Paul A. Jungwirth 121 | */ 122 | Datum 123 | vec_add_with_scalar(PG_FUNCTION_ARGS) 124 | { 125 | Oid elemTypeId1 = get_fn_expr_argtype(fcinfo->flinfo, 0); 126 | Oid elemTypeId2 = get_fn_expr_argtype(fcinfo->flinfo, 1); 127 | Oid scalarTypeId; 128 | int16 elemTypeWidth; 129 | bool elemTypeByValue; 130 | char elemTypeAlignmentCode; 131 | int inputLength; 132 | ArrayType *inputArray, *retArray; 133 | Datum *inputContent, scalarContent, *retContent; 134 | bool *inputNulls, scalarNull, *retNulls; 135 | int arrayPos, scalarPos; 136 | int i; 137 | pgnum scalar; 138 | int dims[1]; 139 | int lbs[1]; 140 | 141 | if (!OidIsValid(elemTypeId1) || !OidIsValid(elemTypeId2)) elog(ERROR, "could not determine data type of input"); 142 | 143 | if (elemTypeId1 == INT2OID || 144 | elemTypeId1 == INT4OID || 145 | elemTypeId1 == INT8OID || 146 | elemTypeId1 == FLOAT4OID || 147 | elemTypeId1 == FLOAT8OID) { 148 | scalarPos = 0; 149 | arrayPos = 1; 150 | scalarTypeId = elemTypeId1; 151 | } else if (elemTypeId2 == INT2OID || 152 | elemTypeId2 == INT4OID || 153 | elemTypeId2 == INT8OID || 154 | elemTypeId2 == FLOAT4OID || 155 | elemTypeId2 == FLOAT8OID) { 156 | scalarPos = 1; 157 | arrayPos = 0; 158 | scalarTypeId = elemTypeId2; 159 | } else { 160 | ereport(ERROR, (errmsg("vec_add scalar operand must be a numeric type"))); 161 | } 162 | 163 | if (PG_ARGISNULL(arrayPos)) { 164 | PG_RETURN_NULL(); 165 | } 166 | inputArray = PG_GETARG_ARRAYTYPE_P(arrayPos); 167 | scalarNull = PG_ARGISNULL(scalarPos); 168 | 169 | if (ARR_ELEMTYPE(inputArray) != scalarTypeId) { 170 | ereport(ERROR, (errmsg("vec_add array elements and scalar operand must be the same type"))); 171 | } 172 | 173 | if (ARR_NDIM(inputArray) != 1) { 174 | ereport(ERROR, (errmsg("vec_add: one-dimensional arrays are required"))); 175 | } 176 | 177 | get_typlenbyvalalign(scalarTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 178 | deconstruct_array(inputArray, scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 179 | &inputContent, &inputNulls, &inputLength); 180 | 181 | retContent = palloc0(sizeof(Datum) * inputLength); 182 | retNulls = palloc0(sizeof(bool) * inputLength); 183 | 184 | if (!scalarNull) { 185 | scalarContent = PG_GETARG_DATUM(scalarPos); 186 | switch(scalarTypeId) { 187 | case INT2OID: 188 | scalar.i16 = DatumGetInt16(scalarContent); 189 | break; 190 | case INT4OID: 191 | scalar.i32 = DatumGetInt32(scalarContent); 192 | break; 193 | case INT8OID: 194 | scalar.i64 = DatumGetInt64(scalarContent); 195 | break; 196 | case FLOAT4OID: 197 | scalar.f4 = DatumGetFloat4(scalarContent); 198 | break; 199 | case FLOAT8OID: 200 | scalar.f8 = DatumGetFloat8(scalarContent); 201 | break; 202 | } 203 | } 204 | for (i = 0; i < inputLength; i++) { 205 | if (scalarNull || inputNulls[i]) { 206 | retNulls[i] = true; 207 | continue; 208 | } 209 | retNulls[i] = false; 210 | switch(scalarTypeId) { 211 | case INT2OID: 212 | retContent[i] = Int16GetDatum(scalar.i16 + DatumGetInt16(inputContent[i])); 213 | break; 214 | case INT4OID: 215 | retContent[i] = Int32GetDatum(scalar.i32 + DatumGetInt32(inputContent[i])); 216 | break; 217 | case INT8OID: 218 | retContent[i] = Int64GetDatum(scalar.i64 + DatumGetInt64(inputContent[i])); 219 | break; 220 | case FLOAT4OID: 221 | retContent[i] = Float4GetDatum(scalar.f4 + DatumGetFloat4(inputContent[i])); 222 | break; 223 | case FLOAT8OID: 224 | retContent[i] = Float8GetDatum(scalar.f8 + DatumGetFloat8(inputContent[i])); 225 | break; 226 | } 227 | } 228 | 229 | dims[0] = inputLength; 230 | lbs[0] = 1; 231 | 232 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 233 | scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 234 | 235 | PG_RETURN_ARRAYTYPE_P(retArray); 236 | } 237 | 238 | -------------------------------------------------------------------------------- /vec_div.c: -------------------------------------------------------------------------------- 1 | Datum vec_div_with_vec(PG_FUNCTION_ARGS); 2 | PG_FUNCTION_INFO_V1(vec_div_with_vec); 3 | 4 | /** 5 | * Does element-wise division between two vectors. 6 | * 7 | * Both vectors must be the same length, 8 | * and returns a vector of that length. 9 | * 10 | * If either vector contains a NULL, 11 | * the result is NULL for that position. 12 | * If either input is NULL itself, 13 | * the result is NULL. 14 | * 15 | * by Paul A. Jungwirth 16 | */ 17 | Datum 18 | vec_div_with_vec(PG_FUNCTION_ARGS) 19 | { 20 | Oid elemTypeId; 21 | int16 elemTypeWidth; 22 | bool elemTypeByValue; 23 | char elemTypeAlignmentCode; 24 | int lhsLength; 25 | ArrayType *lhsArray, *rhsArray, *retArray; 26 | Datum *lhsContent, *rhsContent, *retContent; 27 | bool *lhsNulls, *rhsNulls, *retNulls; 28 | int i; 29 | int dims[1]; 30 | int lbs[1]; 31 | 32 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 33 | PG_RETURN_NULL(); 34 | } 35 | 36 | lhsArray = PG_GETARG_ARRAYTYPE_P(0); 37 | rhsArray = PG_GETARG_ARRAYTYPE_P(1); 38 | 39 | if (ARR_NDIM(lhsArray) == 0 || (ARR_NDIM(rhsArray) == 0)) { 40 | PG_RETURN_NULL(); 41 | } 42 | if (ARR_NDIM(lhsArray) > 1 || (ARR_NDIM(rhsArray) > 1)) { 43 | ereport(ERROR, (errmsg("vec_div: one-dimensional arrays are required"))); 44 | } 45 | 46 | elemTypeId = ARR_ELEMTYPE(lhsArray); 47 | 48 | if (elemTypeId != INT2OID && 49 | elemTypeId != INT4OID && 50 | elemTypeId != INT8OID && 51 | elemTypeId != FLOAT4OID && 52 | elemTypeId != FLOAT8OID) { 53 | ereport(ERROR, (errmsg("vec_div input must be array of SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION"))); 54 | } 55 | if (elemTypeId != ARR_ELEMTYPE(rhsArray)) { 56 | ereport(ERROR, (errmsg("vec_div input arrays must be the same type"))); 57 | } 58 | 59 | get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 60 | 61 | deconstruct_array(lhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 62 | &lhsContent, &lhsNulls, &lhsLength); 63 | deconstruct_array(rhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 64 | &rhsContent, &rhsNulls, &lhsLength); 65 | 66 | retContent = palloc0(sizeof(Datum) * lhsLength); 67 | retNulls = palloc0(sizeof(bool) * lhsLength); 68 | 69 | for (i = 0; i < lhsLength; i++) { 70 | if (lhsNulls[i] || rhsNulls[i]) { 71 | retNulls[i] = true; 72 | continue; 73 | } 74 | retNulls[i] = false; 75 | switch(elemTypeId) { 76 | case INT2OID: 77 | retContent[i] = Int16GetDatum(DatumGetInt16(lhsContent[i]) / DatumGetInt16(rhsContent[i])); 78 | break; 79 | case INT4OID: 80 | retContent[i] = Int32GetDatum(DatumGetInt32(lhsContent[i]) / DatumGetInt32(rhsContent[i])); 81 | break; 82 | case INT8OID: 83 | retContent[i] = Int64GetDatum(DatumGetInt64(lhsContent[i]) / DatumGetInt64(rhsContent[i])); 84 | break; 85 | case FLOAT4OID: 86 | retContent[i] = Float4GetDatum(DatumGetFloat4(lhsContent[i]) / DatumGetFloat4(rhsContent[i])); 87 | break; 88 | case FLOAT8OID: 89 | retContent[i] = Float8GetDatum(DatumGetFloat8(lhsContent[i]) / DatumGetFloat8(rhsContent[i])); 90 | break; 91 | } 92 | } 93 | 94 | dims[0] = lhsLength; 95 | lbs[0] = 1; 96 | 97 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 98 | elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 99 | 100 | PG_RETURN_ARRAYTYPE_P(retArray); 101 | } 102 | 103 | 104 | 105 | Datum vec_div_with_scalar(PG_FUNCTION_ARGS); 106 | PG_FUNCTION_INFO_V1(vec_div_with_scalar); 107 | 108 | /** 109 | * Divides a scalar by/into all elements of a given vector. 110 | * 111 | * If the vector contains a NULL, 112 | * the result is NULL for that position. 113 | * 114 | * If the vector itself is NULL, 115 | * the result is NULL. 116 | * 117 | * If the scalar is NULL, 118 | * then all elements of the resulting vector are NULL. 119 | * 120 | * by Paul A. Jungwirth 121 | */ 122 | Datum 123 | vec_div_with_scalar(PG_FUNCTION_ARGS) 124 | { 125 | Oid elemTypeId1 = get_fn_expr_argtype(fcinfo->flinfo, 0); 126 | Oid elemTypeId2 = get_fn_expr_argtype(fcinfo->flinfo, 1); 127 | Oid scalarTypeId; 128 | int16 elemTypeWidth; 129 | bool elemTypeByValue; 130 | char elemTypeAlignmentCode; 131 | int inputLength; 132 | ArrayType *inputArray, *retArray; 133 | Datum *inputContent, scalarContent, *retContent; 134 | bool *inputNulls, scalarNull, *retNulls; 135 | int arrayPos, scalarPos; 136 | int i; 137 | pgnum scalar; 138 | int dims[1]; 139 | int lbs[1]; 140 | 141 | if (!OidIsValid(elemTypeId1) || !OidIsValid(elemTypeId2)) elog(ERROR, "could not determine data type of input"); 142 | 143 | if (elemTypeId1 == INT2OID || 144 | elemTypeId1 == INT4OID || 145 | elemTypeId1 == INT8OID || 146 | elemTypeId1 == FLOAT4OID || 147 | elemTypeId1 == FLOAT8OID) { 148 | scalarPos = 0; 149 | arrayPos = 1; 150 | scalarTypeId = elemTypeId1; 151 | } else if (elemTypeId2 == INT2OID || 152 | elemTypeId2 == INT4OID || 153 | elemTypeId2 == INT8OID || 154 | elemTypeId2 == FLOAT4OID || 155 | elemTypeId2 == FLOAT8OID) { 156 | scalarPos = 1; 157 | arrayPos = 0; 158 | scalarTypeId = elemTypeId2; 159 | } else { 160 | ereport(ERROR, (errmsg("vec_div scalar operand must be a numeric type"))); 161 | } 162 | 163 | if (PG_ARGISNULL(arrayPos)) { 164 | PG_RETURN_NULL(); 165 | } 166 | inputArray = PG_GETARG_ARRAYTYPE_P(arrayPos); 167 | scalarNull = PG_ARGISNULL(scalarPos); 168 | 169 | if (ARR_ELEMTYPE(inputArray) != scalarTypeId) { 170 | ereport(ERROR, (errmsg("vec_div array elements and scalar operand must be the same type"))); 171 | } 172 | 173 | if (ARR_NDIM(inputArray) != 1) { 174 | ereport(ERROR, (errmsg("vec_div: one-dimensional arrays are required"))); 175 | } 176 | 177 | get_typlenbyvalalign(scalarTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 178 | deconstruct_array(inputArray, scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 179 | &inputContent, &inputNulls, &inputLength); 180 | 181 | retContent = palloc0(sizeof(Datum) * inputLength); 182 | retNulls = palloc0(sizeof(bool) * inputLength); 183 | 184 | if (!scalarNull) { 185 | scalarContent = PG_GETARG_DATUM(scalarPos); 186 | switch(scalarTypeId) { 187 | case INT2OID: 188 | scalar.i16 = DatumGetInt16(scalarContent); 189 | break; 190 | case INT4OID: 191 | scalar.i32 = DatumGetInt32(scalarContent); 192 | break; 193 | case INT8OID: 194 | scalar.i64 = DatumGetInt64(scalarContent); 195 | break; 196 | case FLOAT4OID: 197 | scalar.f4 = DatumGetFloat4(scalarContent); 198 | break; 199 | case FLOAT8OID: 200 | scalar.f8 = DatumGetFloat8(scalarContent); 201 | break; 202 | } 203 | } 204 | for (i = 0; i < inputLength; i++) { 205 | if (scalarNull || inputNulls[i]) { 206 | retNulls[i] = true; 207 | continue; 208 | } 209 | retNulls[i] = false; 210 | if (scalarPos == 0) { 211 | switch(scalarTypeId) { 212 | case INT2OID: 213 | retContent[i] = Int16GetDatum(scalar.i16 / DatumGetInt16(inputContent[i])); 214 | break; 215 | case INT4OID: 216 | retContent[i] = Int32GetDatum(scalar.i32 / DatumGetInt32(inputContent[i])); 217 | break; 218 | case INT8OID: 219 | retContent[i] = Int64GetDatum(scalar.i64 / DatumGetInt64(inputContent[i])); 220 | break; 221 | case FLOAT4OID: 222 | retContent[i] = Float4GetDatum(scalar.f4 / DatumGetFloat4(inputContent[i])); 223 | break; 224 | case FLOAT8OID: 225 | retContent[i] = Float8GetDatum(scalar.f8 / DatumGetFloat8(inputContent[i])); 226 | break; 227 | } 228 | } else { 229 | switch(scalarTypeId) { 230 | case INT2OID: 231 | retContent[i] = Int16GetDatum(DatumGetInt16(inputContent[i]) / scalar.i16); 232 | break; 233 | case INT4OID: 234 | retContent[i] = Int32GetDatum(DatumGetInt32(inputContent[i]) / scalar.i32); 235 | break; 236 | case INT8OID: 237 | retContent[i] = Int64GetDatum(DatumGetInt64(inputContent[i]) / scalar.i64); 238 | break; 239 | case FLOAT4OID: 240 | retContent[i] = Float4GetDatum(DatumGetFloat4(inputContent[i]) / scalar.f4); 241 | break; 242 | case FLOAT8OID: 243 | retContent[i] = Float8GetDatum(DatumGetFloat8(inputContent[i]) / scalar.f8); 244 | break; 245 | } 246 | } 247 | } 248 | 249 | dims[0] = inputLength; 250 | lbs[0] = 1; 251 | 252 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 253 | scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 254 | 255 | PG_RETURN_ARRAYTYPE_P(retArray); 256 | } 257 | 258 | -------------------------------------------------------------------------------- /vec_elements.c: -------------------------------------------------------------------------------- 1 | Datum vec_elements_from_int(PG_FUNCTION_ARGS); 2 | PG_FUNCTION_INFO_V1(vec_elements_from_int); 3 | 4 | /** 5 | * Picks out just the indices you ask for. 6 | * Indexes are 1-based, just like how you'd normally index into a Postgres array. 7 | * 8 | * by Paul A. Jungwirth 9 | */ 10 | Datum 11 | vec_elements_from_int(PG_FUNCTION_ARGS) 12 | { 13 | Oid lhsElemTypeId, rhsElemTypeId; 14 | int16 lhsElemTypeWidth, rhsElemTypeWidth; 15 | bool lhsElemTypeByValue, rhsElemTypeByValue; 16 | char lhsElemTypeAlignmentCode, rhsElemTypeAlignmentCode; 17 | int lhsLength, rhsLength; 18 | ArrayType *lhsArray, *rhsArray, *retArray; 19 | Datum *lhsContent, *rhsContent, *retContent; 20 | bool *lhsNulls, *rhsNulls, *retNulls; 21 | int i; 22 | int dims[1]; 23 | int lbs[1]; 24 | int32 index; 25 | 26 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 27 | PG_RETURN_NULL(); 28 | } 29 | 30 | lhsArray = PG_GETARG_ARRAYTYPE_P(0); 31 | rhsArray = PG_GETARG_ARRAYTYPE_P(1); 32 | 33 | if (ARR_NDIM(lhsArray) > 1 || (ARR_NDIM(rhsArray) > 1)) { 34 | ereport(ERROR, (errmsg("vec_elements: one-dimensional arrays are required"))); 35 | } 36 | 37 | lhsElemTypeId = ARR_ELEMTYPE(lhsArray); 38 | rhsElemTypeId = ARR_ELEMTYPE(rhsArray); 39 | 40 | if (rhsElemTypeId != INT4OID) { 41 | ereport(ERROR, (errmsg("vec_elements index list must be array of INTEGER"))); 42 | } 43 | 44 | get_typlenbyvalalign(lhsElemTypeId, &lhsElemTypeWidth, &lhsElemTypeByValue, &lhsElemTypeAlignmentCode); 45 | get_typlenbyvalalign(rhsElemTypeId, &rhsElemTypeWidth, &rhsElemTypeByValue, &rhsElemTypeAlignmentCode); 46 | 47 | deconstruct_array(lhsArray, lhsElemTypeId, lhsElemTypeWidth, lhsElemTypeByValue, lhsElemTypeAlignmentCode, 48 | &lhsContent, &lhsNulls, &lhsLength); 49 | deconstruct_array(rhsArray, rhsElemTypeId, rhsElemTypeWidth, rhsElemTypeByValue, rhsElemTypeAlignmentCode, 50 | &rhsContent, &rhsNulls, &rhsLength); 51 | 52 | retContent = palloc0(sizeof(Datum) * rhsLength); 53 | retNulls = palloc0(sizeof(bool) * rhsLength); 54 | 55 | for (i = 0; i < rhsLength; i++) { 56 | if (rhsNulls[i]) { 57 | retNulls[i] = true; 58 | continue; 59 | } 60 | index = DatumGetInt32(rhsContent[i]); 61 | if (index < 1) { 62 | ereport(ERROR, (errmsg("vec_elements indices can't be less than 1, but got %d", index))); 63 | } 64 | 65 | if (index > lhsLength) { 66 | retNulls[i] = true; 67 | continue; 68 | } 69 | retNulls[i] = false; 70 | retContent[i] = lhsContent[index - 1]; 71 | } 72 | 73 | dims[0] = rhsLength; 74 | lbs[0] = 1; 75 | 76 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 77 | lhsElemTypeId, lhsElemTypeWidth, lhsElemTypeByValue, lhsElemTypeAlignmentCode); 78 | 79 | PG_RETURN_ARRAYTYPE_P(retArray); 80 | } 81 | -------------------------------------------------------------------------------- /vec_mul.c: -------------------------------------------------------------------------------- 1 | Datum vec_mul_with_vec(PG_FUNCTION_ARGS); 2 | PG_FUNCTION_INFO_V1(vec_mul_with_vec); 3 | 4 | /** 5 | * Does element-wise multiplication between two vectors. 6 | * 7 | * Both vectors must be the same length, 8 | * and returns a vector of that length. 9 | * 10 | * If either vector contains a NULL, 11 | * the result is NULL for that position. 12 | * If either input is NULL itself, 13 | * the result is NULL. 14 | * 15 | * by Paul A. Jungwirth 16 | */ 17 | Datum 18 | vec_mul_with_vec(PG_FUNCTION_ARGS) 19 | { 20 | Oid elemTypeId; 21 | int16 elemTypeWidth; 22 | bool elemTypeByValue; 23 | char elemTypeAlignmentCode; 24 | int lhsLength; 25 | ArrayType *lhsArray, *rhsArray, *retArray; 26 | Datum *lhsContent, *rhsContent, *retContent; 27 | bool *lhsNulls, *rhsNulls, *retNulls; 28 | int i; 29 | int dims[1]; 30 | int lbs[1]; 31 | 32 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 33 | PG_RETURN_NULL(); 34 | } 35 | 36 | lhsArray = PG_GETARG_ARRAYTYPE_P(0); 37 | rhsArray = PG_GETARG_ARRAYTYPE_P(1); 38 | 39 | if (ARR_NDIM(lhsArray) == 0 || (ARR_NDIM(rhsArray) == 0)) { 40 | PG_RETURN_NULL(); 41 | } 42 | if (ARR_NDIM(lhsArray) > 1 || (ARR_NDIM(rhsArray) > 1)) { 43 | ereport(ERROR, (errmsg("vec_mul: one-dimensional arrays are required"))); 44 | } 45 | 46 | elemTypeId = ARR_ELEMTYPE(lhsArray); 47 | 48 | if (elemTypeId != INT2OID && 49 | elemTypeId != INT4OID && 50 | elemTypeId != INT8OID && 51 | elemTypeId != FLOAT4OID && 52 | elemTypeId != FLOAT8OID) { 53 | ereport(ERROR, (errmsg("vec_mul input must be array of SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION"))); 54 | } 55 | if (elemTypeId != ARR_ELEMTYPE(rhsArray)) { 56 | ereport(ERROR, (errmsg("vec_mul input arrays must be the same type"))); 57 | } 58 | 59 | get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 60 | 61 | deconstruct_array(lhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 62 | &lhsContent, &lhsNulls, &lhsLength); 63 | deconstruct_array(rhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 64 | &rhsContent, &rhsNulls, &lhsLength); 65 | 66 | retContent = palloc0(sizeof(Datum) * lhsLength); 67 | retNulls = palloc0(sizeof(bool) * lhsLength); 68 | 69 | for (i = 0; i < lhsLength; i++) { 70 | if (lhsNulls[i] || rhsNulls[i]) { 71 | retNulls[i] = true; 72 | continue; 73 | } 74 | retNulls[i] = false; 75 | switch(elemTypeId) { 76 | case INT2OID: 77 | retContent[i] = Int16GetDatum(DatumGetInt16(lhsContent[i]) * DatumGetInt16(rhsContent[i])); 78 | break; 79 | case INT4OID: 80 | retContent[i] = Int32GetDatum(DatumGetInt32(lhsContent[i]) * DatumGetInt32(rhsContent[i])); 81 | break; 82 | case INT8OID: 83 | retContent[i] = Int64GetDatum(DatumGetInt64(lhsContent[i]) * DatumGetInt64(rhsContent[i])); 84 | break; 85 | case FLOAT4OID: 86 | retContent[i] = Float4GetDatum(DatumGetFloat4(lhsContent[i]) * DatumGetFloat4(rhsContent[i])); 87 | break; 88 | case FLOAT8OID: 89 | retContent[i] = Float8GetDatum(DatumGetFloat8(lhsContent[i]) * DatumGetFloat8(rhsContent[i])); 90 | break; 91 | } 92 | } 93 | 94 | dims[0] = lhsLength; 95 | lbs[0] = 1; 96 | 97 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 98 | elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 99 | 100 | PG_RETURN_ARRAYTYPE_P(retArray); 101 | } 102 | 103 | 104 | 105 | Datum vec_mul_with_scalar(PG_FUNCTION_ARGS); 106 | PG_FUNCTION_INFO_V1(vec_mul_with_scalar); 107 | 108 | /** 109 | * Multiples a scalar by all elements of a given vector. 110 | * 111 | * If the vector contains a NULL, 112 | * the result is NULL for that position. 113 | * 114 | * If the vector itself is NULL, 115 | * the result is NULL. 116 | * 117 | * If the scalar is NULL, 118 | * then all elements of the resulting vector are NULL. 119 | * 120 | * by Paul A. Jungwirth 121 | */ 122 | Datum 123 | vec_mul_with_scalar(PG_FUNCTION_ARGS) 124 | { 125 | Oid elemTypeId1 = get_fn_expr_argtype(fcinfo->flinfo, 0); 126 | Oid elemTypeId2 = get_fn_expr_argtype(fcinfo->flinfo, 1); 127 | Oid scalarTypeId; 128 | int16 elemTypeWidth; 129 | bool elemTypeByValue; 130 | char elemTypeAlignmentCode; 131 | int inputLength; 132 | ArrayType *inputArray, *retArray; 133 | Datum *inputContent, scalarContent, *retContent; 134 | bool *inputNulls, scalarNull, *retNulls; 135 | int arrayPos, scalarPos; 136 | int i; 137 | pgnum scalar; 138 | int dims[1]; 139 | int lbs[1]; 140 | 141 | if (!OidIsValid(elemTypeId1) || !OidIsValid(elemTypeId2)) elog(ERROR, "could not determine data type of input"); 142 | 143 | if (elemTypeId1 == INT2OID || 144 | elemTypeId1 == INT4OID || 145 | elemTypeId1 == INT8OID || 146 | elemTypeId1 == FLOAT4OID || 147 | elemTypeId1 == FLOAT8OID) { 148 | scalarPos = 0; 149 | arrayPos = 1; 150 | scalarTypeId = elemTypeId1; 151 | } else if (elemTypeId2 == INT2OID || 152 | elemTypeId2 == INT4OID || 153 | elemTypeId2 == INT8OID || 154 | elemTypeId2 == FLOAT4OID || 155 | elemTypeId2 == FLOAT8OID) { 156 | scalarPos = 1; 157 | arrayPos = 0; 158 | scalarTypeId = elemTypeId2; 159 | } else { 160 | ereport(ERROR, (errmsg("vec_mul scalar operand must be a numeric type"))); 161 | } 162 | 163 | if (PG_ARGISNULL(arrayPos)) { 164 | PG_RETURN_NULL(); 165 | } 166 | inputArray = PG_GETARG_ARRAYTYPE_P(arrayPos); 167 | scalarNull = PG_ARGISNULL(scalarPos); 168 | 169 | if (ARR_ELEMTYPE(inputArray) != scalarTypeId) { 170 | ereport(ERROR, (errmsg("vec_mul array elements and scalar operand must be the same type"))); 171 | } 172 | 173 | if (ARR_NDIM(inputArray) != 1) { 174 | ereport(ERROR, (errmsg("vec_mul: one-dimensional arrays are required"))); 175 | } 176 | 177 | get_typlenbyvalalign(scalarTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 178 | deconstruct_array(inputArray, scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 179 | &inputContent, &inputNulls, &inputLength); 180 | 181 | retContent = palloc0(sizeof(Datum) * inputLength); 182 | retNulls = palloc0(sizeof(bool) * inputLength); 183 | 184 | if (!scalarNull) { 185 | scalarContent = PG_GETARG_DATUM(scalarPos); 186 | switch(scalarTypeId) { 187 | case INT2OID: 188 | scalar.i16 = DatumGetInt16(scalarContent); 189 | break; 190 | case INT4OID: 191 | scalar.i32 = DatumGetInt32(scalarContent); 192 | break; 193 | case INT8OID: 194 | scalar.i64 = DatumGetInt64(scalarContent); 195 | break; 196 | case FLOAT4OID: 197 | scalar.f4 = DatumGetFloat4(scalarContent); 198 | break; 199 | case FLOAT8OID: 200 | scalar.f8 = DatumGetFloat8(scalarContent); 201 | break; 202 | } 203 | } 204 | for (i = 0; i < inputLength; i++) { 205 | if (scalarNull || inputNulls[i]) { 206 | retNulls[i] = true; 207 | continue; 208 | } 209 | retNulls[i] = false; 210 | switch(scalarTypeId) { 211 | case INT2OID: 212 | retContent[i] = Int16GetDatum(scalar.i16 * DatumGetInt16(inputContent[i])); 213 | break; 214 | case INT4OID: 215 | retContent[i] = Int32GetDatum(scalar.i32 * DatumGetInt32(inputContent[i])); 216 | break; 217 | case INT8OID: 218 | retContent[i] = Int64GetDatum(scalar.i64 * DatumGetInt64(inputContent[i])); 219 | break; 220 | case FLOAT4OID: 221 | retContent[i] = Float4GetDatum(scalar.f4 * DatumGetFloat4(inputContent[i])); 222 | break; 223 | case FLOAT8OID: 224 | retContent[i] = Float8GetDatum(scalar.f8 * DatumGetFloat8(inputContent[i])); 225 | break; 226 | } 227 | } 228 | 229 | dims[0] = inputLength; 230 | lbs[0] = 1; 231 | 232 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 233 | scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 234 | 235 | PG_RETURN_ARRAYTYPE_P(retArray); 236 | } 237 | 238 | -------------------------------------------------------------------------------- /vec_pow.c: -------------------------------------------------------------------------------- 1 | Datum vec_pow_with_vec(PG_FUNCTION_ARGS); 2 | PG_FUNCTION_INFO_V1(vec_pow_with_vec); 3 | 4 | /** 5 | * Does element-wise exponentiation between two vectors. 6 | * 7 | * Both vectors must be the same length, 8 | * and returns a vector of that length. 9 | * 10 | * If either vector contains a NULL, 11 | * the result is NULL for that position. 12 | * If either input is NULL itself, 13 | * the result is NULL. 14 | * 15 | * by Paul A. Jungwirth 16 | */ 17 | Datum 18 | vec_pow_with_vec(PG_FUNCTION_ARGS) 19 | { 20 | Oid elemTypeId; 21 | int16 elemTypeWidth; 22 | bool elemTypeByValue; 23 | char elemTypeAlignmentCode; 24 | int lhsLength; 25 | ArrayType *lhsArray, *rhsArray, *retArray; 26 | Datum *lhsContent, *rhsContent, *retContent; 27 | bool *lhsNulls, *rhsNulls, *retNulls; 28 | int i; 29 | int dims[1]; 30 | int lbs[1]; 31 | 32 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 33 | PG_RETURN_NULL(); 34 | } 35 | 36 | lhsArray = PG_GETARG_ARRAYTYPE_P(0); 37 | rhsArray = PG_GETARG_ARRAYTYPE_P(1); 38 | 39 | if (ARR_NDIM(lhsArray) == 0 || (ARR_NDIM(rhsArray) == 0)) { 40 | PG_RETURN_NULL(); 41 | } 42 | if (ARR_NDIM(lhsArray) > 1 || (ARR_NDIM(rhsArray) > 1)) { 43 | ereport(ERROR, (errmsg("vec_pow: one-dimensional arrays are required"))); 44 | } 45 | 46 | elemTypeId = ARR_ELEMTYPE(lhsArray); 47 | 48 | if (elemTypeId != INT2OID && 49 | elemTypeId != INT4OID && 50 | elemTypeId != INT8OID && 51 | elemTypeId != FLOAT4OID && 52 | elemTypeId != FLOAT8OID) { 53 | ereport(ERROR, (errmsg("vec_pow input must be array of SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION"))); 54 | } 55 | if (elemTypeId != ARR_ELEMTYPE(rhsArray)) { 56 | ereport(ERROR, (errmsg("vec_pow input arrays must be the same type"))); 57 | } 58 | 59 | get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 60 | 61 | deconstruct_array(lhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 62 | &lhsContent, &lhsNulls, &lhsLength); 63 | deconstruct_array(rhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 64 | &rhsContent, &rhsNulls, &lhsLength); 65 | 66 | retContent = palloc0(sizeof(Datum) * lhsLength); 67 | retNulls = palloc0(sizeof(bool) * lhsLength); 68 | 69 | for (i = 0; i < lhsLength; i++) { 70 | if (lhsNulls[i] || rhsNulls[i]) { 71 | retNulls[i] = true; 72 | continue; 73 | } 74 | retNulls[i] = false; 75 | switch(elemTypeId) { 76 | case INT2OID: 77 | retContent[i] = Int16GetDatum(powl(DatumGetInt16(lhsContent[i]), DatumGetInt16(rhsContent[i]))); 78 | break; 79 | case INT4OID: 80 | retContent[i] = Int32GetDatum(powl(DatumGetInt32(lhsContent[i]), DatumGetInt32(rhsContent[i]))); 81 | break; 82 | case INT8OID: 83 | retContent[i] = Int64GetDatum(powl(DatumGetInt64(lhsContent[i]), DatumGetInt64(rhsContent[i]))); 84 | break; 85 | case FLOAT4OID: 86 | retContent[i] = Float4GetDatum(powl(DatumGetFloat4(lhsContent[i]), DatumGetFloat4(rhsContent[i]))); 87 | break; 88 | case FLOAT8OID: 89 | retContent[i] = Float8GetDatum(powl(DatumGetFloat8(lhsContent[i]), DatumGetFloat8(rhsContent[i]))); 90 | break; 91 | } 92 | } 93 | 94 | dims[0] = lhsLength; 95 | lbs[0] = 1; 96 | 97 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 98 | elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 99 | 100 | PG_RETURN_ARRAYTYPE_P(retArray); 101 | } 102 | 103 | 104 | 105 | Datum vec_pow_with_scalar(PG_FUNCTION_ARGS); 106 | PG_FUNCTION_INFO_V1(vec_pow_with_scalar); 107 | 108 | /** 109 | * Raises a scalar to the power of all elements in a given vector, 110 | * or vice versa. 111 | * 112 | * If the vector contains a NULL, 113 | * the result is NULL for that position. 114 | * 115 | * If the vector itself is NULL, 116 | * the result is NULL. 117 | * 118 | * If the scalar is NULL, 119 | * then all elements of the resulting vector are NULL. 120 | * 121 | * by Paul A. Jungwirth 122 | */ 123 | Datum 124 | vec_pow_with_scalar(PG_FUNCTION_ARGS) 125 | { 126 | Oid elemTypeId1 = get_fn_expr_argtype(fcinfo->flinfo, 0); 127 | Oid elemTypeId2 = get_fn_expr_argtype(fcinfo->flinfo, 1); 128 | Oid scalarTypeId; 129 | int16 elemTypeWidth; 130 | bool elemTypeByValue; 131 | char elemTypeAlignmentCode; 132 | int inputLength; 133 | ArrayType *inputArray, *retArray; 134 | Datum *inputContent, scalarContent, *retContent; 135 | bool *inputNulls, scalarNull, *retNulls; 136 | int arrayPos, scalarPos; 137 | int i; 138 | pgnum scalar; 139 | int dims[1]; 140 | int lbs[1]; 141 | 142 | if (!OidIsValid(elemTypeId1) || !OidIsValid(elemTypeId2)) elog(ERROR, "could not determine data type of input"); 143 | 144 | if (elemTypeId1 == INT2OID || 145 | elemTypeId1 == INT4OID || 146 | elemTypeId1 == INT8OID || 147 | elemTypeId1 == FLOAT4OID || 148 | elemTypeId1 == FLOAT8OID) { 149 | scalarPos = 0; 150 | arrayPos = 1; 151 | scalarTypeId = elemTypeId1; 152 | } else if (elemTypeId2 == INT2OID || 153 | elemTypeId2 == INT4OID || 154 | elemTypeId2 == INT8OID || 155 | elemTypeId2 == FLOAT4OID || 156 | elemTypeId2 == FLOAT8OID) { 157 | scalarPos = 1; 158 | arrayPos = 0; 159 | scalarTypeId = elemTypeId2; 160 | } else { 161 | ereport(ERROR, (errmsg("vec_pow scalar operand must be a numeric type"))); 162 | } 163 | 164 | if (PG_ARGISNULL(arrayPos)) { 165 | PG_RETURN_NULL(); 166 | } 167 | inputArray = PG_GETARG_ARRAYTYPE_P(arrayPos); 168 | scalarNull = PG_ARGISNULL(scalarPos); 169 | 170 | if (ARR_ELEMTYPE(inputArray) != scalarTypeId) { 171 | ereport(ERROR, (errmsg("vec_pow array elements and scalar operand must be the same type"))); 172 | } 173 | 174 | if (ARR_NDIM(inputArray) != 1) { 175 | ereport(ERROR, (errmsg("vec_pow: one-dimensional arrays are required"))); 176 | } 177 | 178 | get_typlenbyvalalign(scalarTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 179 | deconstruct_array(inputArray, scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 180 | &inputContent, &inputNulls, &inputLength); 181 | 182 | retContent = palloc0(sizeof(Datum) * inputLength); 183 | retNulls = palloc0(sizeof(bool) * inputLength); 184 | 185 | if (!scalarNull) { 186 | scalarContent = PG_GETARG_DATUM(scalarPos); 187 | switch(scalarTypeId) { 188 | case INT2OID: 189 | scalar.i16 = DatumGetInt16(scalarContent); 190 | break; 191 | case INT4OID: 192 | scalar.i32 = DatumGetInt32(scalarContent); 193 | break; 194 | case INT8OID: 195 | scalar.i64 = DatumGetInt64(scalarContent); 196 | break; 197 | case FLOAT4OID: 198 | scalar.f4 = DatumGetFloat4(scalarContent); 199 | break; 200 | case FLOAT8OID: 201 | scalar.f8 = DatumGetFloat8(scalarContent); 202 | break; 203 | } 204 | } 205 | for (i = 0; i < inputLength; i++) { 206 | if (scalarNull || inputNulls[i]) { 207 | retNulls[i] = true; 208 | continue; 209 | } 210 | retNulls[i] = false; 211 | if (scalarPos == 0) { 212 | switch(scalarTypeId) { 213 | case INT2OID: 214 | retContent[i] = Int16GetDatum(powl(scalar.i16, DatumGetInt16(inputContent[i]))); 215 | break; 216 | case INT4OID: 217 | retContent[i] = Int32GetDatum(powl(scalar.i32, DatumGetInt32(inputContent[i]))); 218 | break; 219 | case INT8OID: 220 | retContent[i] = Int64GetDatum(powl(scalar.i64, DatumGetInt64(inputContent[i]))); 221 | break; 222 | case FLOAT4OID: 223 | retContent[i] = Float4GetDatum(powl(scalar.f4, DatumGetFloat4(inputContent[i]))); 224 | break; 225 | case FLOAT8OID: 226 | retContent[i] = Float8GetDatum(powl(scalar.f8, DatumGetFloat8(inputContent[i]))); 227 | break; 228 | } 229 | } else { 230 | switch(scalarTypeId) { 231 | case INT2OID: 232 | retContent[i] = Int16GetDatum(powl(DatumGetInt16(inputContent[i]), scalar.i16)); 233 | break; 234 | case INT4OID: 235 | retContent[i] = Int32GetDatum(powl(DatumGetInt32(inputContent[i]), scalar.i32)); 236 | break; 237 | case INT8OID: 238 | retContent[i] = Int64GetDatum(powl(DatumGetInt64(inputContent[i]), scalar.i64)); 239 | break; 240 | case FLOAT4OID: 241 | retContent[i] = Float4GetDatum(powl(DatumGetFloat4(inputContent[i]), scalar.f4)); 242 | break; 243 | case FLOAT8OID: 244 | retContent[i] = Float8GetDatum(powl(DatumGetFloat8(inputContent[i]), scalar.f8)); 245 | break; 246 | } 247 | } 248 | } 249 | 250 | dims[0] = inputLength; 251 | lbs[0] = 1; 252 | 253 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 254 | scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 255 | 256 | PG_RETURN_ARRAYTYPE_P(retArray); 257 | } 258 | 259 | -------------------------------------------------------------------------------- /vec_sub.c: -------------------------------------------------------------------------------- 1 | Datum vec_sub_with_vec(PG_FUNCTION_ARGS); 2 | PG_FUNCTION_INFO_V1(vec_sub_with_vec); 3 | 4 | /** 5 | * Does element-wise subtraction between two vectors. 6 | * 7 | * Both vectors must be the same length, 8 | * and returns a vector of that length. 9 | * 10 | * If either vector contains a NULL, 11 | * the result is NULL for that position. 12 | * If either input is NULL itself, 13 | * the result is NULL. 14 | * 15 | * by Paul A. Jungwirth 16 | */ 17 | Datum 18 | vec_sub_with_vec(PG_FUNCTION_ARGS) 19 | { 20 | Oid elemTypeId; 21 | int16 elemTypeWidth; 22 | bool elemTypeByValue; 23 | char elemTypeAlignmentCode; 24 | int lhsLength; 25 | ArrayType *lhsArray, *rhsArray, *retArray; 26 | Datum *lhsContent, *rhsContent, *retContent; 27 | bool *lhsNulls, *rhsNulls, *retNulls; 28 | int i; 29 | int dims[1]; 30 | int lbs[1]; 31 | 32 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 33 | PG_RETURN_NULL(); 34 | } 35 | 36 | lhsArray = PG_GETARG_ARRAYTYPE_P(0); 37 | rhsArray = PG_GETARG_ARRAYTYPE_P(1); 38 | 39 | if (ARR_NDIM(lhsArray) == 0 || (ARR_NDIM(rhsArray) == 0)) { 40 | PG_RETURN_NULL(); 41 | } 42 | if (ARR_NDIM(lhsArray) > 1 || (ARR_NDIM(rhsArray) > 1)) { 43 | ereport(ERROR, (errmsg("vec_sub: one-dimensional arrays are required"))); 44 | } 45 | 46 | elemTypeId = ARR_ELEMTYPE(lhsArray); 47 | 48 | if (elemTypeId != INT2OID && 49 | elemTypeId != INT4OID && 50 | elemTypeId != INT8OID && 51 | elemTypeId != FLOAT4OID && 52 | elemTypeId != FLOAT8OID) { 53 | ereport(ERROR, (errmsg("vec_sub input must be array of SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION"))); 54 | } 55 | if (elemTypeId != ARR_ELEMTYPE(rhsArray)) { 56 | ereport(ERROR, (errmsg("vec_sub input arrays must be the same type"))); 57 | } 58 | 59 | get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 60 | 61 | deconstruct_array(lhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 62 | &lhsContent, &lhsNulls, &lhsLength); 63 | deconstruct_array(rhsArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 64 | &rhsContent, &rhsNulls, &lhsLength); 65 | 66 | retContent = palloc0(sizeof(Datum) * lhsLength); 67 | retNulls = palloc0(sizeof(bool) * lhsLength); 68 | 69 | for (i = 0; i < lhsLength; i++) { 70 | if (lhsNulls[i] || rhsNulls[i]) { 71 | retNulls[i] = true; 72 | continue; 73 | } 74 | retNulls[i] = false; 75 | switch(elemTypeId) { 76 | case INT2OID: 77 | retContent[i] = Int16GetDatum(DatumGetInt16(lhsContent[i]) - DatumGetInt16(rhsContent[i])); 78 | break; 79 | case INT4OID: 80 | retContent[i] = Int32GetDatum(DatumGetInt32(lhsContent[i]) - DatumGetInt32(rhsContent[i])); 81 | break; 82 | case INT8OID: 83 | retContent[i] = Int64GetDatum(DatumGetInt64(lhsContent[i]) - DatumGetInt64(rhsContent[i])); 84 | break; 85 | case FLOAT4OID: 86 | retContent[i] = Float4GetDatum(DatumGetFloat4(lhsContent[i]) - DatumGetFloat4(rhsContent[i])); 87 | break; 88 | case FLOAT8OID: 89 | retContent[i] = Float8GetDatum(DatumGetFloat8(lhsContent[i]) - DatumGetFloat8(rhsContent[i])); 90 | break; 91 | } 92 | } 93 | 94 | dims[0] = lhsLength; 95 | lbs[0] = 1; 96 | 97 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 98 | elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 99 | 100 | PG_RETURN_ARRAYTYPE_P(retArray); 101 | } 102 | 103 | 104 | 105 | Datum vec_sub_with_scalar(PG_FUNCTION_ARGS); 106 | PG_FUNCTION_INFO_V1(vec_sub_with_scalar); 107 | 108 | /** 109 | * Subtracts a scalar to/from all elements of a given vector. 110 | * 111 | * If the vector contains a NULL, 112 | * the result is NULL for that position. 113 | * 114 | * If the vector itself is NULL, 115 | * the result is NULL. 116 | * 117 | * If the scalar is NULL, 118 | * then all elements of the resulting vector are NULL. 119 | * 120 | * by Paul A. Jungwirth 121 | */ 122 | Datum 123 | vec_sub_with_scalar(PG_FUNCTION_ARGS) 124 | { 125 | Oid elemTypeId1 = get_fn_expr_argtype(fcinfo->flinfo, 0); 126 | Oid elemTypeId2 = get_fn_expr_argtype(fcinfo->flinfo, 1); 127 | Oid scalarTypeId; 128 | int16 elemTypeWidth; 129 | bool elemTypeByValue; 130 | char elemTypeAlignmentCode; 131 | int inputLength; 132 | ArrayType *inputArray, *retArray; 133 | Datum *inputContent, scalarContent, *retContent; 134 | bool *inputNulls, scalarNull, *retNulls; 135 | int arrayPos, scalarPos; 136 | int i; 137 | pgnum scalar; 138 | int dims[1]; 139 | int lbs[1]; 140 | 141 | if (!OidIsValid(elemTypeId1) || !OidIsValid(elemTypeId2)) elog(ERROR, "could not determine data type of input"); 142 | 143 | if (elemTypeId1 == INT2OID || 144 | elemTypeId1 == INT4OID || 145 | elemTypeId1 == INT8OID || 146 | elemTypeId1 == FLOAT4OID || 147 | elemTypeId1 == FLOAT8OID) { 148 | scalarPos = 0; 149 | arrayPos = 1; 150 | scalarTypeId = elemTypeId1; 151 | } else if (elemTypeId2 == INT2OID || 152 | elemTypeId2 == INT4OID || 153 | elemTypeId2 == INT8OID || 154 | elemTypeId2 == FLOAT4OID || 155 | elemTypeId2 == FLOAT8OID) { 156 | scalarPos = 1; 157 | arrayPos = 0; 158 | scalarTypeId = elemTypeId2; 159 | } else { 160 | ereport(ERROR, (errmsg("vec_sub scalar operand must be a numeric type"))); 161 | } 162 | 163 | if (PG_ARGISNULL(arrayPos)) { 164 | PG_RETURN_NULL(); 165 | } 166 | inputArray = PG_GETARG_ARRAYTYPE_P(arrayPos); 167 | scalarNull = PG_ARGISNULL(scalarPos); 168 | 169 | if (ARR_ELEMTYPE(inputArray) != scalarTypeId) { 170 | ereport(ERROR, (errmsg("vec_sub array elements and scalar operand must be the same type"))); 171 | } 172 | 173 | if (ARR_NDIM(inputArray) != 1) { 174 | ereport(ERROR, (errmsg("vec_sub: one-dimensional arrays are required"))); 175 | } 176 | 177 | get_typlenbyvalalign(scalarTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode); 178 | deconstruct_array(inputArray, scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode, 179 | &inputContent, &inputNulls, &inputLength); 180 | 181 | retContent = palloc0(sizeof(Datum) * inputLength); 182 | retNulls = palloc0(sizeof(bool) * inputLength); 183 | 184 | if (!scalarNull) { 185 | scalarContent = PG_GETARG_DATUM(scalarPos); 186 | switch(scalarTypeId) { 187 | case INT2OID: 188 | scalar.i16 = DatumGetInt16(scalarContent); 189 | break; 190 | case INT4OID: 191 | scalar.i32 = DatumGetInt32(scalarContent); 192 | break; 193 | case INT8OID: 194 | scalar.i64 = DatumGetInt64(scalarContent); 195 | break; 196 | case FLOAT4OID: 197 | scalar.f4 = DatumGetFloat4(scalarContent); 198 | break; 199 | case FLOAT8OID: 200 | scalar.f8 = DatumGetFloat8(scalarContent); 201 | break; 202 | } 203 | } 204 | for (i = 0; i < inputLength; i++) { 205 | if (scalarNull || inputNulls[i]) { 206 | retNulls[i] = true; 207 | continue; 208 | } 209 | retNulls[i] = false; 210 | if (scalarPos == 0) { 211 | switch(scalarTypeId) { 212 | case INT2OID: 213 | retContent[i] = Int16GetDatum(scalar.i16 - DatumGetInt16(inputContent[i])); 214 | break; 215 | case INT4OID: 216 | retContent[i] = Int32GetDatum(scalar.i32 - DatumGetInt32(inputContent[i])); 217 | break; 218 | case INT8OID: 219 | retContent[i] = Int64GetDatum(scalar.i64 - DatumGetInt64(inputContent[i])); 220 | break; 221 | case FLOAT4OID: 222 | retContent[i] = Float4GetDatum(scalar.f4 - DatumGetFloat4(inputContent[i])); 223 | break; 224 | case FLOAT8OID: 225 | retContent[i] = Float8GetDatum(scalar.f8 - DatumGetFloat8(inputContent[i])); 226 | break; 227 | } 228 | } else { 229 | switch(scalarTypeId) { 230 | case INT2OID: 231 | retContent[i] = Int16GetDatum(DatumGetInt16(inputContent[i]) - scalar.i16); 232 | break; 233 | case INT4OID: 234 | retContent[i] = Int32GetDatum(DatumGetInt32(inputContent[i]) - scalar.i32); 235 | break; 236 | case INT8OID: 237 | retContent[i] = Int64GetDatum(DatumGetInt64(inputContent[i]) - scalar.i64); 238 | break; 239 | case FLOAT4OID: 240 | retContent[i] = Float4GetDatum(DatumGetFloat4(inputContent[i]) - scalar.f4); 241 | break; 242 | case FLOAT8OID: 243 | retContent[i] = Float8GetDatum(DatumGetFloat8(inputContent[i]) - scalar.f8); 244 | break; 245 | } 246 | } 247 | } 248 | 249 | dims[0] = inputLength; 250 | lbs[0] = 1; 251 | 252 | retArray = construct_md_array(retContent, retNulls, 1, dims, lbs, 253 | scalarTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode); 254 | 255 | PG_RETURN_ARRAYTYPE_P(retArray); 256 | } 257 | 258 | --------------------------------------------------------------------------------