68.2. Datos iniciales de los catálogos del sistema #

68.2.1. Formato de los archivos de datos
68.2.2. Asignación de OID
68.2.3. Búsqueda de referencias de OID
68.2.4. Creación automática de tipos de array
68.2.5. Recetas para editar archivos de datos

Cada catálogo que tiene datos iniciales creados manualmente (algunos no los tienen) tiene un archivo .dat correspondiente que contiene sus datos iniciales en un formato editable.

68.2.1. Formato de los archivos de datos #

Cada archivo .dat contiene literales de estructura de datos de Perl que simplemente se evalúan para producir una estructura de datos en memoria que consta de un array de referencias a hash, uno por cada fila del catálogo. Un extracto ligeramente modificado de pg_database.dat demostrará las características clave:

[

# Aquí podría aparecer un comentario.
{ oid => '1', oid_symbol => 'Template1DbOid',
  descr => 'database\'s default template',
  datname => 'template1', encoding => 'ENCODING',
  datlocprovider => 'LOCALE_PROVIDER', datistemplate => 't',
  datallowconn => 't', dathasloginevt => 'f', datconnlimit => '-1', datfrozenxid => '0',
  datminmxid => '1', dattablespace => 'pg_default', datcollate => 'LC_COLLATE',
  datctype => 'LC_CTYPE', datlocale => 'DATLOCALE', datacl => '_null_' },

]

Puntos a tener en cuenta:

  • El diseño general del archivo es: corchete de apertura, uno o más conjuntos de llaves, cada uno de los cuales representa una fila del catálogo, y corchete de cierre. Escribe una coma después de cada llave de cierre.

  • Dentro de cada fila del catálogo, escribe pares de clave => valor separados por comas. Las claves (keys) permitidas son los nombres de las columnas del catálogo, además de las claves de metadatos oid, oid_symbol, array_type_oid y descr. (El uso de oid y oid_symbol se describe en Section 68.2.2 más abajo, mientras que array_type_oid se describe en Section 68.2.4. descr proporciona una cadena de descripción para el objeto, que se insertará en pg_description o pg_shdescription según corresponda). Aunque las claves de metadatos son opcionales, las columnas definidas del catálogo deben proporcionarse todas, excepto cuando el archivo .h del catálogo especifica un valor por defecto para la columna. (En el ejemplo anterior, el campo datdba se ha omitido porque pg_database.h proporciona un valor por defecto adecuado para él).

  • Todos los valores deben estar entre comillas simples. Escapa las comillas simples utilizadas dentro de un valor con una barra diagonal inversa (backslash). Las barras diagonales inversas destinadas a ser datos pueden, pero no es obligatorio, duplicarse; esto sigue las reglas de Perl para literales simples entre comillas. Ten en cuenta que las barras diagonales inversas que aparezcan como datos serán tratadas como escapes por el escáner de bootstrap, de acuerdo con las mismas reglas que para las constantes de cadena de escape (ver Section 4.1.2.2); por ejemplo, \t se convierte en un carácter de tabulación. Si realmente deseas una barra diagonal inversa en el valor final, deberás escribir cuatro de ellas: Perl elimina dos, dejando \\ para que lo vea el escáner de bootstrap.

  • Los valores nulos se representan por _null_. (Ten en cuenta que no hay forma de crear un valor que sea simplemente esa cadena).

  • Los comentarios van precedidos por #, y deben estar en sus propias líneas.

  • Los valores de los campos que son OID de otras entradas del catálogo deben representarse por nombres simbólicos en lugar de OID numéricos reales. (En el ejemplo anterior, dattablespace contiene dicha referencia). Esto se describe en Section 68.2.3 más abajo.

  • Dado que los hashes son estructuras de datos desordenadas, el orden de los campos y la disposición de las líneas no son significativos desde el punto de vista semántico. Sin embargo, para mantener una apariencia consistente, establecemos algunas reglas que son aplicadas por el script de formateo reformat_dat_file.pl:

    • Dentro de cada par de llaves, los campos de metadatos oid, oid_symbol, array_type_oid y descr (si están presentes) van primero, en ese orden, y luego aparecen los campos propios del catálogo en su orden definido.

    • Se insertan saltos de línea entre los campos según sea necesario para limitar la longitud de la línea a 80 caracteres, si es posible. También se inserta un salto de línea entre los campos de metadatos y los campos regulares.

    • Si el archivo .h del catálogo especifica un valor por defecto para una columna, y una entrada de datos tiene ese mismo valor, reformat_dat_file.pl lo omitirá del archivo de datos. Esto mantiene compacta la representación de los datos.

    • reformat_dat_file.pl preserva las líneas en blanco y las líneas de comentarios tal cual.

    Se recomienda ejecutar reformat_dat_file.pl antes de enviar parches de datos de catálogo. Para mayor comodidad, puedes simplemente cambiar al directorio src/include/catalog/ y ejecutar make reformat-dat-files.

  • Si deseas añadir un nuevo método para reducir el tamaño de la representación de los datos, debes implementarlo en reformat_dat_file.pl y también enseñar a Catalog::ParseData() cómo expandir los datos de nuevo a la representación completa.

