Usuarios y seguridad en Firebird 3 (1)

2 comentarios

Como este es un tema largo no lo podremos tratar en un solo artículo de este blog, por eso habrá otros artículos relacionados.

Crear usuarios para las bases de datos y determinar lo que esos usuarios pueden hacer y no pueden hacer, es una tarea básica de administración.

Hasta Firebird 2.5.9 la tarea de administración de usuarios estaba centralizada.

¿Qué significa eso?

Que los datos de los usuarios (sus nombres y los hash de sus contraseñas) se guardan en una «Base de Datos de seguridad», utilizada sólo para eso, y allí se buscan cuando alguien quiere conectarse a una Base de Datos normal.

El nombre de esa «Base de Datos de seguridad», en Firebird 2.x es: security2.fdb

¿Y qué significa que se guarda el hash de la contraseña?

Que en la Base de Datos security2.fdb nunca se guarda la contraseña del usuario sino un número que se obtiene al realizar algunas operaciones con esa contraseña.

Veamos un ejemplo muy simple para entender esto. Supongamos que la contraseña de un usuario es «SUPERMAN», y que el algoritmo para obtener el hash consiste en multiplicar el código ASCII de un carácter por la posición que ocupa dentro de la contraseña, empezando por 1, y sumando todos esos resultados parciales.

S = 83  resultado1 = 83 * 1 =  83
U = 85  resultado2 = 85 * 2 = 170
P = 80  resultado3 = 80 * 3 = 240
E = 69  resultado4 = 69 * 4 = 276
R = 82  resultado5 = 82 * 5 = 410
M = 77  resultado6 = 77 * 6 = 462
A = 65  resultado7 = 65 * 7 = 455
N = 78  resultado8 = 78 * 8 = 624

El hash de SUPERMAN, usando ese algoritmo será: 83 + 170 + 240 + 276 + 410 + 462 + 455 + 624 = 2720

Y en la Base de Datos llamada security2.fdb se guarda el número 2720, no la palabra SUPERMAN. Entonces, cuando el usuario quiere conectarse a una Base de Datos normal introduce su contraseña y el Servidor del Firebird calcula el hash de lo que escribió y lo compara con el hash guardado en security2.fdb. Si es 2720 entonces se le permite la conexión a la Base de Datos. Si el resultado es cualquier otro número, se lo rechaza.

Desde luego que el algoritmo usado por Firebird no es el mostrado arriba, es un algoritmo mucho más complicado. Lo de arriba fue un ejemplo muy sencillo para ilustrar la idea.

La gran ventaja de usar un hash es que la palabra SUPERMAN no la encontrarás jamás dentro de security2.fdb por la sencilla razón de que esa palabra jamás se guardó ahí, lo que sí se guardó fue su hash.

¿Te has dado cuenta que cuando escribes una contraseña/password debes escribir exactamente las mayúsculas y las minúsculas?

Eso es debido a que el código ASCII de una letra mayúscula es diferente al código de esa misma letra en minúscula. Por ejemplo, el código ASCII de la letra «A» es 65 pero el código ASCII de la letra «a» es 97. Al ser números diferentes los hash que se obtengan también serán diferentes, por supuesto.

¿Qué significa que la administración de usuarios está centralizada?

Que los nombres de todos los usuarios y los hash de todos los usuarios (hay un hash para cada usuario, nunca más y nunca menos) se encuentran siempre, sí o sí, en la Base de Datos de nombre security2.fdb

La ventaja de la administración de usuarios centralizada

Cada usuario (y su hash, claro) se crea una sola vez aunque haya muchas bases de datos en el Servidor. Escribiendo su nombre y su contraseña/password podría conectarse a cualquier Base de Datos. Aunque en cada Base de Datos se le podrían restringir los accesos/permisos/derechos, pero no se le podría impedir la conexión.

Y si está conectado a una Base de Datos entonces podría crear objetos dentro de esa Base de Datos, por ejemplo escribiendo:

CREATE TABLE PRUEBA1 (MiColumna1 INTEGER);

Listo, ya creó una tabla llamada PRUEBA1

También podría crear vistas, stored procedures, triggers, etc.

Te guste o no te guste…así funciona.

La desventaja de la administración de usuarios centralizada

Si un usuario conoce el nombre de una Base de Datos y su ubicación, entonces podrá conectarse a ella. Por ejemplo, si la Base de Datos es:

