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

org.springframework.data.relational.core.dialect.AbstractDialect Maven / Gradle / Ivy

There is a newer version: 3.3.2
Show newest version
/*
 * Copyright 2019-2023 the original author or authors.
 *
 * Licensed 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
 *
 *      https://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.springframework.data.relational.core.dialect;

import java.util.OptionalLong;
import java.util.function.Function;

import org.springframework.data.domain.Sort;
import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.data.relational.core.sql.LockOptions;
import org.springframework.data.relational.core.sql.Select;
import org.springframework.data.relational.core.sql.render.SelectRenderContext;

/**
 * Base class for {@link Dialect} implementations.
 *
 * @author Mark Paluch
 * @author Myeonghyeon Lee
 * @author Chirag Tailor
 * @since 1.1
 */
public abstract class AbstractDialect implements Dialect {

	@Override
	public SelectRenderContext getSelectContext() {

		Function afterFromTable = getAfterFromTable();
		Function afterOrderBy = getAfterOrderBy();

		return new DialectSelectRenderContext(afterFromTable, afterOrderBy, orderByNullHandling());
	}

	/**
	 * Returns a {@link Function afterFromTable Function}. Typically used for table hint for SQL Server.
	 *
	 * @return the {@link Function} called on {@code afterFromTable}.
	 */
	protected Function getAfterFromTable() {

		Function afterFromTable = select -> "";

		LockClause lockClause = lock();
		if (lockClause.getClausePosition() == LockClause.Position.AFTER_FROM_TABLE) {
			afterFromTable = new LockRenderFunction(lockClause);
		}

		return afterFromTable.andThen(PrependWithLeadingWhitespace.INSTANCE);
	}

	/**
	 * Returns a {@link Function afterOrderBy Function}. Typically used for pagination.
	 *
	 * @return the {@link Function} called on {@code afterOrderBy}.
	 */
	protected Function getAfterOrderBy() {

		Function afterOrderByLimit = getAfterOrderByLimit();
		Function afterOrderByLock = getAfterOrderByLock();

		return select -> String.valueOf(afterOrderByLimit.apply(select)) + afterOrderByLock.apply(select);
	}

	private Function getAfterOrderByLimit() {
		LimitClause limit = limit();

		if (limit.getClausePosition() == LimitClause.Position.AFTER_ORDER_BY) {
			return new AfterOrderByLimitRenderFunction(limit) //
					.andThen(PrependWithLeadingWhitespace.INSTANCE);
		} else {
			throw new UnsupportedOperationException(String.format("Clause position %s not supported!", limit));
		}
	}

	private Function getAfterOrderByLock() {
		LockClause lock = lock();

		Function afterOrderByLock = select -> "";

		if (lock.getClausePosition() == LockClause.Position.AFTER_ORDER_BY) {
			afterOrderByLock = new LockRenderFunction(lock);
		}

		return afterOrderByLock.andThen(PrependWithLeadingWhitespace.INSTANCE);
	}

	/**
	 * {@link SelectRenderContext} derived from {@link Dialect} specifics.
	 */
	static class DialectSelectRenderContext implements SelectRenderContext {

		private final Function afterFromTable;
		private final Function afterOrderBy;
		private final OrderByNullPrecedence orderByNullPrecedence;

		DialectSelectRenderContext(Function afterFromTable,
				Function afterOrderBy, OrderByNullPrecedence orderByNullPrecedence) {

			this.afterFromTable = afterFromTable;
			this.afterOrderBy = afterOrderBy;
			this.orderByNullPrecedence = orderByNullPrecedence;
		}

		@Override
		public Function afterFromTable() {
			return afterFromTable;
		}

		@Override
		public Function afterOrderBy(boolean hasOrderBy) {
			return afterOrderBy;
		}

		@Override
		public String evaluateOrderByNullHandling(Sort.NullHandling nullHandling) {
			return orderByNullPrecedence.evaluate(nullHandling);
		}
	}

	/**
	 * After {@code ORDER BY} function rendering the {@link LimitClause}.
	 */
	static class AfterOrderByLimitRenderFunction implements Function {

		private final LimitClause clause;

		public AfterOrderByLimitRenderFunction(LimitClause clause) {
			this.clause = clause;
		}

		@Override
		public CharSequence apply(Select select) {

			OptionalLong limit = select.getLimit();
			OptionalLong offset = select.getOffset();

			if (limit.isPresent() && offset.isPresent()) {
				return clause.getLimitOffset(limit.getAsLong(), offset.getAsLong());
			}

			if (limit.isPresent()) {
				return clause.getLimit(limit.getAsLong());
			}

			if (offset.isPresent()) {
				return clause.getOffset(offset.getAsLong());
			}

			return "";
		}
	}

	/**
	 * {@code LOCK} function rendering the {@link LockClause}.
	 */
	static class LockRenderFunction implements Function {

		private final LockClause clause;

		public LockRenderFunction(LockClause clause) {
			this.clause = clause;
		}

		@Override
		public CharSequence apply(Select select) {

			LockMode lockMode = select.getLockMode();

			if (lockMode == null) {
				return "";
			}

			return clause.getLock(new LockOptions(lockMode, select.getFrom()));
		}
	}

	/**
	 * Prepends a non-empty rendering result with a leading whitespace,
	 */
	enum PrependWithLeadingWhitespace implements Function {

		INSTANCE;

		@Override
		public CharSequence apply(CharSequence charSequence) {

			if (charSequence.length() == 0) {
				return charSequence;
			}

			return " " + charSequence;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy