65.5. Índices BRIN #

65.5.1. Introducción
65.5.2. Clases de operadores integradas
65.5.3. Extensibilidad

65.5.1. Introducción #

BRIN significa Block Range Index (Índice de rango de bloques). BRIN está diseñado para manejar tablas muy grandes en las que ciertas columnas tienen alguna correlación natural con su ubicación física dentro de la tabla.

BRIN funciona en términos de rangos de bloques (o «rangos de páginas»). Un rango de bloques es un grupo de páginas físicamente adyacentes en la tabla; para cada rango de bloques, el índice almacena cierta información resumida. Por ejemplo, una tabla que almacene los pedidos de venta de una tienda podría tener una columna de fecha en la que se realizó cada pedido, y la mayor parte del tiempo las entradas de los pedidos anteriores aparecerán antes en la tabla; una tabla que almacene una columna de código postal podría tener todos los códigos de una ciudad agrupados de forma natural.

Los índices BRIN pueden satisfacer consultas mediante escaneos de índices de mapas de bits (bitmap index scans) regulares, y devolverán todas las tuplas de todas las páginas dentro de cada rango si la información resumida almacenada por el índice es consistente con las condiciones de la consulta. El ejecutor de la consulta se encarga de volver a comprobar estas tuplas y descartar aquellas que no coincidan con las condiciones de la consulta; en otras palabras, estos índices tienen pérdidas (son «lossy»). Debido a que un índice BRIN es muy pequeño, escanear el índice añade poca sobrecarga en comparación con un escaneo secuencial, pero puede evitar escanear grandes partes de la tabla que se sabe que no contienen tuplas coincidentes.

Los datos específicos que almacenará un índice BRIN, así como las consultas específicas que el índice podrá satisfacer, dependen de la clase de operadores seleccionada para cada columna del índice. Los tipos de datos que tienen un orden de clasificación lineal pueden tener clases de operadores que almacenen el valor mínimo y máximo dentro de cada rango de bloques, por ejemplo; los tipos geométricos podrían almacenar la caja delimitadora (bounding box) de todos los objetos en el rango de bloques.

El tamaño del rango de bloques se determina en el momento de la creación del índice mediante el parámetro de almacenamiento pages_per_range. El número de entradas del índice será igual al tamaño de la relación en páginas dividido por el valor seleccionado para pages_per_range. Por lo tanto, cuanto menor sea el número, mayor se volverá el índice (debido a la necesidad de almacenar más entradas de índice), pero al mismo tiempo los datos de resumen almacenados pueden ser más precisos y se pueden omitir más bloques de datos durante el escaneo de un índice.

65.5.1.1. Mantenimiento de índices #

En el momento de la creación, se escanean todas las páginas de la tabla y se crea una tupla de índice de resumen para cada rango, incluyendo el rango posiblemente incompleto al final. A medida que se llenan nuevas páginas con datos, los rangos de páginas que ya están resumidos harán que se actualice la información de resumen con los datos de las nuevas tuplas. Cuando se crea una nueva página que no cae dentro del último rango resumido, el rango al que pertenece la nueva página no adquiere automáticamente una tupla de resumen; esas tuplas permanecen sin resumir hasta que se invoque una ejecución de resumen más adelante, creando el resumen inicial para ese rango.

Hay varias formas de desencadenar el resumen inicial de un rango de páginas. Si la tabla es vacumada, ya sea manualmente o por autovacuum, se resumen todos los rangos de páginas no resumidos existentes. Además, si el parámetro autosummarize del índice está activado, lo cual no ocurre por defecto, cada vez que se ejecute autovacuum en esa base de datos, se realizará el resumen para todos los rangos de páginas no resumidos que se hayan llenado, independientemente de si la propia tabla es procesada por autovacuum; ver más abajo.

Por último, se pueden utilizar las siguientes funciones (mientras estas funciones se ejecutan, search_path se cambia temporalmente a pg_catalog, pg_temp):

brin_summarize_new_values(regclass) que resume todos los rangos no resumidos;
brin_summarize_range(regclass, bigint) que resume solo el rango que contiene la página dada, si no está resumido.

