docs: private void parseCommand() throws SnowflakeSQLException
    {
      JsonNode jsonNode = parseCommandInGS(statement, command);
  
      // get command type
      if (!jsonNode.path("data").path("command").isMissingNode())
      {
        commandType = CommandType.valueOf(
            jsonNode.path("data").path("command").asText());
      }
  
      // get source file locations as array (apply to both upload and download)
      JsonNode locationsNode = jsonNode.path("data").path("src_locations");
  
      assert locationsNode.isArray();
  
      String[] src_locations;
  
      try
      {
        src_locations = mapper.readValue(locationsNode.toString(), String[].class);
        initEncryptionMaterial(commandType, jsonNode);
      }
      catch (Exception ex)
      {
        throw new SnowflakeSQLException(ex, SqlState.INTERNAL_ERROR,
                                        ErrorCode.INTERNAL_ERROR.getMessageCode(),
                                        "Failed to parse the locations due to: " + ex.getMessage());
      }
  
      showEncryptionParameter = jsonNode.path("data")
          .path("clientShowEncryptionParameter")
          .asBoolean();
  
      String localFilePathFromGS = null;
  
      // do upload command specific parsing
      if (commandType == CommandType.UPLOAD)
      {
        if (src_locations.length > 0)
        {
          localFilePathFromGS = src_locations[0];
        }
  
        sourceFiles = expandFileNames(src_locations);
  
        autoCompress =
            jsonNode.path("data").path("autoCompress").asBoolean(true);
  
        if (!jsonNode.path("data").path("sourceCompression").isMissingNode())
        {
          sourceCompression =
              jsonNode.path("data").path("sourceCompression").asText();
        }
  
      }
      else
      {
        // do download command specific parsing
        srcFileToEncMat = new HashMap();
  
        // create mapping from source file to encryption materials
        if (src_locations.length == encryptionMaterial.size())
        {
          for (int srcFileIdx = 0; srcFileIdx < src_locations.length; srcFileIdx++)
          {
            srcFileToEncMat.put(src_locations[srcFileIdx],
                                encryptionMaterial.get(srcFileIdx));
          }
        }
  
        sourceFiles = new HashSet<String>(Arrays.asList(src_locations));
  
        localLocation = jsonNode.path("data").path("localLocation").asText();
  
        localFilePathFromGS = localLocation;
  
        if (localLocation.startsWith("~"))
        {
          // replace ~ with user home
          localLocation = System.getProperty("user.home") +
                          localLocation.substring(1);
        }
  
        // it should not contain any ~ after the above replacement
        if (localLocation.contains("~"))
        {
          throw new SnowflakeSQLException(SqlState.IO_ERROR,
                                          ErrorCode.PATH_NOT_DIRECTORY.getMessageCode(),
                                          localLocation);
        }
  
        // todo: replace ~userid with the home directory of a given userid
        // one idea is to get the home directory for current user and replace
        // the last user id with the given user id.
  
        // user may also specify files relative to current directory
        // add the current path if that is the case
        if (!(new File(localLocation)).isAbsolute())
        {
          String cwd = System.getProperty("user.dir");
  
          logger.debug("Adding current working dir to relative file path.");
  
          localLocation = cwd + localFSFileSep + localLocation;
        }
  
        // local location should be a directory
        if ((new File(localLocation)).isFile())
        {
          throw new SnowflakeSQLException(SqlState.IO_ERROR,
                                          ErrorCode.PATH_NOT_DIRECTORY.getMessageCode(), localLocation);
        }
      }
  
      // SNOW-15153: verify that the value after file:// is not changed by GS
      verifyLocalFilePath(localFilePathFromGS);
  
      // more parameters common to upload/download
      String stageLocation = jsonNode.path("data").path("stageInfo").
          path("location").asText();
  
      parallel = jsonNode.path("data").path("parallel").asInt();
  
      overwrite = jsonNode.path("data").path("overwrite").asBoolean(false);
  
      String stageLocationType = jsonNode.path("data").path("stageInfo").
          path("locationType").asText();
  
      String stageRegion = null;
      if (!jsonNode.path("data").path("stageInfo").path("region").isMissingNode())
      {
        stageRegion = jsonNode.path("data").path("stageInfo")
            .path("region").asText();
      }
  
      // endPoint is only available in Azure stages
      String endPoint = null;
      if (!jsonNode.path("data").path("stageInfo").path("endPoint").isMissingNode())
      {
        endPoint = jsonNode.path("data").path("stageInfo").path("endPoint").asText();
      }
  
      // storageAccount is only available in Azure stages
      String storageAccount = null;
      if (!jsonNode.path("data").path("stageInfo").path("endPoint").isMissingNode())
      {
        storageAccount = jsonNode.path("data").path("stageInfo").path("storageAccount").asText();
      }
  
      if ("LOCAL_FS".equalsIgnoreCase(stageLocationType))
      {
        if (stageLocation.startsWith("~"))
        {
          // replace ~ with user home
          stageLocation = System.getProperty("user.home") +
                          stageLocation.substring(1);
        }
  
        if (!(new File(stageLocation)).isAbsolute())
        {
          String cwd = System.getProperty("user.dir");
  
          logger.debug("Adding current working dir to stage file path.");
  
          stageLocation = cwd + localFSFileSep + stageLocation;
        }
  
      }
  
      if (logger.isDebugEnabled())
      {
        logger.debug("Command type: {}", commandType);
  
        if (commandType == CommandType.UPLOAD)
        {
          logger.debug("autoCompress: {}", autoCompress);
  
          logger.debug("source compression: {}", sourceCompression);
        }
        else
        {
          logger.debug("local download location: {}", localLocation);
        }
  
        logger.debug("Source files:");
        for (String srcFile : sourceFiles)
        {
          logger.debug("file: {}", srcFile);
        }
  
        logger.debug("stageLocation: {}", stageLocation);
  
        logger.debug("parallel: {}", parallel);
  
        logger.debug("overwrite: {}", overwrite);
  
        logger.debug("destLocationType: {}", stageLocationType);
  
        logger.debug("stageRegion: {}", stageRegion);
  
        logger.debug("endPoint: {}", endPoint);
  
        logger.debug("storageAccount: {}", storageAccount);
      }
  
      Map stageCredentials = extractStageCreds(jsonNode);
  
      stageInfo = StageInfo.createStageInfo(stageLocationType, stageLocation, stageCredentials,
                                            stageRegion, endPoint, storageAccount);
    }