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

com.sap.cds.jdbc.hana.HanaStatementResolver Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/************************************************************************
 * © 2020-2023 SAP SE or an SAP affiliate company. All rights reserved. *
 ************************************************************************/
package com.sap.cds.jdbc.hana;

import static com.sap.cds.DataStoreConfiguration.IGNORE_LOCALE_ON_HANA;
import static com.sap.cds.impl.sql.SQLHelper.commaSeparated;
import static com.sap.cds.ql.impl.SelectBuilder.COLLATING_HANA;
import static com.sap.cds.ql.impl.SelectBuilder.COLLATING_HANA_OFF;
import static com.sap.cds.ql.impl.SelectBuilder.COLLATING_HANA_WITH_COLLATION;
import static java.util.stream.Collectors.joining;

import java.io.InputStream;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cds.CdsException;
import com.sap.cds.DataStoreConfiguration;
import com.sap.cds.impl.localized.LocaleUtils;
import com.sap.cds.impl.sql.SQLHelper;
import com.sap.cds.jdbc.spi.StatementResolver;
import com.sap.cds.ql.cqn.CqnLock.Mode;
import com.sap.cds.ql.cqn.CqnSortSpecification;
import com.sap.cds.ql.cqn.CqnStatement;

public class HanaStatementResolver implements StatementResolver {
	private static final Logger logger = LoggerFactory.getLogger(HanaStatementResolver.class);
	private static final Properties COLLATIONS = new Properties();

	private final boolean ignoreLocale;
	private final boolean hanaCloud;

	public HanaStatementResolver(DataStoreConfiguration dataStoreConfiguration, int majorVersion) {
		this.ignoreLocale = dataStoreConfiguration.getProperty(IGNORE_LOCALE_ON_HANA, false);
		this.hanaCloud = majorVersion >= 4;
		if (hanaCloud) {
			loadCollations();
		}
	}

	private static void loadCollations() {
		try (InputStream in = HanaStatementResolver.class.getClassLoader().getResourceAsStream("hana/collations.properties")) {
			COLLATIONS.load(in);
		} catch (Exception e) {
			throw new CdsException("Failed to load HANA collations");
		}
	}

	@Override
	public Optional collate(CqnSortSpecification o, Locale locale) {
		if (locale != null) {
			return collation("COLLATE ", locale);
		}
		return Optional.empty();
	}

	private static Optional collation(String prefix, Locale locale) {
		String collationName = getCollationName(locale);
		if (collationName == null) {
			logger.warn("Missing collation name for locale \"{}\"", locale);
			return Optional.empty();
		}
		return Optional.of(prefix + collationName);
	}

	private static String getCollationName(Locale locale) {
		String language = locale.getLanguage();
		if ("zh".equals(language) || "hr".equals(language)) {
			String collationName = COLLATIONS.getProperty(locale.toLanguageTag());
			if (collationName != null) {
				return collationName;
			}
		}
		return COLLATIONS.getProperty(language);
	}

	@Override
	public Optional withCollation(CqnStatement statement, Locale locale) {
		String collationType = (String) statement.hints().get(COLLATING_HANA);
		if (ignoreLocale || locale == null || COLLATING_HANA_OFF.equals(collationType)) {
			return Optional.empty();
		}
		if (COLLATING_HANA_WITH_COLLATION.equals(collationType) && supportsWithCollation(statement)) {
			return collation("WITH COLLATION ", locale);
		}
		return Optional.of(parametersLocale(locale));
	}

	private boolean supportsWithCollation(CqnStatement statement) {
		return hanaCloud && statement.isSelect();
	}

	private static String parametersLocale(Locale locale) {
		return "with parameters('LOCALE' = " + SQLHelper.literal(LocaleUtils.getLocaleString(locale)) + ")";
	}

	/**
	 * HANA Upsert: Updates rows in a table or inserts new rows.
	 * 
	 * https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/ea8b6773be584203bcd99da76844c5ed.html
	 */
	@Override
	public String upsert(String table, Stream keyColumns, Stream upsertColumns,
			Stream upsertValues) {
		String columns = commaSeparated(upsertColumns);
		String values = commaSeparated(upsertValues);

		return Stream.of("UPSERT", table, columns, "VALUES", values, "WITH PRIMARY KEY").collect(joining(" "));
	}

	@Override
	public String lockMode(Mode mode) {
		final String clause;
		switch (mode) {
		case SHARED:
			if (hanaCloud) {
				clause = "FOR SHARE LOCK";
				break;
			}
		default:
			clause = "FOR UPDATE";
		}
		return clause;
	}

	@Override
	public Optional timeoutClause(int timeoutSeconds) {
		if (timeoutSeconds > 0) {
			return Optional.of("WAIT " + timeoutSeconds);
		} else {
			return Optional.of("NOWAIT");
		}
	}

	@Override
	public Optional hints(Map hints) {
		List hanaHints = hints.keySet().stream().filter(h -> h.startsWith("hdb.")).map(h -> h.substring(4)).collect(Collectors.toList());
		if (hanaHints.isEmpty()) {
			return Optional.empty();
		}

		return Optional.of(hanaHints.stream().collect(Collectors.joining(", ", "WITH HINT(", ")")));
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy