Skip to Content

Fernando Nunes Blog (via FeedBurner)

Syndicate content
This is a small repository of information and a few articles about IBM Informix technologyFernando Nuneshttp://www.blogger.com/profile/15733748635390133382noreply@blogger.comBlogger234125
Updated: 1 hour 11 min ago

12.10.xC8 and rolling upgrades / 12.10.xC8 e rolling upgrades

Wed, 2016-12-14 00:31
Something in english about the article (original version here: http://informix-technology.blogspot.com/2016/12/1210xc8-and-rolling-upgrades-1210xc8-e.html)


English version
IBM published an alert (http://www.ibm.com/support/docview.wss?uid=swg21995897) stating that the lastest 12.10 fixpack (xC8) does not support the rolling upgrades feature. A rolling upgrade is the functionality that allows us to upgrade the secondary servers without having to restore them. This is supported only from version major.minor.xCn to major.minor.xCn+1 and when the fixpack does not change physical data structures. Unfortunately that's not the case with 12.10.xC8 as it was needed to make some internal changes to support encryption at rest (EaR).
As such it is clear that rolling upgrades can't be used to upgrade to 12.10.xC8, but that apparently was not clear on the product release notes


Versão Portuguesa
A IBM publicou um alerta (http://www.ibm.com/support/docview.wss?uid=swg21995897) informando que o último fixpack (xC8) da versão 12.10 não suporta a funcionalidade de roling upgrade. O rolling upgrade é o que nos permite fazer um upgrade dos servidores secundários sem ter de restaurar a imagem do primário e re-inicializar a replicação. Isto é suportado apenas entre fixpacks consecutivos (V.v.xCn para V.v.xCn+1 e quando o fixpack não muda estruturas físicas. Infelizmente não é o caso do 12.10.xC8 já que foi necessário mudar algumas estruturas para suportar a encriptação de dbspaces.
Como tal é claro que os rolling upgrades não podem ser usados para passar para a 12.10.xC8, mas aparentemente isso não estava claro nas notas da versão.

String truncate. Column level Encryption / Corte de strings. Encriptação de colunas

Mon, 2016-12-12 04:00
Truncation of strings and it's impact on column level encryption (original version here: http://informix-technology.blogspot.com/2016/12/string-truncate-column-level-encryption.html)


English version
A recent costumer engagement re-activated a dormant issue on my mind... I've already mentioned this is posts, answers in IIUG mailing list, internal and external chats and discussions... Personally I think this is one of the top annoying things in Informix. I'm referring to the fact that on a non-ANSI informix database we truncate a string on insert if it's length exceeds the length of the field. And we do this silently... no error.
There are two RFEs opened for this (appropriately defined as duplicates). The original one is 33830 and the other (duplicate) is 53804. I've seen several reasons for not implementing this, which I'd like to oppose (again):
  1. It's stated in the ANSI standard that it should work like this
    Although the ANSI standard is hard to read, there are some paragraphs that seem to suggest this. But even if it's clearly stated there, it isn't what people want. This can corrupt data. Anybody would prefer an error.
    Additionally we don't truncate on ANSI mode databases.
  2. We would be changing previous behavior
    True. But that could be an option and by default we could keep the old behavior. The author of the RFE suggests a new parameter in $ONCONFIG. That is an option, but I'd prefer also an option on CREATE DATABASE (we already introduced an option for NLSCASE SENSITIVE). Ideally we would have a new ALTER DATABASE to change it. The $ONCONFIG parameter could and should be used to set the default (if the option was not specified on the CREATE DATABASE statement).
    I would not create a new $ONCONFIG parameter. I'd prefer having more options on the EILSEQ_COMPAT_MODE parameter which already controls functionality around the same topic.
  3. It would be hard to implement
    Having basic programming knowledge and considering we don't do this for ANSI mode databases (see test below), I doubt this would be too hard to implement. Somehow I can imagine a piece of current code like

    if ( ANSI_MODE_FLAG && length(input) > col_length)
          raise_exception(1279);

    which would become:

    if ((ANSI_MODE_FLAG || AVOID_TRUNCATE_FLAG) && length(input) > col_length)
          raise_exception(1279);

    ANSI_MODE_FLAG and AVOID_TRUNCATE_FLAG are assumed to be flags set from the logging mode and from the database (or session) options
  4. That's not a database problem. The application must check it's inputs
    Although I can agree with the idea that applications should check the inputs, I know many of them don't check the length. And as we don't silently truncate a big number (100000 for example) when inserted into a SMALLINT column, I can't understand why we do it with strings. The database must help keeping data integrity. And it fails doing that for strings
As mentioned before we don't truncate strings on ANSI mode database. Here's what happens:

castelo@primary:informix-> dbaccess -e stores_ansi test_ansi_truncate.sql

Database selected.

DROP TABLE IF EXISTS test;
Table dropped.


CREATE TABLE test
(
col1 CHAR(5)
);
Table created.



INSERT INTO test VALUES('123456');
1279: Value exceeds string column length.
Error in line 7
Near character position 32


377: Must terminate transaction before closing database.

853: Current transaction has been rolled back due to error
or missing COMMIT WORK.

castelo@primary:informix->

Why don't we simply use ANSI mode databases? Well... They have other limitations (can't use datablades) and conversion of existing ones would require application changes.

Ok... so string truncation is a problem by itself. But this article is about a specially complex and nasty effect of string truncation.
Users looking into column level encryption will notice that the encrypted form of the data will be stored in a character column. And the encryption process will "enlarge" the length of the data. Even more complex, the length of encrypted data depends on various factors. The initial number of "characters" (or digits) is one. Others are if it uses hint or not and the encryption algorithm. Below is a table from the manual that maps the "N" original digits/characters to the final result depending on the algorithm and usage of the hint:



N ENCRYPT_TDESNo Hint ENCRYPT_AESNo Hint ENCRYPT_TDESWith Hint ENCRYPT_AESWith Hint 1 to 7 35 43 87 99 8 to 15 43 43 99 99 16 to 23 55 67 107 119 24 to 31 67 67 119 119 32 to 39 75 87 131 139 40 to 47 87 87 139 139 100 163 171 215 227 200 299 299 355 355 500 695 707 747 759

The previous values can be calculated using the following formulae (again, accordingly to the manual):
  • Encryption by ENCRYPT_TDES( ) with no hint:
    Encrypted size = (4 x ((8 x((N + 8)/8) + 10)/3) + 11)
  • Encryption by ENCRYPT_AES( ) with no hint:
    Encrypted size = (4 x ((16 x((N + 16)/16) + 10)/3) + 11)
  • Encryption by ENCRYPT_TDES( ) with a hint:
    Encrypted size = (4 x ((8 x((N + 8)/8) + 50)/3) + 11)
  • Encryption by ENCRYPT_AES( ) with a hint:
    Encrypted size = (4 x ((16 x((N + 16)/16) + 50)/3) + 11)
The integer division ( / ) returns an integer quotient and discards any remainder.

So, effectively, using the above calculations it should be possible to validate if the value inserted by the user would fit a specific column length. But that would have to be implemented for each column. Could introduce some errors, and would make it impossible to implement column level encryption without changing the application, which is otherwise almost possible (the only change is to provide a password at the session level, if that fits the use case).

And after all this discussion, what is the problem in truncating the strings in this context? Pretty simply it will make it impossible to decrypt the data, so effectively it corrupts data in an unrecoverable way. There's no way to overstate this. An application, application developer or a DBA cannot afford to have that risk. As I usually say when I'm discussing this, if  we loose a few characters of a name, email address, or street address, it's possible that a human can recover the missing data by looking at what is left. But with column level encryption even the loss of a single character means the data is lost. Not something we want to deal with, or have to explain to business managers.
What can we do? We could start by opening a bunch of PMRs and link them to the RFEs above... but apart from pressing IBM there's actually something very easy we can do to solve this for the very strict context of column level encryption.
As mentioned, recently I had a customer who is concerned with the upcoming EU data protection regulations and is considering their options to address the requirements in that new compliance challenge. One of the options is to use column level encryption, but they were highly concerned with the above scenario. So I gave this matter a considerable thought and I think I came up with a relatively reasonable workaround (although I hate to be forced to deal with this in the first place).

The solution is elegant, relatively lightweight, doesn't require any extra application changes and hopefully should be able to avoid the nasty hypotheses of data loss or corruption.
My first approach was to implement a trigger, and try to verify if the input data would fit the column. I quickly realized that the data as seen by the trigger is already truncated, so the approach would not work. But at that same moment it became clear that another approach would work: If the data is already truncated (or not, depending on the sizes of course), all I had to do was try to decrypt it. If the data was actually truncated, it would raise an error. And if I try that within a trigger, the triggering operation will naturally fail, which if the intended outcome. You can check the code at the end of the article.
Trying to decrypt the data would have the following problems:
  1. It would be expensive in terms of CPU resources
  2. If the data has been encrypted with a specific password passed to the encrypt functions (ENCRYPT_AES() or ENCRYPT_TDES() ), I would not be able to DECRYPT_CHAR(), without knowing the password
A solution to both of this problems is to use the GETHINT() function. It apparently is less expensive than DECRYPT_CHAR and at the same time doesn't require the password. So it will work in either cases (password at session level or password at function level). It will also "work" even if no hint was provided. Most importantly it will fail if data truncation had happened.

So basically I need to create a procedure that receives the encrypted data, apply the GETHINT() and let the magic happen. This procedure should be called from triggers set on the column/table (INSERT and UPDATE) that pass the "new" values as parameter. I choose to use LVARCHAR(32000) as it should cover most use cases. Smart blob encryption was not considered. In those cases we don't have a size limit...
The code is shown at the end of the article. Let's see what happens when we run it:


bica@primary:fnunes-> dbaccess -e stores test_truncate.sql

Database selected.

DROP TABLE IF EXISTS test_encrypt;
Table dropped.


CREATE TABLE test_encrypt
(
col1 SERIAL,
col2 CHAR(43)
);
Table created.



SET ENCRYPTION PASSWORD 'blog_password';
Encryption password set.



DROP PROCEDURE IF EXISTS check_encryption;
Routine dropped.


CREATE PROCEDURE check_encryption(str LVARCHAR(32000))
DEFINE dummy CHAR(32);;

SELECT GETHINT(str) INTO dummy FROM sysmaster:sysdual;;
END PROCEDURE;
Routine created.

;

CREATE TRIGGER i_test_encrypt INSERT ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.



CREATE TRIGGER u_test_encrypt UPDATE OF col2 ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.



INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 1'));
1 row(s) inserted.


INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 2'));
1 row(s) inserted.



INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('This longer row will probably not fit!'));
26005: The encrypted data is wrong or corrupted.
Error in line 32
Near character position 1


UPDATE test_encrypt SET col2 = ENCRYPT_AES('This longer row will probably not fit!') WHERE col1 = 1;
26005: The encrypted data is wrong or corrupted.
Error in line 34
Near character position 1

UPDATE test_encrypt SET col2 = ENCRYPT_AES('This fits!') WHERE col1 = 2;
1 row(s) updated.



Database closed.

bica@primary:fnunes->

So, as can be seen, I created a table with a column (col2) defined as CHAR(43) which won't be enough for some data I'll try to INSERT/UPDATE.
Then I setup the session encryption password. I create the procedure and the triggers on the table.
And I move on to the DML. The first two INSERTs use short values and they work. The third INSERT uses a longer string which encrypted version won't fit col2. It raises an error 26005. Then I try a similar string but within an UPDATE and I face the same error. And an update with short length works.

I believe this is a simple, clean and robust solution for this problem. But as usual, use it at your own risk. And don't forget this is a workaround for a specific scenario (column level encryption) to overcome a problem I believe should not exist in the first place.



Versão Portuguesa
Uma visita recente a um cliente reavivou um assunto que andava adormecido na minha mente... Já mencionei isto em artigos, respostas na lista de discussão do IIUG, conversas e discussões internas e externas... Pessoalmente penso que é uma das coisas mais irritantes no Informix. Refiro-me ao facto de que numa base de dados não-ANSI, o Informix corta strings na inserção e alteração se o comprimento das mesmas exceder o tamanho da coluna. E tal acontece de forma silenciosa. Sem erro.
Existem dois RFEs abertos para isto (apropriadamente detetados como duplicados). O original é o 33830 e o outro (duplicado) é o 53804. Já me foram sugeridas várias razões para a não implementação disto, as quais uma vez mais gostaria de refutar:
  1. É referido que o standard ANSI sugere que este é o comportamento correto
    Embora o standard ANSI seja difícil de ler, existem alguns parágrafos que parecem sugerir isto. Mas mesmo que fosse claro, existem contra-argumentos para isto. Ainda que seja o standard, não é o que as pessoas querem. Isto pode corromper dados. Qualquer pessoa preferirá um erro. Adicionalmente e paradoxalmente nós não fazemos o corte em bases de dados criadas em modo ANSI
  2. Estaria a mudar-se o comportamento anterior
    Verdade. Mas isto poderia ser uma opção e por omissão manter-se o comportamento antigo. O autor do RFE sugere um parâmetro no $ONCONFIG. Seria uma opção, mas eu preferiria também uma opção no CREATE DATABASE (já introduzimos uma opção para NLSCASE SENDITIVE). Idealmente deveríamos também ter um novo ALTER DATABASE para mudar a opção. O parâmetro do $ONCONFIG poderia e deveria ser usado como a definição por omissão (se a opção não fosse especificada no CREATE DATABASE).
    Pessoalmente não criaria um novo parâmetro, mas antes daria novos valores possíveis ao parâmetro EILSEQ_COMPAT_MODE, que já controla funcionalidades em torno deste tópico
  3. Seria difícil de implementar
    Tendo conhecimentos básicos de programação, e considerando que já hoje nós não cortamos as strings em bases de dados criadas em modo ANSI (ver o teste abaixo), duvido que isto fosse muito complexo de implementar. De alguma forma imagino um pedaço de código atual semelhante a:

    if ( ANSI_MODE_FLAG && length(input) > col_length)
          raise_exception(1279);

    que deveria tornar-se:

    if ((ANSI_MODE_FLAG || AVOID_TRUNCATE_FLAG) && length(input) > col_length)
          raise_exception(1279);

    ANSI_MODE_FLAG é assumido que será uma flag dependente do mode de logging da base de dados e AVOID_TRUNCATE_FLAG seria dependente da opção de criação da base de dados ou do parâmetro no $ONCONFIG
  4. Isto não é um problema de base de dados. A aplicação tem de validar os seus inputs
    Embora tenha de concordar com a ideia que as aplicações devem validar os seus inputs, sei que muitas delas não verificam o comprimento das strings ou fazem-no pelas definições de estruturas de dados. E se não cortamos de forma silenciosa um número grande (como 100000) ao tentar inserir num SMALLINT, não consigo entender porque o fazemos com strings. A base de dados tem como função essencial assegurar a integridade dos dados, de acordo com as definições dos mesmos. E falha quando se trata de o fazer em strings.
Como mencionado acima, nós não cortamos strings em bases de dados criadas em modo ANSI. Vejamos o que acontece:

castelo@primary:informix-> dbaccess -e stores_ansi test_ansi_truncate.sql

Database selected.

DROP TABLE IF EXISTS teste;
Table dropped.


CREATE TABLE teste
(
col1 CHAR(5)
);
Table created.



INSERT INTO teste VALUES('123456');
1279: Value exceeds string column length.
Error in line 7
Near character position 32


377: Must terminate transaction before closing database.

853: Current transaction has been rolled back due to error
or missing COMMIT WORK.

castelo@primary:informix->

Porque não passamos simplesmente a usar bases de dados em modo ANSI? Bem.... têm outras limitações (não podem usar datablades) e isso requereria mudanças no código das aplicações.

Ok... Então o corte de strings é um problema em si mesmo. Mas este artigo é sobre um efeito complexo e devastador desse corte.
Utilizadores que estejam a considerar o uso de encriptação de colunas, verificarão que a forma encriptada dos dados deverá ser guardada numa coluna com um tipo de dados alfa-numéricos (CHAR ou uma variante). E saberão também que o processo de encriptação fará "crescer" o tamanho dos dados. Ainda mais complicado, o tamanho dos dados encriptados dependerá de vários fatores: O número de caracteres (alfa-numéricos) originais é um desses fatores. Outros serão se se usa ou não uma hint, e o algoritmo de encriptação. Abaixo está uma tabela retirada do manual que mapeia o número original de dígitos / caracteres com o tamanho final, dependendo do algoritmo usado e da utilização ou não de hint:


N ENCRYPT_TDESNo Hint ENCRYPT_AESNo Hint ENCRYPT_TDESWith Hint ENCRYPT_AESWith Hint 1 to 7 35 43 87 99 8 to 15 43 43 99 99 16 to 23 55 67 107 119 24 to 31 67 67 119 119 32 to 39 75 87 131 139 40 to 47 87 87 139 139 100 163 171 215 227 200 299 299 355 355 500 695 707 747 759

Os valores anteriores podem ser calculados com as seguintes fórmulas (de acordo com o manual):
  • Encriptação com ENCRYPT_TDES( ) sem hint:
    Tamanho dados encriptados = (4 x ((8 x((N + 8)/8) + 10)/3) + 11)
  • Encriptação com ENCRYPT_AES( ) sem hint:
    Tamanho dados encriptados = (4 x ((16 x((N + 16)/16) + 10)/3) + 11)
  • Encriptação com ENCRYPT_TDES( ) com hint:
    Tamanho dados encriptados = (4 x ((8 x((N + 8)/8) + 50)/3) + 11)
  • Encriptação com ENCRYPT_AES( ) com hint:
    Tamanho dados encriptados= (4 x ((16 x((N + 16)/16) + 50)/3) + 11)
A divisão inteira ( / ) retorna um quociente inteiro descartando qualquer resto.

Portanto, efetivamente usando os cálculos acima deverá ser possível validar se um valor inserido por um utilizador caberá numa determinada coluna depois de encriptado. Mas isto teria de ser implementado para cada coluna. Poderia introduzir alguns erros, e tornaria impossível implementar a encriptação de colunas sem mudar a aplicação, o que de outra forma seria praticamente possível (a única alteração necessária seria o providenciar uma password de encriptação ao nível de sessão, se tal for permitido pelo caso de uso).

E depois de toda esta discussão, qual é o problema em cortar strings neste contexto? Muito simplesmente, tornará impossível efetuar a desencriptação, pelo que efetivamente tal situação corrompe dados de forma irrecuperável. Não há forma de exagerar esta conclusão. Uma aplicação, um programador ou um DBA não podem dar-se ao luxo de correr esse risco. Como habitualmente digo quando estou a discutir este assunto, se perdermos uns poucos caracteres de um nome, um endereço de correio ou endereço postal, é possível que um ser humano possa recuperar os dados cortados através da consulta do que sobrou. Mas com encriptação de colunas, mesmo a perda de um só carater implica que os dados estejam perdidos. Não é coisa com que queiramos lidar ou que tenhamos de explicar a pessoas responsáveis pelo negócio.
Então o que podemos fazer? Podíamos começar por abrir um rol de PMRs e ligá-los a este RFE acima., mas para além de pressionarmos a IBM, existe algo que efetivamente podemos fazer para resolver o contexto estrito de encriptação de colunas.
Como mencionado, recentemente tive contacto com um cliente que está preocupado com a nova regulação de proteção de dados da EU e está a considerar as opções que têm neste novo desafio de compliance. Uma das opções que têm é usar encriptação do nível da coluna, mas estavam altamente preocupados com o cenário acima. Por causa disso dediquei um considerável esforço ao tema e penso que cheguei a uma forma de contornar o problema (embora odeie ter de lidar com tal situação).

A solução é elegante, relativamente leve, não requer nenhuma modificação na aplicação e espero que esteja à altura de evitar a terrível hipótese de perda de dados ou corrupção.

A minha primeira abordagem foi implementar um trigger e tentar verificar se os dados de input (encriptados) caberiam na coluna. Rapidamente me apercebi que o trigger já vê os dados "cortados", pelo que a abordagem não resultaria. Mas no mesmo momento ficou claro que outra abordagem semelhante resultaria: se os dados já estão potencialmente "cortados", tudo o que deveria fazer seria desencriptá-los. Se tivessem sido efetivamente cortados isto despoletaria um erro. E se tal acontecer dentro de um trigger então a operação que despoletou o trigger iria por arrasto falhar. Pode verificar o código no final do artigo. Tentar desencriptar os dados levantaria os seguintes problemas::
  1. Seria dispendioso em termos de recursos de CPU
  2. Se os dados tivessem sido encriptados com uma password especifica passada às funções de encriptação (ENCRYPT_AES() or ENCRYPT_TDES() ), não seria capaz de efetuar a desencriptação sem saber a password
A solução para ambos estes problemas é usar a função GETHINT(). Deverá ser menos exigente em termos de CPU que a DECRYPT_CHAR() e tem a vantagem de não necessitar de password. Deverá portanto funcionar em ambos os casos (passwords ao nível da sessão e ao nível das funções). Irá também funcionar mesmo que não tenha sido utilizada nenhuma hint. Mas o verdadeiramente importante é que falhará se os dados tiverem sido corrompidos.

Assim, basicamente o que necessito é criar um procedimento que recebe os dados encriptados e aplica o GETHINT(), e deixar a magia acontecer. O procedimento deve ser chamado por triggers definidos na coluna / tabela (INSERT e UPDATE), que passam os "novos" valores como parâmetro
Escolhi o LVARCHAR(32000) como tipo do parâmetros dado que deverá cobrir a maioria dos casos. A encriptação de Smart Blobs não foi considerada. Até porque nesses casos não temos um limite de tamanho...
O código está visível no final do artigo. Vejamos o que acontece quando o corremos:


bica@primary:fnunes-> dbaccess -e stores test_truncate.sql

Database selected.

DROP TABLE IF EXISTS test_encrypt;
Table dropped.


CREATE TABLE test_encrypt
(
col1 SERIAL,
col2 CHAR(43)
);
Table created.



SET ENCRYPTION PASSWORD 'blog_password';
Encryption password set.



DROP PROCEDURE IF EXISTS check_encryption;
Routine dropped.


CREATE PROCEDURE check_encryption(str LVARCHAR(32000))
DEFINE dummy CHAR(32);;

SELECT GETHINT(str) INTO dummy FROM sysmaster:sysdual;;
END PROCEDURE;
Routine created.

;

CREATE TRIGGER i_test_encrypt INSERT ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.



CREATE TRIGGER u_test_encrypt UPDATE OF col2 ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.



INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 1'));
1 row(s) inserted.


INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 2'));
1 row(s) inserted.



INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('This longer row will probably not fit!'));
26005: The encrypted data is wrong or corrupted.
Error in line 32
Near character position 1


