Usando Database Comparer en línea de comandos

4 comentarios

En el artículo:

https://firebird21.wordpress.com/2013/06/10/database-comparer-de-clever-components/

ya habíamos hablado sobre un programa muy bueno para comparar bases de datos llamado Database Comparer, de la empresa Clever Components.

Pues bien, ese programa además de permitirnos interactuar con él por medio del GUI (Graphical User Interface) también nos permite ejecutarlo a través de la línea de comandos, como veremos ahora.

¿Y para qué nos serviría ejecutarlo a través de la línea de comandos?

La utilidad más normal de esta característica es que podemos automatizar el proceso en nuestra aplicación (o sea, en el archivo .EXE que nosotros creamos y que los usuarios ejecutan).

En nuestro .EXE podemos hacer que la comparación (y actualización, si es necesaria) se haga en forma automática o cuando el usuario haga clic sobre algún botón.

Veamos la situación:

Estamos desarrollando un sistema de contabilidad que será usado por muchos estudios contables. Un estudio contable normalmente lleva la contabilidad de muchos clientes. Como sabemos que no es bueno tener en una sola Base de Datos a todos los clientes del estudio contable sino que lo recomendable es que cada uno de sus clientes tenga su propia Base de Datos nos encontramos con un problema.

¿Cuál es el problema?

Que la estructura de una Base de Datos (es decir, sus metadatos) no es fija, por mucho que la hayamos analizado siempre cabe la posibilidad de que alguna vez debamos crear una tabla, o modificar una existente, agregarle índices, vistas, stored procedures, triggers, etc.

Si el estudio contable solamente tuviera uno o dos clientes sería muy sencillo. Con la GUI de Database Comparer en un ratito actualizaríamos las bases de datos y listo, a otra cosa.

Pero lo normal es que los estudios contables tengan decenas o centenas de clientes, y allí la actualización manual ya se vuelve muy impráctica, demora mucho tiempo, y existe la gran posibilidad de no actualizar todas las bases de datos o hacerlo de manera equivocada (actualizando al revés), con la consecuencia de que podrían perderse muchos datos y todos los trastornos que eso ocasionaría.

El comportamiento adecuado

Ante una situación como la antedicha, ¿qué es lo mejor?

  1. Creamos una Base de Datos vacía, modelo, que solamente tiene los metadatos. Por ejemplo la llamamos MASTER.FDB
  2. Por cada cliente del estudio contable tenemos una Base de Datos que cuando se agregó ese cliente simplemente se copió físicamente a MASTER.FDB para tener la Base de Datos del cliente. Así podríamos tener ALICIA.FDBGRACIELA.FDB, SUSANA.FDB, etc., las cuales inicialmente eran una simple copia de MASTER.FDB y después se les fueron agregando los datos que les correspondían.
  3. Cuando debemos cambiar algo en la Base de Datos, la única Base de Datos que tocamos, la única con la cual trabajamos es MASTER.FDB
  4. Cuando el usuario abre una Base de Datos, nuestro programa .EXE compara la versión de MASTER.FDB con la versión de la Base de Datos que él abrió. Por ejemplo, si abrió GRACIELA.FDB se compara a MASTER.FDB con GRACIELA.FDB
  5. Para comparar a ambas bases de datos lo mejor es que tengan una tabla, por ejemplo llamada VERSION con una columna llamada por ejemplo VER_NUMERO. Si el número de versión de MASTER.FDB es más nuevo que el número de versión de GRACIELA.FDB entonces estamos seguros de que GRACIELA.FDB debe ser actualizada
  6. Si descubrimos que GRACIELA.FDB debe ser actualizada, entonces nuestro programa .EXE ejecuta a Database Comparer con sus parámetros de la línea de comandos
  7. De esta manera, no importa si el estudio contable tiene cientos de bases de datos, cada vez que el usuario abra una de esas bases de datos se la comparará con MASTER.FDB y en el caso de que la versión de MASTER.FDB sea más nueva entonces se actualizará la Base de Datos que el usuario abrió.
  8. Y estaremos seguros de que sea cual sea la Base de Datos que el usuario abra, y aunque hayan pasado meses o años desde la última vez que la abrió, siempre estará correctamente actualizada.
  9. Lo único que debemos recordar es que cada vez que cambiamos algo en MASTER.FDB debemos actualizar la columna VER_NUMERO de la tabla VERSION, escribiendo un número que sea mayor que el que existía ahí.

