How to create an operator in PostgreSQL for the hstore type with an int4range value - postgresql

I have a table with an HSTORE column 'ext', where the value is an int4range. An example:
"p1"=>"[10, 18]", "p2"=>"[24, 32]", "p3"=>"[29, 32]", "p4"=>"[18, 19]"
However, when I try to create an expression index on this, I get an error:
CREATE INDEX ix_test3_p1
ON test3
USING gist
(((ext -> 'p1'::text)::int4range));
ERROR: data type text has no default operator class for access method
"gist" SQL state: 42704 Hint: You must specify an operator class for
the index or define a default operator class for the data type.
How do I create the operator for this?
NOTE
Each record may have its own unique set of keys. Each key represents an attribute, and the values the value range. So not all records will have "p1". Consider this an EAV model in hstore.

I don't get that error - I get "functions in index expression must be marked IMMUTABLE"
CREATE TABLE ht (ext hstore);
INSERT INTO ht VALUES ('p1=>"[10,18]"'), ('p1=>"[99,99]"');
CREATE INDEX ht_test_idx ON ht USING GIST ( ((ext->'p1'::text)::int4range) );
ERROR: functions in index expression must be marked IMMUTABLE
CREATE FUNCTION foo(hstore) RETURNS int4range LANGUAGE SQL AS $$ SELECT ($1->'p1')::int4range; $$ IMMUTABLE;
CREATE INDEX ht_test_idx ON ht USING GIST ( foo(ext) );
SET enable_seq_scan=false;
EXPLAIN SELECT * FROM ht WHERE foo(ext) = '[10,19)';
QUERY PLAN
-----------------------------------------------------------------------
Index Scan using ht_test_idx on ht (cost=0.25..8.52 rows=1 width=32)
Index Cond: (foo(ext) = '[10,19)'::int4range)
I'm guessing the cast isn't immutable because you can change the default format of the range from inclusive...exclusive "[...)" to something else. You presumably won't be doing that though.
Obviously you'll want your real function to deal with things like missing "p1" entries, badly formed range values etc.

Related

ERROR: functions in index expression must be marked IMMUTABLE in PostgreSQL 12

I want to create an expression index, but when I create the index, the following message is output:
CREATE INDEX member_lastactivity_idx ON adempiere.member USING btree (member_id, referredby, ((lastactivity::date)>CURRENT_DATE-interval'60 days'));

How to create support functions for gin index on custom operator class in postgres?

I created some custom operators for jsonb type and a class for them all. Problem is that when i create an index
CREATE INDEX idx_name on table USING gin(column_name custom_operator_class)
I get an error
missing support function 2 for attribute 1 of index "idx_name"
I probably need to create support classes for overlap, contains, containedBy and equal, but i am not finding any documentation on how to do that. All i found online is for btree, and nothing for gin. Does anybody know how to do this, or any material where i can find some examples?
If you need more information, i will be glad to say more. Operators are basically for recursive search of keys where date is less than, more than, equal to the specified one
EDIT:
I tried creating support functions like this
CREATE OR REPLACE FUNCTION jb_custom_contains(jsonb, jsonb)
RETURNS bool AS
'SELECT $1 <# $2' LANGUAGE sql IMMUTABLE;
CREATE OR REPLACE FUNCTION jb_custom_contaiedBy(jsonb, jsonb)
RETURNS bool AS
'SELECT $1 #> $2' LANGUAGE sql IMMUTABLE;
CREATE OR REPLACE FUNCTION jb_custom_equals(jsonb, jsonb)
RETURNS bool AS
'SELECT $1 = $2' LANGUAGE sql IMMUTABLE;
CREATE INDEX then doesn't return an error, but the operator won't work properly
To explain your error message:
Support function 2 is defined in src/include/access/gin.h:
/*
* amproc indexes for inverted indexes.
*/
#define GIN_COMPARE_PROC 1
#define GIN_EXTRACTVALUE_PROC 2
#define GIN_EXTRACTQUERY_PROC 3
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
#define GIN_TRICONSISTENT_PROC 6
#define GIN_OPTIONS_PROC 7
#define GINNProcs 7
That is, support function 2 is the extractValue described in the documentation:
There are two methods that an operator class for GIN must provide:
Datum *extractValue(Datum itemValue, int32 *nkeys, bool **nullFlags)
Returns a palloc'd array of keys given an item to be indexed. The number of returned keys must be stored into *nkeys. If any of the keys can be null, also palloc an array of *nkeys bool fields, store its address at *nullFlags, and set these null flags as needed. *nullFlags can be left NULL (its initial value) if all keys are non-null. The return value can be NULL if the item contains no keys.
So the following is missing in your CREATE OPERATOR CLASS statement:
CREATE OPERATOR CLASS custom_operator_class FOR TYPE jsonb USING gin AS
FUNCTION 2 myextractvaluefunc(jsonb, internal),
...;
You are confusing a strategy number with a support function number.
This is documented in table 37.13 and chapter 66 of the official documentation, and there are examples linked therein.

How to use array operators for type bytea[]?

