Consumo de resultados

Retorno de resultados em linha

A maneira mais comum de consumir resultados é passar um retorno de chamada complete para connection.execute(). Quando a instrução tiver terminado a execução e o resultado estiver pronto para ser consumido, o retorno de chamada complete é invocado com as linhas de resultado retornadas em linha:

connection.execute({
  sqlText: 'SELECT * FROM sometable',
  complete: function(err, stmt, rows) {
    if (err) {
      console.error('Failed to execute statement due to the following error: ' + err.message);
    } else {
      console.log('Number of rows produced: ' + rows.length);
    }
  }
});
Copy

Transmissão de resultados

Você também pode consumir um resultado como um fluxo de linhas definindo o parâmetro de conexão streamResult como true em connection.execute ao chamar o método statement.streamRows(). Ativar esse parâmetro faz com que o método retorne um fluxo Readable do Node.js, que você pode usar para consumir linhas conforme elas são recebidas. Para obter mais informações sobre o fluxo Readable, consulte a documentação do Node.js.

Importante

Para qualquer conjunto de resultados que possa exceder a memória padrão do Node, Snowflake recomenda fortemente que você defina streamResult como true ao transmitir resultados. Com o valor padrão (false), o conector armazena todas as linhas em uma matriz antes de transmitir os resultados. Com conjuntos de resultados menores, esse fator normalmente não é um problema. No entanto, com conjuntos de resultados maiores, armazenar todos os resultados na memória pode contribuir para um erro OOM.

Versões recentes do driver Snowflake Node.js (1.6.23 e posteriores) implementam a funcionalidade de pressão de retorno para garantir que, ao consumir resultados, os dados não sejam enviados para o fluxo mais rapidamente do que os dados são lidos do fluxo.

Por exemplo, o seguinte fragmento de código consome os resultados usando o evento Readable:

var connection = snowflake.createConnection({
    account: process.env.SFACCOUNT,
    username: process.env.SFUSER,
    // ...
    streamResult: true
});

// [..rest of the code..]

connection.execute({
  sqlText: "select L_COMMENT from SNOWFLAKE_SAMPLE_DATA.TPCH_SF100.LINEITEM limit 100000;",
  streamResult: true,
  complete: function (err, stmt)
  {
    var stream = stmt.streamRows();
    // Read data from the stream when it is available
    stream.on('readable', function (row)
    {
      while ((row = this.read()) !== null)
      {
        console.log(row);
      }
    }).on('end', function ()
    {
      console.log('done');
    }).on('error', function (err)
    {
      console.log(err);
    });
  }
});
Copy

Resultados do processamento em lote

Por padrão, o método statement.streamRows() produz um fluxo que inclui cada linha do resultado. Entretanto, se você quiser consumir apenas um subconjunto do resultado, ou se quiser consumir linhas de resultados em lotes, pode chamar streamRows() com argumentos start e end. Quando estas opções adicionais são especificadas, apenas as linhas no intervalo solicitado são transmitidas:

connection.execute({
  sqlText: 'SELECT * FROM sometable',
  streamResult: true, // prevent rows from being returned inline in the complete callback
  complete: function(err, stmt, rows) {
    // no rows returned inline because streamResult was set to true
    console.log('rows: ' + rows); // 'rows: undefined'

    // only consume at most the last 5 rows in the result
    rows = [];
    stmt.streamRows({
      start: Math.max(0, stmt.getNumRows() - 5),
      end: stmt.getNumRows() - 1,
    })
    .on('error', function(err) {
      console.error('Unable to consume requested rows');
    })
    .on('data', function(row) {
      rows.push(row);
    })
    .on('end', function() {
      console.log('Number of rows consumed: ' + rows.length);
    });
  }
})
Copy

Conversão do tipo de dados

Quando linhas de resultados são produzidas, o driver automaticamente mapeia tipos de dados SQL para seus equivalentes JavaScript correspondentes. Por exemplo, os valores do tipo TIMESTAMP e DATE são retornados como objetos de data JavaScript.

Para o mapeamento completo dos tipos de dados JavaScript para SQL, consulte a tabela abaixo:

Tipo de dados SQL

Tipo de dados JavaScript

Notas

VARCHAR, CHAR, CHARACTER, STRING, TEXT

Cadeia de caracteres

INT, INTEGER, BIGINT, SMALLINT

Número

Este é o mapeamento padrão. Use o parâmetro de sessão JS_TREAT_INTEGER_AS_BIGINT para mapear para Bigint JavaScript.

NUMBER(precisão, escala), DECIMAL(p, s), NUMERIC(p, s) onde scale = 0