UPDATE test_encrypt SET col2 = ENCRYPT_AES('This longer row will probably not fit!') WHERE col1 = 1;
26005: The encrypted data is wrong or corrupted.
Error in line 34
Near character position 1

UPDATE test_encrypt SET col2 = ENCRYPT_AES('This fits!') WHERE col1 = 2;
1 row(s) updated.



Database closed.

bica@primary:fnunes->

Como se pode ver, crio uma tabela com uma coluna (col2) definida como CHAR(43) e que não será suficiente para alguns dados que irei tentar inserir/alterar.
Depois estabeleço a password de sessão. Crio o procedimento e os triggers na tabela. E passo ao DML. Os primeiros dois INSERTs usam valores curtos e vão funcionar. O terceiro INSERT usa uma string mais longa, cuja versão encriptada não cabe na coluna col2. Despoleta um erro 26005. Depois tento uma string semelhante  mas via um UPDATE e encontramos o mesmo erro. Depois um UPDATE "curto" e funciona
Acredito que isto é simples, "limpo" e robusto para resolver o problema. Mas como é habitual, utilize por sua conta, peso e risco. E relembro que isto é uma forma de contornar um problema que penso que nunca deveria ter acontecido, num contexto específico que é a encriptação de colunas.


Code/Código

DROP TABLE IF EXISTS test_encrypt;
CREATE TABLE test_encrypt
(
col1 SERIAL,
col2 CHAR(43)
);