La interfaz de línea de comandos de Database Comparer

Si abrimos una ventanita “Símbolo del sistema”, nos ubicamos en una carpeta donde se encuentre el programa DBCOMPARER.EXE y lo ejecutamos con la opción /? veremos las opciones disponibles.

DBCOMPARER

Captura 1. Si haces clic en la imagen la verás más grande

Ejemplo de uso

Al programa DBCOMPARER.EXE podemos copiarlo en cualquier carpeta, no es necesario ni obligatorio ejecutarlo en la carpeta donde fue instalado. Lo mejor generalmente es copiar a DBCOMPARER.EXE y al archivo IBDB_CMP.CFG (donde se guardan los alias, las ubicaciones de las bases de datos, etc.) a la misma carpeta donde se encuentra nuestro programa .EXE

DBCOMPARER2

Captura 2. Si haces clic en la imagen la verás más grande

Al escribir el comando anterior lo que hacemos es decirle a DBCOMPARER que compare a la Base de Datos cuyo alias en ese programa es MASTER con la Base de Datos cuyo alias en ese programa es PRUEBA1. Si hay diferencias entonces PRUEBA1 cambiará para que sus metadatos sean idénticos a los metadatos de MASTER.

Cuando el programa finalice, estaremos seguros de que ambas bases de datos tienen exactamente la misma estructura.

Conclusión:

Database Comparer es una muy buena aplicación para comparar las estructuras de las bases de datos y para hacer que sean idénticas en caso de que tengan diferencias.

Si las bases de datos a comparar son pocas, el proceso puede ser realizado manualmente, es muy sencillo, nada complicado.

Pero si las bases de datos son muchas, entonces hacerlo manualmente demorará mucho tiempo y además se corre el riesgo de no compararlas a todas o de compararlas en el sentido erróneo. Por lo tanto es mucho mejor automatizar ese proceso.

La interfaz de línea de comandos justamente nos permite automatizar la comparación y actualización. Por eso, es conveniente usarla.

Enlaces:

http://www.clevercomponents.com/products/dbcomparer/index.asp

http://www.clevercomponents.com/products/index.asp

http://www.clevercomponents.com/downloads/index.asp

Artículos relacionados:

Database Comparer de Clever Components

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios

Funciones útiles con los conjuntos de caracteres

4 comentarios

El Firebird dispone de 3 funciones que podemos usar cuando estamos tratando con conjuntos de caracteres, ellas son:

BIT_LENGTH() que nos devuelve la longitud de un string en bits

CHAR_LENGTH() que nos devuelve la longitud de un string en caracteres

OCTECT_LENGTH() que nos devuelve la longitud de un string en bytes

La diferencia entre ellas es notoria cuando se usan distintos conjuntos de caracteres, como en los ejemplos que vemos a continuación:

Ejemplo 1. Creando una Base de Datos que por defecto usa ISO8859_1

CHARSET1

Captura 1. Si haces clic en la imagen la verás más grande

Ahora le agregamos una tabla llamada TEST:

CREATE TABLE TEST (
   NOMBRE VARCHAR(40));

Y luego le insertamos algunas filas a esta tabla:

INSERT INTO TEST (NOMBRE) VALUES ('aeiou');
INSERT INTO TEST (NOMBRE) VALUES ('áéíóú');
INSERT INTO TEST (NOMBRE) VALUES ('HOLA');
INSERT INTO TEST (NOMBRE) VALUES ('HOLAaeiou');
INSERT INTO TEST (NOMBRE) VALUES ('HOLAáéíóú');

