Wednesday, November 27, 2024

V15: Obtain the query plan of a running query

New feature that allows retrieving a query plan of a running query (original version here)

English version
For anyone involved with RDBMS, the concept of a query plan and its importance is not new. Most performance issues are probably related to bad query plans. So, the ability to obtain the query plan of a query is a base stone of the DBA work. And informix allows it for as long as I can remember. The only "slight" issue is that it only works for a query that we run or simulate to run. Why is this an issue? For three main reasons:

  1. It's an hassle to have to capture a query and it's parameters (if the query is prepared), run it in a tool and obtain the query plan. Specially for short lived queries
  2. The fact that a prepared query may have a different query plan than the one we get in a tool with a query written with values (more on this later)
  3. A long running query may be using a different query plan than the one we get, because some of the conditions may be different (update statistics may have been run, parameters or context may have changed)

So, we could overcome the first point with some work, but we cannot overcome the last points. And in order to understand what is happening with a running query we MUST be able to capture the existing plan of a query that is being run. This is why it was so difficult for me to understand why it took so long to fix this. The only explanation I have is an enormous "distance" between developers and people who use the products. This is not exclusive to Informix. I've personally felt this with other products.
This is just to explain that this was a "since ever" requested feature. I personally officially registered it at

https://ideas.ibm.com/ideas/INFX-I-249

on April 2013. But this was not the first time I pushed for it. I even made some attempts do dig into the memory structures that would show this, but the lack of internal documentation made it a nightmare and a dead end. Anyway, enough with the history. The feature was implemented in version 15, and for me personally this would be the top priority. Let's see how we can use it. There are two interfaces to access the query plan:

  • onstat -g qplan <0 | session_id>
  • query the sysmaster:syssqexplain

 Let's start by opening a session an run a simple query:

 SELECT * FROM customer WHERE customer_num > 110;

 On another session let's identify the session and run the onstat command on that session:


asterix@myhost.onlinedomus.local:informix-> onstat -g qplan 91

IBM Informix Dynamic Server Version 15.0.0.0AEE -- On-Line -- Up 2 days 00:23:07 -- 2193704 Kbytes
2024-11-25 22:34:02 -- Infrastructure Version: 1

Session [91]
QUERY:
------
SELECT * FROM customer WHERE customer_num > 110

QUERY: (OPTIMIZATION TIMESTAMP: 11-25-2024 22:34:03)Estimated Cost: 3
Estimated # of Rows Returned: 18

  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 > 110



asterix@myhost.onlinedomus.local:informix->

Very simple. If a session has no active query (or if we issue the command too late) we'll get:


asterix@myhost.onlinedomus.local:informix-> onstat -g qplan 91

IBM Informix Dynamic Server Version 15.0.0.0AEE -- On-Line -- Up 2 days 00:25:14 -- 2193704 Kbytes
2024-11-25 22:36:10 -- Infrastructure Version: 1

Session [91]: No running query to generate the plan.

asterix@myhost.onlinedomus.local:informix->

Again very simple. Now let's check how this can be done through SQL The feature introduced a new column on the sysmaster:syssqexplain, called sqx_sqlstatementplan. So we simply need to query this with a filter on sqx_session_id. An example:


asterix@myhost.onlinedomus.local:informix-> dbaccess -e sysmaster qplan.sql

Database selected.

SELECT
        sqx_sqlstatementplan
FROM
        sysmaster:syssqexplain
WHERE
        sqx_sessionid = 91



sqx_sqlstatementp+
                    QUERY: (OPTIMIZATION TIMESTAMP: 11-25-2024 22:41:12)Estimat
                    ed Cost: 3
                    Estimated # of Rows Returned: 18

                      1) stores:informix.customer: INDEX PATH

                        (1) Index Name: informix. 100_1
                            Index Keys: customer_num   (Serial, fragments: ALL)

                            Lower Index Filter: stores:informix.customer.custom
                    er_num > 110


1 row(s) retrieved.


Database closed.

asterix@myhost.onlinedomus.local:informix->

Before I end this article, I'd like to get back to point 2) above. The fact that a prepared statement may have a different query plan from what we can reproduce by writing the same statement and using the same values as in it's parameters. This is usually hard to explain to customers who usually don't understand why I don't trust in the query plan obtained in dbaccess when we're trying to analyze a performance issue where prepared statements are involved. The reason why this happens is one of the main reasons why this feature is so important, so I think it's worth the effort to dive a bit into this subject. The first important thing we need to clarify is what is a prepared statement? A prepared statement is a statement sent to the engine with the conditions in the WHERE clause, but where the values used in those conditions CAN be replaced by questions marks. If we wanted to prepared the statement above used as an example, the query text would be: SELECT * FROM customer WHERE customer_num > ?
When the statement is prepared, it is sent to the engine, and the engine validates the syntax. The statement executions will pass the parameters. On the first execution, the engine will calculate the query plan. And this is the crucial point around this discussion. The plan will depend on the first execution values, and in normal circumstances will not change until the statement is re-prepared or re-optimized. I'll try to show this with an example, created in JAVA. Let's start with a snippet of the program code:


