Cada método de acceso a índices se describe mediante una fila en el catálogo del sistema
pg_am.
La entrada pg_am
especifica un nombre y una función de controlador (handler function) para el método de
acceso al índice. Estas entradas se pueden crear y eliminar mediante los comandos SQL
CREATE ACCESS METHOD y
DROP ACCESS METHOD.
La función controladora de un método de acceso a índices debe declararse para aceptar un
único argumento de tipo internal y devolver el
pseudotipo index_am_handler. El argumento es un valor ficticio que
simplemente sirve para evitar que las funciones controladoras se llamen directamente desde
comandos SQL. El resultado de la función debe ser una estructura asignada con palloc de
tipo IndexAmRoutine, que contiene todo lo
que el código central necesita saber para hacer uso del método de acceso al índice.
La estructura IndexAmRoutine, también llamada estructura de la API del método de acceso, incluye campos que especifican diversas
propiedades fijas del método de acceso, como por ejemplo si admite
índices multicolumna. Más importante aún, contiene punteros a funciones de soporte
para el método de acceso, que realizan todo el trabajo real para acceder a los
índices. Estas funciones de soporte son funciones C puras y no son
visibles ni invocables a nivel SQL. Las funciones de soporte se describen
en Section 63.2.
La estructura IndexAmRoutine se define de la siguiente manera:
typedef struct IndexAmRoutine
{
NodeTag type;
/*
* Número total de estrategias (operadores) mediante las cuales podemos recorrer/buscar
* este AM. Cero si el AM no tiene un conjunto fijo de asignaciones de estrategia.
*/
uint16 amstrategies;
/* número total de funciones de soporte que utiliza este AM */
uint16 amsupport;
/* número de función de soporte de opciones de opclass o 0 */
uint16 amoptsprocnum;
/* ¿admite el AM la ordenación mediante el valor de la columna indexada (ORDER BY)? */
bool amcanorder;
/* ¿admite el AM ORDER BY por el resultado de un operador en la columna indexada? */
bool amcanorderbyop;
/* ¿admite el AM el hashing utilizando una API coherente con el AM de hash? */
bool amcanhash;
/* ¿tienen los operadores dentro de una opfamily una semántica de igualdad coherente? */
bool amconsistentequality;
/* ¿tienen los operadores dentro de una opfamily una semántica de ordenación coherente? */
bool amconsistentordering;
/* ¿admite el AM el escaneo hacia atrás? */
bool amcanbackward;
/* ¿admite el AM índices UNIQUE? */
bool amcanunique;
/* ¿admite el AM índices multicolumna? */
bool amcanmulticol;
/* ¿requiere el AM que los escaneos tengan una restricción en la primera columna del índice? */
bool amoptionalkey;
/* ¿maneja el AM condiciones ScalarArrayOpExpr? */
bool amsearcharray;
/* ¿maneja el AM condiciones IS NULL/IS NOT NULL? */
bool amsearchnulls;
/* ¿puede el tipo de datos de almacenamiento del índice diferir del tipo de datos de la columna? */
bool amstorage;
/* ¿se puede agrupar (cluster) sobre un índice de este tipo? */
bool amclusterable;
/* ¿maneja el AM los bloqueos de predicado? */
bool ampredlocks;
/* ¿admite el AM el escaneo en paralelo? */
bool amcanparallel;
/* ¿admite el AM la construcción en paralelo? */
bool amcanbuildparallel;
/* ¿admite el AM columnas incluidas con la cláusula INCLUDE? */
bool amcaninclude;
/* ¿utiliza el AM maintenance_work_mem? */
bool amusemaintenanceworkmem;
/* ¿resume el AM las tuplas, resumiendo al menos todas las tuplas del bloque
* en un solo resumen? */
bool amsummarizing;
/* OR de los flags de vacuum en paralelo */
uint8 amparallelvacuumoptions;
/* tipo de datos almacenados en el índice, o InvalidOid si es variable */
Oid amkeytype;
/* funciones de la interfaz */
ambuild_function ambuild;
ambuildempty_function ambuildempty;
aminsert_function aminsert;
aminsertcleanup_function aminsertcleanup; /* puede ser NULL */
ambulkdelete_function ambulkdelete;
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* puede ser NULL */
amcostestimate_function amcostestimate;
amgettreeheight_function amgettreeheight; /* puede ser NULL */
amoptions_function amoptions;
amproperty_function amproperty; /* puede ser NULL */
ambuildphasename_function ambuildphasename; /* puede ser NULL */
amvalidate_function amvalidate;
amadjustmembers_function amadjustmembers; /* puede ser NULL */
ambeginscan_function ambeginscan;
amrescan_function amrescan;
amgettuple_function amgettuple; /* puede ser NULL */
amgetbitmap_function amgetbitmap; /* puede ser NULL */
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* puede ser NULL */
amrestrpos_function amrestrpos; /* puede ser NULL */
/* funciones de la interfaz para admitir escaneos de índice en paralelo */
amestimateparallelscan_function amestimateparallelscan; /* puede ser NULL */
aminitparallelscan_function aminitparallelscan; /* puede ser NULL */
amparallelrescan_function amparallelrescan; /* puede ser NULL */
/* funciones de la interfaz para admitir la planificación */
amtranslate_strategy_function amtranslatestrategy; /* puede ser NULL */
amtranslate_cmptype_function amtranslatecmptype; /* puede ser NULL */
} IndexAmRoutine;
Para ser útil, un método de acceso a índices también debe tener una o más
familias de operadores (operator families) y
clases de operadores (operator classes) definidas en
pg_opfamily,
pg_opclass,
pg_amop y
pg_amproc.
Estas entradas permiten al planificador
determinar qué tipo de cualificaciones de consulta se pueden usar con
los índices de este método de acceso. Las familias y clases de operadores se describen
en Section 36.16, que es material prerrequisito para leer
este capítulo.
Un índice individual se define mediante una entrada
pg_class
que lo describe como una relación física, más una entrada
pg_index
que muestra el contenido lógico del índice — es decir, el conjunto
de columnas de índice que tiene y la semántica de esas columnas, tal como se captura en
las clases de operadores asociadas. Las columnas del índice (valores clave) pueden ser
columnas simples de la tabla subyacente o expresiones sobre las filas de la tabla.
El método de acceso a índices normalmente no tiene interés en el origen de los
valores clave del índice (siempre se le entregan valores clave precalculados), pero
estará muy interesado en la información de la clase de operadores en
pg_index. Se puede acceder a ambas entradas de catálogo como parte de la estructura de datos Relation que se
pasa a todas las operaciones sobre el índice.
Algunos de los campos de banderas (flags) de IndexAmRoutine tienen implicaciones
que no son obvias. Los requisitos de amcanunique
se analizan en Section 63.5.
La bandera amcanmulticol afirma que el
método de acceso admite índices de múltiples columnas clave, mientras que
amoptionalkey afirma que permite escaneos
donde no se proporciona ninguna cláusula de restricción indexable para la primera columna del índice.
Cuando amcanmulticol es falso,
amoptionalkey dice esencialmente si el
método de acceso admite escaneos de índice completo sin ninguna cláusula de restricción.
Los métodos de acceso que admiten varias columnas de índice deben
admitir escaneos que omitan las restricciones en cualquiera o todas las columnas posteriores
a la primera; sin embargo, se les permite exigir que aparezca alguna restricción
para la primera columna del índice, y esto se señala estableciendo
amoptionalkey en falso.
Una razón por la que un AM de índice podría establecer
amoptionalkey en falso es si no indexa
valores nulos. Dado que la mayoría de los operadores indexables son
estrictos (strict) y, por lo tanto, no pueden devolver true para entradas nulas,
a primera vista resulta atractivo no almacenar entradas de índice para valores nulos:
nunca podrían ser devueltos por un escaneo de índice de todos modos. Sin embargo, este
argumento falla cuando un escaneo de índice no tiene una cláusula de restricción para una columna de
índice dada. En la práctica, esto significa que
los índices que tienen amoptionalkey en verdadero deben
indexar los nulos, ya que el planificador podría decidir utilizar dicho índice
sin ninguna clave de escaneo en absoluto. Una restricción relacionada es que un método de
acceso a índices que admite múltiples columnas de índice debe
admitir la indexación de valores nulos en las columnas posteriores a la primera, porque el planificador
asumirá que el índice se puede utilizar para consultas que no restringen
estas columnas. Por ejemplo, considera un índice sobre (a,b) y una consulta con
WHERE a = 4. El sistema asumirá que el índice se puede
utilizar para buscar filas con a = 4, lo cual es incorrecto si el
índice omite las filas donde b es nulo.
Sin embargo, es aceptable omitir las filas donde la primera columna indexada es nula.
Un método de acceso a índices que sí indexa nulos también puede establecer
amsearchnulls, indicando que admite cláusulas
IS NULL e IS NOT NULL como condiciones de búsqueda.
La bandera amcaninclude indica si el
método de acceso admite columnas “incluidas” (included), es decir, si puede
almacenar (sin procesar) columnas adicionales más allá de las columnas clave.
Los requisitos del párrafo anterior se aplican únicamente a las columnas
clave. En particular, la combinación
de amcanmulticol=false
y amcaninclude=true es
sensata: significa que solo puede haber una columna clave, pero también puede
haber columnas incluidas. Además, se debe permitir que las columnas incluidas sean
nulas, independientemente de amoptionalkey.
La bandera amsummarizing indica si el
método de acceso resume las tuplas indexadas, con una granularidad de resumen
de al menos por bloque.
Los métodos de acceso que no apuntan a tuplas individuales, sino a rangos de bloques
(como BRIN), pueden permitir que la optimización HOT
continúe. Esto no se aplica a los atributos a los que se hace referencia en los predicados
del índice; una actualización de dicho atributo siempre desactiva HOT.