Monday, December 24, 2012

Execution plans on the client / Planos de execução nos clientes

This article is written in Englsih and Portuguese
Este artigo está escrito em Inglês e Português

English version

Introdution

Life is full of coincidences... Because IIUG announced the 2013 IIUG user's conference, to be held in Sand Diego on 21-25 April, I spent some time reviewing my 2010 presentation. The topic was "Use EXPLAIN_SQL with any Tool" and at the time I was proud of some of the achievements I reach. The basic idea was very simple: Use the function EXPLAIN_SQL() introduced in version 11.10 to obtain a query plan in XML format. Then use the engine XML capabilities to apply an XML transformation style sheet (XSLT) to finally get the plan in a manner that could be presented in any tool. Truth is, that although I think the idea is great, the fact that IBM never documented EXPLAIN_SQL properly, my lack of knowledge in XML technology (I know the basics, but I'm not efficient with it), and some technical issues, lead the idea to a stop... No progress in nearly 3 years. This triggered me to think about another approach... Because the basic problem still exists: It's not easy for a programmer with no access to the database server host machine to obtain the query plans.
But I was talking about coincidences... While I was thinking about this issue once again, I receive almost simultaneously two direct inquires about the use of EXPLAIN_SQL (from people who saw references to my presentation).
So, then what is this article about? Well... I think I finally managed to get a way to obtain the query plans on any client in an easy way, almost with no limitations or constraints... That's what I'm going to explain now

Basics

By default Informix writes the query plans to a file in the database server host's filesystem. The default file name is $HOME/sqexplain.out if the user asks for the plan. If it's the DBA that activates the plan generation then the default filename will be $HOME/sqexplain.out.SID where "SID" is the session ID.
The concept was created when most programmers and users were directly connected through a terminal window (Telnet or SSH) to the database server host. Currently this is not the case in many situations. And then you need some mechanism to move the file to the client machine, and open that file. That's technically a mess. And even if you set it up, it's awkward to work with it.
In version 11.10 IBM introduced a function called EXPLAIN_SQL() that allows a client tool to send the query and get back the query execution plan in XML. Then the tool should process this XML and present the query plan in some format (ideally in a graphic form). The problems are that the function inputs were never properly documented, the XML that it outputs is complex, and nearly no tools are able to do this (IBM Optim Data Studio can do it, but I'm not aware of any other tool which is able to implement this). What I explained in the IIUG presentation was a prototype on how to do that to get the query plan in the traditional text form result.
Another improvement introduced in version 10 was the ability to choose the filename. Besides the old SET EXPLAIN ON, version 10 also supported SET EXPLAIN FILE TO "path/name". This allowed for some custom tricks like having a shared filesystem between the server and the clients. But still this is far from a good solution.
Finally, version 10 also introduced the ability to SET EXPLAIN ON AVOID_EXECUTE which will generate the query plan but will not execute the SQL statement.

New solution

Given all the issues with EXPLAIN_SQL I tried to imagine a simple way to retrieve the file from the server and present it in the client. Version 11.70.FC6 introduced some new functions that allow us to read from a file, and this would allow a custom procedure to retrieve the file and return it as a LVARCHAR or CLOB. I implemented a prototype with this, but although it worked is was a bit complex and would require version 11.70.FC6. So I kept trying to reach a more generic solution that would work on most Informix versions. And I believe I got it.
It comes in the form of three or four procedures in SPL. The purpose of these procedures is to emulate the basic functionality of activating the explain, with or without executing, resetting the explain file, and obtain the file. The procedure are presented in the end of the article and I'm going to write a few comments about each of them:

  • set_explain_on()
    • Activates the explain output and resets the explain file. The explain filename is controlled by the DBA, not the user. So they can be put on a specific location
    •  I declare two global variables (available through the user session). One for the explain file name and the other to remember is the user used AVOID_EXECUTE or not
    • I create the explain file name (without PATH). Here I'm concatenating the username and it's session. This rule simulates the situation where a DBA runs onmode -Y. There are pros and cons about this. The good thing is that a user with different sessions can capture different explains. The bad thing is that the filename will be almost unique. So a method of cleaning them up should be implemented. It can be a scheduler task that calls a script or a crontab script... Just look for files where the SID does not exist on the instance
    • Then I create the complete filename. Here I'm using /tmp as the file location but that's not a very good idea. It would be better to have a separate filesystem (or directory with quota). It needs to be written by all. Remember that you should be able to eliminate files on that directory to implement the cleaning script. Otherwise you could just use a single file name for each user. So the file would be re-used. Again, quotas are important to avoid that a user grabs all the space
    • Then we clear the file using a SYSTEM command. This is a major difference from the SET EXPLAIN ON statement. This statement will append to the file. The reason why I clean it, is because when I retrieve the file, I send it to the client side. I don't process it to obtain just the last query.
    • Finally I define the explain file and activate the explain (here without the AVOID_EXECUTE)
    • This procedure would be the replacement for SET EXPLAIN ON
  • set_explain_on_avoid_execute()
    • Exactly the same thing but in this case the statement SET EXPLAIN ON AVOID EXECUTE is used. Same rules for file and file reset
  • reset_explain()
    • This is not really needed. It just resets the explain file (clears its content). In practice calling the set_explain_on() or set_explain_on_avoid_execute() again has the same effect
    • Used just to clear the file. You need to previously have called set_explain_on() or set_explain_on_avoid_execute()
    • This is not really need as calling one of the functions that starts explain has the same practical effect. But for clarity I decided to create it. It can be used between two query execution and explain file request to clear the explain file, so that on the second call the first query plan is not returned
  • get_explain()
    • This is the juicy part... It retrieves the file and returns it as a CLOB. In dbaccess it will not be very readable, but with GUI tools like Optim Data Studio, SQL Squirrel, AGS Server Studio etc. it works perfectly
    • It uses the native Informix function FileToCLOB() to read the explain file and return a CLOB containing it
    • Most tools I tested this on (Squirrel SQL, Server Studio...) will return this as a clickable result set. Once you click it, the explain will be showed in the tool in a separate window

Usage

As an example, we can try this on stores database with the following simple instructions (it assumes the functions were already created):

ids_117fc6@centaurus.onlinedomus.net:informix-> cat test_explain.sql
EXECUTE PROCEDURE set_explain_on();
SELECT * FROM customer WHERE customer_num = 101;
EXECUTE FUNCTION get_explain();
EXECUTE PROCEDURE reset_explain();
SELECT COUNT(*) FROM customer c, orders o WHERE o.customer_num = c.customer_num AND c.state = 'CA';
EXECUTE FUNCTION get_explain();
ids_117fc6@centaurus.onlinedomus.net:informix-> cat test_explain.sql
EXECUTE PROCEDURE set_explain_on();
SELECT * FROM customer WHERE customer_num = 101;
EXECUTE FUNCTION get_explain();
EXECUTE PROCEDURE reset_explain();
SELECT COUNT(*) FROM customer c, orders o WHERE o.customer_num = c.customer_num AND c.state = 'CA';
EXECUTE FUNCTION get_explain();
ids_117fc6@centaurus.onlinedomus.net:informix-> dbaccess stores test_explain.sql

Database selected.


Routine executed.




customer_num  101
fname         Ludwig
lname         Pauli
company       All Sports Supplies
address1      213 Erstwild Court
address2      
city          Sunnyvale
state         CA
zipcode       94086
phone         408-789-8075

1 row(s) retrieved.




explain  

QUERY: (OPTIMIZATION TIMESTAMP: 12-19-2012 01:12:59)
------
SELECT * FROM customer WHERE customer_num = 101

Estimated Cost: 1
Estimated # of Rows Returned: 1

1) informix.customer: INDEX PATH

(1) Index Name: informix. 100_1
Index Keys: customer_num   (Serial, fragments: ALL)
Lower Index Filter: informix.customer.customer_num = 101 


Query statistics:
-----------------

Table map :
----------------------------
Internal name     Table name
----------------------------
t1                customer

type     table  rows_prod  est_rows  rows_scan  time       est_cost
-------------------------------------------------------------------
scan     t1     1          1         1          00:00.00   1       



1 row(s) retrieved.


Routine executed.



(count(*)) 

15

1 row(s) retrieved.




explain  

QUERY: (OPTIMIZATION TIMESTAMP: 12-19-2012 01:12:59)
------
SELECT COUNT(*) FROM customer c, orders o WHERE o.customer_num = c.customer_num AND c.state = 'CA'

Estimated Cost: 5
Estimated # of Rows Returned: 1

1) informix.c: SEQUENTIAL SCAN

Filters: informix.c.state = 'CA' 

2) informix.o: INDEX PATH

(1) Index Name: informix. 102_4
Index Keys: customer_num   (Key-Only)  (Serial, fragments: ALL)
Lower Index Filter: informix.o.customer_num = informix.c.customer_num 
NESTED LOOP JOIN


Query statistics:
-----------------

Table map :
----------------------------
Internal name     Table name
----------------------------
t1                c
t2                orders

type     table  rows_prod  est_rows  rows_scan  time       est_cost
-------------------------------------------------------------------
scan     t1     18         3         28         00:00.00   4       

type     table  rows_prod  est_rows  rows_scan  time       est_cost
-------------------------------------------------------------------
scan     t2     15         23        15         00:00.00   0       

type     rows_prod  est_rows  time       est_cost
-------------------------------------------------
nljoin   15         3         00:00.00   5       

type     rows_prod  est_rows  rows_cons  time
-------------------------------------------------
group    1          1         15         00:00.00



1 row(s) retrieved.


Database closed.


This was run in dbaccess. But in order to see the full potential you should try it on a GUI based tool like ServerStudio, Squirrel SQL, Aqua Data Studio, DBVisualizer etc.

Additional notes


