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

com.sap.cds.impl.ExceptionHandler Maven / Gradle / Ivy

There is a newer version: 3.6.1
Show newest version
/************************************************************************
 * © 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 ************************************************************************/
package com.sap.cds.impl;

import static com.sap.cds.reflect.impl.CdsAnnotatableImpl.removeAt;
import static com.sap.cds.reflect.impl.reader.model.CdsConstants.ANNOTATION_VALID_FROM;
import static com.sap.cds.util.CdsModelUtils.concreteKeyNames;

import java.sql.BatchUpdateException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.sap.cds.CdsDataStoreException;
import com.sap.cds.CdsException;
import com.sap.cds.CdsMissingValueException;
import com.sap.cds.NotNullConstraintException;
import com.sap.cds.UniqueConstraintException;
import com.sap.cds.jdbc.spi.ExceptionAnalyzer;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.util.OccUtils;

public class ExceptionHandler {
	private final CdsEntity entity;
	private final ExceptionAnalyzer exceptionAnalyzer;

	public ExceptionHandler(CdsEntity entity, ExceptionAnalyzer exceptionAnalyzer) {
		this.entity = entity;
		this.exceptionAnalyzer = exceptionAnalyzer;
	}

	public CdsException cdsBatchException(List> entries, int updatedCount, BatchUpdateException ex,
			String sql) {
		if (exceptionAnalyzer.isUniqueConstraint(ex)) {
			return new BatchExceptionHandler(entity).uniqueConstraint(entries, updatedCount, ex);
		}
		if (exceptionAnalyzer.isNotNullConstraint(ex)) {
			return new BatchExceptionHandler(entity).notNullConstraint(entries, updatedCount, ex, sql);
		}
		return dataStoreException(ex, sql);
	}

	public CdsException cdsException(Map entryValues, Exception ex, String sql) {
		Throwable cause = ExceptionAnalyzer.getRootCause(ex);
		if (cause instanceof SQLException sqlEx) {
			if (exceptionAnalyzer.isUniqueConstraint(sqlEx)) {
				return new UniqueConstraintException(entity, entryValues, concreteKeyNames(entity), ex);
			}
			if (exceptionAnalyzer.isNotNullConstraint(sqlEx)) {
				return notNullConstraintException(entity, entryValues, ex, sql);
			}
		}
		return dataStoreException(ex, sql);
	}

	public static CdsException cdsMissingValue(CdsEntity entity, CdsMissingValueException ex) {
		Optional versEl = OccUtils.getVersionElement(entity);
		if (versEl.isPresent()) {
			String versElName = versEl.get().getName();
			if ((OccUtils.versionParam(versElName)).equals(ex.getElementName())) {
				throw new CdsMissingValueException("Versioned entity '%s' must contain a value for element '%s'"
						.formatted(entity.getQualifiedName(), versElName), ex);
			}
		}
		throw ex;
	}

	public static CdsException dataStoreException(Exception ex, String sql) {
		if (ex instanceof SQLException e && isHanaHexEnforced(e)) {
			throw new HanaHexException(ex.getMessage());
		}
		return new CdsDataStoreException("Error executing the statement", new CdsDataStoreException("SQL: " + sql, ex));
	}

	private static boolean isHanaHexEnforced(SQLException e) {
		// HANA HEX enforced but cannot be selected
		return e.getErrorCode() == 256 && e.getMessage().contains("hex enforced but cannot be selected");
	}

	public static class HanaHexException extends CdsDataStoreException {
		private static final long serialVersionUID = 1L;

		public HanaHexException(String message) {
			super(message);
		}
	}

	public static void chainNextExceptions(SQLException ex) {
		SQLException next = ex.getNextException();
		if (ex.getCause() == null && next != null) {
			ex.initCause(next);
		}
	}

	private static List getNullValuedNotNullableElements(CdsEntity entity, Map entry) {
		Stream nullKeys = entity.keyElements().filter(e -> null == entry.get(e.getName()));
		Stream notNullEls = entity.elements()
				.filter(e -> (e.isNotNull() || e.findAnnotation(removeAt(ANNOTATION_VALID_FROM)).isPresent())
						&& null == entry.get(e.getName()));

		return Stream.concat(nullKeys, notNullEls).collect(Collectors.toList());
	}

	private static CdsException notNullConstraintException(CdsEntity entity, Map entryValues,
			Exception ex, String sql) {
		List nonNullableElements = getNullValuedNotNullableElements(entity, entryValues);
		if (nonNullableElements.isEmpty()) {
			return dataStoreException(ex, sql);
		}
		return new NotNullConstraintException(nonNullableElements, ex);
	}

	static class BatchExceptionHandler {

		private final CdsEntity entity;

		public BatchExceptionHandler(CdsEntity entity) {
			this.entity = entity;
		}

		private int getFirstNonPositiveIndex(int[] rcs) {
			for (int pos = 0; pos < rcs.length; pos++) {
				if (rcs[pos] < 0) {
					return pos;
				}
			}

			return rcs.length;
		}

		public CdsException uniqueConstraint(List> entries, int updatedCount,
				BatchUpdateException ex) {
			int[] updateCounts = ex.getUpdateCounts();
			int badPos = getFirstNonPositiveIndex(updateCounts);
			Map entry = entries.get(badPos + updatedCount);

			return new UniqueConstraintException(entity, entry, concreteKeyNames(entity), ex);
		}

		public CdsException notNullConstraint(List> entries, int updatedCount,
				BatchUpdateException ex, String sql) {
			int[] updateCounts = ex.getUpdateCounts();
			int badPos = getFirstNonPositiveIndex(updateCounts);
			Map entryValues = entries.get(badPos + updatedCount);

			return notNullConstraintException(entity, entryValues, ex, sql);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy