1

How to implement contains operator for strings which returns true if left string contains in right string.

Operator name can be any. I tried @@ and code below but

select 'A' @@ 'SAS'

returns false.

How to fix ?

CREATE OR REPLACE FUNCTION public.contains(searchFor text, searchIn text)
RETURNS bool

AS $BODY$ BEGIN

RETURN position( searchFor in searchIn)<>0;

END; $BODY$ language plpgsql immutable  RETURNS NULL ON NULL INPUT;

CREATE OPERATOR public.@@ (
    leftarg = text,
    rightarg = text,
    procedure = public.contains
);

Using Postgres 9.1 and above in windows and linux.

select contains('A' , 'SAS' )

returns true as expected.

Update

I tried in 9.1 code from answer:

CREATE OR REPLACE FUNCTION public.contains(searchFor text, searchIn text)
RETURNS bool
LANGUAGE sql IMMUTABLE
AS $BODY$
SELECT position( searchFor in searchIn )<>0;
$BODY$;


CREATE OPERATOR public.<@ (
    leftarg = text,
    rightarg = text,
    procedure = public.contains
);

but got error

ERROR:  column "searchin" does not exist
LINE 5: SELECT position( searchFor in searchIn )<>0;

How to make it work in 9.1 ? In 9.3 it works.

Using

"PostgreSQL 9.1.2 on x86_64-unknown-linux-gnu, compiled by gcc-4.4.real (Debian 4.4.5-8) 4.4.5, 64-bit"

1 Answer 1

2

PostgreSQL already defines an @@ operator on (text,text) in the pg_catalog schema, which is defined as:

regress=> \do @@
                                  List of operators
   Schema   | Name | Left arg type | Right arg type | Result type |    Description    
------------+------+---------------+----------------+-------------+-------------------
 pg_catalog | @@   | text          | text           | boolean     | text search match

That is taking precedence over the @@ you defined in the public schema.

I suggest using the operator <@ for contains, because that's consistent with the array contains and contained-by operators. There's no usage of it in pg_catalog for text ... but there's no guarantee one won't be added in a future version or by an extension.

If you want to guarantee that your operators take precedence over those in pg_catalog, you need to put them in a different schema and put it first on the search path, explicitly before pg_catalog. So something like:

CREATE SCHEMA my_operators;

CREATE OR REPLACE FUNCTION my_operators.contains(searchFor text, searchIn text)
RETURNS bool
LANGUAGE sql IMMUTABLE
AS $BODY$
SELECT position( searchFor in searchIn)<>0;
$BODY$;

CREATE OPERATOR my_operators.<@ (
    leftarg = text,
    rightarg = text,
    procedure = public.contains
);

then

SET search_path = 'my_operators, pg_catalog, public';

which you can do with ALTER USER and/or ALTER DATABASE to make it a default.

Sign up to request clarification or add additional context in comments.

6 Comments

Answer does not contain RETURNS NULL ON NULL INPUT . Should it added to help planner? Is it reasonable and possible to add COMMUTATOR, RESTRICT, JOIN, HASHES, MERGES clauses ? Is it better to use language sql instead of pgsql ?
@Andrus Actually RETURNS NULL ON NULL INPUT and STRICT can inhibit inlining, and are better omitted if possible. The documentation needs to be clearer about that. Yes, it's quite reasonable to add a commutator @> operator. I don't see how you'd implement hashing.
How to create operator only if it does not exist ? I tried CREATE OPERATOR IF NOT EXISTS public.<@ but got error
@Andrus Use a DO block that looks it up in pg_operator, I guess. Not every statement type has IF NOT EXISTS. Personally I strongly suggest packaging it in an extension instead. The documentation covers the creation of extensions. You don't need any C code for an extension, it can be just an SQL file, Makefile and extension control file.
@Andrus replace searchfor with $1 and searcin with $2
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.