1   import java.sql.*;
2   import java.util.*;
3   import java.text.*;
4   import com.informix.jdbc.*;
5   import java.util.Scanner;
6
7
8   public class cursor_iter
9   {
10      public static void main( String [] args ) {
11
12              Connection conn = null;
13              int count=0;
14              ResultSet dbRes = null;
15              IfxStatement is = null;
16              Statement is1 = null;
17              String timeStamp;
18              SimpleDateFormat dateFormat;
19              com.informix.jdbc.IfmxPreparedStatement ps = null;
20              Statement st = null;
21              Scanner scan = new Scanner(System.in);
22
23              try {
24
25                      Class.forName("com.informix.jdbc.IfxDriver");
26                      conn = DriverManager.getConnection("jdbc:informix-sqli://myhost:10010/example_db:INFORMIXSERVER=goscinny;USER=informix;PASSWORD=MASKEDPWD;");
27
28              } catch (Exception sqle1) {
29                      System.out.println("Database connection has failed.");
30                      System.out.println("Reason: " + sqle1.getMessage());
31              }
32
33
34
35              try {
36                      st = conn.createStatement();
37                      st.executeUpdate("SET EXPLAIN ON");
38
39                      ps = (com.informix.jdbc.IfmxPreparedStatement) conn.prepareStatement("SELECT * FROM example_table WHERE id >= ?");
40                      System.out.println("STEP 1: Statement prepared. Press ENTER to set parameter\n");scan.nextLine();
41
42                      ps.setInt(1, 1);
43                      System.out.println("STEP 2: Parameter set to 1. Press ENTER to execute\n");scan.nextLine();
44                      ResultSet rs = ps.executeQuery();
45                      System.out.println("STEP 3: Query executed with 1. Press ENTER to position in result set\n");scan.nextLine();
46                      rs.next();
47                      System.out.println("STEP 4: Next() executed with 1. Value: " + rs.getString(1) + ". Press ENTER to close result set\n");scan.nextLine();
48                      rs.close();
49                      System.out.println("STEP 5: ResultSet closed. Press ENTER to set parameter\n");scan.nextLine();
50
51                      ps.setInt(1, 2);
52                      System.out.println("STEP 6: Parameter set to 2. Press ENTER to execute query\n");scan.nextLine();
53                      rs = ps.executeQuery();
54                      System.out.println("STEP 7: Query executed with 2. Press ENTER to position in result set\n");scan.nextLine();
55                      rs.next();
56                      System.out.println("STEP 8: Next() executed with 2. Value: " + rs.getString(1) + ". Press ENTER to close result set\n");scan.nextLine();
57                      rs.close();
58                      System.out.println("STEP 9: ResultSet closed. Press ENTER to set parameter\n");scan.nextLine();
59
60                      ps.setInt(1, 7);
61                      System.out.println("STEP 10: Parameter set to 7. Press ENTER to execute with reoptimization\n");scan.nextLine();
62                      rs = ps.executeQuery(false,true);
63                      System.out.println("STEP 11: Query executed with 7 with re-optimization. Press ENTER to position in result set\n");scan.nextLine();
64                      rs.next();
65                      System.out.println("STEP 12: Next() executed with 7. Value: " + rs.getString(1) + ". Press ENTER to close\n");scan.nextLine();
66                      rs.close();
67                      System.out.println("STEP 13: ResultSet closed. Press ENTER to set parameter\n");scan.nextLine();
68
69                      ps.setInt(1, 1);
70                      System.out.println("STEP 14: Parameter set to 1. Press ENTER to execute query\n");scan.nextLine();
71                      rs = ps.executeQuery();
72                      System.out.println("STEP 15: Query executed with 1. Press ENTER to position in result set\n");scan.nextLine();
73                      rs.next();
74                      System.out.println("STEP 16: Next() executed with 1. Value: " + rs.getString(1) + ". Press ENTER to close result set\n");scan.nextLine();
75                      rs.close();
76                      System.out.println("STEP 17: ResultSet closed. Press ENTER to finish\n");scan.nextLine();
77              } catch (SQLException sqle) {

The code is fairly simple (disclaimer: it may contain some errors or bad practices). The list of actions is:

  • Lines 23-31: It connects to the DB
  • Lines 36,37: Activates the explain
  • Line 39: Prepares a statement with one host variable
  • Lines 42-49: Sets the host variable to "1" and executes the query, fetches a row and closes the result set
  • Lines 51-58: Repeats the execution for value "2"
  • Lines 60-67: Repeats the execution for value "7", but with re-optimization
  • Lines 69-76: Repeats the execution for value 1 again

Each step will wait for user input. The purpose of the code is to show the following:

  1. The first query plan calculation happens on the first execution and the plan uses a sequencial scan (because the query has a filter >= 1 and "1" is the lowest table value, so statistically the query will return the whole table
  2. The second execution does not recalculate the plan
  3. The third execution uses a very selective value (7), and is executed with the option to re-optimize the plan. And because we have a different value (with much higher selectivity) it will choose an index
  4. The last execution doesn't request re-optimization, and although it uses the first value (1) it will use the index

Hopefully when you analyze the behavior you'll understand:

  1. Prepared statements calculate the query plan for the value(s) passed in the first execution, unless we request a re-optimization (which will recalculate the plan and from then on) this one will be used until a new re-optimization is requested
  2. New simple execution will re-use the latest calculated plan
  3. A plan calculated once can be good for certain values and bad for others. Prepared statements don't care about this
  4. This "issue" is only meaningful if for a certain plan you have values that are good and others that would benefit from a different plan. In other words, if your table distribution is not relatively uniform across values
  5. Most important conclusion: Given the previous points it should become clear why the need for this feature should be so obvious for anyone using Informix.
     

To make it a bit more clear why we get two different plans here is the column distributions:


goscinny@myhost.onlinedomus.local:informix-> dbschema -d example_db -hd example_table | head -33

DBSCHEMA Schema Utility       INFORMIX-SQL Version 12.10.FC15

{

Distribution for idt.example_table.id
Constructed on 2024-10-19 01:13:30.77780
High Mode, 0.500000 Resolution

--- DISTRIBUTION ---
    (                                   1)
 1: (        10,          1,            7)

--- OVERFLOW ---

 1: (  16670876,                        1)
 2: (  16664127,                        2)
 3: (  16659895,                        3)
 4: (  16664556,                        4)
 5: (  16670374,                        5)
 6: (  16670185,                        6)

goscinny@myhost.onlinedomus.local:informix->

So, the values 1,2,3,4,5 and 6 are considered overflows meaning they have many rows (~16.6M each). Value 7 has only 10 rows. So, if we start the executions with value 1, it will choose a sequential scan which is the best option because we're expecting to read the whole table. But once we have that query plan, an execution with "7" would take a long time (which is why I used "2" in the second execution).
On the other hand, if we use "7" on the first execution, the engine chooses the index path, which again is the best option for this value, but would not be a good option for "1" (and most probably the others).

This is the nature of prepared statements with tables with uneven distributions. The workarounds for these cases are not in the scope for this article (it could be a good idea to cover this in another article), but I hope this makes it very clear why we need to get the effective plan of a running query and not a plan we get on dbaccess by writing the query with values.

And here is video capture of the above code. On the left side you can see what is being written to the sqexplain.out file where it becomes evident when the query plans are calculated and re-used. On the right side there's the code running and pausing for user input.

One final note: When we re-optimize we seem to get a "phantom" execution of the query with the previous plan and the new value. If you check it, you'll see the statistics of the execution are equal to the last one  used (for value 2). I believe this is a glitch or bug in the SET EXPLAIN code. A sequential scan for value 7 would take a long time to return (I tested it). And it would not return the same stats as for the value 2

Here's the video to make this more clear:




Versão Portuguesa

Para qualquer pessoa envolvida com RDBMS, o conceito de plano de execução e a sua importância não será novidade. A maioria dos problemas de performance estão provavelmente relacionados com maus planos de execução. Assim, a capacidade de obter um plano de execução de uma query, é como uma pedra de fundação do trabalho de DBA. E o Informix permite isso desde que me recordo. O único "ligeiro" problema é que isso só funciona para uma query que vamos executar ou simular a execução. Porque é que isto é um problema? Por três razões principais:

  1. É trabalhoso ter de obter uma query e os seus parâmetros (se a query fôr "PREPAREd"), executá-la numa ferramenta e daí obter o plano se execução. Em especial se a query fôr de curta duração
  2. O facto de que uma query "PREPAREd" pode ter um plano de execução diferente daquele que obtemos correndo a query directamente com valores numa ferramenta (mais sobre isto mais adiante)
  3. Uma query com uma execução muito longa, pode estar a usar um plano de execução diferente daquele que obtemos, porque as condições podem ter-se alterado (as estatísticas podem ter sido refeitas, parâmetros ou contexto pode ser diferente...)

Portanto, podemos contornar ou ignorar o primeiro ponto, com mais ou menos trabalho, mas não conseguimos resolver os últimos pontos. E para entendermos o que se passa com uma query em execução TEMOS mesmo de ser capazes de capturar o seu efectivo plano de execução. É por isto que sempre me foi muito difícil entender porque demorou tanto tempo a corrigir isto. A única explicação que encontro é uma grande "distância" entre quem desenvolve e quem usa os produtos. Isto não se passa apenas no Informix. Tenho-o sentido com outros produtos também.
Serve tudo isto para explicar que esta funcionalidade era um pedido "desde sempre". Registei-o oficialmente aqui

https://ideas.ibm.com/ideas/INFX-I-249

em Abril de 2013. Mas este não foi o primeiro momento em que batalhei por isto. Cheguei mesmo a fazer algumas tentativas para vasculhar estruturas de memória que poderiam conter esta informação. Mas a falta de documentação sobre essas estruturas tornou a tarefa num pesadelo e beco sem saída.

Bom, mas chega de história. A funcionalidade foi implementada na versão 15, e para mim sempre seria uma prioridade absoluta. Vejamos como a podemos utilizar. Existem duas interfaces para aceder ao plano de execução:

  • onstat -g qplan <0 | id_sessao>
  • consultar a sysmaster:syssqexplain

 Comecemos por abrir uma sessão e executar uma query simples::

 SELECT * FROM customer WHERE customer_num > 110;

Noutra sessão vamos identificar a sessão de base de dados onde a query está a correr e executar o comando onstat contra essa sessão:


asterix@myhost.onlinedomus.local:informix-> onstat -g qplan 91

IBM Informix Dynamic Server Version 15.0.0.0AEE -- On-Line -- Up 2 days 00:23:07 -- 2193704 Kbytes
2024-11-25 22:34:02 -- Infrastructure Version: 1

Session [91]
QUERY:
------
SELECT * FROM customer WHERE customer_num > 110

QUERY: (OPTIMIZATION TIMESTAMP: 11-25-2024 22:34:03)Estimated Cost: 3
Estimated # of Rows Returned: 18

  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 > 110



asterix@myhost.onlinedomus.local:informix->

Muito simples. Se a sessão não tiver nenhuma query activa (ou se dermos o comando demasiado tarde) é isto que obtemos:


asterix@myhost.onlinedomus.local:informix-> onstat -g qplan 91

IBM Informix Dynamic Server Version 15.0.0.0AEE -- On-Line -- Up 2 days 00:25:14 -- 2193704 Kbytes
2024-11-25 22:36:10 -- Infrastructure Version: 1

Session [91]: No running query to generate the plan.

asterix@myhost.onlinedomus.local:informix->

Novamente muito simples. Agora vejamos como podemos aceder a esta informação via SQL. A funcionalidade introduziu uma nova coluna na sysmaster:syssqexplain, chamada sqx_sqlstatementplan. Portanto só temos de interrogar esta view seleccionando esta coluna e filtrando pela coluna  sqx_session_id. Um exemplo:


asterix@myhost.onlinedomus.local:informix-> dbaccess -e sysmaster qplan.sql

Database selected.

SELECT
        sqx_sqlstatementplan
FROM
        sysmaster:syssqexplain
WHERE
        sqx_sessionid = 91



sqx_sqlstatementp+
                    QUERY: (OPTIMIZATION TIMESTAMP: 11-25-2024 22:41:12)Estimat
                    ed Cost: 3
                    Estimated # of Rows Returned: 18

                      1) stores:informix.customer: INDEX PATH

                        (1) Index Name: informix. 100_1
                            Index Keys: customer_num   (Serial, fragments: ALL)

                            Lower Index Filter: stores:informix.customer.custom
                    er_num > 110


1 row(s) retrieved.


Database closed.

asterix@myhost.onlinedomus.local:informix->

Antes de terminar este artigo, gostaria de voltar ao ponto 2) acima. É um facto que uma query PREPAREd pode usar um plano de execução diferente do que conseguimos reproduzir, usando o mesmo SQL e parâmetros. É habitual ter alguma dificuldade em explicar isto a clientes, que geralmente não entendem porque não confio nos planos de execução obtidos por exemplo no dbaccess, quando tentamos analisar problemas de performance que envolvam este tipo de instruções.

A razão porque isto acontece é uma das principais razões porque esta funcionalidade é tão importante, e assim penso que vale o esforço de aprofundar mais o tema. Temos de começar por clarificar o que é uma instrução "PREPAREd". É uma instrução enviada ao motor de base de dados, em que os valores das condições da cláusula WHERE podem ser substituídos por pontos de interrogação. Caso quiséssemos fazer o PREPARE do exemplo acima usaríamos: SELECT * FROM customer WHERE customer_num > ?

Quando a instrução é "PREPAREd", é enviada ao motor, e o motor irá validá-la sintacticamente. Depois as várias execuções da instrução irão passar parâmetros. Na primeira execução o motor irá calcular o plano de execução. E este é o ponto crucial para a discussão. O plano irá depender dos valores passados para a primeira execução, e em circunstâncias normais não irá mudar até que a instrução seja novamente PREPAREd ou re-optimizada. Vou tentar evidenciar isto com um exemplo, criado em JAVA. Comecemos por ver um excerto desse programa:


1   import java.sql.*;
2   import java.util.*;
3   import java.text.*;
4   import com.informix.jdbc.*;
5   import java.util.Scanner;
6
7
8   public class cursor_iter
9   {
10      public static void main( String [] args ) {
11
12              Connection conn = null;
13              int count=0;
14              ResultSet dbRes = null;
15              IfxStatement is = null;
16              Statement is1 = null;
17              String timeStamp;
18              SimpleDateFormat dateFormat;
19              com.informix.jdbc.IfmxPreparedStatement ps = null;
20              Statement st = null;
21              Scanner scan = new Scanner(System.in);
22
23              try {
24
25                      Class.forName("com.informix.jdbc.IfxDriver");
26                      conn = DriverManager.getConnection("jdbc:informix-sqli://myhost:10010/example_db:INFORMIXSERVER=goscinny;USER=informix;PASSWORD=MASKEDPWD;");
27
28              } catch (Exception sqle1) {
29                      System.out.println("Database connection has failed.");
30                      System.out.println("Reason: " + sqle1.getMessage());
31              }
32
33
34
35              try {
36                      st = conn.createStatement();
37                      st.executeUpdate("SET EXPLAIN ON");
38
39                      ps = (com.informix.jdbc.IfmxPreparedStatement) conn.prepareStatement("SELECT * FROM example_table WHERE id >= ?");
40                      System.out.println("STEP 1: Statement prepared. Press ENTER to set parameter\n");scan.nextLine();
41
42                      ps.setInt(1, 1);
43                      System.out.println("STEP 2: Parameter set to 1. Press ENTER to execute\n");scan.nextLine();
44                      ResultSet rs = ps.executeQuery();
45                      System.out.println("STEP 3: Query executed with 1. Press ENTER to position in result set\n");scan.nextLine();
46                      rs.next();
47                      System.out.println("STEP 4: Next() executed with 1. Value: " + rs.getString(1) + ". Press ENTER to close result set\n");scan.nextLine();
48                      rs.close();
49                      System.out.println("STEP 5: ResultSet closed. Press ENTER to set parameter\n");scan.nextLine();
50
51                      ps.setInt(1, 2);
52                      System.out.println("STEP 6: Parameter set to 2. Press ENTER to execute query\n");scan.nextLine();
53                      rs = ps.executeQuery();
54                      System.out.println("STEP 7: Query executed with 2. Press ENTER to position in result set\n");scan.nextLine();
55                      rs.next();
56                      System.out.println("STEP 8: Next() executed with 2. Value: " + rs.getString(1) + ". Press ENTER to close result set\n");scan.nextLine();
57                      rs.close();
58                      System.out.println("STEP 9: ResultSet closed. Press ENTER to set parameter\n");scan.nextLine();
59
60                      ps.setInt(1, 7);
61                      System.out.println("STEP 10: Parameter set to 7. Press ENTER to execute with reoptimization\n");scan.nextLine();
62                      rs = ps.executeQuery(false,true);
63                      System.out.println("STEP 11: Query executed with 7 with re-optimization. Press ENTER to position in result set\n");scan.nextLine();
64                      rs.next();
65                      System.out.println("STEP 12: Next() executed with 7. Value: " + rs.getString(1) + ". Press ENTER to close\n");scan.nextLine();
66                      rs.close();
67                      System.out.println("STEP 13: ResultSet closed. Press ENTER to set parameter\n");scan.nextLine();
68
69                      ps.setInt(1, 1);
70                      System.out.println("STEP 14: Parameter set to 1. Press ENTER to execute query\n");scan.nextLine();
71                      rs = ps.executeQuery();
72                      System.out.println("STEP 15: Query executed with 1. Press ENTER to position in result set\n");scan.nextLine();
73                      rs.next();
74                      System.out.println("STEP 16: Next() executed with 1. Value: " + rs.getString(1) + ". Press ENTER to close result set\n");scan.nextLine();
75                      rs.close();
76                      System.out.println("STEP 17: ResultSet closed. Press ENTER to finish\n");scan.nextLine();
77              } catch (SQLException sqle) {

O código é bastante simples (salvaguarda: o código pode conter erros ou más práticas). A lista de acções é:

  • Linhas 23-31: Abre a conexão à BD
  • Linhas 36,37: Activa a escrita do plano de execução para ficheiro
  • Linhas 39: Faz o PREPARE com uma variável "host"
  • Linhas 42-49: Define a variável "host" a "1", executa a query, posiciona-se numa linha e fecha o "result set"
  • Linhas 51-58: Repete a execução para o valor "2"
  • Linhas 60-67: Repete a execução para o valor "7", mas desta feita com re-optimização
  • Linhas 69-76: Repete a execução para o valor "1" novamente

Em cada passo espera pelo input do utilizador. Os objectivos deste código são mostrar o seguinte:

  1. O cálculo do primeiro plano de execução acontece na primeira execução, e usa uma busca sequencial na tabela (pois tem um filtro ">= 1" e "1" é o menor valor da tabela, portanto estatisticamente deveremos ler a tabela toda)
  2. A segunda execução não recalcula o plano
  3. A terceira execução usa um valor muito selectivo (7), e é efectuada com a opção de re-optimização. Sendo um valor muito mais selectivo o plano vai escolher acesso por índice
  4. A última execução não pede re-optimização, e embora use o primeiro valor (1) que despoletou um acesso sequencial vai usar o plano com acesso por índice calculado na interação anterior.

Espero que depois de analisado este comportamento possa entender o seguinte:

  1. Instruções PREPAREd calculam o plano para o(s) valor(es) passado(s) na primeira execução, e a menos que seja pedida uma re-optimização (que irá recalcular o plano a usar daí em diante), irá usar sempre o mesmo plano calculado na primeira execução.
  2. Novas execuções "simples" irão utilizar o último plano calculado
  3. Um plano calculado uma vez pode ser bom para certos valores e mau para outros. Instruções PRERAREd trabalham mesmo assim
  4. Este "problema" só tem impacto se para determinado plano há valores "bons" e outros "maus". Por outras palavras, só tem impacto quando a tabela tem distribuições não uniformes entre os diferentes valores possíveis.
  5. A conclusão mais importante: Dados os pontos anteriores, deverá estar muito claro para quem usa Informix porque necessitamos tanto desta nova funcionalidade.
     

Para tornar o exemplo um pouco mais claro, e explicar porque obtemos diferentes planos, aqui fica a distribuição de valores na coluna usada na cndição:


goscinny@myhost.onlinedomus.local:informix-> dbschema -d example_db -hd example_table | head -33

DBSCHEMA Schema Utility       INFORMIX-SQL Version 12.10.FC15

{

Distribution for idt.example_table.id
Constructed on 2024-10-19 01:13:30.77780
High Mode, 0.500000 Resolution

--- DISTRIBUTION ---
    (                                   1)
 1: (        10,          1,            7)

--- OVERFLOW ---

 1: (  16670876,                        1)
 2: (  16664127,                        2)
 3: (  16659895,                        3)
 4: (  16664556,                        4)
 5: (  16670374,                        5)
 6: (  16670185,                        6)

goscinny@myhost.onlinedomus.local:informix->

Portanto, os valores 1,2,3,4,5 e 6 são considerados "overflows", o que significa que têm muitas linhas (~16.6M cada). O valor 7 só tem 10 linhas. Assim se iniciamos as execuções com o valor "1", irá escolher uma busca sequencial, dado que espera ler toda a tabela, e assim é o método mais eficiente. Mas uma vez que tenhamos esse plano, uma execução para o valor "7" demoraria muito tempo (daí ter usado "2" na segunda execução). Por outro lado, se usamos o "7" na primeira execução ficaremos com um plano de acesso por índice, pois é a melhor opção para este valor. Mas não será a melhor opção para o valor "1" (e provavelmente para os outros).

Esta é a natureza das instruções PREPAREd com tabelas com distribuições "irregulares". As formas de contornar estes casos não estão no âmbito deste artigo (poderia ser uma boa ideia fazer um artigo dedicado ao tema), mas espero que isto torne claro porque necessitamos mesmo de conseguir obter o plano efectivo de uma query em execução, e não o plano que é gerado pela query escrita com os valores.

E abaixo está uma captura de video da execução do código explicado acima. Do lado esquerdo pode seguir o que vai sendo escrito no ficheiro de saída do SET EXPLAIN. o que evidencia quando é que os planos são calculados e re-utilizados. Do lado direito tem a execução do código com as pausas para input do utilizador.

Uma nota final: Quando se efectua a re-optimização, aparentemente obtemos uma execução "fantasma" da query com o plano anterior e o valor novo. Se atentar nos dados, verá que as estatísticas e execução são iguais às mostradas no passo anterior (para o valor "2"). Suponho que isto seja um bug do SET EXPLAIN. Uma pesquisa sequencial para o valor "7" demoraria muito mais tempo a retornar (eu tentei). E naturalmente não retornaria as mesmas estatísticas que foram retornadas pelo valor "2".

Aqui fica o vídeo:





Monday, November 25, 2024

Heads up!

 Warning: old tools with new version (original version here)

English version
In the post about the release of version 15 I've mentioned an heads up about the risk of using the new version of the engine with current versions of the tools (4GL/ISQL). I hope I can show the issue more clearly here while we start to grasp on the expanded capacity of version 15.

Version 15 has two "modes" of operation. The "compatibility mode" or "Infrastructure Version: 0" and the new version "Infrastructure Version: 1". Currently I have no idea if future changes will create new modes.

In "Compatibility Mode" the engine cannot create "LARGE TABLEs". If you try to use the "CREATE LARGE TABLE tabname...." you'll get:

   261: Cannot create table (informix.test_large).
 21568: V1 infrastructure features have not yet been enabled.

The mode is shown for example if you run "onstat -":

IBM Informix Dynamic Server Version 15.0.0.0AEE -- On-Line -- Up 00:21:11 -- 222740 Kbytes
2024-11-24 02:28:09 -- Infrastructure Version: 0 (Compatibility mode)

There is a process to "convert" an instance from Infrastructure Version 0 to 1, but I won't cover that in this post. Let's now see what happens in an instance created from scratch in version 15. By default all tables will be in "LARGE TABLE" mode. But we have the option to create "SMALL TABLEs". A way to check if a table is in LARGE or SMALL mode is by running oncheck:

asterix@myhost.onlinedomus.local-> oncheck -pt stores:customer | head -10


TBLspace Report for stores:informix.customer

    Physical Address               8:566
    Creation date                  11/19/2024 15:31:46
    TBLspace Flags                 400000802        Row Locking
                                                    TBLspace uses 4 bit bit-maps
                                                    TBLspace uses large rowids
    Maximum row size               134
asterix@myhost.onlinedomus.local->

Note the "TBLspace uses large rowids" section. Let's create a small table and check it:

asterix@myhost.onlinedomus.local-> dbaccess -e stores small_test.sql

Database selected.

DROP TABLE IF EXISTS customer_small;
Table dropped.


CREATE SMALL TABLE customer_small
(
    customer_num SERIAL NOT NULL ,
    fname CHAR(15),
    lname CHAR(15),
    company CHAR(20),
    address1 CHAR(20),
    address2 CHAR(20),
    city CHAR(15),
    state CHAR(2),
    zipcode CHAR(5),
    phone CHAR(18),
    PRIMARY KEY (customer_num)
) LOCK MODE ROW;
Table created.



INSERT INTO customer_small SELECT * FROM customer;
28 row(s) inserted.




Database closed.

asterix@myhost.onlinedomus.local->

So now if we check the customer_small table:

asterix@myhost.onlinedomus.local-> oncheck -pt stores:customer_small | head -10


TBLspace Report for stores:informix.customer_small

    Physical Address               8:662
    Creation date                  11/24/2024 02:47:29

    TBLspace Flags                 802              Row Locking
                                                    TBLspace uses 4 bit bit-maps
    Maximum row size               134
    Number of special columns      0
asterix@myhost.onlinedomus.local->

There is no indication of "large rowids" so it's a "small" table. Now let's see what happens if we try to use ISQL on these two tables. I'm using 7.51.FC3, currently the latest available version of the tools.

 
 
As you can see the form works as expected for "customer_small" table, but raises an error for "customer" table. Let's check the error with the tools environment:
asterix@myhost.onlinedomus.loca-> finderr -21569
Message number -21569 not found.
 
asterix@myhost.onlinedomus.loca:informix->

The error is unknown. Not a surprise as the error didn't exist when this version of the tools was created. So let's check it in the engine environment:
  
  asterix@myhost.onlinedomus.local:informix-> . setinfx 5
Welcome to CDC Instance (ADM) Asterix
asterix@myhost.onlinedomus.local:informix-> finderr -21569
-21569  This client cannot create or open tables that use large rowids.

You are attempting to create or open a large table, but your client program
is not compatible with large tables. Set the TABLE_SIZE session environment
variable to "SMALL" or rebuild your client using the latest client libraries.


asterix@myhost.onlinedomus.local:informix->
So, it makes sense. The engine understands the application is expecting small rowids and raises an error if the table was created with LARGE mode.

This is the risk you face if you try to use the engine in version 15 before a new version of the tools are released. Note that this will affect any application that relies or handles rowids. The problem will not manifest if you upgrade an instance and avoid changing it to the new Infrastructure Mode, or in other words, while you keep in Compatibility Mode.
At the time of this writing I am unaware of any method to force the Infrastructure Version 0 on a newly created instance. This would mean that any environment that depends on rowids would require a tools refresh before any upgrade is considered. Don't take this lightly or you'll risk serious issues on your applications. An alternative would be (in a newly created instance) to create all the tables as SMLL

Let it be clear that there is an environment option to change the default type of tables we wish to create (when we don't specify the SMALL nor the LARGE option). That option is set with the command:

SET ENVIRONMENT TABLE_SIZE <SMALL|LARGE>


Note that if you try to set it to "LARGE" in an instance running in compatibility mode you'll get the error:

mouraria@psygnosis.onlinedomus.local:informix-> finderr -26041
-26041  Invalid values specified for the %s environment variable.

An invalid value was provided for the environment variable. Please check
your user documentation, and provide the correct value, and try again.

Versão Portuguesa
No artigo sobre a disponibilização da versão 15, mencionei um aviso sobre o risco de usar a nova versão do motor com as versões actuais das ferramentas (4GL/ISQL). Espero agora poder mostrar aqui de forma mais clara este tema, enquanto começamos a entrar mais na capacidade estendida da versão 15

A versão 15 tem dois "modos" de operação. O modo de compatibilidade  ou "Infrastructure Version 0" e o novo modo ou "Infrastructure Version 1". De momento não faço ideia se futuras alterações poderão dar origem a outros modos.

Em modo de compatibilidade o motor não pode criar "LARGE TABLEs". Se tentarmos usar "CREATE LARGE TABLE nom_tabela...." obteremos:

   261: Cannot create table (informix.test_large).
 21568: V1 infrastructure features have not yet been enabled.

O modo pode ser mostrado quando executamos "onstat -":

IBM Informix Dynamic Server Version 15.0.0.0AEE -- On-Line -- Up 00:21:11 -- 222740 Kbytes
2024-11-24 02:28:09 -- Infrastructure Version: 0 (Compatibility mode)

Existe um processo para "converter" uma instância do modo "Infrastructure Version 0" para "Infrastructure Version 1", mas não vou cobrir isso neste artigo. Vejamos agora o que acontece numa instância criada de raiz na versão 15. Por omissão todas as tabelas serão "LARGE TABLES". Mas temos a opção de criar "SMALL TABLEs". Uma forma de verificar o tipo de tabela é usar o comando oncheck:

asterix@myhost.onlinedomus.local-> oncheck -pt stores:customer | head -10


TBLspace Report for stores:informix.customer

    Physical Address               8:566
    Creation date                  11/19/2024 15:31:46
    TBLspace Flags                 400000802        Row Locking
                                                    TBLspace uses 4 bit bit-maps
                                                    TBLspace uses large rowids
    Maximum row size               134
asterix@myhost.onlinedomus.local->

Repare-se na secção "TBLspace uses large rowids". Vamos criar uma SMALL TABLE e verificar:

asterix@myhost.onlinedomus.local-> dbaccess -e stores small_test.sql

Database selected.

DROP TABLE IF EXISTS customer_small;
Table dropped.


CREATE SMALL TABLE customer_small
(
    customer_num SERIAL NOT NULL ,
    fname CHAR(15),
    lname CHAR(15),
    company CHAR(20),
    address1 CHAR(20),
    address2 CHAR(20),
    city CHAR(15),
    state CHAR(2),
    zipcode CHAR(5),
    phone CHAR(18),
    PRIMARY KEY (customer_num)
) LOCK MODE ROW;
Table created.



INSERT INTO customer_small SELECT * FROM customer;
28 row(s) inserted.




Database closed.

asterix@myhost.onlinedomus.local->

Verifiquemos então a tabela customer_small:

asterix@myhost.onlinedomus.local-> oncheck -pt stores:customer_small | head -10


TBLspace Report for stores:informix.customer_small

    Physical Address               8:662
    Creation date                  11/24/2024 02:47:29

    TBLspace Flags                 802              Row Locking
                                                    TBLspace uses 4 bit bit-maps
    Maximum row size               134
    Number of special columns      0
asterix@myhost.onlinedomus.local->

Não há referência a "large rowids" portanto é uma "SMALL TABLE". Agora vejamos o que acontece se tentarmos usar o ISQL com ambas as tabelas. Vou usar a versão 7.51.FC3, actualmente a última versão disponível das ferramentas.

 
 
Como se verifica, o "form" funciona normalmente para a tabela "customer_small", mas retorna um erro para a tabela "customer". Vamos verificar o erro com o ambiente das ferramentas:
asterix@myhost.onlinedomus.loca-> finderr -21569
Message number -21569 not found.
 
asterix@myhost.onlinedomus.loca:informix->

O erro é desconhecido. Não é uma surpresa visto que o erro não existia quando esta versão das ferramentas foi criada. Verifiquemos então no ambiente do motor:
  
  asterix@myhost.onlinedomus.local:informix-> . setinfx 5
Welcome to CDC Instance (ADM) Asterix
asterix@myhost.onlinedomus.local:informix-> finderr -21569
-21569  This client cannot create or open tables that use large rowids.

You are attempting to create or open a large table, but your client program
is not compatible with large tables. Set the TABLE_SIZE session environment
variable to "SMALL" or rebuild your client using the latest client libraries.


asterix@myhost.onlinedomus.local:informix->
Portanto tudo faz sentido. O motor entende que a aplicação está à espera de rowids "curtos" e cria um erro se a tabela em causa foi criada como LARGE TABLE.
 
Este é o risco que enfrentamos se usarmos a versão 15 do motor antes da disponibilização de novas versões das ferramentas. Note-se que isto afectará qualquer aplicação que dependa ou manipule rowids. O problema não se manifesta se fizermos upgrade a uma instância e evitarmos mudar o "Infrastructure Mode", ou por outras palavras, enquanto a mantivermos em modo de compatibilidade.

à data de escrita deste artigo desconheço se há alguma forma de forçar o modo de compatibilidade numa instância criada de raiz na versão 15. Isto significa que qualquer upgrade para a versão 15 com recriação de instância requererá uma nova versão das tools. Não menospreze este tema pois haverá sérios riscos de as aplicações deixarem de funcionar. Uma alternativa seria (numa instância criada de raiz) criar todas as tabelas como SMALL.

Note-se que há uma opção de ambiente para mudar o tipo de tabela a criar por omissão (quando não se usa o SMALL ou o LARGE no CREATE TABLE).
Essa opção é usar o comando:

SET ENVIRONMENT TABLE_SIZE <SMALL|LARGE> 

Refira-se ainda que, uma tentativa de estabelecer esta opção como LARGE, numa instância que se encontre em modo de compatibilidade gerará um erro:

mouraria@psygnosis.onlinedomus.local:informix-> finderr -26041
-26041  Invalid values specified for the %s environment variable.

An invalid value was provided for the environment variable. Please check
your user documentation, and provide the correct value, and try again.

Friday, November 22, 2024

Informix version 15 is GA

Informix version 15 is GA! (original version here)

English version
Version 15 of Informix was officially released and is generally available in the usual places!
I'll try to cover the new functionalities here in the upcoming weeks in some detail. For now I just want to mention the overall impression and a list of new features with brief comments. On the overall feeling, I'd say it's very good, although not perfect... But it really depends on our expectations and main concerns. When we see a new major version we all hope that all our required and more dearest features are there. And it never happens. And then there are the new features we didn't expect. In this particular case we had some public sessions where some points were disclosed so some of what is there was to be expected. A few things mentioned before were left out. So the surprise factor was not that high since that public session, but if we go before that, and someone asked me if I'd expect major physical changes in the internal structures of the engine I would clearly say: "no way". But our amazing developers did it! Eventually these big changes forced a few others to be left out, and that's why I say it's not perfect. But by changing these things now they're laying the foundations to future improvements. I cannot go through this without recalling the question I heard constantly around 2001 when IBM bought Informix: "What's the future of the product?". I always answered "honestly I don't think anyone can be sure, but it's being developed". And 23 years later (yes, 23!) we're seeing major architectural changes! So this is overall the feeling I have. Now let's see a brief list of new features and some comments:

  • Java: Engine does not include a JRE environment. And Java version 11 is needed (Informix HQ and Java UDRs for example)

  • GSKit updated to version 8.0.60.1

  • The "real thing": The internal limits were changed. This means that a lot of limits that we're used to are now much higher. So high that it looks like there is no limits. I'd say none of us will have to worry about limits during our career. But we've seen many examples in the past where people assured similar things and then it turned out as big failed previsions. Let me quote the new limits from the fine manual:
    • A single table fragment/partition can have 140 trillion pages
    • A chunk may be as large as 8 exabytes (this is the unit after Petabyte which is above Terabyte. So an exabyte would be 1024x1024 Terabytes. Yes.... a very large number)
    • Data rows per page depends on pagesize but can reach 29122 in 256KB pages (maximum was 255 independently of page size)
    • Page size can be 2, 4 ,8, 16, 32, 64, 128 and 256 KBytess (maximum was 32)
    • Data rows per fragment/partition is 9.2 quintillion 
There would be much more to say about limits. I'd say for now that the table/disk structures for now don't have "real" limits. The limits that can still have some impact would be things like number of user threads, row size (32KBytes) and a few other that were not changed at this phase. I'll get back to the limits in the end of this post for an important heads up!
  • External smart blobs: Smart blobs can now be stored in external filesystems. This can have a huge impact in terms of backups. A tradicional smart blobspace has to exist to store the smart blobs metadata
  • Invisible indexes, is a new functionality that allows a DBA to set an index to "invisible". An invisible index will be maintained as a normal index (for INSERTs, UPDATEs and DELETEs) but will not be used by queries. This allows a DBA to "test" a system behavior as if the index did not exist, and revert easily and immediately if the result is not good. An index can also be created in "invisible mode" and turned visible later.
    I would expect some functionality to change a session to allow the use of invisible indexes for that session only. That would allow a DBA to test the use of an index before really turning it on for all the sessions. BUT NOTE THIS IS JUST A PERSONAL IDEA: THERE IS NO SUGGESTION THAT THIS FEATURE WILL BE IMPLEMENTED IN THE FUTURE

  • Obtain query plan of a running query. This one could have put tears in my eyes... I need this most of the times I have a performance issue to solve. I think an image speaks louder than a thousand words, so let me just show this:
Yes... I opened this feature 11 years ago.
  • Improved audit records analysis. This means the Session ID (SID) can appear in the audit logs. Since I mentioned images, let me show another one:

    Yes... I opened this feature 9 years ago. And before that I had many internal discussions to try to push this. The argument was simple: The information is nearly useless without this because just by using the client process ID means that in many cases we cannot correlate the actions shown in the log. The argument against was always the dreadful "we cannot make the outputs incompatible with previous behavior". Check!
  • Replication update: changes to '--cascade replicate'. The --cascade replicate option is now generally available for replicates, also through templates. A replicate having the --cascaderepl option turned on will be able, on its only sending participant, to pick up transaction traffic arriving through other replicates. This used to be available only for SPL replication situations

  • Informix HQ 3.0
    • Automatic account lockout after 5 invalid access attempts
    • Support for "LARGE TABLES". We can create LARGE TABLEs and see if a table is categorized as SMALL or LARGE table. A future article will dive into what is a LARGE table. For now let's just assume it's a table with expanded capacity
    • Droping constraints directly from the user interface
    • Multiple servers refresh simultaneously

  • CDC API support on secondary servers. I've been using InfoSphere Change Data Capture (InfoSphere CDC) very much lately, which uses the Informix CDC API. Being able to take advantage of secondary servers will be great news for Informix customers who use CDC to send data to other systems.

  • New version format. Not exactly a new functionality, but something we all need to get used to. Traditionally Informix versions had the format M.R.xyz.Extra where:
    • M.R was the major release (11.50, 11.70, 12.10, 14.10)
    • x was the platform indication (F for 64 bit, U for 32 bit and T for Windows)
    • y was the type of release (B for Beta, C for initial release, D for later release with significant changes)
    • z was the fixpack number
    • Extra was a mark for specific fixes like "X1"
Now it will use: M.R.m.f-Extra where:
  • M is the major version
  • R is the release version
  • m is the "modification" identifier (a modpack includes changes)
  • f is the fixpack (a fixpack should include fixes)
  • Extra is an identifier for special builds
  • Client SDK and JDBC versions will match the server version (avoids a lot of confusion)

Heads up!

I mentioned before I'd go back to the limits for an important heads up. And I think this is really important. The new limits are implemented among others with a major change to what we know as "RowID". A row ID in "traditional" informix structure was a four byte value, divided into two components: 3 bytes of "page address" (thus the limit of around 16M pages in a fragment/partition) and 1 byte for a slot table pointer (thus the limit of 255 rows per page). New "expanded" RowIDs should be able to use 8 bytes. Traditional tools like 4GL and ISQL have some functionality that depends on RowIDs. So, existing versions will not be able to use the new extended RowIDs. If you check the compatibility page (https://www.ibm.com/support/pages/node/502131) you'll see the 7.51.FC3 tools version is certified for Informix V15.0.0.0, BUT only for "small table mode". I don't want to get into details in this generic post, but if you upgrade an existing instance to V15, you'll be in "compatible mode" and the extended capacity is not available. If you change it to "infrastructure version 1" or if you create an instance from scratch you'll be in "new version mode" and the expanded capacity will be available. Existing tools will not work 100% in this case so you must be very careful on the steps you take. A new version of the tools is being worked on but at the time of this writing there is no ETA for it. I was assured this is a priority and the development team is working on it.
 
 
 



Versão Portuguesa 

A versão 15 do Informix foi oficialmente lançada e está disponível nos sítios habituais!

Vou tentar cobrir as novas funcionalidades na próximas semanas com algum detalhe. Por agora apenas pretendo expor a impressão geral e uma lista de funcionalidades com uns breves comentários. Relativamente à impressão geral é bastante boa, mas não perfeita... Mas isto depende das nossas expectativas e principais preocupações. Quando vemos uma nova versão todos sonhamos que todos os nossos desejos de funcionalidades tenham sido implementados. E isto nunca acontece. E depois existem as funcionalidades que não esperávamos. Neste caso em particular, nós tivemos umas sessões públicas onde alguns pontos foram divulgados, portanto uma boa parte do que foi implementado já era esperado. Algumas das coisas discutidas anteriormente não foram implementadas. Mas digamos que não houve um grande factor surpresa desde essa sessão pública. Mas se recuarmos ao ponto no tempo antes da sessão, e me tivessem perguntado se eu esperava grandes mudanças na organização física dos dados (estruturas internas do motor), a minha resposta seria: "Nem pensar!". Mas a "nossa" extraordinária equipa de desenvolvimento fê-lo! Eventualmente o esforço para implementar estas mudanças obrigou a deixar de fora outras funcionalidades discutidas publicamente e por isso digo que a sensação não é perfeita.
Mas ao implementar estas mudanças agora, o desenvolvimento está a estabelecer bases para futuras alterações e melhoria. Não consigo deixar de me lembrar da pergunta que ouvia frequentemente por volta de 2001, quando a IBM adquiriu a Informix: "Qual é o futuro do produto?". Eu respondia sempre: "Honestamente penso que ninguém pode afirmar com certeza, mas continua a ser desenvolvido". E 23 anos depois (sim, 23!) estamos a assistir a grandes mudanças estruturais! Portanto esta é a sensação que tenho. Vejamos agora uma lista de novas funcionalidades e alguns comentários sobre as mesmas:

  • Java: O motor deixa de incluir um JRE. E a versão Java necessária é a 11 (Informix HQ e Java UDRs por exemplo)

  • GSKit actualizado para a versão 8.0.60.1

  • A grande mudança: Os limites internos foram mudados. Isto significa que muitos dos limites a que nos habituámos estão agora muito maiores. Tão maiores que dão a sensação que deixou de haver limites. Diria que nenhum de nós terá de se preocupar com limites durante a nossa carreira. Mas já vimos muitas personalidades ilustres fazerem este tipo de afirmação e depois revela-se que foram apenas previsões falhadas. Deixem-me citar alguns limites a partir do manual:
    • Um único fragmento/partição de uma tabela pode ter 140 triliões de páginas
    • Um chink pode atingir 8 exabytes (isto é a unidade acima do Petabyte que está acima do Terabyte. Portanto um exabyte serão 1024x1024 Terabytes. Sim.... um número muito grande)
    • Número de linhas por página depende do tamanho da página mas pode chegar às 29122 em páginas de 256KB (o máximo era 255 independentemente do tamanho da página)
    • Os tamanhos de página podem ser 2, 4 ,8, 16, 32, 64, 128 e 256 KBytess (o máximo era 32)
    • Linhas por fragmento/partição é de 9.2 quintiliões
Haveria muito mais para dizer sobre os limites. Direi por agora que as estruturas de tabelas/disco não têm limites "reais". Os limites que poderão ainda ter algum impacto serão coisas como o número de threads de utilizador, tamanho de linha (32Kbytes) e alguns outros que não foram mudados nesta fase. Voltarei ao tema dos limites no final deste artigo para um alerta importante
  • Smart Blobs externos: Os Smart blobs podem agora ser armazenados em sistemas de ficheiros. Isto pode ter um enorme impacto no backups. Um smart blobspace tradicional terá se existir para guardar a metadata dos smart blobs
  • Indices invisíveis é uma nova funcionalidade que permite ao DBA tornar um índice "invisível". Um índice invisível será mantido como um índice normal (para INSERTs, UPDATEs e DELETEs) mas não será usado em queries. Assim um DBA poderá testar o comportamento do sistema como se o índice não existisse, e reverter fácil e rapidamente caso o resultado não seja bom. Um índice pode também ser criado em "modo invisível" e tornado visível mais tarde.
    Eu esperaria que houvesse uma funcionalidade a nível de sessão que permita o uso de índices marcados como invisíveis para a sessão apenas. Isto auxiliaria um DBA a testar o uso de um índice antes que o torne "público" para todas as sessões. MAS NOTE_SE QUE ISTO É APENAS UMA IDEIA PESSOAL. NÃO HÀ QUALQUER SUGESTÃO QUE TAL FUNCIONALIDADE VENHA A SER IMPLEMENTADA NO FUTURO

  • Obter o plano de execução de uma  query que esteja a correr. Com esta podia facilmente verter umas lágrimas... Necessito disto na maioria das vezes que observo algum problema de performance. Mas penso que uma imagem vale mais que mil palavras, portanto deixem-me só partilhar isto:
Sim... Abri este pedido de melhoria há 11 anos atrás.
  • Melhoria na análise dos registos de auditing. Isto significa que a identificação da sessão pode aparecer nos logs de auditing. Já que falamos de imagens deixem-me partilhar mais uma:

    Sim.... Abri esta funcionalidade há 9 anos atrás. E antes disso tive várias discussões internas para tentar promover isto. O argumento era simples: A informação guardada (sem ID de sessão) era virtualmente inútil, pois usando apenas o ID do processo cliente não permitia correlacionar as operações auditadas. O contra-argumento era sempre o drástico "não podemos mudar os outputs para formatos incompatíveis com versões anteriores". Check!
  • Alterações na replicação: mudanças na opção '--cascade replicate'. Esta opção está agora genericamente disponível para replicates, e também através de templates. Um replicate com esta opção activada, poderá, no seu único participante marcado como sender, replicar dados que cheguem de outros replicates. Isto só estava disponível para replicação por SPL

  • Informix HQ 3.0
    • Bloqueio automático de contas ao fim de 5 tentativas de acesso falhadas
    • Suporte para "LARGE TABLES". Podemos criar LARGE TABLEs e ver se uma tabela está caracterizada como SMALL ou LARGE. Um próximo artigo aprodunará o tema das LARGE TABLEs. Por agora vamos apenas assumir que uma LARGE TABLE é uma tabela com capacidade expandida
    • Eliminar constraints directamente pelo GUI
    • Refrescamento de vários servidores em simultâneo

  • Suporte à API de CDC API nos servidores secundários.Tenho usado o InfoSphere Change Data Capture (InfoSphere CDC) muito ultimamente, um produto que usa a  Informix CDC API. Poder tirar proveito dos servidores secundários para este fim é uma excelente notícia para os clientes Informix que usam o CDC para enviar dados para outros sistemas.

  • Novo formato da versão. Não é exactamente uma nova funcionalidade, mas sim algo a que teremos de nos habituar. Tradicionalmente o Informix usava o formato M.R.xyz.Extra onde:
    • M.R era a major release (11.50, 11.70, 12.10, 14.10)
    • x era o indicativo de plataforma (F para 64 bits, U para 32 bits e T para Windows)
    • y era o tipo da release (B para Beta, C para release inicial, D para releases posteriores com mudanças significativas)
    • z era o número do fixpack
    • Extra era a marca para fixes específicos como "X1"
Agora utilizará: M.R.m.f-Extra onde:
  • M é a major version
  • R é a release version
  • m é o indicador de "modificação" (um modpack incluirá mudanças)
  • f é o fixpack (um fixpack deverá incluir apenas correcções)
  • Extra é um identificador para special builds
  • O Client SDK e o JDBC seguirão as versões do motor (evita muitas confusões)

Aviso!

Mencionei acima que voltaria aos limites para um aviso importante. E parece-me verdadeiramente importante. Os novos limites foram implementados entre outras mudanças pela mudança completa do que conhecemos como "RowID". Um row ID na estrutura tradicional do Informix é uma estrutura com quatro bytes dividido em dois componentes: 3 bytes para o endereço de página (daí o limite de cerca de 16M de páginas por fragmento/partição) e 1 byte para um ponteiro na slot table (daí o limite de 255 linhas por página). Os novos RowIDs "expandidos" poderão usar 8 bytes. As ferramentas tradicionais como o 4GL e o ISQL têm algumas funcionalidades que dependem do RowID. Portanto as versões existentes não serão capazes de usar os RowIDs expandidos. Se verificar a página de compatibilidade (https://www.ibm.com/support/pages/node/502131) verifica que a versão 7.51.FC3 das ferramentas está certificada para Informix V15.0.0.0, MAS só para "small table mode". Não quero entrar em demasiados detalhes neste artigo genérico, mas quando fazemos upgrade a uma instância já existente para a versão 15, estaremos em "modo de compatibilidade" e a capacidade estendida não estará disponível. Se mudarmos para "versão 1 da infira-estrutura", ou se criarmos uma instância nova na V15 estaremos então em "modo de versão nova" e a capacidade estendida estará disponível. As ferramentas existentes não funcionarão a 100% neste caso e portanto é muito importante ter cuidado com os passos que damos. Uma nova versão das ferramentas está a ser desenvolvida, mas à data da escrita deste artigo não existe uma data de disponibilização definida. Foi-me assegurado que isto é uma prioridade e que a equipa de desenvolvimento está a trabalhar nisto