68.2.2. Asignación de OID #

A una fila del catálogo que aparece en los datos iniciales se le puede asignar manualmente un OID escribiendo un campo de metadatos oid => nnnn. Además, si se asigna un OID, se puede crear una macro de C para ese OID escribiendo un campo de metadatos oid_symbol => nombre.

Las filas de catálogo precargadas deben tener OID preasignados si existen referencias de OID hacia ellas en otras filas precargadas. También se necesita un OID preasignado si el OID de la fila debe ser referenciado desde el código C. Si no se aplica ninguno de los casos, se puede omitir el campo de metadatos oid, en cuyo caso el código de bootstrap asigna un OID automáticamente. En la práctica, solemos preasignar OID para todas o ninguna de las filas precargadas en un catálogo determinado, incluso si solo algunas de ellas están realmente referenciadas de forma cruzada.

Escribir el valor numérico real de cualquier OID en el código C se considera una práctica muy mala; en su lugar, utiliza siempre una macro. Las referencias directas a los OID de pg_proc son lo suficientemente comunes como para que exista un mecanismo especial para crear las macros necesarias de forma automática; consulta src/backend/utils/Gen_fmgrtab.pl. De manera similar — pero, por razones históricas, no se hace de la misma manera — existe un método automático para crear macros para los OID de pg_type. Por lo tanto, las entradas de oid_symbol no son necesarias en esos dos catálogos. Asimismo, las macros para los OID de pg_class de los catálogos del sistema y los índices se configuran automáticamente. Para todos los demás catálogos del sistema, tienes que especificar manualmente cualquier macro que necesites a través de entradas oid_symbol.

Para encontrar un OID disponible para una nueva fila precargada, ejecuta el script src/include/catalog/unused_oids. Este imprime rangos inclusivos de OID no utilizados (por ejemplo, la línea de salida 45-900 significa que los OID del 45 al 900 no se han asignado aún). Actualmente, los OID del 1 al 9999 están reservados para la asignación manual; el script unused_oids simplemente busca a través de las cabeceras de los catálogos y los archivos .dat para ver cuáles no aparecen. También puedes usar el script duplicate_oids para comprobar si hay errores. (genbki.pl asignará OID a cualquier fila que no haya obtenido uno manualmente, y también detectará OID duplicados en tiempo de compilación).

Al elegir OID para un parche que no se espera que se integre (commit) inmediatamente, la mejor práctica es utilizar un grupo de OID más o menos consecutivos que comiencen con alguna elección aleatoria en el rango 8000—9999. Esto minimiza el riesgo de colisiones de OID con otros parches que se estén desarrollando simultáneamente. Para mantener el rango 8000—9999 libre para fines de desarrollo, después de que un parche haya sido integrado en el repositorio git principal, sus OID deben renombrarse en el espacio disponible por debajo de ese rango. Típicamente, esto se hará cerca del final de cada ciclo de desarrollo, moviendo todos los OID consumidos por los parches integrados en ese ciclo al mismo tiempo. El script renumber_oids.pl se puede utilizar para este propósito. Si se detecta que un parche no integrado tiene conflictos de OID con algún parche integrado recientemente, renumber_oids.pl también puede ser útil para recuperarse de esa situación.

Debido a esta convención de posiblemente renombrar los OID asignados por los parches, los OID asignados por un parche no deben considerarse estables hasta que el parche se haya incluido en una versión oficial. Sin embargo, no cambiamos los OID de objetos asignados manualmente una vez publicados, ya que eso crearía diversos problemas de compatibilidad.

Si genbki.pl necesita asignar un OID a una entrada de catálogo que no tiene un OID asignado manualmente, utilizará un valor en el rango 10000—11999. El contador de OID del servidor se establece en 10000 al inicio de una ejecución de bootstrap, de modo que cualquier objeto creado sobre la marcha durante el procesamiento de bootstrap también reciba OID en este rango. (El mecanismo habitual de asignación de OID se encarga de evitar cualquier conflicto).

Los objetos con OID por debajo de FirstUnpinnedObjectId (12000) se consideran «anclados» (pinned), lo que evita que se eliminen. (Hay un pequeño número de excepciones, que están codificadas directamente en IsPinnedObject()). initdb fuerza al contador de OID a subir hasta FirstUnpinnedObjectId tan pronto como está listo para crear objetos no anclados. Por lo tanto, los objetos creados durante las fases posteriores de initdb, como los creados al ejecutar el script information_schema.sql, no estarán anclados, mientras que todos los objetos conocidos por genbki.pl sí lo estarán.