Cuando el resumen automático (autosummarization) está activado, se envía una solicitud a autovacuum para ejecutar un resumen específico para un rango de bloques cuando se detecta una inserción para el primer elemento de la primera página del siguiente rango de bloques, para ser cumplida la próxima vez que un trabajador de autovacuum termine de ejecutarse en la misma base de datos. Si la cola de solicitudes está llena, la solicitud no se registra y se envía un mensaje al log del servidor:

LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was not recorded

Cuando esto sucede, el rango permanecerá sin resumir hasta la siguiente ejecución regular de vacuum en la tabla, o hasta que se invoque una de las funciones mencionadas anteriormente.

Por el contrario, se puede anular el resumen de un rango utilizando la función brin_desummarize_range(regclass, bigint), lo cual es útil cuando la tupla del índice ya no es una representación muy buena porque los valores existentes han cambiado. Consulta Section 9.28.8 para más detalles.

65.5.2. Clases de operadores integradas #

La distribución principal de PostgreSQL incluye las clases de operadores BRIN que se muestran en Table 65.4.

Las clases de operadores minmax almacenan los valores mínimos y máximos que aparecen en la columna indexada dentro del rango. Las clases de operadores de inclusion (inclusión) almacenan un valor que incluye los valores de la columna indexada dentro del rango. Las clases de operadores bloom construyen un filtro de Bloom para todos los valores del rango. Las clases de operadores minmax-multi almacenan múltiples valores mínimos y máximos, representando los valores que aparecen en la columna indexada dentro del rango.

Table 65.4. Clases de operadores BRIN integradas