D:\CONTABILIDAD\DATABASES\MARCELA.FDB

con solamente conocer eso ya podrá realizar una conexión exitosa.

Lo mismo si no conoce la ruta y el nombre de la Base de Datos pero sí conoce el alias que se le asignó en el archivo ALIASES.CONF, podrá conectarse con éxito.

Y como ya sabes, si se conectó a una Base de Datos entonces podrá crear objetos dentro de esa Base de Datos.

Resumiendo:

La administración de usuarios centralizada tiene la ventaja de que se escribe poco porque el nombre de cada usuario y su contraseña se determinan una sola vez y pueden ser usados en múltiples bases de datos.

La desventaja es que cualquier usuario podría conectarse a cualquier Base de Datos si conoce el nombre y la ubicación (o el alias) de esa Base de Datos. Y aunque con los comandos GRANT y REVOKE se podría limitar las operaciones que puede realizar, que tenga esa posibilidad de poder conectarse a la Base de Datos que se le antoje, es un riesgo a la seguridad.

Otro riesgo a la seguridad, aún mucho más grave, es que si consigue copiar la Base de Datos (en un pen-drive, por ejemplo) y luego la lleva a una computadora donde conoce la contraseña del usuario SYSDBA entonces podrá ver todo lo que se le ocurra ver. O agregar. O borrar. O modificar.

En muchas empresas, sobre todo las pequeñas y medianas, es imposible evitar que algún empleado copie en un pen-drive una Base de Datos. Y algo así, podría ser muy grave.

Afortunadamente, con Firebird 3 se aumentó mucho la seguridad, como veremos en los siguientes artículos de esa serie.

Artículos relacionados:

Usuarios y seguridad en Firebird 3 (2)

Usuarios y seguridad en Firebird 3 (3)

Usuarios y seguridad en Firebird 3 (4)

Usuarios y seguridad en Firebird 3 (5)

Usuarios y seguridad en Firebird 3 (6)

El índice del blog Firebird21

Migrando bases de datos a Firebird 3

Deja un comentario

En general lo más recomendable suele ser que si tienes una aplicación que usa bases de datos de Firebird anteriores a la versión 3 que dejes todo como está y que uses la versión 3 solamente para tus nuevos desarrollos.

Pero…por «h» o «b» motivo a veces se necesita que una Base de Datos antigua pueda ser usada por el Firebird 3.

En este artículo verás como hacerlo.

ODS (On Disk Structure)

Cada Base de Datos tiene internamente una estructura, una forma de guardar su contenido. Esa estructura va cambiando con cada versión del Firebird. Para poder saber con cual versión del Firebird fue creada una Base de Datos se guarda dentro de ella un número llamado ODS, que sirve para identificarla.

Hasta la versión 2.5.9 una versión del Firebird podía leer bases de datos creadas con versiones anteriores aunque siempre se aconsejaba que se actualizara la versión para que se le agregaran las mejoras que tenía la versión más nueva.

Con la versión 3.0 ya no sucede así. El Firebird 3 no puede leer bases de datos creadas con versiones anteriores del Firebird.

Versión del FirebirdODS
1.010.0
1.510.1
2.011.0
2.111.1
2.511.2
3.012.0
Tabla que muestra las versiones del Firebird y sus correspondientes ODS

¿Cómo se puede conocer cuál es el ODS de una Base de Datos?

Usando el programa GSTAT con la opción -h

Con el programa GSTAT.EXE y la opción -h se puede conocer el ODS de una Base de Datos

¿Cómo se migraba antes?

  • Se hacía un backup con la versión antigua
  • Se hacía el restore con la versión nueva

Por ejemplo, si una Base de Datos había sido creada con Firebird 2.1 (o sea que su ODS era 11.1) y se quería actualizarla a Firebird 2.5 (o sea que su nueva ODS pasaría a ser 11.2):

  • Se hacía un backup con el Servidor 2.1
  • Se hacía un restore con el Servidor 2.5

Y listo, eso era todo. Las nuevas características que tenía la versión 2.5 (ODS 11.2) se agregaban a la Base de Datos que tenía el ODS 11.1. la cual a partir de ese momento tendría un ODS 11.2. Y ya, se terminó la tarea, todo ok.

¿Cómo se migra para pasar a ODS 12.0?