Los OID asignados durante el funcionamiento normal de la base de datos están restringidos a ser 16384 o superiores. Esto asegura que el rango 10000—16383 esté libre para los OID asignados automáticamente por genbki.pl o durante initdb. Estos OID asignados automáticamente no se consideran estables y pueden cambiar de una instalación a otra.

68.2.3. Búsqueda de referencias de OID #

En principio, las referencias cruzadas de una fila inicial del catálogo a otra podrían escribirse simplemente escribiendo el OID preasignado de la fila referenciada en el campo remitente. Sin embargo, eso va en contra de la política del proyecto, porque es propenso a errores, difícil de leer y está sujeto a romperse si se renombra un OID recién asignado. Por lo tanto, genbki.pl proporciona mecanismos para escribir referencias simbólicas en su lugar. Las reglas son las siguientes:

  • El uso de referencias simbólicas se habilita en una columna de catálogo específica adjuntando BKI_LOOKUP(lookuprule) a la definición de la columna, donde lookuprule es el nombre del catálogo referenciado, por ejemplo, pg_proc. BKI_LOOKUP se puede adjuntar a columnas de tipo Oid, regproc, oidvector o Oid[]; en los dos últimos casos implica realizar una búsqueda en cada elemento del array.

  • También se permite adjuntar BKI_LOOKUP(encoding) a las columnas enteras para hacer referencia a las codificaciones de conjuntos de caracteres, que actualmente no están representadas como OID del catálogo, pero tienen un conjunto de valores conocidos por genbki.pl.

  • En algunas columnas del catálogo, se permite que las entradas sean cero en lugar de una referencia válida. Si se permite esto, escribe BKI_LOOKUP_OPT en lugar de BKI_LOOKUP. Entonces podrás escribir 0 para una entrada. (Si la columna se declara como regproc, opcionalmente puedes escribir - en lugar de 0). Excepto por este caso especial, todas las entradas en una columna BKI_LOOKUP deben ser referencias simbólicas. genbki.pl advertirá sobre nombres no reconocidos.

  • La mayoría de los tipos de objetos del catálogo se referencian simplemente por sus nombres. Ten en cuenta que los nombres de tipo deben coincidir exactamente con el campo typname de la entrada pg_type referenciada; no puedes utilizar ningún alias como integer para int4.

  • Una función puede representarse por su proname, si este es único entre las entradas de pg_proc.dat (esto funciona como la entrada de regproc). De lo contrario, escríbela como proname(argtypename,argtypename,...), al igual que regprocedure. Los nombres de tipo de argumento deben escribirse exactamente como aparecen en el campo proargtypes de la entrada en pg_proc.dat. No insertes espacios.

  • Los operadores se representan por oprname(lefttype,righttype), escribiendo los nombres de tipo exactamente como aparecen en los campos oprleft y oprright de la entrada en pg_operator.dat. (Escribe 0 para el operando omitido de un operador unario).

  • Los nombres de las clases de operadores (opclasses) y familias de operadores (opfamilies) solo son únicos dentro de un método de acceso, por lo que se representan por access_method_name/object_name.

  • En ninguno de estos casos se contempla la calificación por esquema; se espera que todos los objetos creados durante el bootstrap estén en el esquema pg_catalog.

genbki.pl resuelve todas las referencias simbólicas mientras se ejecuta, y coloca OID numéricos simples en el archivo BKI emitido. Por lo tanto, no es necesario que el backend de bootstrap lidie con referencias simbólicas.

Es conveniente marcar las columnas de referencia de OID con BKI_LOOKUP o BKI_LOOKUP_OPT incluso si el catálogo no tiene datos iniciales que requieran búsqueda. Esto permite a genbki.pl registrar las relaciones de clave foránea que existen en los catálogos del sistema. Esa información se utiliza en las pruebas de regresión para comprobar si hay entradas incorrectas. Consulta también las macros DECLARE_FOREIGN_KEY, DECLARE_FOREIGN_KEY_OPT, DECLARE_ARRAY_FOREIGN_KEY y DECLARE_ARRAY_FOREIGN_KEY_OPT, que se utilizan para declarar relaciones de clave foránea que son demasiado complejas para BKI_LOOKUP (típicamente, claves foráneas de múltiples columnas).

68.2.4. Creación automática de tipos de array #

