sql >> Base de Datos >  >> RDS >> PostgreSQL

Cómo escapar del carácter de signo de interrogación (?) con Spring JpaRepository

En caso de escapar ? no es posible, puede crear un operador duplicado con un nombre diferente.

Nuevo operador

Sintaxis para crear operadores en Postgres:

CREATE OPERATOR name (
    PROCEDURE = function_name
    [, LEFTARG = left_type ] [, RIGHTARG = right_type ]
    [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]
    [, RESTRICT = res_proc ] [, JOIN = join_proc ]
    [, HASHES ] [, MERGES ]
)

En caso de ?| usado en jsonb será:

CREATE OPERATOR ^|(
  PROCEDURE = jsonb_exists_any,
  LEFTARG = jsonb,
  RIGHTARG = _text,
  RESTRICT = contsel,
  JOIN = contjoinsel);

He usado ^| como ejemplo, nombre alternativo. Puede ser cualquier secuencia de esta lista:+ - * / < > = ~ ! @ # % ^ & | ?`.

Puede encontrar la definición actual del operador que le interesa consultando la tabla pg_catalog.pg_operator.

SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');

También puede usar una herramienta GUI como pgAdmin y navegar por pg_catalog para obtener la definición de SQL lista para su reutilización.

Habilitación de índice

Si desea utilizar el índice para este "nuevo" operador, deberá crear una nueva clase de operador y, opcionalmente, una familia. En nuestro caso, necesitamos ambos, ya que no podemos agregarlo a la familia existente, porque el operador predeterminado ya está tomando el espacio de estrategia.

Al igual que con los operadores, se recomienda usar una herramienta GUI como pgAdmin para buscar clases de operadores y simplemente copiar y pegar.

Primero, tomamos el OID del operador del que hicimos un duplicado:

SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');

Lo mismo para la familia de operadores (lo obtendremos de la tabla de clases de operadores), estamos buscando la clase gin ya que esta es la que admite ?| . opcdefault se usa, porque hay una clase opcional jsonb_path_ops que no admite este operador:

SELECT opcfamily
  FROM pg_opclass
 WHERE opcintype = (SELECT oid FROM pg_type WHERE typname = 'jsonb')
   AND opcmethod = (SELECT oid FROM pg_am WHERE amname = 'gin')
   AND opcdefault

Luego obtenemos la estrategia utilizada por el operador que duplicamos:

SELECT amopstrategy,
       (SELECT typname FROM pg_type WHERE oid = amoplefttype) AS left_t, 
       (SELECT typname FROM pg_type WHERE oid = amoprighttype) AS right_t,*
FROM pg_amop
WHERE amopfamily = 4036 --family oid
  AND amopopr = 3248 --operator oid

Luego funciones usadas por clase:

SELECT amprocnum, amproc::text, pg_get_function_identity_arguments(amproc::oid) AS args,
      (SELECT typname FROM pg_type WHERE oid = amproclefttype) AS left_t,
      (SELECT typname FROM pg_type WHERE oid = amprocrighttype) AS right_t,*
FROM pg_amproc
WHERE amprocfamily = 4036 --op family

Esto nos lleva a esta clase de operador. Creará una familia de operadores si aún no existe.

CREATE OPERATOR CLASS jsonb_ops_custom
   FOR TYPE jsonb USING gin AS
   OPERATOR 10  ^|(jsonb, _text),
   FUNCTION 1  gin_compare_jsonb(text, text),
   FUNCTION 2  gin_extract_jsonb(jsonb, internal, internal),
   FUNCTION 3  gin_extract_jsonb_query(jsonb, internal, smallint, internal, internal, internal, internal),
   FUNCTION 4  gin_consistent_jsonb(internal, smallint, jsonb, integer, internal, internal, internal, internal),
   FUNCTION 6  gin_triconsistent_jsonb(internal, smallint, jsonb, integer, internal, internal, internal);

Ahora solo necesita crear un índice usando el nombre del operador que se creó, algo como:

CREATE INDEX ON jsonb_table USING gin(jsonb_column jsonb_ops_custom)

Y deberías poder usar index:

SET enable_seqscan = off;
EXPLAIN ANALYZE
SELECT * FROM jsonb_table WHERE jsonb_column ^| array['b', 'c'];