Is it possible to use array operators on a type of bytea[]?
For example:
CREATE TABLE test (
metadata bytea[]
);
SELECT * FROM test WHERE test.metadata && ANY($1);
// could not find array type for data type bytea[]
If it's not possible, is there an alternative approach without changing the type from bytea[]?
postgresql 12.x
Do not use ANY, just compare the arrays directly using an array constructor and array functions
CREATE TABLE test (
metadata bytea[]
);
INSERT INTO public.test (metadata) VALUES('{"x","y"}');
SELECT * FROM test t WHERE metadata && array[E'\x78'::bytea];
When using ANY, the left-hand expression is evaluated and compared to each element of the right-hand array using the given operator, which must yield a Boolean result. So the original sql was trying to do something like bytea[] && bytea.
This applies not only for bytea[], but any array type e.g text[] or integer[].

Postgres: create index on attribute of attribute in JSONB column?

I'm working in Postgres 9.6.5. I have the following table:
id | integer
data | jsonb
The data in the data column is nested, in the form:
{ 'identification': { 'registration_number': 'foo' }}
I'd like to index registration_number, so I can query on it. I've tried this (based on this answer):
CREATE INDEX ON mytable((data->>'identification'->>'registration_number'));
But got this:
ERROR: operator does not exist: text ->> unknown
LINE 1: CREATE INDEX ON psc((data->>'identification'->>'registration... ^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
What am I doing wrong?
You want:
CREATE INDEX ON mytable((data -> 'identification' ->> 'registration_number'));
The -> operator returns the jsonb object under the key, and the ->> operator returns the jsonb object under the key as text. The most notable difference between the two operators is that ->> will "unwrap" string values (i.e. remove double quotes from the TEXT representation).
The error you're seeing is reported because data ->> 'identification' returns text, and the subsequent ->> is not defined for the text type.
Since version 9.3 Postgres has the #> and #>> operators. This operators allow the user to specify a path (using an array of text) inside jsonb column to get the value.
You could use this operator to achieve your goal in a simpler way.
CREATE INDEX ON mytable((data #>> '{identification, registration_number}'));

PostgreSQL index array of int4range using GIN - custom operator class

Here is my table:
CREATE TABLE
mytable
(
id INT NOT NULL PRIMARY KEY,
val int4range[]
);
I want to index the val column:
CREATE INDEX
ix_mytable_val
ON mytable
USING GIN (INT4RANGE(val, '[]')); // error, as is GIN(val)
I came up with the following:
CREATE OPERATOR CLASS gin_int4range_ops
DEFAULT FOR TYPE int4range[] USING gin AS
OPERATOR 1 <(anyrange,anyrange),
OPERATOR 2 <=(anyrange,anyrange),
OPERATOR 3 =(anyrange,anyrange),
OPERATOR 4 >=(anyrange,anyrange),
OPERATOR 5 >(anyrange,anyrange),
FUNCTION 1 lower(anyrange),
FUNCTION 2 upper(anyrange),
FUNCTION 3 isempty(anyrange),
FUNCTION 4 lower_inc(anyrange),
FUNCTION 5 upper_inc(anyrange);
But when I try to create the index, it fails (error below). However, if I call the create from within a DO $$ block, it executes.
If the create index executed, I get the error on INSERT INTO instead.
"ERROR: cache lookup failed for type 1"
I also tried this:
OPERATOR 1 &&(anyrange,anyrange),
OPERATOR 2 <#(anyrange,anyrange),
OPERATOR 3 #>(anyrange,anyrange),
OPERATOR 4 =(anyrange,anyrange),
In order to try and solve this, I have rebooted PG, the machine, and vacuumed the DB. I believe there is an error in the CREATE OPERATOR code.
If I can index an array of custom type of (int, int4range), that would be even better.
I've spent quite some time (a full day) wading through documentation, forums, etc., but can find nothing that really helps me to understand how to solve this (i.e. create a working custom operator class).
You need to CREATE OPERATOR CLASS based on Range Functions and Operators, for example:
CREATE OPERATOR CLASS gin_int4range_ops
DEFAULT FOR TYPE int4range[] USING gin AS
OPERATOR 1 =(anyrange,anyrange),
FUNCTION 1 lower(anyrange),
FUNCTION 2 upper(anyrange),
FUNCTION 3 isempty(anyrange),
FUNCTION 4 lower_inc(anyrange),
FUNCTION 5 upper_inc(anyrange);
Now you can CREATE INDEX:
CREATE INDEX ix_mytable4_vhstore_low
ON mytable USING gin (val gin_int4range_ops);
Check also:
Operator Classes and Operator Families
CREATE OPERATOR CLASS
The following query shows all defined operator classes:
SELECT am.amname AS index_method,
opc.opcname AS opclass_name
FROM pg_am am, pg_opclass opc
WHERE opc.opcmethod = am.oid
ORDER BY index_method, opclass_name;
This query shows all defined operator families and all the operators included in each family:
SELECT am.amname AS index_method,
opf.opfname AS opfamily_name,
amop.amopopr::regoperator AS opfamily_operator
FROM pg_am am, pg_opfamily opf, pg_amop amop
WHERE opf.opfmethod = am.oid AND
amop.amopfamily = opf.oid
ORDER BY index_method, opfamily_name, opfamily_operator;