A continuación usamos las 3 funciones anteriores para ver los resultados que obtenemos:

SELECT
   NOMBRE,
   BIT_LENGTH(NOMBRE),
   CHAR_LENGTH(NOMBRE),
   OCTET_LENGTH(NOMBRE)
FROM
   TEST

CHARSET3

Captura 2. Si haces clic en la imagen la verás más grande

Lo que podemos notar es que usando el conjunto de caracteres ISO8859_1 siempre CHAR_LENGTH es igual a OCTET_LENGTH y que al multiplicar a cualquiera de ellos por 8 obtenemos BIT_LENGTH.

Ejemplo 2. Creando una Base de Datos que por defecto usa UTF8

 CHARSET2Captura 3. Si haces clic en la imagen la verás más grande

Ahora le agregamos una tabla llamada TEST:

CREATE TABLE TEST (
   NOMBRE VARCHAR(40));

Y luego le insertamos algunas filas a esta tabla:

INSERT INTO TEST (NOMBRE) VALUES ('aeiou');
INSERT INTO TEST (NOMBRE) VALUES ('áéíóú');
INSERT INTO TEST (NOMBRE) VALUES ('HOLA');
INSERT INTO TEST (NOMBRE) VALUES ('HOLAaeiou');
INSERT INTO TEST (NOMBRE) VALUES ('HOLAáéíóú');

A continuación usamos las 3 funciones anteriores para ver los resultados que obtenemos:

SELECT
   NOMBRE,
   BIT_LENGTH(NOMBRE),
   CHAR_LENGTH(NOMBRE),
   OCTET_LENGTH(NOMBRE)
FROM
   TEST

CHARSET4Captura 4. Si haces clic en la imagen la verás más grande

Y ahora lo que notamos es que CHAR_LENGTH es igual a OCTET_LENGTH solamente cuando se usan los caracteres ASCII, es decir cuando no hay vocales acentuadas. En cambio cuando hay vocales acentuadas cada una de ellas usa 2 octetos (ó 2 bytes, son sinónimos). En la última fila podemos ver que hay 9 caracteres pero que se necesitan 14 bytes para almacenarlos ¿por qué? porque cada letra no acentuada ocupa 1 byte y cada letra acentuada ocupa 2 bytes. Entonces, la palabra “HOLA” se guarda en 4 bytes y las vocales “áéíóú” se guardan en 10 bytes. La suma da 14 bytes.

En todos los casos BIT_LENGTH es igual a OCTET_LENGTH multiplicado por 8. Eso es lógico, ya que cada octeto (o byte, son sinónimos) tiene 8 bits.

Conclusión:

Como se puede ver en los ejemplos anteriores, si todo el texto que se introducirá en las columnas de tipo CHAR o VARCHAR estará en alguno de los idiomas europeos occidentales (español, portugués, francés, inglés, alemán, etc.) entonces debemos crear a nuestra Base de Datos con el conjunto de caracteres ISO8859_1 porque si usamos UTF8 cada vocal acentuada y cada letra eñe ocupará 2 bytes, en lugar de solamente 1 byte. Y por lo tanto, con ISO8859_1 se ahorra espacio y con UTF8 se desperdicia espacio.

¿Cuándo usaríamos UTF8? Cuando el texto puede estar escrito también con otros lenguajes europeos, como el checo, el polaco, el ruso, el ucraniano, etc. Si ese no es el caso, lo mejor es usar ISO8859_1 porque se ahorrará espacio en el disco duro.

Artículos relacionados:

Entendiendo a los conjuntos de caracteres

Algo más sobre los conjuntos de caracteres

Entendiendo COLLATE

Consultando sin importar mayúsculas ni acentos

El índice del blog Firebird21

El foro del blog Firebird21

Impidiendo la conexión a una Base de Datos

4 comentarios