Se complicó el tema porque Firebird 3 solamente puede leer bases de datos cuyo ODS es 12.0

No reconoce ODS anteriores. ¿Por qué no? Porque hay muchas nuevas características, y mantener la compatibilidad con las versiones anteriores haría que el código fuente fuera mucho más grande, más difícil de mantener, y también más propenso a tener errores. Entonces, se decidió que Firebird 3 solamente pueda leer ODS 12.0

Por lo tanto, ahora hay que realizar varios pasos para asegurarse de que una Base de Datos con un ODS inferior a 12.0 pueda ser convertida a ODS 12.0

  1. Verificar que la Base de Datos se encuentra en buen estado
  2. Usar un script para crear la nueva Base de Datos
  3. Si la Base de Datos usa palabras que ahora son reservadas, cambiar esas palabras por otras palabras
  4. Realizar el ciclo backup/restore

Paso 1. Verificar que la Base de Datos se encuentra en buen estado

Usando la versión actual del Firebird (por ejemplo, la 2.5.9) escribir en línea de comandos:

   GBAK -user SYSDBA -password masterkey -backup_database -verbose -garbage_collect -service service_mgr MiBaseDatos.fdb MiBackup.fbk

Si el backup falló:

1.1 Desconecta a todos los usuarios de la Base de Datos

1.2 Haz una copia de la Base de Datos en otra carpeta o en otro disco duro

1.3 Usa el programa GFIX para corregir el fallo

      GFIX -validate -full -user SYSDBA -password masterkey MiBaseDatos.fdb

Cuando el backup se haya realizado sin problemas, escribir en la línea de comandos:

     GBAK -user SYSDBA -password masterkey -create_database -verbose -service service_mgr MiBackup.fbk MiNuevaBaseDatos.fdb

Algunos comentarios: el nombre del usuario puede ser otro, la contraseña del usuario puede ser otra, las opciones pueden escribirse abreviadas si se prefiere (-v es igual a -verbose, por ejemplo), si se usa service_mgr el backup se realiza en la computadora donde se encuentra el Servidor, no en la computadora del usuario, no se debería restaurar una Base de Datos con el nombre de una Base de Datos ya existente. Solamente cuando todo esté ok se la debería renombrar.

El restore también puede fallar. Y si eso ocurre debes solucionar los problemas antes de continuar.

Paso 2. Usar un script para crear la nueva Base de Datos

Como en Firebird 3 se agregaron varias palabras reservadas podría ocurrir que palabras que antes no eran reservadas (y por lo tanto, no causaban problemas) ahora sí son reservadas y por lo tanto no puedes usarlas para darle nombre a tus tablas, triggers, índices, dominios, vistas, stored procedures, nombres de variables, etc.

La forma más rápida para detectar si existen problemas con las palabras reservadas es:

  • Extraer los metadatos en un script
  • Usar ese script para generar una Base de Datos en Firebird 3

Si hubiera algún error entonces lo sabrías y podrías corregirlo.

Paso 3. Si la Base de Datos usa palabras que ahora son reservadas, cambiar esas palabras por otras palabras

Como el script es un archivo de texto entonces con el Bloc de Notas del Windows o con cualquier otro editor de texto podrías buscar las palabras problemáticas y reemplazarlas por otras.

Paso 4. Realizar el ciclo backup/restore

Repetir los pasos del 1. al 3. hasta que todo esté ok. Cuando eso ocurra:

  • Hacer un backup con la versión anterior del Firebird
  • Hacer el restore con la versión 3 del Firebird

Conclusión:

Como el Firebird 3 solamente puede conectarse a una Base de Datos cuyo ODS es 12.0 se necesita asegurar que pueda ser convertida si se la creó con un ODS menor.

Entonces:

  1. Se hace un backup con la versión actual del Firebird (ejemplo: 2.5.9)
  2. Si se encontró algún error, se lo corrige y luego se regresa al paso 1.
  3. Se hace un restore con la versión actual del Firebird (ejemplo: 2.5.9)
  4. Si se encontró algún error, se lo corrige y luego se regresa al paso 1.
  5. Se crea un script conteniendo los metadatos solamente
  6. Con el Firebird 3 se usa ese script para crear la nueva Base de Datos
  7. Si se encontró algún error al crear la nueva Base de Datos (generalmente palabras reservadas del Firebird 3 que están siendo usadas), se anota el problema, se corrige el script y luego se regresa al paso 6.
  8. Cuando el paso 6. se haya completado exitosamente, todos los problemas encontrados y anotados en el paso 7. se usan para modificar los metadatos de la Base de Datos en la versión actual del Firebird (ejemplo: 2.5.9)
  9. Se hace un nuevo backup con la versión actual del Firebird (ejemplo: 2.5.9), el cual ya tendrá solucionados todos los problemas
  10. Se hace el restore con Firebird 3

