Build options to enable types2 inference

This commit is contained in:
Simon Binder 2020-01-04 22:29:21 +01:00
parent 523eabaa2a
commit 70259c8f83
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
10 changed files with 98 additions and 15 deletions

View File

@ -52,6 +52,9 @@ At the moment, moor supports these options:
column constraint (e.g. `user_name VARCHAR NOT NULL JSON KEY userName`) column constraint (e.g. `user_name VARCHAR NOT NULL JSON KEY userName`)
* `generate_connect_constructor`: Generate necessary code to support the [isolate runtime]({{< relref "isolates.md" >}}). * `generate_connect_constructor`: Generate necessary code to support the [isolate runtime]({{< relref "isolates.md" >}}).
This is a build option because isolates are still experimental. This will be the default option eventually. This is a build option because isolates are still experimental. This will be the default option eventually.
* `use_experimental_inference`: Use a new, experimental type inference algorithm when analyzing sql statements. The
algorithm is designed to yield more accurate results on nullability and complex constructs. Note that it's in a
preview state at the moment, which means that generated code might change after a minor update.
* `sqlite_modules`: This list can be used to enable sqlite extensions, like those for json or full-text search. * `sqlite_modules`: This list can be used to enable sqlite extensions, like those for json or full-text search.
Modules have to be enabled explicitly because they're not supported on all platforms. See the following section for Modules have to be enabled explicitly because they're not supported on all platforms. See the following section for
details. details.

View File

@ -54,6 +54,12 @@ class MoorOptions {
@JsonKey(name: 'generate_connect_constructor', defaultValue: false) @JsonKey(name: 'generate_connect_constructor', defaultValue: false)
final bool generateConnectConstructor; final bool generateConnectConstructor;
/// Whether the new, experimental type inference engine should be used.
///
/// The new engine is experimental at the moment.
@JsonKey(name: 'use_experimental_inference', defaultValue: false)
final bool useExperimentalInference;
@JsonKey(name: 'sqlite_modules', defaultValue: []) @JsonKey(name: 'sqlite_modules', defaultValue: [])
final List<SqlModule> modules; final List<SqlModule> modules;
@ -68,6 +74,7 @@ class MoorOptions {
this.useDataClassNameForCompanions = false, this.useDataClassNameForCompanions = false,
this.useColumnNameAsJsonKeyWhenDefinedInMoorFile = false, this.useColumnNameAsJsonKeyWhenDefinedInMoorFile = false,
this.generateConnectConstructor = false, this.generateConnectConstructor = false,
this.useExperimentalInference,
this.modules = const []}); this.modules = const []});
factory MoorOptions.fromJson(Map<String, dynamic> json) => factory MoorOptions.fromJson(Map<String, dynamic> json) =>

View File

@ -16,6 +16,7 @@ MoorOptions _$MoorOptionsFromJson(Map<String, dynamic> json) {
'use_data_class_name_for_companions', 'use_data_class_name_for_companions',
'use_column_name_as_json_key_when_defined_in_moor_file', 'use_column_name_as_json_key_when_defined_in_moor_file',
'generate_connect_constructor', 'generate_connect_constructor',
'use_experimental_inference',
'sqlite_modules' 'sqlite_modules'
]); ]);
final val = MoorOptions( final val = MoorOptions(
@ -42,6 +43,9 @@ MoorOptions _$MoorOptionsFromJson(Map<String, dynamic> json) {
generateConnectConstructor: $checkedConvert( generateConnectConstructor: $checkedConvert(
json, 'generate_connect_constructor', (v) => v as bool) ?? json, 'generate_connect_constructor', (v) => v as bool) ??
false, false,
useExperimentalInference: $checkedConvert(
json, 'use_experimental_inference', (v) => v as bool) ??
false,
modules: $checkedConvert( modules: $checkedConvert(
json, json,
'sqlite_modules', 'sqlite_modules',
@ -61,6 +65,7 @@ MoorOptions _$MoorOptionsFromJson(Map<String, dynamic> json) {
'useColumnNameAsJsonKeyWhenDefinedInMoorFile': 'useColumnNameAsJsonKeyWhenDefinedInMoorFile':
'use_column_name_as_json_key_when_defined_in_moor_file', 'use_column_name_as_json_key_when_defined_in_moor_file',
'generateConnectConstructor': 'generate_connect_constructor', 'generateConnectConstructor': 'generate_connect_constructor',
'useExperimentalInference': 'use_experimental_inference',
'modules': 'sqlite_modules' 'modules': 'sqlite_modules'
}); });
} }