Número

Este é o mapeamento padrão. Use o parâmetro de sessão JS_TREAT_INTEGER_AS_BIGINT para mapear para Bigint JavaScript.

NUMBER(precisão, escala), DECIMAL(p, s), NUMERIC(p, s) onde scale > 0

Número

FLOAT, FLOAT4, FLOAT8, DOUBLE, DOUBLE PRECISION, REAL

Número

TIMESTAMP, TIMESTAMP_LTZ, TIMESTAMP_NTZ, TIMESTAMP_TZ

Data

Valores TIMESTAMP_NTZ são retornados em UTC.

DATE

Data

TIME

Cadeia de caracteres

O tipo de dados TIME em SQL não tem equivalente em JavaScript, portanto é mapeado para uma cadeia de caracteres JavaScript.

BOOLEAN

Booleano

VARIANT, ARRAY, OBJECT

JSON

Busca de tipos de dados inteiros como Bigint

Por padrão, colunas INTEGER do Snowflake (incluindo BIGINT, NUMBER(p, 0) etc.) são convertidas no tipo de dados Number do JavaScript. No entanto, os maiores valores inteiros legais do Snowflake são maiores do que os maiores valores numéricos legais JavaScript. Para converter as colunas INTEGER do Snowflake em JavaScript Bigint, que pode armazenar valores maiores que JavaScript Number, defina o parâmetro de sessão JS_TREAT_INTEGER_AS_BIGINT.

Você pode usar os seguintes métodos para definir esses valores de parâmetro:

  • Use a instrução ALTER SESSION, como mostrado abaixo:

    connection.execute( {
                        sqlText: 'ALTER SESSION SET JS_TREAT_INTEGER_AS_BIGINT = TRUE',
                        complete: function ...
                        }
                      );
    
    Copy
  • Especifique o parâmetro nas informações de configuração da conexão:

    var connection = snowflake.createConnection(
          {
          username: 'fakeusername',
          password: 'fakepassword',
          account: 'fakeaccountidentifier',
          jsTreatIntegerAsBigInt: true
          }
        );
    
    Copy

Busca de tipos de dados inteiros como cadeias de caracteres

Ao chamar connection.execute(), você pode usar a opção fetchAsString para retornar os seguintes tipos de dados como cadeia de caracteres: Boolean, Number, Date, Buffer e JSON.

Você pode usar esta opção, por exemplo, para retornar:

  • Versões formatadas de valores do tipo DATE e TIMESTAMP (ou suas variantes).

  • Versões de cadeia de caracteres de tipos SQL numéricos que não podem ser convertidas em números JavaScript sem perda de precisão.

O exemplo a seguir usa fetchAsString para converter um valor Number de alta precisão em uma cadeia de caracteres:

connection.execute({
  sqlText: 'SELECT 1.123456789123456789123456789 as "c1"',
  fetchAsString: ['Number'],
  complete: function(err, stmt, rows) {
    if (err) {
      console.error('Failed to execute statement due to the following error: ' + err.message);
    } else {
      console.log('c1: ' + rows[0].c1); // c1: 1.123456789123456789123456789
    }
  }
});
Copy

Análise de dados XML

A partir da versão 1.7.0 do driver, você pode usar as seguintes opções de configuração da biblioteca fast-xml-parser para personalizar como o driver processa os atributos do documento XML ao consultar colunas com conteúdo XML. Para obter mais informações sobre essas opções suportadas e como elas afetam a análise de dados XML, consulte Opções de xmlParserConfig.

Por padrão, o driver Node.js ignora os atributos do elemento XML ao retornar dados XML de uma consulta. Por exemplo, no conteúdo XML a seguir, o elemento <piece> inclui um atributo id:

<exhibit name="Art Show">
  <piece id="000001">
    <name>Mona Lisa</name>
    <artist>Leonardo da Vinci</artist>
    <year>1503</year>
  </piece>
  <piece id="000002">
    <name>The Starry Night</name>
    <artist>Vincent van Gogh</artist>
    <year>1889</year>
  </piece>
</exhibit>
Copy

Por padrão, quando o driver Node.js retorna o conjunto de resultados, ele ignora o atributo id e retorna a saída a seguir. Observe que os nomes e valores dos atributos não estão incluídos.

{
  exhibit: {
    piece: [
      {
        "name": "Mona Lisa",
        "artist": "Leonardo da Vinci",
        "year": "1503",
      },
      {
        "name": "The Starry Night",
        "artist": "Vincent van Gogh",
        "year": "1889",
      }
    ]
  }
}

Para definir as opções do fast-xml-parser, crie um elemento xmlParserConfig semelhante ao exemplo a seguir:

const snowflake = require('snowflake-sdk');
snowflake.configure({
  xmlParserConfig: {
    /*  Parameters that you can override
    *   ignoreAttributes - default true,
    *   attributeNamePrefix - default '@_',
    *   attributesGroupName - default unset,
    *   alwaysCreateTextNode - default false
    */
    ignoreAttributes: false, attributesGroupName: '@', attributeNamePrefix: ''
  }
});
Copy

Com essas configurações, o driver analisa os dados XML e produz o seguinte:

{
  exhibit: {
    piece: [
      {
        "name": "Mona Lisa",
        "artist": "Leonardo da Vinci",
        "year": "1503",
        '@': { id: '000001' }
      },
      {
        "name": "The Starry Night",
        "artist": "Vincent van Gogh",
        "year": "1889",
        '@': { id: '000002' }
      }
    ],
    '@': { name: 'Art Show' }
  }

Como retornar conjuntos de resultados que contêm nomes de colunas duplicados

Na versão 1.8.0, o driver Snowflake Node.js introduziu uma nova opção de configuração rowMode que permite especificar como você deseja que o driver retorne conjuntos de resultados que contêm nomes de colunas duplicados.

Antes da versão 1.8.0, o driver Snowflake Node.js sempre retornava o conjunto de resultados de um comando SELECT como um objeto JavaScript. Nos casos em que o conjunto de resultados continha nomes e valores de colunas duplicados, alguns elementos poderiam ser omitidos devido à maneira como os objetos JavaScript manipulam nomes duplicados.

A opção rowMode permite especificar como os dados do conjunto de resultados são retornados para evitar possível perda de informações, inclusive como:

  • array

  • object (padrão)

  • object_with_renamed_duplicated_columns

Para ilustrar, suponha que você envie a seguinte consulta:

select *
from (select 'a' as key, 1 as foo, 3 as name) as table1
join (select 'a' as key, 2 as foo, 3 as name2) as table2 on table1.key = table2.key
join (select 'a' as key, 3 as foo) as table3 on table1.key = table3.key
Copy

Com base no valor de rowMode, o driver retorna os conjuntos de resultados da seguinte forma:

  • object (ou não definido)

    {KEY: 'a', FOO: 3, NAME: 3, NAME2: 3};
    
  • array

    ['a', 1, 3, 'a', 2, 3, 'a', 3];
    
  • object_with_renamed_duplicated_columns

    {KEY: 'a', FOO: 1, NAME: 3, KEY_2: 'a', FOO_2: 2, NAME2: 3, KEY_3: 'a', FOO_3: 3};
    

Você pode definir o parâmetro rowMode no nível de configuração da conexão ou da instrução, conforme mostrado abaixo. Se definido em ambos os locais, o valor do nível de instrução terá precedência.

  • Nível de configuração

    snowflake.createConnection({
      account: account,
      username: username,
      ...
      rowMode: 'array'})
    
    Copy
  • Nível de instrução

    connection.execute({
      sqlText: sql,
      rowMode: 'array',
      ...
      )}
    
    Copy

Personalização de como os conjuntos de resultados processam dados JSON e XML

O driver Snowflake Node.js fornece os seguintes analisadores padrão para processar dados JSON e XML em conjuntos de resultados:

  • JSON: retorna o resultado de um novo objeto Function.

  • XML: fast-xml-parser.

    O módulo fast-xml-parser padrão oferece suporte a um subconjunto de recursos, conforme descrito em Análise de dados XML.

Se preferir usar um analisador personalizado, você pode usar os seguintes exemplos para configurá-los:

  • Use o analisador eval JSON, que o driver usava antes da versão 1.6.21:

    const snowflake = require('snowflake-sdk');
    snowflake.configure({
      jsonColumnVariantParser: rawColumnValue => eval("(" + rawColumnValue + ")")
    })
    
    Copy
  • Use o analisador fast-xml-parser, com a capacidade de personalizar todas as suas opções:

    const snowflake = require('snowflake-sdk');
    snowflake.configure({
      xmlColumnVariantParser: rawColumnValue => new (require("fast-xml-parser")).XMLParser().parse(rawColumnValue)
    })
    
    Copy
  • Configure analisadores personalizados para ambos na mesma declaração:

    const snowflake = require('snowflake-sdk');
    snowflake.configure({
      jsonColumnVariantParser: rawColumnValue => JSON.parse(rawColumnValue),
      xmlColumnVariantParser: rawColumnValue => new (require("fast-xml-parser")).XMLParser().parse(rawColumnValue)
    })
    
    Copy