En ocasiones queremos evitar que un usuario, un proceso o una computadora se conecten a nuestra Base de Datos. O varios usuarios, varios procesos o varias computadoras.

Ya habíamos visto algunos artículos sobre este tema:

Como evitar que se conecten a una Base de Datos

Evitando que un usuario se conecte más de una vez a la Base de Datos

Evitando que un usuario se conecte más de una vez (método mejorado)

En el presente artículo nos explayaremos más sobre como evitar las conexiones indeseadas.

A partir del Firebird 2.1 podemos usar los llamados triggers de las bases de datos (database triggers, en inglés) y nos servirán perfectamente para lo que pretendemos lograr.

El primer paso es crear una excepción, la cual mostrará un mensaje al usuario. En realidad esto no es obligatorio, podríamos dejar que el Firebird muestre su mensaje por defecto pero ese mensaje estará en inglés. Si queremos personalizar el mensaje entonces deberemos crear una excepción, como la siguiente:

CREATE EXCEPTION
   E_ACCESO_NO_PERMITIDO 'No tienes permiso para conectarte a esta Base de Datos' ;

A continuación crearemos los triggers de bases de datos que necesitaremos:

Un trigger para evitar que los usuarios se conecten a la Base de Datos:

Listado 1.

CREATE TRIGGER NEW_DBTRIGGER_C
   ACTIVE ON CONNECT
   POSITION 2
AS
BEGIN

   IF (CURRENT_USER IN ('JUAN', 'MARIA', 'ESTELA','MONICA')) THEN
      EXCEPTION E_ACCESO_NO_PERMITIDO;

END;

Ningún usuario cuyo nombre esté dentro del IN (en este caso: JUAN, MARIA, ESTELA y MONICA) podrá conectarse a la Base de Datos.

Un trigger para evitar que los programas se conecten a la Base de Datos:

Listado 2.

CREATE TRIGGER NEW_DBTRIGGER_C1
   ACTIVE ON CONNECT
   POSITION 3
AS
BEGIN

   IF (EXISTS(SELECT
                 *
              FROM
                 MON$ATTACHMENTS M
              WHERE
                 M.MON$ATTACHMENT_ID = CURRENT_CONNECTION AND
                (NOT M.MON$REMOTE_PROCESS IN ('MI_PROG1.EXE', 'MI_PROG2.EXE')))) THEN
      EXCEPTION E_ACCESO_NO_PERMITIDO;

END;

En este caso solamente permitiremos el acceso a los programas cuyos nombres se encuentren en el IN, o sea: MI_PROG1.EXE y MI_PROG2.EXE. Evidentemente que tú escribirías allí los nombres de tus propios programas.

¿Cuál es el defecto que tiene esta protección? Que si el intruso sabe que MI_PROG1.EXE puede conectarse entonces podría renombrar a su propio programa como MI_PROG1.EXE y así conseguirá la conexión. Por eso siempre es importante que los mensajes de las excepciones le den al intruso muy poca información. Sería muy mala idea que el mensaje sea algo como: “Solamente se pueden conectar MI_PROG1.EXE y MI_PROG2.EXE” porque en ese caso lo primero que se le ocurrirá al intruso será renombrar a su programa y conseguirá conectarse. Además, hay que tomar en cuenta otro aspecto: no se debe impedir el acceso a los programas legítimos que podamos necesitar, como: GBAK.EXE

Un trigger para evitar que desde una computadora se conecten a la Base de Datos:

Listado 3.

CREATE TRIGGER NEW_DBTRIGGER_C2
   ACTIVE ON CONNECT
   POSITION 4
AS
BEGIN

   IF (EXISTS(SELECT
                 1
              FROM
                 MON$ATTACHMENTS M
              WHERE
                 M.MON$ATTACHMENT_ID = CURRENT_CONNECTION AND
                 M.MON$REMOTE_ADDRESS IN ('192.168.0.100', '192.168.0.104'))) THEN
      EXCEPTION E_ACCESO_NO_PERMITIDO;

