/* * 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.drill.exec.planner.sql; import java.io.IOException; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.parser.SqlParseException; import org.apache.calcite.tools.RelConversionException; import org.apache.calcite.tools.ValidationException; import org.apache.drill.common.exceptions.UserException; import org.apache.drill.exec.ops.QueryContext; import org.apache.drill.exec.physical.PhysicalPlan; import org.apache.drill.exec.planner.sql.handlers.AbstractSqlHandler; import org.apache.drill.exec.planner.sql.handlers.DefaultSqlHandler; import org.apache.drill.exec.planner.sql.handlers.ExplainHandler; import org.apache.drill.exec.planner.sql.handlers.SetOptionHandler; import org.apache.drill.exec.planner.sql.handlers.SqlHandlerConfig; import org.apache.drill.exec.planner.sql.parser.DrillSqlCall; import org.apache.drill.exec.planner.sql.parser.SqlCreateTable; import org.apache.drill.exec.testing.ControlsInjector; import org.apache.drill.exec.testing.ControlsInjectorFactory; import org.apache.drill.exec.util.Pointer; import org.apache.drill.exec.work.foreman.ForemanSetupException; import org.apache.drill.exec.work.foreman.SqlUnsupportedException; import org.apache.hadoop.security.AccessControlException; public class DrillSqlWorker { private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillSqlWorker.class); private static final ControlsInjector injector = ControlsInjectorFactory.getInjector(DrillSqlWorker.class); private DrillSqlWorker() { } /** * Converts sql query string into query physical plan. * * @param context query context * @param sql sql query * @return query physical plan */ public static PhysicalPlan getPlan(QueryContext context, String sql) throws SqlParseException, ValidationException, ForemanSetupException { return getPlan(context, sql, null); } /** * Converts sql query string into query physical plan. * In case of any errors (that might occur due to missing function implementation), * checks if local function registry should be synchronized with remote function registry. * If sync took place, reloads drill operator table * (since functions were added to / removed from local function registry) * and attempts to converts sql query string into query physical plan one more time. * * @param context query context * @param sql sql query * @param textPlan text plan * @return query physical plan */ public static PhysicalPlan getPlan(QueryContext context, String sql, Pointer<String> textPlan) throws ForemanSetupException { Pointer<String> textPlanCopy = textPlan == null ? null : new Pointer<>(textPlan.value); try { return getQueryPlan(context, sql, textPlan); } catch (Exception e) { if (context.getFunctionRegistry().syncWithRemoteRegistry( context.getDrillOperatorTable().getFunctionRegistryVersion())) { context.reloadDrillOperatorTable(); return getQueryPlan(context, sql, textPlanCopy); } throw e; } } /** * Converts sql query string into query physical plan. * * @param context query context * @param sql sql query * @param textPlan text plan * @return query physical plan */ private static PhysicalPlan getQueryPlan(QueryContext context, String sql, Pointer<String> textPlan) throws ForemanSetupException { final SqlConverter parser = new SqlConverter(context); injector.injectChecked(context.getExecutionControls(), "sql-parsing", ForemanSetupException.class); final SqlNode sqlNode = parser.parse(sql); final AbstractSqlHandler handler; final SqlHandlerConfig config = new SqlHandlerConfig(context, parser); switch(sqlNode.getKind()){ case EXPLAIN: handler = new ExplainHandler(config, textPlan); break; case SET_OPTION: handler = new SetOptionHandler(context); break; case OTHER: if(sqlNode instanceof SqlCreateTable) { handler = ((DrillSqlCall)sqlNode).getSqlHandler(config, textPlan); break; } if (sqlNode instanceof DrillSqlCall) { handler = ((DrillSqlCall)sqlNode).getSqlHandler(config); break; } // fallthrough default: handler = new DefaultSqlHandler(config, textPlan); } try { return handler.getPlan(sqlNode); } catch(ValidationException e) { String errorMessage = e.getCause() != null ? e.getCause().getMessage() : e.getMessage(); throw UserException.validationError(e) .message(errorMessage) .build(logger); } catch (AccessControlException e) { throw UserException.permissionError(e) .build(logger); } catch(SqlUnsupportedException e) { throw UserException.unsupportedError(e) .build(logger); } catch (IOException | RelConversionException e) { throw new QueryInputException("Failure handling SQL.", e); } } }