View File

@ -35,11 +35,16 @@ class MoorSession {
/// Creates a properly configured [SqlEngine]. /// Creates a properly configured [SqlEngine].
SqlEngine spawnEngine() { SqlEngine spawnEngine() {
return SqlEngine( final sqlOptions = EngineOptions(
useMoorExtensions: true, useMoorExtensions: true,
enableJson1Module: options.hasModule(SqlModule.json1), enableJson1: options.hasModule(SqlModule.json1),
enableFts5: options.hasModule(SqlModule.fts5), enabledExtensions: [
if (options.hasModule(SqlModule.fts5)) const Fts5Extension(),
],
enableExperimentalTypeInference: options.useExperimentalInference,
); );
return SqlEngine.withOptions(sqlOptions);
} }
FileType _findFileType(String path) { FileType _findFileType(String path) {

View File

@ -3,7 +3,9 @@ library sqlparser;
export 'src/analysis/analysis.dart'; export 'src/analysis/analysis.dart';
export 'src/ast/ast.dart'; export 'src/ast/ast.dart';
export 'src/engine/module/fts5.dart' show Fts5Extension;
export 'src/engine/module/module.dart'; export 'src/engine/module/module.dart';
export 'src/engine/options.dart';
export 'src/engine/sql_engine.dart'; export 'src/engine/sql_engine.dart';
export 'src/reader/parser/parser.dart' show ParsingError; export 'src/reader/parser/parser.dart' show ParsingError;
export 'src/reader/syntactic_entity.dart'; export 'src/reader/syntactic_entity.dart';

View File

@ -7,6 +7,9 @@ import 'package:sqlparser/src/engine/options.dart';
import 'package:sqlparser/src/reader/tokenizer/token.dart'; import 'package:sqlparser/src/reader/tokenizer/token.dart';
import 'package:sqlparser/src/utils/meta.dart'; import 'package:sqlparser/src/utils/meta.dart';
import 'types2/types.dart';
export 'types2/types.dart' show TypeInferenceResults;
part 'context.dart'; part 'context.dart';
part 'error.dart'; part 'error.dart';
part 'schema/column.dart'; part 'schema/column.dart';

View File

@ -25,12 +25,28 @@ class AnalysisContext {
/// [ResultSet.resolvedColumns] of a select statement. /// [ResultSet.resolvedColumns] of a select statement.
/* late final */ TypeResolver types; /* late final */ TypeResolver types;
/// Experimental new type resolver with better support for nullability and
/// complex structures.
///
/// By using [TypeInferenceResults.typeOf], the type of an [Expression],
/// a [Variable] and [ResultSet.resolvedColumns] may be resolved or inferred.
///
/// This field is null when experimental type inference is disabled.
///
/// Please note that types2 is experimental at the moment. Changes to how
/// [types] resolves types are considered breaking and are handled
/// accordingly. [types2] may change results in any update.
@experimental
TypeInferenceResults types2;
/// Constructs a new analysis context from the AST and the source sql. /// Constructs a new analysis context from the AST and the source sql.
AnalysisContext(this.root, this.sql, EngineOptions options, AnalysisContext(this.root, this.sql, EngineOptions options,
{AnalyzeStatementOptions stmtOptions, this.schemaSupport}) {AnalyzeStatementOptions stmtOptions, this.schemaSupport})
: stmtOptions = stmtOptions ?? const AnalyzeStatementOptions() { : stmtOptions = stmtOptions ?? const AnalyzeStatementOptions() {
if (!options.enableExperimentalTypeInference) {
types = TypeResolver(this, options); types = TypeResolver(this, options);
} }
}
/// Reports an analysis error. /// Reports an analysis error.
void reportError(AnalysisError error) { void reportError(AnalysisError error) {

View File

@ -11,8 +11,11 @@ part 'resolving_visitor.dart';
class TypeInferenceSession { class TypeInferenceSession {
final TypeGraph graph = TypeGraph(); final TypeGraph graph = TypeGraph();
final AnalysisContext context; final AnalysisContext context;
TypeInferenceResults results;
TypeInferenceSession(this.context); TypeInferenceSession(this.context) {
results = TypeInferenceResults._(this);
}
void markTypeResolved(Typeable t, ResolvedType r) { void markTypeResolved(Typeable t, ResolvedType r) {
graph[t] = r; graph[t] = r;
@ -42,3 +45,16 @@ class TypeInferenceSession {
graph.performResolve(); graph.performResolve();
} }
} }
/// Apis to view results of a type inference session.
class TypeInferenceResults {
final TypeInferenceSession session;
TypeInferenceResults._(this.session);
/// Finds the resolved type of [t], or `null` if the type of [t] could not
/// be inferred.
ResolvedType typeOf(Typeable t) {
return session.typeOf(t);
}
}

View File

@ -9,14 +9,21 @@ class EngineOptions {
/// Enables functions declared in the `json1` module for analysis /// Enables functions declared in the `json1` module for analysis
final bool enableJson1; final bool enableJson1;
/// Enables the new, experimental type inference.
final bool enableExperimentalTypeInference;
/// All [Extension]s that have been enabled in this sql engine. /// All [Extension]s that have been enabled in this sql engine.
final List<Extension> enabledExtensions; final List<Extension> enabledExtensions;
final List<FunctionHandler> _addedFunctionHandlers = []; final List<FunctionHandler> _addedFunctionHandlers = [];
final Map<String, FunctionHandler> addedFunctions = {}; final Map<String, FunctionHandler> addedFunctions = {};
EngineOptions( EngineOptions({
this.useMoorExtensions, this.enableJson1, this.enabledExtensions); this.useMoorExtensions = false,
this.enableJson1 = false,
this.enabledExtensions = const [],
this.enableExperimentalTypeInference = false,
});
void addFunctionHandler(FunctionHandler handler) { void addFunctionHandler(FunctionHandler handler) {
_addedFunctionHandlers.add(handler); _addedFunctionHandlers.add(handler);

View File

@ -1,6 +1,7 @@
import 'dart:collection'; import 'dart:collection';
import 'package:sqlparser/sqlparser.dart'; import 'package:sqlparser/sqlparser.dart';
import 'package:sqlparser/src/analysis/types2/types.dart' as t2;
import 'package:sqlparser/src/engine/module/fts5.dart'; import 'package:sqlparser/src/engine/module/fts5.dart';
import 'package:sqlparser/src/engine/options.dart'; import 'package:sqlparser/src/engine/options.dart';
import 'package:sqlparser/src/reader/parser/parser.dart'; import 'package:sqlparser/src/reader/parser/parser.dart';
@ -20,14 +21,18 @@ class SqlEngine {
SchemaFromCreateTable _schemaReader; SchemaFromCreateTable _schemaReader;
@Deprecated('Use SqlEngine.withOptions instead')
SqlEngine( SqlEngine(
{bool useMoorExtensions = false, {bool useMoorExtensions = false,
bool enableJson1Module = false, bool enableJson1Module = false,
bool enableFts5 = false}) bool enableFts5 = false})
: options = _constructOptions( : this.withOptions(_constructOptions(
moor: useMoorExtensions, moor: useMoorExtensions,
json1: enableJson1Module, json1: enableJson1Module,
fts5: enableFts5) { fts5: enableFts5,
));
SqlEngine.withOptions(this.options) {
for (final extension in options.enabledExtensions) { for (final extension in options.enabledExtensions) {
extension.register(this); extension.register(this);
} }
@ -188,9 +193,19 @@ class SqlEngine {
node node
..acceptWithoutArg(ColumnResolver(context)) ..acceptWithoutArg(ColumnResolver(context))
..acceptWithoutArg(ReferenceResolver(context)) ..acceptWithoutArg(ReferenceResolver(context));
..acceptWithoutArg(TypeResolvingVisitor(context))
..acceptWithoutArg(LintingVisitor(options, context)); if (options.enableExperimentalTypeInference) {
final session = t2.TypeInferenceSession(context);
final resolver = t2.TypeResolver(session);
node.acceptWithoutArg(resolver);
session.finish();
context.types2 = session.results;
} else {
node.acceptWithoutArg(TypeResolvingVisitor(context));
}
node.acceptWithoutArg(LintingVisitor(options, context));
} catch (_) { } catch (_) {
rethrow; rethrow;
} }
@ -210,7 +225,11 @@ class SqlEngine {
final extensions = [ final extensions = [
if (fts5) const Fts5Extension(), if (fts5) const Fts5Extension(),
]; ];
return EngineOptions(moor, json1, extensions); return EngineOptions(
useMoorExtensions: moor,
enableJson1: json1,
enabledExtensions: extensions,
);
} }
} }