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.
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.
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 => .
Además, si se asigna un OID, se puede crear una macro de C para ese OID escribiendo un campo
de metadatos nnnnoid_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.
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( a la definición de la columna,
donde lookuprule)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).
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 =>
en la entrada del tipo escalar en nnnnpg_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( en valor)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.
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(. El archivo de datos solo necesita
ajustarse añadiendo el campo en las filas existentes donde se necesite un valor diferente al por defecto.
valor)
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):
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);
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';
+ }
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.
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);
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