Y con esto, tu Base de Datos creada con una versión anterior del Firebird ya estará lista para ser usada por el Firebird 3.

Artículos relacionados:

Haciendo backups con GBAK (1)

Haciendo backups con GBAK (2)

Haciendo backups con GBAK (3)

Manteniendo la Base de Datos en buen estado

¿Qué es la versión de ODS?

El índice del blog Firebird21

Arquitecturas de Firebird – Un repaso

7 comentarios

Como desde dentro de unos días empezaré a escribir varios artículos relacionados con Firebird 3 creí conveniente hacer un repaso sobre algunos puntos muy importantes, ya que es necesario entenderlos bien para poder sacarle el máximo provecho al Firebird 3. Todo lo que viene a continuación ya fue explicado en otros artículos de este blog y por lo tanto se trata sólo de un repaso de lo ya visto con anterioridad.

Arquitecturas

Existen 3 arquitecturas, ellas son:

  • Classic
  • SuperServer
  • SuperClassic

Modos de Servidor y Cliente

Existen 2 modos de tener al Servidor y al Cliente, ellos son:

  • Servidor y Cliente en archivos separados
  • Servidor y Cliente en el mismo archivo

Tipos de cachés del Firebird

Los cachés ocupan una parte de la memoria RAM del Servidor para que los accesos a los datos sean más rápidos porque buscar algo en la memoria RAM siempre es mucho más rápido que buscarlo en el disco duro. En Firebird existen 2 tipos de caché:

  • Caché de los metadatos. Aquí se guardan las estructuras de los objetos de la Base de Datos: tablas, triggers, vistas, procedimientos almacenados, etc.
  • Caché de las páginas. Las últimas páginas que fueron accedidas por el Servidor en una Base de Datos.

La cantidad de memoria RAM que ocuparán los cachés se define en el archivo FIREBIRD.CONF mediante el parámetro DefaultDbCachePages (para todas las bases de datos), o mediante el programa GFIX y la opción -buffers (para una Base de Datos en particular). En ambos casos se debe especificar la cantidad de páginas, y no la cantidad de bytes. La cantidad de bytes que tiene cada página de la Base de Datos (todas sus páginas ocupan exactamente la misma cantidad de bytes) se determina al crear la Base de Datos o al hacer un restore con el programa GBAK.

En SuperServer todas las conexiones a la Base de Datos comparten el mismo caché. O sea, hay un solo caché para todas las conexiones.

En Classic y en SuperClassic cada conexión tiene su propio caché. Y el caché de una conexión no se comparte con las demás conexiones. Es totalmente independiente.

Classic

  • Cada conexión a la Base de Datos es un proceso. Hay un caché para cada proceso. El caché de un proceso no se comparte (no es conocido) con los otros procesos. Ejemplo: si se tienen 20 conexiones, se tienen 20 procesos, y se tienen 20 cachés. Sería como tener a 20 Firebird ejecutándose al mismo tiempo.
  • Un tamaño de caché grande puede afectar mucho al rendimiento porque el Sistema Operativo al no encontrar lugar en la memoria RAM hará uso de la memoria virtual, haciendo por lo tanto frecuentes swap al disco duro.
  • El hecho de que cada proceso sea independiente de los demás procesos es una gran ventaja cuando un proceso está sobrecargando al Servidor porque se cierra el proceso problemático y listo, asunto solucionado y sin afectar a los otros procesos. También cuando una UDF (User Defined Function) causa la caída del Servidor porque al «matar» a ese proceso problemático solamente él finalizará, los demás procesos ni se enterarán.
  • Para un mejor rendimiento, se aconseja usar Classic con un Sistema Operativo de 64 bits y con un Firebird también de 64 bits.

