PigUtils.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.calcite.adapter.pig;

import java.util.regex.Pattern;

/**
 * Utility methods for generating Pig Latin scripts.
 *
 * <p>Unlike SQL, Pig Latin has no mechanism to quote identifiers, so names
 * that do not match its identifier syntax cannot be represented in a script
 * and must be rejected; see
 * <a href="https://pig.apache.org/docs/r0.18.0/basic.html#identifiers">
 * Pig Latin identifiers</a>.
 */
public final class PigUtils {

  /** Private constructor to prevent instantiation. */
  private PigUtils() {
  }

  /**
   * Matches a valid Pig Latin identifier: an ASCII letter followed by any
   * number of letters, digits or underscores.
   */
  private static final Pattern IDENTIFIER =
      Pattern.compile("[A-Za-z][A-Za-z0-9_]*");

  /**
   * Validates that the given name is a valid Pig Latin identifier, throwing
   * if it is not.
   *
   * <p>Identifiers are used in generated scripts as relation aliases, field
   * names and {@code AS} aliases, where they cannot be quoted or escaped;
   * an invalid name would produce a script that Pig cannot parse.
   *
   * @param name Name to validate
   * @throws IllegalArgumentException if {@code name} is not a valid Pig Latin
   *                                   identifier
   */
  public static void checkValidIdentifier(String name) {
    if (!IDENTIFIER.matcher(name).matches()) {
      throw new IllegalArgumentException(
          "Invalid Pig Latin identifier: '" + name + "'. Pig Latin identifiers"
              + " must start with a letter, followed by letters, digits or"
              + " underscores");
    }
  }

  /**
   * Escapes the given string for use inside a Pig Latin string literal.
   *
   * <p>Backslashes and single quotes are escaped with a backslash, so that
   * the value cannot break out of the {@code '...'} literal or be re-interpreted
   * as a Pig Latin escape sequence such as {@code \t} or {@code \u0001}.
   *
   * @param s String to escape
   * @return the escaped string
   */
  public static String escapeStringLiteral(String s) {
    return s.replace("\\", "\\\\").replace("'", "\\'");
  }

  /**
   * Converts the given string to a Pig Latin string literal, escaping it
   * first and then wrapping it in single quotes.
   *
   * @param s String to quote
   * @return the quoted and escaped string literal
   */
  public static String quoteStringLiteral(String s) {
    return "'" + escapeStringLiteral(s) + "'";
  }
}