My first feeling is that this article should not be necessary. It's about time that this problem had a "native" solution, although most people I found still like character based, local access using dbaccess. On the other hand, I always find it interesting to show how easy it is to solve some of the "traditional" issues.
This method raises some problems which are easy to solve, but if you think about using it, it's better to solve them in advance:
  1. Where should we create these procedures and function? If you only have one database in your instance that's easy. But typically customers have several databases in a single instance. The answer is that you can create them in any database and call them referencing the external database (same logging assumed), or if you prefer you can create them in all the databases.
  2. If the location of the explain files is to be a single location for all users, then it must be writable by all users. This may raise some issues, and you should avoid them. The issues that came to mind are:
    1. A user with enough privileges could try to flood the explain file or create a new file in an attempt to fill the filesystem and cause some DoS attack.
    2. Because the location has to be writable by all users, a user with resource privileges could create a procedure to remove all the files
    3. A user can potentially obtain other user's query plan files.
    4. You must be able to remove the files with an administrative user (like informix)

      The solution is to create an informix owned directory writable by anyone (this solves issue 4). You should configure quotas to avoid issue 1. If you're concerned about issue 2 and 3, then you can solve them using filesystem ACLs. And you can have some random factor for the filename... The user doesn't need to know the location of the files neither the file name... Of course, the user can get them from the global variables (assuming their names are "public") and it can retrieve the shared location from the procedure's code
      So if you want it bullet proof use quotas and ACLs. Another option would be to write the explain files in each user $HOME (like informix does by default), but that raises yet another potential issue. The argument for FileToCLOB must be a full pathname. So if all your user's $HOME is created in /home (or any other global location) this would not be a problem. The point is that you can't use the location "~user/" or "./
      Removing the files could be done in a script (as simple as removing all the files older than today), which could be called by the database scheduler, cron, or any other scheduler. You could also create a sysdbclose() procedure to clean up the explain file created
  3. Because the explain file is cumulative, get_explain() will always retrieve the full explain history. reset_explain() can solve this. And we could of course replace FileToCLOB by a custom C code function that returns just the last query plan.
  4. The FileToCLOB() function needs a smart BLOB space to be present and configured in SBSPACENAME $ONCONFIG parameter. If you miss this, an error 9810/12053 will be raised
