<commit_before>/*
 * Copyright 2015, Yahoo Inc.
 * Copyrights licensed under the Apache 2.0 License.
 * See the accompanying LICENSE file for terms.
 */
package com.yahoo.squidb.processor;

import com.yahoo.aptutils.utils.AptUtils;
import com.yahoo.squidb.processor.data.ModelSpec;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import javax.lang.model.element.Element;
import javax.tools.Diagnostic;

public final class SqlUtils {

    private static final Pattern ALPHANUMERIC = Pattern.compile("[a-zA-Z0-9_]+");

    /**
     * @return true if word is a SQLite keyword. If a word is a SQLite keyword, it is possible that it could be used as
     * a table or column name, but it is highly recommended that you not do so. Use
     * {@link #isRestrictedKeyword(String)} to check if a word definitely cannot be used as an identifier.
     */
    public static boolean isKeyword(String word) {
        return word == null || ALL_KEYWORDS.contains(word.toUpperCase());
    }

    /**
     * @return true if word is a restricted SQLite keyword, i.e. cannot be used as a table or column name.
     * If a word is a SQLite keyword, it *may* be used as a table or column name, but it is highly recommended that
     * you not do so.
     */
    public static boolean isRestrictedKeyword(String word) {
        return word == null || RESTRICTED_KEYWORDS.contains(word.toUpperCase());
    }

    /**
     * Checks to see if an identifier (for e.g. a table or column name) is a keyword and if so if it is restricted.
     * Logs an error to the given model spec if the identifier is restricted and logs a warning if the identifier
     * is a non-restricted keyword.
     *
     * @return false if the identifier is a restricted keyword, true otherwise
     */
    public static boolean checkIdentifier(String identifier, String type, ModelSpec<?> modelSpec, Element element,
            AptUtils aptUtils) {
        if (AptUtils.isEmpty(identifier)) {
            modelSpec.logError(type + " name cannot be null or empty", element);
            return false;
        } else if (Character.isDigit(identifier.charAt(0))) {
            modelSpec.logError(type + " name '" + identifier + "' cannot start with a digit", element);
            return false;
        } else if (isKeyword(identifier)) {
            if (isRestrictedKeyword(identifier)) {
                modelSpec.logError(type + " name '" + identifier + "' is a reserved SQLite keyword that cannot be "
                        + "used as a " + type + " name", element);
                return false;
            } else {
                aptUtils.getMessager().printMessage(Diagnostic.Kind.WARNING, type + " name '" + identifier + "' is a "
                        + "SQLite keyword. It may be allowed as an identifier but it is recommended you choose a "
                        + "non-keyword name instead", element);
            }
        } else if (!ALPHANUMERIC.matcher(identifier).matches()) {
            aptUtils.getMessager().printMessage(Diagnostic.Kind.WARNING, type + " name '" + identifier + "' contains "
                    + "non-alphanumeric characters, which may not be fully supported by SquiDB or SQLite in some "
                    + "cases. It is strongly recommended you use only alphanumeric characters and '_' in your "
                    + "identifiers", element);
        }
        return true;
    }

    // This code is generated by running scripts/sqlite-keywords.sh. We should periodically check for new keywords,
    // add them to that script, and re-run it to update this code.
    private static final Set<String> ALL_KEYWORDS = new HashSet<>();
    private static final Set<String> RESTRICTED_KEYWORDS = new HashSet<>();
    static {
        ALL_KEYWORDS.add("ABORT");
        ALL_KEYWORDS.add("ACTION");
        ALL_KEYWORDS.add("ADD"); RESTRICTED_KEYWORDS.add("ADD");<commit_msg>Update the warning about non-alphanumeric characters to indicate we might consider it a warning in future versions<commit_after>/*
 * Copyright 2015, Yahoo Inc.
 * Copyrights licensed under the Apache 2.0 License.
 * See the accompanying LICENSE file for terms.
 */
package com.yahoo.squidb.processor;

import com.yahoo.aptutils.utils.AptUtils;
import com.yahoo.squidb.processor.data.ModelSpec;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import javax.lang.model.element.Element;
import javax.tools.Diagnostic;

public final class SqlUtils {

    private static final Pattern ALPHANUMERIC = Pattern.compile("[a-zA-Z0-9_]+");

    /**
     * @return true if word is a SQLite keyword. If a word is a SQLite keyword, it is possible that it could be used as
     * a table or column name, but it is highly recommended that you not do so. Use
     * {@link #isRestrictedKeyword(String)} to check if a word definitely cannot be used as an identifier.
     */
    public static boolean isKeyword(String word) {
        return word == null || ALL_KEYWORDS.contains(word.toUpperCase());
    }

    /**
     * @return true if word is a restricted SQLite keyword, i.e. cannot be used as a table or column name.
     * If a word is a SQLite keyword, it *may* be used as a table or column name, but it is highly recommended that
     * you not do so.
     */
    public static boolean isRestrictedKeyword(String word) {
        return word == null || RESTRICTED_KEYWORDS.contains(word.toUpperCase());
    }

    /**
     * Checks to see if an identifier (for e.g. a table or column name) is a keyword and if so if it is restricted.
     * Logs an error to the given model spec if the identifier is restricted and logs a warning if the identifier
     * is a non-restricted keyword.
     *
     * @return false if the identifier is a restricted keyword, true otherwise
     */
    public static boolean checkIdentifier(String identifier, String type, ModelSpec<?> modelSpec, Element element,
            AptUtils aptUtils) {
        if (AptUtils.isEmpty(identifier)) {
            modelSpec.logError(type + " name cannot be null or empty", element);
            return false;
        } else if (Character.isDigit(identifier.charAt(0))) {
            modelSpec.logError(type + " name '" + identifier + "' cannot start with a digit", element);
            return false;
        } else if (isKeyword(identifier)) {
            if (isRestrictedKeyword(identifier)) {
                modelSpec.logError(type + " name '" + identifier + "' is a reserved SQLite keyword that cannot be "
                        + "used as a " + type + " name", element);
                return false;
            } else {
                aptUtils.getMessager().printMessage(Diagnostic.Kind.WARNING, type + " name '" + identifier + "' is a "
                        + "SQLite keyword. It may be allowed as an identifier but it is recommended you choose a "
                        + "non-keyword name instead", element);
            }
        } else if (!ALPHANUMERIC.matcher(identifier).matches()) {
            aptUtils.getMessager().printMessage(Diagnostic.Kind.WARNING, type + " name '" + identifier + "' contains "
                    + "non-alphanumeric characters, which may not be fully supported by SquiDB or SQLite in some "
                    + "cases. It is strongly recommended you use only alphanumeric characters and '_' in your "
                    + "identifiers. This may be considered an error in future versions of SquiDB.", element);
        }
        return true;
    }

    // This code is generated by running scripts/sqlite-keywords.sh. We should periodically check for new keywords,
    // add them to that script, and re-run it to update this code.
    private static final Set<String> ALL_KEYWORDS = new HashSet<>();
    private static final Set<String> RESTRICTED_KEYWORDS = new HashSet<>();
    static {
        ALL_KEYWORDS.add("ABORT");
        ALL_KEYWORDS.add("ACTION");
        ALL_KEYWORDS.add("ADD"); RESTRICTED_KEYWORDS.add("ADD");
{'fraction_non_alphanumeric': 0.09167293704539754, 'fraction_numerical': 0.002257336343115124, 'mean_word_length': 2.5842696629213484, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6887759', 'n_tokens_mistral': 2164, 'n_tokens_neox': 2103, 'n_words': 826}