All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.snowflake.common.core.SqlFormatScanner Maven / Gradle / Ivy

There is a newer version: 5.1.4
Show newest version
/*
 * Copyright (c) 2016 Snowflake Computing Inc. All right reserved.
 */
package net.snowflake.common.core;

import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.TimeZone;
import net.snowflake.common.util.GSCommonLogUtil;
import net.snowflake.common.util.GenericGSCommonLogger;

/** This class is used to match multiple SQL formats when parsing input strings */
public class SqlFormatScanner {
  static GenericGSCommonLogger LOGGER =
      GSCommonLogUtil.getLogger(MethodHandles.lookup().lookupClass());

  /** The constructor */
  public SqlFormatScanner() {
    m_formats = new ArrayList<>();
  }

  /**
   * Add a parsed SqlFormat to the set
   *
   * @param fmt parsed SqlFormat
   */
  public void addFormat(SqlFormat fmt) {
    m_formats.add(fmt);
  }

  /**
   * Parse a format string with alternate input formats
   *
   * @param model the format model (see SqlFormat)
   * @param str the format string
   * @return null on success, error message on error
   */
  public String setFormats(int model, String str) {
    //    LOGGER.log(Level.INFO, String.format(
    //        "SqlFormatScanner::setFormats(%d, %s)", model, str));

    if (str.length() == 0) return "empty input format";

    boolean gotAuto = false;
    String s = str;
    do {
      if (s.startsWith("auto") || s.startsWith("AUTO")) {
        if (s.length() == 4) s = "";
        else if (s.charAt(4) != '|') return "unknown format element: '" + s + "'";
        else {
          s = s.substring(5);
          if (s.length() == 0) return "no format elements after |";
        }

        if (gotAuto) return "duplicate AUTO in format list";
        gotAuto = true;

        switch (model) {
          default:
            assert false;

          case SqlFormat.NUMERIC:
            for (SqlFormat f : s_numFormats) m_formats.add(f);
            break;

          case SqlFormat.TIME:
            for (SqlFormat f : s_timeFormats) m_formats.add(f);
            break;

          case SqlFormat.ANY:
            for (SqlFormat f : s_numFormats) m_formats.add(f);
            for (SqlFormat f : s_timeFormats) m_formats.add(f);
            // fall through

          case SqlFormat.TS_NTZ:
          case SqlFormat.TS_TZ:
            for (SqlFormat f : s_tsFormats) m_formats.add(f);
            // fall through

          case SqlFormat.DATE:
            for (SqlFormat f : s_dateFormats) m_formats.add(f);
            break;
        }
      } else {
        SqlFormat f = new SqlFormat();

        s = f.setFormat(model, s);
        if (s == null) return f.getErrorMsg();
        if (!f.checkScanModel(model)) {
          switch (model) {
            case SqlFormat.NUMERIC:
              s = "numbers";
              break;

            case SqlFormat.DATE:
              s = "dates";
              break;

            case SqlFormat.TIME:
              s = "time";
              break;

            case SqlFormat.TS_TZ:
            case SqlFormat.TS_NTZ:
              s = "timestamps";
              break;
          }
          return "missing or conflicting format elements required for parsing " + s;
        }
        m_formats.add(f);
      }
    } while (s.length() > 0);
    return null;
  }

  /**
   * Parse date using this list of formats (returns SFDate)
   *
   * @param str The string to parse
   * @param cenBound Century boundary for YY (1900-2100)
   * @return SFDate object or null on an error
   */
  public SFDate parseDate(String str, int cenBound) {
    TmExt tm = null;
    for (SqlFormat f : m_formats) {
      tm = f.parseTm(str, cenBound);
      if (tm != null) break;
    }
    if (tm == null) return null;
    return tm.getDate();
  }

  public SFDate parseDate(String str) {
    return parseDate(str, SqlFormat.DEFAULT_CENTURY_BOUNDARY);
  }

  /**
   * Parse time of day using this format (returns SFTime)
   *
   * @param str The string to parse
   * @return SFTime object or null on an error
   */
  public SFTime parseTime(String str) {
    TmExt tm = null;
    for (SqlFormat f : m_formats) {
      tm = f.parseTm(str, 2000);
      if (tm != null) break;
    }
    if (tm == null) return null;
    return tm.getTime();
  }

  /**
   * Parse timestamp using this format (returns SFTimestamp)
   *
   * @param str The string to parse
   * @param tz the timezone to use by default
   * @param cenBound Century boundary for YY (1900-2100)
   * @return SFTimestamp object or null on an error
   */
  public SFTimestamp parseTimestamp(String str, TimeZone tz, int cenBound) {
    TmExt tm = null;
    for (SqlFormat f : m_formats) {
      tm = f.parseTm(str, cenBound);
      if (tm != null) break;
    }
    if (tm == null) return null;
    return tm.getTimestamp(tz);
  }

  public SFTimestamp parseTimestamp(String str, TimeZone tz) {
    return parseTimestamp(str, tz, SqlFormat.DEFAULT_CENTURY_BOUNDARY);
  }

  // The list of formats in this class
  private ArrayList m_formats;

  // The pre-parsed AUTO formats
  private static final ArrayList s_numFormats;
  private static final ArrayList s_timeFormats;
  private static final ArrayList s_dateFormats;
  private static final ArrayList s_tsFormats;

  static {
    //
    // The lists of default AUTO formats for various models
    // NB: THESE LIST MUST MATCH THE LISTS IN XP's SqlFormatParams.cpp
    //
    final String autoNum[] = {"TM9", "TME"};

    s_numFormats = new ArrayList<>();
    for (String s : autoNum) {
      SqlFormat f = new SqlFormat();
      String res = f.setFormat(SqlFormat.NUMERIC, s);
      assert res != null && res.equals("");
      assert f.checkScanModel(SqlFormat.NUMERIC);
      s_numFormats.add(f);
    }

    final String autoTime[] = {
      "HH24:MI", "HH24:MI:SS", "HH24:MI:SS.FF", "HH12:MI_AM", "HH12:MI:SS_AM", "HH12:MI:SS.FF_AM"
    };

    s_timeFormats = new ArrayList<>();
    for (String s : autoTime) {
      SqlFormat f = new SqlFormat();
      String res = f.setFormat(SqlFormat.TIME, s);
      assert res != null && res.equals("");
      assert f.checkScanModel(SqlFormat.TIME);
      s_timeFormats.add(f);
    }

    final String autoDate[] = {"YYYY-MM-DD", "YYYY-MON-DD", "DD-MON-YYYY", "MM/DD/YYYY"};

    s_dateFormats = new ArrayList<>();
    for (String s : autoDate) {
      SqlFormat f = new SqlFormat();
      String res = f.setFormat(SqlFormat.DATE, s);
      assert res != null && res.equals("");
      assert f.checkScanModel(SqlFormat.DATE);
      s_dateFormats.add(f);
    }

    final String autoTs[] = {
      // ISO-ish
      "YYYY-MM-DD\"T\"HH24:MI",
      "YYYY-MM-DD\"T\"HH24:MI:SS",
      "YYYY-MM-DD\"T\"HH24:MI:SS.FF",
      "YYYY-MM-DD\"T\"HH24:MITZISO",
      "YYYY-MM-DD\"T\"HH24:MI:SSTZISO",
      "YYYY-MM-DD\"T\"HH24:MI:SS.FFTZISO",

      // ?
      "YYYY-MM-DD HH24:MI",
      "YYYY-MM-DD HH24:MI:SS",
      "YYYY-MM-DD HH24:MI:SS.FF",
      "YYYY-MM-DD HH24:MI_TZH:TZM",
      "YYYY-MM-DD HH24:MI:SS_TZH:TZM",
      "YYYY-MM-DD HH24:MI:SS.FF_TZH:TZM",
      "YYYY-MM-DD HH24:MI_TZISO",
      "YYYY-MM-DD HH24:MI:SS_TZISO",
      "YYYY-MM-DD HH24:MI:SS.FF_TZISO",

      // RFC-ish
      "Dy, DD Mon YYYY HH24:MI:SS",
      "Dy, DD Mon YYYY HH24:MI:SS.FF",
      "Dy, DD Mon YYYY HH12:MI:SS_AM",
      "Dy, DD Mon YYYY HH12:MI:SS.FF_AM",
      "Dy, DD Mon YYYY HH24:MI:SS TZHTZM",
      "Dy, DD Mon YYYY HH24:MI:SS.FF TZHTZM",
      "Dy, DD Mon YYYY HH12:MI:SS_AM TZHTZM",
      "Dy, DD Mon YYYY HH12:MI:SS.FF_AM TZHTZM",

      // Misc
      "MM/DD/YYYY HH24:MI:SS",
      "MM/DD/YYYY HH24:MI:SS.FF",

      // Twitter
      "Dy Mon DD HH24:MI:SS TZHTZM YYYY",
    };

    s_tsFormats = new ArrayList<>();
    for (String s : autoTs) {
      SqlFormat f = new SqlFormat();
      String res = f.setFormat(SqlFormat.TS_TZ, s);
      assert res != null && res.equals("");
      assert f.checkScanModel(SqlFormat.TS_TZ);
      s_tsFormats.add(f);
    }
  }
}
;




© 2015 - 2025 Weber Informatics LLC | Privacy Policy