NombreOperadores indexables
bit_minmax_ops= (bit,bit)
< (bit,bit)
> (bit,bit)
<= (bit,bit)
>= (bit,bit)
box_inclusion_ops@> (box,point)
<< (box,box)
&< (box,box)
&> (box,box)
>> (box,box)
<@ (box,box)
@> (box,box)
~= (box,box)
&& (box,box)
<<| (box,box)
&<| (box,box)
|&> (box,box)
|>> (box,box)
bpchar_bloom_ops= (character,character)
bpchar_minmax_ops= (character,character)
< (character,character)
<= (character,character)
> (character,character)
>= (character,character)
bytea_bloom_ops= (bytea,bytea)
bytea_minmax_ops= (bytea,bytea)
< (bytea,bytea)
<= (bytea,bytea)
> (bytea,bytea)
>= (bytea,bytea)
char_bloom_ops= ("char","char")
char_minmax_ops= ("char","char")
< ("char","char")
<= ("char","char")
> ("char","char")
>= ("char","char")
date_bloom_ops= (date,date)
date_minmax_ops= (date,date)
< (date,date)
<= (date,date)
> (date,date)
>= (date,date)
date_minmax_multi_ops= (date,date)
< (date,date)
<= (date,date)
> (date,date)
>= (date,date)
float4_bloom_ops= (float4,float4)
float4_minmax_ops= (float4,float4)
< (float4,float4)
> (float4,float4)
<= (float4,float4)
>= (float4,float4)
float4_minmax_multi_ops= (float4,float4)
< (float4,float4)
> (float4,float4)
<= (float4,float4)
>= (float4,float4)
float8_bloom_ops= (float8,float8)
float8_minmax_ops= (float8,float8)
< (float8,float8)
<= (float8,float8)
> (float8,float8)
>= (float8,float8)
float8_minmax_multi_ops= (float8,float8)
< (float8,float8)
<= (float8,float8)
> (float8,float8)
>= (float8,float8)
inet_inclusion_ops<< (inet,inet)
<<= (inet,inet)
>> (inet,inet)
>>= (inet,inet)
= (inet,inet)
&& (inet,inet)
inet_bloom_ops= (inet,inet)
inet_minmax_ops= (inet,inet)
< (inet,inet)
<= (inet,inet)
> (inet,inet)
>= (inet,inet)
inet_minmax_multi_ops= (inet,inet)
< (inet,inet)
<= (inet,inet)
> (inet,inet)
>= (inet,inet)
int2_bloom_ops= (int2,int2)
int2_minmax_ops= (int2,int2)
< (int2,int2)
> (int2,int2)
<= (int2,int2)
>= (int2,int2)
int2_minmax_multi_ops= (int2,int2)
< (int2,int2)
> (int2,int2)
<= (int2,int2)
>= (int2,int2)
int4_bloom_ops= (int4,int4)
int4_minmax_ops= (int4,int4)
< (int4,int4)
> (int4,int4)
<= (int4,int4)
>= (int4,int4)
int4_minmax_multi_ops= (int4,int4)
< (int4,int4)
> (int4,int4)
<= (int4,int4)
>= (int4,int4)
int8_bloom_ops= (bigint,bigint)
int8_minmax_ops= (bigint,bigint)
< (bigint,bigint)
> (bigint,bigint)
<= (bigint,bigint)
>= (bigint,bigint)
int8_minmax_multi_ops= (bigint,bigint)
< (bigint,bigint)
> (bigint,bigint)
<= (bigint,bigint)
>= (bigint,bigint)
interval_bloom_ops= (interval,interval)
interval_minmax_ops= (interval,interval)
< (interval,interval)
<= (interval,interval)
> (interval,interval)
>= (interval,interval)
interval_minmax_multi_ops= (interval,interval)
< (interval,interval)
<= (interval,interval)
> (interval,interval)
>= (interval,interval)
macaddr_bloom_ops= (macaddr,macaddr)
macaddr_minmax_ops= (macaddr,macaddr)
< (macaddr,macaddr)
<= (macaddr,macaddr)
> (macaddr,macaddr)
>= (macaddr,macaddr)
macaddr_minmax_multi_ops= (macaddr,macaddr)
< (macaddr,macaddr)
<= (macaddr,macaddr)
> (macaddr,macaddr)
>= (macaddr,macaddr)
macaddr8_bloom_ops= (macaddr8,macaddr8)
macaddr8_minmax_ops= (macaddr8,macaddr8)
< (macaddr8,macaddr8)
<= (macaddr8,macaddr8)
> (macaddr8,macaddr8)
>= (macaddr8,macaddr8)
macaddr8_minmax_multi_ops= (macaddr8,macaddr8)
< (macaddr8,macaddr8)
<= (macaddr8,macaddr8)
> (macaddr8,macaddr8)
>= (macaddr8,macaddr8)
name_bloom_ops= (name,name)
name_minmax_ops= (name,name)
< (name,name)
<= (name,name)
> (name,name)
>= (name,name)
numeric_bloom_ops= (numeric,numeric)
numeric_minmax_ops= (numeric,numeric)
< (numeric,numeric)
<= (numeric,numeric)
> (numeric,numeric)
>= (numeric,numeric)
numeric_minmax_multi_ops= (numeric,numeric)
< (numeric,numeric)
<= (numeric,numeric)
> (numeric,numeric)
>= (numeric,numeric)
oid_bloom_ops= (oid,oid)
oid_minmax_ops= (oid,oid)
< (oid,oid)
> (oid,oid)
<= (oid,oid)
>= (oid,oid)
oid_minmax_multi_ops= (oid,oid)
< (oid,oid)
> (oid,oid)
<= (oid,oid)
>= (oid,oid)
pg_lsn_bloom_ops= (pg_lsn,pg_lsn)
pg_lsn_minmax_ops= (pg_lsn,pg_lsn)
< (pg_lsn,pg_lsn)
> (pg_lsn,pg_lsn)
<= (pg_lsn,pg_lsn)
>= (pg_lsn,pg_lsn)
pg_lsn_minmax_multi_ops= (pg_lsn,pg_lsn)
< (pg_lsn,pg_lsn)
> (pg_lsn,pg_lsn)
<= (pg_lsn,pg_lsn)
>= (pg_lsn,pg_lsn)
range_inclusion_ops= (anyrange,anyrange)
< (anyrange,anyrange)
<= (anyrange,anyrange)
>= (anyrange,anyrange)
> (anyrange,anyrange)
&& (anyrange,anyrange)
@> (anyrange,anyelement)
@> (anyrange,anyrange)
<@ (anyrange,anyrange)
<< (anyrange,anyrange)
>> (anyrange,anyrange)
&< (anyrange,anyrange)
&> (anyrange,anyrange)
-|- (anyrange,anyrange)
text_bloom_ops= (text,text)
text_minmax_ops= (text,text)
< (text,text)
<= (text,text)
> (text,text)
>= (text,text)
tid_bloom_ops= (tid,tid)
tid_minmax_ops= (tid,tid)
< (tid,tid)
> (tid,tid)
<= (tid,tid)
>= (tid,tid)
tid_minmax_multi_ops= (tid,tid)
< (tid,tid)
> (tid,tid)
<= (tid,tid)
>= (tid,tid)
timestamp_bloom_ops= (timestamp,timestamp)
timestamp_minmax_ops= (timestamp,timestamp)
< (timestamp,timestamp)
<= (timestamp,timestamp)
> (timestamp,timestamp)
>= (timestamp,timestamp)
timestamp_minmax_multi_ops= (timestamp,timestamp)
< (timestamp,timestamp)
<= (timestamp,timestamp)
> (timestamp,timestamp)
>= (timestamp,timestamp)
timestamptz_bloom_ops= (timestamptz,timestamptz)
timestamptz_minmax_ops= (timestamptz,timestamptz)
< (timestamptz,timestamptz)
<= (timestamptz,timestamptz)
> (timestamptz,timestamptz)
>= (timestamptz,timestamptz)
timestamptz_minmax_multi_ops= (timestamptz,timestamptz)
< (timestamptz,timestamptz)
<= (timestamptz,timestamptz)
> (timestamptz,timestamptz)
>= (timestamptz,timestamptz)
time_bloom_ops= (time,time)
time_minmax_ops= (time,time)
< (time,time)
<= (time,time)
> (time,time)
>= (time,time)
time_minmax_multi_ops= (time,time)
< (time,time)
<= (time,time)
> (time,time)
>= (time,time)
timetz_bloom_ops= (timetz,timetz)
timetz_minmax_ops= (timetz,timetz)
< (timetz,timetz)
<= (timetz,timetz)
> (timetz,timetz)
>= (timetz,timetz)
timetz_minmax_multi_ops= (timetz,timetz)
< (timetz,timetz)
<= (timetz,timetz)
> (timetz,timetz)
>= (timetz,timetz)
uuid_bloom_ops= (uuid,uuid)
uuid_minmax_ops= (uuid,uuid)
< (uuid,uuid)
> (uuid,uuid)
<= (uuid,uuid)
>= (uuid,uuid)
uuid_minmax_multi_ops= (uuid,uuid)
< (uuid,uuid)
> (uuid,uuid)
<= (uuid,uuid)
>= (uuid,uuid)
varbit_minmax_ops= (varbit,varbit)
< (varbit,varbit)
> (varbit,varbit)
<= (varbit,varbit)
>= (varbit,varbit)