SET ENCRYPTION PASSWORD 'blog_password';

DROP PROCEDURE IF EXISTS check_encryption;
CREATE PROCEDURE check_encryption(str LVARCHAR(32000))
DEFINE dummy CHAR(32);

SELECT GETHINT(str) INTO dummy FROM sysmaster:sysdual;
END PROCEDURE;

CREATE TRIGGER i_test_encrypt INSERT ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);

CREATE TRIGGER u_test_encrypt UPDATE OF col2 ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);

INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 1'));
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 2'));

INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('This longer row will probably not fit!'));

UPDATE test_encrypt SET col2 = ENCRYPT_AES('This longer row will probably not fit!') WHERE col1 = 1;
UPDATE test_encrypt SET col2 = ENCRYPT_AES('This fits!') WHERE col1 = 2;

Informix 12.10.xC8 is out! / Informix 12.10.xC8 saíu!

Tue, 2016-12-06 03:00
What's new in Informix 12.10.xC8 fixpack (original version here: http://informix-technology.blogspot.com/2016/12/informix-1210xc8-is-out-informix.html)


English version
And a new fixpack sees the daylight! This time is version 12.10.xC8. Contrary to the previous one, I think this one brings some exciting news. As usual here's the official list from the documentation but with my own comments:
  • Migration
    • Server changes that affect migration
      A new $ONCONFIG parameter (DISK_ENCRYPTION) was introduced
      A new session environment variable (USE_SHARDING) was introduced
      A new environment variable (IFX_LO_READONLY) was introduced
      This fixpack will force some internal structures migration. In order to go back, reversion (onmode -b) must be used
  • Administration
    • View and rerun DB-Access commands
      DBAccess now has a new option (-history) which can be used in non-menu mode, so that the commands run are labeled with a number, which makes it easier to re-run a previously entered command (run #). A list of commands and their numbers can be retunred by "history"
  • JSON compatibility
    • Consistent sharded insert, update, and delete operations
      When operating with a sharded collection, when we inserted a row that wouldn't belong to the cluster node we were connected to, the record would be moved asynchronously to the correct server. Now we can configure the session so that the servers move the object synchronously (two phase commit) before returning the COMMIT.
    • Complex text search with regular expressions
      We finally introduced the regular expression capabilities in the engine. This is a new auto-register datablade (we used to have a non-official bladelet before). It works in MongoDB API and in normal SQL by introducing a few new functions like regexp_match(), regexp_split etc.
    • JSON compatibility enhancements
      Several improvements in MongoDB API clients:
      • They can use native cursors which makes it perform better on "pagination" queries. Previously the client would need to issue a new query each time
      • The cursors above will have a timeout so that they won't keep opened after the user idles for too long
      • New option to close idle connections on JSON listener
      • A new listener parameter (listener.http.headers.size.maximum) allows controlling the maximum size of incoming HTTP headers
  • Enterprise replication
    • Consistent sharded insert, update, and delete operation
      Same as above but for traditional tables
    • List Enterprise Replication definition commands
      New "cdr list catalog" command will retrieve the CDR commands needed do duplicate current environment (for testing, or move to production)
  • Application development
    • Rename system-generated indexes
      A system generated index is an index automatically created by Informix to support a primary key, unique or foreign key constraint. If we remove the constrain the index is automatically removed. These indexes are distinguished from standard ones because their name starts with a " " (space).
      We now have the chance to rename these indexes which brings two advantages:
      • We may want to standardize the indexes name
      • We may want to remove the constraint but keep the index (which will make it faster if we want to re-implement the constraint)
    • Temporary table space usage
      One of my favorite. Is was nearly impossible to retrieve the session ID associated with temporary objects in the temporary dbspaces (I recently created a script called ixtempuse to overcome this). A new column (SID) was created in sysptnhdr. The column will be populated for temporary objects like hash tables, sort structures, view materialization structures etc. So, from now on it will be trivial to identify the session(s) consuming all of the temporary space
    • Suspend validation of check constraints
      Just like foreign key constraints, the check constraints now accept the "no validate" clause. This is very important in the following scenarios:
      • When importing data, we know it should be ok
      • Informix will verify that the data in the table being attached to a fragmented table (as a new fragment) validates the expression or list/interval that it used for the fragmentation scheme. This can be time consuming during the execution of ALTER FRAGMENT ATTACH as it forces a full scan of the consumed table. A solution for this would be to create a check constraint in the new table before the ATTACH. This would still consume resources and time, but it wouldn't be blocking anything. Now, with the new feature, we can make those check constraints instantaneous and then the engine can use them to skip the full scan!
    • JDBC 4.0 compliance
      We finally improved the Informix native driver compatibility with the JDBC standard. I've been thinking that having "native" and DRDA drivers puts Informix in a tricky situation: On one hand the DRDA drivers are easily updated because they're used by several databases. This makes them a preferable choice. But they don't support all the Informix specific functionality and in order to fix that the DRDA protocol would probably require changes. Being an open standard makes it difficult. It's nice to see investment in the native drivers.
    • Query smart large objects with read-only access
      An application can open smart blobs in read-only mode, which avoids the locking overhead that otherwise would be required
  • Security
    • Encrypt storage spaces
      From this fixpack onward we will be able to use transparent data encryption at the dbspace level. I won't go into high details, but this encryption is totally transparent for the application and database administrators. It intends to protect the database data from system administrators and possible disk copying or theft or privacy issues arising from old disk disposal. The way it works is pretty simple. After configuring a new parameter (DISK_ENCRYPTION) with a name of a keystore, any new dbspace will be encrypted by default. There's an option if you want to create a non encrypted dbspace.
      Encryption of existing dbspaces can be done by backup/restore. Decryption of already encrypted dbspaces can also be done by backup/restore. Each dbspace will use it's own encryption key. Space usage will not increase. Encryption of a table or set of tables can be achieved by moving it to an encrypted dbspace.
  • Time series
    • Advanced analytic for time series data
      New functions to analyze time series data for patterns or abnormalities including quantify similarity, distance, and correlation between two sequences
    • Enhancements to time series functions
      Several improvements on what can be used in expressions for CountIf and Apply functions
  • Embeddability
    • Easier embedded deployment
      ifxdeploy can be used by non-root users and the configuration file is deprecated in favor of command line parameter that were extended.
In global it seems a very promising fixpack (much more interesing than the latest one). Besides all the improvements in the NoSQL areas, it includes a series of important improvements for the common user, like disk encryption, better JDBC compatibility and regular expressions as SQL functions. And naturally it includes bug fixes.

[EDIT 2016-12-13]
Accordingly to an alert from IBM (http://www.ibm.com/support/docview.wss?uid=swg21995897) this fixpack does NOT support rolling upgrades. This isn't a surprise as it changes some internal page structures and by definition no fixpack that does that supports rolling upgrade. But It's better to make it clear

    Versão Portuguesa
    E um novo fixpack vê a luz do dia! Desta feita é a versão 12.10.xC8. Contrariamente ao anterior penso que este traz novidades muito interessantes. Como habitualmente aqui está a lista oficial mas com os meus comentários:
    • Migração
      • Mudanças no servidor que afetam a migração
        Introdução de um novo parâmetro do $ONCONFIG (DISK_ENCRYPTION)
        Introdução de uma nova variável de sessão (USE_SHARDING)
        Introdução de uma nova variável de ambiente (IFX_LO_READONLY)
        Este fixpack força mudanças na estrutura interna. Para voltar atrás será necessário efetuar uma reversão (onmode -b)
    • Administração
      • Ver e re-executar comandos do DBAccess
        O dbaccess tem agora uma opção (-history) que pode ser usada em modo direto (sem menus). Esta opção faz com que os comandos executados sejam numerados, para mais fácil re-execução de um comando anterior (run #). A lista de comandos já executados e o seu respetivo número pode ser obtida com o comando "history"
    • Compatibilidade JSON
      • Operações (INSERT/UPDATE/DELETE) em shards de forma consistente
        Quando trabalhamos com uma sharded collection, e inserimos uma linha que deverá pertencer a outro nó que não aquele a que estamos ligados, o registo deveria ser movido para o destino de forma assíncrona para o servidor correto. Agora, se assim o desejarmos, podemos pedir que a movimentação seja síncrona (two phase commit) e só então nos seja retornado o COMMIT
      • Pesquisa de texto utilizando expressões regulares
        Finalmente introduzimos as funcionalidades dadas pelas expressões regulares no motor. Trata-se de um novo datablade que se auto-regista quando usamos as suas funções pela primeira vez (já existia um bladelet não oficialmente suportado). Funciona no MongoDB API e em SQL normal, com a introdução de uma série de funções como regexp_match(), regexp_split() etc.
      • Melhorias de compatibilidade JSON
        Várias novidades em clientes MongoDB API:
        • Podem utilizar cursores nativos, o que permite maior eficiência em queries para efetuar paginação. Anteriormente o cliente teria de enviar uma query para cada página
        • Os cursores referidos anteriormente têm um timeout para evitar que fiquem abertos mesmo após longos períodos de inatividade dos clientes
        • Nova opção para fechar conexões inativas no listener de JSON
        • Um novo parâmetro do listener (listener.http.headers.size.maximum) permite controlar o tamanho máximo dos cabeçalhos dos pedidos HTTP recebidos
    • Enterprise replication
      • Operações (INSERT/UPDATE/DELETE) em shards de forma consistente
        Como a nota anterior para sharded collections, mas para tabelas tradicionais
      • Listar comandos de Enterprise Replication
        Novo comando "cdr list catalog" permite obter uma lista de comandos CDR necessária para duplicar o ambiente corrente (para testes ou cópia para produção)
    • Desenvolvimento aplicacional
      • Renomear índices gerados pelo sistema
        Um índice gerado automaticamente pelo sistema é um índice criado pelo Informix para suportar uma constraint como chave primária, chave estrangeira, ou unique constraint.
        Se removermos a constraint o índice é automaticamente eliminado. Estes índices distinguem-se dos normais porque o seu nome começa com um " " (espaço).
        Agora temos a possibilidade de renomear estes índices o que tem duas vantagens:
        • Podemos querer standardizar os nomes dos índices
        • Podemos querer remover a constraint mas manter o índice (o que tornará mais rápida a recriação da constraint se for essa a intenção)
      • Utilização de espaço temporário
        Uma das minhas favoritas! Era quase impossível obter a identificação das sessões que estavam a consumir espaço temporário na instância. Recentemente criei um script chamado ixtempuse para contornar essa dificuldade). Agora foi adicionada uma nova coluna (SID) à view da sysmaster chamada sysptnhdr. A coluna será preenchida para objetos temporários como hash tables, sorts, materialização de views etc. Portanto a partir deste fixpack será trivial obter a informação de que sessão(ões) está a consumir espaço temporário.
      • Suspender a validação de check constraints
        Tals como as chaves estrangeiras, agora as check constraints também aceita a cláusula "no validate". Isto é muito importante nos seguintes cenários:
        • Quando importamos dados e sabemos que os mesmos estão corretos
        • O Informix verifica que os dados de uma tabela que está a ser adicionada como fragmento de uma tabela fragmentada/particionada, validam a expressão de fragmentação em uso.
          Isto pode consumir muito tempo e recursos durante a execução da instrução ALTER FRAGMENT ATTACH, dado que força um full scan na tabela que é consumida. Uma solução para isto era criar uma check constraint na tabela que vamos adicionar que correspondesse à expressão de fragmentação. O otimizador tem inteligência para detetar a relação e evitar a verificação (dado que a constraint já o garante). Isto evita o tempo durante o ATTACH, mas implicava gastar os recursos na validação quando se criava a constraint. Ora agora com o "no validate" podemos criar a constraint instantaneamente, e evitar por completo o desperdício de tempo. Claro que a responsabilidade de garantir que os dados carregados estão corretos fica inteiramente a cargo do utilizador.
      • Conformidade com JDBC 4.0
        Finalmente melhoramos a compatibilidade do driver nativo com o standard JDBC. Eu penso que termos drivers nativos e DRDA coloca o Informix numa situação delicada: Por um lado os drivers DRDA estão mais adequados aos vários standards e são mais frequentemente atualizados pois podem ser usados em várias bases de dados da IBM. Mas por outro lado não suportam algumas funcionalidades específicas do Informix e para corrigir isto o standard DRDA teria de ser modificado. Sendo um standard aberto isto não é fácil. Por isso é muito bom ver investimento feito nos drivers nativos.
      • Acesso a smart large objects em modo de leitura apenas
        Uma aplicação pode optar por abrir smart large objects apenas em modo de leitura o que evita a sobrecarga que a gestão de locks traz.
    • Segurança
      • Encriptação de ficheiros de dados
        A partir deste fixpack poderemos usar encriptação de dados transparente ao nível dos dbspaces. Não irei entrar em detalhes muito técnicos, mas esta encriptação é totalmente transparente para as aplicações e utilizadores da base de dados. Destina-se a proteger os dados de administradores de sistema e possíveis cópias ou roubos de disco ou de problemas de privacidade levantados pela descontinuação de uso de discos antigos.
        A forma como funciona é muito simples. Após configurar um novo parâmetro (DISK_ENCRYPTION) com o nome de uma keystore, qualquer dbspace novo será encriptado. Existe uma opção para os criar sem encriptação.
        A encriptação de dbspaces já existentes pode ser alcançada através de backup/restore. Desencriptação de dbsapces já encriptados pode ser obtido da mesma forma.
        Cada dbspace terá a sua própria chave de encriptação. O espaço ocupado não crescerá por via da encriptação. Encriptação de uma tabela ou conjunto de tabelas específico pode ser conseguido pela movimentação das mesmas para um dbspace encriptado
    • Time series
      • Analítica avançada sobre dados timeseries
        Novas funções para analisar dados timeseries, como pesquisa de padrões ou anormalidades, incluindo quantificação de similaridade, distância e correlação entre duas sequências
      • Melhorias em funções de timeseries
        Várias melhorias no que pode ser usado como expressões para as funções CountIF e Apply
    • Embeddability
      • Distribuição "embebida" mais fácil
        O ifxdeploy pode ser usado como non-root e o seu ficheiro de configuração foi descontinuado em favor de parâmetros na linha de comandos, que foram expandidos
    Vendo na globalidade parece-me um fixpack bastante promissor (e bem mais interessante que o último). Para além das melhorias na área do NoSQL, existem várias melhorias importantes para o utilizador comum, como a encriptação de disco, melhor compatibilidade JDBC, verificação de uso de espaço temporário e expressões regulares em funções SQL. E naturalmente uma série de correções de bugs como em qualquer fixpack.

    [EDITADO 2016-12-13]
    Segundo um alerta da IBM (http://www.ibm.com/support/docview.wss?uid=swg21995897) este fixpack NÃO suporta rolling upgrades. Isto não é uma surpresa pois muda algumas estruturas nas páginas, e por definição nenhum fixpack que o façao suporta rolling upgrade. Mas é melhor deixar isto bem claro.

    Informix functionality demo / Demo de funcionalidades Informix

    Mon, 2016-12-05 03:00
    If you want to see the power and versatility of Informix, check the video mentioned in this post (original version here: http://informix-technology.blogspot.com/2016/12/informix-functionality-demo-demo-de.html)



    English version
    Very recently a new video about Informix functionality was made available. I usually don't post about such events, but this video is not a customer endorsing Informix as usual. It's a long time Informix consultant and IT specialist, author of several Informix related books (one of which I have the honor of owning a signed copy), Carlton Doe, leading us through several Informix features.. In around 17m you'll be able to check several innovative features of Informix in a potential real use case. From IoT, platform diversity, replication, ease of use and integration in modern environments you'll be able to see it all...

    Check below!

    Versão Portuguesa
    Muito recentemente um novo vídeo sobre funcionalidades do Informix foi disponibilizado. Normalmente não publico nada sobre este tipo de eventos, mas este vídeo não é de um cliente ou parceiro a louvar o Informix. É um consultor e especialista de TI, autor de vários livros sobre Informix (oum dos quais tenho a honra de possuir uma cópia autografada), Carlton Doe, guiando-nos por várias funcionalidades do Informix. Em cerca de 17m poderá verificar várias funcionalidades inovadoras do Informix num potencial cenário real. De IoT, diversidade de plataformas, replicação, facilidade de utilização e integração em ambientes modernos poderá ver tudo...

    Veja abaixo!


    INFORMIXDIR and files configuration / INFORMIXDIR e configuração de ficheiros

    Thu, 2016-12-01 18:21
    Setting up INFORMIXDIR and instance files, specially in a cluster configuration (original version here: http://informix-technology.blogspot.com/2016/11/informixdir-and-files-configuration.html)


    English version
    Ok... the article title is a bit confusing, but sometimes so is this subject. I've received a few questions about how to setup the Informix related files, in particular if a cluster (active/passive) configuration is being used. Let's start by listing the files I'm referring to
    Files or directories we can configure
    • INFORMIXDIR
      The product installation directory. Note that different instances can share a single $INFORMIXDIR
    • ONCONFIG file
      This is the instance configuration file. All the parameters for instance initialization and subsequent startups are contained in this file. This file is pointed to by the environment variable $ONCONFIG, but the variable only specifies the file name. The file location must be $INFORMIXDIR/etc
    • online.log
      This is the instance log file where the instances messages will be written. The file can have the name we want and it's location is defined by an $ONCONFIG parameter (MSGPATH)
    • SQLHOSTS
      This is the file containing the names, addresses, ports, protocols and options of each listener. By default it's $INFORMIXDIR/etc/sqlhosts, but it can be overridden by the value in $INFORMIXSQLHOSTS variable (full PATH)
    • chunks
      The chunks are Informix datafiles. In the good old days, when everybody was using RAW devices, the recommendation used to be creating symbolic links that would point to the raw devices. I still like to do this, even when using cooked files (datafiles in operating system file system). It gives us an extra flexibility in case we need to move the physical files
    • console file
      This file is somehow "ignored" by many customers. It's a file where some messages are written. We don't normally need to check it. The full path is defined by an $ONCONFIG parameter (CONSOLE)
    • Restore point dir
      This is a file system location (directory) where Informix write some information during upgrades. It's defined by an $ONCONFIG parameter (RESTORE_POINT_DIR)
    • LTAPEDEV/TAPEDEV
      These are the locations for logical log backup and instance backups, defined by the $ONCONFIG parameters with the same names. They can be defined with a special value ("/dev/null") if you want/need to discard the objects. Note that LTAPEDEV requires an instance restart if we're changing it to/from "/dev/null". Normally they'll point to a directory in the file system (or a file on very old Informix versions). If we're using a storage manager, their value is not really important (unless we're referring to LTAPEDEV and it has the "/dev/null" value - this is not compatible with the use of onbar and a storage manager)
    • BAR activity log
      The onbar activity log. Defined by the $ONCONFIG parameter BAR_ACT_LOG
    • BAR debug log
      The onbar debug log. Only used for debugging and pointed to by the $ONCONFIG parameter BAR_DEBUG_LOG
    • IXBAR file
      The very important file which contains the onbar catalog. This is defined by an $ONCONFIG parameter (BAR_IXBAR_PATH). The default is $INFORMIXDIR/etc/ixbar.N where "N" is the instance SERVERNUM
    • BAR BSA library path
      The full location of the XBSA library we're using, if we setup a storage manager. The location will be dependent on the storage manager Informix client installation. The value is defined in the BAR_BSA_LIBPATH $ONCONFIG parameter
    • Database Library Path
      The location where we allow the engine to load dynamic libraries for extensibility purposes. The location is defined by the $ONCONFIG parameter DB_LIBRARY_PATH
    • Data Replication lost and found
      Base file name for HDR environments to store transactions committed on a primary server that were not successfully transmitted to the secondary server. This is defined by the $ONCONFIG parameter DRLOSTFOUND
    • Dump directory
      The directory where shared memory dumps and diagnostic files will be written. This must be a filesystem location with enough storage for a few shared memory dumps (assuming the instance is configured to create them in case of failure). The location is defined by an $ONCONFIG parameter (DUMPDIR)
    • Alarmprogram
      The script/program to run when certain events happen. The location is defined by an $ONCONFIG parameter (ALARMPROGRAM)
    • System alarm program
      The system alarm program/script. This one is called when serious (crash/assert fails) happen and is defined by an $ONCONFIG parameter (SYSALARMPROGRAM)
    • Java Virtual Processor log
      File that takes trace outputs and stack dumps from the java virtual CPUs. Defined in the $ONCONFIG parameter JVPLOGFILE
    • ADTCFG file
      This is the file controlling the auditing feature. It exists in $INFORMIXDIR/aaodir. After chnages are made the system creates an adtcfg.N where N is the value of SERVERNUM.
    Aspects to considerThe INFORMIXDIR The first thing to consider is the $INFORMIXDIR. Many people usually use /opt/informix, or /usr/informix and install the product there. This is not the best option because when we're planning an upgrade we need to have both the old and the new version available. Some customers solve this by installing the product in a directory that contains the version (let's assume "my" standard) like /usr/informix/srvr1210fc7w1 and then create a symbolic link /usr/informix/server and point it to the version they want to use. This may work for a single instance, but becomes problematic if you use several instances and you want the option to upgrade them at different moments

    The existence of a cluster configuration raises another problem. Should you install the product on both nodes, or just in a single cluster controlled path that moves between nodes? There are people who defend the idea of a single INFORMIXDIR installation which is moved to the active node. But again, if you have several instances and you consider spreading them across the two nodes (all in a single node if the hardware fails or needs maintenance) that won't fulfill the requirements. A single $INFORMIXDIR moving between nodes simplifies other issues. If you have an INFORMIXDIR on node A, and an INFORMIXDIR on node B, you'll have two $ONCONFIG, ADTCFGs and maybe a few others that you cannot define their full path.

    Another aspect related to this, is that if you configure role separation (a very nice and free Informix feature that allows different people to have different security functions in an Informix instance), if you have a single INFORMIXDIR for several instances, you can't separate the roles differently across the instances. In most cases this would not be an issue. Actually most customers don't have enough people managing the database to be able to separate the roles even for a single instance.
    The log filesThe log files, like online.log, onbar activity and debug logs etc. sometimes have defaults that are inside $INFORMIXDIR, and even when the defaults are not like that, too often I find customers that put them there. The problem in doing that is simple. When we upgrade, the logs history becomes "broken" because now you have logs inside the old and the new $INFORMIXDIR.

    Another obvious problem with logs comes from cluster environments where an instance moves from node A to node B. If the logs are not pointing to a common cluster resource you'll have parts of the logs spread across the nodes.
    On the other hand, if the logs are pointing to a common location we loose track of where the instance was started. And for problem analysis this is important (more about this in a later article).
    The chunksThe decision here is if we use symbolic links or not. This used to be much more important because sometimes a change in the raw devices would mean a different path. And we couldn't change the path of the chunks as the engine knows them. In modern systems and Informix versions, we can create the raw devices with specific names (so if we need to rebuild or change some options we can re-use the old names) and we can change the path name of the chunks by doing a "fake" external backup/restore with chunk renaming.
    The scriptsUsually the instances require a set of scripts maintained by the DBAs. Once again, too many often I see these scripts inside the $INFORMIXDIR, or $INFORMIXDIR/bin. As mentioned earlier, this is not a good practice because during upgrades it would be a mess, and the same happens if you run several instances on the same box and consider having different INFORMIXDIRs (one reason would be that they can be in different versions).

    The tricky part is that sometimes the scripts must be customized for each instance. And another problem (again) arises from the use of a cluster environment. In that case you may need to keep as many copies of the scripts as nodes you're running. Eventually, if the cluster allows you to have a common mount point or a cluster filesystem (like GPFS, or GFS/GFS2) you may have a solution. For situations where a single script must have different configurations for different instances, we can use a "case/esac" structure in SHELL conditioned by $INFORMIXSERVER. This is a possibility for example to implement a common alarm program which may need to have different backup configurations for example.
    RecommendationsINFORMIXDIRI usually install the product in locations that clearly indicate the version (/usr/informix/srvr1210fc7w1). But then I use a script (ixvirtdir) to create a "special" INFORMIXDIR for each specific instance. The script takes a few parameters like the existing INFORMIXDIR and the new one which I normally define with a suffix equal to the instance name. Additionally we can provide operating system groups that will be used as DBSA, AAO and DBSSO for role separation. The "special" INFORMIXDIR will be a new directory where inside I'll create a link to each original INFORMIXDIR's sub-directory with a few exceptions. Let's see an example:

    castelo@primary:informix-> echo $INFORMIXSERVER $INFORMIXDIR
    castelo /opt/informix/srvr1210fc7w1_castelo
    castelo@primary:informix-> pwd
    /opt/informix/srvr1210fc7w1_castelo
    castelo@primary:informix-> ls -l
    total 84
    drwxrwx--- 2 informix ixaao 4096 Aug 18 02:46 aaodir
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 bin -> /opt/informix/srvr1210fc7w1/bin
    drwxrwx--- 2 informix ixdbsso 4096 Aug 1 13:44 dbssodir
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 demo -> /opt/informix/srvr1210fc7w1/demo
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 doc -> /opt/informix/srvr1210fc7w1/doc
    drwxrwxr-x 4 informix ixdbsa 4096 Aug 23 11:10 etc
    lrwxrwxrwx 1 informix informix 34 Nov 13 03:21 extend -> /opt/informix/srvr1210fc7w1/extend
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 gls -> /opt/informix/srvr1210fc7w1/gls
    lrwxrwxrwx 1 informix informix 33 Nov 13 03:21 gskit -> /opt/informix/srvr1210fc7w1/gskit
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 help -> /opt/informix/srvr1210fc7w1/help
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 incl -> /opt/informix/srvr1210fc7w1/incl
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 isa -> /opt/informix/srvr1210fc7w1/isa
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 lib -> /opt/informix/srvr1210fc7w1/lib
    lrwxrwxrwx 1 informix informix 35 Nov 13 03:21 license -> /opt/informix/srvr1210fc7w1/license
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 msg -> /opt/informix/srvr1210fc7w1/msg
    lrwxrwxrwx 1 informix informix 38 Nov 13 03:21 properties -> /opt/informix/srvr1210fc7w1/properties
    lrwxrwxrwx 1 informix informix 35 Nov 13 03:21 release -> /opt/informix/srvr1210fc7w1/release
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 SDK -> /opt/informix/srvr1210fc7w1/SDK
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 snmp -> /opt/informix/srvr1210fc7w1/snmp
    drwxrwx--- 3 informix ixdbsa 4096 Aug 1 13:44 ssl
    drwxrwx--- 2 informix informix 4096 Aug 23 11:10 tmp


    So, INFORMIXDIR is /opt/informix/srvr1210fc7w1_castelo. "castelo" is the INFORMIXSERVER which acts as a suffix. Most sub-directories are just links to the base product installation. The exceptions are "aaodir", "dbssodir", "etc", "ssl" and "tmp". These are real directories for several reasons. In a cluster configuration, the "virtual" INFORMIXDIR would be on a cluster resource and the base product installation would either also be in a cluster resource (single instance on the machine, or if the decision to move to another node would imply move all the instances), or a local product installation. Note that by creating that list of sub-directories as real directories, I'm isolating everything that can vary from one instance to the other. Note also that these sub-directories would not contain very large content, with the possible exception for "tmp" if it's defined as the DUMPDIR for example.
    With this method we can achieve maximum flexibility and instance isolation in the following situations:
    1. Several instances on a single machine
    2. Cluster environments even with several instances that don't necessarily move from one node to another as a group
    3. Full role separation at each instance level, allowing for different groups for different instances
    Informix user homeThe home of the informix user (let's assume /home/informix) will contain two directories: "bin" and "etc". In the "bin" directory I put every script I use for the instances management including the alarmprogram.sh. In the "etc" I put all the configuration scripts/files like:
    • sqlhosts (single file per machine) facilitates configurations for distributed queries etc.
    • The configuration file (infx_env.lst) for my script to position across the instances environment (setinfx)
    • The configuration file for the alarmprogram.sh (alarm_conf.sh) where each instance specific configurations are defined in a case/esac structure
    This setup does not solve the cluster problems. In a cluster environment, assuming all the nodes may have some instance(s) running at the same time, this structure must be replicated across nodes. If however you move all the instances as a single block, the /home/informix could be a cluster resource (or at least the two sub-directories - /home/informix/etc and /home/informix/bin ). In any case the "bin" directory must be placed in the PATH environment variable.
    We should also try to avoid having two copies of configuration files like $INFORMIXSQLHOSTS, and we may consider using symbolic links to a shared location.
    Log files and othersThings like online.log, onbar activity and debug files, ixbar etc should be put on a cluster resource that moves with each instance. On a non cluster environment these files can be created inside a informix's HOME sub-directory like /home/informix/logs and /home/informix/conf.
    Even if the files are created inside a cluster resource, specific for each instance I prefer to create the files with a suffix like INFORMIXSERVER (e.g. online_${INFORMIXSERVER}.log, ixbar.${INFORMIXSERVER} etc.
    It's important to underline the importance of having a single IXBAR file in the cluster. Otherwise you'll have the catalog information spread across the cluster and it will make the restore process impossible.
    Chunks and linksFor the chunks I still prefer to use symbolic links. These can point to cooked files or RAW devices. Both the links and the files (RAW or cooked) must be configured as a cluster resource on a cluster environment.
    DUMP dirsThere are a few directories that are used sporadically but are very important. One is the configuration of the parameter DUMPDIR where the assert fail files are written. Somehow similar are the parameters RESTORE_POINT_DIR, DRLOSTFOUND and PLOG_OVERFLOW_PATH.
    These are important for certain activities, but typically once the issue is solved the files can and are removed. So, it's important that their locations point to large enough and existing directories, but it's not very relevant if they're specific for each instance or a single cluster location.


    Versão Portuguesa
    OK... o título do artigo é um pouco confuso, mas por vezes também este assunto o é. Tenho recebido algumas questões sobre como configurar os ficheiros usados pelo Informix, em particular num ambiente de cluster (ativo/passivo). Comecemos por enunciar uma lista de ficheiros a que me estou a referir.
    Ficheiros ou diretórios que podemos configurar
    • INFORMIXDIR
      O diretório de instalação do produto. Note-se que diferentes instâncias podem partilhar um único $INFORMIXDIR
    • Ficheiro ONCONFIG
      É o ficheiro de configuração da instância. Todos os parâmetros para a inicialização da instância e sub-sequentes arranques estão contidos neste ficheiro. A variável de ambiente $ONCONFIG aponta para o nome do ficheiro, sendo que a sua localização é forçosamente $INFORMIXDIR/etc
    • online.log
      O ficheiro de log onde as mensagens da instância são escritas. O ficheiro pode ter o nome e localização que quisermos e isto é definido pelo parâmetro MSGPATH no $ONCONFIG
    • SQLHOSTS
      O ficheiro que contém os nomes, endereços, portos TCP, protocolos e opções de cada listener. Por omissão a sua localização é $INFORMIXDIR/etc/sqlhosts, mas pode ser redefinido pelo valor da variável de ambiente $INFORMIXSQLHSOSTS (caminho completo)
    • chunks
      Os chunks são os "ficheiros" de dados do Informix. Nos bons velhos tempos em que toda a gente usava dispositivos RAW, a recomendação era utilizar links simbólicos que apontariam para esses dispositivos. Eu ainda gosto desta ideia, mesmo que se utilizem ficheiros em filesystem (cooked files). Esta opção oferece uma flexibilidade extra caso necessitemos de mover os ficheiros físicos
    • Ficheiro "console"
      Este ficheiro costuma ser de alguma forma ignorado por muitos clientes. É um ficheiro onde algumas mensagens são escritas. Normalmente não é muito necessário verificar estas mensagens. O caminho completo é definido por um parâmetro do $ONCONFIG (CONSOLE)
    • Restore point dir
      É uma localização em filesystem (diretório) onde o Informix escreve alguma informação durante os upgrades. É definido por um parâmetro do $ONCONFIG (RESTORE_POINT_DIR)
    • LTAPEDEV/TAPEDEV
      São as localizações para o backup dos logical logs e backup de instâncias, definidos pelos parâmetros de $ONCONFIG com o mesmo nome. Podem ser definidos com um valor especial ("/dev/null") se quisermos descartar os objetos. Note-se que o LTAPEDEV requer um restart da instância para mudar de/para "/dev/null". Normalmente apontam para um diretório no file system (ou em versões muito antigas do Informix apontarão para ficheiros). Se usarmos um storage manager o seu valor não é muito importante (exceto se o LTAEPDEV estiver para "/dev/null" - isto não é compatível com a utilização de um storage manager), visto que na realidade os objetos não serão guardados nesta localização, mas antes enviados para o storage manager.
    • Log de atividade do onbar
      Log de atividade de backup usando o onbar. Definido por um parâmetro do $ONCONFIG (BAR_ACT_LOG)
    • Log de debug do onbar
      Localização onde é escrito o debug do onbar. Apenas é usado se ativarmos o debug. O seu valor é definido por um parâmetro do $ONCONFIG (BAR_DEBUG_LOG)
    • Ficheiro IXBAR
      Um ficheiro muito importante que contém o catálogo do onbar. É definido pelo parâmetro BAR_IXBAR_PATH do $ONCONFIG. Por omissão é $INFORMIXDIR/etc/ixbar.N onde "N" é o valor do parâmetro SERVERNUM (único para cada instância)
    • Caminho da biblioteca BAR BSA
      O caminho completo para a biblioteca XBSA que estamos a usar, caso estejamos a integrar com um storage manager. A localização será dependente da instalação do cliente Informix do storage manager. O valor é definido pelo parâmetro BAR_BSA_LIBPATH do $ONCONFIG
    • Database Library Path
      A localização onde permitimos que o motor carregue bibliotecas dinâmicas para fins de extensibilidade. O valor é definido pelo parâmetro DB_LIBRARY_PATH do $ONCONFIG
    • Data Replication lost and found
      Nome base onde os ambientes HDR podem guardar transações committed no servidor primário que não foram transmitidas com sucesso para o servidor secundário. Definido pelo parâmetro DRLOSTFOUND do $ONCONFIG
    • Diretório de dumps
      Directório onde os dumps de memória partilhada e ficheiros de diagnóstico serão criados. Tem de ser uma localização com espaço suficiente para alguns dumps de memória (assumindo que a instância está configurada para os gerar em caso de falhas). A localização é definida pelo parâmetro "DUMPDIR" do $ONCONFIG
    • Alarmprogram
      O script ou programa que é executado pelo motor quando certos eventos acontecem. A localização é definida pelo parâmetro ALARMPROGRAM do $ONCONFIG
    • System alarm program
      Semelhante ao alarmprogram mas para ser chamado em casos de falhas graves (quedas do motor ou assert fails). Definido pelo parâmetro SYSALARMPROGRAM do $ONCONFIG
    • Log dos processadores virtuais para Java
      Ficheiro que recebe os "traces" e "dumps" de "stack" do JVPs. Definido pelo parâmetro JVPLOGFILE do $ONCONFIG
    • ADTCFG
      Ficheiro de configuração do audit. Existe no diretório $INFORMIXDIR/aaodir e depois de efetuadas alterações será criado o ficheiro adtcfg.N onde N é o valor do parâmetro $SERVERNUM
    Aspetos a considerar  O INFORMIXDIX A primeira coisa a considerar é o valor de $INFORMIXDIR. Muitas pessoas simplesmente usam /opt/informix ou /usr/informix e instalam aí o produto. Isto não é a melhor opção pois durante os upgrades devemos ter disponíveis ambas as versões (antiga e nova). Em alguns casos resolvem isto instalado o produto num diretório que contém a versão (vamos assumir o "meu" standard) como /usr/informix/srvr1210fc7w1 e depois criam um link simbólico do tipo /usr/informix/server que aponta para a instalação que pretendem usar. Isto pode funcionar quando existe apenas uma instância no servidor, mas torna-se complicado quando há várias instalações e se pretende a opção de fazer upgrades em alturas diferentes.

    A existência de um cluster levanta ainda mais um problema. Deveremos instalar o produto em ambos os nós ou apenas numa localização controlada pelo cluster e que é movida entre os nós? Há quem defenda a ideia de uma única instalação (INFORMIXDIR) que é movida para o nó que se quer "ativo". Mas novamente, se temos várias instâncias e consideramos dividir a carga entre os nós (apenas tendo as instâncias todas num só nó se o outro estiver indisponível), uma só instalação não responde aos requisitos. Ter um só $INFORMIXDIR para cada instância que é movido dentro do cluster simplifica outros aspetos. Se temos um INFORMIXDIR no nó A, e outro INFORMIXDIR no nó B, teremos dois ficheiros $ONCONFIG, ADTCFG e eventualmente outros cujo caminho não pode ser mudado (só o nome).

    Outro aspeto relacionado com isto, é se configurarmos a separação de funções (role separation), que é uma excelente funcionalidade do Informix (sem custos adicionais) que permite que diferentes grupos de pessoas tenham acesso a diferentes funções numa instância Informix, então um só INFORMIXDIR para várias instâncias não permite fazer também a segregação por instância. Embora na realidade a maioria dos clientes não tenha pessoas suficientes para a separação de uma única instância...
    Ficheiros de logOs ficheiros de log, como o online.log, o de actividade do onbar, o de debug do onbar etc. por vezes têm valores por omissão dentro do $INFORMIXDIR, e mesmo quando assim não é, muitas vezes encontro clientes que os colocam lá. O problema de fazer isto é simples. Quando fazemos um ugrade o histórico dos ficheiros perde-se, pois parte fica no $INFORMIXDIR antigo e outra parte no novo (ou obriga a cópia para que tal não aconteça)

    Outro problema óbvio com os logs deriva de ambientes de cluster onde as instâncias "saltitam" entre o nó A e o nó B. Se os logs não estiverem a apontar uma uma localização comum nos dois nós, acabamos com partes dos ficheiros no nó A e outra parte no nó B.

    Por outro lado, se os logs estiverem a apontar para uma localização comum perdemos o registo de onde estava a instância em cada momento (o que pode ser conveniente para despiste de problemas - mais sobre isto num futuro artigo)
    Os chunksAqui a decisão é se usamos links simbólicos ou não. Isto era muito mais importante antigamente porque por vezes alguma alteração nos raw devices implicava um caminho diferente e não era possível trocar o nome dos chunks pelo qual o motor os conhece. Em ambientes modernos e versões recentes do Informix, podemos criar os raw devices com os nomes que quisermos (se necessitarmos de reconstruir ou mudar opções podemos re-utilizar os nomes), e também podemos mudar os caminhos como o Informix os conhece através de um falso backup/restore externo.
    Os scriptsNormalmente as instâncias requerem um conjunto de scripts mantidos pelos DBAs. Mais uma vez, vezes demais eu vejo estes scripts dentro do $INFORMIXDIR ou $INFORMIXDIR/bin. Como mencionado atrás isto não é uma boa prática, pois durante os upgrades torna-se uma confusão, e o mesmo se passa se necessitarmos de várias instâncias no mesmo servidor e pensarmos em ter vários $INFORMIXDIR (bastando para isso que estejam em versões diferentes)

    O difícil pode ser que alguns scripts podem ter se ser adaptados para cada instância. E novamente a existência de um cluster pode trazer problemas. Neste caso poderemos ter de manter tantas cópias quantos nós existam no mesmo. Se porventura o cluster permitir algum tipo de sistema de ficheiros partilhado (GPFS ou GFS/GFS2 por exemplo), poderá haver solução para isso.
    Os casos em que um só script tem de estar adaptado a várias instâncias poderemos usar uma estrutura "case/esac" dentro do script, cujo condicionamento terá por base o valor de $INFORMIXSERVER. Podemos usar isto por exemplo para ter um "alarmprogram" comum, que pode ter por exemplo diferentes tipos de configuração de backups.
    RecomendaçõesINFORMIXDIRNormalmente instalo o produto numa localização que indique claramente a versão (/usr/informix/srvr1210fc7w1), mas depois utilizo um script (ixvirtdir) para criar um $INFORMIXDIR "especial" para cada instância. O script recebe alguns parâmetros como o $INFORMIXDIR existente (da instalação) e o novo (que normalmente defino com um sufixo igual ao INFORMIXSERVER). Opcionalmente posso dar os grupos de sistema operativo a que correspondem os DBSA, AAO e DBSSOs para a separação de funções.
    O INFORMIXDIR "especial" será um novo diretório onde serão criados links simbólicos para os sub-diretórios existentes dentro de $INFORMIXDIR, com algumas exceções. Vejamos um exemplo:

    castelo@primary:informix-> echo $INFORMIXSERVER $INFORMIXDIR
    castelo /opt/informix/srvr1210fc7w1_castelo
    castelo@primary:informix-> pwd
    /opt/informix/srvr1210fc7w1_castelo
    castelo@primary:informix-> ls -l
    total 84
    drwxrwx--- 2 informix ixaao 4096 Aug 18 02:46 aaodir
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 bin -> /opt/informix/srvr1210fc7w1/bin
    drwxrwx--- 2 informix ixdbsso 4096 Aug 1 13:44 dbssodir
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 demo -> /opt/informix/srvr1210fc7w1/demo
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 doc -> /opt/informix/srvr1210fc7w1/doc
    drwxrwxr-x 4 informix ixdbsa 4096 Aug 23 11:10 etc
    lrwxrwxrwx 1 informix informix 34 Nov 13 03:21 extend -> /opt/informix/srvr1210fc7w1/extend
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 gls -> /opt/informix/srvr1210fc7w1/gls
    lrwxrwxrwx 1 informix informix 33 Nov 13 03:21 gskit -> /opt/informix/srvr1210fc7w1/gskit
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 help -> /opt/informix/srvr1210fc7w1/help
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 incl -> /opt/informix/srvr1210fc7w1/incl
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 isa -> /opt/informix/srvr1210fc7w1/isa
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 lib -> /opt/informix/srvr1210fc7w1/lib
    lrwxrwxrwx 1 informix informix 35 Nov 13 03:21 license -> /opt/informix/srvr1210fc7w1/license
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 msg -> /opt/informix/srvr1210fc7w1/msg
    lrwxrwxrwx 1 informix informix 38 Nov 13 03:21 properties -> /opt/informix/srvr1210fc7w1/properties
    lrwxrwxrwx 1 informix informix 35 Nov 13 03:21 release -> /opt/informix/srvr1210fc7w1/release
    lrwxrwxrwx 1 informix informix 31 Nov 13 03:21 SDK -> /opt/informix/srvr1210fc7w1/SDK
    lrwxrwxrwx 1 informix informix 32 Nov 13 03:21 snmp -> /opt/informix/srvr1210fc7w1/snmp
    drwxrwx--- 3 informix ixdbsa 4096 Aug 1 13:44 ssl
    drwxrwx--- 2 informix informix 4096 Aug 23 11:10 tmp

    Portanto, o INFORMIXDIR é /opt/informix/srvr1210fc7w1_castelo. "castelo" é o INFORMIXSERVER que atúa como sufixo para se identificar facilmente a que instância pertence. A maioria dos sub-diretórios dentro do $INFORMIXDIR são apenas links simbólicos para a instalação base do produto. As exceções são "aaodir", "dbssodir", "etc", "ssl" e "tmp". Estes são criados como sub-diretórios por várias razões. Num ambiente cluster o INFORMIXDIR "virtual" pode ser um recurso de cluster e a instalação base seria ou um recurso de cluster (no caso se haver apenas uma instância, ou se a movimentação for feita em bloco), ou uma instalação local em cada nó. Note-se que ao criar aquela lista específica de sub-diretórios como sub-diretórios reais, estou a isolar tudo o que pode variar de uma instância para outra. Note-se também que tipicamente estes diretórios não deverão requerer muito espaço, com a possível exceção do "tmp" se estiver definido como DUMPDIR por exemplo.
    Com este método podemos alcançar o máximo de flexibilidade e isolamento das instâncias para as seguintes situações:
    1. Várias instâncias num só servidor
    2. Ambiente de cluster, mesmo com várias instâncias que não sejam necessariamente movidas em bloco para outro(s) nós
    3. Completa separação de papéis em cada instância, permitindo diferentes grupos para diferentes instâncias
    $HOME do utilizador Informix A $HOME do utilizador informix (vamos assumir /home/informix) deverá conter duas diretorias:
    "bin" e "etc".  Na "bin", coloco todos os scripts que utilizo para gestão das instâncias, incluindo o alarmprogram.sh. Na "etc" coloco todos os ficheiros ou scripts de configuração como:
    • sqlhosts (ficheiro único por máquina) facilita as configurações para queries distribuídas etc.
    • Ficheiro de configuração do meu script que permite posicionar o ambiente de cada instância (setinfx)
    • Ficheiro de configuração do alarmprogram.sh onde cada secção específica para cada instância é permitido por uma estrutura case/esac
    Esta configuração não resolve os problemas de cluster. Num ambiente de cluster, assmindo que todos os nós podem ter alguma(s) instãncia(s) a correr ao mesmo tempo a estrutura da $HOME do informix tem de ser replicada pelos nós. No entanto se mover as instâncias em bloco, o diretório pode ser um recurso de cluster (ou pelo menos as duas referidas sub-diretorias). Em qualquer caso o "bin" tem de ser colocado na variável de ambiente $PATH.
    Deve ser evitada a duplicação dos ficheiros de configuração (um por cada nó do cluster), nomeadamente do $INFORMIXSQLHOSTS, para que baste alterar num só nó. Uma hipótese para isto seria criar links simbólicos para um ficheiro num recurso de cluster
    Log files and othersCoisas como o online.log, ficheiros de atividade e debug do onbar, ixbar.* etc. devem ser colocados num recurso de cluster que seja movido com a instância. Num ambiente não cluster estes ficheiros podem ser colocados por exemplo num sub-diretório da $HOME do informix como /home/informix/logs e /home/informix/conf
    Mesmo que os ficheiros sejam criados num recurso de cluster associado à instâncias, prefiro criá-los com um sufixo como $INFORMIXSERVER (ex: online_$INFORMIXSERVER.log, ixbar.$INFORMIXSERVER etc.
    É importante sublinhar a importância de ter apenas um IXBAR no cluster. Caso contrário iremos acabar com o catálogo espalhado pelo cluster o que tornará o processo de restore muito difícil ou impossível.
    Chunks e linksPara os chunks ainda continuo a preferir usar links simbólicos. Estes podem apontar para cooked files ou RAW. Tantos os links como os ficheiros (RAW ou cooked) devem ser configurados como um recurso de cluster.
    Diretórios de DUMP Existem alguns diretórios que só são usados esporadicamente mas que são muito importantes. Um é usado na configuração do parâmetro DUMPDIR, onde os ficheiros de assert fails são escritos. De alguma forma semelhantes são os parâmetros RESTORE_POINT_DIR, DRLOSTFOUND e PLOG_OVERFLOW_PATH.
    Estes são importantes em certas ocasiões ou actividades, mas normalmente após resolvido o problema os ficheiros podem ser removidos. Portanto, é importante que as suas localizações tenham espaço suficiente e sejam diretórios existentes, mas não é assim tão relevante se são específicos de uma instância ou uma só localização em cluster.
    Copyright