END;

En este caso, las computadoras cuyos IP son 192.168.0.100 ó 192.168.0.104 jamás podrán conectarse a nuestra Base de Datos, siempre se les rechazarán todos los intentos de conexión.

Un trigger para evitar que desde una subred se conecten a la Base de Datos:

Listado 4.

A veces queremos que se rechace la conexión desde toda una subred y escribir todos los IP después del IN como se mostró en el ejemplo anterior sería muy largo y muy tedioso. Para esos casos tenemos una mucha mejor alternativa:


CREATE TRIGGER NEW_DBTRIGGER_C3
   ACTIVE ON CONNECT
   POSITION 4
AS
BEGIN

   IF (EXISTS(SELECT
                 1
              FROM
                 MON$ATTACHMENTS M
              WHERE
                 M.MON$ATTACHMENT_ID = CURRENT_CONNECTION AND
                 M.MON$REMOTE_ADDRESS STARTING WITH '192.168.14')) THEN
      EXCEPTION E_ACCESO_NO_PERMITIDO;

END;

Aquí estamos rechazando las conexiones de todas las computadoras de la subred 192.168.14, o en otras palabras cualquier computadora cuyo IP empiece con esos números será rechazada. Esto puede ser muy útil cuando por ejemplo tenemos una Base de Datos para los sueldos y jornales de los empleados y no queremos que desde otras computadoras de la Empresa puedan curiosear en ella. Entonces, de un plumazo las rechazamos a todas las que no se encuentran en nuestra misma subred.

Un trigger para evitar que se conecten fuera del horario autorizado:

Si el horario laboral de la empresa va de 8:00 a 18:00 entonces sería muy sospechoso que alguien tratara de conectarse a las 23:45 ¿verdad? ¿Por qué alguien querría conectarse a esa hora? Para estos casos podríamos tener un trigger que restrigiera el acceso fuera del horario establecido.

Listado 5.

CREATE TRIGGER NEW_DBTRIGGER_C4
   ACTIVE ON CONNECT
   POSITION 5
AS
BEGIN

 IF (CURRENT_USER <> 'SYSDBA' AND
     CURRENT_TIME < '07:45:00' OR CURRENT_TIME > '18:15:00') THEN
    EXCEPTION E_ACCESO_NO_PERMITIDO;

END;

Aquí, solamente al usuario SYSDBA le permitimos conectarse a cualquier hora que desee, todos los demás usuarios deberán iniciar la conexión entre las 07:45:00 y las 18:15:00

Desde luego que también podríamos restringir la conexión por los días de la semana: de Lunes a Viernes tendrían un horario de conexión, los Sábados tendrían otro horario de conexión y los Domingos tendrían otro horario.

O podríamos realizar combinaciones: MARCELA tiene un horario, SILVIA tiene otro horario, SUSANA tiene otro horario, etc.

Conclusión:

Para mantener la confidencialidad y la seguridad de los datos y de la información muchas veces es muy importante impedir que personas no autorizadas puedan conectarse a la Base de Datos. Esas personas podrían tener derechos de conexión a otras bases de datos de la Empresa, pero no a esta Base de Datos.

Usando los triggers de las bases de datos podemos impedir (o al menos dificultar) que se conecten quienes no deberían conectarse. Pero recuerda que ningún método es infalible. El usuario SYSDBA y el creador de la Base de Datos siempre podrán conectarse. Y podrían irse a almorzar dejando la conexión abierta y el intruso aprovechar la ocasión.

Hay muchísimas combinaciones más que puedes realizar pero con los ejemplos anteriores ya tendrás una buena idea de lo que se puede lograr.

Artículos relacionados:

Como evitar que se conecten a una Base de Datos

Evitando que un usuario se conecte más de una vez a la Base de Datos

Evitando que un usuario se conecte más de una vez (método mejorado)

El índice del blog Firebird21

El foro del blog Firebird21