├── docs └── GNUmakefile ├── src ├── pgbitmap.d ├── GNUmakefile ├── Makefile ├── pgbitmap.h ├── pgbitmap_interface.sqs └── pgbitmap.c ├── test ├── GNUmakefile ├── Makefile └── test_bitmap.sql ├── COPYRIGHT ├── bin ├── makefilter~ ├── makefilter ├── find_pg_config~ └── find_pg_config ├── .gitignore ├── pgbitmap.control ├── META.json ├── LICENSE ├── GNUmakefile └── README.md /docs/GNUmakefile: -------------------------------------------------------------------------------- 1 | ../src/GNUmakefile -------------------------------------------------------------------------------- /src/pgbitmap.d: -------------------------------------------------------------------------------- 1 | src/pgbitmap.o src/pgbitmap.d: \ 2 | src/pgbitmap.c \ 3 | src/pgbitmap.h 4 | -------------------------------------------------------------------------------- /src/GNUmakefile: -------------------------------------------------------------------------------- 1 | # ---------- 2 | # GNUmakefile 3 | # 4 | # Copyright (c) 2015, 2018 Marc Munro 5 | # Author: Marc Munro 6 | # License: BSD 7 | # 8 | # ---------- 9 | # 10 | 11 | all: 12 | 13 | %:: 14 | cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@ 15 | -------------------------------------------------------------------------------- /test/GNUmakefile: -------------------------------------------------------------------------------- 1 | # ---------- 2 | # GNUmakefile 3 | # 4 | # Copyright (c) 2015, 2018 Marc Munro 5 | # Author: Marc Munro 6 | # License: BSD 7 | # 8 | # ---------- 9 | # 10 | 11 | all: 12 | 13 | %:: 14 | cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@ 15 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Vbitmap - a bitmap type intended for fast testing of privilege bits 2 | 3 | Copyright (c) 2015 4 | Marc Munro, 5 | Munro Information Services Ltd, 6 | Vancouver BC, Canada 7 | 8 | This software is released under the BSD License as described in the 9 | associated LICENSE file distributed with this software. 10 | -------------------------------------------------------------------------------- /bin/makefilter~: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | awk ' 4 | BEGIN { 5 | failed = 0 6 | } 7 | /^GNUmakefile.*recipe.*failed/ { 8 | failed = 1 9 | next 10 | } 11 | /^make.*check_.*Error 2/ { 12 | failed = 1 13 | next 14 | } 15 | { print } 16 | END { 17 | if (failed) exit(2) 18 | }' 19 | 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore backup and temp files 2 | *~ 3 | \#*# 4 | \.\#* 5 | 6 | # Build files 7 | PG_CONFIG 8 | PG_VERSION 9 | 10 | # Log files 11 | *.log 12 | 13 | # The html directory 14 | html 15 | 16 | # autotools crap 17 | autom4te.cache 18 | config.status 19 | 20 | # C dependency files 21 | src/*.d 22 | 23 | # The generated sql installation file 24 | pgbitmap--*sql 25 | -------------------------------------------------------------------------------- /pgbitmap.control: -------------------------------------------------------------------------------- 1 | # pgbitmap.control 2 | # 3 | # Postgres extension control file for pgbitmap 4 | # 5 | # Copyright (c) 2020 Marc Munro 6 | # Author: Marc Munro 7 | # License: BSD 8 | # 9 | 10 | directory = 'extension' 11 | default_version = '0.9.5' 12 | module_pathname = '$libdir/pgbitmap' 13 | superuser = true 14 | relocatable = false 15 | 16 | comment = 'Provides a bitmap data type.' 17 | -------------------------------------------------------------------------------- /bin/makefilter: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | # 3 | # Filter error messages from make output. This is for the 4 | # zipfile target which performs multiple tests. 5 | # 6 | # Copyright (c) 2020 Marc Munro 7 | # Author: Marc Munro 8 | # License: BSD 9 | 10 | awk ' 11 | BEGIN { 12 | failed = 0 13 | } 14 | /^GNUmakefile.*recipe.*failed/ { 15 | failed = 1 16 | next 17 | } 18 | /^make.*check_.*Error 2/ { 19 | failed = 1 20 | next 21 | } 22 | { print } 23 | END { 24 | if (failed) exit(2) 25 | }' 26 | 27 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pgbitmap", 3 | "abstract": "Bitmap-type extension for PostgreSQL", 4 | "description": "Provides a type for storing and manipulating bitmaps (space efficient arrays of bits).", 5 | "version": "0.9.5", 6 | "maintainer": ["Marc Munro "], 7 | "license": { 8 | "BSD": "http://www.opensource.org/licenses/bsd-license.html" 9 | }, 10 | "prereqs": { 11 | "runtime": { 12 | "requires": { 13 | "PostgreSQL": "9.5.0" 14 | } 15 | } 16 | }, 17 | "provides": { 18 | "pgbitmap": { 19 | "file": "pgbitmap--0.9.5.sql", 20 | "version": "0.9.5", 21 | "docfile": "README.md" 22 | } 23 | }, 24 | "resources": { 25 | "homepage": "https://github.com/marcmunro/pgbitmap", 26 | "repository": { 27 | "url": "https://github.com/marcmunro/pgbitmap.git", 28 | "type": "git" 29 | } 30 | }, 31 | "meta-spec": { 32 | "version": "1.0.0", 33 | "url": "http://pgxn.org/meta/spec.txt" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | # 3 | # Makefile for test directory of pgbitmap 4 | # 5 | # Copyright (c) 2020 Marc Munro 6 | # Author: Marc Munro 7 | # License: BSD 8 | # 9 | # Do not attempt to use this makefile explicitly: its targets are available 10 | # and should be built from the main GNUmakefile in the parent directory. 11 | # The GNUmakefile in this directory will build using the parent GNUmakefile 12 | # so using make in this directory will work as long as you don't 13 | # try to specify this makefile. It even works with emacs compile and 14 | # next-error functions though the number of makefiles involved seems a 15 | # little alarming at first. 16 | # The whole strangeness of this makefile hierarchy derives from a, 17 | # possibly misguided, attempt to avoid recursive make (see the article 18 | # "Recursive make considered harmful" for a rationale). 19 | 20 | .PHONY: unit test 21 | 22 | # You can run this using several target names. 23 | unit test: 24 | psql -f test/test_bitmap.sql 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions 3 | are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | 2. Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 12 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 13 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 14 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 15 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 16 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 17 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 18 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 19 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 20 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | # 3 | # Makefile for src directory of pgbitmap 4 | # 5 | # Copyright (c) 2020 Marc Munro 6 | # Author: Marc Munro 7 | # License: BSD 8 | # 9 | # Do not attempt to use this makefile explicitly: its targets are available 10 | # and should be built from the main GNUmakefile in the parent directory. 11 | # The GNUmakefile in this directory will build using the parent GNUmakefile 12 | # so using make in this directory will work as long as you don't 13 | # try to specify this makefile. It even works with emacs compile and 14 | # next-error functions though the number of makefiles involved seems a 15 | # little alarming at first. 16 | # The whole strangeness of this makefile hierarchy derives from a, 17 | # possibly misguided, attempt to avoid recursive make (see the article 18 | # "Recursive make considered harmful" for a rationale). 19 | 20 | 21 | SOURCES = src/pgbitmap.c 22 | 23 | ifdef EXTENSION 24 | LIBDIR=$(DESTDIR)$(datadir)/extension 25 | else 26 | LIBDIR=$(DESTDIR)$(pkglibdir) 27 | endif 28 | 29 | INSTALLED_LIB = $(LIBDIR)/$(addsuffix $(DLSUFFIX), pgbitmap) 30 | PGBITMAP_LIB = $(BUILD_DIR)/$(addsuffix $(DLSUFFIX), pgbitmap) 31 | HEADERS = $(wildcard src/*.h) 32 | SRC_CLEAN = $(PGBITMAP_LIB) 33 | 34 | all: $(PGBITMAP_CONTROL) 35 | 36 | $(PGBITMAP_CONTROL): src/pgbitmap_interface.sqs 37 | @echo Creating $(PGBITMAP_CONTROL) 38 | @sed -e 's!@LIBPATH@!$$libdir/pgbitmap!g' <$< >$@ 39 | 40 | -------------------------------------------------------------------------------- /bin/find_pg_config~: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Figure out where to find pg_config. This is primarily build for 3 | # debian and debian like systems where multiple versions of postgres may 4 | # be installed. 5 | # Return a simplified (2-part) version string. 6 | # 7 | strip_version() 8 | { 9 | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\)\.[0-9][0-9]*.*/\1/' 10 | } 11 | 12 | # Attempt to read the postgres version by connecting to the database 13 | # server. If that fails, use the contents of the PG_VERSION file, if it 14 | # exists. 15 | # 16 | pgver() 17 | { 18 | if [ "x$1" != "x" ]; then 19 | echo $1 20 | else 21 | if ver=`psql --no-psqlrc --tuples-only \ 22 | --command="select version()"`; then 23 | # All was well, so record the version in PG_VERSION for later 24 | # use by install (which is run from root and may not have access 25 | # to psql or postgres databases. 26 | ver=`echo ${ver} | awk '{print $2}' | strip_version` 27 | echo $ver >PG_VERSION 28 | echo $ver 29 | else 30 | if [ -f ./PG_VERSION ]; then 31 | cat ./PG_VERSION 32 | else 33 | echo "Cannot establish postgres version..." 1>&2 34 | echo "Specify PG_VERSION explicitly in the make command." 1>&2 35 | exit 2 36 | fi 37 | fi 38 | fi 39 | } 40 | 41 | if [ -r PG_CONFIG ]; then 42 | cat PG_CONFIG 43 | else 44 | # Maybe the correct pg_config is where it claims to be. 45 | # 46 | if ver=`pgver $1`; then 47 | major_ver=`echo "${ver}" | cut -d. -f1` 48 | if [ "0${major_ver}" -ge 10 ]; then 49 | # This is a postgres version after pg 10, when version 50 | # numbering changed. We now only need the major part of 51 | # the version number. 52 | ver=${major_ver} 53 | fi 54 | if cver=`pg_config --version | strip_version`; then 55 | if [ "x${cver}" = "x${ver}" ]; then 56 | which pg_config > PG_CONFIG 57 | cat PG_CONFIG 58 | exit 0 59 | fi 60 | fi 61 | 62 | # Or maybe, we can figure it out from the database version 63 | if [ -f /usr/lib/postgresql/${ver}/bin/pg_config ]; then 64 | echo /usr/lib/postgresql/${ver}/bin/pg_config > PG_CONFIG 65 | cat PG_CONFIG 66 | exit 0 67 | fi 68 | 69 | # If we get here, we really have not much idea, nor anywhere else to 70 | # look. 71 | 72 | echo "Cannot find pg_config for postgres version ${ver}" 1>&2 73 | exit 2 74 | fi 75 | fi 76 | -------------------------------------------------------------------------------- /bin/find_pg_config: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # Figure out where to find pg_config. This is primarily built 4 | # for debian and debian like systems where multiple versions of 5 | # postgres may be installed. Return a simplified (2-part) 6 | # version string. 7 | # 8 | # Copyright (c) 2020 Marc Munro 9 | # Author: Marc Munro 10 | # License: BSD 11 | # 12 | 13 | strip_version() 14 | { 15 | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\)\.[0-9][0-9]*.*/\1/' 16 | } 17 | 18 | # Attempt to read the postgres version by connecting to the database 19 | # server. If that fails, use the contents of the PG_VERSION file, if it 20 | # exists. 21 | # 22 | pgver() 23 | { 24 | if [ "x$1" != "x" ]; then 25 | echo $1 26 | else 27 | if ver=`psql --no-psqlrc --tuples-only \ 28 | --command="select version()"`; then 29 | # All was well, so record the version in PG_VERSION for later 30 | # use by install (which is run from root and may not have access 31 | # to psql or postgres databases. 32 | ver=`echo ${ver} | awk '{print $2}' | strip_version` 33 | echo $ver >PG_VERSION 34 | echo $ver 35 | else 36 | if [ -f ./PG_VERSION ]; then 37 | cat ./PG_VERSION 38 | else 39 | echo "Cannot establish postgres version..." 1>&2 40 | echo "Specify PG_VERSION explicitly in the make command." 1>&2 41 | exit 2 42 | fi 43 | fi 44 | fi 45 | } 46 | 47 | if [ -r PG_CONFIG ]; then 48 | cat PG_CONFIG 49 | else 50 | # Maybe the correct pg_config is where it claims to be. 51 | # 52 | if ver=`pgver $1`; then 53 | major_ver=`echo "${ver}" | cut -d. -f1` 54 | if [ "0${major_ver}" -ge 10 ]; then 55 | # This is a postgres version after pg 10, when version 56 | # numbering changed. We now only need the major part of 57 | # the version number. 58 | ver=${major_ver} 59 | fi 60 | if cver=`pg_config --version | strip_version`; then 61 | if [ "x${cver}" = "x${ver}" ]; then 62 | which pg_config > PG_CONFIG 63 | cat PG_CONFIG 64 | exit 0 65 | fi 66 | fi 67 | 68 | # Or maybe, we can figure it out from the database version 69 | if [ -f /usr/lib/postgresql/${ver}/bin/pg_config ]; then 70 | echo /usr/lib/postgresql/${ver}/bin/pg_config > PG_CONFIG 71 | cat PG_CONFIG 72 | exit 0 73 | fi 74 | 75 | # If we get here, we really have not much idea, nor anywhere else to 76 | # look. 77 | 78 | echo "Cannot find pg_config for postgres version ${ver}" 1>&2 79 | exit 2 80 | fi 81 | fi 82 | -------------------------------------------------------------------------------- /src/pgbitmap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pgbitmap.h 3 | * \code 4 | * Author: Marc Munro 5 | * Copyright (c) 2020 Marc Munro 6 | * License: BSD 7 | * 8 | * \endcode 9 | * @brief 10 | * Define bitmap datatypes 11 | * 12 | */ 13 | 14 | #include "postgres.h" 15 | #include "funcapi.h" 16 | 17 | #ifndef BITMAP_DATATYPES 18 | /** 19 | * Prevent this header from being included multiple times. 20 | */ 21 | #define BITMAP_DATATYPES 1 22 | 23 | /* Bitmaps will be based on 64-bit integers if pointer types are 64-bit, 24 | * unless FORCE_32_BIT is defined. 25 | */ 26 | #ifdef FORCE_32_BIT 27 | #undef USE_64_BIT 28 | #else 29 | #if (SIZEOF_VOID_P == 8) 30 | /** 31 | * Use 64-bit word definitions throughout. 32 | */ 33 | #define USE_64_BIT 1 34 | #else 35 | #undef USE_64_BIT 36 | #endif 37 | #endif 38 | 39 | #ifdef USE_64_BIT 40 | /** 41 | * Use 64-bit words. 42 | */ 43 | #define ELEMBITS 64 44 | #else 45 | /** 46 | * Use 32-bit words. 47 | */ 48 | #define ELEMBITS 32 49 | #endif 50 | 51 | //#define BITMAP_DEBUG 1 52 | #ifdef BITMAP_DEBUG 53 | #define DBG_ELEMS 1 54 | #define SETCANARY(b) do { \ 55 | b->bitset[ARRAYELEMS(b->bitmin, b->bitmax)] = 0; \ 56 | b->canary = 0; \ 57 | } while (false) 58 | 59 | #define CHKCANARY(b) \ 60 | ((b->bitset[ARRAYELEMS(b->bitmin, b->bitmax)] == 0) && \ 61 | (b->canary == 0)) 62 | 63 | #ifdef USE_64_BIT 64 | #define CANARYELEM uint64 canary; 65 | #else 66 | #define CANARYELEM uint32 canary; 67 | #endif 68 | #else 69 | #define DBG_ELEMS 0 70 | #define SETCANARY(b) 71 | #define CHKCANARY(b) true 72 | #define CANARYELEM 73 | #endif 74 | 75 | 76 | 77 | /** 78 | * Gives the bitmask index for the bitzero value of a Bitmap. This is 79 | * part of the "normalisation" process for bitmap ranges. This process 80 | * allows unlike bitmaps to be more easily compared by forcing bitmap 81 | * indexes to be normalised around 32 or 64 bit word boundaries. 82 | * 83 | * @param x The bitzero value of a bitmap 84 | * 85 | * @return The bitmask index representing x. 86 | */ 87 | #ifdef USE_64_BIT 88 | #define BITZERO(x) ((x) & 0xffffffffffffffc0) 89 | #else 90 | #define BITZERO(x) ((x) & 0xffffffe0) 91 | #endif 92 | 93 | /** 94 | * Gives the index of the word for a given bit, assuming bitmin is zero. 95 | * 96 | * @param x The bit in question 97 | * 98 | * @return The array index of the bit. 99 | */ 100 | #ifdef USE_64_BIT 101 | #define BITSET_ELEM(x) ((x) >> 6) 102 | #else 103 | #define BITSET_ELEM(x) ((x) >> 5) 104 | #endif 105 | 106 | /** 107 | * Gives the index into ::bitmasks for the bit specified in x. 108 | * 109 | * @param x The bit in question 110 | * 111 | * @return The bitmask index 112 | */ 113 | #ifdef USE_64_BIT 114 | #define BITSET_BIT(x) (x & 0x3f) 115 | #else 116 | #define BITSET_BIT(x) (x & 0x1f) 117 | #endif 118 | 119 | /** 120 | * Gives the number of array elements in a ::Bitmap that runs from 121 | * element min to element max. 122 | * 123 | * @param min 124 | * @param max 125 | * 126 | * @return The number of elements in the bitmap. 127 | */ 128 | #ifdef USE_64_BIT 129 | #define ARRAYELEMS(min,max) (((max - BITZERO(min)) >> 6) + 1) 130 | #else 131 | #define ARRAYELEMS(min,max) (((max - BITZERO(min)) >> 5) + 1) 132 | #endif 133 | 134 | 135 | /** 136 | * Return the smaller of a or b. Note that expressions a and b may be 137 | * evaluated more than once. 138 | * 139 | * @param a 140 | * @param b 141 | * 142 | * @return The smaller value of a or b. 143 | */ 144 | #define MIN(a,b) ((a < b)? a: b) 145 | 146 | /** 147 | * Return the larger of a or b. Note that expressions a and b may be 148 | * evaluated more than once. 149 | * 150 | * @param a 151 | * @param b 152 | * 153 | * @return The smaller value of a or b. 154 | */ 155 | #define MAX(a,b) ((a > b)? a: b) 156 | 157 | #ifdef USE_64_BIT 158 | /** 159 | * bm_int is the bitmap integer type (a 64-bit value). 160 | */ 161 | typedef uint64 bm_int; 162 | #else 163 | /** 164 | * bm_int is the bitmap integer type (a 32-bit value). 165 | */ 166 | typedef uint32 bm_int; 167 | #endif 168 | 169 | /** 170 | * A bitmap is stored as an array of integer values. Note that the size 171 | * of a Bitmap structure is determined dynamically at run time as the 172 | * size of the array is only known then. 173 | */ 174 | typedef struct Bitmap { 175 | char vl_len[4]; /**< Standard postgres length header */ 176 | int32 bitmin; /**< The index of the lowest bit the bitmap has 177 | * stored */ 178 | int32 bitmax; /**< The index of the highest bit the bitmap has 179 | * stored */ 180 | CANARYELEM 181 | bm_int bitset[0]; /**< Element zero of the array of int4 values 182 | * comprising the bitmap. */ 183 | } Bitmap; 184 | 185 | /** 186 | * Defines a boolean type to make our code more readable. 187 | */ 188 | typedef unsigned char boolean; 189 | 190 | /** 191 | * Provide a macro for getting a bitmap datum. 192 | */ 193 | #define DatumGetBitmap(x) ((Bitmap *) PG_DETOAST_DATUM(DatumGetPointer(x))) 194 | 195 | /** 196 | * Provide a macro for dealing with bitmap arguments. 197 | */ 198 | #define PG_GETARG_BITMAP(x) DatumGetBitmap( \ 199 | PG_DETOAST_DATUM(PG_GETARG_DATUM(x))) 200 | /** 201 | * Provide a macro for returning bitmap results. 202 | */ 203 | #define PG_RETURN_BITMAP(x) PG_RETURN_POINTER(x) 204 | 205 | extern bool bitmapTestbit(Bitmap *bitmap, int32 bit); 206 | extern Bitmap *bitmapCopy(Bitmap *bitmap); 207 | 208 | extern Datum bitmap_in(PG_FUNCTION_ARGS); 209 | extern Datum bitmap_out(PG_FUNCTION_ARGS); 210 | extern Datum bitmap_is_empty(PG_FUNCTION_ARGS); 211 | extern Datum bitmap_bits(PG_FUNCTION_ARGS); 212 | extern Datum bitmap_new_empty(PG_FUNCTION_ARGS); 213 | extern Datum bitmap_new(PG_FUNCTION_ARGS); 214 | extern Datum bitmap_bitmin(PG_FUNCTION_ARGS); 215 | extern Datum bitmap_bitmax(PG_FUNCTION_ARGS); 216 | extern Datum bitmap_setbit(PG_FUNCTION_ARGS); 217 | extern Datum bitmap_testbit(PG_FUNCTION_ARGS); 218 | extern Datum bitmap_setmin(PG_FUNCTION_ARGS); 219 | extern Datum bitmap_setmax(PG_FUNCTION_ARGS); 220 | extern Datum bitmap_equal(PG_FUNCTION_ARGS); 221 | extern Datum bitmap_nequal(PG_FUNCTION_ARGS); 222 | extern Datum bitmap_union(PG_FUNCTION_ARGS); 223 | extern Datum bitmap_clearbit(PG_FUNCTION_ARGS); 224 | extern Datum bitmap_union(PG_FUNCTION_ARGS); 225 | extern Datum bitmap_intersection(PG_FUNCTION_ARGS); 226 | extern Datum bitmap_minus(PG_FUNCTION_ARGS); 227 | extern Datum bitmap_lt(PG_FUNCTION_ARGS); 228 | extern Datum bitmap_le(PG_FUNCTION_ARGS); 229 | extern Datum bitmap_gt(PG_FUNCTION_ARGS); 230 | extern Datum bitmap_ge(PG_FUNCTION_ARGS); 231 | extern Datum bitmap_cmp(PG_FUNCTION_ARGS); 232 | 233 | 234 | #endif 235 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # GNUmakefile 2 | # 3 | # PGXS-based makefile for pgbitmap 4 | # 5 | # Copyright (c) 2020 Marc Munro 6 | # Author: Marc Munro 7 | # License: BSD 8 | # 9 | # For a list of targets use make help. 10 | # 11 | 12 | # Default target. Dependencies for this are further defined in the 13 | # included makefiles for SUBDIRS below. 14 | all: 15 | 16 | BUILD_DIR = $(shell pwd) 17 | MODULE_big = pgbitmap 18 | OBJS = $(SOURCES:%.c=%.o) 19 | DEPS = $(SOURCES:%.c=%.d) 20 | EXTENSION=pgbitmap 21 | MODULEDIR=extension 22 | PGBITMAP_VERSION = $(shell \ 23 | grep default_version pgbitmap.control | sed 's/[^0-9.]*\([0-9.]*\).*/\1/') 24 | 25 | PGBITMAP_CONTROL = pgbitmap--$(PGBITMAP_VERSION).sql 26 | OLD_PGBITMAP_CONTROLS = $(shell ls pgbitmap--*.sql | grep -v $(PGBITMAP_VERSION)) 27 | 28 | SUBDIRS = src test 29 | EXTRA_CLEAN = $(SRC_CLEAN) 30 | include $(SUBDIRS:%=%/Makefile) 31 | 32 | 33 | DATA = $(wildcard pgbitmap--*.sql) 34 | 35 | PG_CONFIG := $(shell bin/find_pg_config) 36 | PGXS := $(shell $(PG_CONFIG) --pgxs) 37 | include $(PGXS) 38 | 39 | ifneq ($(origin FORCE_32_BIT), undefined) 40 | DFORCE_32_BIT = -DFORCE_32_BIT=1 41 | endif 42 | 43 | override CFLAGS := $(CFLAGS) -g -O0 $(DFORCE_32_BIT) 44 | 45 | include $(DEPS) 46 | 47 | .PHONY: deps zipfile clean make_deps clean pgbitmap_clean list \ 48 | zipfile help docs 49 | 50 | # Build per-source dependency files for inclusion 51 | # This ignores header files and any other non-local files (such as 52 | # postgres include files). Since I don't know if this will work 53 | # on non-Unix platforms, we will ship pgbitmap with the dep files 54 | # in place). This target is mostly for maintainers who may wish 55 | # to rebuild dep files. 56 | %.d: %.c 57 | @echo Recreating $@ 58 | @$(SHELL) -ec "$(CC) -MM -MT $*.o $(CPPFLAGS) $< | \ 59 | xargs -n 1 | grep '^[^/]' | \ 60 | sed -e '1,$$ s/$$/ \\\\/' -e '$$ s/ \\\\$$//' \ 61 | -e '2,$$ s/^/ /' | \ 62 | sed 's!$*.o!& $@!g'" > $@ 63 | 64 | # Target used by recursive call from deps target below. This ensures 65 | # that make deps always rebuilds the dep files even if they are up to date. 66 | make_deps: $(DEPS) 67 | @>/dev/null 68 | 69 | # Target that rebuilds all dep files unconditionally. There should be a 70 | # simpler way to do this using .PHONY but I can't figure out how. 71 | deps: 72 | rm -f $(DEPS) 73 | $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" make_deps 74 | 75 | # Run doxygen to build html docs 76 | # Docs are installed to github using the following set of commands: 77 | # $ git commit -a 78 | # $ git checkout gh-pages 79 | # $ git merge master 80 | # $ make docs 81 | # $ git add docs 82 | # $ git commit -a 83 | # $ git push github gh-pages 84 | # $ git checkout master 85 | 86 | docs/html: $(SOURCES) 87 | doxygen docs/Doxyfile 88 | 89 | docs: docs/html 90 | 91 | 92 | 93 | ## 94 | # release targets 95 | # 96 | ZIPFILE_BASENAME = pgbitmap-$(PGBITMAP_VERSION) 97 | ZIPFILENAME = $(ZIPFILE_BASENAME).zip 98 | ONLINE_DOCS = https://marcmunro.github.io/pgbitmap/docs/html/index.html 99 | GIT_UPSTREAM = github origin 100 | 101 | # Ensure that we are in the master git branch 102 | check_branch: 103 | @[ `git rev-parse --abbrev-ref HEAD` = master ] || \ 104 | (echo " CURRENT GIT BRANCH IS NOT MASTER" 1>&2 && exit 2) 105 | 106 | # Check that our metadata file for pgxs is up to date. This is very 107 | # simplistic but aimed only at ensuring you haven't forgotten to 108 | # update the file. 109 | check_meta: META.json 110 | @grep '"version"' META.json | head -2 | cut -d: -f2 | \ 111 | tr -d '",' | \ 112 | while read a; do \ 113 | [ "x$$a" = "x$(PGBITMAP_VERSION)" ] || \ 114 | (echo " INCORRECT VERSION ($$a) IN META.json"; exit 2); \ 115 | done 116 | @grep '"file"' META.json | cut -d: -f2 | tr -d '",' | \ 117 | while read a; do \ 118 | [ "x$$a" = "xpgbitmap--$(PGBITMAP_VERSION).sql" ] || \ 119 | (echo " INCORRECT FILE NAME ($$a) IN META.json"; exit 2); \ 120 | done 121 | 122 | # Check that head has been tagged. We assume that if it has, then it 123 | # has been tagged correctly. 124 | check_tag: 125 | @tag=`git tag --points-at HEAD`; \ 126 | if [ "x$${tag}" = "x" ]; then \ 127 | echo " NO GIT TAG IN PLACE"; \ 128 | exit 2; \ 129 | fi 130 | 131 | # Check that the latest docs have been published. 132 | check_docs: docs 133 | @[ "x`cat docs/html/index.html | md5sum`" = \ 134 | "x`curl -s $(ONLINE_DOCS) | md5sum`" ] || \ 135 | (echo " LATEST DOCS NOT PUBLISHED"; exit 2) 136 | 137 | # Check that there are no uncomitted changes. 138 | check_commit: 139 | @git status -s | wc -l | grep '^0$$' >/dev/null || \ 140 | (echo " UNCOMMITTED CHANGES FOUND"; exit 2) 141 | 142 | # Check that we have pushed the latest changes 143 | check_origin: 144 | @err=0; \ 145 | for origin in $(GIT_UPSTREAM); do \ 146 | git diff --quiet master $${origin}/master 2>/dev/null || \ 147 | { echo " UNPUSHED UPDATES FOR $${origin}"; \ 148 | err=2; }; \ 149 | done; exit $$err 150 | 151 | # Check that this version appears in the change history 152 | check_history: 153 | @grep "^$(PGBITMAP_VERSION)" \ 154 | README.md >/dev/null || \ 155 | (echo " CURRENT VERSION NOT RECORDED IN CHANGE HISTORY"; \ 156 | exit 2) 157 | 158 | # Create a zipfile for release to pgxn, but only if everthing is ready 159 | # to go. Note that we distribute our dependencies in case our user is 160 | # going to build things manually and can't build them themselves, and 161 | # our built docs as we don't require users to have a suitable build 162 | # environment for building them themselves, and having the docs 163 | # installed locally is a good thing. 164 | zipfile: 165 | @$(MAKE) -k --no-print-directory \ 166 | check_branch check_meta check_tag check_docs \ 167 | check_commit check_origin check_history 2>&1 | \ 168 | bin/makefilter 1>&2 169 | @$(MAKE) do_zipfile 170 | 171 | do_zipfile: pgbitmap_clean deps docs 172 | git archive --format zip --prefix=$(ZIPFILE_BASENAME)/ \ 173 | --output $(ZIPFILENAME) master 174 | 175 | 176 | # Target to remove generated and backup files. 177 | clean: pgbitmap_clean docs_clean 178 | 179 | pgbitmap_clean: 180 | @rm -f PG_VERSION PG_CONFIG $(OBJS) \ 181 | $(OLD_PGBITMAP_CONTROLS) $(MODULE_big).so \ 182 | *~ src/*~ test/*~ pgbitmap*.zip 183 | 184 | docs_clean: 185 | @rm -rf docs/html docs/*~ 186 | 187 | # Provide a list of the targets buildable by this makefile. 188 | list help: 189 | @echo "\n\ 190 | Major targets for this makefile are:\n\n\ 191 | all - build pgbitmap library\n\ 192 | clean - remove target and object files\n\ 193 | deps - recreate the .d dependency files\n\ 194 | docs - run doxygen to create html docs\n\ 195 | zipfile - create a zipfile suitable for pgxn\n\ 196 | test - run unit tests on installed extension\n\ 197 | install - install the extension (may require root)\n\ 198 | help - show this list of major targets\n\ 199 | \n\ 200 | " 201 | -------------------------------------------------------------------------------- /src/pgbitmap_interface.sqs: -------------------------------------------------------------------------------- 1 | /* ---------- 2 | * pgbitmap_interface.sqs (or a file derived from it) 3 | * 4 | * Source file from which pgbitmap--.sql is generated using 5 | * sed. 6 | * 7 | * Copyright (c) 2020 Marc Munro 8 | * Author: Marc Munro 9 | * License: BSD 10 | * 11 | * ---------- 12 | */ 13 | 14 | 15 | create 16 | function bitmap_in(textin cstring) returns bitmap 17 | as '@LIBPATH@', 'bitmap_in' 18 | language C immutable strict; 19 | 20 | comment on function bitmap_in(cstring) is 21 | 'Read the serialised string representation of a bitmap, TEXTIN, into a bitmap.'; 22 | 23 | 24 | create 25 | function bitmap_out(bitmap bitmap) returns cstring 26 | as '@LIBPATH@', 'bitmap_out' 27 | language C immutable strict; 28 | 29 | comment on function bitmap_out(bitmap) is 30 | 'create a serialised string representation of BITMAP.'; 31 | 32 | 33 | create type bitmap ( 34 | input = bitmap_in, 35 | output = bitmap_out, 36 | internallength = variable, 37 | alignment = double, 38 | storage = main 39 | ); 40 | 41 | comment on type bitmap is 42 | 'A set-of-bits type, intended for use in managing sets of privileges for 43 | security purposes.'; 44 | 45 | 46 | create 47 | function bits(bitmap bitmap) returns setof int4 48 | as '@LIBPATH@', 'bitmap_bits' 49 | language C immutable strict; 50 | 51 | comment on function bits(bitmap) is 52 | 'Return a set of integers showing the contents of BITMAP'; 53 | 54 | 55 | create 56 | function bitmap() returns bitmap 57 | as '@LIBPATH@', 'bitmap_new_empty' 58 | language C immutable strict; 59 | 60 | comment on function bitmap() is 61 | 'Return an empty bitmap'; 62 | 63 | create 64 | function bitmap(bitno int4) returns bitmap 65 | as '@LIBPATH@', 'bitmap_new' 66 | language C immutable strict; 67 | 68 | comment on function bitmap(int4) is 69 | 'Return a bitmap containing the single bit provided in BITNO.'; 70 | 71 | 72 | create 73 | function is_empty(bitmap bitmap) returns boolean 74 | as '@LIBPATH@', 'bitmap_is_empty' 75 | language C immutable strict; 76 | 77 | comment on function is_empty(bitmap) is 78 | 'Predicate identifying whether BITMAP is empty'; 79 | 80 | 81 | create 82 | function bitmin(bitmap bitmap) returns int4 83 | as '@LIBPATH@', 'bitmap_bitmin' 84 | language C immutable strict; 85 | 86 | comment on function bitmin(bitmap) is 87 | 'Return the number of the minimum bit stored in BITMAP. NULL, if no 88 | bits'; 89 | 90 | 91 | create 92 | function bitmax(bitmap bitmap) returns int4 93 | as '@LIBPATH@', 'bitmap_bitmax' 94 | language C immutable strict; 95 | 96 | comment on function bitmax(bitmap) is 97 | 'Return the number of the maximum bit stored in BITMAP. NULL, if no 98 | bits'; 99 | 100 | 101 | create 102 | function bitmap_setbit(bitmap bitmap, bitno int4) returns bitmap 103 | as '@LIBPATH@', 'bitmap_setbit' 104 | language C immutable strict; 105 | 106 | comment on function bitmap_setbit(bitmap, int4) is 107 | 'In BITMAP, set the bit given by BITNO'; 108 | 109 | create operator + ( 110 | procedure = bitmap_setbit, 111 | leftarg = bitmap, 112 | rightarg = int4 113 | ); 114 | 115 | 116 | create 117 | function bitmap_testbit(bitmap bitmap, bitno int4) returns bool 118 | as '@LIBPATH@', 'bitmap_testbit' 119 | language C immutable strict; 120 | 121 | comment on function bitmap_testbit(bitmap, int4) is 122 | 'In BITMAP, test the bit, BITNO, returning true if set, otherwise false.'; 123 | 124 | create operator ? ( 125 | procedure = bitmap_testbit, 126 | leftarg = bitmap, 127 | rightarg = int4 128 | ); 129 | 130 | 131 | create 132 | function bitmap_setmin(bitmap bitmap, bitmin int4) returns bitmap 133 | as '$libdir/pgbitmap', 'bitmap_setmin' 134 | language C immutable strict; 135 | 136 | comment on function bitmap_setmin(bitmap, int4) is 137 | 'In BITMAP, clear any bits that are less than BITMIN.'; 138 | 139 | create 140 | function bitmap_setmax(bitmap bitmap, bitmax int4) returns bitmap 141 | as '$libdir/pgbitmap', 'bitmap_setmax' 142 | language C immutable strict; 143 | 144 | comment on function bitmap_setmin(bitmap, int4) is 145 | 'In BITMAP, clear any bits that are greater than BITMAX.'; 146 | 147 | create 148 | function bitmap_equal(bitmap1 bitmap, bitmap2 bitmap) returns bool 149 | as '$libdir/pgbitmap', 'bitmap_equal' 150 | language C immutable strict; 151 | 152 | comment on function bitmap_equal(bitmap, bitmap) is 153 | 'Predicate returning true if bitmap1 and bitmap2 have the same bits set.'; 154 | 155 | create operator = ( 156 | procedure = bitmap_equal, 157 | leftarg = bitmap, 158 | rightarg = bitmap, 159 | commutator = =, 160 | negator = <> 161 | ); 162 | 163 | create 164 | function bitmap_nequal(bitmap1 bitmap, bitmap2 bitmap) returns bool 165 | as '$libdir/pgbitmap', 'bitmap_nequal' 166 | language C immutable strict; 167 | 168 | comment on function bitmap_nequal(bitmap, bitmap) is 169 | 'Predicate returning false if bitmap1 and bitmap2 have the same bits set.'; 170 | 171 | create operator <> ( 172 | procedure = bitmap_nequal, 173 | leftarg = bitmap, 174 | rightarg = bitmap, 175 | commutator = <>, 176 | negator = = 177 | ); 178 | 179 | create 180 | function bitmap_lt(bitmap1 bitmap, bitmap2 bitmap) returns bool 181 | as '$libdir/pgbitmap', 'bitmap_lt' 182 | language C immutable strict; 183 | 184 | create operator < ( 185 | procedure = bitmap_lt, 186 | leftarg = bitmap, 187 | rightarg = bitmap, 188 | commutator = >, 189 | negator = >= 190 | ); 191 | 192 | create 193 | function bitmap_le(bitmap1 bitmap, bitmap2 bitmap) returns bool 194 | as '$libdir/pgbitmap', 'bitmap_le' 195 | language C immutable strict; 196 | 197 | create operator <= ( 198 | procedure = bitmap_le, 199 | leftarg = bitmap, 200 | rightarg = bitmap, 201 | commutator = >=, 202 | negator = > 203 | ); 204 | 205 | create 206 | function bitmap_gt(bitmap1 bitmap, bitmap2 bitmap) returns bool 207 | as '$libdir/pgbitmap', 'bitmap_gt' 208 | language C immutable strict; 209 | 210 | create operator > ( 211 | procedure = bitmap_gt, 212 | leftarg = bitmap, 213 | rightarg = bitmap, 214 | commutator = <, 215 | negator = <= 216 | ); 217 | 218 | create 219 | function bitmap_ge(bitmap1 bitmap, bitmap2 bitmap) returns bool 220 | as '$libdir/pgbitmap', 'bitmap_ge' 221 | language C immutable strict; 222 | 223 | create operator >= ( 224 | procedure = bitmap_ge, 225 | leftarg = bitmap, 226 | rightarg = bitmap, 227 | commutator = <=, 228 | negator = < 229 | ); 230 | 231 | create 232 | function bitmap_cmp(bitmap, bitmap) returns int4 233 | as '$libdir/pgbitmap', 'bitmap_ge' 234 | language C immutable strict; 235 | 236 | create operator class bitmap_ops 237 | default for type bitmap using btree as 238 | operator 1 < , 239 | operator 2 <= , 240 | operator 3 = , 241 | operator 4 >= , 242 | operator 5 >, 243 | function 1 bitmap_cmp(bitmap, bitmap); 244 | 245 | 246 | 247 | create 248 | function bitmap_union(bitmap1 bitmap, bitmap2 bitmap) returns bitmap 249 | as '@LIBPATH@', 'bitmap_union' 250 | language C immutable strict; 251 | 252 | comment on function bitmap_union(bitmap, bitmap) is 253 | 'Return the union of BITMAP and BITMAP2'; 254 | 255 | create operator + ( 256 | procedure = bitmap_union, 257 | leftarg = bitmap, 258 | rightarg = bitmap, 259 | commutator = + 260 | ); 261 | 262 | 263 | create 264 | function bitmap_clearbit(bitmap bitmap, bitno int4) returns bitmap 265 | as '@LIBPATH@', 'bitmap_clearbit' 266 | language C immutable strict; 267 | 268 | comment on function bitmap_clearbit(bitmap, int4) is 269 | 'In BITMAP, rest the bit, BITNO, to zero.'; 270 | 271 | create operator - ( 272 | procedure = bitmap_clearbit, 273 | leftarg = bitmap, 274 | rightarg = int4 275 | ); 276 | 277 | 278 | create 279 | function bitmap_intersection(bitmap1 bitmap, vitmap2 bitmap) returns bitmap 280 | as '@LIBPATH@', 'bitmap_intersection' 281 | language C immutable strict; 282 | 283 | comment on function bitmap_intersection(bitmap, bitmap) is 284 | 'Return the intersection of BITMAP and BITMAP2'; 285 | 286 | create operator * ( 287 | procedure = bitmap_intersection, 288 | leftarg = bitmap, 289 | rightarg = bitmap, 290 | commutator = * 291 | ); 292 | 293 | 294 | create function bitmap_int_agg(bitmap bitmap, bitno int4) returns bitmap 295 | as '@LIBPATH@', 'bitmap_setbit' 296 | language C immutable; 297 | 298 | create aggregate bitmap_of(integer) ( 299 | sfunc = bitmap_int_agg, 300 | stype = bitmap); 301 | 302 | comment on aggregate bitmap_of(integer) is 303 | 'Aggregate a set of integers into a bitmap'; 304 | 305 | 306 | create function bitmap_union_agg(bitmap1 bitmap, 307 | bitmap2 bitmap) returns bitmap 308 | as '@LIBPATH@', 'bitmap_union' 309 | language C immutable; 310 | 311 | create aggregate union_of(bitmap) ( 312 | sfunc = bitmap_union_agg, 313 | stype = bitmap); 314 | 315 | comment on aggregate union_of(bitmap) is 316 | 'Union an aggregate of bitmaps into a single bitmap'; 317 | 318 | 319 | create function bitmap_intersect_agg(bitmap1 bitmap, 320 | bitmap2 bitmap) returns bitmap 321 | as '@LIBPATH@', 'bitmap_intersection' 322 | language C immutable; 323 | 324 | create aggregate intersect_of(bitmap) ( 325 | sfunc = bitmap_intersect_agg, 326 | stype = bitmap); 327 | 328 | comment on aggregate intersect_of(bitmap) is 329 | 'Intersect an aggregate of bitmaps into a single bitmap'; 330 | 331 | 332 | create 333 | function to_array(bitmap) 334 | returns int[] as 335 | $$ 336 | select coalesce(array_agg(bits), '{}'::int[]) from bits($1); 337 | $$ 338 | language sql; 339 | 340 | comment on function to_array(bitmap) is 341 | 'Convert a bitmap into an array - this may be a good way of getting a 342 | user-readable version of a bitmap.'; 343 | 344 | 345 | create 346 | function to_bitmap(int[]) 347 | returns bitmap as 348 | $$ 349 | select coalesce(bitmap_of(unnest), bitmap()) from unnest($1) 350 | $$ 351 | language sql; 352 | 353 | comment on function to_bitmap(int[]) is 354 | 'Convert an array of integers into a bitmap.'; 355 | 356 | create function bitmap_minus(bitmap1 bitmap, bitmap2 bitmap) returns bitmap 357 | as '$libdir/pgbitmap', 'bitmap_minus' 358 | language C immutable strict; 359 | 360 | comment on function bitmap_minus(bitmap, bitmap) is 361 | 'Return a bitmap containing the bits from BITMAP1 with all matching bits 362 | from BITMAP2 cleared.'; 363 | 364 | create operator - ( 365 | procedure = bitmap_minus, 366 | leftarg = bitmap, 367 | rightarg = bitmap 368 | ); 369 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pgbitmap - Bitmap Extension for Postgres 2 | ======================================== 3 | 4 | This extension creates a space-optimised, non-sparse, bitmap type for 5 | postgres. 6 | 7 | A bitmap is an array of bits, indexed by an integer. Bitmaps provide an 8 | efficient means to implement sets and `pgbitmap` provides operations 9 | for: 10 | 11 | - creating new bitmaps; 12 | - adding an element to a bitmap; 13 | - removing an element from a bitmap; 14 | - testing for inclusion of an element in a bitmap; 15 | - finding the minimum and maximum bits in the bitmap; 16 | - unioning bitmaps together (set union/logical or); 17 | - intersecting bitmaps (set intersection/logical and); 18 | - subtracting one bitmap from another; 19 | - converting bitmaps to and from textual representations; 20 | - converting bitmaps to and from arrays; 21 | - aggregating bits, and bitmaps, into bitmaps. 22 | 23 | Status 24 | ====== 25 | 26 | This is a beta release. The plan is to give it real-life usage before 27 | releasing a production version if all looks well. 28 | 29 | There are no known bugs or deficiencies. If you find any problems or 30 | want enhancements, contact me and I will do what I can to respond 31 | quickly. 32 | 33 | Change History 34 | ============== 35 | 36 | 0.2 (alpha) Initial release 37 | 38 | 0.3 (alpha) Fix for bitmap corruption when adding bit to empty bitmap. 39 | 40 | 0.5 (alpha) Change name of extension to pgbitmap from bitmap. 41 | 42 | 0.6 (alpha) Minor fixes to Makefiles find_pg_config and docs 43 | 44 | 0.9 (beta) Minor updates to documentation and to allow distribution 45 | through pgxn. Updated to Beta status as it all seems stable enough. 46 | 47 | 0.9.1 (beta) Updated to convince pgxn to fully index the extension. 48 | 49 | 0.9.2 (beta) Fix for bitmap union bug on unions of empty bitmaps. 50 | 51 | 0.9.3 (beta) No-change update for release to PGXN 52 | 53 | 0.9.4 (beta) Utility functions made available to other 54 | extensions. 55 | 56 | 0.9.5 (beta) Fix to (unused by pgbitmap) definition of DatumGetBitmap 57 | 58 | 59 | Doxygen Docs 60 | ============ 61 | 62 | `pgbitmap` is documented internally using Doxygen, with this page 63 | acting as the start page. The current docs can be found 64 | [here](https://marcmunro.github.io/pgbitmap/docs/html/index.html). 65 | 66 | Why not use the Postgres Bitstring Type? 67 | ======================================== 68 | 69 | The standard Postgres bit type is limited in a number of ways. In 70 | particular, each bit string starts at bit zero so a bitstring for bit 71 | 1,000,000 would contain the overhead of 1,000,000 zero bits. It also, 72 | currently, does not have all of the functionality that this bitmap 73 | type provides. 74 | 75 | What is this useful for? 76 | ======================== 77 | 78 | Pgbitmap was developed as a means of implementing sets of integers. It is 79 | particularly suited for managing sets of privileges for Virtual Private 80 | Database implementations. 81 | 82 | API Summary 83 | =========== 84 | 85 | Functions: 86 | ```.c 87 | bitmap_in(text) -> bitmap 88 | 89 | bitmap_out(bitmap) -> text 90 | 91 | bitmap() -> bitmap implemented by bitmap_new_empty() 92 | 93 | bitmap(integer) -> bitmap implemented by bitmap_new() 94 | 95 | bitmap_setbit(bitmap, integer) -> bitmap 96 | 97 | bitmap_testbit(bitmap, integer) -> boolean 98 | 99 | bitmap_clearbit(bitmap, integer) -> bitmap 100 | 101 | is_empty(bitmap) -> boolean implemented by bitmap_is_empty() 102 | 103 | bitmin(bitmap) -> integer implemented by bitmap_bitmin() 104 | 105 | bitmax(bitmap) -> integer implemented by bitmap_bitmax() 106 | 107 | bitmap_setmin(bitmap, integer) -> bitmap 108 | 109 | bitmap_setmax(bitmap, integer) -> bitmap 110 | 111 | bitmap_equal(bitmap, bitmap) -> boolean 112 | 113 | bitmap_nequal(bitmap, bitmap) -> boolean 114 | 115 | bitmap_lt(bitmap, bitmap) -> boolean 116 | 117 | bitmap_le(bitmap, bitmap) -> boolean 118 | 119 | bitmap_gt(bitmap, bitmap) -> boolean 120 | 121 | bitmap_ge(bitmap, bitmap) -> boolean 122 | 123 | bitmap_cmp(bitmap, bitmap) -> integer 124 | 125 | bitmap_union(bitmap, bitmap) -> bitmap 126 | 127 | bitmap_intersection(bitmap, bitmap) -> bitmap 128 | 129 | bitmap_minus(bitmap, bitmap) -> bitmap 130 | 131 | to_array(bitmap) -> array of integer 132 | 133 | to_bitmap(array of integer) -> bitmap implemented using aggregate bitmap_of() 134 | ``` 135 | 136 | Set Returning Functions: 137 | ```.c 138 | bits(bitmap) -> set of integer implemented by bitmap_bits() 139 | ``` 140 | 141 | Operators: 142 | ```.c 143 | bitmap + integer -> bitmap implemented by bitmap_setbit() 144 | 145 | bitmap ? integer -> boolean implemented by bitmap_testbit() 146 | 147 | bitmap - integer -> bitmap implemented by bitmap_clearbit() 148 | 149 | bitmap = bitmap -> boolean implemented by bitmap_equal() 150 | 151 | bitmap <> bitmap -> boolean implemented using bitmap_equal() 152 | 153 | bitmap < bitmap -> boolean implemented by bitmap_lt() 154 | 155 | bitmap <= bitmap -> boolean implemented by bitmap_le() 156 | 157 | bitmap > bitmap -> boolean implemented by bitmap_gt() 158 | 159 | bitmap >= bitmap -> boolean implemented by bitmap_ge() 160 | 161 | bitmap + bitmap -> bitmap implemented by bitmap_union() 162 | 163 | bitmap * bitmap -> bitmap implemented by bitmap_intersection() 164 | 165 | bitmap - bitmap -> bitmap implemented by bitmap_minus() 166 | ``` 167 | 168 | Aggregates: 169 | ```.c 170 | bitmap_of(integer) -> bitmap implemented by bitmap_setbit() 171 | 172 | union_of(bitmap) -> bitmap implemented by bitmap_union() 173 | 174 | intersect_of(bitmap) -> bitmap implemented by bitmap_intersection() 175 | ``` 176 | 177 | API Details and Examples 178 | ======================== 179 | 180 | Conversion to and from text 181 | --------------------------- 182 | ``` 183 | bitmap_in(text) -> bitmap 184 | 185 | bitmap_out(bitmap) -> text 186 | ``` 187 | 188 | The bitmap type has a compact textual representation that is not 189 | intended to be human-readable. This textual representation enables 190 | bitmaps to be used in hstore, and in text-based backups. 191 | 192 | In addition to the functions described above, casts, `::text`, `::bitmap`, 193 | can also be used. 194 | 195 | Creating bitmaps 196 | ---------------- 197 | ``` 198 | bitmap() -> bitmap 199 | 200 | bitmap(integer) -> bitmap 201 | 202 | to_bitmap(array of integer) -> bitmap 203 | 204 | bitmap_of(aggregate of integer) -> bitmap 205 | ``` 206 | 207 | An empty bitmap can be created using `bitmap()`. A bitmap with a single 208 | element included (ie a single bit set to 1) can also be created using 209 | `bitmap(n)`. Bitmaps are more usually created from arrays or queries. 210 | The following queries return identical bitmaps: 211 | 212 | ``` 213 | select bitmap() + 1 + 2 + 3; 214 | 215 | select bitmap(1) + 2 + 3; 216 | 217 | select bitmap_setbit(bitmap_setbit(bitmap(1), 2), 3); 218 | 219 | select to_bitmap('{1, 2, 3}'); 220 | 221 | select array[1, 2, 3]::bitmap; 222 | 223 | select bitmap_of(x) 224 | from generate_series(1, 3) x; 225 | ``` 226 | 227 | Bit Manipulation and Testing 228 | ---------------------------- 229 | ``` 230 | bitmap_setbit(bitmap, integer) -> bitmap 231 | 232 | bitmap + integer -> bitmap 233 | 234 | bitmap_clearbit(bitmap, integer) -> bitmap 235 | 236 | bitmap - integer -> bitmap 237 | 238 | bitmap_testbit(bitmap, integer) -> boolean 239 | 240 | bitmap ? integer -> boolean 241 | ``` 242 | Elements can be added to a bitmap using the `bitmap_setbit()` function 243 | or the `+` operator. They can be removed using `bitmap_clearbit()` or 244 | the `-` operator, and can be tested using the `bitmap_testbit()` 245 | function or the `?` operator. The setbit and clearbit functions are 246 | rarely directly used in SQL, as array or aggregation operations are 247 | usually faster. 248 | 249 | This is how you might test for a privilege, in a round-about sort of way: 250 | ``` 251 | select bitmap_of(privilege_id) ? 42 252 | from my_privileges; 253 | ``` 254 | Bitmap Range Functions 255 | ---------------------- 256 | ``` 257 | is_empty(bitmap) -> boolean 258 | 259 | bitmin(bitmap) -> integer 260 | 261 | bitmin(bitmap) -> integer 262 | 263 | bitmap_setmin(bitmap, integer) -> bitmap 264 | 265 | bitmap_setmax(bitmap, integer) -> bitmap 266 | ``` 267 | Bitmaps are stored as ranges of bits. There are a number of functions 268 | for checking and manipulating bitmap ranges. 269 | 270 | `is_empty()` returns true if the bitmap contains no elements. 271 | 272 | `bitmin()` returns the lowest value element in the bitmap (ie the lowest 273 | bit that is set). If the bitmap is empty, it returns null. 274 | 275 | `bitmax()` returns the highest element in the bitmap, or null if the bitmap is 276 | empty. 277 | 278 | `bitmap_setmin()` and `bitmap_setmax()` can be used to efficiently clear 279 | large sections of a bitmap. The result of: 280 | 281 | ``` 282 | select bitmap_setmin(bitmap_of(x), 200) 283 | from generate_series(1, 205) x; 284 | ``` 285 | 286 | would be a bitmap with elements 200 to 205. 287 | 288 | Bitmap Comparison Functions and Operators 289 | ----------------------------------------- 290 | ``` 291 | bitmap_equal(bitmap, bitmap) -> boolean 292 | 293 | bitmap_nequal(bitmap, bitmap) -> boolean 294 | 295 | bitmap_lt(bitmap, bitmap) -> boolean 296 | 297 | bitmap_le(bitmap, bitmap) -> boolean 298 | 299 | bitmap_gt(bitmap, bitmap) -> boolean 300 | 301 | bitmap_ge(bitmap, bitmap) -> boolean 302 | 303 | bitmap_cmp(bitmap, bitmap) -> integer 304 | 305 | bitmap = bitmap -> boolean 306 | 307 | bitmap <> bitmap -> boolean 308 | 309 | bitmap < bitmap -> boolean 310 | 311 | bitmap <= bitmap -> boolean 312 | 313 | bitmap > bitmap -> boolean 314 | 315 | bitmap >= bitmap -> boolean 316 | ``` 317 | Bitmaps may be compared. This is primarily for the purpose of sorting 318 | and indexing. Testing for equality or inequality is probably the only 319 | useful comparison from an API perspective. 320 | 321 | Set Operations on Bitmaps 322 | ------------------------- 323 | ``` 324 | bitmap_union(bitmap, bitmap) -> bitmap 325 | 326 | bitmap_intersection(bitmap, bitmap) -> bitmap 327 | 328 | bitmap_minus(bitmap, bitmap) -> bitmap 329 | 330 | bitmap + bitmap -> bitmap 331 | 332 | bitmap * bitmap -> bitmap 333 | 334 | bitmap - bitmap -> bitmap 335 | ``` 336 | These functions and operators act on a pair of bitmaps to yield a 337 | result. 338 | 339 | `bitmap_union()`, the `+` operator, returns a bitmap containing all 340 | elements from both arguments. The following queries return identical 341 | results: 342 | 343 | ``` 344 | select bitmap_union(to_bitmap({'1 2 3'}), 345 | to_bitmap({'1 3 5'})); 346 | 347 | select to_bitmap({'1 2 3 5}'); 348 | ``` 349 | 350 | `bitmap_intersect()`, the `*` operator, yields the set of common elements 351 | from its arguments. The following queries return identical results: 352 | ``` 353 | select to_bitmap({'1 2 3}') * to_bitmap('{3, 4, 5}'); 354 | 355 | select bitmap(3); 356 | ``` 357 | 358 | `bitmap_minus()`, the `-` operator, yields a bitmap containing all elements 359 | from the first argument that do not appear in the second. These queries 360 | return identical results: 361 | ``` 362 | select to_bitmap({'1 2 3}') - to_bitmap('{3, 4, 5}'); 363 | 364 | select to_bitmap('{1, 2}'); 365 | ``` 366 | 367 | Extracting all Elements of a Bitmap 368 | ----------------------------------- 369 | ``` 370 | to_array(bitmap) -> array of integer 371 | 372 | bits(bitmap) -> set of integer 373 | ``` 374 | 375 | These functions return the elements of a bitmap, either as an array, or 376 | as a set. Use the set returning function like this: 377 | ``` 378 | select bits as privilege_id 379 | from bits(privileges); 380 | ``` 381 | The `to_array()` function can also be invoked as a cast, eg: 382 | ``` 383 | select privileges::int4[]; 384 | ``` 385 | 386 | Bitmap Aggregates 387 | ----------------- 388 | ``` 389 | union_of(bitmap) -> bitmap 390 | 391 | intersect_of(bitmap) -> bitmap 392 | ``` 393 | 394 | These functions aggregate a collection of bitmaps using the union or 395 | intersect operations. Eg to identify all privileges of all members of a 396 | group of offices, we could use something like this: 397 | ``` 398 | select union_of(privs) as office_privs, office_name 399 | from user_privs 400 | where office_name like '%admin%' 401 | group by office_name; 402 | ``` 403 | 404 | Installing pgbitmap using pgxn 405 | ------------------------------ 406 | 407 | If you're using the pgxn client all you need to do is this: 408 | ``` 409 | $ pgxn install pgbitmap 410 | $ pgxn load -d mydb pgbitmap 411 | ``` 412 | 413 | Building pgbitmap Manually 414 | -------------------------- 415 | 416 | Pgbitmap can be built using the standard Postgres PGXS build mechanism 417 | as described here 418 | [https://www.postgresql.org/docs/12/extend-pgxs.html]. 419 | 420 | The build will need to be able to find the pg_config executable that 421 | matches your Postgres version. It will attempt to find this using 422 | `find_pg_config ` (in the top-level pgbitmap directory). If it cannot 423 | find pg_config the build will fail. 424 | 425 | You can manually define the location in the `PG_CONFIG` file: 426 | ``` 427 | $ echo >PG_CONFIG 428 | ``` 429 | 430 | From the pgbitmap directory (the root directory of the extension), use 431 | the following commands: 432 | ``` 433 | $ make 434 | ``` 435 | To build the extension, followed by: 436 | 437 | ``` 438 | $ sudo make install 439 | ``` 440 | To install it. You may then need to stop and restart your database 441 | service to have postgres recognise the extension. 442 | 443 | To test the installation use: 444 | 445 | ``` 446 | $ make test 447 | ``` 448 | 449 | To create html documentation (in docs/html/index.html) use: 450 | 451 | ``` 452 | $ make docs 453 | ``` 454 | 455 | You will need to have doxygen and dot installed. 456 | 457 | -------------------------------------------------------------------------------- /test/test_bitmap.sql: -------------------------------------------------------------------------------- 1 | -- Format the output for quiet tests. 2 | \set ECHO none 3 | \set QUIET 1 4 | \pset format unaligned 5 | \pset tuples_only true 6 | \pset pager 7 | 8 | 9 | -- If there were any failures we need to fail with an error status 10 | \set ON_ERROR_ROLLBACK 1 11 | \set ON_ERROR_STOP true 12 | 13 | begin; 14 | 15 | create or replace 16 | function record_test(n integer) returns bool as 17 | $$ 18 | begin 19 | create temporary table if not exists my_tests ( 20 | tests_run bitmap 21 | ); 22 | insert 23 | into my_tests 24 | select bitmap() 25 | where not exists (select 1 from my_tests); 26 | update my_tests set tests_run = tests_run + n; 27 | return false; 28 | end; 29 | $$ 30 | language 'plpgsql' security definer volatile 31 | set client_min_messages = 'error'; 32 | 33 | 34 | 35 | -- Create functions for running tests. 36 | create or replace 37 | function expect(cmd text, n integer, msg text) 38 | returns bool as 39 | $$ 40 | declare 41 | res integer; 42 | rc integer; 43 | begin 44 | execute cmd into res; 45 | get diagnostics rc = row_count; 46 | if rc = 1 then 47 | if (res != n) or ((res is null) != (n is null)) then 48 | raise exception '% Expecting %, got %', msg, n, res; 49 | end if; 50 | else 51 | raise exception '% Expecting %, got no rows', msg, n; 52 | end if; 53 | return false; 54 | end; 55 | $$ 56 | language 'plpgsql' security definer volatile; 57 | 58 | create or replace 59 | function expect(cmd text, n bool, msg text) 60 | returns bool as 61 | $$ 62 | declare 63 | res bool; 64 | rc integer; 65 | begin 66 | execute cmd into res; 67 | get diagnostics rc = row_count; 68 | if rc = 1 then 69 | if (res != n) or ((res is null) != (n is null)) then 70 | raise exception '% Expecting %, got %', msg, n, res; 71 | end if; 72 | else 73 | raise exception '% Expecting %, got no rows', msg, n; 74 | end if; 75 | return false; 76 | end; 77 | $$ 78 | language 'plpgsql' security definer volatile; 79 | 80 | create or replace 81 | function expect(val integer, n integer, msg text) 82 | returns bool as 83 | $$ 84 | begin 85 | if (val != n) or ((val is null) != (n is null)) then 86 | raise exception '% Expecting %, got %', msg, n, val; 87 | end if; 88 | return false; 89 | end; 90 | $$ 91 | language 'plpgsql' security definer volatile; 92 | 93 | create or replace 94 | function expect(val bool, n bool, msg text) 95 | returns bool as 96 | $$ 97 | begin 98 | if (val != n) or ((val is null) != (n is null)) then 99 | raise exception '% Expecting %, got %', msg, n, val; 100 | end if; 101 | return false; 102 | end; 103 | $$ 104 | language 'plpgsql' security definer volatile; 105 | 106 | create extension pgbitmap; 107 | 108 | -- We put expect() into the where clause so that no rows are returned 109 | -- from each test. This enables much cleaner output. 110 | 111 | -- Test bits() on empty and non-empty bitmaps 112 | select null 113 | where record_test(1) 114 | or expect((select count(*) from bits(bitmap()))::integer, 115 | 0, 'EMPTY BITMAP SHOULD HAVE 0 BITS') 116 | or expect((select count(*) from bits(bitmap(532)))::integer, 117 | 1, 'THIS NON-EMPTY-BITMAP SHOULD HAVE 1 BIT'); 118 | 119 | select null 120 | where record_test(2) 121 | or expect('select bits from bits(bitmap(532))', 122 | 532, 'NON-EMPTY-BITMAP NOT FOUND(2)'); 123 | 124 | -- Test that serialisation of bitmap yields sane results 125 | select null 126 | where record_test(3) 127 | or expect('select * from bits(cast(cast(bitmap(532) as text) as bitmap))', 128 | 532, 'BITMAP SERIALISATION FAILS'); 129 | 130 | -- Test is_empty() 131 | select null 132 | where record_test(4) 133 | or expect(is_empty(bitmap()), 134 | true, 'BITMAP SHOULD BE EMPTY') 135 | or expect(is_empty(bitmap(5)), 136 | false, 'BITMAP SHOULD NOT BE EMPTY'); 137 | 138 | -- Test bitmin() 139 | select null 140 | where record_test(5) 141 | or expect(bitmin(bitmap()), 142 | null, 'BITMIN OF EMPTY BITMAP SHOULD BE NULL') 143 | or expect(bitmin(bitmap(14)), 144 | 14, 'BITMIN SHOULD BE 14') 145 | or expect(bitmin(bitmap(73)), 146 | 73, 'BITMIN SHOULD BE 73'); 147 | 148 | -- Test bitmax() 149 | select null 150 | where record_test(6) 151 | or expect(bitmax(bitmap()), 152 | null, 'BITMAX OF EMPTY BITMAP SHOULD BE NULL') 153 | or expect(bitmax(bitmap(14)), 154 | 14, 'BITMAX SHOULD BE 14') 155 | or expect(bitmax(bitmap(73)), 156 | 73, 'BITMAX SHOULD BE 73'); 157 | 158 | -- Test setbit, testbit 159 | with set1 as ( 160 | select bitmap(2099) as bm1), 161 | set2 as ( 162 | select bitmap_setbit(bm1, 199) as bm2 from set1), 163 | set3 as ( 164 | select bm2 + 23 as bm3 from set2), 165 | bits as ( 166 | select bits from set3 cross join bits(bm3)) 167 | select null 168 | from bits 169 | where record_test(7) 170 | or expect((select bits from bits where bits = 23), 171 | 23, '23 MUST BE IN THE SET') 172 | or expect((select bits from bits where bits = 199), 173 | 199, '199 MUST BE IN THE SET') 174 | or expect((select bits from bits where bits = 2099), 175 | 2099, '2099 MUST BE IN THE SET') 176 | union all 177 | select null 178 | where expect((select count(*) from bits)::integer, 179 | 3, 'THERE MUST BE 3 ELEMENTS IN BITS') 180 | union all 181 | select null 182 | from set3 183 | cross join set2 184 | cross join set1 185 | where expect(bitmin(bm3), 23, 'BITMIN OF SET3 MUST BE 23') 186 | or expect(bitmin(bm2), 199, 'BITMIN OF SET2 MUST BE 199') 187 | or expect(bitmin(bm1), 2099, 'BITMIN OF SET1 MUST BE 2099') 188 | or expect(bitmax(bm1), 2099, 'BITMAX OF SET1 MUST BE 2099') 189 | or expect(bitmax(bm2), 2099, 'BITMAX OF SET2 MUST BE 2099') 190 | or expect(bitmax(bm3), 2099, 'BITMAX OF SET3 MUST BE 2099') 191 | or expect(bitmap_testbit(bm3, 199), true, '199 MUST BE FOUND IN SET3') 192 | or expect(bm3 ? 199, true, '199 MUST BE FOUND IN SET3(2)') 193 | or expect(bitmap_testbit(bm1, 199), false, '199 MUST NOT BE FOUND IN SET1') 194 | or expect(bm1 ? 199, false, '199 MUST NOT BE FOUND IN SET1(2)'); 195 | 196 | 197 | -- Test clearbit operations 198 | with set1 as ( 199 | select bitmap(23) + 217 + 98 + 99 as bm1) 200 | select null 201 | from set1 202 | where record_test(8) 203 | or expect((select count(*) from bits(bm1 - 217))::integer, 204 | 3, 'THERE SHOULD BE 3 BITS AFTER CLEARING ONE') 205 | or expect((select count(*) from bits(bitmap_clearbit(bm1, 99)))::integer, 206 | 3, 'THERE SHOULD BE 3 BITS AFTER CLEARING ONE(2)') 207 | or expect((select count(*) from bits(bitmap_clearbit(bm1, 7)))::integer, 208 | 4, 'THERE SHOULD BE 4 BITS AFTER CLEARING NONE'); 209 | 210 | 211 | with set1 as ( 212 | select bitmap(23) + 217 + 98 + 99 as bm1) 213 | select null 214 | from set1 215 | where record_test(9) 216 | or expect((select count(*) from bits(bm1 - 217))::integer, 217 | 3, 'THERE SHOULD BE 3 BITS AFTER CLEARING ONE') 218 | or expect((select count(*) from bits(bitmap_clearbit(bm1, 99)))::integer, 219 | 3, 'THERE SHOULD BE 3 BITS AFTER CLEARING ONE(2)') 220 | or expect((select count(*) from bits(bitmap_clearbit(bm1, 7)))::integer, 221 | 4, 'THERE SHOULD BE 4 BITS AFTER CLEARING NONE'); 222 | 223 | -- Test union operations 224 | with set1 as ( 225 | select bitmap(23) + bitmap(217) as bm1), 226 | set2 as ( 227 | select bitmap(98) + bitmap(99) as bm2) 228 | select null 229 | from set1 cross join set2 230 | where record_test(10) 231 | or expect(bm1 ? 23, true, '23 MUST BE IN UNION') 232 | or expect(bm1 ? 217, true, '217 MUST BE IN UNION') 233 | or expect(bm2 ? 98, true, '98 MUST BE IN UNION') 234 | or expect(bm2 ? 99, true, '99 MUST BE IN UNION') 235 | or expect((bm1 + bm2) ? 217, true, '217 MUST BE IN SUPER-UNION') 236 | or expect((bm1 + bm2) ? 99, true, '99 MUST BE IN SUPER-UNION') 237 | or expect((select count(*) from bits(bm1 + bm2))::integer, 238 | 4, 'SUPERUNION MUST HAVE 4 ENTRIES'); 239 | 240 | -- Intersect and basic aggregate operations 241 | with set1 as ( 242 | select bitmap(23) + 24 + 27 + 28 as bm1), 243 | set2 as ( 244 | select bitmap_of(x) as bm2 245 | from (select generate_series as x 246 | from generate_series(26, 35)) s) 247 | select null 248 | from set1 cross join set2 249 | where record_test(11) 250 | or expect((select count(*) from bits(bm1 * bm2))::integer, 251 | 2, 'INTERSECTION SHOULD HAVE 2 ENTRIES') 252 | or expect((bm1 * bm2) ? 27, true, '27 SHOULD BE IN INTERSECTION') 253 | or expect((bm1 * bm2) ? 28, true, '28 SHOULD BE IN INTERSECTION') 254 | or expect((bm1 * bm2) ? 26, false, '26 SHOULD NOT BE IN INTERSECTION'); 255 | 256 | 257 | -- Bitmap union as an aggregate 258 | with set1 as ( 259 | select bitmap(23) + 24 + 27 + 28 as bm1), 260 | set2 as ( 261 | select bitmap_of(x) as bm2 262 | from (select generate_series as x 263 | from generate_series(26, 35)) s), 264 | all_sets as ( 265 | select bm1 as bma from set1 union all select bm2 from set2), 266 | set3 as ( 267 | select union_of(bma) as bm3 from all_sets) 268 | select null 269 | from set3 270 | where record_test(12) 271 | or expect((select count(*) from bits(bm3))::integer, 272 | 12, 'BITMAP UNION SHOULD HAVE 12 ELEMENTS'); 273 | 274 | 275 | -- Array to bitmap 276 | with array1 as ( 277 | select array(select generate_series(99, 130)) as a1), 278 | set1 as ( 279 | select to_bitmap(a1) as bm1 -- bitmap from array 280 | from array1), 281 | set2 as ( 282 | select bitmap(123) + 124 as bm2), 283 | set3 as ( 284 | select bm1 * bm2 as bm3 -- Intersect 285 | from set1 cross join set2) 286 | select null 287 | from set3 288 | where record_test(13) 289 | or expect((select count(*) from bits(bm3))::integer, 2, 290 | 'INTERSECT FROM ARRAY SHOULD HAVE 2 ELEMENTS') 291 | or expect(bm3 ? 123, true, 'INTERSECT FROM ARRAY SHOULD CONTAIN 123'); 292 | 293 | -- bitmap to array 294 | with array1 as ( 295 | select array(select generate_series(99, 130)) as a1), 296 | set1 as ( 297 | select to_bitmap(a1) as bm1 -- bitmap from array 298 | from array1), 299 | array2 as ( 300 | select to_array(bm1) as a2 301 | from set1) 302 | select null 303 | from array1 cross join array2 304 | where record_test(14) 305 | or expect(a1 = a2, true, 306 | 'ARRAYS SHOULD BE EQUAL'); 307 | 308 | 309 | -- bitmap equality 310 | with array1 as ( 311 | select array(select generate_series(99, 130)) as a1), 312 | set1 as ( 313 | select to_bitmap(a1) as bm1 -- bitmap from array 314 | from array1), 315 | set2 as ( 316 | select to_bitmap(a1) as bm2 -- bitmap from array 317 | from array1) 318 | select null 319 | from set1 cross join set2 320 | where record_test(15) 321 | or expect(bm1 = bm2, true, 'BITMAPS SHOULD BE EQUAL') 322 | or expect((bm1 - 122) = bm2, false, 'BITMAPS SHOULD NOT BE EQUAL') 323 | or expect((bm1 - 122) != bm2, true, 'BITMAPS SHOULD NOT BE EQUAL(2)'); 324 | 325 | 326 | -- setmin 327 | with set1 as ( 328 | select to_bitmap('{13, 171, 222, 279, 322, 700}') as bm1), 329 | set2 as ( 330 | select bitmap_setmin(bm1, 171) as bm2 from set1) 331 | select null 332 | from set2 333 | where record_test(16) 334 | or expect((select count(*) from bits(bm2))::integer, 335 | 5, 'SHOULD BE 5 ELEMENTS AFTER SETMIN') 336 | or expect((select min(bits) from bits(bm2)), 337 | 171, 'SETMIN SMALLEST SHOULD BE 171') 338 | or expect(bitmin(bm2), 171, 'SETMIN SMALLEST SHOULD BE 171(2)'); 339 | 340 | -- setmax 341 | with set1 as ( 342 | select to_bitmap('{13, 171, 222, 279, 322, 701, 900}') as bm1), 343 | set2 as ( 344 | select bitmap_setmax(bm1, 700) as bm2 from set1) 345 | select null 346 | from set2 347 | where record_test(17) 348 | or expect((select count(*) from bits(bm2))::integer, 349 | 5, 'SHOULD BE 5 ELEMENTS AFTER SETMAX') 350 | or expect((select max(bits) from bits(bm2)), 351 | 322, 'SETMAX LARGEST SHOULD BE 322') 352 | or expect(bitmax(bm2), 322, 'SETMAX LARGEST SHOULD BE 322(2)'); 353 | 354 | -- bitmap_minus 355 | with set1 as ( 356 | select to_bitmap('{13, 171, 222, 279, 322, 701, 900}') as bm1), 357 | set2 as ( 358 | select to_bitmap('{13, 172, 223, 279, 322, 701, 900}') as bm2), 359 | set3 as ( 360 | select bm1 - bm2 as bm3 from set1 cross join set2), 361 | set4 as ( 362 | select bitmap_minus(bm2, bm1) as bm4 from set1 cross join set2), 363 | set5 as ( 364 | select to_bitmap('{171, 222}') as bm5), -- bm1 - bm2 365 | set6 as ( 366 | select to_bitmap('{172, 223}') as bm6) -- bm2 - bm1 367 | select null 368 | from set3 cross join set4 cross join set5 cross join set6 369 | where record_test(18) 370 | or expect(bm3 = bm5, true, 'BM3 SHOULD MATCH BM5') 371 | or expect(bm4 = bm6, true, 'BM4 SHOULD MATCH BM6'); 372 | 373 | -- intersect aggregate 374 | with set1 as ( 375 | select to_bitmap('{13, 171, 222, 279, 322, 701, 900}') as bm1), 376 | set2 as ( 377 | select to_bitmap('{13, 172, 223, 279, 322, 701, 900}') as bm2), 378 | set3 as ( 379 | select to_bitmap('{172, 223, 279, 322, 701}') as bm3), 380 | setall as ( 381 | select bm1 as bma from set1 382 | union 383 | select bm2 from set2 384 | union 385 | select bm3 from set3), 386 | isect as ( 387 | select intersect_of(bma) bi from setall) 388 | select null 389 | from isect 390 | where record_test(19) 391 | or expect((select count(*) from bits(bi))::integer, 392 | 3, 'INTERSECTION AGGREGATE SHOULD HAVE 3 ENTRIES') 393 | or expect(bi ? 279, true, '279 SHOULD BE IN INTERSECTION AGG') 394 | or expect(bi ? 322, true, '322 SHOULD BE IN INTERSECTION AGG') 395 | or expect(bi ? 701, true, '701 SHOULD BE IN INTERSECTION AGG'); 396 | 397 | -- Publish the set of tests that we have run 398 | select 'Tests run: ' || to_array(tests_run)::text as "Passed tests" 399 | from my_tests; 400 | 401 | -- Misceleaneous 402 | -- Bitmap corruption bug in version 0.2 403 | with x as (select (bitmap() + 1)::text) 404 | select null from x 405 | where record_test(19); 406 | 407 | -- Server crash bug in version 0.2 408 | select union_of(x) from (select null::bitmap as x) x; 409 | 410 | -- Null handling bug in version 0.3alpha 411 | create or replace 412 | function test3a( 413 | param in integer, 414 | privs out bitmap, 415 | x out integer) 416 | returns setof record as 417 | $$ 418 | with base(ord, priv) as 419 | ( 420 | values (1, 1), 421 | (2, null), 422 | (3, 3) 423 | ), 424 | base2(ord, priv) as 425 | ( 426 | values (4, 4), 427 | (5, null), 428 | (6, 6) 429 | ), 430 | set1 as 431 | ( 432 | select bitmap_of(priv) 433 | from base 434 | ), 435 | set2 as 436 | ( 437 | select bitmap_of(priv) 438 | from base2 439 | ), 440 | set3(ord, privs, x) as 441 | ( 442 | select param, bitmap_of(priv), 1 443 | from base2 444 | union 445 | select 2, null, 1 446 | union 447 | select 3, bitmap_of(priv), 1 448 | from base 449 | ), 450 | set4(privs, x) as 451 | ( 452 | select union_of(privs), x 453 | from set3 454 | group by x 455 | ) 456 | select privs, x 457 | from set4; 458 | 459 | $$ 460 | language 'sql'; 461 | 462 | select null 463 | from test3a(1) x 464 | cross join test3a(4) y 465 | where record_test(20) 466 | or expect(x.privs = y.privs, true, 467 | 'test3a bitmaps are not equal'); 468 | 469 | rollback; 470 | \echo ALL TESTS PASSED 471 | 472 | -------------------------------------------------------------------------------- /src/pgbitmap.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pgbitmap.c 3 | * \code 4 | * Author: Marc Munro 5 | * Copyright (c) 2020 Marc Munro 6 | * License: BSD 7 | * 8 | * \endcode 9 | * @brief 10 | * Define a bitmap type for postgres. 11 | * 12 | */ 13 | 14 | #include "pgbitmap.h" 15 | 16 | PG_MODULE_MAGIC; 17 | 18 | 19 | /** 20 | * The length of a 64-bit integer as a base64 string. 21 | * This should really be 8 but the last char is always '=' so we can 22 | * optimise it away. 23 | */ 24 | #define INT32SIZE_B64 7 25 | 26 | /** 27 | * The length of a 32-bit integer as a base64 string. 28 | */ 29 | #define INT64SIZE_B64 12 30 | 31 | 32 | 33 | #ifdef USE_64_BIT 34 | /** 35 | * Array of bit positions for int64, indexed by bitno. 36 | */ 37 | static 38 | uint64 bitmasks[64] = { 39 | 0x00000001, 0x00000002, 0x00000004, 0x00000008, 40 | 0x00000010, 0x00000020, 0x00000040, 0x00000080, 41 | 0x00000100, 0x00000200, 0x00000400, 0x00000800, 42 | 0x00001000, 0x00002000, 0x00004000, 0x00008000, 43 | 0x00010000, 0x00020000, 0x00040000, 0x00080000, 44 | 0x00100000, 0x00200000, 0x00400000, 0x00800000, 45 | 0x01000000, 0x02000000, 0x04000000, 0x08000000, 46 | 0x10000000, 0x20000000, 0x40000000, 0x80000000, 47 | 0x0000000100000000, 0x0000000200000000, 48 | 0x0000000400000000, 0x0000000800000000, 49 | 0x0000001000000000, 0x0000002000000000, 50 | 0x0000004000000000, 0x0000008000000000, 51 | 0x0000010000000000, 0x0000020000000000, 52 | 0x0000040000000000, 0x0000080000000000, 53 | 0x0000100000000000, 0x0000200000000000, 54 | 0x0000400000000000, 0x0000800000000000, 55 | 0x0001000000000000, 0x0002000000000000, 56 | 0x0004000000000000, 0x0008000000000000, 57 | 0x0010000000000000, 0x0020000000000000, 58 | 0x0040000000000000, 0x0080000000000000, 59 | 0x0100000000000000, 0x0200000000000000, 60 | 0x0400000000000000, 0x0800000000000000, 61 | 0x1000000000000000, 0x2000000000000000, 62 | 0x4000000000000000, 0x8000000000000000}; 63 | 64 | #else 65 | /** 66 | * Array of bit positions for int32, indexed by bitno. 67 | */ 68 | static 69 | uint32 bitmasks[] = {0x00000001, 0x00000002, 0x00000004, 0x00000008, 70 | 0x00000010, 0x00000020, 0x00000040, 0x00000080, 71 | 0x00000100, 0x00000200, 0x00000400, 0x00000800, 72 | 0x00001000, 0x00002000, 0x00004000, 0x00008000, 73 | 0x00010000, 0x00020000, 0x00040000, 0x00080000, 74 | 0x00100000, 0x00200000, 0x00400000, 0x00800000, 75 | 0x01000000, 0x02000000, 0x04000000, 0x08000000, 76 | 0x10000000, 0x20000000, 0x40000000, 0x80000000}; 77 | 78 | #endif 79 | 80 | 81 | /* BEGIN SECTION OF CODE COPIED FROM pgcrypto.c (via veil) */ 82 | 83 | static const char _base64[] = 84 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 85 | 86 | static const short b64lookup[128] = { 87 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 88 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 89 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 90 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, 91 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 92 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 93 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 94 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 95 | }; 96 | 97 | static unsigned 98 | b64_encode(const char *src, unsigned len, char *dst) 99 | { 100 | char *p, 101 | *lend = dst + 76; 102 | const char *s, 103 | *end = src + len; 104 | int pos = 2; 105 | uint32 buf = 0; 106 | 107 | s = src; 108 | p = dst; 109 | 110 | while (s < end) 111 | { 112 | buf |= (unsigned char) *s << (pos << 3); 113 | pos--; 114 | s++; 115 | 116 | /* write it out */ 117 | if (pos < 0) 118 | { 119 | *p++ = _base64[(buf >> 18) & 0x3f]; 120 | *p++ = _base64[(buf >> 12) & 0x3f]; 121 | *p++ = _base64[(buf >> 6) & 0x3f]; 122 | *p++ = _base64[buf & 0x3f]; 123 | 124 | pos = 2; 125 | buf = 0; 126 | } 127 | if (p >= lend) 128 | { 129 | *p++ = '\n'; 130 | lend = p + 76; 131 | } 132 | } 133 | if (pos != 2) 134 | { 135 | *p++ = _base64[(buf >> 18) & 0x3f]; 136 | *p++ = _base64[(buf >> 12) & 0x3f]; 137 | *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '='; 138 | *p++ = '='; 139 | } 140 | 141 | return p - dst; 142 | } 143 | 144 | static unsigned 145 | b64_decode(const char *src, unsigned len, char *dst) 146 | { 147 | const char *srcend = src + len, 148 | *s = src; 149 | char *p = dst; 150 | char c; 151 | int b = 0; 152 | uint32 buf = 0; 153 | int pos = 0, 154 | end = 0; 155 | 156 | while (s < srcend) 157 | { 158 | c = *s++; 159 | 160 | if (c == ' ' || c == '\t' || c == '\n' || c == '\r') 161 | continue; 162 | 163 | if (c == '=') 164 | { 165 | /* end sequence */ 166 | if (!end) 167 | { 168 | if (pos == 2) 169 | end = 1; 170 | else if (pos == 3) 171 | end = 2; 172 | else 173 | ereport(ERROR, 174 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), 175 | errmsg("unexpected \"=\""))); 176 | } 177 | b = 0; 178 | } 179 | else 180 | { 181 | b = -1; 182 | if (c > 0 && c < 127) 183 | b = b64lookup[(unsigned char) c]; 184 | if (b < 0) 185 | ereport(ERROR, 186 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), 187 | errmsg("invalid symbol"))); 188 | } 189 | /* add it to buffer */ 190 | buf = (buf << 6) + b; 191 | pos++; 192 | if (pos == 4) 193 | { 194 | *p++ = (buf >> 16) & 255; 195 | if (end == 0 || end > 1) 196 | *p++ = (buf >> 8) & 255; 197 | if (end == 0 || end > 2) 198 | *p++ = buf & 255; 199 | buf = 0; 200 | pos = 0; 201 | } 202 | } 203 | 204 | if (pos != 0) 205 | ereport(ERROR, 206 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), 207 | errmsg("invalid end sequence"))); 208 | 209 | return p - dst; 210 | } 211 | 212 | /* END SECTION OF CODE COPIED FROM pgcrypto.c */ 213 | 214 | /* 215 | * Low-level bit operation functions follow 216 | ********************************************************************** 217 | */ 218 | 219 | 220 | 221 | 222 | /** 223 | * Predicate identifying whether the bitmap is empty. This takes a 224 | * pretty simplistic view of what it means to be empty: any properly 225 | * contructed non-empty bitmap will have bits set in at least the bitmin 226 | * and bitmax positions, 227 | * 228 | * @param bitmap The ::Bitmap being scanned. 229 | * 230 | * @return True if the bit at bitmin is zero. 231 | */ 232 | 233 | static boolean 234 | bitmapEmpty(Bitmap *bitmap) 235 | { 236 | return ((bitmap->bitset[0]) == 0); 237 | } 238 | 239 | /** 240 | * Clear all bits in a ::Bitmap. 241 | * 242 | * @param bitmap The ::Bitmap within which all bits are to be cleared 243 | */ 244 | static void 245 | clearBitmap(Bitmap *bitmap) 246 | { 247 | int32 elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax); 248 | int32 i; 249 | 250 | for (i = 0; i < elems; i++) { 251 | bitmap->bitset[i] = 0; 252 | } 253 | } 254 | 255 | 256 | /** 257 | * Return a new, possibly uninitialised, ::Bitmap. 258 | * 259 | * @param min The lowest value bit to be stored in the bitmap 260 | * @param max The highest value bit to be stored in the bitmap 261 | * 262 | * @return New appropriately sized bitmap. 263 | */ 264 | static Bitmap * 265 | newBitmap(int32 min, int32 max) 266 | { 267 | int32 elems = ARRAYELEMS(min, max); 268 | int32 size = sizeof(Bitmap) + (sizeof(bm_int) * (elems + DBG_ELEMS)); 269 | Bitmap *bitmap = bitmap = palloc(size); 270 | 271 | SET_VARSIZE(bitmap, size); 272 | 273 | bitmap->bitmin = min; 274 | bitmap->bitmax = max; 275 | SETCANARY(bitmap); 276 | return bitmap; 277 | } 278 | 279 | 280 | /** 281 | * Test a bit within a ::Bitmap. If the bit is outside of the acceptable 282 | * range return false. 283 | * 284 | * @param bitmap The ::Bitmap within which the bit is to be set. 285 | * @param bit The bit to be tested. 286 | * 287 | * @return True if the bit is set, false otherwise. 288 | */ 289 | bool 290 | bitmapTestbit(Bitmap *bitmap, 291 | int32 bit) 292 | { 293 | if ((bit > bitmap->bitmax) || 294 | (bit < bitmap->bitmin)) 295 | { 296 | return false; 297 | } 298 | else { 299 | int32 relative_bit = bit - BITZERO(bitmap->bitmin); 300 | int32 element = BITSET_ELEM(relative_bit); 301 | 302 | return (bitmap->bitset[element] & 303 | bitmasks[BITSET_BIT(relative_bit)]) != 0; 304 | } 305 | } 306 | 307 | #ifdef BITMAP_DEBUG 308 | static void 309 | printBitmap(char *label, Bitmap *bitmap) 310 | { 311 | int i; 312 | fprintf(stderr, "%s: %s<%d, %d>:", label, 313 | CHKCANARY(bitmap)? "": "BROKEN BITMAP", 314 | bitmap->bitmin, bitmap->bitmax); 315 | for (i = bitmap->bitmin; i <= bitmap->bitmax; i++) { 316 | fprintf(stderr, "%c", bitmapTestbit(bitmap, i)? '1': '0'); 317 | } 318 | 319 | fprintf(stderr, ":\n"); 320 | } 321 | 322 | static char * 323 | bitmapString(Bitmap *bitmap) 324 | { 325 | static char result[200]; 326 | int i; 327 | char *here; 328 | i = sprintf(result, "%s<%d, %d>", 329 | CHKCANARY(bitmap)? "": "BROKEN BITMAP:", 330 | bitmap->bitmin, bitmap->bitmax); 331 | here = result + i; 332 | for (i = bitmap->bitmin; i <= bitmap->bitmax; i++) { 333 | here += sprintf(here, "%c", bitmapTestbit(bitmap, i)? '1': '0'); 334 | } 335 | 336 | return result; 337 | } 338 | #endif 339 | 340 | /** 341 | * Create a copy of a bitmap. 342 | * 343 | * @param bitmap The original bitmap 344 | * 345 | * @return Copy of bitmap. 346 | */ 347 | Bitmap * 348 | bitmapCopy(Bitmap *bitmap) 349 | { 350 | Bitmap *result; 351 | int32 i; 352 | 353 | result = newBitmap(bitmap->bitmin, bitmap->bitmax); 354 | 355 | for (i = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax) - 1; i >= 0; i--) { 356 | result->bitset[i] = bitmap->bitset[i]; 357 | } 358 | return result; 359 | } 360 | 361 | 362 | /** 363 | * Take an existing bitmap, and return a larger copy that will be able 364 | * to store a specified bit. 365 | * 366 | * @param bitmap The original bitmap 367 | * @param bit A new bit that the returned bitmap must be capable of 368 | * storing. 369 | * 370 | * @return New bitmap with appropriate bitmin and bitmax values. 371 | */ 372 | static Bitmap * 373 | extendBitmap(Bitmap *bitmap, 374 | int32 bit) 375 | { 376 | Bitmap *result; 377 | int32 orig_elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax); 378 | int32 new_elems; 379 | int32 elems_offset; 380 | int32 extra_elems; 381 | int32 to_clear; 382 | int32 i; 383 | //elog(NOTICE, "Adding %d to %d->%d", bit, bitmap->bitmin, bitmap->bitmax); 384 | 385 | // Allocate new bitmap (no need to clear it) 386 | result = newBitmap(MIN(bit, bitmap->bitmin), 387 | MAX(bit, bitmap->bitmax)); 388 | //elog(NOTICE, "New: %d->%d", result->bitmin, result->bitmax); 389 | 390 | // Copy existing bitset into our new bitset. 391 | elems_offset = BITSET_ELEM(bitmap->bitmin) - BITSET_ELEM(result->bitmin); 392 | //elog(NOTICE, "orig_elems %d, offset %d", orig_elems, elems_offset); 393 | for (i = 0; i < orig_elems; i++) { 394 | //elog(NOTICE, "Copying %d into %d", i, i + elems_offset); 395 | result->bitset[i + elems_offset] = bitmap->bitset[i]; 396 | } 397 | 398 | // Clear any preceding elements 399 | for (i = 0; i < elems_offset; i++) { 400 | //elog(NOTICE, "Clearing %d", i); 401 | result->bitset[i] = 0; 402 | } 403 | 404 | // Clear any following elements 405 | new_elems = ARRAYELEMS(result->bitmin, result->bitmax); 406 | extra_elems = new_elems - orig_elems; 407 | to_clear = extra_elems - elems_offset; 408 | //elog(NOTICE, "new_elems %d, extra_elems %d, to_clear %d", 409 | // new_elems, extra_elems, to_clear); 410 | for (i = new_elems - to_clear; i < new_elems; i++) { 411 | //elog(NOTICE, "Clearing %d", i); 412 | result->bitset[i] = 0; 413 | } 414 | return result; 415 | } 416 | 417 | 418 | /** 419 | * Set a bit within a ::Bitmap. 420 | * 421 | * @param bitmap The ::Bitmap within which the bit is to be set. 422 | * @param bit The bit to be set. 423 | * 424 | * @return Possibly new bitmap with the specified bit set. 425 | */ 426 | static void 427 | doSetBit(Bitmap *bitmap, 428 | int32 bit) 429 | { 430 | int32 relative_bit; 431 | int32 element; 432 | 433 | relative_bit = bit - BITZERO(bitmap->bitmin); 434 | element = BITSET_ELEM(relative_bit); 435 | bitmap->bitset[element] |= bitmasks[BITSET_BIT(relative_bit)]; 436 | 437 | /* The tests below handle the case where the bitmap was previously 438 | * empty. */ 439 | if ((bit < bitmap->bitmin) || !bitmapTestbit(bitmap, bitmap->bitmin)) { 440 | bitmap->bitmin = bit; 441 | } 442 | if ((bit > bitmap->bitmax) || !bitmapTestbit(bitmap, bitmap->bitmax)) { 443 | bitmap->bitmax = bit; 444 | } 445 | } 446 | 447 | /** 448 | * Return a new ::Bitmap with bit set. 449 | * 450 | * @param bitmap The ::Bitmap within which the bit is to be set. 451 | * @param bit The bit to be set. 452 | * 453 | * @return New bitmap with the specified bit set. 454 | */ 455 | static Bitmap * 456 | bitmapSetbit(Bitmap *bitmap, 457 | int32 bit) 458 | { 459 | Bitmap *result; 460 | 461 | if ((bit > bitmap->bitmax) || (bit < bitmap->bitmin)) { 462 | result = extendBitmap(bitmap, bit); 463 | } 464 | else { 465 | result = bitmapCopy(bitmap); 466 | } 467 | doSetBit(result, bit); 468 | return result; 469 | } 470 | 471 | 472 | /** 473 | * Return the next set bit in the ::Bitmap. 474 | * 475 | * @param bitmap The ::Bitmap being scanned. 476 | * @param inbit The starting bit from which to scan the bitmap 477 | * @param found Boolean that will be set to true when a set bit has been 478 | * found. 479 | * 480 | * @return The bit id of the found bit, or the inbit parameter if no set 481 | * bits were found. 482 | */ 483 | static int32 484 | bitmapNextBit(Bitmap *bitmap, 485 | int32 inbit, 486 | bool *found) 487 | { 488 | int32 bit = inbit; 489 | while (bit <= bitmap->bitmax) { 490 | if (bitmapTestbit(bitmap, bit)) { 491 | *found = true; 492 | return bit; 493 | } 494 | bit++; 495 | } 496 | *found = false; 497 | return inbit; 498 | } 499 | 500 | static char * 501 | serialise_bitmap(Bitmap *bitmap); 502 | 503 | /** 504 | * Take an existing ::Bitmap, and shrink it to be bounded by the elements 505 | * containing the highest and lowest bits. 506 | * 507 | * @param bitmap The original ::Bitmap 508 | * 509 | * @return Bitmap with updated bitmin and bitmax values. 510 | */ 511 | static void 512 | reduceBitmap(Bitmap *bitmap) 513 | { 514 | int32 bit = bitmap->bitmin; 515 | bool found; 516 | int32 first_elem; 517 | int32 last_elem = BITSET_ELEM(bitmap->bitmax) - BITSET_ELEM(bitmap->bitmin); 518 | int32 i; 519 | 520 | bit = bitmapNextBit(bitmap, bit, &found); 521 | if (!found) { 522 | /* Bitmap is empty: do a quick exit. */ 523 | bitmap->bitmax = bitmap->bitmin; 524 | return; 525 | } 526 | first_elem = BITSET_ELEM(bit) - BITSET_ELEM(bitmap->bitmin); 527 | if (first_elem) { 528 | /* The first elements have no bits, so we need to re-base 529 | * the bitset array. */ 530 | for (i = first_elem; i <= last_elem; i++) { 531 | bitmap->bitset[i - first_elem] = bitmap->bitset[i]; 532 | } 533 | last_elem -= first_elem; 534 | } 535 | bitmap->bitmin = bit; 536 | 537 | /* Now find last bit (we know that found is true at this point). */ 538 | while (found) { 539 | bit += 1; 540 | bit = bitmapNextBit(bitmap, bit, &found); 541 | } 542 | bitmap->bitmax = bit - 1; /* -1 undoes the last increment in above loop. */ 543 | } 544 | 545 | /** 546 | * Clear a bit within a ::Bitmap. 547 | * 548 | * @param bitmap The ::Bitmap within which the bit is to be cleared. 549 | * @param bit The bit to be set. 550 | * 551 | * @return Possibly new bitmap with the specified bit set. 552 | */ 553 | static void 554 | doClearBit(Bitmap *bitmap, 555 | int32 bit) 556 | { 557 | int32 relative_bit = bit - BITZERO(bitmap->bitmin); 558 | int32 element = BITSET_ELEM(relative_bit); 559 | if ((bit <= bitmap->bitmax) && (bit >= bitmap->bitmin)) 560 | { 561 | bitmap->bitset[element] &= ~(bitmasks[BITSET_BIT(relative_bit)]); 562 | } 563 | } 564 | 565 | /** 566 | * Clear a bit within a ::Bitmap. 567 | * 568 | * @param bitmap The ::Bitmap within which the bit is to be cleared. 569 | * @param bit The bit to be cleared. 570 | * 571 | * @return The original bitmap, possibly shrunk, with the specified bit 572 | * cleared. 573 | */ 574 | static Bitmap * 575 | bitmapClearbit(Bitmap *bitmap, 576 | int32 bit) 577 | { 578 | doClearBit(bitmap, bit); 579 | if ((bit == bitmap->bitmin) || (bit == bitmap->bitmax)) { 580 | reduceBitmap(bitmap); 581 | } 582 | 583 | return bitmap; 584 | } 585 | 586 | 587 | /** 588 | * Ensure bitmin of bitmap is no less than parameter. This provides the 589 | * means to quickly truncate a bitmap. 590 | * 591 | * @param bitmap The ::Bitmap to be modified 592 | * @param bitmin The new minimum value 593 | * 594 | * @return A newly allocated bitmap which has no lower bits than bitmin 595 | */ 596 | static Bitmap * 597 | bitmapSetMin(Bitmap *bitmap, int bitmin) 598 | { 599 | Bitmap *result = newBitmap(MAX(bitmap->bitmin, bitmin), 600 | bitmap->bitmax); 601 | int32 bitmap_elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax); 602 | int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax); 603 | int32 lost_elems = bitmap_elems - res_elems; 604 | int32 i; 605 | 606 | /* Copy the bitset elements that we need. */ 607 | for (i = 0; i < res_elems; i++) { 608 | result->bitset[i] = bitmap->bitset[i + lost_elems]; 609 | } 610 | 611 | if (result->bitmin != bitmap->bitmin) { 612 | /* Clear any bits below bitmin */ 613 | if (result->bitmin > BITZERO(result->bitmin)) { 614 | for (i = BITSET_ELEM(result->bitmin - 1); i >= 0; i--) { 615 | result->bitset[0] &= ~(bitmasks[BITSET_BIT(i)]); 616 | } 617 | } 618 | /* Update bitmin to match the lowest bit that is actually set. */ 619 | /* If the bitmap is sparse, there may be entire words that are 620 | * empty of bits. Let's deal with that situation first. */ 621 | lost_elems = 0; 622 | for (i = 0; i < res_elems; i++) { 623 | if (result->bitset[i] == 0) { 624 | lost_elems++; 625 | } 626 | else { 627 | break; 628 | } 629 | } 630 | if (lost_elems) { 631 | for (i = 0; i < (res_elems - lost_elems); i++) { 632 | result->bitset[i] = result->bitset[i + lost_elems]; 633 | } 634 | result->bitmin = BITZERO(result->bitmin + (lost_elems * ELEMBITS)); 635 | } 636 | 637 | for (i = BITSET_ELEM(result->bitmin); i < ELEMBITS; i++) { 638 | if (result->bitset[0] & bitmasks[BITSET_BIT(i)]) { 639 | result->bitmin = BITZERO(result->bitmin) + i; 640 | break; 641 | } 642 | } 643 | } 644 | 645 | return result; 646 | } 647 | 648 | /** 649 | * Ensure bitmax of bitmap is no more than parameter 650 | * 651 | * @param bitmap The ::Bitmap to be modified 652 | * @param bitmax The new minimum value 653 | * 654 | * @return A newly allocated bitmap which has no higher bits than bitmax 655 | */ 656 | static Bitmap * 657 | bitmapSetMax(Bitmap *bitmap, int bitmax) 658 | { 659 | Bitmap *result = newBitmap(bitmap->bitmin, 660 | MIN(bitmap->bitmax, bitmax)); 661 | int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax); 662 | int32 last_elem; 663 | int32 lost_elems; 664 | int32 i; 665 | 666 | /* Copy the bitset elements that we need. */ 667 | for (i = 0; i < res_elems; i++) { 668 | result->bitset[i] = bitmap->bitset[i]; 669 | } 670 | 671 | if (result->bitmax != bitmap->bitmax) { 672 | last_elem = res_elems - 1; 673 | /* Clear any bits above bitmax */ 674 | for (i = BITSET_BIT(result->bitmax) + 1; i < ELEMBITS; i++) { 675 | result->bitset[last_elem] &= ~(bitmasks[BITSET_BIT(i)]); 676 | } 677 | 678 | /* Update bitmax to match the highest bit that is actually set. */ 679 | lost_elems = 0; 680 | for (i = last_elem; i > 0; i--) { 681 | if (result->bitset[i] == 0) { 682 | // We found an empty word 683 | lost_elems++; 684 | } 685 | else { 686 | break; 687 | } 688 | } 689 | if (lost_elems) { 690 | result->bitmax = BITZERO(result->bitmax) - 691 | (lost_elems * ELEMBITS) + ELEMBITS - 1; 692 | } 693 | last_elem -= lost_elems; 694 | 695 | for (i = ELEMBITS - 1; i > 0; i--) { 696 | if (result->bitset[last_elem] & bitmasks[BITSET_BIT(i)]) { 697 | result->bitmax = BITZERO(result->bitmax) + i; 698 | break; 699 | } 700 | } 701 | } 702 | 703 | return result; 704 | } 705 | 706 | 707 | /** 708 | * Test for equality of 2 bitmaps 709 | * 710 | * @param bitmap1 The first ::Bitmap to be compared 711 | * @param bitmap2 The second ::Bitmap to be compared 712 | * 713 | * @return True if the bitmaps are the same. 714 | */ 715 | static bool 716 | bitmapEqual(Bitmap *bitmap1, 717 | Bitmap *bitmap2) 718 | { 719 | int32 elems; 720 | int32 i; 721 | if ((bitmap1->bitmin == bitmap2->bitmin) && 722 | (bitmap1->bitmax == bitmap2->bitmax)) { 723 | elems = ARRAYELEMS(bitmap1->bitmin, bitmap1->bitmax); 724 | for (i = 0; i < elems; i++) { 725 | if (bitmap1->bitset[i] != bitmap2->bitset[i]) { 726 | return false; 727 | } 728 | } 729 | return true; 730 | } 731 | else { 732 | /* bitmin and bitmax do not agree but they could both be empty 733 | * bitmaps, so check for that. 734 | */ 735 | return bitmapEmpty(bitmap1) && bitmapEmpty(bitmap2); 736 | } 737 | } 738 | 739 | 740 | /** 741 | * Create the union of two bitmaps. 742 | * 743 | * @param bitmap1 The first ::Bitmap to be unioned. 744 | * @param bitmap2 The second ::Bitmap, to be unioned with bitmap1. 745 | * 746 | * @return A newly allocated bitmap which is the union 747 | */ 748 | static Bitmap * 749 | bitmapUnion(Bitmap *bitmap1, 750 | Bitmap *bitmap2) 751 | { 752 | Bitmap *result = newBitmap(MIN(bitmap1->bitmin, bitmap2->bitmin), 753 | MAX(bitmap1->bitmax, bitmap2->bitmax)); 754 | int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax); 755 | int32 bit_offset1 = BITZERO(result->bitmin) - BITZERO(bitmap1->bitmin); 756 | int32 bit_offset2 = BITZERO(result->bitmin) - BITZERO(bitmap2->bitmin); 757 | int32 elem_offset1 = BITSET_ELEM(bit_offset1); 758 | int32 elem_offset2 = BITSET_ELEM(bit_offset2); 759 | int32 elem_max1 = BITSET_ELEM((bitmap1->bitmax - 760 | BITZERO(bitmap1->bitmin))); 761 | int32 elem_max2 = BITSET_ELEM((bitmap2->bitmax - 762 | BITZERO(bitmap2->bitmin))); 763 | bm_int elem; 764 | int32 to; 765 | int32 from; 766 | 767 | for (to = 0; to < res_elems; to++) { 768 | elem = 0; 769 | from = to + elem_offset1; 770 | if ((from >= 0) && (from <= elem_max1)) { 771 | elem = bitmap1->bitset[from]; 772 | } 773 | from = to + elem_offset2; 774 | if ((from >= 0) && (from <= elem_max2)) { 775 | elem |= bitmap2->bitset[from]; 776 | } 777 | result->bitset[to] = elem; 778 | } 779 | // This would normally be unncessary but it is possible that 780 | // either of the source bitmaps are empty, so we do this to be 781 | // safe. 782 | reduceBitmap(result); 783 | return result; 784 | } 785 | 786 | 787 | /** 788 | * Create the intersection of two bitmaps. 789 | * 790 | * @param bitmap1 The first ::Bitmap to be intersected. 791 | * @param bitmap2 The second ::Bitmap, to be intersected with bitmap1. 792 | * 793 | * @return A newly allocated bitmap which is the intersection 794 | */ 795 | static Bitmap * 796 | bitmapIntersect(Bitmap *bitmap1, 797 | Bitmap *bitmap2) 798 | { 799 | Bitmap *result = newBitmap(MAX(bitmap1->bitmin, bitmap2->bitmin), 800 | MIN(bitmap1->bitmax, bitmap2->bitmax)); 801 | int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax); 802 | int32 bit_offset1 = BITZERO(result->bitmin) - BITZERO(bitmap1->bitmin); 803 | int32 bit_offset2 = BITZERO(result->bitmin) - BITZERO(bitmap2->bitmin); 804 | int32 elem_offset1 = BITSET_ELEM(bit_offset1); 805 | int32 elem_offset2 = BITSET_ELEM(bit_offset2); 806 | int32 elem_max1 = BITSET_ELEM((bitmap1->bitmax - 807 | BITZERO(bitmap1->bitmin))); 808 | int32 elem_max2 = BITSET_ELEM((bitmap2->bitmax - 809 | BITZERO(bitmap2->bitmin))); 810 | bm_int elem; 811 | int32 to; 812 | int32 from; 813 | 814 | for (to = 0; to < res_elems; to++) { 815 | elem = 0; 816 | from = to + elem_offset1; 817 | if ((from >= 0) && (from <= elem_max1)) { 818 | elem = bitmap1->bitset[from]; 819 | 820 | from = to + elem_offset2; 821 | if ((from >= 0) && (from <= elem_max2)) { 822 | elem &= bitmap2->bitset[from]; 823 | } 824 | else { 825 | elem = 0; 826 | } 827 | } 828 | result->bitset[to] = elem; 829 | } 830 | reduceBitmap(result); 831 | return result; 832 | } 833 | 834 | 835 | /** 836 | * Create the subtraction of two bitmaps. 837 | * 838 | * @param bitmap1 The ::Bitmap from which bitmap2 will be subtracted 839 | * @param bitmap2 The ::Bitmap to be subtracted from from bitmap1. 840 | * 841 | * @return A newly allocated bitmap which is the subtraction 842 | */ 843 | static Bitmap * 844 | bitmapMinus(Bitmap *bitmap1, 845 | Bitmap *bitmap2) 846 | { 847 | Bitmap *result = bitmapCopy(bitmap1); 848 | bool found = !bitmapEmpty(bitmap2); 849 | int32 bit = MAX(bitmap1->bitmin, bitmap2->bitmin); 850 | 851 | /* Now clear any bits that match from bitmap2 */ 852 | while (found && (bit <= bitmap1->bitmax)) { 853 | doClearBit(result, bit); 854 | bit++; 855 | bit = bitmapNextBit(bitmap2, bit, &found); 856 | } 857 | reduceBitmap(result); 858 | return result; 859 | } 860 | 861 | 862 | /* 863 | * Serialisation functions follow 864 | ********************************************************************** 865 | */ 866 | 867 | 868 | /** 869 | * Serialise an int32 value as a base64 stream (truncated to save a 870 | * byte) into *p_stream. 871 | * 872 | * @param p_stream Pointer into stream currently being written. This 873 | * must be large enought to take the contents to be written. This 874 | * pointer is updated to point to the next free slot in the stream after 875 | * writing the int32 value, and is null terminated at that position. 876 | * @param value The value to be written to the stream. 877 | */ 878 | static void 879 | serialise_int32(char **p_stream, int32 value) 880 | { 881 | int32 len = b64_encode((char *) &value, sizeof(int32), *p_stream); 882 | (*p_stream) += (len - 1); /* X: dumb optimisation saves a byte */ 883 | (**p_stream) = '\0'; 884 | } 885 | 886 | 887 | /** 888 | * De-serialise an int32 value from a base64 character stream. 889 | * 890 | * @param p_stream Pointer into the stream currently being read. 891 | * must be large enought to take the contents to be written. This 892 | * pointer is updated to point to the next free slot in the stream after 893 | * reading the int32 value. 894 | * @return the int32 value read from the stream 895 | */ 896 | static int32 897 | deserialise_int32(char **p_stream) 898 | { 899 | int32 value; 900 | char *endpos = (*p_stream) + INT32SIZE_B64; 901 | char endchar = *endpos; 902 | *endpos = '='; /* deal with dumb optimisation (X) above */ 903 | b64_decode(*p_stream, INT32SIZE_B64 + 1, (char *) &value); 904 | *endpos = endchar; 905 | (*p_stream) += INT32SIZE_B64; 906 | return value; 907 | } 908 | 909 | 910 | /** 911 | * Serialise a binary stream as a base64 stream into *p_stream. 912 | * 913 | * @param p_stream Pointer into stream currently being written. This 914 | * pointer is updated to point to the next free slot in the stream after 915 | * writing the contents of instream and is null terminated at that 916 | * position. 917 | * 918 | * @param bytes The number of bytes to be written. 919 | * @param instream The binary stream to be written. 920 | */ 921 | static void 922 | serialise_stream(char **p_stream, int32 bytes, char *instream) 923 | { 924 | int32 len = b64_encode(instream, bytes, *p_stream); 925 | (*p_stream)[len] = '\0'; 926 | (*p_stream) += len; 927 | } 928 | 929 | 930 | /** 931 | * Return the length of a base64 encoded stream for a binary stream of 932 | * ::bytes length. 933 | * 934 | * @param bytes The length of the input binary stream in bytes 935 | * @return The length of the base64 character stream required to 936 | * represent the input stream. 937 | */ 938 | static int 939 | streamlen(int32 bytes) 940 | { 941 | return (4 * ((bytes + 2) / 3)); 942 | } 943 | 944 | 945 | /** 946 | * De-serialise a binary stream. 947 | * 948 | * @param p_stream Pointer into the stream currently being read. 949 | * pointer is updated to point to the next free slot in the stream after 950 | * reading the stream. 951 | * @param bytes The number of bytes to be read 952 | * @param outstream Pointer into the pre-allocated memory are into which 953 | * the binary from p_stream is to be written. 954 | */ 955 | static void 956 | deserialise_stream(char **p_stream, int32 bytes, char *outstream) 957 | { 958 | int32 len = streamlen(bytes); 959 | b64_decode(*p_stream, len, outstream); 960 | (*p_stream) += len; 961 | } 962 | 963 | 964 | /** 965 | * Serialise a bitmap. 966 | * 967 | * @param bitmap The bitmap to be serialised. 968 | */ 969 | static char * 970 | serialise_bitmap(Bitmap *bitmap) 971 | { 972 | int32 elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax); 973 | int32 stream_len = (INT32SIZE_B64 * 2) + 974 | streamlen(sizeof(bm_int) * elems) + 1; 975 | char *stream = palloc(stream_len * sizeof(char)); 976 | char *streamstart = stream; 977 | 978 | serialise_int32(&stream, bitmap->bitmin); 979 | serialise_int32(&stream, bitmap->bitmax); 980 | serialise_stream(&stream, elems * sizeof(bm_int), 981 | (char *) &(bitmap->bitset)); 982 | *stream = '\0'; 983 | 984 | /* Ensure bitmap is valid. */ 985 | if (bitmap->bitmin != bitmap->bitmax) { 986 | if (! (bitmapTestbit(bitmap, bitmap->bitmin) && 987 | bitmapTestbit(bitmap, bitmap->bitmax))) 988 | { 989 | ereport(ERROR, 990 | (errcode(ERRCODE_INTERNAL_ERROR), 991 | errmsg("Corrupted bitmap"), 992 | errdetail("one of bitmin or bitmax is not set."))); 993 | } 994 | } 995 | return streamstart; 996 | } 997 | 998 | 999 | /** 1000 | * De-serialise a bitmap. 1001 | * 1002 | * @param charstream The serialised character string containing the bitmap. 1003 | */ 1004 | static Bitmap * 1005 | deserialise_bitmap(char *charstream) 1006 | { 1007 | Bitmap *bitmap; 1008 | char **p_stream = &charstream; 1009 | int32 elems; 1010 | int32 bitmin; 1011 | int32 bitmax; 1012 | 1013 | if (strcmp(charstream, "[]") == 0) { 1014 | bitmap = newBitmap(0, 0); 1015 | clearBitmap(bitmap); 1016 | } 1017 | else { 1018 | bitmin = deserialise_int32(p_stream); 1019 | bitmax = deserialise_int32(p_stream); 1020 | bitmap = newBitmap(bitmin, bitmax); 1021 | elems = ARRAYELEMS(bitmin, bitmax); 1022 | 1023 | deserialise_stream(p_stream, elems * sizeof(bitmap->bitset[0]), 1024 | (char *) &(bitmap->bitset[0])); 1025 | } 1026 | return bitmap; 1027 | } 1028 | 1029 | 1030 | /** 1031 | * Compare 2 bitmaps for indexing/sorting purposes 1032 | * 1033 | * @param bitmap1 The first ::Bitmap to be compared 1034 | * @param bitmap2 The second ::Bitmap to be compared 1035 | * 1036 | * @return < 0, 0, or > 0 like strcmp 1037 | */ 1038 | static int 1039 | bitmapCmp(Bitmap *bitmap1, 1040 | Bitmap *bitmap2) 1041 | { 1042 | if (bitmapEqual(bitmap1, bitmap2)) { 1043 | return 0; 1044 | } 1045 | else { 1046 | char *s1 = serialise_bitmap(bitmap1); 1047 | char *s2 = serialise_bitmap(bitmap2); 1048 | int result = strcmp(s1, s2); 1049 | pfree(s1); 1050 | pfree(s2); 1051 | return result; 1052 | } 1053 | } 1054 | 1055 | 1056 | /* 1057 | * Interface functions follow 1058 | ********************************************************************** 1059 | */ 1060 | 1061 | 1062 | PG_FUNCTION_INFO_V1(bitmap_in); 1063 | /** 1064 | * bitmap_in(serialised_bitmap text) returns bitmap 1065 | * Create a Bitmap initialised from a (base64) text value. 1066 | * 1067 | * @param fcinfo Params as described_below 1068 | *
serialised_bitmap text A base64 serialiastion of a 1069 | * bitmap value. 1070 | * @return Bitmap the newly created bitmap 1071 | */ 1072 | Datum 1073 | bitmap_in(PG_FUNCTION_ARGS) 1074 | { 1075 | char *stream; 1076 | Bitmap *bitmap; 1077 | 1078 | stream = PG_GETARG_CSTRING(0); 1079 | bitmap = deserialise_bitmap(stream); 1080 | 1081 | PG_RETURN_BITMAP(bitmap); 1082 | } 1083 | 1084 | 1085 | PG_FUNCTION_INFO_V1(bitmap_out); 1086 | /** 1087 | * bitmap_out(bitmap bitmap) returns text 1088 | * Create a (base64) text representation of a bitmap. 1089 | * 1090 | * @param fcinfo Params as described_below 1091 | *
bitmap bitmap A base64 serialiastion of a 1092 | * bitmap value. 1093 | * @return cstring the newly serialised text stream. 1094 | */ 1095 | Datum 1096 | bitmap_out(PG_FUNCTION_ARGS) 1097 | { 1098 | char *result; 1099 | Bitmap *bitmap; 1100 | 1101 | bitmap = PG_GETARG_BITMAP(0); 1102 | result = serialise_bitmap(bitmap); 1103 | 1104 | PG_RETURN_CSTRING(result); 1105 | } 1106 | 1107 | 1108 | PG_FUNCTION_INFO_V1(bitmap_is_empty); 1109 | /** 1110 | * bitmap_is_empty(bitmap bitmap) returns boolean 1111 | * Predicate to identify whether a bitmap is empty or not. 1112 | * 1113 | * @param fcinfo Params as described_below 1114 | *
bitmap bitmap The bitmap being examined. 1115 | * bitmap value. 1116 | * @return boolean true, if the bitmap has no bits. 1117 | */ 1118 | Datum 1119 | bitmap_is_empty(PG_FUNCTION_ARGS) 1120 | { 1121 | Bitmap *bitmap; 1122 | 1123 | bitmap = PG_GETARG_BITMAP(0); 1124 | PG_RETURN_BOOL(bitmapEmpty(bitmap)); 1125 | } 1126 | 1127 | 1128 | PG_FUNCTION_INFO_V1(bitmap_bitmin); 1129 | /** 1130 | * bitmap_bitmin(bitmap bitmap) returns boolean 1131 | * Return the lowest bit set in the bitmap, or NULL if none are set. 1132 | * This relies on bitmap->bitmin always identifying the lowest numbered 1133 | * bit in the bitset, unless the bitmap is empty. 1134 | * 1135 | * @param fcinfo Params as described_below 1136 | *
bitmap bitmap The bitmap being examined. 1137 | * @return integer The lowest bit set in the bitmap or 1138 | * NULL if there are no bits set. 1139 | */ 1140 | Datum 1141 | bitmap_bitmin(PG_FUNCTION_ARGS) 1142 | { 1143 | Bitmap *bitmap = PG_GETARG_BITMAP(0); 1144 | int32 relative_bit = bitmap->bitmin - BITZERO(bitmap->bitmin); 1145 | 1146 | if (bitmap->bitset[0] & bitmasks[relative_bit]) { 1147 | PG_RETURN_INT32(bitmap->bitmin); 1148 | } 1149 | if (bitmap->bitmin != bitmap->bitmax) { 1150 | ereport(ERROR, 1151 | (errcode(ERRCODE_INTERNAL_ERROR), 1152 | errmsg("Corrupted bitmap"), 1153 | errdetail("bitzeo is incorrect for bitmap (range %d..%d).", 1154 | bitmap->bitmin, bitmap->bitmax))); 1155 | } 1156 | PG_RETURN_NULL(); 1157 | } 1158 | 1159 | 1160 | PG_FUNCTION_INFO_V1(bitmap_bitmax); 1161 | /** 1162 | * bitmap_bitmax(bitmap bitmap) returns boolean 1163 | * Return the highest bit set in the bitmap, or NULL if none are set. 1164 | * This relies on bitmap->bitmax always identifying the highest numbered 1165 | * bit in the bitset, unless the bitmap is empty. 1166 | * 1167 | * @param fcinfo Params as described_below 1168 | *
bitmap bitmap The bitmap being examined. 1169 | * @return integer The lowest bit set in the bitmap or 1170 | * NULL if there are no bits set. 1171 | */ 1172 | Datum 1173 | bitmap_bitmax(PG_FUNCTION_ARGS) 1174 | { 1175 | Bitmap *bitmap = PG_GETARG_BITMAP(0); 1176 | int32 relative_bit = bitmap->bitmax - BITZERO(bitmap->bitmin); 1177 | int32 elem = BITSET_ELEM(relative_bit); 1178 | 1179 | if (bitmap->bitset[elem] & bitmasks[BITSET_BIT(relative_bit)]) { 1180 | PG_RETURN_INT32(bitmap->bitmax); 1181 | } 1182 | if (bitmap->bitmin != bitmap->bitmax) { 1183 | ereport(ERROR, 1184 | (errcode(ERRCODE_INTERNAL_ERROR), 1185 | errmsg("Corrupted bitmap"), 1186 | errdetail("bitmax is incorrect for bitmap (range %d..%d).", 1187 | bitmap->bitmin, bitmap->bitmax))); 1188 | } 1189 | PG_RETURN_NULL(); 1190 | } 1191 | 1192 | 1193 | PG_FUNCTION_INFO_V1(bitmap_bits); 1194 | /** 1195 | * bitmap_bits(name text) returns setof int4 1196 | * Return the set of all bits set in the specified Bitmap. 1197 | * 1198 | * @param fcinfo name text The name of the bitmap. 1199 | * @return setof int4The set of bits that are set in the 1200 | * bitmap. 1201 | */ 1202 | Datum 1203 | bitmap_bits(PG_FUNCTION_ARGS) 1204 | { 1205 | struct bitmap_bits_state { 1206 | Bitmap *bitmap; 1207 | int32 bit; 1208 | } *state; 1209 | FuncCallContext *funcctx; 1210 | MemoryContext oldcontext; 1211 | bool found; 1212 | Datum datum; 1213 | 1214 | if (SRF_IS_FIRSTCALL()) 1215 | { 1216 | funcctx = SRF_FIRSTCALL_INIT(); 1217 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 1218 | state = palloc(sizeof(struct bitmap_bits_state)); 1219 | MemoryContextSwitchTo(oldcontext); 1220 | 1221 | state->bitmap = PG_GETARG_BITMAP(0); 1222 | state->bit = state->bitmap->bitmin; 1223 | funcctx->user_fctx = state; 1224 | } 1225 | 1226 | funcctx = SRF_PERCALL_SETUP(); 1227 | state = funcctx->user_fctx; 1228 | 1229 | state->bit = bitmapNextBit(state->bitmap, state->bit, &found); 1230 | 1231 | if (found) { 1232 | datum = Int32GetDatum(state->bit); 1233 | state->bit++; 1234 | SRF_RETURN_NEXT(funcctx, datum); 1235 | } 1236 | else { 1237 | SRF_RETURN_DONE(funcctx); 1238 | } 1239 | } 1240 | 1241 | 1242 | PG_FUNCTION_INFO_V1(bitmap_new_empty); 1243 | /** 1244 | * bitmap_new_empty() returns bitmap; 1245 | * Create an empty Bitmap, for dealing with a named range of 1246 | * values. 1247 | * 1248 | * @param fcinfo Params as described_below 1249 | * @return Bitmap the newly allocated bitmap 1250 | */ 1251 | Datum 1252 | bitmap_new_empty(PG_FUNCTION_ARGS) 1253 | { 1254 | Bitmap *bitmap; 1255 | bitmap = newBitmap(0, 0); 1256 | clearBitmap(bitmap); 1257 | 1258 | PG_RETURN_BITMAP(bitmap); 1259 | } 1260 | 1261 | 1262 | PG_FUNCTION_INFO_V1(bitmap_new); 1263 | /** 1264 | * bitmap_new() returns bitmap; 1265 | * Create or re-initialise a Bitmap, for dealing with a named range of 1266 | * values. 1267 | * 1268 | * @param fcinfo Params as described_below 1269 | *
bitno int32 The first integer value to be recorded in 1270 | * the bitmap 1271 | * @return Bitmap the newly allocated bitmap 1272 | */ 1273 | Datum 1274 | bitmap_new(PG_FUNCTION_ARGS) 1275 | { 1276 | Bitmap *bitmap; 1277 | int32 bit; 1278 | 1279 | if (PG_ARGISNULL(0)) { 1280 | PG_RETURN_NULL(); 1281 | } 1282 | bit = PG_GETARG_INT32(0); 1283 | bitmap = newBitmap(bit, bit); 1284 | clearBitmap(bitmap); 1285 | doSetBit(bitmap, bit); 1286 | 1287 | PG_RETURN_BITMAP(bitmap); 1288 | } 1289 | 1290 | 1291 | PG_FUNCTION_INFO_V1(bitmap_setbit); 1292 | /** 1293 | * bitmap_setbit(bitmap bitmap, bit int4) returns bool 1294 | * Set the given bit in the bitmap, returning TRUE. This can be used as 1295 | * an aggregate function in which case the bitmap parameter will be null 1296 | * for the first call. In this case simply create a bitmap from the 1297 | * second argument. 1298 | * 1299 | * @param fcinfo Params as described_below 1300 | *
bitmap bitmap The bitmap to be manipulated. 1301 | *
bitno int4 The bitnumber to be set. 1302 | * @return bitmap The result. 1303 | */ 1304 | Datum 1305 | bitmap_setbit(PG_FUNCTION_ARGS) 1306 | { 1307 | Bitmap *bitmap; 1308 | int32 bitno; 1309 | Bitmap *result; 1310 | 1311 | if (PG_ARGISNULL(0)) { 1312 | if (PG_ARGISNULL(1)) { 1313 | PG_RETURN_NULL(); 1314 | } 1315 | bitno = PG_GETARG_INT32(1); 1316 | result = newBitmap(bitno, bitno); 1317 | clearBitmap(result); 1318 | doSetBit(result, bitno); 1319 | } 1320 | else { 1321 | bitno = PG_GETARG_INT32(1); 1322 | bitmap = PG_GETARG_BITMAP(0); 1323 | if (PG_ARGISNULL(1)) { 1324 | result = bitmapCopy(bitmap); 1325 | } 1326 | else { 1327 | result = bitmapSetbit(bitmap, bitno); 1328 | } 1329 | } 1330 | 1331 | PG_RETURN_BITMAP(result); 1332 | } 1333 | 1334 | 1335 | PG_FUNCTION_INFO_V1(bitmap_testbit); 1336 | /** 1337 | * bitmap_testbit(bitmap bitmap, bit int4) returns bool 1338 | * Test the given bit in the bitmap, returning TRUE if it is 1. 1339 | * 1340 | * @param fcinfo Params as described_below 1341 | *
bitmap bitmap The bitmap to be manipulated. 1342 | *
bitno int4 The bitnumber to be tested. 1343 | * @return bool the truth value of the bit. 1344 | */ 1345 | Datum 1346 | bitmap_testbit(PG_FUNCTION_ARGS) 1347 | { 1348 | Bitmap *bitmap; 1349 | int32 bitno; 1350 | bool result; 1351 | 1352 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1353 | PG_RETURN_NULL(); 1354 | } 1355 | bitmap = PG_GETARG_BITMAP(0); 1356 | bitno = PG_GETARG_INT32(1); 1357 | result = bitmapTestbit(bitmap, bitno); 1358 | 1359 | PG_RETURN_BOOL(result); 1360 | } 1361 | 1362 | 1363 | PG_FUNCTION_INFO_V1(bitmap_setmin); 1364 | /** 1365 | * bitmap_setmin(bitmap bitmap, bitmin int4) returns bitmap 1366 | * Return a new bitmap having no bits less than bitmin 1367 | * 1368 | * @param fcinfo Params as described_below 1369 | *
bitmap bitmap The bitmap to be manipulated. 1370 | *
bitmin int4 The new minimum bit. 1371 | * @return bitmap The new bitmap. 1372 | */ 1373 | Datum 1374 | bitmap_setmin(PG_FUNCTION_ARGS) 1375 | { 1376 | Bitmap *bitmap; 1377 | int32 bitmin; 1378 | Bitmap *result; 1379 | 1380 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1381 | PG_RETURN_NULL(); 1382 | } 1383 | bitmap = PG_GETARG_BITMAP(0); 1384 | bitmin = PG_GETARG_INT32(1); 1385 | result = bitmapCopy(bitmap); 1386 | result = bitmapSetMin(result, bitmin); 1387 | 1388 | PG_RETURN_BITMAP(result); 1389 | } 1390 | 1391 | 1392 | PG_FUNCTION_INFO_V1(bitmap_setmax); 1393 | /** 1394 | * bitmap_setmax(bitmap bitmap, bitmax int4) returns bitmap 1395 | * Return a new bitmap having no bits greater than bitmax 1396 | * 1397 | * @param fcinfo Params as described_below 1398 | *
bitmap bitmap The bitmap to be manipulated. 1399 | *
bitmax int4 The new maximum bit. 1400 | * @return bitmap The new bitmap. 1401 | */ 1402 | Datum 1403 | bitmap_setmax(PG_FUNCTION_ARGS) 1404 | { 1405 | Bitmap *bitmap; 1406 | int32 bitmax; 1407 | Bitmap *result; 1408 | 1409 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1410 | PG_RETURN_NULL(); 1411 | } 1412 | bitmap = PG_GETARG_BITMAP(0); 1413 | bitmax = PG_GETARG_INT32(1); 1414 | result = bitmapCopy(bitmap); 1415 | result = bitmapSetMax(result, bitmax); 1416 | 1417 | PG_RETURN_BITMAP(result); 1418 | } 1419 | 1420 | 1421 | PG_FUNCTION_INFO_V1(bitmap_equal); 1422 | /** 1423 | * bitmap_equal(bitmap1 bitmap, bitmap2 bitmap) returns bool 1424 | * Return true if the bitmaps are equivalent, 1425 | * 1426 | * @param fcinfo Params as described_below 1427 | *
bitmap1 bitmap The first bitmap 1428 | *
bitmap2 bitmap The second bitmap 1429 | * @return bool true if the bitmaps contain the same bits. 1430 | */ 1431 | Datum 1432 | bitmap_equal(PG_FUNCTION_ARGS) 1433 | { 1434 | Bitmap *bitmap1; 1435 | Bitmap *bitmap2; 1436 | bool result; 1437 | 1438 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1439 | PG_RETURN_NULL(); 1440 | } 1441 | bitmap1 = PG_GETARG_BITMAP(0); 1442 | bitmap2 = PG_GETARG_BITMAP(1); 1443 | result = bitmapEqual(bitmap1, bitmap2); 1444 | 1445 | PG_RETURN_BOOL(result); 1446 | } 1447 | 1448 | 1449 | PG_FUNCTION_INFO_V1(bitmap_nequal); 1450 | /** 1451 | * bitmap_nequal(bitmap1 bitmap, bitmap2 bitmap) returns bool 1452 | * Return true if the bitmaps are not equivalent, 1453 | * 1454 | * @param fcinfo Params as described_below 1455 | *
bitmap1 bitmap The first bitmap 1456 | *
bitmap2 bitmap The second bitmap 1457 | * @return bool true unless the bitmaps contain the same bits. 1458 | */ 1459 | Datum 1460 | bitmap_nequal(PG_FUNCTION_ARGS) 1461 | { 1462 | Bitmap *bitmap1; 1463 | Bitmap *bitmap2; 1464 | bool result; 1465 | 1466 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1467 | PG_RETURN_NULL(); 1468 | } 1469 | bitmap1 = PG_GETARG_BITMAP(0); 1470 | bitmap2 = PG_GETARG_BITMAP(1); 1471 | result = !bitmapEqual(bitmap1, bitmap2); 1472 | 1473 | PG_RETURN_BOOL(result); 1474 | } 1475 | 1476 | 1477 | PG_FUNCTION_INFO_V1(bitmap_cmp); 1478 | /** 1479 | * bitmap_cmp(bitmap1 bitmap, bitmap2 bitmap) returns bool 1480 | * Return result of comparison of bitmap1's string representation with 1481 | * bitmap2's. 1482 | * 1483 | * @param fcinfo Params as described_below 1484 | *
bitmap1 bitmap The first bitmap 1485 | *
bitmap2 bitmap The second bitmap 1486 | * @return int4 result of comparison 1487 | */ 1488 | Datum 1489 | bitmap_cmp(PG_FUNCTION_ARGS) 1490 | { 1491 | Bitmap *bitmap1; 1492 | Bitmap *bitmap2; 1493 | int32 result; 1494 | 1495 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1496 | PG_RETURN_NULL(); 1497 | } 1498 | bitmap1 = PG_GETARG_BITMAP(0); 1499 | bitmap2 = PG_GETARG_BITMAP(1); 1500 | result = bitmapCmp(bitmap1, bitmap2); 1501 | 1502 | PG_RETURN_INT32(result); 1503 | } 1504 | 1505 | 1506 | PG_FUNCTION_INFO_V1(bitmap_lt); 1507 | /** 1508 | * bitmap_lt(bitmap1 bitmap, bitmap2 bitmap) returns bool 1509 | * Return true if bitmap1's string representation should be sorted 1510 | * earlier than bitmap2's. 1511 | * 1512 | * @param fcinfo Params as described_below 1513 | *
bitmap1 bitmap The first bitmap 1514 | *
bitmap2 bitmap The second bitmap 1515 | * @return bool true unless the bitmaps contain the same bits. 1516 | */ 1517 | Datum 1518 | bitmap_lt(PG_FUNCTION_ARGS) 1519 | { 1520 | Bitmap *bitmap1; 1521 | Bitmap *bitmap2; 1522 | bool result; 1523 | 1524 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1525 | PG_RETURN_NULL(); 1526 | } 1527 | bitmap1 = PG_GETARG_BITMAP(0); 1528 | bitmap2 = PG_GETARG_BITMAP(1); 1529 | result = bitmapCmp(bitmap1, bitmap2) < 0; 1530 | 1531 | PG_RETURN_BOOL(result); 1532 | } 1533 | 1534 | 1535 | PG_FUNCTION_INFO_V1(bitmap_le); 1536 | /** 1537 | * bitmap_le(bitmap1 bitmap, bitmap2 bitmap) returns bool 1538 | * Return true if bitmap1's string representation should be sorted 1539 | * earlier than, or the same as, bitmap2's. 1540 | * 1541 | * @param fcinfo Params as described_below 1542 | *
bitmap1 bitmap The first bitmap 1543 | *
bitmap2 bitmap The second bitmap 1544 | * @return bool true unless the bitmaps contain the same bits. 1545 | */ 1546 | Datum 1547 | bitmap_le(PG_FUNCTION_ARGS) 1548 | { 1549 | Bitmap *bitmap1; 1550 | Bitmap *bitmap2; 1551 | bool result; 1552 | 1553 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1554 | PG_RETURN_NULL(); 1555 | } 1556 | bitmap1 = PG_GETARG_BITMAP(0); 1557 | bitmap2 = PG_GETARG_BITMAP(1); 1558 | result = bitmapCmp(bitmap1, bitmap2) <= 0; 1559 | 1560 | PG_RETURN_BOOL(result); 1561 | } 1562 | 1563 | 1564 | PG_FUNCTION_INFO_V1(bitmap_gt); 1565 | /** 1566 | * bitmap_gt(bitmap1 bitmap, bitmap2 bitmap) returns bool 1567 | * Return true if bitmap1's string representation should be sorted 1568 | * later than bitmap2's. 1569 | * 1570 | * @param fcinfo Params as described_below 1571 | *
bitmap1 bitmap The first bitmap 1572 | *
bitmap2 bitmap The second bitmap 1573 | * @return bool true unless the bitmaps contain the same bits. 1574 | */ 1575 | Datum 1576 | bitmap_gt(PG_FUNCTION_ARGS) 1577 | { 1578 | Bitmap *bitmap1; 1579 | Bitmap *bitmap2; 1580 | bool result; 1581 | 1582 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1583 | PG_RETURN_NULL(); 1584 | } 1585 | bitmap1 = PG_GETARG_BITMAP(0); 1586 | bitmap2 = PG_GETARG_BITMAP(1); 1587 | result = bitmapCmp(bitmap1, bitmap2) > 0; 1588 | 1589 | PG_RETURN_BOOL(result); 1590 | } 1591 | 1592 | 1593 | PG_FUNCTION_INFO_V1(bitmap_ge); 1594 | /** 1595 | * bitmap_ge(bitmap1 bitmap, bitmap2 bitmap) returns bool 1596 | * Return true if bitmap1's string representation should be sorted 1597 | * later than, or the same as, bitmap2's. 1598 | * 1599 | * @param fcinfo Params as described_below 1600 | *
bitmap1 bitmap The first bitmap 1601 | *
bitmap2 bitmap The second bitmap 1602 | * @return bool true unless the bitmaps contain the same bits. 1603 | */ 1604 | Datum 1605 | bitmap_ge(PG_FUNCTION_ARGS) 1606 | { 1607 | Bitmap *bitmap1; 1608 | Bitmap *bitmap2; 1609 | bool result; 1610 | 1611 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1612 | PG_RETURN_NULL(); 1613 | } 1614 | bitmap1 = PG_GETARG_BITMAP(0); 1615 | bitmap2 = PG_GETARG_BITMAP(1); 1616 | result = bitmapCmp(bitmap1, bitmap2) >= 0; 1617 | 1618 | PG_RETURN_BOOL(result); 1619 | } 1620 | 1621 | 1622 | PG_FUNCTION_INFO_V1(bitmap_union); 1623 | /** 1624 | * bitmap_union(bitmap1 bitmap, bitmap2 bitmap) returns bitmap 1625 | * Return the union of 2 bitmaps. This can be used as an aggregate 1626 | * function in which case the bitmap1 parameter will be null for the 1627 | * first call. In this case simply return the second argument. 1628 | * 1629 | * @param fcinfo Params as described_below 1630 | *
bitmap1 bitmap The first bitmap 1631 | *
bitmap2 bitmap The second bitmap 1632 | * @return bitmap the union of the two bitmaps. 1633 | */ 1634 | Datum 1635 | bitmap_union(PG_FUNCTION_ARGS) 1636 | { 1637 | Bitmap *bitmap1; 1638 | Bitmap *bitmap2; 1639 | Bitmap *result; 1640 | 1641 | if (PG_ARGISNULL(0)) { 1642 | if (PG_ARGISNULL(1)) { 1643 | PG_RETURN_NULL(); 1644 | } 1645 | else { 1646 | bitmap2 = PG_GETARG_BITMAP(1); 1647 | result = bitmapCopy(bitmap2); 1648 | } 1649 | } 1650 | else { 1651 | bitmap1 = PG_GETARG_BITMAP(0); 1652 | if (PG_ARGISNULL(1)) { 1653 | result = bitmapCopy(bitmap1); 1654 | } 1655 | else { 1656 | bitmap2 = PG_GETARG_BITMAP(1); 1657 | result = bitmapUnion(bitmap1, bitmap2); 1658 | } 1659 | } 1660 | PG_RETURN_BITMAP(result); 1661 | } 1662 | 1663 | 1664 | PG_FUNCTION_INFO_V1(bitmap_clearbit); 1665 | /** 1666 | * bitmap_clearbit(bitmap bitmap, bit int4) returns bool 1667 | * Clear the given bit in the bitmap, returning FALSE. 1668 | * 1669 | * @param fcinfo Params as described_below 1670 | *
bitmap bitmap The bitmap to be manipulated. 1671 | *
bitno int4 The bitnumber to be set. 1672 | * @return bool TRUE 1673 | */ 1674 | Datum 1675 | bitmap_clearbit(PG_FUNCTION_ARGS) 1676 | { 1677 | Bitmap *bitmap; 1678 | int32 bitno; 1679 | Bitmap *result; 1680 | 1681 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1682 | PG_RETURN_NULL(); 1683 | } 1684 | bitmap = PG_GETARG_BITMAP(0); 1685 | bitno = PG_GETARG_INT32(1); 1686 | result = bitmapCopy(bitmap); 1687 | result = bitmapClearbit(result, bitno); 1688 | 1689 | PG_RETURN_BITMAP(result); 1690 | } 1691 | 1692 | 1693 | PG_FUNCTION_INFO_V1(bitmap_intersection); 1694 | /** 1695 | * bitmap_intersection(bitmap1 bitmap, bitmap2 bitmap) 1696 | * returns bitmap 1697 | * Return the intersection of 2 bitmaps. This can be used as an 1698 | * aggregate function in which case the bitmap1 parameter will be null 1699 | * for the first call. In this case simply return the second argument. 1700 | * 1701 | * @param fcinfo Params as described_below 1702 | *
bitmap1 bitmap The first bitmap 1703 | *
bitmap2 bitmap The second bitmap 1704 | * @return bitmap the intersection of the two bitmaps. 1705 | */ 1706 | Datum 1707 | bitmap_intersection(PG_FUNCTION_ARGS) 1708 | { 1709 | Bitmap *bitmap1; 1710 | Bitmap *bitmap2; 1711 | Bitmap *result; 1712 | 1713 | if (PG_ARGISNULL(0)) { 1714 | if (PG_ARGISNULL(1)) { 1715 | PG_RETURN_NULL(); 1716 | } 1717 | bitmap2 = PG_GETARG_BITMAP(1); 1718 | result = bitmapCopy(bitmap2); 1719 | } 1720 | else { 1721 | bitmap1 = PG_GETARG_BITMAP(0); 1722 | if (PG_ARGISNULL(1)) { 1723 | result = bitmapCopy(bitmap1); 1724 | } 1725 | bitmap2 = PG_GETARG_BITMAP(1); 1726 | result = bitmapIntersect(bitmap1, bitmap2); 1727 | } 1728 | PG_RETURN_BITMAP(result); 1729 | } 1730 | 1731 | 1732 | PG_FUNCTION_INFO_V1(bitmap_minus); 1733 | /** 1734 | * bitmap_minus(bitmap1 bitmap, bitmap2 bitmap) 1735 | * returns bitmap 1736 | * Return the bitmap1 with all bits from bitmap2 subtracted (cleared) 1737 | * from it. 1738 | * 1739 | * @param fcinfo Params as described_below 1740 | *
bitmap1 bitmap The first bitmap 1741 | *
bitmap2 bitmap The second bitmap 1742 | * @return bitmap the subtraction of the two bitmaps. 1743 | */ 1744 | Datum 1745 | bitmap_minus(PG_FUNCTION_ARGS) 1746 | { 1747 | Bitmap *bitmap1; 1748 | Bitmap *bitmap2; 1749 | Bitmap *result; 1750 | 1751 | if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { 1752 | PG_RETURN_NULL(); 1753 | } 1754 | bitmap1 = PG_GETARG_BITMAP(0); 1755 | bitmap2 = PG_GETARG_BITMAP(1); 1756 | result = bitmapMinus(bitmap1, bitmap2); 1757 | 1758 | PG_RETURN_BITMAP(result); 1759 | } 1760 | --------------------------------------------------------------------------------