65.5.2.1. Parámetros de las clases de operadores #

Algunas de las clases de operadores integradas permiten especificar parámetros que afectan al comportamiento de la clase de operadores. Cada clase de operadores tiene su propio conjunto de parámetros permitidos. Solo las clases de operadores bloom y minmax-multi permiten especificar parámetros:

Las clases de operadores bloom aceptan estos parámetros:

n_distinct_per_range

Define el número estimado de valores distintos no nulos en el rango de bloques, utilizado por los índices bloom de BRIN para dimensionar el filtro de Bloom. Se comporta de forma similar a la opción n_distinct para ALTER TABLE. Cuando se establece en un valor positivo, se asume que cada rango de bloques contiene este número de valores distintos no nulos. Cuando se establece en un valor negativo, que debe ser mayor o igual que -1, se asume que el número de valores distintos no nulos crece linealmente con el número máximo posible de tuplas en el rango de bloques (alrededor de 290 filas por bloque). El valor por defecto es -0.1, y el número mínimo de valores distintos no nulos es 16.

false_positive_rate

Define la tasa de falsos positivos deseada utilizada por los índices bloom de BRIN para dimensionar el filtro de Bloom. Los valores deben estar entre 0.0001 y 0.25. El valor por defecto es 0.01, que representa una tasa de falsos positivos del 1%.

Las clases de operadores minmax-multi aceptan estos parámetros:

values_per_range

Define el número máximo de valores almacenados por los índices minmax de BRIN para resumir un rango de bloques. Cada valor puede representar un punto o un límite de un intervalo. Los valores deben estar entre 8 y 256, y el valor por defecto es 32.

65.5.3. Extensibilidad #

La interfaz de BRIN tiene un alto nivel de abstracción, lo que requiere que el implementador del método de acceso solo implemente la semántica del tipo de datos al que se accede. La propia capa de BRIN se encarga de la concurrencia, el registro y la búsqueda en la estructura del índice.

Todo lo que se necesita para poner en funcionamiento un método de acceso BRIN es implementar unos pocos métodos definidos por el usuario, que definen el comportamiento de los valores de resumen almacenados en el índice y la forma en que interactúan con las claves de escaneo (scan keys). En resumen, BRIN combina extensibilidad con generalidad, reutilización de código y una interfaz limpia.

Hay cuatro métodos que debe proporcionar una clase de operadores para BRIN:

BrinOpcInfo *opcInfo(Oid type_oid)

Devuelve información interna sobre los datos de resumen de las columnas indexadas. El valor de retorno debe apuntar a un BrinOpcInfo asignado mediante palloc, el cual tiene esta definición:

typedef struct BrinOpcInfo
{
    /* Number of columns stored in an index column of this opclass */
    uint16      oi_nstored;

    /* Opaque pointer for the opclass' private use */
    void       *oi_opaque;

    /* Type cache entries of the stored columns */
    TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER];
} BrinOpcInfo;

Las rutinas de la clase de operadores pueden utilizar BrinOpcInfo.oi_opaque para pasar información entre las funciones de soporte durante el escaneo de un índice.

bool consistent(BrinDesc *bdesc, BrinValues *column, ScanKey *keys, int nkeys)

Devuelve si todas las entradas de ScanKey son consistentes con los valores indexados dados para un rango. El número de atributo a utilizar se pasa como parte de la clave de escaneo. Se pueden pasar múltiples claves de escaneo para el mismo atributo a la vez; el número de entradas viene determinado por el parámetro nkeys.

bool consistent(BrinDesc *bdesc, BrinValues *column, ScanKey key)

Devuelve si el ScanKey es consistente con los valores indexados dados para un rango. El número de atributo a utilizar se pasa como parte de la clave de escaneo. Esta es una variante antigua de la función consistent, mantenida por compatibilidad hacia atrás.

bool addValue(BrinDesc *bdesc, BrinValues *column, Datum newval, bool isnull)

Dada una tupla de índice y un valor indexado, modifica el atributo indicado de la tupla para que represente adicionalmente el nuevo valor. Si se realizó alguna modificación en la tupla, se devuelve true.

bool unionTuples(BrinDesc *bdesc, BrinValues *a, BrinValues *b)

Consolida dos tuplas de índice. Dadas dos tuplas de índice, modifica el atributo indicado de la primera de ellas para que represente ambas tuplas. La segunda tupla no se modifica.

Una clase de operadores para BRIN puede especificar opcionalmente el siguiente método:

void options(local_relopts *relopts)

Define un conjunto de parámetros visibles para el usuario que controlan el comportamiento de la clase de operadores.

A la función options se le pasa un puntero a una estructura local_relopts, que debe llenarse con un conjunto de opciones específicas de la clase de operadores. Se puede acceder a las opciones desde otras funciones de soporte utilizando las macros PG_HAS_OPCLASS_OPTIONS() y PG_GET_OPCLASS_OPTIONS().

Dado que tanto la extracción de claves de los valores indexados como la representación de la clave en BRIN son flexibles, estas pueden depender de parámetros especificados por el usuario.

La distribución principal incluye soporte para cuatro tipos de clases de operadores: minmax, minmax-multi, inclusion y bloom. Las definiciones de clases de operadores que las utilizan se distribuyen para los tipos de datos integrados según corresponda. El usuario puede definir clases de operadores adicionales para otros tipos de datos utilizando definiciones equivalentes, sin tener que escribir ningún código fuente; es suficiente con declarar las entradas correspondientes en el catálogo. Ten en cuenta que las suposiciones sobre la semántica de las estrategias de los operadores están integradas en el código fuente de las funciones de soporte.