La mayoría de los tipos de datos escalares deben tener un tipo de array correspondiente (es decir, un tipo de array varlena estándar cuyo tipo de elemento es el tipo escalar, y que está referenciado por el campo typarray de la entrada del tipo escalar en pg_type). genbki.pl puede generar automáticamente la entrada en pg_type para el tipo de array en la mayoría de los casos.

Para utilizar esta funcionalidad, simplemente escribe un campo de metadatos array_type_oid => nnnn en la entrada del tipo escalar en pg_type, especificando el OID a utilizar para el tipo de array. A continuación, puedes omitir el campo typarray, ya que se completará automáticamente con ese OID.

El nombre del tipo de array generado es el nombre del tipo escalar con un guion bajo antecedido. Los otros campos de la entrada de array se completan a partir de las anotaciones BKI_ARRAY_DEFAULT(valor) en pg_type.h, o si no existe una, se copian del tipo escalar. (También hay un caso especial para typalign). A continuación, los campos typelem y typarray de las dos entradas se configuran para referenciarse mutuamente.

68.2.5. Recetas para editar archivos de datos #

Aquí tienes algunas sugerencias sobre las formas más sencillas de realizar tareas comunes al actualizar archivos de datos de catálogos.

Añadir una nueva columna con un valor por defecto a un catálogo:  Añade la columna al archivo de cabecera con una anotación BKI_DEFAULT(valor). El archivo de datos solo necesita ajustarse añadiendo el campo en las filas existentes donde se necesite un valor diferente al por defecto.

Añadir un valor por defecto a una columna existente que no lo tiene:  Añade una anotación BKI_DEFAULT al archivo de cabecera, y a continuación ejecuta make reformat-dat-files para eliminar las entradas de campos ahora redundantes.

Eliminar una columna, tenga un valor por defecto o no:  Elimina la columna de la cabecera, y a continuación ejecuta make reformat-dat-files para eliminar las entradas de campos ahora inútiles.

Cambiar o eliminar un valor por defecto existente:  No puedes cambiar simplemente el archivo de cabecera, ya que eso haría que los datos actuales se interpreten incorrectamente. Primero ejecuta make expand-dat-files para volver a escribir los archivos de datos con todos los valores por defecto insertados explícitamente, a continuación cambia o elimina la anotación BKI_DEFAULT, y luego ejecuta make reformat-dat-files para eliminar los campos superfluos de nuevo.

Edición masiva ad-hoc:  reformat_dat_file.pl se puede adaptar para realizar muchos tipos de cambios masivos. Busca sus comentarios de bloque que muestran dónde se puede insertar código de una sola vez. En el siguiente ejemplo, vamos a consolidar dos campos booleanos en pg_proc en un solo campo de caracteres (char):

  1. Añade la nueva columna, con un valor por defecto, a pg_proc.h:

    +    /* ver las categorías PROKIND_ más abajo */
    +    char        prokind BKI_DEFAULT(f);
    

  2. Crea un nuevo script basado en reformat_dat_file.pl para insertar los valores adecuados sobre la marcha:

    -           # En este punto tenemos la fila completa en memoria como un hash
    -           # y podemos hacer las operaciones que queramos. Como está escrito, solo
    -           # elimina los valores por defecto, pero este script se puede adaptar para
    -           # hacer ediciones masivas de una sola vez.
    +           # Cambio de una sola vez para migrar a prokind
    +           # El valor por defecto ya se ha completado para este momento, así que cambia a otros
    +           # valores según corresponda
    +           if ($values{proisagg} eq 't')
    +           {
    +               $values{prokind} = 'a';
    +           }
    +           elsif ($values{proiswindow} eq 't')
    +           {
    +               $values{prokind} = 'w';
    +           }
    

  3. Ejecuta el nuevo script:

    $ cd src/include/catalog
    $ perl  rewrite_dat_with_prokind.pl  pg_proc.dat
    

    En este punto, pg_proc.dat tiene las tres columnas: prokind, proisagg y proiswindow, aunque aparecerán solo en las filas donde tengan valores distintos a los por defecto.

  4. Elimina las columnas antiguas de pg_proc.h:

    -    /* ¿es una agregación? */
    -    bool        proisagg BKI_DEFAULT(f);
    -
    -    /* ¿es una función de ventana? */
    -    bool        proiswindow BKI_DEFAULT(f);
    

  5. Finalmente, ejecuta make reformat-dat-files para eliminar las entradas antiguas e inútiles de pg_proc.dat.

Para ver más ejemplos de scripts utilizados para la edición masiva, consulta convert_oid2name.pl y remove_pg_type_oid_symbols.pl adjuntos a este mensaje: https://www.postgresql.org/message-id/CAJVSVGVX8gXnPm+Xa=DxR7kFYprcQ1tNcCT5D0O3ShfnM6jehA@mail.gmail.com