Skip to content

Commit

Permalink
Add collation support and bump the versio number to make the collecti…
Browse files Browse the repository at this point in the history
…on type collatable
  • Loading branch information
jim-mlodgenski committed Feb 20, 2025
1 parent f1d5347 commit 7b6b444
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 7 deletions.
6 changes: 3 additions & 3 deletions META.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "pgcollection",
"abstract": "A collection data type",
"description": "pgcollection is a memory optimized data type for PostgreSQL used as a high performance data structure inside of plpglsql functions to create associative arrays",
"version": "0.9.0",
"version": "0.9.1",
"maintainer": "Jim Mlodgenski <[email protected]>",
"license": "apache_2_0",
"prereqs": {
Expand All @@ -14,9 +14,9 @@
},
"provides": {
"pgcollection": {
"file": "sql/collection--0.9.sql",
"file": "sql/collection--0.9.1.sql",
"docfile": "pgcollection.md",
"version": "0.9.0"
"version": "0.9.1"
}
},
"resources": {
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
EXTENSION = collection
EXTVERSION = 0.9.0
EXTVERSION = 0.9.1
DATA = $(wildcard sql/*.sql)

PGFILEDESC = "pgcollection - collection data type for PostgreSQL"
Expand Down
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ CREATE EXTENSION collection;
| next(collection) | collection | Sets the collection iterator to the next item |
| prev(collection) | collection | Stes the collection iterator to the previous item |
| copy(collection) | collection | Returns a copy of a collection without a context switch |
| sort(collection) | collection | Sorts a collection by the keys in the default collation order and points to the first item |
| sort(collection) | collection | Sorts a collection by the keys in collation order and points to the first item |
| isnull(collection) | bool | Returns true if the current location of the iterator is null |
| key(collection) | text | Returns the key of the item the collection is pointed at |
| value(collection) | text | Returns the value as text of the item the collection is pointed at |
Expand Down Expand Up @@ -198,6 +198,33 @@ END
$$;
```

### Collations

A collection is a collatable type meaning that the sort order of the keys in a
collection is dependent on the collation defined for the collection. A
collection will use the collation of the database by default, but alternative
collations can be used when defining the collection variable.

```sql
DO
$$
DECLARE
t_capital collection COLLATE "en_US";
r record;
BEGIN
t_capital['USA'] := 'Washington, D.C.';
t_capital['United Kingdom'] := 'London';
t_capital['Japan'] := 'Tokyo';

t_capital := sort(t_capital);
FOR r IN SELECT * FROM keys_to_table(t_capital) AS k
LOOP
RAISE NOTICE 'The current element is %', r.k;
END LOOP;
END
$$;
```

### Next and Prev Functions

When iterating over a collection, control may be needed on how to move over
Expand Down
2 changes: 1 addition & 1 deletion collection.control
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# collection
comment = 'Collection is a memory optimized data type primarily used as a high performance data structure inside of plpglsql functions.'
default_version = '0.9'
default_version = '0.9.1'
module_pathname = '$libdir/collection'
relocatable = true
182 changes: 182 additions & 0 deletions sql/collection--0.9.1.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@

CREATE TYPE collection;

CREATE FUNCTION collection_in(cstring)
RETURNS collection
AS 'MODULE_PATHNAME'
LANGUAGE c
STRICT IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION collection_out(collection)
RETURNS cstring
AS 'MODULE_PATHNAME'
LANGUAGE c
STRICT IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION collection_typmodin(cstring[])
RETURNS integer
AS 'MODULE_PATHNAME'
LANGUAGE c
STRICT IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION collection_typmodout(integer)
RETURNS cstring
AS 'MODULE_PATHNAME'
LANGUAGE c
STRICT IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION collection_subscript_handler(internal)
RETURNS internal
AS 'MODULE_PATHNAME', 'collection_subscript_handler'
LANGUAGE C
STRICT IMMUTABLE PARALLEL SAFE;

CREATE TYPE collection (
INPUT = collection_in,
OUTPUT = collection_out,
TYPMOD_IN = collection_typmodin,
TYPMOD_OUT = collection_typmodout,
SUBSCRIPT = collection_subscript_handler,
INTERNALLENGTH = -1,
STORAGE = extended,
COLLATABLE = true
);

CREATE FUNCTION collection (collection, integer)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_cast'
LANGUAGE c
IMMUTABLE;

CREATE CAST (collection AS collection)
WITH FUNCTION collection(collection, integer)
AS IMPLICIT;

CREATE FUNCTION add(collection, text, anyelement)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_add'
LANGUAGE c;

CREATE FUNCTION add(collection, text, text)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_add'
LANGUAGE c;

CREATE FUNCTION count(collection)
RETURNS int
AS 'MODULE_PATHNAME', 'collection_count'
STRICT
LANGUAGE c;

CREATE FUNCTION find(collection, text, anyelement)
RETURNS anyelement
AS 'MODULE_PATHNAME', 'collection_find'
LANGUAGE c;

CREATE FUNCTION find(collection, text)
RETURNS text
AS 'MODULE_PATHNAME', 'collection_find'
LANGUAGE c;

CREATE FUNCTION delete(collection, text)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_delete'
LANGUAGE c;

CREATE FUNCTION sort(collection)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_sort'
STRICT
LANGUAGE c;

CREATE FUNCTION copy(collection)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_copy'
STRICT
LANGUAGE c;

CREATE FUNCTION key(collection)
RETURNS text
AS 'MODULE_PATHNAME', 'collection_key'
STRICT
LANGUAGE c;

CREATE FUNCTION value(collection, anyelement)
RETURNS anyelement
AS 'MODULE_PATHNAME', 'collection_value'
LANGUAGE c;

CREATE FUNCTION value(collection)
RETURNS text
AS 'MODULE_PATHNAME', 'collection_value'
STRICT
LANGUAGE c;

CREATE FUNCTION next(collection)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_next'
LANGUAGE c;

CREATE FUNCTION isnull(collection)
RETURNS bool
AS 'MODULE_PATHNAME', 'collection_isnull'
STRICT
LANGUAGE c;

CREATE FUNCTION prev(collection)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_prev'
LANGUAGE c;

CREATE FUNCTION first(collection)
RETURNS collection
AS 'MODULE_PATHNAME', 'collection_first'
LANGUAGE c;

CREATE FUNCTION keys_to_table(collection)
RETURNS SETOF text
AS 'MODULE_PATHNAME', 'collection_keys_to_table'
LANGUAGE c;

CREATE FUNCTION values_to_table(collection)
RETURNS SETOF text
AS 'MODULE_PATHNAME', 'collection_values_to_table'
LANGUAGE c;

CREATE FUNCTION values_to_table(collection, anyelement)
RETURNS SETOF anyelement
AS 'MODULE_PATHNAME', 'collection_values_to_table'
LANGUAGE c;

CREATE FUNCTION to_table(collection, OUT key text, OUT value text)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'collection_to_table'
LANGUAGE c;

CREATE FUNCTION to_table(collection, anyelement, OUT key text, OUT value anyelement)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'collection_to_table'
LANGUAGE c;

CREATE FUNCTION value_type(collection)
RETURNS regtype
AS 'MODULE_PATHNAME', 'collection_value_type'
LANGUAGE c;

CREATE FUNCTION collection_stats(OUT add int8, OUT context_switch int8,
OUT delete int8, OUT find int8, OUT sort int8)
AS 'MODULE_PATHNAME', 'collection_stats'
LANGUAGE c;

CREATE FUNCTION collection_stats_reset()
RETURNS void
AS 'MODULE_PATHNAME', 'collection_stats_reset'
LANGUAGE c;

CREATE VIEW collection_stats
AS SELECT add, context_switch, delete, find, sort
FROM collection_stats();

CREATE CAST (collection AS json)
WITH INOUT
AS IMPLICIT;
5 changes: 4 additions & 1 deletion src/collection_userfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ PG_FUNCTION_INFO_V1(collection_stats_reset);
StatsCounters stats;

static int by_key(const struct collection *a, const struct collection *b);
static Oid collection_collation = DEFAULT_COLLATION_OID;

Datum
collection_add(PG_FUNCTION_ARGS)
Expand Down Expand Up @@ -256,6 +257,8 @@ collection_sort(PG_FUNCTION_ARGS)

colhdr = fetch_collection(fcinfo, 0);

collection_collation = PG_GET_COLLATION();

pgstat_report_wait_start(collection_we_sort);

if (colhdr->head)
Expand Down Expand Up @@ -675,5 +678,5 @@ collection_stats_reset(PG_FUNCTION_ARGS)
static int
by_key(const struct collection *a, const struct collection *b)
{
return varstr_cmp(a->key, strlen(a->key), b->key, strlen(b->key), DEFAULT_COLLATION_OID);
return varstr_cmp(a->key, strlen(a->key), b->key, strlen(b->key), collection_collation);
}
31 changes: 31 additions & 0 deletions test/expected/iteration.out
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,34 @@ END
$$;
NOTICE: Iteration test 14
NOTICE: find after next: Hello World
DO $$
DECLARE
u collection COLLATE "en_US";
v collection COLLATE "C";
BEGIN
RAISE NOTICE 'Iteration test 15';
u['a'] := '1'::text;
u['B'] := '2'::text;
u['c'] := '3'::text;
v := copy(u);

u := sort(u);
WHILE NOT isnull(u) LOOP
RAISE NOTICE 'u value: %', value(u);
u := next(u);
END LOOP;

v := sort(v);
WHILE NOT isnull(v) LOOP
RAISE NOTICE 'v value: %', value(v);
v := next(v);
END LOOP;
END
$$;
NOTICE: Iteration test 15
NOTICE: u value: 1
NOTICE: u value: 2
NOTICE: u value: 3
NOTICE: v value: 2
NOTICE: v value: 1
NOTICE: v value: 3
25 changes: 25 additions & 0 deletions test/sql/iteration.sql
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,28 @@ BEGIN
RAISE NOTICE 'find after next: %', find(u, 'aaa');
END
$$;

DO $$
DECLARE
u collection COLLATE "en_US";
v collection COLLATE "C";
BEGIN
RAISE NOTICE 'Iteration test 15';
u['a'] := '1'::text;
u['B'] := '2'::text;
u['c'] := '3'::text;
v := copy(u);

u := sort(u);
WHILE NOT isnull(u) LOOP
RAISE NOTICE 'u value: %', value(u);
u := next(u);
END LOOP;

v := sort(v);
WHILE NOT isnull(v) LOOP
RAISE NOTICE 'v value: %', value(v);
v := next(v);
END LOOP;
END
$$;

0 comments on commit 7b6b444

Please sign in to comment.