Having all these in mind, I do think this is a very feasible solution for a very old Informix issue.
I would still like to see EXPLAIN_SQL being more documented and explored. The idea of transforming an XML containing the explain into whatever you prefer (explain using other databases format for example) really pleases me. But It's much more difficult to implement. This method is terribly simple and should work with any version equal or newer to 10 (so any currently fully supported version)

    Versão Portuguesa

    Introdução

    A vida está cheia de coincidências... Devido ao anúncio que o IIUG fez da conferência de utilizadores de 2013 IIUG, que terá lugar em São Diego, de 21 a 25 de Abril, passei algum tempo a rever a minha apresentação de 2010. O tema era  "Use EXPLAIN_SQL with any Tool" e na altura estava bastante orgulhoso dos progressos que tinha alcançado. A ideia base era muito simples: Usar a função EXPLAIN_SQL introduzida na versão 11.10 para obter o plano de execução em formato XML. Depois usar as capacidades de XML do motor para aplicar uma transformação usando uma transformation style sheet (XSLT), para finalmente obter o plano numa forma que pudesse ser apresentado em qualquer ferramenta. A verdade é que, apesar de ainda achar que a ideia era boa, o facto de a IBM nunca ter documentado a função EXPLAIN_SQL com detalhe, a minha falta de conhecimento em tecnologia XML (sei o básico, mas não sou eficiente a lidar com a tecnologia), e mais alguns problemas técnicos levaram a ideia a um beco sem saída... Sem progresso em quase três anos.
    Isto incentivou-me a pensar noutra abordagem... Porque o problema de fundo ainda existe: Não é fácil a um programador sem acesso à máquina da base de dados obter os planos de execução.
    Mas estava a falar de coincidências... Enquanto pensava sobre este tema novamente, recebi quase em simultâneo duas questões diretas sobre o uso da função EXPLAIN_SQL (de pessoas que terão visto referências para a minha apresentação).
    Portanto, sobre o que é este artigo? Bom... Julgo que finalmente criei uma maneira de obter o plano de execução no cliente, de uma forma fácil e praticamente sem limitações... É o que vou explicar de seguida.

    Principios báasicos

    Por omissão o Informix escreve os planos de execução em ficheiro no servidor de base de dados. O nome pré-definido do ficheiro é $HOME/sqexplain.out se o utilizador pedir para ser gerado o plano. Se for o DBA a ativar a geração dos planos de execução, usando o onmod -Y então o nome será $HOME/sqlexplain.out.SID onde "SID" será o session ID.
    Esta forma de trabalhar foi pensada quando a maioria dos programadores e utilizadores estavam diretamente ligados através de uma consola (Telnet ou SSH) ao servidor de base de dados. Atualmente a situação já não é esta na maioria dos casos. E assim é necessário um mecanismo para mover o ficheiro para a máquina cliente, e depois o utilizador terá de o abrir. Tecnicamente é uma confusão e é um processo arcaico para se trabalhar.
     Na versão 11.10 a IBM introduziu uma função chamada EXPLAIN_SQL() que permite a uma ferramenta cliente enviar uma query e receber o plano de execução em formato XML. Depois a ferramenta tem de processar esse XML para apresentar o plano de execução numa forma legível (preferencialmente de forma gráfica). Os problemas são que os inputs da função nunca foram devidamente documentados,, o XML resultante é complexo, e praticamente nenhuma ferramenta é capaz de usar esta funcionalidade (o IBM Optim Data Studio implementa-a, mas não tenho conhecimento de mais nenhum caso). O que apresentei na sessão da conferência do IIUG foi um protótipo de como usar esta função para obter o plano de execução num formato de texto (que seria utilizável em qualquer ferramenta).
    Outra melhoria, introduzida na versão 10 foi a possibilidade de escolher outro nome de ficheiro para escrever os planos de execução. Para além do tradicional SET EXPLAIN ON, a versão 10 introduziu o SET EXPLAIN FILE TO "caminho/ficheiro". Isto abriu a porta a alguns truques como ter um sistema de ficheiros partilhado entre o servidor de base de dados e os clientes. Mas mesmo assim ainda está longe de ser uma boa solução.
    Por último a versão 10 também introduziu a possibilidade de executar SET EXPLAIN ON AVOID_EXECUTE, o que permite gerar o plano de execução sem efetivamente correr a query.

    Nova solução

    Dados todos os problemas com a função EXPLAIN_SQL(), eu tentei imaginar uma forma simples de obter o ficheiro do servidor para o cliente. E a versão 11.70.xC6 introduziu algumas novas funções que nos permitem ler de um ficheiro e apresentar o resultado no cliente. Isto permitiria criar uma função que lesse o ficheiro e retornasse um LVARCHAR ou CLOB. Cheguei a implementar um protótipo com estas funções, mas apesar de funcionar seria um pouco complexo e necessitava da versão 11.70.xC6. Por isso continuei a tentar obter uma solução mais genérica que funcionasse com a maioria das versões de Informix. E penso que consegui.
    Aparece sob a forma de três ou quatro procedimentos em SPL. O objetivo destes procedimentos é emular a funcionalidade básica de ativar a escrita de planos de execução, com ou sem execução das queries, reinicialização do ficheiros de planos e obtenção desse mesmo ficheiro. O código destes procedimentos pode ser encontrado no final do artigo e vou comentar alguns pontos:
    • set_explain_on()
      • Ativa a escrita dos planos de execução e re-inicializa o ficheiro. O nome do ficheiro é controlado pelo DBA, não pelo utilizador. Podem assim ser colocados num local específico e controlado
      • Declaro duas variáveis globais (disponíveis em toda a sessão do utilizador). Uma para o nome do ficheiro de planos e a outra para manter informação sobre o uso do AVOID_EXECUTE
      • Defino o nome do ficheiro (sem caminho). Estou a concatenar o nome do utilizador com o número da sessão. Esta regra simula o comportamento obtido quando é o DBA a ativar a geração de planos. Há vantagens e desvantagens nesta abordagem. A vantagem é que um utilizador com mais que uma sessão pode obter informação independente de cada uma. A desvantagem é que o nome do ficheiro será praticamente único. Portanto haverá que criar um método de limpeza. Pode ser uma tarefa escalonada no próprio motor de base de dados (que chame um script) ou algo colocado no crontab... Bastará procurar por ficheiros cujo sufixo (SID) não tenha correspondência nas sessões de base de dados
      • Depois defino o nome completo do ficheiro. Aqui estou a usar /tmp, mas isso não será boa ideia. Seria melhor ter um filesystem (ou pasta com quota). Necessita de estar aberto para escrita por todos os utilizadores. Lembre-se que deverá conseguir eliminar os ficheiros criados por qualquer utilizador para implementar o método de limpeza.
        Outra opção seria usar um único ficheiro para cada utilizador. Assim o ficheiro seria sempre re-utilizado. Mesmo assim o uso de quotas é importante para evitar que um utilizador esgote todo o espaço
      • Depois o ficheiro é limpo recorrendo à instrução SYSTEM. Isto é uma grande diferença em relação à instrução SET EXPLAIN ON. Esta instrução adiciona ao conteúdo do ficheiro caso já exista. A razão porque o limpo é porque ao pedir o ficheiro, todo o seu conteúdo é enviado para o cliente. Não processo o conteúdo à procura apenas da última query
      • Finalmente defino o nomes do ficheiro e ativo a escrita dos planos (aqui sem o AVOID_EXECUTE)
      • Este procedimento seria o substituto da instrução SET EXPLAIN ON
    • set_explain_on_avoid_execute()
      • Exactamente igual ao de cima, mas aqui usando o SET EXPLAIN ON AVOID_EXECUTE. As mesmas regras para o ficheiro e a re-inicialização do mesmo
    • reset_explain()
      • Não será realmente necessário. Apenas re-inicializa o ficheiro onde são escritos os planos de execução. Na prática, chamar o set_explain_on() ou set_explain_on_avoide_execute() novamente tem o mesmo efeito. Mas decidi criá-lo por clareza. Pode ser usado entre execuções de queries diferentes para que na segunda chamada ao procedimneto que devolve o plan, vir apenas o da segunda query.
      • Usado apenas para limpar o ficheiro. Terá de ter chamado antes o set_explain_on() ou o set_explain_on_avoid_execute()
    • get_explain()
      • É o ponto fulcral do mecanismo. Usa a função nativa do Informix FileToCLOB() para ler o ficheiro de planos e retornar um CLOB que o contém.
      • Na maioria das ferramentas em que testei (Squirrel SQL, Server Studio....) o resultado é apresentado como uma célula onde se pode clicar. Depois de o fazer o plano de execução será mostrado numa janela à parte

    Utilização

    Como exemplo, podemos tentar usar isto na base de dados stores usando as seguintes instruções (pressupõe que as funções já tenham sido criadas):
     ids_117fc6@centaurus.onlinedomus.net:informix-> cat teste_explain.sql
    EXECUTE PROCEDURE set_explain_on();
    SELECT * FROM customer WHERE customer_num = 101;
    EXECUTE FUNCTION get_explain();
    EXECUTE PROCEDURE reset_explain();
    SELECT COUNT(*) FROM customer c, orders o WHERE o.customer_num = c.customer_num AND c.state = 'CA';
    EXECUTE FUNCTION get_explain();
    ids_117fc6@centaurus.onlinedomus.net:informix-> cat test_explain.sql
    EXECUTE PROCEDURE set_explain_on();
    SELECT * FROM customer WHERE customer_num = 101;
    EXECUTE FUNCTION get_explain();
    EXECUTE PROCEDURE reset_explain();
    SELECT COUNT(*) FROM customer c, orders o WHERE o.customer_num = c.customer_num AND c.state = 'CA';
    EXECUTE FUNCTION get_explain();
    ids_117fc6@centaurus.onlinedomus.net:informix-> dbaccess stores teste_explain.sql
    
    Database selected.
    
    
    Routine executed.
    
    
    
    
    customer_num  101
    fname         Ludwig
    lname         Pauli
    company       All Sports Supplies
    address1      213 Erstwild Court
    address2      
    city          Sunnyvale
    state         CA
    zipcode       94086
    phone         408-789-8075
    
    1 row(s) retrieved.
    
    
    
    
    explain  
    
    QUERY: (OPTIMIZATION TIMESTAMP: 12-19-2012 01:12:59)
    ------
    SELECT * FROM customer WHERE customer_num = 101
    
    Estimated Cost: 1
    Estimated # of Rows Returned: 1
    
    1) informix.customer: INDEX PATH
    
    (1) Index Name: informix. 100_1
    Index Keys: customer_num   (Serial, fragments: ALL)
    Lower Index Filter: informix.customer.customer_num = 101 
    
    
    Query statistics:
    -----------------
    
    Table map :
    ----------------------------
    Internal name     Table name
    ----------------------------
    t1                customer
    
    type     table  rows_prod  est_rows  rows_scan  time       est_cost
    -------------------------------------------------------------------
    scan     t1     1          1         1          00:00.00   1       
    
    
    
    1 row(s) retrieved.
    
    
    Routine executed.
    
    
    
    (count(*)) 
    
    15
    
    1 row(s) retrieved.
    
    
    
    
    explain  
    
    QUERY: (OPTIMIZATION TIMESTAMP: 12-19-2012 01:12:59)
    ------
    SELECT COUNT(*) FROM customer c, orders o WHERE o.customer_num = c.customer_num AND c.state = 'CA'
    
    Estimated Cost: 5
    Estimated # of Rows Returned: 1
    
    1) informix.c: SEQUENTIAL SCAN
    
    Filters: informix.c.state = 'CA' 
    
    2) informix.o: INDEX PATH
    
    (1) Index Name: informix. 102_4
    Index Keys: customer_num   (Key-Only)  (Serial, fragments: ALL)
    Lower Index Filter: informix.o.customer_num = informix.c.customer_num 
    NESTED LOOP JOIN
    
    
    Query statistics:
    -----------------
    
    Table map :
    ----------------------------
    Internal name     Table name
    ----------------------------
    t1                c
    t2                orders
    
    type     table  rows_prod  est_rows  rows_scan  time       est_cost
    -------------------------------------------------------------------
    scan     t1     18         3         28         00:00.00   4       
    
    type     table  rows_prod  est_rows  rows_scan  time       est_cost
    -------------------------------------------------------------------
    scan     t2     15         23        15         00:00.00   0       
    
    type     rows_prod  est_rows  time       est_cost
    -------------------------------------------------
    nljoin   15         3         00:00.00   5       
    
    type     rows_prod  est_rows  rows_cons  time
    -------------------------------------------------
    group    1          1         15         00:00.00
    
    
    
    1 row(s) retrieved.
    
    
    Database closed.
    
    
    Isto foi executado no dbaccess. Mas para verificar todo o potencial, deve testar numa ferramenta com interface gráfica como ServerStudio, Squirrel SQL, Aqua Data Tudio, DBVisualizer etc.

    Notas adicionais

    Gostaria de afirmar que este artigo não deveria ser necessário. Já é tempo de que este problema tenha uma solução "nativa". Isto apesar de a maioria das pessoas que encontro ainda gostar do acesso local, baseado em caracter como o dbaccess. Por outro lado acho sempre interessante mostrar como é fácil resolver alguns destes "problemas tradicionais".
    Este método levanta alguns problemas que são fáceis de resolver, mas se pensar usá-lo, é melhor resolvê-los antecipadamente:
    1. Onde devemos criar os procedimentos e função? Se apenas tiver uma base de dados na sua instância será fácil. Mas tipicamente os clientes possuem mais que uma base de dados em cada instância. A resposta é que pode criá-los em qualquer base de dados e chamá-las referenciando a base de dados externa (assumindo que o modo de logging é o mesmo), ou se preferir pode criá-los em todas as bases de dados
    2. Se a localização dos ficheiros for uma localização única para todos os utilizadores, então terá de ter permissões de escrita para todos eles. Isto pode leavantar alguns problemas, e deverá evitá-los. Os problemas que me ocorrem são:
      1. Um utilizador com os privilégios adequados poderia tentar inundar o ficheiro de planos ou ir criando novos ficheiros numa tentativa de encher o filesystem
      2. Porque a localização tem de permitir escrita por todos, um utilizador com privilégio resource poderá criar um procedimento para apagar os ficheiros de outros utilizadores
      3. Um utilizador potencialmente pode obter ficheiros de outros utilizadores
      4. Tem de poder remover os ficheiros com um utilizador de administração (como informix)

        A solução passa por criar uma pasta com dono informix, e permissões de escrita para todos os utilizadores. (resolve o problema 4). Deverá configurar quotas para evitar o problema 1. Se estiver preocupado com os assuntos 2 e 4, poderá resolvê-los usando ACLs de filesystem. E pode introduzir algum factor aleatório na construção do nome de ficheiro...  O utilizador não necessita de saber nem a localização dos ficheiros nem o nome dos ficheiros. Naturalmente o utilizador pode obter isto pelas variáveis globais (assumindo que os seus nomes são "publicos") e pode também saber a localização pela análise do código dos procedimentos.
        Portanto se quer algo à prova de bala tem de usar quostas e ACLs. Outra opção seria escrever os ficheiros em cada $HOME dos utilizadores (como o informix faz por omissão) mas isso levanta ainda outro problema: O argumento da função FileToCLOB tem de ser um caminho absoluto. Assim, se todos os utilizadores tiverem o $HOME definido para /home (ou qualquer outra localização) isto não seria um problema. O ponto é que não pode usar como localização não pode ser "~user" ou "./"
        Remover os ficheiros pode ser feito num script (tão simples como remover os ficheiros com mais que um dia), que poderá ser chamado pelo scheduler da base de dados, cron ou qualquer outro scheduler. Poderiam também criar um procedimento sysdbclose() para limpar o ficheiro de planos que tenha sido criado

    3. Porque o ficheiro de planos é cumulativo, get_explain() irá sempre retornar toda a história. reset_explain() pode resolver isto. E pode claro substituir FileToCLOB() por uma função em C que retorne apenas o último plano contido no ficheiro
    4. A função FileToCLOB requer que o sistema tenha um smart BLOB space, e que esteja configurado no parâmetro SBSPACENAMEno ficheiro $ONCONFIG . Caso tal não aconteça será retornado um erro 9810/12053
    Tendo tudo isto em consideração acredito que esta seja uma solução fácil para um antigo problema com o Informix
    Ainda gostaria de ver o EXPLAIN_SQL bem documentado e explorado. A ideia de transformar o XML contendo o plano de execução no que quer que preferiss (plano usando a nomenclatura ou aspecto de outras bases de dados por exemplo) agrada-me verdadeiramente. Mas é muito mais difícil de implementar. Ao contrário este método é muito simples e deverá funcionar com qualquer versão dos servidor a partir da 10 (portanto qualquer versão actualmente suportada)

    The code/O código

    1  CREATE PROCEDURE set_explain_on()
    2  DEFINE GLOBAL explain_file_name VARCHAR(255) DEFAULT NULL;
    3  DEFINE GLOBAL explain_execute BOOLEAN DEFAULT NULL;
    4  DEFINE exp_file, sys_cmd VARCHAR(255);
    5       LET explain_file_name = USER||'.'||DBINFO('sessionid');
    6       LET exp_file = '/tmp/'||explain_file_name;
    7       LET sys_cmd='cat /dev/null > '||exp_file;
    8       SYSTEM(sys_cmd);
    9       SET EXPLAIN FILE TO exp_file;
    10      SET EXPLAIN ON;
    11      LET explain_execute = 't';
    12  END PROCEDURE;

    1  CREATE PROCEDURE set_explain_on_avoid_execute()
    2  DEFINE GLOBAL explain_file_name VARCHAR(255) DEFAULT NULL;
    3  DEFINE GLOBAL explain_execute BOOLEAN DEFAULT NULL;
    4  DEFINE exp_file, sys_cmd VARCHAR(255);
    5    LET explain_file_name = USER||'.'||DBINFO('sessionid');
    6    LET exp_file = '/tmp/'||explain_file_name;
    7    LET sys_cmd='cat /dev/null > '||exp_file;
    8    SYSTEM(sys_cmd);
    9    SET EXPLAIN FILE TO exp_file;
    10   SET EXPLAIN ON AVOID_EXECUTE;
    11   LET explain_execute = 'f';
    12 END PROCEDURE;

    1  CREATE PROCEDURE reset_explain()
    2  DEFINE GLOBAL explain_file_name VARCHAR(255) DEFAULT NULL;
    3  DEFINE GLOBAL explain_execute BOOLEAN DEFAULT NULL;
    4  DEFINE exp_file,sys_cmd VARCHAR(255);
    5  IF explain_file_name IS NOT NULL
    6  THEN
    7    LET exp_file = '/tmp/'||explain_file_name;
    8    SET EXPLAIN OFF;
    9    LET sys_cmd='cat /dev/null > '||exp_file;
    10   SYSTEM(sys_cmd);
    11   SET EXPLAIN FILE TO exp_file;
    12   IF explain_execute = 't'
    13   THEN
    14     SET EXPLAIN ON;
    15   ELSE
    16     IF explain_execute = 'f'
    17     THEN
    18       SET EXPLAIN ON AVOID_EXECUTE;
    19     ELSE
    20       RAISE EXCEPTION -746, "Execute option of set explain is not defined!";
    21     END IF;
    22   END IF;
    23 ELSE
    24   RAISE EXCEPTION -746, "Explain file is not set!";
    25 END IF;
    26 END PROCEDURE;

    1  CREATE PROCEDURE get_explain() RETURNING CLOB AS explain;
    2  DEFINE GLOBAL explain_file_name VARCHAR(255) DEFAULT NULL;
    3  DEFINE exp_file VARCHAR(255);
    4  DEFINE v_ret CLOB;
    5
    6  IF explain_file_name IS NOT NULL
    7  THEN
    8    LET exp_file = '/tmp/'||explain_file_name;
    9    LET v_ret = FILETOCLOB(exp_file,'server');
    10   RETURN v_ret;
    11 END IF;
    12 END PROCEDURE;

    Tuesday, December 18, 2012

    11.7.FC7 is out... / versão 11.7.FC7...

    This article is written in English and Portuguese
    Este artigo está escrito em Inglês e Português

    English version

    I usually post an article about each Informix fixpack. This time we have 11.70.FC7, but this is a different situation. The latest fixpack is just a security release. You may have noticed the official announcements from IBM (https://www.ibm.com/support/docview.wss?uid=swg21618994) and CVE
    (http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-4857).
    The problem detected relates to a stack overflow issue. Note that on October another issue also caused a warning recommending the upgrade to 11.70.FC5.
    So now, to be safe you should consider moving to 11.70.FC7. If you`re still using 11.50 than you can obtain a special build on top of 11.50.FC9W2. These special fixes are available on Fix Central

    Versão Portuguesa

    Habitualmente publico um artigo sobre cada fixpack do Informix. Desta feita temos a 11.70.FC7, mas esta é uma situação diferente. Este último fixpack é apenas uma atualização de segurança. Poderá ter verificado os anúncios da IBM (https://www.ibm.com/support/docview.wss?uid=swg21618994) e CVE (http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-4857).
    O problema detetado é do tipo stack overflow. Note que em Outubro um outro problema também causou um alerta e recomendava a atualização para a versão 11.70.FC5.
    Agora,  para estar seguro deve considerar atualizar para a 11.70.FC7. Se ainda estiver a usar a 11.50 então pode obter um build especial sobre a 11.50.FC9W2. Estes builds estão disponíveis no Fix Central

    Saturday, December 15, 2012

    SSL certificates expiration / expiração de certificados SSL

    This article is written in English and Portuguese
    Este artigo está escrito em Inglês e Português


    English version:

    Introduction


    In a recent customer engagement I was questioned about a topic that to be honest I never had to work with before. It relates to SSL connections to Informix and how should we handle the certificates, in particular the fact that they can/will expire... And what happens then.
    Configuring SSL connections is a relatively simple process. It's well explained in the security manual and it's not my objective to explain it in detail here. Nevertheless, for the readers who haven't done it before here's a quick reference (if you really want to do it, read the manual!). Note that this example uses a self signed certificate, so this will be also copied to the client(s):

    • Server configuration
      1. Create a key database where the server certificates will be stored. This can be done with a command included in the IBM Global Security Kit which is bundled in the engine package:

        gsk8capicmd -keydb -create -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -pw $MYPASSWORD -type cms -stash

        The command above uses two variables that you need to set or replace by real values:
        SERVER_NAME is the value of the DBSERVERNAME parameter in $INFORMIXDIR/etc/$ONCONFIG
        MYPASSWORD is the password used to encrypt/access the keystore.

        We're creating the file of type "cms" in $INFORMIXDIR/ssl with the name of the engine and we're requesting the creation of a stash file which will allow the engine to get the password and access the keystore without having to specify the password again
        Naturally this file is security sensitive.
      2. Then we need to create a certificate file and store it in the keystore we created above. For testing purposes we're going to create a self sign certificate. Proper setups would use a CA (Certification Authority) to sign the certificates. The CA could be external or one implemented in your company (depending on the needs):

        gsk8capicmd -cert -create -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -pw ${MYPASSWORD} -label ${SERVER_NAME}_label -dn "CN=`hostname`" -size ${KEYSIZE} -default_cert yes -expire ${EXPIRE}

        The variables that need to be set or replaced by values are:

        SERVER_NAME and MYPASSWORD as explain above
        KEYSIZE to specify the key length (e.g 1024)
        EXPIRE to define the number of days that the certificate will be valid. Default should be 365 (one year)

        Note that we're defining a "label" for this certificate (${SERVER_NAME}_label) so that the engine can define which certificate it will use from the ones stored in the keystore. This label will be used in the engine configuration (see next step)
      3. Configure the label to define which certificate will be used by the engine. This is done by setting the SSL_KEYSTORE_LABEL:

        SSL_KEYSTORE_LABEL panther_label
      4. Create an entry in $INFORMIXSQLHOSTS for SSL (e.g panther_ssl) using the onsocssl protocol
      5. Configure one NETTYPE entry in $INFORMIXDIR/etc/$ONCONFIG:

        NETTYPE        onsocssl,1,20,NET
      6. Configure one (or more) virtual processor for SSL:

        VPCLASS ssl,num=1,noage
      7. Make sure that the new SSL port is configured in DBSERVERALIAS or DBSERVERNAME
      8. Extract the certificate so that it can be imported in the clients database (for self signed certificates):

        gsk8capicmd -cert -extract -db $INFORMIXDIR/ssl/${SERVER_NAME}.kdb -format ascii -label ${SERVER_NAME}_label -pw ${MYPASSWORD} -target ${SERVER_NAME}_label.cert
    •  On the client side
      1. Create the keystore on the client side:

        gsk8capicmd -keydb -create -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MYPASSWORD} -type cms -stash
      2. Import the self signed certificate that we extracted in the server side last step:

        gsk8capicmd -cert -add -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MYPASSWORD} -label ${SERVER_NAME}_label -file ${SERVER_NAME}_label.cert -format ascii

        note that the PATH specified after "-file" needs to point to the file extracted from the server keystore. You may need to FTP/SCP the file or use another copy method.
        In a proper setup you would import the CA certificate used to sign the certificates used in the servers to which the client wants to connect to
      3. Configure the client $INFORMIXSQLHOSTS file with the appropriate entry to define the SSL enabled port
      4. Create a file named $INFORMIXDIR/etc/conssl.cfg with the following content:

        SSL_KEYSTORE_FILE   /usr/informix/client_INFORMIXDIR/etc/clientdb.kdb   # Keystore file
        SSL_KEYSTORE_STH    /usr/informix/client_INFORMIXDIR/etc/clientdb.sth   # Keystore stash file

        This will tell the clients where the client side keystore and stash file are.
      5. Make sure the keystore and stash file have appropriate filesystem permissions (don't allow access to non-authorized people)

    The problem

    Once you have an SSL enabled environment you should be able to connect your clients (to which you need to make the certificate available). But then, you have the conditions to hit the problem. As you may have noticed, the certificates have an lifetime period. They're not valid before a date and after another date. By default the initial date is the time when you create them and the valid period is typically one year. What happens after that period? The customer already found that... No new connections will be possible (which is understandable since the certificate is expired), but the problem is that you cannot re-validate the certificate with the engine online. If you try to connect using an expired certificate you'll get the following error in the engine log:

    15:27:18  listener-thread: err = -28014: oserr = 0: errstr = GSK_ERROR_BAD_DATE: Secure Sockets Layer error: GSK_ERROR_BAD_DATE.

    The error message is:
    -28014    Secure Sockets Layer error.

    An error occurred during a Secure Socket Layer operation. For more information,
    refer to the accompanying IBM Global Security Kit (GSKit) error message.

    So, the problem summary is how to avoid getting into a situation where you have a running engine with an expired certificate.

    How to avoid it


    In generic terms, a way to avoid this is to keep track of the certificates expiration dates. Before they expire we should create new ones and schedule a stop of the server to make the change. Now, if you have a regular maintenance window this will be easier. But if you have a 24x7 system the stop becomes an issue. I'll split the solution into three parts:

    1. You should create a certificate that is secure enough for a specific time period. This period should be enough to accommodate enough time so that you can wait for a stop caused by another reason (OS maintenance/upgrade, a crash, a monthly/quarter/yearly stop). This will probably mean that you need to create a certificate that will last longer than the period you'd like to change it. Let's assume you establish as a policy that you'd like to exchange the certificates every 6 months. But you're only allowed to do a schedule stop once per year... So, to be safe you may want to create the certificate with 18 months before expiration. You should consider the correct key size to guarantee the security of the certificate for the interval desired. The following site may help with that:

      http://www.keylength.com/en/
    2. You need to setup everything that it's possible to do without stopping the engine, before the stop. The idea is to make the stop as short and simple as possible. So you can create a new certificate and store it in the same keystore, as long as you use a different label. This can be done before the stop while everything is working. This way, the only thing you'll have to do during the stop is to change the SSL_KEYSTORE_LABEL parameter.
    3. You must create a tracking system that will alert you with enough time, before the certificate expires. You have two ways to do this: You store each certificate ID (label for example) and it's expiration date in a database, and periodically run  a process that will alert you if any of the certificates will expire in a pre-defined period of time (e.g in the next 3 - choose your number -  months). The other option is to create an alarm that checks each server's certificate and generates some sort of alert (email, monitoring system etc.)
    In order to help with point 3 above, I have a little script that can be changed/adapted to your particular needs:

    1    #!/bin/sh
    2   
    3    SERVER_NAME=`onstat -c | grep DBSERVERNAME | grep -v "^[     ]*#" | awk '{print $2}'`
    4    SERVER_LABEL=`onstat -c | grep SSL_KEYSTORE_LABEL | grep -v "^[     ]*#" | awk '{print $2}'`
    5   
    6    MYDATE=`gsk8capicmd -cert -details -locale en -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -stashed -label ${SERVER_LABEL} | grep After | awk '{print $4 " " $5 " " $6}'`
    7   
    8    NUM_DAYS=`dbaccess sysmaster <<EOF 2>/dev/null| grep "DAYS:" | awk -F':' '{print $2+0}'
    9    select "DAYS:"||extend(TO_DATE("$MYDATE", "%B %d, %Y"), YEAR TO DAY) - extend(TODAY,YEAR TO DAY) FROM sysmaster:sysdual;
    10    EOF`
    11   
    12   
    13    if [ $NUM_DAYS -gt 0 ]
    14    then
    15        echo "Certificate is still valid for $NUM_DAYS days"
    16    else
    17        echo "Certificate has expired!"
    18    fi

    Let's leave aside the SHELL tricks and particularities. What the script it doing is pretty simple:
    1. Collects the DBSERVERNAME and SSL_KEYSTORE_LABLE $ONCONFIG parameters
    2. Calls gdk8capicmd command to display the certificate details. It extracts the line "Not After :". Note that I use the "-locale en" to define the date format.
    3. It uses Informix with an inline SQL script to calculate the number of days that the certificate still has before expiring


    Versão Portuguesa:

    Introducão

    Durante uma visita recente a um cliente fui questionado sobre um tema com o qual muito honestamente nunca tinha lidado. Está relacionado com ligações SSL ao Informix e como deveremos gerir os certificados, em particular o facto de que podem expirar... e o que acontece então.
    Configurar ligações SSL é um processo relativamente simples. Está bem explicado no manual de segurança e não é meu objetivo explicar detalhadamente aqui como o fazer. Em todo o caso, e pensando em todos os leitores que nunca o fizeram, aqui fica um guia rápido (se realmente o quiser fazer não deixe de seguir o manual). Note que este exemplo utiliza certificados auto-assinados, portanto estes são também copiados para os clientes:
    • Configuração do servidor
      1. Criar uma base de dados de chaves onde os certificados serão guardados. Isto pode ser feito com um comando incluído no IBM Global Security Kit, que é fornecido com o pacote do motor:

        gsk8capicmd -keydb -create -db $INFORMIXDIR/ssl/$NOME_SERVIDOR.kdb -pw $MINHA_PASSWORD -type cms -stash

        O comando acima utiliza duas variáveis que necessita de definir ou substituir pelo seu valor real:
        NOME_SERVIDOR é o valor do parâmetro DBSERVERNAME do $INFORMIXDIR/etc/$ONCONFIG
        MINHA_PASSWORD é a palavra chave usada para encriptar e aceder à base de dados de chaves/certificados (keystore)
        Estamos a criar o ficheiro do tipo "cms" em $INFORMIXDIR/ssl com o nome do motor e pedimos a criação de um ficheiro stash que permitirá ao motor obter a password para aceder à keystore sem que tenhamos de especificar a password novamente. Naturalmente este ficheiro é sensível do ponto de vista da segurança.
      2. Depois necessitamos de criar um ficheiro de certificado e guardá-lo na keystore que criámos. Para efeitos de teste criamos um certificado auto-assinado. Configurações reais provavelmente usariam uma CA (Certificate Authority) para assinar os certificados.
        A CA poderia ser external ou uma implementada na empresa (dependendo das necessidades)

        gsk8capicmd -cert -create -db $INFORMIXDIR/ssl/${NOME_SERVIDOR}.kdb -pw ${MINHA_PASSWORD} -label ${NOME_SERVIDOR}_label -dn "CN=`hostname`" -size ${TAMANHO_CHAVE} -default_cert yes -expire ${EXPIRA}

        As variáveis que necessitam de ser substituidas por valores são:

        NOME_SERVIDOR and MINHA_PASSWORD como explicado acima
        TAMANHO_CHAVE para especificar o tamanho da chave (ex: 1024)
        EXPIRA para definir o número de dias de duração do certificado. Por omissão deverá ser 365 (um ano)
        Note que estamos a definir uma "label" ou etiqueta para este certificado ( ${NOME_SERVIDOR}_label) para que o motor possa identificar qual o certificado que usará dos que estão guardados na keystore.
        Esta etiqueta será usada na configuração do motor (próximo passo)
      3. Configurar a etiqueta que define qual o certificado a usar pelo motor. Isto é feito definindo o parâmetro SSL_KEYSTORE_LABEL:

        SSL_KEYSTORE_LABEL panther_label
      4. Criar uma entrada no $INFORMIXSQLHOSTS para SSL (ex: panther_ssl) usando o protocolo onsocssl
      5. Configurar uma entrada NETTYPE no $INFORMIXDIR/etc/$ONCONFIG:
        NETTYPE socssl

        NETTYPE        onsocssl,1,20,NET
      6. Configurar um (ou mais) processadores virtuais para SSL:

        VPCLASS ssl,num=1,noage
      7. Garantir que a nova porta SSL está configurada como DBSERVERNAME ou uma entrada no DBSERVERALIAS
      8. Extrair o certificado de forma que possa ser importado para os repositórios dos clientes (para certificados auto-assinados):

        gsk8capicmd -cert -extract -db $INFORMIXDIR/ssl/${NOME_SERVIDOR}.kdb -format ascii -label ${NOME_SERVIDOR}_label -pw ${MINHA_PASSWORD} -target ${NOME_SERVIDOR}_label.cert
    • Do lado do cliente
      1. Criar a keystore do lado do cleinte:

        gsk8capicmd -keydb -create -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MINHA_PASSWORD} -type cms -stash
      2. Importar o certificado auto-assinado que extraímos no último passo do lado do servidor:

        gsk8capicmd -cert -add -db $INFORMIXDIR/etc/clientdb.kdb -pw ${MINHA_PASSWORD} -label ${SERVER_NAME}_label -file ${NOME_SERVIDOR}_label.cert -format ascii

        note que o caminho especificado depois da opção "-file" tem de apontar para o ficheiro extraído da keystore do servidor. Poderá ter de o copiar por FTP/SCP ou usando qualquer outro método de transferência.
      3. Numa configuração mais rigorosa deveria importar o certificado da CA usada para assinar os certificados usados nos servidores a que o cliente se pretenda ligar
      4. Configurar o ficheiro $INFORMIXSQLHOSTS do cliente com a nova entrada que usa o porto SSL
      5. Criar um ficheiro com o nome $INFORMIXDIR/etc/conssl.cfg com o seguinte conteúdo:

        SSL_KEYSTORE_FILE   /usr/informix/client_INFORMIXDIR/etc/clientdb.kdb   # Keystore file
        SSL_KEYSTORE_STH    /usr/informix/client_INFORMIXDIR/etc/clientdb.sth   # Keystore stash file

        Isto indica aos clientes qual a keystore e o ficheiro de stash
      6. Garanta que o ficheiro da keystore e o stash têm as permissões adequadas (não permitir o acesso a pessoas não autorizadas)

    O problema

    Assim que tiver um ambiente com SSL ativado, deverá conseguir conectar os clientes (aos quais tem de disponibilizar o certificado). E então estará nas condições para encontrar o problema. Como terá notado, os certificados têm um período de vida. Não são válidos antes de uma data e não são válidos depois de outra data. Por omissão a data inicial é o momento em que cria o certificado e a duração típica é um ano. O que acontece findo esse período? O cliente já tinha descoberto... Novas conexões não serão estabelecidas (compreensível dado que o certificado está expirado), mas o problema é que não o pode revalidar com o motor a funcionar. Se tentar conectar-se com um certificado expirado terá a seguinte mensagem no online.log:


    15:27:18  listener-thread: err = -28014: oserr = 0: errstr = GSK_ERROR_BAD_DATE: Secure Sockets Layer error: GSK_ERROR_BAD_DATE.

    A mensagem de erro correspondente é:
    -28014    Secure Sockets Layer error.

    An error occurred during a Secure Socket Layer operation. For more information,
    refer to the accompanying IBM Global Security Kit (GSKit) error message.

    Em resumo, o problema é como evitar ficar numa situação em que se tenha um motor a funcionar com um certificado expirado

    Como evitar

    Em termos gerais, uma forma de evitar isto será manter um registo das datas de expiração. Antes de os certificados expirarem devemos criar novos e agendar uma paragem do servidor para efectuar a mudança. Se tiver uma janela de manutenção regular, isto será fácil. Mas se o seu sistema é 24x7 a paragem torna-se um problema. Vou partir a solução em três partes:

    1. Deve criar um certificado se seja suficientemente seguro por um determinado tempo. Este período deve ser suficiente para acomodar atrasos no agendamento da paragem do servidor de forma que possa esperar por outras razões de paragem (manutenção de SO, um crash, uma paragem mensal/trimestral ou anual). Isto provavelmente significa que terá de criar um certificado que dure mais tempo que aquele com que o deseja renovar. Por exemplo, se quiser mudar os certificados de seis em seis meses, mas só tem autorização para fazer uma paragem anual... Por isso, para ficar descansado poderá criar os certificados com um tempo de vida de 18 meses. Deve considerar o tamanho da chave necessário para garantir a segurança do certificado para o período desejado. O seguinte site poderá ajudar nisso:

      http://www.keylength.com/en/

    2. Necessita de preparar tudo o que seja possível sem parar o motor, antes da paragem programada. A ideia é que a paragem seja tão breve e simples quanto possível.. Isto incluí a criação do novo certificado e guardá-lo na mesma keystore, desde que utilize outra etiqueta (label). Isto pode ser feito antes da paragem enquanto o motor está a funcionar. Desta forma a única coisa que será necessário fazer durante a paragem é mudar o parâmetro SSL_KEYSTORE_LABEL.
    3. Deve criar um sistema que mantenha o rasto e que o alerte com antecedência sempre que um certificado estiver para expirar. Tem duas formas de o fazer: Pode guardar um identificador de certificado (por exemplo a etiqueta - label -) e a sua data de expiração numa base de dados, e periodicamente executar um processo que o alerte se algum dos certificados expirar num futuro próximo (ex: 3 meses). A outra opção é criar um alarm que verifique o certificado de cada um dos seus servidores e o alerte quando a expiração estiver próxima (por email, sistema de monitorização etc.)
    Para ajudar no ponto 3 acima, criei um pequeno script que pode ser alterado/adaptado às suas necessidades específicas:

    1    #!/bin/sh
    2   
    3    SERVER_NAME=`onstat -c | grep DBSERVERNAME | grep -v "^[     ]*#" | awk '{print $2}'`
    4    SERVER_LABEL=`onstat -c | grep SSL_KEYSTORE_LABEL | grep -v "^[     ]*#" | awk '{print $2}'`
    5   
    6    MYDATE=`gsk8capicmd -cert -details -locale en -db $INFORMIXDIR/ssl/$SERVER_NAME.kdb -stashed -label ${SERVER_LABEL} | grep After | awk '{print $4 " " $5 " " $6}'`
    7   
    8    NUM_DAYS=`dbaccess sysmaster <<EOF 2>/dev/null| grep "DAYS:" | awk -F':' '{print $2+0}'
    9    select "DAYS:"||extend(TO_DATE("$MYDATE", "%B %d, %Y"), YEAR TO DAY) - extend(TODAY,YEAR TO DAY) FROM sysmaster:sysdual;
    10    EOF`
    11   
    12   
    13    if [ $NUM_DAYS -gt 0 ]
    14    then
    15        echo "O certificado está válido por $NUM_DAYS dias"
    16    else
    17        echo "O certificado está expirado!"
    18    fi

    Deixando de lado os truques e particularidades da SHELL, o que o script está a fazer é bastante simples:
    1. Obtém os parâmetros DBSERVERNAME e SSL_KEYSTORE_LABLE do $ONCONFIG
    2. Chama o comando gdk8capicmd para obter os detalhes do certificado. Extrai a linha "Not After :". Note que uso a opção "-locale en" para definir o formato da data
    3. Usa o Informix com um script inline de SQL para calcular o número de dias até à data de expiração do certificado

    Saturday, November 10, 2012

    Why NOT Informix (again)? / Porque não Informix (novamente)

    This article is written in English and Portuguese
    Este artigo está escrito em Inglês e Português


    English version:
    A few month ago I wrote an article ( http://informix-technology.blogspot.pt/2012/03/why-or-why-not-prque-ou-porque-nao.html) where I picked on a twit made by someone who was asking why "some people still use Informix"... And hopefully I changed the point to "why shouldn't you use it"?

    Today there was an IBM webcast that raised the same point. I tried to attend it, but because I was working on a customer (Informix) I was not able to do so. Thankfully the author, Tom Rieger, sent the presentation and a link to a video posted on Youtube with the conference recording. I will not go through the presentation here... I'll just put the link... If you want a rather clear vision of what Informix can do, and who's using it, check it. I find it a bit surprising because usually we're very "shy" regarding to explaining who/how people are using Informix.











    Versão Portuguesa:

    Há alguns meses atrás escrevi um artigo ( http://informix-technology.blogspot.pt/2012/03/why-or-why-not-prque-ou-porque-nao.html) onde peguei num twit criado por alguém que perguntava "porque é que alguém ainda usa Informix"... Espero ter voltado a questão para "porque não deveriam usar?"

    Hoje foi emitido um webcast que levantava o mesmo ponto. Tentei assistir, mas porque estava a trabalhar num cliente (Informix) não me foi possível. Felizmente o autor, Tom Rieger, enviou a apresentação e um URL de um video colocado no Youtube com a gravação da conferência. Não vou descrever a apresentação aqui... apenas deixarei o URL... Se quer uma visão clara do que o Informix pode fazer e sobre quem o usa, veja-o. Achei-o bastante surpreendente porque habitualmente somos bastante "envergonhados" no que diz respeita a explicar quem/como usa Informix. Naturalmente o video está em Inglês














     

    Wednesday, November 07, 2012

    "It's the errors, stupid..."

    This article is written in English and Portuguese
    Este artigo está escrito em Inglês e Português


    English version:

    This post's title is an allusion to "It's the economy, stupid" phrase that become popular in one of the USA presidential campaigns. It seems appropriate for today, and for the short problem description I'm going to describe.
    Today, while working on a customer site, I was confronted with the following scenario:
    A developer complained that two programs working against a non-logged database (yes, some customers still use them...) raised errors (-243/111) while DELETEing an overlapped result set. The customer DBAs replied the same as I probably would: "That can happen even on a non-logged database. Please use some sort of "SET LOCK MODE TO WAIT...' "
    But the program was already using it... So... why does it happen?!

    Well... we all know that there are some differences between logged and non-logged databases, but a complete list is hard to find. One is that we cannot change the isolation level in a non-logged database. It raises an error stating that the only mode allowed is DIRTY READ. But it does accept the SET LOCK MODE WAIT statement, so we would expect it to enforce it...
    In short, what was I missing, apart from a possible, but improbable bug? The answer is, I'm missing a careful analysis of the error codes! And I should be ashamed, because I have recommended many times that people MUST always look carefully into the errors, before trying to create explanations for problems that they haven't qualified. Precisely what I was doing... So let's see some facts:

    1. The instruction causing he error was something like:
      DELETE FROM some_table WHERE indexed_field = VALUE
      And the query plan used the index on the column
    2. Error -243 reads:
      -243    Could not position within a table table-name.

      The database server cannot set the file position to a particular row
      within the file that represents a table. Check the accompanying ISAM
      error code for more information. A hardware error might have occurred,
      or the table or index might have been corrupted (truncated). Unless the
      ISAM error code or an operating-system message points to another cause,
      run the oncheck utility (secheck with IBM Informix SE or tbcheck with
      IBM Informix OnLine) to check and repair table and index.
    3. Error -111 reads:
      -111    ISAM error: no record found.

      The ISAM processor cannot locate the requested record. For C-ISAM
      programs, no record was found with the requested index value or record
      number, depending on the retrieval mode in use. Make sure that the
      correct index is in use. For SQL products, see the SQL error message or
      return code. Probably no row was found for this query
    4. And finnaly, error -107 reads:
      -107    ISAM error: record is locked.

      Another user request has locked the record that you requested or the
      file (table) that contains it. This condition is normally transient. A
      program can recover by rolling back the current transaction, waiting a
      short time, and re-executing the operation. For interactive SQL, redo
      the operation. For C-ISAM programs, review the program logic and make
      sure that it can handle this case, which is a normal event in
      multiprogramming systems. You can obtain exclusive access to a table by
      passing the ISEXCLLOCK flag to isopen. For SQL programs, review the
      program logic and make sure that it can handle this case, which is a
      normal event in multiprogramming systems. The simplest way to handle
      this error is to use the statement SET LOCK MODE TO WAIT. For bulk
      updates, see the LOCK TABLE statement and the EXCLUSIVE clause of the
      DATABASE statement.
    Why did I mentioned error -107? Because that's the usual error when you get a -243, -244 or -245... And because when you can't access a table/record, that's the usual error. That's why the suggestion to SET LOCK MODE WAIT... is the first idea that pops to mind.
    So, we were clearly overlooking the ISAM error code. Now that we passed that phase, what does -111 means in this context? That's not that evident, and I haven't found a good reference in the documentation yet.
    In this case, both queries were using the index and found the rowid. But when then they moved to access the data, the row was already deleted by the other session. That's why the error is not -107, but -111 instead. You may be wondering "What?! Isn't that an inconsistency in the data processing?". Well, yes... And that's why things work differently if you're on a logged database, depending on the isolation level you use. In some cases you'd just receive a "no rows found" response. In other situations, where the isolation level would make this impossible, you'd get this -111 error (so that you notice something was wrong). But on non-logged databases the mechanisms that allow that, are not implemented, so the behavior is to raise an error.

    The main points here is that you must always:
    1. Get the SQLCODE and the ISAM errors. Only the SQLCODE is too vague in many situations
    2. Look into them, and stop assuming things based just on SQLCODE or your previous experience (the richer it is, the more chances you'll have that it traps your mind)
    ... and I would add: "Just stop using non-logged databases!"


    Versão Portuguesa:

    O título deste artigo é uma alusão à frase "It's the economy, stupid" que se tornou popular numa das campanhas presidenciais dos EUA. Pareceu-me apropriada para hoje e para o problema que vou descrever.
    Hoje, durante uma presença num cliente, fui confrontado com o seguinte cenário:
    Um programador queixou-se que dois programas a trabalhar numa base de dados sem logging (sim, alguns clientes ainda as usam...) estoiravam com erros (-243/-111) numa operação de DELETE em que os universos de registos se misturavam. Um dos DBAs do cliente respondeu como eu provavelmente teria feito: "Isso pode acontecer mesmo numa base de dados sem logging. Por favor usem algum tipo de 'SET LOCK MODE TO WAIT...'"
    Mas os programas já o estavam a usar... Portanto... Porque é que os erros aconteciam?!


    Bom... todos sabemos que existem diferenças entre as bases de dados com logging e as que não têm logging, mas não é fácil encontrar uma lista. Uma das diferenças é que não podemos mudar o nível de isolamento numa base de dados sem logging. Retorna-nos um erro indicando que o único nível  suportado é o DIRTY READ. Mas no caso do SET LOCK MODE WAIT é aceite e seria de esperar que fosse respeitado.
    Em suma, o que me estava a escapar, aparte algum possível mas improvável bug. A resposta é que me estava a escapar uma análise séria dos códigos de erro! E deveria ter vergonha, porque recomendei muitas vezes que as pessoas TÊM de olhar cuidadosamente para os erros antes de tentarem criar explicações para problemas que não chegaram a classificar/caracterizar corretamente. Exactamente o que estava a fazer.... Vejamos então os factos:

    1. A instrução que causava o erro era semelhante a:
      DELETE FROM some_table WHERE indexed_field = VALUE
      E o plano de execução usava o índice na coluna usada no WHERE
    2. A descrição do erro -243 é:
      -243    Could not position within a table table-name.

      The database server cannot set the file position to a particular row
      within the file that represents a table. Check the accompanying ISAM
      error code for more information. A hardware error might have occurred,
      or the table or index might have been corrupted (truncated). Unless the
      ISAM error code or an operating-system message points to another cause,
      run the oncheck utility (secheck with IBM Informix SE or tbcheck with
      IBM Informix OnLine) to check and repair table and index.
    3. A descrição do erro -111 é:
      -111    ISAM error: no record found.

      The ISAM processor cannot locate the requested record. For C-ISAM
      programs, no record was found with the requested index value or record
      number, depending on the retrieval mode in use. Make sure that the
      correct index is in use. For SQL products, see the SQL error message or
      return code. Probably no row was found for this query
    4. E finalmente a descrição do erro -107 é:
      -107    ISAM error: record is locked.

      Another user request has locked the record that you requested or the
      file (table) that contains it. This condition is normally transient. A
      program can recover by rolling back the current transaction, waiting a
      short time, and re-executing the operation. For interactive SQL, redo
      the operation. For C-ISAM programs, review the program logic and make
      sure that it can handle this case, which is a normal event in
      multiprogramming systems. You can obtain exclusive access to a table by
      passing the ISEXCLLOCK flag to isopen. For SQL programs, review the
      program logic and make sure that it can handle this case, which is a
      normal event in multiprogramming systems. The simplest way to handle
      this error is to use the statement SET LOCK MODE TO WAIT. For bulk
      updates, see the LOCK TABLE statement and the EXCLUSIVE clause of the
      DATABASE statement.
    Porque referi o erro -107? Porque é o erro ISAM habitual quando temos um -243, -244 ou -245... E é o erro mais comum que explica porque não foi possível aceder a uma tabela/registo. É por isso que a sugestão de alter o LOCK MODE é a primeira ideia que vem à cabeça.

    Portanto, estava claramente a ignorar o código de erro ISAM. Agora que passámos essa fase, o que quer dizer o erro -111 neste contexto? Não é  muito evidente, e ainda não encontrei uma boa explicação na documentação.

    Neste caso, ambas as queries estavam a usar o índice para obterem o rowid. Mas quando depois iam aceder aos dados, a linha já tinha sido apagada pela outra sessão. É por isso que em vez do habitual -107 temos o -111. Pode estar a pensar "Hmmm?! Mas isso não é um processamento inconsistente?". Bem, sim... E é por isso que as coisas funcionam de forma diferente numa BD transacional (com log), dependendo do nível de isolamento que estamos a usar. Em alguns casos receberíamos apenas um "no rows found". Noutras situações, caso o nível de isolamento tornasse a situação impossível, então obteria-mos o erro -111 (para sinalizar que algo de verdadeiramente errado se passou). Mas em bases de dados sem logging os mecanismos que permitem esse comportamento não estão implementados, logo é retornado o erro.


    Os pontos principais são que temos sempre de:
    1. Obter os erros SQLCODE e ISAM. Apenas o SQLCODE pode ser demasiado vago em muitas situações.
    2. Examinar bem ambos os erros, e evitar tirar conclusões precipitadas baseadas apenas no SQLCODE ou na nossa experiência passada (quanto mais rica for, maiores serão as hipóteses de ficarmos presos a um raciocino que pode não ser o correto)
    ... e poderia adicionar "Pare de usar bases de dados sem logging!"

    Wednesday, October 31, 2012

    Online LRU cleaning changes / mudanças na limpeza das LRUs online

    This article is written in English and Portuguese
    Este artigo está escrito em Inglês e Português

    English version:

    Very quick post to document something which is not properly documented. While working at a customer I had the need to change the LRU cleaning parameters (min_dirty, max_dirty) without stopping the engine. The fine manual says this can be done by dynamically change the AUTO_LRU_TUNING parameter:

    onmode -wm AUTO_LRU_TUNING=1,min=0.3,max=1

    And this works fine... But there is a catch: If, like me yesterday, you have more than one BUFFERPOOL this will change the max/min for all of them. You may not want that... I had the solution somewhere in my personal notes, but as usual this was hard to find. Now that I did, I'm just sharing.... So, the solution would be:

    onmode -wm AUTO_LRU_TUNING=bpool=0,1,min=0.3,max=1

    Et voilá... the non documented option bpool allows us to define which of the BUFFERPOOL we want to change.


    Versão Portuguesa:

    Um artigo muito rápido para deixar documentado algo que não está completament documentado. Enquanto estava a trabalhar num cliente surgiu a necessidade de alterar os parâmetros de limpeza das LRUs (min_dirty, max_dirty) sem parar o motor. O manual refere que tal pode ser feito dinamicamente mudando o parâmetro AUTO_LRU_TUNING:

    onmode -wm AUTO_LRU_TUNING=1,min=0.3,max=1


    E isto funciona como esperado. Mas há um problema: Se estiver como eu ontem, em que tinha mais que uma BUFFERPOOL isto mudaria os parâmetros da todas elas. Podemos não desejar isto. Tinha a solução algures nas minhas notas, mas como é habitual não foi fácil de encontrar. Agora que o fiz estou apenas a partilhar informação. Assim a solução seria:


    onmode -wm AUTO_LRU_TUNING=bpool=0,1,min=0.3,max=1


    Et voilá... a opção não documentada "bpool" permite-nos definir qual das pools queremos mudar

    Monday, October 29, 2012

    Informix for newbies / Informix para novatos

    This article is written in English and Portuguese
    Este artigo está escrito em Inglês e Português


    English Version:

    In recent weeks I've received or seen several requests for information from newcomers to Informix. This was requested directly while working at a customer, through direct email (blog driven) and was a public request in the IIUG mailing list. I suppose I could create a great blog post talking about how this means that Informix is raising more and more interest, but sharing information was the main reason to create this blog and I intend to stick with that. So, the purpose of this article is simply to share some information with the hope that it can help people who is interested in finding more about Informix. So my suggestions are (no special order):

    • http://www.informix-zone.com/
      This is a site created by Eric Herber. It was created several years ago (before I created my blog) and contains a lot of Informix related information. Specifically and for beginners, you can find an installation guide, a link to Informix Developer Edition download and links to the Informix Virtual appliance, a virtual machine image which already contains a pre-installed Informix environment and a guide to learn more from it
    • http://www.freeinformix.com/
      This is another site (created by Oninit) which contains a lot of Informix information and news. For learning purposes I recommend the Informix certification tutorials, which are well organized and provide information about several product areas
    • http://www-01.ibm.com/support/docview.wss?uid=swg27019520
      A central place to get links of most (if not all) Informix documentation.
    • http://planetids.com/
      A site that acts as a blog and other publications aggregator. You'll find links to a lot of blogs and also several Informix related publications (books, IBM redbooks etc.). We should thank again to Eric Herber for this excellent repository of information
    • Administering Informix Dynamic Server Building the Foundation (book by Carlton Doe)
      This is probably the most recent book about Informix. Written by a long time and distinguish element of the Informix community. You can find it easily in the usual bookstores (online or not)
    • Informix Library
      A page inside the IBM site where you can find lot's of links to Informix related documents. In particular I'd like to point the link to Informix related Redbooks. Redbooks are freely available books (PDF and recently ePub), sponsored by IBM
    • International Informix Users Group
      This is the main Informix user's group. The registration is free, by email, and you'll not receive spam.The group does a lot to promote Informix and to spread information related to it.
      You can find links, news, utilities etc. on the IIUG website.
      I really recommend that you subscribe to the group and participate in the several mailing lists it provides. You'll always find someone with some free time to answer your questions
    • Informix Chat with the labs
      Monthly webcasts which cover inumerous technical topics. These webcasts are recorded. Currently to access the files from past sessions require sending an email to request access
    • Informix on Facebook and Informix on Twitter
      The so called social networks are great places to interact and find information

    For someone ho wants to start with Informix I'd recommend the use of the Virtual Appliance (everything already installed), or if you feel more adventurous just pick up the Developer Edition and follow the installation guide in Informix-Zone website. From there on, it really depends on what you want to do, but the certification tutorials seem a good choice.
    Feel free to comment on this post if you'd like to add references.




    Versão Portuguesa:

    Nas últimas semanas recebi vários pedidos de informação de recém-chegados ao Informix. Foi-me pedida ajuda durante uma deslocação a um cliente, recebi um pedido diretamente por email (através do blog) e houve também um pedido público na lista de correio do IIUG. Provavelmente poderia criar um ótimo artigo falando de como isto significa que há mais interesse no Informix, mas o objetivo principal da criação deste blog foi a partilha de informação e tento manter-me fiel a esse princípio. Assim sendo, o que pretendo aqui é deixar alguma informação com a esperança que possa ajudar quem esteja interessado em aprender mais sobre Informix. As minhas sugestões são (sem nenhuma ordem especial):
    • http://www.informix-zone.com/
      Este é um website criado por Eric Herber. Foi criado há vários anos (antes de ter criado o meu blog) e contém muita informação relacionada com Informix. Em particular e para iniciados, pode encontrar um guia de instalação, uma ligação ao download do Informix Developer Edition e ligações à Informix Virtual appliance, uma imagem de uma máquina virtual que contém já pré-instalado um ambiente Informix e um guia para aprender mais com ele
    • http://www.freeinformix.com/
      Outro website com muita informação sobre Informix (criado pela Oninit). Para aprender sugiro os Informix certification tutorials, que são bem organizados e fornecem muita informação sobre vários assuntos relacionados com o produto
    • http://www-01.ibm.com/support/docview.wss?uid=swg27019520
      Uma única localização onde se pode aceder a quase toda (senão toda) a documentação sobre Informix
    • http://planetids.com/
      Um website que atua como agregador de blogs e outras publicações. Encontrará ligações para artigos de blogs e também outro tipo de publicações relacionadas com Informix (livros, redbooks da IBM etc.). Devemos agradecer novamente ao Eric Herber a existência deste website.
    • Administering Informix Dynamic Server Building the Foundation (livro de Carlton Doe)
      É provavelmente o livro mais recente sobre Informix. Escrito por um membro bem conhecido e de longa data da comunidade Informix. Pode facilmente encontrá-lo nas livrarias habituais (online ou físicas)
    • Informix Library
      Uma página dentro do website da IBM onde pode encontrar inúmeras ligações para documentação relacionada com Informix. Gostaria de salientar a ligação aos Redbooks relacionados com Informix. Os Redbooks são livros de acesso livre (PDFs e mais recentemente ePubs), patrocinados pela IBM
    • Grupo International de Utilizadores Informix
      Este é o grupo de utilizadores principal. O registo é gratuito, por correio eletrónico, e não receberá spam. O grupo faz muito para promover o Informix e para divulgar informação relacionada com o produto. Pode encontrar ligações, noticias, scripts etc.
      Recomendo vivamente a inscrição no grupo e a participação nas várias listas de email que disponibiliza. Encontrará sempre alguém com algum tempo livre para responder às suas questões. Para os leitores que preferem o Português, existe também o Grupo Brasileiro de Usuários de Informix - BRIUG
    • Informix Chat with the labs
      Webcasts com regularidade mensal que cobrem inúmeros temas técnicos. Estes webcasts são gravados. Neste momento o acesso requer o envio de um email com o pedido de acesos aos ficheiros das conferências passadas
    • Informix no Facebook (e em Português) e Informix no Twitter
      As redes sociais são lugares ótimos para interagir e procurar informação

    Para quem queira começar com o Informix sugiro a utilização da Virtual Applliance (com tudo já instalado) ou caso se sinta mais aventureiro, efectuar o download da Developer Edition e seguir o guia de instalação do website Informix-Zone. A partir daí, realmente depende do que quer fazer, mas os guias para certificação parecem ser uma boa escolha
     Não hesite em comentar este artigo caso queira adicionar outras referências.

    Wednesday, October 24, 2012

    Informix 11.70.xC6

    This article is written in English and Portuguese
    Este artigo está escrito em Inglês e Português


    English Version:

    As usual, I'm just reviewing the latest IBM Informix fixpack. The software was just released (yesterday, October 23). It's important to note that this fixpack is clearly on the "mature" phase of 11.70 and also that IBM is already working with selected customers on the next major release. As such, we should not expect too many news... Nevertheless, and I've been mentioning this to several customers, IBM uses the Agile methodology in Informix development. And one of the goals of this methodology is to provide new functionalities to customers as soon as possible. So, what am I saying with this mixed message? That even for a "quiet" fixpack we can find surprising new features. As usual, let's take a look at the release notes and comment on the new features section:

    • Administration
      • Enhancements to the OpenAdmin Tool (OAT) for Informix
        Just a version refresh for several components (Apache, PHP and PDO_INFORMIX)
      • Support for the same XID for transactions on different databases
        A new parameter IFX_XA_UNIQUEXID_IN_DATABASE allows that a global (XA) transaction ID can be repeated in different databases in the same instance. Or in other words, the scope of the transaction ID can be changed from the instance to the database level. This is essentially a compatibility feature.
      • Coordinating transactions within a high-availability cluster
        This is one of the surprises... A new parameter CLUSTER_TXN_SCOPE allows a DBA to define if the applications receive the COMMIT confirmation immediately or after the transaction information was propagated through the cluster (or just the current secondary server). This can be a bit confusing and some experimentation is required, but it basically avoids that an application that takes advantage of several cluster nodes hits some apparent data consistency issue caused by the asynchronous nature of the logical log propagation. It can also be useful if an application that changes data, triggers some other process that will read that same data from another node in the cluster. I would say this can be an invaluable option for future expansion of the clustering capabilities in Informix
      • Easier failover configuration for Connection Managers in a high-availability cluster
        We can now specify the failover policy at the engine level by setting a new parameter HA_FOC_ORDER that will take priority over the policy specified in the connection manager(s) connected to the instance
    • Application development
      • Enhanced support for OUT and INOUT parameters in SPL routines
        Another surprise. For most cases and most people this will seem irrelevant. But I recall a couple of situations where I've found the lack of support for OUT/INOUT parameters in SPL a big limitation. I'd like to return to this topic in a future blog post... Time will tell...
      • Additional functions for spatial data
        Enhanced compatibility with the ISO/OpenGIS standards
      • Return the default values of columns
        Enhancement to the Datablade API
      • SPL routines for application compatibility
        Yet another surprise. The functionality will not be a surprise for anyone participating in the vNext process (EVP), but it surely is surprising to see it in 11.7. The idea behind this is terribly simple, and I must confess I'm a big fan for this. We all know that Informix is extensible and that it's easy to create new functions in C language to implement some lacking functions. In fact I've been publishing a few examples of this. So, it's a great pleasure to see that IBM decided to do that and implement some compatibility functions in a new datablade. More specifically some well known packages called DBMS_ALERT, DBMS_LOB, DBMS_OUTPUT, DBMS_RANDOM, UTL_FILE are implemented.
        This can be a great help for anyone porting applications to Informix
    • Time Series data
      • Load time series data faster through a virtual table
        Performance improvement for the virtual table interface for TimeSeries containers
    A few final comments:
    1. During an exchange of emails with one of the product architects a few years ago, he wrote something like "we should avoid creating too many parameters... Let's try to keep it simple"
      I could not agree more with him, and seeing so many new parameters worries me a bit and reminds me of this talk. On the other hand, a parameter means control, and I like that. So I'd say they're a necessary evil. On the good side, they're normally well commented in the default ONCONFIG and the newly introduced ones tend to be dynamic.
    2. If I worked in marketing, at this time I'd probably be keeping new features hidden so that I'd include them in the next major version as new stuff. It's good to see that this is not hapening and the main concern is clearly to provide value for customers ASAP
    3. Please, please, please.... Let the compatibility packages be just the beginning. I cannot find any reason why functions are not implemented this way, even if it's not the fastest implementation. In most cases developers need the functionality more than the performance. And later it can be moved into the native SQL layer implementation without breaking compatibility. This cannot be a solution for some SQL constructs, but for "pure" functions I'd use it. In fact it's not new... We've seen this in publicly available bladelets

    Versão Portuguesa:

    Como vem sendo habitual vou rever a última release do IBM Informix. O software acabou de ser disponibilizado (ontem, 23 de Outubro). É importante notar que esta release está já na fase "madura" da versão 11.70 e também que a IBM está já a trabalhar com clientes selecionados sobre a próxima versão do produto. Como tal não deveremos esperar grandes novidades... Ainda assim, e tenho referido isto a vários clientes recentemente, a IBM utiliza a metodologia Agile no desenvolvimento do Informix. E um dos objetivos do Agile é disponibilizar novas funcionalidades aos utilizadores tão cedo quanto possível. Portanto, o que estou a querer dizer com estas ideias contraditórias? Que mesmo numa release "calma" podemos encontrar novas e surpreendentes funcionalidades. Como é costume vamos espreitar as notas da release e comentar a secção de novas funcionalidades: 

    • Administração
      • Melhorias no Open Admin Tool (OAT) para Informix
        Apenas o refrescamento de versões de vários componentes (Apache, PHP e PDO_INFORMIX)
      • Suporte para o mesmo XID para transacções em bases de dados diferentes
        Um novo parâmetro IFX_XA_UNIQUEXID_IN_DATABASE permite que o identificador de uma transação global (XA) possa repetir-se em bases de dados diferentes dentro da mesma instância. Ou por outras palavras, que o alcance de um identificador de transação possa ser alterado da instância para o nível da base de dados. É essencialmente uma funcionalidade para melhorar a compatibilidade com ferramentas externas
      • Coordenação de transações dentro de um cluster de alta disponibilidade
        Esta é uma das surpresas... Um novo parâmetro (CLUSTER_TXN_SCOPE) permite definir se a aplicação recebe a confirmação de um COMIIT imediatamente ou depois de a transação se propagar pelo cluster. (ou apenas para o nó secundário corrente). Isto pode parecer um pouco confuso e é necessária alguma experiência, mas basicamente evita que uma aplicação que tire proveito de vários nós do cluster encontre alguma aparente inconsistência de dados, causada pela natureza assíncrona da propagação dos logical logs. Pode ser também útil se uma aplicação que altere dados, despolete depois outro processo que vá ler esses mesmos dados em outro nó do cluster. Diria que isto pode ser uma opção de muito valor para as futuras expansões de funcionalidade de clustering do Informix
      • Mais fácil configuração de failover para os Connection Managers num cluster de alta disponibilidade
        Podemos especificar a política de failover ao nível do motor através de um novo parâmetro (HA_FOC_ORDER) que terá prioridade sobre o que for especificado na configuração do Connection Manager(s) ligado(s) à instância
    • Desenvolvimento aplicacional
      • Melhoria no suporte a parâmetros OUT e INOUT nos procedimentos/funções em SPL
        Mais uma surpresa. Na maioria dos casos e para a maioria das pessoas isto parecerá irrelevante. Mas lembro-me de um par de situações onde a falta de suporte a parâmetros OUT/INOUT foi uma grande limitação. Gostaria de retornar ao tema num próximo artigo... O tempo o dirá...
      • Mais funções para dados spatial
        Melhoria na compatibilidade com os standards  ISO/OpenGIS
      • Retornar os valores por omissão das colunas
        Melhorias na API dos Datablades
      • Rotinas SPL para compatibilidade aplicacional
        Mais uma surpresa. Esta funcionalidade não será novidade para quem esteja a participar no processo de avaliação da próxima versão (EVP), mas é surpreendente vê-la na 11.7. A ideia por detrás disto é extremamente simples, e devo confessar que sou um fã da mesma. Todos sabemos que o Informix é facilmente extensível e que é fácil criar funções em C que implementem funções que não existem no Informix. Inclusivamente já tenho publicado alguns artigos dando alguns exemplos. Assim, é com grande prazer que vejo a IBM decidir implementar funções "de compatibilidade" num novo datablade. Mais especificamente alguns pacotes bem conhecidos, chamados DBMS_ALERT, DBMS_LOB, DBMS_OUTPUT, DBMS_RANDOM, UTL_FILE foram implementados.
        Isto pode ser uma enorme ajuda a quem esteja a portar aplicações para Informix
    • Dados Time Serie
      • Carregar dados time series mais rápido através de uma tabela virtual
        Melhoria de performance na interface de tabela virtual sobre repositórios TimeSeries
    Alguns comentários finais:
    1. Durante uma troca de emails com um dos arquitetos do Informix, há alguns anos atrás, ele escreveu algo como "devemos evitar criar muitos parâmetros... Tentemos manter a coisa simples"
      Não posso estar mais de acordo com ele, e ver tantos novos parâmetros assusta-me um pouco e relembra-me esta conversa. Por outro lado um parâmetro significa controlo e eu gosto disso. Diria que são um mal necessário. A parte boa é que estão normalmente bem comentados no ONCONFIG de exemplo e que os novos que têm sido introduzidos são normalmente dinâmicos
    2. Se trabalhasse no marketing, por esta altura estaria provavelmente a esconder funcionalidades para que as pudesse anunciar aquando da próxima versão. É bom ver que tal não está a acontecer, e que a preocupação continua a ser entregar funcionalidades aos clientes logo que possível
    3. Por favor, por favor, por favor!.... Que a introdução dos packages de compatibilidade seja apenas o princípio. Não consigo encontrar nenhuma razão para que funções não sejam implementadas desta forma, mesmo que isto não seja a implementação mais rápida. Na maioria dos casos os programadores necessitam da funcionalidade mais que da performance. E mais tarde podem ser movidas para a implementação na camada de SQL nativa sem quebrar a compatibilidade. Isto não poderá ser a solução para alguma sintaxe do SQL, mas para funções "puras" eu escolheria este caminho. E na realidade não é novo. Já vimos isto em bladelets disponíveis para o público