You could concatenate the function parameter into a string and cast to jsonpath. But nested string values are double-quoted in a jsonpath expression (while Postgres string types use single-quotes). Neither format() nor any of the quote_*() family of functions handles that properly.
You could double-quote manually, escaping nested double-quotes if any. Or cast to json / jsonb and back to text to make Postgres do the work for you, reliably. Either with jsonb_build_object('arg1', arg1) to build the jsonb argument for the function call. Or with to_json(arg1)::text for string concatenation.
The largely equivalent function jsonb_path_exists() offers the option to pass parameters to the jsonpath expression safely. That's not implemented for the operator @?.
CREATE OR REPLACE FUNCTION public.new_function1(arg1 text DEFAULT 'Miha')
RETURNS SETOF public.declarations -- schema-qualify to be safe
LANGUAGE sql STABLE PARALLEL SAFE AS -- PARALLEL SAFE in Postgres 9.6 or later
$func$
SELECT *
FROM public.declarations
WHERE jsonb_path_exists(target => data
, path => 'strict $.** ? (@ == $arg1)' -- can use a param ...
, vars => jsonb_build_object('arg1', arg1) -- ... which is defined in next arg
, silent => true); -- to behave exactly like original operator @?
$func$;
Using the optional, more verbose function call with named parameters for clarity. See:
Major downside: Postgres indexes are bound to operators, not functions. So this form gets no index support. No good for big tables. Related:
Operator @?
CREATE OR REPLACE FUNCTION public.new_function1(arg1 text DEFAULT 'Miha')
RETURNS SETOF public.declarations
LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT *
FROM public.declarations
WHERE data @? format('strict $.** ? (@ == $s)' -- note: strict
, to_json(arg1)::text -- safe conversion
)::jsonpath;
$func$;
I added strict as advised in the manual:
To avoid surprising results, we recommend using the .** accessor only in the strict mode.
Wouldn't break in this particular case, but it's still cheaper with strict.
You could also use dynamic SQL with EXECUTE in a PL/pgSQL function and pass the jsonpath expression in a USING clause. (The same issue with double-quotes applies.) See:
Index
CREATE INDEX declarations_gin_idx ON public.declarations USING gin (data);
Important detail for your particular jsonpath expression, quoting the manual:
The jsonb_ops operator class also supports .* and .** accessors,
but the jsonb_path_ops operator class does not.
So we cannot use the more optimized operator class jsonb_path_ops for this. See:
jsonb_path_exists(). That can take avarsparameter that you can create withjsonb_build_object(); or 2. Useformat()or text concatenation to build the query. e.g.format('$.** ? (@ == "%s")', arg1)select * from declarations where data @? format('$.** ? (@ == "%s")', arg1)::jsonpath;it must be cast to jsonpath type.