Analysis support for table-valued functions

This commit is contained in:
Simon Binder 2020-01-26 13:51:53 +01:00
parent 223f1615ab
commit 5622ed5c43
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
5 changed files with 53 additions and 4 deletions

View File

@ -17,6 +17,12 @@ class AnalysisContext {
/// outside.
final AnalyzeStatementOptions stmtOptions;
/// Options passed to the surrounding engine.
///
/// This contains information about enabled sqlite modules and how to resolve
/// some functions.
final EngineOptions engineOptions;
/// Utilities to read types.
final SchemaFromCreateTable schemaSupport;
@ -40,11 +46,11 @@ class AnalysisContext {
TypeInferenceResults types2;
/// Constructs a new analysis context from the AST and the source sql.
AnalysisContext(this.root, this.sql, EngineOptions options,
AnalysisContext(this.root, this.sql, this.engineOptions,
{AnalyzeStatementOptions stmtOptions, this.schemaSupport})
: stmtOptions = stmtOptions ?? const AnalyzeStatementOptions() {
if (!options.enableExperimentalTypeInference) {
types = TypeResolver(this, options);
if (!engineOptions.enableExperimentalTypeInference) {
types = TypeResolver(this, engineOptions);
}
}

View File

@ -135,6 +135,29 @@ class ColumnResolver extends RecursiveVisitor<void, void> {
_handle(query, availableColumns);
}
},
isTableFunction: (function) {
final functions = context.engineOptions.addedFunctions;
final lowercaseName = function.name.toLowerCase();
ResultSet resolved;
if (functions.containsKey(lowercaseName)) {
final handler = functions[lowercaseName];
if (handler is TableValuedFunctionHandler) {
resolved = handler.resolveTableValued(context, function);
}
}
if (resolved == null) {
context.reportError(AnalysisError(
type: AnalysisErrorType.unknownFunction,
message: 'Could not extract the result set for this table function',
relevantNode: function,
));
} else {
function.resultSet = resolved;
availableColumns.addAll(resolved.resolvedColumns);
}
},
);
}

View File

@ -118,6 +118,12 @@ class AstPreparingVisitor extends RecursiveVisitor<void, void> {
// Queryables, so we can deal with them by visiting the children and
// dont't need to do anything here.
},
isTableFunction: (function) {
if (function.as != null) {
scope.register(function.as, function);
}
scope.register(function.name, function);
},
);
visitChildren(e, arg);

View File

@ -171,7 +171,7 @@ class UsingConstraint extends JoinConstraint {
}
class TableValuedFunction extends Queryable
implements TableOrSubquery, SqlInvocation, Renamable {
implements TableOrSubquery, SqlInvocation, Renamable, ResolvesToResultSet {
@override
final String name;
@ -181,6 +181,9 @@ class TableValuedFunction extends Queryable
@override
final FunctionParameters parameters;
@override
ResultSet resultSet;
TableValuedFunction(this.name, this.parameters, {this.as});
@override

View File

@ -47,6 +47,17 @@ abstract class FunctionHandler {
void reportErrors(SqlInvocation call, AnalysisContext context) {}
}
abstract class TableValuedFunctionHandler implements FunctionHandler {
/// Resolve the result set of a table-valued function.
///
/// Should return null when the result set can't be resolved.
///
/// See also:
/// - https://www.sqlite.org/vtab.html#tabfunc2
ResultSet resolveTableValued(
AnalysisContext context, TableValuedFunction call);
}
/// An sqlite module, which can be used in a `CREATE VIRTUAL TABLE` statement
/// to find providers.
abstract class Module implements Referencable, VisibleToChildren {