<commit_before>/**
 * Copyright 2015 <NAME>
 *
 * Licensed under the BSD-3 License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/BSD-3-Clause
 *
 * 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 com.palantir.nexus.db.sql;


import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.palantir.common.exception.PalantirRuntimeException;
import com.palantir.exception.PalantirSqlException;
import com.palantir.nexus.db.DBType;
import com.palantir.nexus.db.SqlClause;
import com.palantir.util.TextUtils;


public class SQLString extends BasicSQLString {
    private static final Pattern ALL_WORD_CHARS_REGEX = Pattern.compile("^[a-zA-Z_0-9\\.\\-]*$"); //$NON-NLS-1$

    //TODO (DCohen): Combine cachedKeyed and cachedUnregistered maps into one.
    /**
     * Rewritten unregistered queries.
     * Key: String with all whitespace removed
     * Value: the new SQLString to run instead.
     */
    protected static volatile ImmutableMap<String, FinalSQLString> cachedUnregistered = ImmutableMap.of();
    /** Rewritten registered queries */
    protected static volatile ImmutableMap<String, FinalSQLString> cachedKeyed = ImmutableMap.of();
    /** All registered queries */
    protected static final ConcurrentMap<String, FinalSQLString> registeredValues = new ConcurrentHashMap<String, FinalSQLString>();
    /** DB-specific registered queries */
    protected static final ConcurrentMap<String, ConcurrentMap<DBType, FinalSQLString>> registeredValuesOverride = new ConcurrentHashMap<String, ConcurrentMap<DBType, FinalSQLString>>();

    protected static interface OnUseCallback {
        public void noteUse(SQLString used);
    }
    //by default, no callback. This is set in OverridableSQLString
    protected static OnUseCallback callbackOnUse = new OnUseCallback(){
        @Override
        public void noteUse(SQLString used) {
           //do nothing
        }};

    /**<commit_msg>Add lock object to protect writes to SQL overwrite caches<commit_after>/**
 * Copyright 2015 <NAME>
 *
 * Licensed under the BSD-3 License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/BSD-3-Clause
 *
 * 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 com.palantir.nexus.db.sql;


import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

import javax.annotation.concurrent.GuardedBy;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.palantir.common.exception.PalantirRuntimeException;
import com.palantir.exception.PalantirSqlException;
import com.palantir.nexus.db.DBType;
import com.palantir.nexus.db.SqlClause;
import com.palantir.util.TextUtils;


public class SQLString extends BasicSQLString {
    private static final Pattern ALL_WORD_CHARS_REGEX = Pattern.compile("^[a-zA-Z_0-9\\.\\-]*$"); //$NON-NLS-1$

    /**
     * Callers changing the value of cachedUnregistered and
     * cachedKeyed should be synchronized on this lock. Readers
     * do not need to - the values of those maps are not guaranteed
     * to be in sync with each other.
     */
    private static final Object cacheLock = new Object();
    //TODO (DCohen): Combine cachedKeyed and cachedUnregistered maps into one.
    /**
     * Rewritten unregistered queries.
     * Key: String with all whitespace removed
     * Value: the new SQLString to run instead.
     */
    @GuardedBy("cacheLock")
    protected static volatile ImmutableMap<String, FinalSQLString> cachedUnregistered = ImmutableMap.of();
    /** Rewritten registered queries */
    @GuardedBy("cacheLock")
    protected static volatile ImmutableMap<String, FinalSQLString> cachedKeyed = ImmutableMap.of();
    /** All registered queries */
    protected static final ConcurrentMap<String, FinalSQLString> registeredValues = new ConcurrentHashMap<String, FinalSQLString>();
    /** DB-specific registered queries */
    protected static final ConcurrentMap<String, ConcurrentMap<DBType, FinalSQLString>> registeredValuesOverride = new ConcurrentHashMap<String, ConcurrentMap<DBType, FinalSQLString>>();

    protected static interface OnUseCallback {
        public void noteUse(SQLString used);
    }
    //by default, no callback. This is set in OverridableSQLString
    protected static OnUseCallback callbackOnUse = new OnUseCallback(){
        @Override
        public void noteUse(SQLString used) {
           //do nothing
        }};

    protected static interface CallableCheckedException<T, E extends Exception> {
        T call() throws E;
    }

    /**
     * Runs the provided callable while holding the lock for the override caches.
     * Callers replacing the caches should hold this lock.
     */
    protected <T, E extends Exception> T runWithCacheLock(CallableCheckedException<T, E> callable) throws E {
        synchronized (cacheLock) {
            return callable.call();
        }
    }

    /**
{'fraction_non_alphanumeric': 0.09035197988686361, 'fraction_numerical': 0.0028284098051539913, 'mean_word_length': 3.930286599535244, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 24, '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': '7589661', 'n_tokens_mistral': 1749, 'n_tokens_neox': 1718, 'n_words': 560}