SuperServer

  • Hay un único proceso, el cual es utilizado por todas las conexiones a la Base de Datos. Ese proceso tiene un thread (hilo) por cada conexión a la Base de Datos. Es mediante ese thread (hilo) que una conexión envía solicitudes al Servidor y recibe los resultados.
  • Hay un solo caché, el cual es compartido por todas las conexiones.
  • Las ventajas de tener un solo caché son: a) se usa menos memoria RAM, b) hay una menor carga de I/O porque las páginas no necesitan ser sincronizadas constantemente, c) si un thread leyó una página que otro thread luego también necesita leer entonces los datos ya están en el caché y no deben ser leídos nuevamente del disco duro, aumentando por consiguiente la rapidez.
  • La desventaja de tener un solo caché es que si una conexión sobrecarga al Servidor o le causa problemas, cerrar el proceso implicará que todas las conexiones se mueran.
  • Hasta el Firebird 2.5.9 el SuperServer solamente puede usar un «core» por Base de Datos, sin importar cuantos «cores» tenga la computadora del Servidor. Si hay una sola Base de Datos entonces se puede usar un solo «core», pero si hay varias bases de datos entonces cada una de ellas podría usar un «core» distinto. Esto es determinado mediante el parámetro CPUAffinityMask del archivo FIREBIRD.CONF
  • A partir del Firebird 3 una Base de Datos puede usar muchos «cores» de la computadora donde se encuentra el Servidor.

SuperClassic

Es un híbrido entre Classic y SuperServer.

  • Como en SuperServer, hay un solo proceso y cada conexión tiene un thread (hilo) para enviar las solicitudes al Servidor y recibir los resultados.
  • El caché es independiente para cada thread (hilo). O sea que el caché de un thread (hilo) no se comparte con los otros threads (hilos).
  • La sincronización entre páginas es más eficiente que con Classic porque al existir un solo proceso no se necesita estar comunicándose con otros procesos.
  • El consumo de memoria RAM por los cachés es el mismo que en Classic.
  • Como se usa un solo proceso, consume menos recursos del Servidor que Classic.
  • Se aconseja usarlo si el Sistema Operativo es de 64 bits y el Firebird instalado también es de 64 bits y la versión del Firebird es hasta la 2.5.9

Embedded

Tanto Classic como SuperClassic como SuperServer tienen al Servidor en un archivo y al Cliente en otro archivo.

Pero también se puede tener al Servidor y al Cliente en un solo archivo. Eso se llama «embedded» (o «incrustado», en español). Estas son sus características:

  • Permite distribuir a tu aplicación junto con el Firebird, sin necesidad de instalar al Firebird
  • Es por lo tanto muy útil para versiones de demostración, para aplicaciones mono-usuario, y para catálogos digitales.
  • Hasta el Firebird 2.5.9 había que renombrar al archivo fbembed.dll como fbclient.dll para tener al Servidor y al Cliente funcionando. Desde Firebird 3 eso ya no se hace porque el archivo fbclient.dll normal puede funcionar también como «incrustado».
  • Tu aplicación debe acceder a la Base de Datos solamente usando la ruta y el nombre, por ejemplo: D:\CONTABILIDAD\BASESDATOS\CONTA.FDB sin escribir «localhost» ni la IP de la computadora ni una carpeta compartida.
  • El usuario no debe escribir su nombre ni su contraseña (password) para acceder a la Base de Datos.
  • Se puede restringir el acceso a algunos de los objetos de la Base de Datos mediante los comandos GRANT y REVOKE.

¿Cuál arquitectura elegir?

Muchas veces es imposible saber de antemano cual es la mejor arquitectura para un caso en particular y mediante prueba y error se debe determinar. ¿Por qué? porque depende de las características del Servidor, del Sistema Operativo, de la cantidad de conexiones, de la aplicación. Pero podemos dar una guía la cual debe tomarse como guía, no está «escrita en piedra».

Classic

  • Si la computadora del Servidor tiene varios/muchos procesadores
  • Si habrá muchas (25+) conexiones simultáneas
  • Si se necesita el máximo de estabilidad, porque la caída de un proceso no afecta a los otros procesos

SuperClassic

  • Si la computadora del Servidor tiene varios/muchos procesadores
  • Si habrá muchas (25+) conexiones simultáneas
  • Si el Sistema Operativo es de 64 bits
  • Si el Firebird es de 64 bits y la versión es hasta la 2.5.9

