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

org.apache.flink.sql.parser.node.SqlToTreeConverter Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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
 *
 *     http://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.apache.flink.sql.parser.node;

import org.apache.flink.sql.parser.ddl.SqlCreateTable;
import org.apache.flink.sql.parser.ddl.SqlCreateView;
import org.apache.flink.sql.parser.plan.FlinkSqlValidator;

import org.apache.flink.shaded.guava18.com.google.common.collect.ImmutableList;

import org.apache.calcite.sql.JoinType;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlInsert;
import org.apache.calcite.sql.SqlJoin;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlMatchRecognize;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlSelect;
import org.apache.calcite.sql.SqlSnapshot;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.Util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
 * Converts a SQL parse tree. (consisting of
 * {@link org.apache.calcite.sql.SqlNode} objects) into a relational node tree
 * (consisting of {@link SqlTreeNode} objects)
 */
public class SqlToTreeConverter {

	private final Map context = new HashMap<>();
	private final List nodes = new ArrayList<>();
	private final FlinkSqlValidator validator;

	public SqlToTreeConverter(FlinkSqlValidator validator) {
		this.validator = validator;
	}

	/**
	 * Converts a query's parse tree into a relation tree.
	 *
	 * @param query sql query parser tree
	 */
	public void convertSql(SqlNode query) {
		SqlTreeNode node = convertQueryRecursive(query);
		if (node != null) {
			nodes.add(node);
		}
	}

	public String getJSON() {
		SqlTreeJSONGenerator generator = new SqlTreeJSONGenerator();
		return generator.getJSON(nodes);
	}

	/**
	 * Recursively converts a query to a tree node.
	 *
	 * @param query Query
	 * @return Relational expression
	 */
	private SqlTreeNode convertQueryRecursive(SqlNode query) {
		final SqlKind kind = query.getKind();
		switch (kind) {
			case SELECT:
				return convertSelect((SqlSelect) query);
			case INSERT:
				return convertInsert((SqlInsert) query);
			case CREATE_VIEW:
				return convertView((SqlCreateView) query);
			case CREATE_TABLE:
				return registerTable((SqlCreateTable) query);
			case UNION:
			case INTERSECT:
			case EXCEPT:
				return convertSetOp((SqlCall) query);
			case OTHER_DDL:
				// ignore create function ...
				return null;
			default:
				throw new AssertionError("not a query: " + query);
		}
	}

	private SqlTreeNode convertView(SqlCreateView view) {
		String viewName = view.getName();
		SqlTreeNode input = convertQueryRecursive(view.getQuery());
		context.put(viewName, input);
		return input;
	}

	private SqlTreeNode convertInsert(SqlInsert insert) {
		SqlTreeNode input = convertQueryRecursive(insert.getSource());
		String tableName = insert.getTargetTable().toString();
		return SqlTreeNodes.sink(insert.getParserPosition(), input, tableName);

	}

	private SqlTreeNode convertSelect(SqlSelect query) {
		SqlTreeNode input = convertFrom(query.getFrom());
		SqlTreeNode filter = convertWhere(input, query.getWhere());
		if (filter != null) {
			input = filter;
		}

		if (validator.isAggregate(query)) {
			SqlNodeList groupList = query.getGroup();
			if (groupList != null) {
				input = SqlTreeNodes.group(groupList.getParserPosition(), input, groupList);
			} else {
				// it's an aggregate without GROUP BY
				SqlNode agg = validator.getAggregate(query);
				requireNonNull(agg, "Can not find aggregate from query: " + query);
				input = SqlTreeNodes.group(agg.getParserPosition(), input);
			}
		} else {
			SqlNodeList selectList = query.getSelectList();
			input = SqlTreeNodes.select(query.getParserPosition(), input, selectList);
		}
		return convertOrder(query, input);
	}

