docs: static public ResultOutput processResult(ResultInput resultData,
                                             SFSession sfSession)
    throws SnowflakeSQLException
    {
      ResultOutput resultOutput = new ResultOutput();
  
      logger.debug("Entering processResult");
      JsonNode rootNode = resultData.resultJSON;
  
      SnowflakeUtil.checkErrorAndThrowException(rootNode);
  
      // get the query id
      resultOutput.queryId = rootNode.path("data").path("queryId").asText();
  
      JsonNode databaseNode = rootNode.path("data").path("finalDatabaseName");
      resultOutput.finalDatabaseName = databaseNode.isNull() ? null : databaseNode.asText();
  
      JsonNode schemaNode = rootNode.path("data").path("finalSchemaName");
      resultOutput.finalSchemaName = schemaNode.isNull() ? null : schemaNode.asText();
  
      JsonNode roleNode = rootNode.path("data").path("finalRoleName");
      resultOutput.finalRoleName = roleNode.isNull() ? null : roleNode.asText();
  
      JsonNode warehouseNode = rootNode.path("data").path("finalWarehouseName");
      resultOutput.finalWarehouseName = warehouseNode.isNull() ? null : warehouseNode.asText();
  
      resultOutput.statementType = SFStatementType.lookUpTypeById(
          rootNode.path("data").path("statementTypeId").asLong());
  
      resultOutput.totalRowCountTruncated
          = rootNode.path("data").path("totalTruncated").asBoolean();
  
      if (logger.isDebugEnabled())
      {
        logger.debug("query id: {}", resultOutput.queryId);
      }
  
      // extract parameters
      resultOutput.parameters =
          SessionUtil.getCommonParams(rootNode.path("data").path("parameters"));
  
      // initialize column metadata
      resultOutput.columnCount = rootNode.path("data").path("rowtype").size();
  
      for (int i = 0; i < resultOutput.columnCount; i++)
      {
        JsonNode colNode = rootNode.path("data").path("rowtype").path(i);
  
        SnowflakeColumnMetadata columnMetadata
            = SnowflakeUtil.extractColumnMetadata(
            colNode, sfSession.isJdbcTreatDecimalAsInt());
  
        resultOutput.resultColumnMetadata.add(columnMetadata);
  
        if (logger.isDebugEnabled())
        {
          logger.debug("Get column metadata: {}",
                       columnMetadata.toString());
        }
      }
  
      resultOutput.currentChunkRowset = rootNode.path("data").path("rowset");
  
      if (resultOutput.currentChunkRowset == null ||
          resultOutput.currentChunkRowset.isMissingNode())
      {
        resultOutput.currentChunkRowCount = 0;
      }
      else
      {
        resultOutput.currentChunkRowCount = resultOutput.currentChunkRowset.size();
      }
  
      logger.debug("First chunk row count: {}",
                   resultOutput.currentChunkRowCount);
  
      JsonNode chunksNode = rootNode.path("data").path("chunks");
  
      if (!chunksNode.isMissingNode())
      {
        resultOutput.chunkCount = chunksNode.size();
  
        // Try to get the Query Result Master Key
        JsonNode qrmkNode = rootNode.path("data").path("qrmk");
        final String qrmk = qrmkNode.isMissingNode() ?
                            null : qrmkNode.textValue();
  
        JsonNode chunkHeaders = rootNode.path("data").path("chunkHeaders");
  
        // the initialization of chunk downloader will start prefetching
        // first few chunks
        if (resultOutput.chunkCount > 0)
        {
          logger.debug("#chunks={}, initialize chunk downloader",
                       resultOutput.chunkCount);
  
          // prefetch threads
          int resultPrefetchThreads = 4;
          if (resultOutput.parameters.get(CLIENT_PREFETCH_THREADS) != null)
          {
            resultPrefetchThreads =
                (int) resultOutput.parameters.get(CLIENT_PREFETCH_THREADS);
          }
  
          /*
           * Should JsonParser be used instead of the original Json deserializer.
           */
          boolean useJsonParserV2 = false;
          if (resultOutput.parameters.get("JDBC_USE_JSON_PARSER") != null)
          {
            useJsonParserV2 =
                (boolean) resultOutput.parameters.get("JDBC_USE_JSON_PARSER");
          }
  
          // initialize the chunk downloader
          resultOutput.chunkDownloader =
              new SnowflakeChunkDownloader(resultOutput.columnCount,
                                           chunksNode,
                                           resultPrefetchThreads,
                                           qrmk,
                                           chunkHeaders,
                                           resultData.networkTimeoutInMilli,
                                           useJsonParserV2,
                                           initMemoryLimit(resultOutput));
        }
      }
  
      String sqlTimestampFormat = (String) effectiveParamValue(
          resultOutput.parameters, "TIMESTAMP_OUTPUT_FORMAT");
  
      // Special handling of specialized formatters, use a helper function
      resultOutput.timestampNTZFormatter = specializedFormatter(
          resultOutput.parameters,
          "timestamp_ntz",
          "TIMESTAMP_NTZ_OUTPUT_FORMAT",
          sqlTimestampFormat);
  
      resultOutput.timestampLTZFormatter = specializedFormatter(
          resultOutput.parameters,
          "timestamp_ltz",
          "TIMESTAMP_LTZ_OUTPUT_FORMAT",
          sqlTimestampFormat);
  
      resultOutput.timestampTZFormatter = specializedFormatter(
          resultOutput.parameters,
          "timestamp_tz",
          "TIMESTAMP_TZ_OUTPUT_FORMAT",
          sqlTimestampFormat);
  
      String sqlDateFormat = (String) effectiveParamValue(
          resultOutput.parameters,
          "DATE_OUTPUT_FORMAT");
  
      resultOutput.dateFormatter = new SnowflakeDateTimeFormat(sqlDateFormat);
  
      if (logger.isDebugEnabled())
      {
        logger.debug("sql date format: {}, java date format: {}",
                     sqlDateFormat,
                     resultOutput.dateFormatter.toSimpleDateTimePattern());
      }
  
      String sqlTimeFormat = (String) effectiveParamValue(
          resultOutput.parameters,
          "TIME_OUTPUT_FORMAT");
  
      resultOutput.timeFormatter = new SnowflakeDateTimeFormat(sqlTimeFormat);
      if (logger.isDebugEnabled())
      {
        logger.debug("sql time format: {}, java time format: {}",
                     sqlTimeFormat,
                     resultOutput.timeFormatter.toSimpleDateTimePattern());
      }
  
      String timeZoneName = (String) effectiveParamValue(
          resultOutput.parameters, "TIMEZONE");
      resultOutput.timeZone = TimeZone.getTimeZone(timeZoneName);
  
      resultOutput.honorClientTZForTimestampNTZ =
          (boolean) effectiveParamValue(
              resultOutput.parameters,
              "CLIENT_HONOR_CLIENT_TZ_FOR_TIMESTAMP_NTZ");
  
      logger.debug("Honoring client TZ for timestamp_ntz? {}",
                   resultOutput.honorClientTZForTimestampNTZ);
  
      String binaryFmt = (String) effectiveParamValue(
          resultOutput.parameters, "BINARY_OUTPUT_FORMAT");
      resultOutput.binaryFormatter =
          SFBinaryFormat.getSafeOutputFormat(binaryFmt);
  
      // result version
      JsonNode versionNode = rootNode.path("data").path("version");
  
      if (!versionNode.isMissingNode())
      {
        resultOutput.resultVersion = versionNode.longValue();
      }
  
      // number of binds
      JsonNode numberOfBindsNode = rootNode.path("data").path("numberOfBinds");
  
      if (!numberOfBindsNode.isMissingNode())
      {
        resultOutput.numberOfBinds = numberOfBindsNode.intValue();
      }
  
      JsonNode arrayBindSupported = rootNode.path("data")
          .path("arrayBindSupported");
      resultOutput.arrayBindSupported = !arrayBindSupported.isMissingNode()
                                        && arrayBindSupported.asBoolean();
  
      // time result sent by GS (epoch time in millis)
      JsonNode sendResultTimeNode = rootNode.path("data").path("sendResultTime");
      if (!sendResultTimeNode.isMissingNode())
      {
        resultOutput.sendResultTime = sendResultTimeNode.longValue();
      }
  
      logger.debug("result version={}", resultOutput.resultVersion);
  
      return resultOutput;
    }