Consume menos recursos que el Classic porque la sincronización del caché no requiere comunicación entre procesos. Y es que hay un único proceso.

Si se «muere» el proceso entonces todas las conexiones también se mueren.

IMPORTANTE: No se recomienda usar SuperClassic en Firebird 3

SuperServer

En Firebird hasta 2.5.9:

  • Si hay pocas (25-) conexiones simultáneas
  • Si las operaciones con la Base de Datos (INSERT, UPDATE, DELETE, SELECT, EXECUTE PROCEDURE, vistas, triggers, etc.) son rápidas y están bien optimizadas.
  • Si se requiere que varias/todas las conexiones compartan el caché

Desde Firebird 3.0:

  • Si la computadora del Servidor tiene varias CPU o varios «cores»
  • Si se desea que el caché esté compartido
  • Si las caídas del Servidor nunca (o muy raramente) ocurren, porque la aplicación está bien diseñada y la Base de Datos también está bien diseñada

IMPORTANTE: Se recomienda el uso de SuperServer con Firebird 3, en general esta es la mejor opción.

Versiones de 32 bits y de 64 bits

El Firebird tiene versiones de 32 bits y de 64 bits. ¿Cuál elegir?

  • Si el Sistema Operativo es de 32 bits, sólo se puede instalar el Firebird de 32 bits
  • Si el Sistema Operativo es de 64 bits, se puede instalar el Firebird de 32 bits o el Firebird de 64 bits, o sea: cualquiera de ellos
  • El Firebird de 64 bits consume un 30% más de memoria que el Firebird de 32 bits
  • El Firebird de 64 bits es más rápido que el Firebird de 32 bits
  • El Firebird de 32 bits puede usar un máximo de 2 Gb de memoria RAM en el Servidor. Eso significa que la suma de los bytes usados por los procesos más los bytes usados por los cachés (de metadatos y de páginas) no puede superar a los 2 Gb. Si en la memoria RAM hay más de 2 Gb (4, 6, 8, 12, 16, etc.) esos Gb adicionales son desaprovechados porque para el Firebird no existen, ya que él puede reconocer como máximo hasta 2 Gb.
  • Si la aplicación que se conecta a la Base de Datos es de 32 bits entonces, sí o sí, debe cargar al archivo fbclient.dll de 32 bits
  • Si la aplicación que se conecta a la Base de Datos es de 64 bits entonces, sí o sí, debe cargar el archivo fbclient.dll de 64 bits

Problemas posibles con las UDFs de terceros

Si en la Base de Datos se usan UDFs (User Defined Function) de terceras partes y esas UDFs fueron compiladas en 32 bits, entonces no podrán ser usadas cuando el Servidor sea de 64 bits.

Por lo tanto, o consigues una versión de esas UDFs que esté compilada con 64 bits o te verás obligado a usar el Firebird de 32 bits.

Las UDFs que se distribuyen junto con el Firebird no tienen ese problema porque siempre son compiladas con los bits que corresponden al Servidor que instalas.

Pero sí puedes tener problemas con las UDFs creadas por terceros.

Artículos relacionados:

Diferencias entre SuperServer, Classic, y SuperClassic

Entendiendo la memoria caché

Classic: optimizando el tamaño del caché

SuperServer: optimizando el tamaño del caché

Optimizando SuperServer: poniendo toda la Base de Datos en la memoria caché

Entendiendo a GSTAT (1)

El índice del blog Firebird21

Versión 3.0.7 liberada

3 comentarios

Al momento de escribir este artículo la última revisión de Firebird es la 3.0.7 y fue liberada el 20 de octubre de 2020.

Firebird sigue mejorando día a día. En esta revisión se corrigieron algunos errores y se agregaron algunas mejoras.

Puedes descargar esta revisión desde:

http://www.firebirdsql.org/en/firebird-3-0-7/

La Release Notes correspondiente (o sea, la documentación sobre los cambios y las mejoras) puedes descargar desde:

https://www.firebirdsql.org/file/documentation/release_notes/Firebird-3.0.7-ReleaseNotes.pdf

Y la guía para usar Firebird 3 puedes descargar desde:

https://www.firebirdsql.org/file/documentation/pdf/en/firebirddocs/qsg3/firebird-3-quickstartguide.pdf

Artículos relacionados:

El índice del blog Firebird21