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

com.sap.cds.jdbc.hana.hierarchies.HierarchyFunctionMapper Maven / Gradle / Ivy

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

import java.util.ArrayDeque;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.sap.cds.impl.Context;
import com.sap.cds.impl.PreparedCqnStmt.Parameter;
import com.sap.cds.impl.sql.SQLStatementBuilder.SQLStatement;
import com.sap.cds.impl.sql.SelectStatementBuilder;
import com.sap.cds.impl.sql.TokenToSQLTransformer;
import com.sap.cds.jdbc.spi.SqlMapping;
import com.sap.cds.jdbc.spi.TableFunctionMapper;
import com.sap.cds.jdbc.spi.TableNameResolver;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnSelect;
import com.sap.cds.ql.cqn.CqnSortSpecification;
import com.sap.cds.ql.cqn.CqnSource;
import com.sap.cds.ql.cqn.CqnStructuredTypeRef;
import com.sap.cds.ql.hana.CqnHierarchy;
import com.sap.cds.ql.hana.CqnHierarchyGenerator;
import com.sap.cds.ql.hana.CqnHierarchySubset;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsModel;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.util.CdsModelUtils;
import com.sap.cds.util.CqnStatementUtils;

public abstract class HierarchyFunctionMapper implements TableFunctionMapper {

	private static final String SOURCE = "SOURCE";
	private static final String LPAREN = "(";
	private static final String RPAREN = ")";

	protected final Context context;
	protected final T hierarchy;
	protected final List params;
	protected final TokenToSQLTransformer toSQL;
	protected final CdsModel model;

	private HierarchyFunctionMapper(Context context, List params, T hierarchy) {
		this.context = context;
		this.model = context.getCdsModel();
		this.hierarchy = hierarchy;
		this.params = params;
		this.toSQL = toSQL(context, params, hierarchy.source());
	}

	private static TokenToSQLTransformer toSQL(Context context, List params, CqnSource source) {
		if (source.isRef()) {
			CqnStructuredTypeRef ref = source.asRef();
			CdsEntity entity = CdsModelUtils.entity(context.getCdsModel(), ref);
			SqlMapping sqlMapping = context.getDbContext().getSqlMapping(entity);
			String tableName = sqlMapping.tableName();

			return TokenToSQLTransformer.notCollating(context, sqlMapping::columnName, entity, tableName, params);
		} else if (source.isSelect()) {
			CdsStructuredType rowType = CqnStatementUtils.rowType(context.getCdsModel(), source.asSelect());
			SqlMapping sqlMapping = context.getDbContext().getSqlMapping(rowType);

			return TokenToSQLTransformer.notCollating(context, sqlMapping::columnName, source.asSelect(), rowType,
					params);
		} else if (source.isTableFunction()) {
			return toSQL(context, params, source.asTableFunction().source());
		} else {
			throw new IllegalStateException();
		}
	}

	@Override
	public String toSQL() {
		StringBuilder sql = new StringBuilder(hierarchy.name());
		sql.append(LPAREN);
		sql.append(SOURCE);
		sql.append(" ");
		CqnSource source = hierarchy.source();
		if (source.isRef()) {
			CqnStructuredTypeRef ref = source.asRef();
			TableNameResolver resolver = context.getTableNameResolver();
			CdsEntity entity = CdsModelUtils.entity(context.getCdsModel(), ref);
			sql.append(resolver.tableName(entity));
		} else if (source.isSelect()) {
			sql.append(LPAREN);
			CqnSelect subquery = source.asSelect();
			SQLStatement stmt = new SelectStatementBuilder(context, params, subquery, new ArrayDeque<>(), true, true).build();
			sql.append(stmt.sql());
			sql.append(RPAREN);
		} else if (source instanceof CqnHierarchy inner) {
			String innerSQL = create(context, params, inner).toSQL();
			sql.append(innerSQL);
		} else {
			throw new IllegalStateException();
		}

		appendClauses(sql);

		sql.append(RPAREN);
		return new SQLStatement(sql.toString(), List.of()).sql();
	}

	protected CdsStructuredType rowType() {
		return CqnStatementUtils.rowType(model, hierarchy);
	}

	abstract void appendClauses(StringBuilder builder);

	@SuppressWarnings("unchecked")
	public static  HierarchyFunctionMapper create(Context context, List params,
			T hierarchy) {
		if (hierarchy.isGenerator()) {
			return (HierarchyFunctionMapper) new Generator(context, params, hierarchy.asGenerator());
		} else if (hierarchy.isHierarchySubset()) {
			return (HierarchyFunctionMapper) new HierarchySubset(context, params, hierarchy.asHierarchySubset());
		} else {
			throw new IllegalStateException();
		}
	}

	private static class Generator extends HierarchyFunctionMapper {

		public Generator(Context context, List params, CqnHierarchyGenerator hierarchy) {
			super(context, params, hierarchy);
		}

		private static final String SIBL_ORDER_BY = "SIBLING ORDER BY";
		private static final String DEPTH = "DEPTH";

		@Override
		void appendClauses(StringBuilder sql) {
			List siblingOrderBy = hierarchy.orderBy();
			if (!siblingOrderBy.isEmpty()) {
				appendSiblingOrderBy(sql, siblingOrderBy);
			}

			if (hierarchy.depth() != null) {
				sql.append(" ").append(DEPTH).append(" ").append(hierarchy.depth());
			}

			hierarchy.startWhere().map(w -> toSQL.toSQL(rowType(), w)).ifPresent(s -> 
				sql.append(" START WHERE ").append(s));

			sql.append(" NO CACHE");
		}

		private void appendSiblingOrderBy(StringBuilder sql, List siblingOrderBy) {
			sql.append(" ").append(SIBL_ORDER_BY).append(" ");

			sql.append(siblingOrderBy.stream().map(o -> sort(o)).collect(Collectors.joining(", ")));
		}

		private String sort(CqnSortSpecification o) {
			StringBuilder sort = new StringBuilder(toSQL.apply(o.value()));
			sort.append(" " + SelectStatementBuilder.sortOrderToSql(o));
			return sort.toString();
		}
	}

	private static class HierarchySubset extends HierarchyFunctionMapper {

		public HierarchySubset(Context context, List params, CqnHierarchySubset hierarchyNavigation) {
			super(context, params, hierarchyNavigation);
		}

		@Override
		void appendClauses(StringBuilder sql) {
			Optional startWhere = hierarchy.startWhere();
			startWhere.map(w -> toSQL.toSQL(rowType(), w)).ifPresent(s ->
				sql.append(" START WHERE ").append(s));

			// distance
			sql.append(" DISTANCE").append(optional(" FROM ", hierarchy.from())).append(optional(" TO ", hierarchy.to()));
		}

		String optional(String s, int i) {
			if (Integer.MAX_VALUE == i || Integer.MIN_VALUE == i) {
				return "";
			}
			return s + i;
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy