use of io.trino.sql.PlannerContext in project trino by trinodb.
the class TestPushProjectionIntoTableScan method createRule.
private static PushProjectionIntoTableScan createRule(RuleTester tester) {
PlannerContext plannerContext = tester.getPlannerContext();
TypeAnalyzer typeAnalyzer = tester.getTypeAnalyzer();
return new PushProjectionIntoTableScan(plannerContext, typeAnalyzer, new ScalarStatsCalculator(plannerContext, typeAnalyzer));
}
use of io.trino.sql.PlannerContext in project trino by trinodb.
the class CallTask method execute.
@Override
public ListenableFuture<Void> execute(Call call, QueryStateMachine stateMachine, List<Expression> parameters, WarningCollector warningCollector) {
if (!transactionManager.isAutoCommit(stateMachine.getSession().getRequiredTransactionId())) {
throw new TrinoException(NOT_SUPPORTED, "Procedures cannot be called within a transaction (use autocommit mode)");
}
Session session = stateMachine.getSession();
QualifiedObjectName procedureName = createQualifiedObjectName(session, call, call.getName());
CatalogName catalogName = plannerContext.getMetadata().getCatalogHandle(stateMachine.getSession(), procedureName.getCatalogName()).orElseThrow(() -> semanticException(CATALOG_NOT_FOUND, call, "Catalog '%s' does not exist", procedureName.getCatalogName()));
Procedure procedure = procedureRegistry.resolve(catalogName, procedureName.asSchemaTableName());
// map declared argument names to positions
Map<String, Integer> positions = new HashMap<>();
for (int i = 0; i < procedure.getArguments().size(); i++) {
positions.put(procedure.getArguments().get(i).getName(), i);
}
// per specification, do not allow mixing argument types
Predicate<CallArgument> hasName = argument -> argument.getName().isPresent();
boolean anyNamed = call.getArguments().stream().anyMatch(hasName);
boolean allNamed = call.getArguments().stream().allMatch(hasName);
if (anyNamed && !allNamed) {
throw semanticException(INVALID_ARGUMENTS, call, "Named and positional arguments cannot be mixed");
}
// get the argument names in call order
Map<String, CallArgument> names = new LinkedHashMap<>();
for (int i = 0; i < call.getArguments().size(); i++) {
CallArgument argument = call.getArguments().get(i);
if (argument.getName().isPresent()) {
String name = argument.getName().get().getCanonicalValue();
if (names.put(name, argument) != null) {
throw semanticException(INVALID_ARGUMENTS, argument, "Duplicate procedure argument: %s", name);
}
if (!positions.containsKey(name)) {
throw semanticException(INVALID_ARGUMENTS, argument, "Unknown argument name: %s", name);
}
} else if (i < procedure.getArguments().size()) {
names.put(procedure.getArguments().get(i).getName(), argument);
} else {
throw semanticException(INVALID_ARGUMENTS, call, "Too many arguments for procedure");
}
}
procedure.getArguments().stream().filter(Argument::isRequired).filter(argument -> !names.containsKey(argument.getName())).map(Argument::getName).findFirst().ifPresent(argument -> {
throw semanticException(INVALID_ARGUMENTS, call, "Required procedure argument '%s' is missing", argument);
});
// get argument values
Object[] values = new Object[procedure.getArguments().size()];
Map<NodeRef<Parameter>, Expression> parameterLookup = parameterExtractor(call, parameters);
for (Entry<String, CallArgument> entry : names.entrySet()) {
CallArgument callArgument = entry.getValue();
int index = positions.get(entry.getKey());
Argument argument = procedure.getArguments().get(index);
Expression expression = ExpressionTreeRewriter.rewriteWith(new ParameterRewriter(parameterLookup), callArgument.getValue());
Type type = argument.getType();
Object value = evaluateConstantExpression(expression, type, plannerContext, session, accessControl, parameterLookup);
values[index] = toTypeObjectValue(session, type, value);
}
// fill values with optional arguments defaults
for (int i = 0; i < procedure.getArguments().size(); i++) {
Argument argument = procedure.getArguments().get(i);
if (!names.containsKey(argument.getName())) {
verify(argument.isOptional());
values[i] = toTypeObjectValue(session, argument.getType(), argument.getDefaultValue());
}
}
// validate arguments
MethodType methodType = procedure.getMethodHandle().type();
for (int i = 0; i < procedure.getArguments().size(); i++) {
if ((values[i] == null) && methodType.parameterType(i).isPrimitive()) {
String name = procedure.getArguments().get(i).getName();
throw new TrinoException(INVALID_PROCEDURE_ARGUMENT, "Procedure argument cannot be null: " + name);
}
}
// insert session argument
List<Object> arguments = new ArrayList<>();
Iterator<Object> valuesIterator = asList(values).iterator();
for (Class<?> type : methodType.parameterList()) {
if (ConnectorSession.class.equals(type)) {
arguments.add(session.toConnectorSession(catalogName));
} else if (ConnectorAccessControl.class.equals(type)) {
arguments.add(new InjectedConnectorAccessControl(accessControl, session.toSecurityContext(), catalogName.getCatalogName()));
} else {
arguments.add(valuesIterator.next());
}
}
accessControl.checkCanExecuteProcedure(session.toSecurityContext(), procedureName);
stateMachine.setRoutines(ImmutableList.of(new RoutineInfo(procedureName.getObjectName(), session.getUser())));
try {
procedure.getMethodHandle().invokeWithArguments(arguments);
} catch (Throwable t) {
if (t instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
throwIfInstanceOf(t, TrinoException.class);
throw new TrinoException(PROCEDURE_CALL_FAILED, t);
}
return immediateVoidFuture();
}
use of io.trino.sql.PlannerContext in project trino by trinodb.
the class CreateTableTask method internalExecute.
@VisibleForTesting
ListenableFuture<Void> internalExecute(CreateTable statement, Session session, List<Expression> parameters, Consumer<Output> outputConsumer) {
checkArgument(!statement.getElements().isEmpty(), "no columns for table");
Map<NodeRef<Parameter>, Expression> parameterLookup = parameterExtractor(statement, parameters);
QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName());
Optional<TableHandle> tableHandle = plannerContext.getMetadata().getTableHandle(session, tableName);
if (tableHandle.isPresent()) {
if (!statement.isNotExists()) {
throw semanticException(TABLE_ALREADY_EXISTS, statement, "Table '%s' already exists", tableName);
}
return immediateVoidFuture();
}
CatalogName catalogName = getRequiredCatalogHandle(plannerContext.getMetadata(), session, statement, tableName.getCatalogName());
LinkedHashMap<String, ColumnMetadata> columns = new LinkedHashMap<>();
Map<String, Object> inheritedProperties = ImmutableMap.of();
boolean includingProperties = false;
for (TableElement element : statement.getElements()) {
if (element instanceof ColumnDefinition) {
ColumnDefinition column = (ColumnDefinition) element;
String name = column.getName().getValue().toLowerCase(Locale.ENGLISH);
Type type;
try {
type = plannerContext.getTypeManager().getType(toTypeSignature(column.getType()));
} catch (TypeNotFoundException e) {
throw semanticException(TYPE_NOT_FOUND, element, "Unknown type '%s' for column '%s'", column.getType(), column.getName());
}
if (type.equals(UNKNOWN)) {
throw semanticException(COLUMN_TYPE_UNKNOWN, element, "Unknown type '%s' for column '%s'", column.getType(), column.getName());
}
if (columns.containsKey(name)) {
throw semanticException(DUPLICATE_COLUMN_NAME, column, "Column name '%s' specified more than once", column.getName());
}
if (!column.isNullable() && !plannerContext.getMetadata().getConnectorCapabilities(session, catalogName).contains(NOT_NULL_COLUMN_CONSTRAINT)) {
throw semanticException(NOT_SUPPORTED, column, "Catalog '%s' does not support non-null column for column name '%s'", catalogName.getCatalogName(), column.getName());
}
Map<String, Object> columnProperties = columnPropertyManager.getProperties(catalogName, column.getProperties(), session, plannerContext, accessControl, parameterLookup, true);
columns.put(name, ColumnMetadata.builder().setName(name).setType(type).setNullable(column.isNullable()).setComment(column.getComment()).setProperties(columnProperties).build());
} else if (element instanceof LikeClause) {
LikeClause likeClause = (LikeClause) element;
QualifiedObjectName originalLikeTableName = createQualifiedObjectName(session, statement, likeClause.getTableName());
if (plannerContext.getMetadata().getCatalogHandle(session, originalLikeTableName.getCatalogName()).isEmpty()) {
throw semanticException(CATALOG_NOT_FOUND, statement, "LIKE table catalog '%s' does not exist", originalLikeTableName.getCatalogName());
}
RedirectionAwareTableHandle redirection = plannerContext.getMetadata().getRedirectionAwareTableHandle(session, originalLikeTableName);
TableHandle likeTable = redirection.getTableHandle().orElseThrow(() -> semanticException(TABLE_NOT_FOUND, statement, "LIKE table '%s' does not exist", originalLikeTableName));
QualifiedObjectName likeTableName = redirection.getRedirectedTableName().orElse(originalLikeTableName);
if (!tableName.getCatalogName().equals(likeTableName.getCatalogName())) {
String message = "CREATE TABLE LIKE across catalogs is not supported";
if (!originalLikeTableName.equals(likeTableName)) {
message += format(". LIKE table '%s' redirected to '%s'.", originalLikeTableName, likeTableName);
}
throw semanticException(NOT_SUPPORTED, statement, message);
}
TableMetadata likeTableMetadata = plannerContext.getMetadata().getTableMetadata(session, likeTable);
Optional<LikeClause.PropertiesOption> propertiesOption = likeClause.getPropertiesOption();
if (propertiesOption.isPresent() && propertiesOption.get() == LikeClause.PropertiesOption.INCLUDING) {
if (includingProperties) {
throw semanticException(NOT_SUPPORTED, statement, "Only one LIKE clause can specify INCLUDING PROPERTIES");
}
includingProperties = true;
inheritedProperties = likeTableMetadata.getMetadata().getProperties();
}
try {
accessControl.checkCanSelectFromColumns(session.toSecurityContext(), likeTableName, likeTableMetadata.getColumns().stream().map(ColumnMetadata::getName).collect(toImmutableSet()));
} catch (AccessDeniedException e) {
throw new AccessDeniedException("Cannot reference columns of table " + likeTableName);
}
if (propertiesOption.orElse(EXCLUDING) == INCLUDING) {
try {
accessControl.checkCanShowCreateTable(session.toSecurityContext(), likeTableName);
} catch (AccessDeniedException e) {
throw new AccessDeniedException("Cannot reference properties of table " + likeTableName);
}
}
likeTableMetadata.getColumns().stream().filter(column -> !column.isHidden()).forEach(column -> {
if (columns.containsKey(column.getName().toLowerCase(Locale.ENGLISH))) {
throw semanticException(DUPLICATE_COLUMN_NAME, element, "Column name '%s' specified more than once", column.getName());
}
columns.put(column.getName().toLowerCase(Locale.ENGLISH), column);
});
} else {
throw new TrinoException(GENERIC_INTERNAL_ERROR, "Invalid TableElement: " + element.getClass().getName());
}
}
Map<String, Object> properties = tablePropertyManager.getProperties(catalogName, statement.getProperties(), session, plannerContext, accessControl, parameterLookup, true);
accessControl.checkCanCreateTable(session.toSecurityContext(), tableName, properties);
Set<String> specifiedPropertyKeys = statement.getProperties().stream().map(property -> property.getName().getValue()).collect(toImmutableSet());
Map<String, Object> finalProperties = combineProperties(specifiedPropertyKeys, properties, inheritedProperties);
ConnectorTableMetadata tableMetadata = new ConnectorTableMetadata(tableName.asSchemaTableName(), ImmutableList.copyOf(columns.values()), finalProperties, statement.getComment());
try {
plannerContext.getMetadata().createTable(session, tableName.getCatalogName(), tableMetadata, statement.isNotExists());
} catch (TrinoException e) {
// connectors are not required to handle the ignoreExisting flag
if (!e.getErrorCode().equals(ALREADY_EXISTS.toErrorCode()) || !statement.isNotExists()) {
throw e;
}
}
outputConsumer.accept(new Output(tableName.getCatalogName(), tableName.getSchemaName(), tableName.getObjectName(), Optional.of(tableMetadata.getColumns().stream().map(column -> new OutputColumn(new Column(column.getName(), column.getType().toString()), ImmutableSet.of())).collect(toImmutableList()))));
return immediateVoidFuture();
}
use of io.trino.sql.PlannerContext in project trino by trinodb.
the class LocalDynamicFiltersCollector method createDynamicFilter.
// Called during TableScan planning (no need to be synchronized as local planning is single threaded)
public DynamicFilter createDynamicFilter(List<Descriptor> descriptors, Map<Symbol, ColumnHandle> columnsMap, TypeProvider typeProvider, PlannerContext plannerContext) {
Multimap<DynamicFilterId, Descriptor> descriptorMap = extractSourceSymbols(descriptors);
// Iterate over dynamic filters that are collected (correspond to one of the futures), and required for filtering (correspond to one of the descriptors).
// It is possible that some dynamic filters are collected in a different stage - and will not available here.
// It is also possible that not all local dynamic filters are needed for this specific table scan.
List<ListenableFuture<TupleDomain<ColumnHandle>>> predicateFutures = descriptorMap.keySet().stream().filter(futures.keySet()::contains).map(filterId -> {
// Probe-side columns that can be filtered with this dynamic filter resulting domain.
return Futures.transform(requireNonNull(futures.get(filterId), () -> format("Missing dynamic filter %s", filterId)), // Construct a probe-side predicate by duplicating the resulting domain over the corresponding columns.
domain -> TupleDomain.withColumnDomains(descriptorMap.get(filterId).stream().collect(toImmutableMap(descriptor -> {
Symbol probeSymbol = Symbol.from(descriptor.getInput());
return requireNonNull(columnsMap.get(probeSymbol), () -> format("Missing probe column for %s", probeSymbol));
}, descriptor -> {
Type targetType = typeProvider.get(Symbol.from(descriptor.getInput()));
Domain updatedDomain = descriptor.applyComparison(domain);
if (!updatedDomain.getType().equals(targetType)) {
return applySaturatedCasts(plannerContext.getMetadata(), plannerContext.getFunctionManager(), plannerContext.getTypeOperators(), session, updatedDomain, targetType);
}
return updatedDomain;
}))), directExecutor());
}).collect(toImmutableList());
Set<ColumnHandle> columnsCovered = descriptorMap.values().stream().map(Descriptor::getInput).map(Symbol::from).map(probeSymbol -> requireNonNull(columnsMap.get(probeSymbol), () -> "Missing probe column for " + probeSymbol)).collect(toImmutableSet());
return new TableSpecificDynamicFilter(columnsCovered, predicateFutures);
}
use of io.trino.sql.PlannerContext in project trino by trinodb.
the class LogicalPlanner method getInsertPlan.
private RelationPlan getInsertPlan(Analysis analysis, Table table, Query query, TableHandle tableHandle, List<ColumnHandle> insertColumns, Optional<TableLayout> newTableLayout, Optional<WriterTarget> materializedViewRefreshWriterTarget) {
TableMetadata tableMetadata = metadata.getTableMetadata(session, tableHandle);
Map<NodeRef<LambdaArgumentDeclaration>, Symbol> lambdaDeclarationToSymbolMap = buildLambdaDeclarationToSymbolMap(analysis, symbolAllocator);
RelationPlanner planner = new RelationPlanner(analysis, symbolAllocator, idAllocator, lambdaDeclarationToSymbolMap, plannerContext, Optional.empty(), session, ImmutableMap.of());
RelationPlan plan = planner.process(query, null);
ImmutableList.Builder<Symbol> builder = ImmutableList.builder();
for (int i = 0; i < plan.getFieldMappings().size(); i++) {
if (!plan.getDescriptor().getFieldByIndex(i).isHidden()) {
builder.add(plan.getFieldMappings().get(i));
}
}
List<Symbol> visibleFieldMappings = builder.build();
Map<String, ColumnHandle> columns = metadata.getColumnHandles(session, tableHandle);
Assignments.Builder assignments = Assignments.builder();
boolean supportsMissingColumnsOnInsert = metadata.supportsMissingColumnsOnInsert(session, tableHandle);
ImmutableList.Builder<ColumnMetadata> insertedColumnsBuilder = ImmutableList.builder();
for (ColumnMetadata column : tableMetadata.getColumns()) {
if (column.isHidden()) {
continue;
}
Symbol output = symbolAllocator.newSymbol(column.getName(), column.getType());
int index = insertColumns.indexOf(columns.get(column.getName()));
if (index < 0) {
if (supportsMissingColumnsOnInsert) {
continue;
}
Expression cast = new Cast(new NullLiteral(), toSqlType(column.getType()));
assignments.put(output, cast);
insertedColumnsBuilder.add(column);
} else {
Symbol input = visibleFieldMappings.get(index);
Type tableType = column.getType();
Type queryType = symbolAllocator.getTypes().get(input);
if (queryType.equals(tableType) || typeCoercion.isTypeOnlyCoercion(queryType, tableType)) {
assignments.put(output, input.toSymbolReference());
} else {
Expression cast = noTruncationCast(input.toSymbolReference(), queryType, tableType);
assignments.put(output, cast);
}
insertedColumnsBuilder.add(column);
}
}
ProjectNode projectNode = new ProjectNode(idAllocator.getNextId(), plan.getRoot(), assignments.build());
List<ColumnMetadata> insertedColumns = insertedColumnsBuilder.build();
List<Field> fields = insertedColumns.stream().map(column -> Field.newUnqualified(column.getName(), column.getType())).collect(toImmutableList());
Scope scope = Scope.builder().withRelationType(RelationId.anonymous(), new RelationType(fields)).build();
plan = new RelationPlan(projectNode, scope, projectNode.getOutputSymbols(), Optional.empty());
plan = planner.addRowFilters(table, plan, failIfPredicateIsNotMeet(metadata, session, PERMISSION_DENIED, AccessDeniedException.PREFIX + "Cannot insert row that does not match to a row filter"), node -> {
Scope accessControlScope = analysis.getAccessControlScope(table);
// hidden fields are not accessible in insert
return Scope.builder().like(accessControlScope).withRelationType(accessControlScope.getRelationId(), accessControlScope.getRelationType().withOnlyVisibleFields()).build();
});
List<String> insertedTableColumnNames = insertedColumns.stream().map(ColumnMetadata::getName).collect(toImmutableList());
String catalogName = tableHandle.getCatalogName().getCatalogName();
TableStatisticsMetadata statisticsMetadata = metadata.getStatisticsCollectionMetadataForWrite(session, catalogName, tableMetadata.getMetadata());
if (materializedViewRefreshWriterTarget.isPresent()) {
return createTableWriterPlan(analysis, plan.getRoot(), plan.getFieldMappings(), materializedViewRefreshWriterTarget.get(), insertedTableColumnNames, insertedColumns, newTableLayout, statisticsMetadata);
}
InsertReference insertTarget = new InsertReference(tableHandle, insertedTableColumnNames.stream().map(columns::get).collect(toImmutableList()));
return createTableWriterPlan(analysis, plan.getRoot(), plan.getFieldMappings(), insertTarget, insertedTableColumnNames, insertedColumns, newTableLayout, statisticsMetadata);
}
Aggregations