También son posibles clases de operadores que implementen semánticas completamente diferentes, siempre que se escriban las implementaciones de las cuatro funciones de soporte principales descritas anteriormente. Ten en cuenta que no se garantiza la compatibilidad hacia atrás entre versiones principales: por ejemplo, en versiones posteriores podrían requerirse funciones de soporte adicionales.

Para escribir una clase de operadores para un tipo de datos que implementa un conjunto totalmente ordenado, es posible utilizar las funciones de soporte de minmax junto con los operadores correspondientes, como se muestra en Table 65.5. Todos los miembros de la clase de operadores (funciones y operadores) son obligatorios.

Table 65.5. Números de función y soporte para clases de operadores Minmax

Miembro de la clase de operadoresObjeto
Función de soporte 1función interna brin_minmax_opcinfo()
Función de soporte 2función interna brin_minmax_add_value()
Función de soporte 3función interna brin_minmax_consistent()
Función de soporte 4función interna brin_minmax_union()
Estrategia de operador 1operador menor que
Estrategia de operador 2operador menor o igual que
Estrategia de operador 3operador igual a
Estrategia de operador 4operador mayor o igual que
Estrategia de operador 5operador mayor que

Para escribir una clase de operadores para un tipo de datos complejo que tiene valores incluidos dentro de otro tipo, es posible utilizar las funciones de soporte de inclusión junto con los operadores correspondientes, como se muestra en Table 65.6. Requiere solo una función adicional, que se puede escribir en cualquier lenguaje. Se pueden definir más funciones para obtener funcionalidades adicionales. Todos los operadores son opcionales. Algunos operadores requieren otros operadores, como se muestra en las dependencias de la tabla.

Table 65.6. Números de función y soporte para clases de operadores de inclusión

Miembro de la clase de operadoresObjetoDependencia
Función de soporte 1función interna brin_inclusion_opcinfo() 
Función de soporte 2función interna brin_inclusion_add_value() 
Función de soporte 3función interna brin_inclusion_consistent() 
Función de soporte 4función interna brin_inclusion_union() 
Función de soporte 11función para fusionar dos elementos 
Función de soporte 12función opcional para comprobar si dos elementos son fusionables 
Función de soporte 13función opcional para comprobar si un elemento está contenido dentro de otro 
Función de soporte 14función opcional para comprobar si un elemento está vacío 
Estrategia de operador 1operador a la izquierda deEstrategia de operador 4
Estrategia de operador 2operador no se extiende a la derecha deEstrategia de operador 5
Estrategia de operador 3operador se solapa con (overlaps) 
Estrategia de operador 4operador no se extiende a la izquierda deEstrategia de operador 1
Estrategia de operador 5operador a la derecha deEstrategia de operador 2
Estrategia de operador 6, 18operador idéntico a o igual aEstrategia de operador 7
Estrategia de operador 7, 16, 24, 25operador contiene o igual a 
Estrategia de operador 8, 26, 27operador está contenido por o igual aEstrategia de operador 3
Estrategia de operador 9operador no se extiende por encima deEstrategia de operador 11
Estrategia de operador 10operador está por debajo deEstrategia de operador 12
Estrategia de operador 11operador está por encima deEstrategia de operador 9
Estrategia de operador 12operador no se extiende por debajo deEstrategia de operador 10
Estrategia de operador 20operador menor queEstrategia de operador 5
Estrategia de operador 21operador menor o igual queEstrategia de operador 5
Estrategia de operador 22operador mayor queEstrategia de operador 1
Estrategia de operador 23operador mayor o igual queEstrategia de operador 1

Los números de las funciones de soporte del 1 al 10 están reservados para las funciones internas de BRIN, por lo que las funciones a nivel de SQL comienzan con el número 11. La función de soporte número 11 es la función principal requerida para construir el índice. Debe aceptar dos argumentos con el mismo tipo de datos que la clase de operadores y devolver la unión de ellos. La clase de operadores de inclusión puede almacenar valores de unión con diferentes tipos de datos si se define con el parámetro STORAGE. El valor de retorno de la función de unión debe coincidir con el tipo de datos de STORAGE.

Los números de función de soporte 12 y 14 se proporcionan para dar soporte a irregularidades de los tipos de datos integrados. La función número 12 se utiliza para dar soporte a direcciones de red de diferentes familias que no son fusionables. La función número 14 se utiliza para dar soporte a rangos vacíos. La función número 13 es una función opcional pero recomendada, que permite comprobar el nuevo valor antes de pasarlo a la función de unión. Como el framework BRIN puede omitir algunas operaciones cuando la unión no cambia, el uso de esta función puede mejorar el rendimiento del índice.

Para escribir una clase de operadores para un tipo de datos que implementa solo un operador de igualdad y soporta hashing, es posible utilizar los procedimientos de soporte de bloom junto con los operadores correspondientes, como se muestra en Table 65.7. Todos los miembros de la clase de operadores (procedimientos y operadores) son obligatorios.

Table 65.7. Procedimiento y números de soporte para clases de operadores Bloom

Miembro de la clase de operadoresObjeto
Procedimiento de soporte 1función interna brin_bloom_opcinfo()
Procedimiento de soporte 2función interna brin_bloom_add_value()
Procedimiento de soporte 3función interna brin_bloom_consistent()
Procedimiento de soporte 4función interna brin_bloom_union()
Procedimiento de soporte 5función interna brin_bloom_options()
Procedimiento de soporte 11función para calcular el hash de un elemento
Estrategia de operador 1operador igual a

Los números de procedimientos de soporte 1-10 están reservados para las funciones internas de BRIN, por lo que las funciones a nivel de SQL comienzan con el número 11. La función de soporte número 11 es la función principal requerida para construir el índice. Debe aceptar un argumento con el mismo tipo de datos que la clase de operadores y devolver un hash del valor.

La clase de operadores minmax-multi también está pensada para tipos de datos que implementan un conjunto totalmente ordenado, y puede verse como una extensión simple de la clase de operadores minmax. Mientras que la clase de operadores minmax resume los valores de cada rango de bloques en un único intervalo contiguo, minmax-multi permite resumirlos en múltiples intervalos más pequeños para mejorar la gestión de los valores atípicos (outliers). Es posible utilizar los procedimientos de soporte de minmax-multi junto con los operadores correspondientes, como se muestra en Table 65.8. Todos los miembros de la clase de operadores (procedimientos y operadores) son obligatorios.

Table 65.8. Procedimiento y números de soporte para clases de operadores minmax-multi

Miembro de la clase de operadoresObjeto
Procedimiento de soporte 1función interna brin_minmax_multi_opcinfo()
Procedimiento de soporte 2función interna brin_minmax_multi_add_value()
Procedimiento de soporte 3función interna brin_minmax_multi_consistent()
Procedimiento de soporte 4función interna brin_minmax_multi_union()
Procedimiento de soporte 5función interna brin_minmax_multi_options()
Procedimiento de soporte 11función para calcular la distancia entre dos valores (longitud de un rango)
Estrategia de operador 1operador menor que
Estrategia de operador 2operador menor o igual que
Estrategia de operador 3operador igual a
Estrategia de operador 4operador mayor o igual que
Estrategia de operador 5operador mayor que

Tanto la clase de operadores minmax como la de inclusión soportan operadores entre distintos tipos de datos, aunque con estos las dependencias se vuelven más complicadas. La clase de operadores minmax requiere que se defina un conjunto completo de operadores en los que ambos argumentos tengan el mismo tipo de datos. Permite soportar tipos de datos adicionales definiendo conjuntos adicionales de operadores. Las estrategias de operador de la clase de operadores de inclusión dependen de otra estrategia de operador, como se muestra en Table 65.6, o de la misma estrategia de operador que ellas mismas. Requieren que el operador de dependencia se defina con el tipo de datos STORAGE como el argumento del lado izquierdo y el otro tipo de datos soportado como el argumento del lado derecho del operador soportado. Consulta float4_minmax_ops como un ejemplo de minmax, y box_inclusion_ops como un ejemplo de inclusión.