	private SqlTreeNode convertSetOp(SqlCall call) {
		final SqlTreeNode left = convertQueryRecursive(call.operand(0));
		final SqlTreeNode right = convertQueryRecursive(call.operand(1));
		switch (call.getKind()) {
			case UNION:
				return SqlTreeNodes.union(call.getParserPosition(), ImmutableList.of(left, right));

			case INTERSECT:
				throw Util.unexpected(call.getKind());

			case EXCEPT:
				throw Util.unexpected(call.getKind());

			default:
				throw Util.unexpected(call.getKind());
		}
	}

	private SqlTreeNode convertOrder(SqlSelect select, SqlTreeNode input) {
		SqlNodeList orderBy = select.getOrderList();
		if (orderBy == null || orderBy.getList().isEmpty()) {
			return input;
		}

		return SqlTreeNodes.topn(orderBy.getParserPosition(), input, "");
	}

	private SqlTreeNode convertWhere(SqlTreeNode input, SqlNode where) {
		if (where == null) {
			return null;
		}
		return SqlTreeNodes.filter(where.getParserPosition(), input, where);
	}

	private SqlTreeNode convertFrom(SqlNode from) {
		SqlCall call;
		switch (from.getKind()) {
			case IDENTIFIER:
				String tableName = from.toString();
				return context.get(tableName);
			case AS:
				return convertFrom(((SqlCall) from).operand(0));
			case MATCH_RECOGNIZE:
				return convertMatchRecognize((SqlCall) from);
			case SNAPSHOT:
				return convertTemporal((SqlSnapshot) from);
			case JOIN:
				return convertJoin((SqlJoin) from);
			case SELECT:
			case INTERSECT:
			case EXCEPT:
			case UNION:
				return convertQueryRecursive(from);
			case LATERAL:
				call = (SqlCall) from;
				return convertFrom((call.getOperandList().get(0)));
			case COLLECTION_TABLE:
				call = (SqlCall) from;
				return SqlTreeNodes.udtf(from.getParserPosition(), call.getOperandList().get(0));
			case VALUES:
				throw new UnsupportedOperationException();
			default:
				throw new AssertionError("Not supported operator " + from);
		}
	}

	private SqlTreeNode convertJoin(SqlJoin join) {
		SqlNode left = join.getLeft();
		SqlNode right = join.getRight();
		SqlTreeNode leftNode = convertFrom(left);
		SqlTreeNode rightNode = convertFrom(right);
		if (rightNode instanceof SqlTreeNodes.SqlTableFunctionNode) {
			// udtf
			return SqlTreeNodes.correlate(join.getParserPosition(), leftNode, rightNode.explain());
		} else {
			JoinType joinType = join.getJoinType();
			return SqlTreeNodes.join(join.getParserPosition(), leftNode, rightNode, joinType);
		}
	}

	private SqlTreeNode convertTemporal(SqlSnapshot call) {
		String tableName = call.getTableRef().toString();
		return context.get(tableName);
	}

	private SqlTreeNode convertMatchRecognize(SqlCall call) {
		SqlMatchRecognize matchRecognize = (SqlMatchRecognize) call;
		SqlNode tableRef = matchRecognize.getTableRef();
		SqlTreeNode input = convertFrom(tableRef);
		SqlNode pattern = matchRecognize.getPattern();
		return SqlTreeNodes.cep(call.getParserPosition(), input, pattern);
	}

	/**
	 * Register source and dim tables.
	 */
	public SqlTreeNode registerTable(SqlCreateTable node) {
		SqlParserPos pos = node.getParserPosition();
		String tableName = node.getTableName().toString();
		SqlTreeNode table;
		switch (node.getTableType()) {
			case "SOURCE":
				table = SqlTreeNodes.source(pos, tableName);
				context.put(tableName, table);
				return table;
			case "SINK":
				// ignore
				return null;
			default:
				throw new IllegalArgumentException(
					"Illegal table type of SqlCreateTable: " + node.getTableType());
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy