From 3dfe5d4ed477c46b8e773e3c4d6b190664babac0 Mon Sep 17 00:00:00 2001 From: Vladimir Sitnikov Date: Thu, 28 May 2020 19:04:40 +0300 Subject: [PATCH] [CALCITE-4199] Add nullability annotations --- .../java/org/apache/calcite/DataContext.java | 10 +- .../calcite/adapter/clone/ArrayTable.java | 46 +- .../calcite/adapter/clone/ColumnLoader.java | 27 +- .../adapter/enumerable/AggAddContext.java | 4 +- .../adapter/enumerable/AggImpState.java | 14 +- .../adapter/enumerable/AggResultContext.java | 4 +- .../calcite/adapter/enumerable/EnumUtils.java | 166 ++-- .../enumerable/EnumerableAggregate.java | 17 +- .../enumerable/EnumerableAggregateBase.java | 19 +- .../enumerable/EnumerableAggregateRule.java | 4 +- .../EnumerableBatchNestedLoopJoin.java | 6 +- .../enumerable/EnumerableBindable.java | 8 +- .../adapter/enumerable/EnumerableCalc.java | 6 +- .../enumerable/EnumerableConvention.java | 8 +- .../enumerable/EnumerableCorrelate.java | 6 +- .../adapter/enumerable/EnumerableFilter.java | 6 +- .../enumerable/EnumerableHashJoin.java | 6 +- .../enumerable/EnumerableInterpretable.java | 23 +- .../enumerable/EnumerableIntersect.java | 4 +- .../adapter/enumerable/EnumerableLimit.java | 14 +- .../enumerable/EnumerableLimitSort.java | 16 +- .../adapter/enumerable/EnumerableMatch.java | 20 +- .../enumerable/EnumerableMergeJoin.java | 42 +- .../enumerable/EnumerableMergeJoinRule.java | 4 +- .../adapter/enumerable/EnumerableMinus.java | 6 +- .../enumerable/EnumerableNestedLoopJoin.java | 6 +- .../adapter/enumerable/EnumerableProject.java | 6 +- .../adapter/enumerable/EnumerableRel.java | 6 +- .../enumerable/EnumerableRelFactories.java | 7 +- .../enumerable/EnumerableRelImplementor.java | 6 +- .../adapter/enumerable/EnumerableSort.java | 14 +- .../enumerable/EnumerableSortRule.java | 4 +- .../enumerable/EnumerableSortedAggregate.java | 17 +- .../EnumerableSortedAggregateRule.java | 4 +- .../EnumerableTableFunctionScan.java | 19 +- .../enumerable/EnumerableTableModify.java | 6 +- .../enumerable/EnumerableTableModifyRule.java | 4 +- .../enumerable/EnumerableTableScan.java | 19 +- .../enumerable/EnumerableTableScanRule.java | 4 +- .../enumerable/EnumerableTraitsUtils.java | 9 +- .../adapter/enumerable/EnumerableUnion.java | 4 +- .../adapter/enumerable/EnumerableValues.java | 12 +- .../adapter/enumerable/EnumerableWindow.java | 63 +- .../adapter/enumerable/JavaRowFormat.java | 16 +- .../enumerable/NestedBlockBuilderImpl.java | 1 + .../calcite/adapter/enumerable/PhysType.java | 6 +- .../adapter/enumerable/PhysTypeImpl.java | 12 +- .../adapter/enumerable/RexImpTable.java | 74 +- .../enumerable/RexToLixTranslator.java | 113 +-- .../enumerable/StrictAggImplementor.java | 5 +- .../enumerable/impl/AggResultContextImpl.java | 27 +- .../adapter/java/ReflectiveSchema.java | 37 +- .../adapter/jdbc/JdbcCatalogSchema.java | 20 +- .../adapter/jdbc/JdbcQueryProvider.java | 4 +- .../calcite/adapter/jdbc/JdbcRules.java | 54 +- .../calcite/adapter/jdbc/JdbcSchema.java | 65 +- .../calcite/adapter/jdbc/JdbcTable.java | 27 +- .../calcite/adapter/jdbc/JdbcTableScan.java | 4 +- .../jdbc/JdbcToEnumerableConverter.java | 11 +- .../jdbc/JdbcToEnumerableConverterRule.java | 4 +- .../calcite/adapter/jdbc/JdbcUtils.java | 38 +- .../config/CalciteConnectionConfig.java | 13 +- .../config/CalciteConnectionConfigImpl.java | 19 +- .../config/CalciteConnectionProperty.java | 16 +- .../calcite/config/CalciteSystemProperty.java | 7 +- .../calcite/interpreter/AggregateNode.java | 69 +- .../interpreter/BindableConvention.java | 4 +- .../apache/calcite/interpreter/Bindables.java | 46 +- .../calcite/interpreter/CollectNode.java | 4 +- .../apache/calcite/interpreter/Compiler.java | 4 +- .../apache/calcite/interpreter/Context.java | 5 +- .../interpreter/InterpretableConverter.java | 4 +- .../calcite/interpreter/InterpretableRel.java | 6 +- .../calcite/interpreter/Interpreter.java | 86 +- .../calcite/interpreter/Interpreters.java | 4 +- .../apache/calcite/interpreter/JoinNode.java | 12 +- .../org/apache/calcite/interpreter/Nodes.java | 4 +- .../NoneToBindableConverterRule.java | 4 +- .../org/apache/calcite/interpreter/Row.java | 28 +- .../apache/calcite/interpreter/Scalar.java | 6 +- .../apache/calcite/interpreter/SortNode.java | 12 +- .../apache/calcite/interpreter/Source.java | 4 +- .../calcite/interpreter/TableScanNode.java | 33 +- .../calcite/interpreter/ValuesNode.java | 5 +- .../calcite/jdbc/CachingCalciteSchema.java | 38 +- .../calcite/jdbc/CalciteConnectionImpl.java | 36 +- .../apache/calcite/jdbc/CalciteFactory.java | 4 +- .../calcite/jdbc/CalciteJdbc41Factory.java | 44 +- .../apache/calcite/jdbc/CalciteMetaImpl.java | 23 +- .../apache/calcite/jdbc/CalcitePrepare.java | 62 +- .../apache/calcite/jdbc/CalciteSchema.java | 61 +- .../java/org/apache/calcite/jdbc/Driver.java | 17 +- .../apache/calcite/jdbc/JavaCollation.java | 9 +- .../apache/calcite/jdbc/JavaRecordType.java | 6 +- .../calcite/jdbc/JavaTypeFactoryImpl.java | 27 +- .../calcite/jdbc/SimpleCalciteSchema.java | 32 +- .../apache/calcite/materialize/Lattice.java | 112 +-- .../calcite/materialize/LatticeChildNode.java | 7 +- .../calcite/materialize/LatticeNode.java | 17 +- .../calcite/materialize/LatticeRootNode.java | 1 + .../calcite/materialize/LatticeSpace.java | 9 +- .../calcite/materialize/LatticeSuggester.java | 62 +- .../calcite/materialize/LatticeTable.java | 9 +- .../materialize/MaterializationActor.java | 18 +- .../materialize/MaterializationKey.java | 4 +- .../materialize/MaterializationService.java | 59 +- .../calcite/materialize/MutableNode.java | 21 +- .../org/apache/calcite/materialize/Path.java | 4 +- .../SqlLatticeStatisticProvider.java | 7 +- .../org/apache/calcite/materialize/Step.java | 11 +- .../apache/calcite/materialize/TileKey.java | 4 +- .../calcite/materialize/TileSuggester.java | 7 +- .../org/apache/calcite/model/JsonColumn.java | 12 +- .../calcite/model/JsonCustomSchema.java | 25 +- .../apache/calcite/model/JsonCustomTable.java | 23 +- .../apache/calcite/model/JsonFunction.java | 27 +- .../apache/calcite/model/JsonJdbcSchema.java | 48 +- .../org/apache/calcite/model/JsonLattice.java | 60 +- .../apache/calcite/model/JsonMapSchema.java | 14 + .../calcite/model/JsonMaterialization.java | 27 +- .../org/apache/calcite/model/JsonMeasure.java | 19 +- .../org/apache/calcite/model/JsonRoot.java | 19 +- .../org/apache/calcite/model/JsonSchema.java | 18 +- .../org/apache/calcite/model/JsonStream.java | 17 +- .../org/apache/calcite/model/JsonTable.java | 13 +- .../org/apache/calcite/model/JsonTile.java | 14 +- .../org/apache/calcite/model/JsonType.java | 19 +- .../calcite/model/JsonTypeAttribute.java | 17 +- .../org/apache/calcite/model/JsonView.java | 25 +- .../apache/calcite/model/ModelHandler.java | 121 ++- .../calcite/plan/AbstractRelOptPlanner.java | 42 +- .../org/apache/calcite/plan/Contexts.java | 10 +- .../org/apache/calcite/plan/Convention.java | 6 +- .../calcite/plan/ConventionTraitDef.java | 17 +- .../calcite/plan/RelCompositeTrait.java | 4 +- .../calcite/plan/RelOptAbstractTable.java | 14 +- .../apache/calcite/plan/RelOptCluster.java | 28 +- .../apache/calcite/plan/RelOptCostImpl.java | 4 +- .../apache/calcite/plan/RelOptLattice.java | 7 +- .../apache/calcite/plan/RelOptListener.java | 10 +- .../calcite/plan/RelOptMaterialization.java | 49 +- .../calcite/plan/RelOptMaterializations.java | 2 +- .../apache/calcite/plan/RelOptPlanner.java | 19 +- .../calcite/plan/RelOptPredicateList.java | 17 + .../org/apache/calcite/plan/RelOptQuery.java | 4 +- .../org/apache/calcite/plan/RelOptRule.java | 22 +- .../apache/calcite/plan/RelOptRuleCall.java | 9 +- .../calcite/plan/RelOptRuleOperand.java | 26 +- .../org/apache/calcite/plan/RelOptSchema.java | 4 +- .../plan/RelOptSchemaWithSampling.java | 8 +- .../org/apache/calcite/plan/RelOptTable.java | 16 +- .../org/apache/calcite/plan/RelOptUtil.java | 188 +++-- .../java/org/apache/calcite/plan/RelRule.java | 11 +- .../org/apache/calcite/plan/RelTrait.java | 4 +- .../org/apache/calcite/plan/RelTraitDef.java | 4 +- .../plan/RelTraitPropagationVisitor.java | 4 +- .../org/apache/calcite/plan/RelTraitSet.java | 30 +- .../calcite/plan/RexImplicationChecker.java | 49 +- .../calcite/plan/SubstitutionVisitor.java | 102 +-- .../apache/calcite/plan/TableAccessMap.java | 5 +- .../apache/calcite/plan/ViewExpanders.java | 10 +- .../calcite/plan/VisitorDataContext.java | 33 +- .../calcite/plan/hep/HepInstruction.java | 29 +- .../apache/calcite/plan/hep/HepPlanner.java | 100 ++- .../apache/calcite/plan/hep/HepProgram.java | 6 +- .../calcite/plan/hep/HepProgramBuilder.java | 11 +- .../plan/hep/HepRelMetadataProvider.java | 17 +- .../apache/calcite/plan/hep/HepRelVertex.java | 4 +- .../apache/calcite/plan/hep/HepRuleCall.java | 4 +- .../plan/volcano/AbstractConverter.java | 4 +- .../apache/calcite/plan/volcano/Dumpers.java | 29 +- .../plan/volcano/IterativeRuleDriver.java | 5 +- .../plan/volcano/IterativeRuleQueue.java | 14 +- .../apache/calcite/plan/volcano/RelSet.java | 30 +- .../calcite/plan/volcano/RelSubset.java | 63 +- .../calcite/plan/volcano/RuleQueue.java | 2 +- .../plan/volcano/TopDownRuleDriver.java | 64 +- .../plan/volcano/TopDownRuleQueue.java | 4 +- .../calcite/plan/volcano/VolcanoCost.java | 4 +- .../calcite/plan/volcano/VolcanoPlanner.java | 129 ++- .../volcano/VolcanoRelMetadataProvider.java | 17 +- .../calcite/plan/volcano/VolcanoRuleCall.java | 23 +- .../plan/volcano/VolcanoRuleMatch.java | 1 + .../calcite/prepare/CalciteCatalogReader.java | 18 +- .../calcite/prepare/CalciteMaterializer.java | 13 +- .../calcite/prepare/CalcitePrepareImpl.java | 65 +- .../calcite/prepare/LixToRelTranslator.java | 51 +- .../apache/calcite/prepare/PlannerImpl.java | 69 +- .../org/apache/calcite/prepare/Prepare.java | 95 ++- .../calcite/prepare/QueryableRelBuilder.java | 24 +- .../calcite/prepare/RelOptTableImpl.java | 89 +- .../org/apache/calcite/profile/Profiler.java | 25 +- .../apache/calcite/profile/ProfilerImpl.java | 15 +- .../calcite/profile/SimpleProfiler.java | 7 +- .../apache/calcite/rel/AbstractRelNode.java | 62 +- .../org/apache/calcite/rel/PhysicalNode.java | 10 +- .../org/apache/calcite/rel/RelCollation.java | 5 +- .../apache/calcite/rel/RelCollationImpl.java | 7 +- .../calcite/rel/RelCollationTraitDef.java | 4 +- .../apache/calcite/rel/RelDistribution.java | 5 +- .../calcite/rel/RelDistributionTraitDef.java | 4 +- .../apache/calcite/rel/RelDistributions.java | 11 +- .../apache/calcite/rel/RelFieldCollation.java | 13 +- .../java/org/apache/calcite/rel/RelInput.java | 20 +- .../java/org/apache/calcite/rel/RelNode.java | 17 +- .../org/apache/calcite/rel/RelVisitor.java | 10 +- .../org/apache/calcite/rel/RelWriter.java | 8 +- .../apache/calcite/rel/convert/Converter.java | 4 +- .../calcite/rel/convert/ConverterImpl.java | 8 +- .../calcite/rel/convert/ConverterRule.java | 8 +- .../rel/convert/TraitMatchingRule.java | 4 +- .../apache/calcite/rel/core/Aggregate.java | 14 +- .../calcite/rel/core/AggregateCall.java | 30 +- .../org/apache/calcite/rel/core/Calc.java | 7 +- .../org/apache/calcite/rel/core/Collect.java | 4 +- .../apache/calcite/rel/core/Correlate.java | 24 +- .../calcite/rel/core/CorrelationId.java | 4 +- .../org/apache/calcite/rel/core/Filter.java | 17 +- .../apache/calcite/rel/core/Intersect.java | 7 +- .../org/apache/calcite/rel/core/Join.java | 9 +- .../org/apache/calcite/rel/core/Match.java | 10 +- .../org/apache/calcite/rel/core/Project.java | 30 +- .../apache/calcite/rel/core/RelFactories.java | 66 +- .../org/apache/calcite/rel/core/Sample.java | 4 +- .../org/apache/calcite/rel/core/Snapshot.java | 5 +- .../org/apache/calcite/rel/core/Sort.java | 12 +- .../calcite/rel/core/TableFunctionScan.java | 27 +- .../apache/calcite/rel/core/TableModify.java | 40 +- .../apache/calcite/rel/core/TableScan.java | 6 +- .../apache/calcite/rel/core/Uncollect.java | 6 +- .../org/apache/calcite/rel/core/Values.java | 4 + .../org/apache/calcite/rel/core/Window.java | 15 +- .../calcite/rel/externalize/RelDotWriter.java | 17 +- .../calcite/rel/externalize/RelEnumTypes.java | 4 +- .../calcite/rel/externalize/RelJson.java | 137 ++-- .../rel/externalize/RelJsonReader.java | 86 +- .../rel/externalize/RelJsonWriter.java | 33 +- .../rel/externalize/RelWriterImpl.java | 16 +- .../calcite/rel/externalize/RelXmlWriter.java | 15 +- .../apache/calcite/rel/hint/HintStrategy.java | 10 +- .../calcite/rel/hint/HintStrategyTable.java | 23 +- .../org/apache/calcite/rel/hint/RelHint.java | 5 +- .../calcite/rel/logical/LogicalAggregate.java | 10 +- .../calcite/rel/logical/LogicalCorrelate.java | 7 +- .../calcite/rel/logical/LogicalFilter.java | 4 +- .../calcite/rel/logical/LogicalJoin.java | 14 +- .../calcite/rel/logical/LogicalMatch.java | 10 +- .../calcite/rel/logical/LogicalProject.java | 11 +- .../calcite/rel/logical/LogicalSort.java | 8 +- .../rel/logical/LogicalTableFunctionScan.java | 18 +- .../rel/logical/LogicalTableModify.java | 10 +- .../calcite/rel/logical/LogicalValues.java | 2 +- .../calcite/rel/logical/LogicalWindow.java | 6 +- .../calcite/rel/metadata/BuiltInMetadata.java | 72 +- .../metadata/CachingRelMetadataProvider.java | 12 +- .../metadata/ChainedRelMetadataProvider.java | 8 +- .../metadata/JaninoRelMetadataProvider.java | 7 +- .../calcite/rel/metadata/MetadataFactory.java | 4 +- .../rel/metadata/MetadataFactoryImpl.java | 20 +- .../calcite/rel/metadata/NullSentinel.java | 6 +- .../ReflectiveRelMetadataProvider.java | 11 +- .../calcite/rel/metadata/RelColumnOrigin.java | 4 +- .../rel/metadata/RelMdAllPredicates.java | 33 +- .../calcite/rel/metadata/RelMdCollation.java | 81 +- .../rel/metadata/RelMdColumnOrigins.java | 29 +- .../rel/metadata/RelMdColumnUniqueness.java | 36 +- .../rel/metadata/RelMdDistinctRowCount.java | 44 +- .../rel/metadata/RelMdDistribution.java | 6 +- .../rel/metadata/RelMdExplainVisibility.java | 4 +- .../rel/metadata/RelMdExpressionLineage.java | 36 +- .../rel/metadata/RelMdLowerBoundCost.java | 6 +- .../rel/metadata/RelMdMaxRowCount.java | 26 +- .../calcite/rel/metadata/RelMdMemory.java | 8 +- .../rel/metadata/RelMdMinRowCount.java | 12 +- .../calcite/rel/metadata/RelMdNodeTypes.java | 44 +- .../metadata/RelMdPercentageOriginalRows.java | 19 +- .../rel/metadata/RelMdPopulationSize.java | 20 +- .../calcite/rel/metadata/RelMdPredicates.java | 23 +- .../calcite/rel/metadata/RelMdRowCount.java | 26 +- .../rel/metadata/RelMdSelectivity.java | 34 +- .../calcite/rel/metadata/RelMdSize.java | 91 ++- .../rel/metadata/RelMdTableReferences.java | 30 +- .../calcite/rel/metadata/RelMdUniqueKeys.java | 28 +- .../calcite/rel/metadata/RelMdUtil.java | 69 +- .../rel/metadata/RelMetadataProvider.java | 4 +- .../rel/metadata/RelMetadataQuery.java | 89 +- .../rel/metadata/RelMetadataQueryBase.java | 13 +- .../calcite/rel/metadata/UnboundMetadata.java | 4 +- .../calcite/rel/mutable/MutableAggregate.java | 8 +- .../calcite/rel/mutable/MutableBiRel.java | 1 + .../calcite/rel/mutable/MutableCalc.java | 4 +- .../calcite/rel/mutable/MutableCollect.java | 4 +- .../calcite/rel/mutable/MutableCorrelate.java | 4 +- .../calcite/rel/mutable/MutableExchange.java | 4 +- .../calcite/rel/mutable/MutableFilter.java | 4 +- .../calcite/rel/mutable/MutableJoin.java | 4 +- .../calcite/rel/mutable/MutableMatch.java | 4 +- .../calcite/rel/mutable/MutableMultiRel.java | 1 + .../calcite/rel/mutable/MutableProject.java | 6 +- .../calcite/rel/mutable/MutableRel.java | 10 +- .../rel/mutable/MutableRelVisitor.java | 12 +- .../calcite/rel/mutable/MutableRels.java | 9 +- .../calcite/rel/mutable/MutableSample.java | 4 +- .../calcite/rel/mutable/MutableScan.java | 25 +- .../calcite/rel/mutable/MutableSetOp.java | 4 +- .../calcite/rel/mutable/MutableSingleRel.java | 1 + .../calcite/rel/mutable/MutableSort.java | 12 +- .../rel/mutable/MutableTableFunctionScan.java | 12 +- .../rel/mutable/MutableTableModify.java | 16 +- .../calcite/rel/mutable/MutableUncollect.java | 4 +- .../calcite/rel/mutable/MutableValues.java | 4 +- .../calcite/rel/mutable/MutableWindow.java | 4 +- .../rel/rel2sql/RelToSqlConverter.java | 14 +- .../calcite/rel/rel2sql/SqlImplementor.java | 121 +-- .../rel/rules/AggregateCaseToFilterRule.java | 3 +- ...AggregateExpandDistinctAggregatesRule.java | 6 +- .../rel/rules/AggregateJoinTransposeRule.java | 4 +- .../rel/rules/AggregateProjectMergeRule.java | 10 +- .../AggregateProjectPullUpConstantsRule.java | 2 +- .../rules/AggregateReduceFunctionsRule.java | 9 +- .../rel/rules/AggregateStarTableRule.java | 6 +- .../rules/AggregateUnionTransposeRule.java | 4 +- .../calcite/rel/rules/CalcRelSplitter.java | 8 +- .../calcite/rel/rules/CoerceInputsRule.java | 4 +- .../calcite/rel/rules/DateRangeRules.java | 49 +- .../rules/ExchangeRemoveConstantKeysRule.java | 4 +- .../calcite/rel/rules/FilterJoinRule.java | 4 +- .../rel/rules/FilterMultiJoinMergeRule.java | 4 +- .../calcite/rel/rules/JoinCommuteRule.java | 8 +- .../rel/rules/JoinProjectTransposeRule.java | 4 +- .../rel/rules/JoinToMultiJoinRule.java | 42 +- .../calcite/rel/rules/LoptJoinTree.java | 10 +- .../calcite/rel/rules/LoptMultiJoin.java | 76 +- .../rel/rules/LoptOptimizeJoinRule.java | 22 +- .../rel/rules/LoptSemiJoinOptimizer.java | 14 +- .../apache/calcite/rel/rules/MultiJoin.java | 32 +- .../rel/rules/MultiJoinOptimizeBushyRule.java | 4 +- .../rel/rules/ProjectAggregateMergeRule.java | 3 +- .../rel/rules/ProjectJoinJoinRemoveRule.java | 2 +- .../calcite/rel/rules/PushProjector.java | 30 +- .../calcite/rel/rules/ReduceDecimalsRule.java | 18 +- .../rel/rules/ReduceExpressionsRule.java | 18 +- .../calcite/rel/rules/SemiJoinRule.java | 4 +- .../rel/rules/SortRemoveConstantKeysRule.java | 2 +- .../calcite/rel/rules/SortRemoveRule.java | 3 +- .../calcite/rel/rules/SpatialRules.java | 6 +- .../rel/rules/UnionPullUpConstantsRule.java | 2 +- .../calcite/rel/rules/ValuesReduceRule.java | 13 +- .../MaterializedViewAggregateRule.java | 44 +- .../materialize/MaterializedViewJoinRule.java | 29 +- .../materialize/MaterializedViewRule.java | 66 +- .../rel/type/DelegatingTypeSystem.java | 4 +- .../rel/type/DynamicRecordTypeImpl.java | 9 +- .../apache/calcite/rel/type/RelCrossType.java | 1 + .../apache/calcite/rel/type/RelDataType.java | 27 +- .../calcite/rel/type/RelDataTypeFactory.java | 8 +- .../rel/type/RelDataTypeFactoryImpl.java | 58 +- .../calcite/rel/type/RelDataTypeField.java | 2 + .../rel/type/RelDataTypeFieldImpl.java | 4 +- .../calcite/rel/type/RelDataTypeImpl.java | 47 +- .../calcite/rel/type/RelDataTypeSystem.java | 12 +- .../rel/type/RelDataTypeSystemImpl.java | 4 +- .../calcite/rel/type/RelRecordType.java | 9 +- .../org/apache/calcite/rex/LogicVisitor.java | 19 +- .../apache/calcite/rex/RexBiVisitorImpl.java | 4 +- .../org/apache/calcite/rex/RexBuilder.java | 61 +- .../java/org/apache/calcite/rex/RexCall.java | 11 +- .../apache/calcite/rex/RexCallBinding.java | 6 +- .../org/apache/calcite/rex/RexChecker.java | 15 +- .../apache/calcite/rex/RexCorrelVariable.java | 6 +- .../apache/calcite/rex/RexDynamicParam.java | 4 +- .../org/apache/calcite/rex/RexExecutable.java | 13 +- .../apache/calcite/rex/RexExecutorImpl.java | 4 +- .../apache/calcite/rex/RexFieldAccess.java | 4 +- .../org/apache/calcite/rex/RexInputRef.java | 4 +- .../apache/calcite/rex/RexInterpreter.java | 4 +- .../org/apache/calcite/rex/RexLiteral.java | 107 ++- .../org/apache/calcite/rex/RexLocalRef.java | 4 +- .../apache/calcite/rex/RexMultisetUtil.java | 4 +- .../java/org/apache/calcite/rex/RexNode.java | 11 +- .../org/apache/calcite/rex/RexNormalize.java | 2 +- .../java/org/apache/calcite/rex/RexOver.java | 9 +- .../calcite/rex/RexPermuteInputsShuttle.java | 4 +- .../org/apache/calcite/rex/RexProgram.java | 61 +- .../apache/calcite/rex/RexProgramBuilder.java | 35 +- .../org/apache/calcite/rex/RexRangeRef.java | 4 +- .../org/apache/calcite/rex/RexShuttle.java | 19 +- .../org/apache/calcite/rex/RexSimplify.java | 128 +-- .../apache/calcite/rex/RexSqlConvertlet.java | 4 +- .../calcite/rex/RexSqlConvertletTable.java | 4 +- .../rex/RexSqlReflectiveConvertletTable.java | 6 +- .../rex/RexSqlStandardConvertletTable.java | 14 +- .../org/apache/calcite/rex/RexSubQuery.java | 7 +- .../apache/calcite/rex/RexTableInputRef.java | 9 +- .../calcite/rex/RexToSqlNodeConverter.java | 10 +- .../rex/RexToSqlNodeConverterImpl.java | 13 +- .../apache/calcite/rex/RexUnaryBiVisitor.java | 4 +- .../org/apache/calcite/rex/RexUnknownAs.java | 3 +- .../java/org/apache/calcite/rex/RexUtil.java | 96 ++- .../apache/calcite/rex/RexVisitorImpl.java | 4 +- .../org/apache/calcite/rex/RexWindow.java | 5 +- .../apache/calcite/rex/RexWindowBound.java | 13 +- .../apache/calcite/rex/RexWindowBounds.java | 11 +- .../runtime/AbstractImmutableList.java | 29 +- .../apache/calcite/runtime/ArrayBindable.java | 4 +- .../runtime/ArrayEnumeratorCursor.java | 6 +- .../org/apache/calcite/runtime/Automaton.java | 4 +- .../calcite/runtime/AutomatonBuilder.java | 2 + .../runtime/CalciteContextException.java | 19 +- .../calcite/runtime/CalciteException.java | 1 + .../calcite/runtime/CalciteResource.java | 8 +- .../calcite/runtime/CompressionFunctions.java | 4 +- .../org/apache/calcite/runtime/ConsList.java | 29 +- .../runtime/DeterministicAutomaton.java | 5 +- .../apache/calcite/runtime/Enumerables.java | 14 +- .../org/apache/calcite/runtime/FlatLists.java | 112 +-- .../apache/calcite/runtime/GeoFunctions.java | 10 +- .../apache/calcite/runtime/Geometries.java | 4 +- .../java/org/apache/calcite/runtime/Hook.java | 11 +- .../apache/calcite/runtime/JsonFunctions.java | 96 ++- .../java/org/apache/calcite/runtime/Like.java | 6 +- .../org/apache/calcite/runtime/Matcher.java | 6 +- .../apache/calcite/runtime/PredicateImpl.java | 2 +- .../calcite/runtime/RandomFunction.java | 4 +- .../org/apache/calcite/runtime/Resources.java | 142 ++-- .../calcite/runtime/ResultSetEnumerable.java | 47 +- .../calcite/runtime/SpaceFillingCurve2D.java | 1 + .../apache/calcite/runtime/SqlFunctions.java | 288 +++---- .../runtime/TrustAllSslSocketFactory.java | 13 +- .../org/apache/calcite/runtime/Utilities.java | 21 +- .../apache/calcite/runtime/XmlFunctions.java | 44 +- .../calcite/schema/FilterableTable.java | 4 +- .../calcite/schema/ModifiableTable.java | 8 +- .../schema/ProjectableFilterableTable.java | 8 +- .../apache/calcite/schema/ScannableTable.java | 4 +- .../org/apache/calcite/schema/Schema.java | 10 +- .../org/apache/calcite/schema/SchemaPlus.java | 8 +- .../org/apache/calcite/schema/Schemas.java | 89 +- .../org/apache/calcite/schema/Statistic.java | 12 +- .../org/apache/calcite/schema/Statistics.java | 26 +- .../java/org/apache/calcite/schema/Table.java | 4 +- .../apache/calcite/schema/TableFactory.java | 5 +- .../apache/calcite/schema/TemporalTable.java | 5 +- .../org/apache/calcite/schema/Wrapper.java | 16 +- .../calcite/schema/impl/AbstractSchema.java | 22 +- .../calcite/schema/impl/AbstractTable.java | 6 +- .../schema/impl/AggregateFunctionImpl.java | 12 +- .../calcite/schema/impl/DelegatingSchema.java | 10 +- .../schema/impl/ListTransientTable.java | 19 +- .../schema/impl/LongSchemaVersion.java | 4 +- .../schema/impl/MaterializedViewTable.java | 11 +- .../schema/impl/ModifiableViewTable.java | 22 +- .../schema/impl/ReflectiveFunctionBase.java | 4 +- .../schema/impl/ScalarFunctionImpl.java | 4 +- .../apache/calcite/schema/impl/StarTable.java | 8 +- .../schema/impl/TableFunctionImpl.java | 16 +- .../calcite/schema/impl/TableMacroImpl.java | 13 +- .../apache/calcite/schema/impl/ViewTable.java | 13 +- .../calcite/schema/impl/ViewTableMacro.java | 15 +- .../server/CalciteServerStatement.java | 6 +- .../calcite/server/DdlExecutorImpl.java | 1 + .../calcite/sql/ExplicitOperatorBinding.java | 6 +- .../sql/SqlAbstractDateTimeLiteral.java | 4 +- .../apache/calcite/sql/SqlAggFunction.java | 31 +- .../java/org/apache/calcite/sql/SqlAlter.java | 11 +- .../org/apache/calcite/sql/SqlBasicCall.java | 23 +- .../calcite/sql/SqlBasicTypeNameSpec.java | 10 +- .../apache/calcite/sql/SqlBinaryOperator.java | 17 +- .../calcite/sql/SqlBinaryStringLiteral.java | 12 +- .../java/org/apache/calcite/sql/SqlCall.java | 49 +- .../apache/calcite/sql/SqlCallBinding.java | 28 +- .../calcite/sql/SqlCharStringLiteral.java | 17 +- .../org/apache/calcite/sql/SqlCollation.java | 19 +- .../apache/calcite/sql/SqlDataTypeSpec.java | 22 +- .../apache/calcite/sql/SqlDateLiteral.java | 6 +- .../org/apache/calcite/sql/SqlDelete.java | 27 +- .../apache/calcite/sql/SqlDescribeSchema.java | 11 +- .../apache/calcite/sql/SqlDescribeTable.java | 23 +- .../org/apache/calcite/sql/SqlDialect.java | 161 ++-- .../calcite/sql/SqlDialectFactoryImpl.java | 4 +- .../apache/calcite/sql/SqlDynamicParam.java | 6 +- .../org/apache/calcite/sql/SqlExplain.java | 25 +- .../org/apache/calcite/sql/SqlFunction.java | 53 +- .../calcite/sql/SqlFunctionalOperator.java | 8 +- .../calcite/sql/SqlGroupedWindowFunction.java | 18 +- .../org/apache/calcite/sql/SqlIdentifier.java | 30 +- .../apache/calcite/sql/SqlInfixOperator.java | 8 +- .../org/apache/calcite/sql/SqlInsert.java | 23 +- .../calcite/sql/SqlInternalOperator.java | 4 +- .../calcite/sql/SqlIntervalLiteral.java | 10 +- .../calcite/sql/SqlIntervalQualifier.java | 28 +- .../calcite/sql/SqlJdbcDataTypeName.java | 8 +- .../calcite/sql/SqlJdbcFunctionCall.java | 23 +- .../java/org/apache/calcite/sql/SqlJoin.java | 44 +- .../org/apache/calcite/sql/SqlLiteral.java | 103 +-- .../apache/calcite/sql/SqlMatchFunction.java | 4 +- .../apache/calcite/sql/SqlMatchRecognize.java | 52 +- .../java/org/apache/calcite/sql/SqlMerge.java | 49 +- .../java/org/apache/calcite/sql/SqlNode.java | 13 +- .../org/apache/calcite/sql/SqlNodeList.java | 48 +- .../apache/calcite/sql/SqlNumericLiteral.java | 36 +- .../org/apache/calcite/sql/SqlOperator.java | 68 +- .../calcite/sql/SqlOperatorBinding.java | 14 +- .../apache/calcite/sql/SqlOperatorTable.java | 4 +- .../org/apache/calcite/sql/SqlOrderBy.java | 15 +- .../java/org/apache/calcite/sql/SqlPivot.java | 10 +- .../calcite/sql/SqlPostfixOperator.java | 14 +- .../apache/calcite/sql/SqlPrefixOperator.java | 22 +- .../calcite/sql/SqlRowTypeNameSpec.java | 3 +- .../org/apache/calcite/sql/SqlSelect.java | 96 ++- .../apache/calcite/sql/SqlSelectOperator.java | 6 +- .../org/apache/calcite/sql/SqlSetOption.java | 25 +- .../org/apache/calcite/sql/SqlSnapshot.java | 9 +- .../calcite/sql/SqlSpecialOperator.java | 8 +- .../calcite/sql/SqlSplittableAggFunction.java | 21 +- .../org/apache/calcite/sql/SqlSyntax.java | 6 +- .../apache/calcite/sql/SqlTimeLiteral.java | 6 +- .../calcite/sql/SqlTimestampLiteral.java | 6 +- .../apache/calcite/sql/SqlUnnestOperator.java | 14 +- .../calcite/sql/SqlUnresolvedFunction.java | 10 +- .../org/apache/calcite/sql/SqlUpdate.java | 31 +- .../java/org/apache/calcite/sql/SqlUtil.java | 94 ++- .../org/apache/calcite/sql/SqlWindow.java | 86 +- .../calcite/sql/SqlWindowTableFunction.java | 6 +- .../java/org/apache/calcite/sql/SqlWith.java | 11 +- .../org/apache/calcite/sql/SqlWithItem.java | 18 +- .../org/apache/calcite/sql/SqlWriter.java | 8 +- .../apache/calcite/sql/SqlWriterConfig.java | 50 +- .../apache/calcite/sql/advise/SqlAdvisor.java | 41 +- .../advise/SqlAdvisorGetHintsFunction.java | 2 +- .../advise/SqlAdvisorGetHintsFunction2.java | 2 +- .../calcite/sql/advise/SqlAdvisorHint.java | 8 +- .../calcite/sql/advise/SqlAdvisorHint2.java | 7 +- .../calcite/sql/advise/SqlSimpleParser.java | 25 +- .../sql/ddl/SqlAttributeDefinition.java | 24 +- .../calcite/sql/ddl/SqlCheckConstraint.java | 7 +- .../calcite/sql/ddl/SqlColumnDeclaration.java | 15 +- .../sql/ddl/SqlCreateForeignSchema.java | 25 +- .../sql/ddl/SqlCreateMaterializedView.java | 7 +- .../calcite/sql/ddl/SqlCreateTable.java | 9 +- .../apache/calcite/sql/ddl/SqlCreateType.java | 9 +- .../apache/calcite/sql/ddl/SqlCreateView.java | 7 +- .../calcite/sql/ddl/SqlKeyConstraint.java | 7 +- .../sql/dialect/BigQuerySqlDialect.java | 18 +- .../sql/dialect/ClickHouseSqlDialect.java | 10 +- .../calcite/sql/dialect/Db2SqlDialect.java | 2 +- .../calcite/sql/dialect/HiveSqlDialect.java | 10 +- .../calcite/sql/dialect/HsqldbSqlDialect.java | 6 +- .../sql/dialect/JethroDataSqlDialect.java | 14 +- .../calcite/sql/dialect/MssqlSqlDialect.java | 16 +- .../calcite/sql/dialect/MysqlSqlDialect.java | 12 +- .../calcite/sql/dialect/OracleSqlDialect.java | 4 +- .../sql/dialect/PostgresqlSqlDialect.java | 4 +- .../calcite/sql/dialect/PrestoSqlDialect.java | 10 +- .../sql/dialect/RedshiftSqlDialect.java | 6 +- .../calcite/sql/dialect/SparkSqlDialect.java | 6 +- .../calcite/sql/dialect/SybaseSqlDialect.java | 10 +- .../sql/fun/SqlAbstractGroupFunction.java | 4 +- .../sql/fun/SqlArrayValueConstructor.java | 6 +- .../calcite/sql/fun/SqlBasicAggFunction.java | 5 +- .../calcite/sql/fun/SqlBetweenOperator.java | 5 +- .../calcite/sql/fun/SqlBitOpAggFunction.java | 4 +- .../org/apache/calcite/sql/fun/SqlCase.java | 23 +- .../calcite/sql/fun/SqlCaseOperator.java | 24 +- .../calcite/sql/fun/SqlCastFunction.java | 4 +- .../calcite/sql/fun/SqlConvertFunction.java | 4 +- .../calcite/sql/fun/SqlCountAggFunction.java | 4 +- .../calcite/sql/fun/SqlExtractFunction.java | 8 +- .../calcite/sql/fun/SqlFloorFunction.java | 3 +- .../calcite/sql/fun/SqlGeoFunctions.java | 6 +- .../sql/fun/SqlJsonArrayAggAggFunction.java | 9 +- .../calcite/sql/fun/SqlJsonArrayFunction.java | 10 +- .../calcite/sql/fun/SqlJsonDepthFunction.java | 4 +- .../sql/fun/SqlJsonObjectFunction.java | 10 +- .../sql/fun/SqlJsonPrettyFunction.java | 4 +- .../calcite/sql/fun/SqlJsonQueryFunction.java | 8 +- .../calcite/sql/fun/SqlJsonTypeFunction.java | 4 +- .../calcite/sql/fun/SqlJsonValueFunction.java | 4 +- .../apache/calcite/sql/fun/SqlLibrary.java | 8 +- .../calcite/sql/fun/SqlLibraryOperators.java | 6 +- .../sql/fun/SqlLiteralChainOperator.java | 10 +- .../sql/fun/SqlMapValueConstructor.java | 17 +- .../calcite/sql/fun/SqlMinMaxAggFunction.java | 4 +- .../sql/fun/SqlMonotonicBinaryOperator.java | 4 - .../sql/fun/SqlMonotonicUnaryFunction.java | 4 +- .../sql/fun/SqlMultisetQueryConstructor.java | 11 +- .../sql/fun/SqlMultisetValueConstructor.java | 10 +- .../calcite/sql/fun/SqlOverlayFunction.java | 4 +- .../sql/fun/SqlPosixRegexOperator.java | 6 +- .../calcite/sql/fun/SqlStdOperatorTable.java | 22 +- .../calcite/sql/fun/SqlSumAggFunction.java | 4 +- .../sql/fun/SqlSumEmptyIsZeroAggFunction.java | 4 +- .../calcite/sql/fun/SqlTrimFunction.java | 6 +- .../sql/parser/SqlAbstractParserImpl.java | 21 +- .../calcite/sql/parser/SqlParserPos.java | 16 +- .../calcite/sql/parser/SqlParserUtil.java | 12 +- .../calcite/sql/pretty/SqlPrettyWriter.java | 94 ++- .../calcite/sql/type/AbstractSqlType.java | 4 +- .../type/AssignableOperandTypeChecker.java | 6 +- .../apache/calcite/sql/type/BasicSqlType.java | 16 +- .../sql/type/CompositeOperandTypeChecker.java | 3 +- .../sql/type/CursorReturnTypeInference.java | 4 +- .../type/JavaToSqlTypeConversionRules.java | 4 +- .../sql/type/MatchReturnTypeInference.java | 4 +- .../sql/type/NonNullableAccessors.java | 50 ++ .../calcite/sql/type/ObjectSqlType.java | 12 +- .../apache/calcite/sql/type/OperandTypes.java | 2 +- .../apache/calcite/sql/type/ReturnTypes.java | 19 +- .../sql/type/SameOperandTypeChecker.java | 4 +- ...meOperandTypeExceptLastOperandChecker.java | 8 +- .../calcite/sql/type/SqlOperandMetadata.java | 2 - .../sql/type/SqlReturnTypeInference.java | 4 +- .../sql/type/SqlReturnTypeInferenceChain.java | 4 +- .../calcite/sql/type/SqlTypeCoercionRule.java | 7 +- .../type/SqlTypeExplicitPrecedenceList.java | 4 +- .../calcite/sql/type/SqlTypeFactoryImpl.java | 19 +- .../calcite/sql/type/SqlTypeFamily.java | 6 +- .../calcite/sql/type/SqlTypeMappingRules.java | 4 +- .../apache/calcite/sql/type/SqlTypeName.java | 20 +- .../sql/type/SqlTypeTransformCascade.java | 4 +- .../calcite/sql/type/SqlTypeTransforms.java | 20 +- .../apache/calcite/sql/type/SqlTypeUtil.java | 39 +- .../TableFunctionReturnTypeInference.java | 7 +- .../sql/util/ChainedSqlOperatorTable.java | 4 +- .../org/apache/calcite/sql/util/IdPair.java | 4 +- .../sql/util/ListSqlOperatorTable.java | 4 +- .../sql/util/ReflectiveSqlOperatorTable.java | 8 +- .../calcite/sql/util/SqlBasicVisitor.java | 14 +- .../apache/calcite/sql/util/SqlShuttle.java | 34 +- .../apache/calcite/sql/util/SqlString.java | 13 +- .../sql/validate/AbstractNamespace.java | 34 +- .../calcite/sql/validate/AggChecker.java | 15 +- .../calcite/sql/validate/AggFinder.java | 13 +- .../calcite/sql/validate/AggVisitor.java | 5 +- .../sql/validate/AggregatingSelectScope.java | 17 +- .../calcite/sql/validate/AliasNamespace.java | 10 +- .../sql/validate/CollectNamespace.java | 4 +- .../calcite/sql/validate/CollectScope.java | 6 +- .../sql/validate/DelegatingNamespace.java | 12 +- .../calcite/sql/validate/DelegatingScope.java | 32 +- .../DelegatingSqlValidatorCatalogReader.java | 8 +- .../calcite/sql/validate/EmptyScope.java | 12 +- .../calcite/sql/validate/FieldNamespace.java | 12 +- .../sql/validate/IdentifierNamespace.java | 32 +- .../calcite/sql/validate/JoinNamespace.java | 8 +- .../calcite/sql/validate/JoinScope.java | 16 +- .../calcite/sql/validate/ListScope.java | 26 +- .../sql/validate/MatchRecognizeNamespace.java | 8 +- .../sql/validate/MatchRecognizeScope.java | 2 +- .../calcite/sql/validate/OrderByScope.java | 13 +- .../sql/validate/ParameterNamespace.java | 4 +- .../calcite/sql/validate/ParameterScope.java | 4 +- .../calcite/sql/validate/PivotNamespace.java | 4 +- .../calcite/sql/validate/PivotScope.java | 6 +- .../sql/validate/ProcedureNamespace.java | 10 +- .../calcite/sql/validate/SchemaNamespace.java | 10 +- .../calcite/sql/validate/ScopeChild.java | 1 - .../calcite/sql/validate/SelectNamespace.java | 17 +- .../calcite/sql/validate/SelectScope.java | 24 +- .../calcite/sql/validate/SetopNamespace.java | 21 +- .../calcite/sql/validate/SqlMonikerImpl.java | 4 +- .../calcite/sql/validate/SqlNameMatcher.java | 6 +- .../calcite/sql/validate/SqlNameMatchers.java | 12 +- .../sql/validate/SqlNonNullableAccessors.java | 97 +++ .../calcite/sql/validate/SqlQualified.java | 12 +- .../sql/validate/SqlScopedShuttle.java | 12 +- .../validate/SqlUserDefinedAggFunction.java | 10 +- .../sql/validate/SqlUserDefinedFunction.java | 12 +- .../validate/SqlUserDefinedTableFunction.java | 6 +- .../validate/SqlUserDefinedTableMacro.java | 10 +- .../calcite/sql/validate/SqlValidator.java | 34 +- .../validate/SqlValidatorCatalogReader.java | 9 +- .../sql/validate/SqlValidatorException.java | 1 + .../sql/validate/SqlValidatorImpl.java | 758 +++++++++++------- .../sql/validate/SqlValidatorNamespace.java | 14 +- .../sql/validate/SqlValidatorScope.java | 26 +- .../sql/validate/SqlValidatorUtil.java | 120 +-- .../sql/validate/SqlValidatorWithHints.java | 6 +- .../validate/TableConstructorNamespace.java | 4 +- .../calcite/sql/validate/TableNamespace.java | 12 +- .../calcite/sql/validate/TableScope.java | 4 +- .../calcite/sql/validate/UnnestNamespace.java | 9 +- .../sql/validate/WithItemNamespace.java | 14 +- .../calcite/sql/validate/WithNamespace.java | 4 +- .../calcite/sql/validate/WithScope.java | 4 +- .../implicit/AbstractTypeCoercion.java | 70 +- .../sql/validate/implicit/TypeCoercion.java | 20 +- .../validate/implicit/TypeCoercionImpl.java | 58 +- .../DeduplicateCorrelateVariables.java | 7 +- .../sql2rel/InitializerExpressionFactory.java | 4 +- .../NullInitializerExpressionFactory.java | 5 +- .../sql2rel/ReflectiveConvertletTable.java | 23 +- .../calcite/sql2rel/RelDecorrelator.java | 82 +- .../calcite/sql2rel/RelFieldTrimmer.java | 13 +- .../sql2rel/RelStructuredTypeFlattener.java | 37 +- .../sql2rel/SqlRexConvertletTable.java | 4 +- .../calcite/sql2rel/SqlToRelConverter.java | 589 ++++++++------ .../sql2rel/StandardConvertletTable.java | 35 +- .../statistic/QuerySqlStatisticProvider.java | 17 +- .../apache/calcite/tools/FrameworkConfig.java | 12 +- .../org/apache/calcite/tools/Frameworks.java | 44 +- .../java/org/apache/calcite/tools/Hoist.java | 6 +- .../apache/calcite/tools/PigRelBuilder.java | 15 +- .../org/apache/calcite/tools/Programs.java | 5 +- .../org/apache/calcite/tools/RelBuilder.java | 197 ++--- .../calcite/tools/RelBuilderFactory.java | 4 +- .../org/apache/calcite/tools/RuleSets.java | 4 +- .../util/BarfingInvocationHandler.java | 6 +- .../java/org/apache/calcite/util/BitSets.java | 7 +- .../org/apache/calcite/util/BitString.java | 4 +- .../org/apache/calcite/util/BlackholeMap.java | 6 +- .../apache/calcite/util/BuiltInMethod.java | 15 +- .../org/apache/calcite/util/CastingList.java | 10 +- .../org/apache/calcite/util/ChunkList.java | 126 +-- .../org/apache/calcite/util/Compatible.java | 4 +- .../org/apache/calcite/util/CompositeMap.java | 18 +- .../apache/calcite/util/ConversionUtil.java | 4 +- .../org/apache/calcite/util/DateString.java | 8 +- .../util/DelegatingInvocationHandler.java | 8 +- .../apache/calcite/util/EquivalenceSet.java | 4 +- .../org/apache/calcite/util/Filterator.java | 12 +- .../org/apache/calcite/util/Glossary.java | 56 +- .../apache/calcite/util/ImmutableBeans.java | 37 +- .../apache/calcite/util/ImmutableBitSet.java | 82 +- .../apache/calcite/util/ImmutableIntList.java | 35 +- .../calcite/util/ImmutableNullableList.java | 6 +- .../calcite/util/ImmutableNullableMap.java | 15 +- .../calcite/util/ImmutableNullableSet.java | 23 +- .../calcite/util/IntegerIntervalSet.java | 4 +- .../org/apache/calcite/util/JsonBuilder.java | 21 +- .../java/org/apache/calcite/util/Litmus.java | 15 +- .../java/org/apache/calcite/util/NameMap.java | 6 +- .../org/apache/calcite/util/NameMultimap.java | 4 +- .../java/org/apache/calcite/util/NameSet.java | 10 +- .../org/apache/calcite/util/NlsString.java | 49 +- .../org/apache/calcite/util/NumberUtil.java | 40 +- .../java/org/apache/calcite/util/Pair.java | 9 +- .../calcite/util/PartiallyOrderedSet.java | 59 +- .../org/apache/calcite/util/Permutation.java | 10 +- .../util/PrecedenceClimbingParser.java | 65 +- .../org/apache/calcite/util/ReflectUtil.java | 45 +- .../util/ReflectiveVisitDispatcher.java | 10 +- .../calcite/util/RelToSqlConverterUtil.java | 9 +- .../calcite/util/SaffronProperties.java | 7 +- .../java/org/apache/calcite/util/Sarg.java | 8 +- .../calcite/util/SerializableCharset.java | 9 +- .../calcite/util/SimpleNamespaceContext.java | 5 +- .../java/org/apache/calcite/util/Source.java | 4 +- .../calcite/util/SourceStringReader.java | 6 +- .../java/org/apache/calcite/util/Sources.java | 26 +- .../org/apache/calcite/util/Template.java | 6 +- .../org/apache/calcite/util/TimeString.java | 8 +- .../calcite/util/TimeWithTimeZoneString.java | 4 +- .../apache/calcite/util/TimestampString.java | 4 +- .../util/TimestampWithTimeZoneString.java | 4 +- .../apache/calcite/util/TryThreadLocal.java | 4 +- .../java/org/apache/calcite/util/Util.java | 121 +-- .../org/apache/calcite/util/XmlOutput.java | 27 +- .../util/graph/AttributedDirectedGraph.java | 31 +- .../util/graph/DefaultDirectedGraph.java | 61 +- .../calcite/util/graph/DefaultEdge.java | 10 +- .../util/graph/DepthFirstIterator.java | 1 + .../calcite/util/graph/DirectedGraph.java | 8 +- .../org/apache/calcite/util/graph/Graphs.java | 7 +- .../util/graph/TopologicalOrderIterator.java | 28 +- .../calcite/util/javac/JaninoCompiler.java | 45 +- .../calcite/util/javac/JavaCompilerArgs.java | 5 +- .../util/mapping/AbstractSourceMapping.java | 1 + .../util/mapping/AbstractTargetMapping.java | 1 + .../apache/calcite/util/mapping/IntPair.java | 4 +- .../apache/calcite/util/mapping/Mappings.java | 28 +- .../calcite/util/trace/CalciteLogger.java | 35 +- .../util/trace/CalciteTimingTracer.java | 3 +- .../calcite/util/trace/CalciteTrace.java | 5 +- .../calcite/adapter/generate/RangeTable.java | 4 +- .../plan/volcano/CollationConversionTest.java | 3 +- .../plan/volcano/TraitConversionTest.java | 3 +- .../plan/volcano/TraitPropagationTest.java | 3 +- .../plan/volcano/VolcanoPlannerTraitTest.java | 3 +- .../rel2sql/RelToSqlConverterStructsTest.java | 7 +- .../apache/calcite/rex/RexExecutorTest.java | 7 +- .../calcite/rex/RexProgramBuilderBase.java | 7 +- .../calcite/schemas/HrClusteredSchema.java | 4 +- .../calcite/sql/parser/SqlParserTest.java | 5 +- .../apache/calcite/test/CalciteAssert.java | 7 +- .../calcite/test/CollectionTypeTest.java | 13 +- .../calcite/test/CountriesTableFunction.java | 6 +- .../apache/calcite/test/InterpreterTest.java | 7 +- .../org/apache/calcite/test/JdbcTest.java | 3 +- .../calcite/test/MaterializationTest.java | 3 +- .../calcite/test/MockRelOptPlanner.java | 6 +- .../org/apache/calcite/test/ModelTest.java | 15 +- .../apache/calcite/test/RelBuilderTest.java | 2 +- .../apache/calcite/test/RelMetadataTest.java | 4 +- .../calcite/test/ScannableTableTest.java | 13 +- .../calcite/test/SqlHintsConverterTest.java | 3 +- .../calcite/test/SqlJsonFunctionsTest.java | 8 +- .../calcite/test/SqlToRelConverterTest.java | 3 +- .../calcite/test/SqlValidatorTestCase.java | 3 +- .../calcite/test/StatesTableFunction.java | 6 +- .../org/apache/calcite/test/StreamTest.java | 23 +- .../test/catalog/MockCatalogReader.java | 6 +- .../test/fuzzer/RexProgramFuzzyTest.java | 3 +- .../apache/calcite/tools/FrameworksTest.java | 5 +- .../calcite/util/ImmutableBeanTest.java | 8 +- .../java/org/apache/calcite/util/Smalls.java | 8 +- .../adapter/druid/CeilOperatorConversion.java | 3 +- .../calcite/adapter/druid/DimensionSpec.java | 2 +- .../adapter/druid/DruidDateTimeUtils.java | 5 +- .../adapter/druid/DruidExpressions.java | 3 +- .../adapter/druid/DruidJsonFilter.java | 3 +- .../calcite/adapter/druid/DruidQuery.java | 9 +- .../druid/DruidSqlOperatorConverter.java | 2 +- .../calcite/adapter/druid/DruidTable.java | 3 +- .../adapter/druid/DruidTableFactory.java | 3 +- .../druid/ExtractionDimensionSpec.java | 3 +- .../druid/FloorOperatorConversion.java | 3 +- .../calcite/adapter/druid/Granularities.java | 7 +- .../calcite/adapter/druid/Granularity.java | 4 +- .../adapter/druid/NaryOperatorConverter.java | 3 +- .../druid/SubstringOperatorConversion.java | 2 +- .../adapter/druid/TimeExtractionFunction.java | 3 +- .../elasticsearch/ElasticsearchMapping.java | 3 +- .../adapter/csv/CsvFilterableTable.java | 18 +- .../adapter/csv/CsvScannableTable.java | 14 +- .../adapter/csv/CsvStreamScannableTable.java | 14 +- .../adapter/csv/CsvStreamTableFactory.java | 4 +- .../apache/calcite/adapter/csv/CsvTable.java | 10 +- .../calcite/adapter/csv/CsvTableFactory.java | 4 +- .../adapter/csv/CsvTranslatableTable.java | 6 +- .../calcite/example/maze/MazeTable.java | 8 +- .../calcite/adapter/file/CsvEnumerator.java | 36 +- .../calcite/adapter/file/CsvFieldType.java | 6 +- .../calcite/adapter/file/CsvTableFactory.java | 4 +- .../adapter/file/CsvTranslatableTable.java | 6 +- .../calcite/adapter/file/JsonEnumerator.java | 10 +- .../adapter/file/JsonScannableTable.java | 14 +- .../calcite/adapter/file/JsonTable.java | 6 +- .../simple/GeodeSimpleScannableTable.java | 12 +- .../calcite/adapter/innodb/InnodbFilter.java | 3 +- .../innodb/InnodbFilterTranslator.java | 3 +- .../adapter/innodb/InnodbTableScan.java | 3 +- .../adapter/kafka/KafkaMessageEnumerator.java | 10 +- .../adapter/kafka/KafkaStreamTable.java | 12 +- .../adapter/kafka/KafkaTableFactory.java | 4 +- .../apache/calcite/linq4j/BaseQueryable.java | 8 +- .../calcite/linq4j/DefaultEnumerable.java | 69 +- .../calcite/linq4j/DefaultQueryable.java | 11 +- .../org/apache/calcite/linq4j/Enumerable.java | 3 + .../calcite/linq4j/EnumerableDefaults.java | 312 +++---- .../linq4j/EnumerableOrderedQueryable.java | 4 +- .../calcite/linq4j/EnumerableQueryable.java | 25 +- .../org/apache/calcite/linq4j/Enumerator.java | 3 + .../calcite/linq4j/ExtendedEnumerable.java | 63 +- .../calcite/linq4j/ExtendedQueryable.java | 22 +- .../org/apache/calcite/linq4j/Grouping.java | 3 + .../apache/calcite/linq4j/GroupingImpl.java | 7 +- .../org/apache/calcite/linq4j/Linq4j.java | 17 +- .../org/apache/calcite/linq4j/LookupImpl.java | 23 +- .../calcite/linq4j/MemoryEnumerator.java | 4 +- .../apache/calcite/linq4j/MemoryFactory.java | 8 +- .../apache/calcite/linq4j/ModularInteger.java | 4 +- .../org/apache/calcite/linq4j/Nullness.java | 57 ++ .../org/apache/calcite/linq4j/Queryable.java | 3 + .../calcite/linq4j/QueryableDefaults.java | 10 +- .../calcite/linq4j/QueryableFactory.java | 17 +- .../calcite/linq4j/QueryableRecorder.java | 23 +- .../apache/calcite/linq4j/RawEnumerable.java | 3 + .../apache/calcite/linq4j/RawQueryable.java | 7 +- .../calcite/linq4j/TransformedEnumerator.java | 4 +- .../calcite/linq4j/function/Functions.java | 27 +- .../apache/calcite/linq4j/package-info.java | 7 + .../calcite/linq4j/tree/AbstractNode.java | 6 +- .../linq4j/tree/ArrayLengthRecordField.java | 10 +- .../calcite/linq4j/tree/BinaryExpression.java | 70 +- .../calcite/linq4j/tree/BlockBuilder.java | 20 +- .../calcite/linq4j/tree/BlockStatement.java | 12 +- .../apache/calcite/linq4j/tree/Blocks.java | 4 +- .../calcite/linq4j/tree/CatchBlock.java | 4 +- .../calcite/linq4j/tree/ClassDeclaration.java | 8 +- .../linq4j/tree/ClassDeclarationFinder.java | 8 +- .../linq4j/tree/ConditionalExpression.java | 4 +- .../linq4j/tree/ConditionalStatement.java | 4 +- .../linq4j/tree/ConstantExpression.java | 30 +- .../linq4j/tree/ConstantUntypedNull.java | 4 +- .../linq4j/tree/ConstructorDeclaration.java | 3 +- .../linq4j/tree/DeclarationStatement.java | 8 +- .../tree/DeterministicCodeOptimizer.java | 16 +- .../apache/calcite/linq4j/tree/Evaluator.java | 10 +- .../calcite/linq4j/tree/ExpressionType.java | 12 +- .../calcite/linq4j/tree/ExpressionWriter.java | 6 +- .../calcite/linq4j/tree/Expressions.java | 120 ++- .../calcite/linq4j/tree/FieldDeclaration.java | 8 +- .../calcite/linq4j/tree/ForEachStatement.java | 4 +- .../calcite/linq4j/tree/ForStatement.java | 10 +- .../linq4j/tree/FunctionExpression.java | 30 +- .../calcite/linq4j/tree/GotoStatement.java | 18 +- .../calcite/linq4j/tree/IndexExpression.java | 12 +- .../calcite/linq4j/tree/LabelStatement.java | 4 +- .../calcite/linq4j/tree/LabelTarget.java | 4 +- .../calcite/linq4j/tree/MemberExpression.java | 10 +- .../linq4j/tree/MethodCallExpression.java | 14 +- .../linq4j/tree/MethodDeclaration.java | 3 +- .../linq4j/tree/NewArrayExpression.java | 12 +- .../calcite/linq4j/tree/NewExpression.java | 8 +- .../calcite/linq4j/tree/OptimizeShuttle.java | 8 +- .../linq4j/tree/ParameterExpression.java | 6 +- .../apache/calcite/linq4j/tree/Primitive.java | 73 +- .../calcite/linq4j/tree/PseudoField.java | 4 +- .../linq4j/tree/ReflectedPseudoField.java | 6 +- .../apache/calcite/linq4j/tree/Shuttle.java | 32 +- .../linq4j/tree/TernaryExpression.java | 4 +- .../calcite/linq4j/tree/ThrowStatement.java | 4 +- .../calcite/linq4j/tree/TryStatement.java | 8 +- .../linq4j/tree/TypeBinaryExpression.java | 4 +- .../org/apache/calcite/linq4j/tree/Types.java | 43 +- .../calcite/linq4j/tree/UnaryExpression.java | 4 +- .../calcite/linq4j/tree/VisitorImpl.java | 10 +- .../calcite/linq4j/tree/WhileStatement.java | 4 +- .../calcite/adapter/pig/PigTableFactory.java | 4 +- .../apache/calcite/piglet/PigRelBuilder.java | 2 + .../org/apache/calcite/piglet/PigTable.java | 4 +- .../apache/calcite/piglet/PigUdfFinder.java | 3 +- .../calcite/adapter/os/DuTableFunction.java | 6 +- .../adapter/os/FilesTableFunction.java | 30 +- .../adapter/os/GitCommitsTableFunction.java | 16 +- .../calcite/adapter/os/JpsTableFunction.java | 6 +- .../calcite/adapter/os/PsTableFunction.java | 12 +- .../adapter/os/StdinTableFunction.java | 6 +- .../adapter/os/VmstatTableFunction.java | 12 +- .../calcite/adapter/tpcds/TpcdsSchema.java | 20 +- .../chinook/PreferredAlbumsTableFactory.java | 4 +- .../chinook/PreferredGenresTableFactory.java | 4 +- .../calcite/adapter/redis/RedisTable.java | 4 +- .../adapter/redis/RedisTableFactory.java | 4 +- 935 files changed, 11576 insertions(+), 7192 deletions(-) create mode 100644 core/src/main/java/org/apache/calcite/sql/type/NonNullableAccessors.java create mode 100644 core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java create mode 100644 linq4j/src/main/java/org/apache/calcite/linq4j/Nullness.java diff --git a/core/src/main/java/org/apache/calcite/DataContext.java b/core/src/main/java/org/apache/calcite/DataContext.java index 1ea46bef8332..f487ff23f00f 100644 --- a/core/src/main/java/org/apache/calcite/DataContext.java +++ b/core/src/main/java/org/apache/calcite/DataContext.java @@ -25,6 +25,8 @@ import com.google.common.base.CaseFormat; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Modifier; @@ -42,17 +44,17 @@ public interface DataContext { /** * Returns a sub-schema with a given name, or null. */ - SchemaPlus getRootSchema(); + @Nullable SchemaPlus getRootSchema(); /** * Returns the type factory. */ - JavaTypeFactory getTypeFactory(); + @Nullable JavaTypeFactory getTypeFactory(); /** * Returns the query provider. */ - QueryProvider getQueryProvider(); + @Nullable QueryProvider getQueryProvider(); /** * Returns a context variable. @@ -62,7 +64,7 @@ public interface DataContext { * * @param name Name of variable */ - Object get(String name); + @Nullable Object get(String name); /** Variable that may be asked for in a call to {@link DataContext#get}. */ enum Variable { diff --git a/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java b/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java index db045dbf632d..c9f97493cb8c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java @@ -41,6 +41,8 @@ import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Array; import java.lang.reflect.Type; import java.util.AbstractList; @@ -82,9 +84,9 @@ class ArrayTable extends AbstractQueryableTable implements ScannableTable { return Statistics.of(content.size, keys, content.collations); } - @Override public Enumerable scan(DataContext root) { - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { final Content content = supplier.get(); return content.arrayEnumerator(); } @@ -225,7 +227,7 @@ public static List asList(final Representation representation, // Cache size. It might be expensive to compute. final int size = representation.size(dataSet); return new AbstractList() { - @Override public Object get(int index) { + @Override public @Nullable Object get(int index) { return representation.getObject(dataSet, index); } @@ -243,9 +245,9 @@ public interface Representation { /** Converts a value set into a compact representation. If * {@code sources} is not null, permutes. */ - Object freeze(ColumnLoader.ValueSet valueSet, int[] sources); + Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources); - Object getObject(Object dataSet, int ordinal); + @Nullable Object getObject(Object dataSet, int ordinal); int getInt(Object dataSet, int ordinal); /** Creates a data set that is the same as a given data set @@ -277,7 +279,7 @@ public static class ObjectArray implements Representation { return RepresentationType.OBJECT_ARRAY; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { // We assume the values have been canonized. final List list = permuteList(valueSet.values, sources); return list.toArray(new Comparable[0]); @@ -334,7 +336,7 @@ public static class PrimitiveArray implements Representation { return RepresentationType.PRIMITIVE_ARRAY; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { //noinspection unchecked return primitive.toArray2( permuteList((List) valueSet.values, sources)); @@ -344,7 +346,7 @@ public static class PrimitiveArray implements Representation { return primitive.permute(dataSet, sources); } - @Override public Object getObject(Object dataSet, int ordinal) { + @Override public @Nullable Object getObject(Object dataSet, int ordinal) { return p.arrayItem(dataSet, ordinal); } @@ -375,7 +377,7 @@ public static class PrimitiveDictionary implements Representation { return RepresentationType.PRIMITIVE_DICTIONARY; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { throw new UnsupportedOperationException(); // TODO: } @@ -423,7 +425,7 @@ public static class ObjectDictionary implements Representation { return RepresentationType.OBJECT_DICTIONARY; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { final int n = valueSet.map.keySet().size(); int extra = valueSet.containsNull ? 1 : 0; Comparable[] codeValues = @@ -486,7 +488,7 @@ public static class StringDictionary implements Representation { return RepresentationType.STRING_DICTIONARY; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { throw new UnsupportedOperationException(); // TODO: } @@ -524,7 +526,7 @@ public static class ByteStringDictionary implements Representation { return RepresentationType.BYTE_STRING_DICTIONARY; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { throw new UnsupportedOperationException(); // TODO: } @@ -565,7 +567,7 @@ public static class Constant implements Representation { return RepresentationType.CONSTANT; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { final int size = valueSet.values.size(); return Pair.of(size == 0 ? null : valueSet.values.get(0), size); } @@ -626,7 +628,7 @@ public static class BitSlicedPrimitiveArray implements Representation { return RepresentationType.BIT_SLICED_PRIMITIVE_ARRAY; } - @Override public Object freeze(ColumnLoader.ValueSet valueSet, int[] sources) { + @Override public Object freeze(ColumnLoader.ValueSet valueSet, int @Nullable [] sources) { final int chunksPerWord = 64 / bitCount; final List valueList = permuteList(valueSet.values, sources); @@ -783,7 +785,7 @@ public static void orLong( } private static List permuteList( - final List list, final int[] sources) { + final List list, final int @Nullable [] sources) { if (sources == null) { return list; } @@ -828,13 +830,13 @@ public Enumerator enumerator() { } } - public Enumerator arrayEnumerator() { + public Enumerator<@Nullable Object[]> arrayEnumerator() { return new ArrayEnumerator(size, columns); } /** Enumerator over a table with a single column; each element * returned is an object. */ - private static class ObjectEnumerator implements Enumerator { + private static class ObjectEnumerator implements Enumerator<@Nullable Object> { final int rowCount; final Object dataSet; final Representation representation; @@ -846,7 +848,7 @@ private static class ObjectEnumerator implements Enumerator { this.representation = column.representation; } - @Override public Object current() { + @Override public @Nullable Object current() { return representation.getObject(dataSet, i); } @@ -864,7 +866,7 @@ private static class ObjectEnumerator implements Enumerator { /** Enumerator over a table with more than one column; each element * returned is an array. */ - private static class ArrayEnumerator implements Enumerator { + private static class ArrayEnumerator implements Enumerator<@Nullable Object[]> { final int rowCount; final List columns; int i = -1; @@ -874,8 +876,8 @@ private static class ArrayEnumerator implements Enumerator { this.columns = columns; } - @Override public Object[] current() { - Object[] objects = new Object[columns.size()]; + @Override public @Nullable Object[] current() { + @Nullable Object[] objects = new Object[columns.size()]; for (int j = 0; j < objects.length; j++) { final Column pair = columns.get(j); objects[j] = pair.representation.getObject(pair.dataSet, i); diff --git a/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java b/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java index 012f8ee3451b..f3aef5e7c38f 100644 --- a/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java +++ b/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java @@ -27,6 +27,8 @@ import org.apache.calcite.rel.type.RelProtoDataType; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.sql.Date; import java.sql.Time; @@ -62,6 +64,7 @@ class ColumnLoader { * @param sourceTable Source data * @param protoRowType Logical row type * @param repList Physical row types, or null if not known */ + @SuppressWarnings("method.invocation.invalid") ColumnLoader(JavaTypeFactory typeFactory, Enumerable sourceTable, RelProtoDataType protoRowType, @@ -195,7 +198,7 @@ private void load(final RelDataType elementType, // We have discovered a the first unique key in the table. sort[0] = pair.i; final Comparable[] values = - valueSet.values.toArray(new Comparable[list.size()]); + valueSet.values.toArray(new Comparable[0]); final Kev[] kevs = new Kev[list.size()]; for (int i = 0; i < kevs.length; i++) { kevs[i] = new Kev(i, values[i]); @@ -230,15 +233,16 @@ private void load(final RelDataType elementType, * value needs to be converted to a {@link Long}. Similarly * {@link java.sql.Date} and {@link java.sql.Time} values to * {@link Integer}. */ - private static List wrap(ColumnMetaData.Rep rep, List list, + private static List wrap(ColumnMetaData.Rep rep, List list, RelDataType type) { switch (type.getSqlTypeName()) { case TIMESTAMP: switch (rep) { case OBJECT: case JAVA_SQL_TIMESTAMP: - return Util.transform(list, + final List<@Nullable Long> longs = Util.transform((List<@Nullable Timestamp>) list, (Timestamp t) -> t == null ? null : t.getTime()); + return longs; default: break; } @@ -247,7 +251,7 @@ private static List wrap(ColumnMetaData.Rep rep, List list, switch (rep) { case OBJECT: case JAVA_SQL_TIME: - return Util.transform(list, (Time t) -> t == null + return Util.transform((List<@Nullable Time>) list, (Time t) -> t == null ? null : (int) (t.getTime() % DateTimeUtils.MILLIS_PER_DAY)); default: @@ -258,9 +262,10 @@ private static List wrap(ColumnMetaData.Rep rep, List list, switch (rep) { case OBJECT: case JAVA_SQL_DATE: - return Util.transform(list, (Date d) -> d == null - ? null - : (int) (d.getTime() / DateTimeUtils.MILLIS_PER_DAY)); + return Util.<@Nullable Date, @Nullable Integer>transform( + (List<@Nullable Date>) list, (Date d) -> d == null + ? null + : (int) (d.getTime() / DateTimeUtils.MILLIS_PER_DAY)); default: break; } @@ -279,15 +284,15 @@ static class ValueSet { final Class clazz; final Map map = new HashMap<>(); final List values = new ArrayList<>(); - Comparable min; - Comparable max; + @Nullable Comparable min; + @Nullable Comparable max; boolean containsNull; ValueSet(Class clazz) { this.clazz = clazz; } - void add(Comparable e) { + void add(@Nullable Comparable e) { if (e != null) { final Comparable old = e; e = map.get(e); @@ -367,7 +372,7 @@ private long toLong(Object o) { } } - private boolean canBeLong(Object o) { + private boolean canBeLong(@Nullable Object o) { return o instanceof Boolean || o instanceof Character || o instanceof Number; diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java index 3fc2c899b1f4..bdb3a706fbec 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java @@ -19,6 +19,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -41,7 +43,7 @@ public interface AggAddContext extends AggResultContext { * Returns {@link org.apache.calcite.rex.RexNode} representation of the * filter, or null. */ - RexNode rexFilterArgument(); + @Nullable RexNode rexFilterArgument(); /** * Returns Linq4j form of arguments. diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java index 94ff78573410..12e496d15fc0 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggImpState.java @@ -19,6 +19,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.rel.core.AggregateCall; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + import java.util.List; /** @@ -28,21 +30,21 @@ public class AggImpState { public final int aggIdx; public final AggregateCall call; public final AggImplementor implementor; - public AggContext context; - public Expression result; - public List state; - public Expression accumulatorAdder; + public @MonotonicNonNull AggContext context; + public @MonotonicNonNull Expression result; + public @MonotonicNonNull List state; + public @MonotonicNonNull Expression accumulatorAdder; public AggImpState(int aggIdx, AggregateCall call, boolean windowContext) { this.aggIdx = aggIdx; this.call = call; - this.implementor = - RexImpTable.INSTANCE.get(call.getAggregation(), windowContext); + AggImplementor implementor = RexImpTable.INSTANCE.get(call.getAggregation(), windowContext); if (implementor == null) { throw new IllegalArgumentException( "Unable to get aggregate implementation for aggregate " + call.getAggregation() + (windowContext ? " in window context" : "")); } + this.implementor = implementor; } } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggResultContext.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggResultContext.java index 4191e7e889c9..ab130a0e65eb 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggResultContext.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggResultContext.java @@ -19,6 +19,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.rel.core.AggregateCall; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Information for a call to * {@link AggImplementor#implementResult(AggContext, AggResultContext)} @@ -32,7 +34,7 @@ public interface AggResultContext extends NestedBlockBuilder, AggResetContext { * accumulator were aggregated. Most aggregate functions depend on only the * accumulator, but quasi-aggregate functions such as GROUPING access at the * key. */ - Expression key(); + @Nullable Expression key(); /** Returns an expression that references the {@code i}th field of the key, * cast to the appropriate type. */ diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java index ebbd9f212741..1e8b8193cec7 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java @@ -56,10 +56,13 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.math.BigDecimal; +import java.text.Collator; import java.util.AbstractList; import java.util.ArrayDeque; import java.util.ArrayList; @@ -70,6 +73,8 @@ import java.util.Locale; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * Utilities for generating programs in the Enumerable (functional) * style. @@ -122,7 +127,7 @@ static List fieldTypes( static List fieldRowTypes( final RelDataType inputRowType, - final List extraInputs, + final @Nullable List extraInputs, final List argList) { final List inputFields = inputRowType.getFieldList(); return new AbstractList() { @@ -130,7 +135,8 @@ static List fieldRowTypes( final int arg = argList.get(index); return arg < inputFields.size() ? inputFields.get(arg).getType() - : extraInputs.get(arg - inputFields.size()).getType(); + : requireNonNull(extraInputs, "extraInputs") + .get(arg - inputFields.size()).getType(); } @Override public int size() { return argList.size(); @@ -186,12 +192,12 @@ static Expression joinSelector(JoinRelType joinType, PhysType physType, * stored as {@code Integer} type, {@code java.sql.Timestamp} is * stored as {@code Long} type. */ - static Expression toInternal(Expression operand, Type targetType) { + static Expression toInternal(Expression operand, @Nullable Type targetType) { return toInternal(operand, operand.getType(), targetType); } private static Expression toInternal(Expression operand, - Type fromType, Type targetType) { + Type fromType, @Nullable Type targetType) { if (fromType == java.sql.Date.class) { if (targetType == int.class) { return Expressions.call(BuiltInMethod.DATE_TO_INT.method, operand); @@ -255,7 +261,7 @@ private static Expression fromInternal(Expression operand, && Primitive.isBox(targetType)) { // E.g. operand is "int", target is "Long", generate "(long) operand". return Expressions.convert_(operand, - Primitive.ofBox(targetType).primitiveClass); + Primitive.unbox(targetType)); } return operand; } @@ -293,11 +299,11 @@ static Type fromInternal(Type type) { return type; } - private static Type toInternal(RelDataType type) { + private static @Nullable Type toInternal(RelDataType type) { return toInternal(type, false); } - static Type toInternal(RelDataType type, boolean forceNotNull) { + static @Nullable Type toInternal(RelDataType type, boolean forceNotNull) { switch (type.getSqlTypeName()) { case DATE: case TIME: @@ -309,7 +315,7 @@ static Type toInternal(RelDataType type, boolean forceNotNull) { } } - static List internalTypes(List operandList) { + static List<@Nullable Type> internalTypes(List operandList) { return Util.transform(operandList, node -> toInternal(node.getType())); } @@ -360,13 +366,13 @@ public static Expression convert(Expression operand, Type fromType, // Generate "SqlFunctions.toShort(x)". return Expressions.call( SqlFunctions.class, - "to" + SqlFunctions.initcap(toPrimitive.primitiveName), + "to" + SqlFunctions.initcap(toPrimitive.getPrimitiveName()), operand); default: // Generate "Short.parseShort(x)". return Expressions.call( - toPrimitive.boxClass, - "parse" + SqlFunctions.initcap(toPrimitive.primitiveName), + toPrimitive.getBoxClass(), + "parse" + SqlFunctions.initcap(toPrimitive.getPrimitiveName()), operand); } } @@ -376,12 +382,12 @@ public static Expression convert(Expression operand, Type fromType, // Generate "SqlFunctions.toCharBoxed(x)". return Expressions.call( SqlFunctions.class, - "to" + SqlFunctions.initcap(toBox.primitiveName) + "Boxed", + "to" + SqlFunctions.initcap(toBox.getPrimitiveName()) + "Boxed", operand); default: // Generate "Short.valueOf(x)". return Expressions.call( - toBox.boxClass, + toBox.getBoxClass(), "valueOf", operand); } @@ -391,7 +397,7 @@ public static Expression convert(Expression operand, Type fromType, if (fromPrimitive != null) { // E.g. from "float" to "double" return Expressions.convert_( - operand, toPrimitive.primitiveClass); + operand, toPrimitive.getPrimitiveClass()); } if (fromNumber || fromBox == Primitive.CHAR) { // Generate "x.shortValue()". @@ -401,7 +407,7 @@ public static Expression convert(Expression operand, Type fromType, // Generate "SqlFunctions.toShort(x)" return Expressions.call( SqlFunctions.class, - "to" + SqlFunctions.initcap(toPrimitive.primitiveName), + "to" + SqlFunctions.initcap(toPrimitive.getPrimitiveName()), operand); } } else if (fromNumber && toBox != null) { @@ -434,7 +440,7 @@ public static Expression convert(Expression operand, Type fromType, // Convert it first and generate "Byte.valueOf((byte)x)" // Because there is no method "Byte.valueOf(int)" in Byte return Expressions.box( - Expressions.convert_(operand, toBox.primitiveClass), + Expressions.convert_(operand, toBox.getPrimitiveClass()), toBox); } // Convert datetime types to internal storage type: @@ -499,7 +505,7 @@ public static Expression convert(Expression operand, Type fromType, // E.g. from "int" to "String" // Generate "Integer.toString(x)" return Expressions.call( - fromPrimitive.boxClass, + fromPrimitive.getBoxClass(), "toString", operand); } @@ -544,7 +550,7 @@ public static Expression convert(Expression operand, Type fromType, } /** Converts a value to a given class. */ - public static T evaluate(Object o, Class clazz) { + public static @Nullable T evaluate(Object o, Class clazz) { // We need optimization here for constant folding. // Not all the expressions can be interpreted (e.g. ternary), so // we rely on optimization capabilities to fold non-interpretable @@ -555,7 +561,7 @@ public static T evaluate(Object o, Class clazz) { final Expression expr = convert(Expressions.constant(o), clazz); bb.add(Expressions.return_(null, expr)); - final FunctionExpression convert = + final FunctionExpression convert = Expressions.lambda(bb.toBlock(), ImmutableList.of()); return clazz.cast(convert.compile().dynamicInvoke()); } @@ -640,9 +646,9 @@ public static MethodCallExpression call(Class clazz, String methodName, * @return MethodCallExpression that call the given name method * @throws RuntimeException if no suitable method found */ - public static MethodCallExpression call(Class clazz, String methodName, - List arguments, Expression targetExpression) { - Class[] argumentTypes = Types.toClassArray(arguments); + public static MethodCallExpression call(Class clazz, String methodName, + List arguments, @Nullable Expression targetExpression) { + Class[] argumentTypes = Types.toClassArray(arguments); try { Method candidate = clazz.getMethod(methodName, argumentTypes); return Expressions.call(targetExpression, candidate, arguments); @@ -650,7 +656,7 @@ public static MethodCallExpression call(Class clazz, String methodName, for (Method method : clazz.getMethods()) { if (method.getName().equals(methodName)) { final boolean varArgs = method.isVarArgs(); - final Class[] parameterTypes = method.getParameterTypes(); + final Class[] parameterTypes = method.getParameterTypes(); if (Types.allAssignable(varArgs, parameterTypes, argumentTypes)) { return Expressions.call(targetExpression, method, arguments); } @@ -667,15 +673,15 @@ public static MethodCallExpression call(Class clazz, String methodName, } } - private static List matchMethodParameterTypes(boolean varArgs, - Class[] parameterTypes, List arguments) { + private static @Nullable List matchMethodParameterTypes(boolean varArgs, + Class[] parameterTypes, List arguments) { if ((varArgs && arguments.size() < parameterTypes.length - 1) || (!varArgs && arguments.size() != parameterTypes.length)) { return null; } final List typeMatchedArguments = new ArrayList<>(); for (int i = 0; i < arguments.size(); i++) { - Class parameterType = + Class parameterType = !varArgs || i < parameterTypes.length - 1 ? parameterTypes[i] : Object.class; @@ -697,8 +703,8 @@ private static List matchMethodParameterTypes(boolean varA * @return Converted argument expression that matches the parameter type. * Returns null if it is impossible to match. */ - private static Expression matchMethodParameterType( - Expression argument, Class parameter) { + private static @Nullable Expression matchMethodParameterType( + Expression argument, Class parameter) { Type argumentType = argument.getType(); if (Types.isAssignableFrom(parameter, argumentType)) { return argument; @@ -843,10 +849,11 @@ static Expression tumblingWindowSelector( * enumerator based on a specified key. Elements are windowed into sessions separated by * periods with no input for at least the duration specified by gap parameter. */ - public static Enumerable sessionize(Enumerator inputEnumerator, + public static Enumerable<@Nullable Object[]> sessionize( + Enumerator<@Nullable Object[]> inputEnumerator, int indexOfWatermarkedColumn, int indexOfKeyColumn, long gap) { - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { return new SessionizationEnumerator(inputEnumerator, indexOfWatermarkedColumn, indexOfKeyColumn, gap); } @@ -854,12 +861,12 @@ public static Enumerable sessionize(Enumerator inputEnumerat } /** Enumerator that converts rows into sessions separated by gaps. */ - private static class SessionizationEnumerator implements Enumerator { - private final Enumerator inputEnumerator; + private static class SessionizationEnumerator implements Enumerator<@Nullable Object[]> { + private final Enumerator<@Nullable Object[]> inputEnumerator; private final int indexOfWatermarkedColumn; private final int indexOfKeyColumn; private final long gap; - private final Deque list; + private final Deque<@Nullable Object[]> list; private boolean initialized; /** @@ -871,7 +878,7 @@ private static class SessionizationEnumerator implements Enumerator { * @param indexOfKeyColumn the index of column that acts as grouping key * @param gap gap parameter */ - SessionizationEnumerator(Enumerator inputEnumerator, + SessionizationEnumerator(Enumerator<@Nullable Object[]> inputEnumerator, int indexOfWatermarkedColumn, int indexOfKeyColumn, long gap) { this.inputEnumerator = inputEnumerator; this.indexOfWatermarkedColumn = indexOfWatermarkedColumn; @@ -881,12 +888,12 @@ private static class SessionizationEnumerator implements Enumerator { initialized = false; } - @Override public Object[] current() { + @Override public @Nullable Object[] current() { if (!initialized) { initialize(); initialized = true; } - return list.pollFirst(); + return list.removeFirst(); } @Override public boolean moveNext() { @@ -906,7 +913,7 @@ private static class SessionizationEnumerator implements Enumerator { } private void initialize() { - List elements = new ArrayList<>(); + List<@Nullable Object[]> elements = new ArrayList<>(); // initialize() will be called when inputEnumerator.moveNext() is true, // thus firstly should take the current element. elements.add(inputEnumerator.current()); @@ -915,21 +922,25 @@ private void initialize() { elements.add(inputEnumerator.current()); } - Map, Object[]>> sessionKeyMap = new HashMap<>(); - for (Object[] element : elements) { - sessionKeyMap.putIfAbsent(element[indexOfKeyColumn], new SortedMultiMap<>()); - Pair initWindow = computeInitWindow( - SqlFunctions.toLong(element[indexOfWatermarkedColumn]), gap); - sessionKeyMap.get(element[indexOfKeyColumn]).putMulti(initWindow, element); + Map<@Nullable Object, SortedMultiMap, @Nullable Object[]>> sessionKeyMap = + new HashMap<>(); + for (@Nullable Object[] element : elements) { + SortedMultiMap, @Nullable Object[]> session = + sessionKeyMap.computeIfAbsent(element[indexOfKeyColumn], k -> new SortedMultiMap<>()); + Object watermark = requireNonNull(element[indexOfWatermarkedColumn], + "element[indexOfWatermarkedColumn]"); + Pair initWindow = computeInitWindow( + SqlFunctions.toLong(watermark), gap); + session.putMulti(initWindow, element); } // merge per key session windows if there is any overlap between windows. - for (Map.Entry, Object[]>> perKeyEntry - : sessionKeyMap.entrySet()) { - Map, List> finalWindowElementsMap = new HashMap<>(); + for (Map.Entry<@Nullable Object, SortedMultiMap, @Nullable Object[]>> + perKeyEntry : sessionKeyMap.entrySet()) { + Map, List<@Nullable Object[]>> finalWindowElementsMap = new HashMap<>(); Pair currentWindow = null; - List tempElementList = new ArrayList<>(); - for (Map.Entry, List> sessionEntry + List<@Nullable Object[]> tempElementList = new ArrayList<>(); + for (Map.Entry, List<@Nullable Object[]>> sessionEntry : perKeyEntry.getValue().entrySet()) { // check the next window can be merged. if (currentWindow == null || !isOverlapped(currentWindow, sessionEntry.getKey())) { @@ -950,14 +961,15 @@ private void initialize() { } if (!tempElementList.isEmpty()) { + requireNonNull(currentWindow, "currentWindow is null"); finalWindowElementsMap.put(currentWindow, new ArrayList<>(tempElementList)); } // construct final results from finalWindowElementsMap. - for (Map.Entry, List> finalWindowElementsEntry + for (Map.Entry, List<@Nullable Object[]>> finalWindowElementsEntry : finalWindowElementsMap.entrySet()) { - for (Object[] element : finalWindowElementsEntry.getValue()) { - Object[] curWithWindow = new Object[element.length + 2]; + for (@Nullable Object[] element : finalWindowElementsEntry.getValue()) { + @Nullable Object[] curWithWindow = new Object[element.length + 2]; System.arraycopy(element, 0, curWithWindow, 0, element.length); curWithWindow[element.length] = finalWindowElementsEntry.getKey().left; curWithWindow[element.length + 1] = finalWindowElementsEntry.getKey().right; @@ -984,10 +996,11 @@ private Pair computeInitWindow(long ts, long gap) { * Create enumerable implementation that applies hopping on each element from the input * enumerator and produces at least one element for each input element. */ - public static Enumerable hopping(Enumerator inputEnumerator, + public static Enumerable<@Nullable Object[]> hopping( + Enumerator<@Nullable Object[]> inputEnumerator, int indexOfWatermarkedColumn, long emitFrequency, long windowSize, long offset) { - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { return new HopEnumerator(inputEnumerator, indexOfWatermarkedColumn, emitFrequency, windowSize, offset); } @@ -995,13 +1008,13 @@ public static Enumerable hopping(Enumerator inputEnumerator, } /** Enumerator that computes HOP. */ - private static class HopEnumerator implements Enumerator { - private final Enumerator inputEnumerator; + private static class HopEnumerator implements Enumerator<@Nullable Object[]> { + private final Enumerator<@Nullable Object[]> inputEnumerator; private final int indexOfWatermarkedColumn; private final long emitFrequency; private final long windowSize; private final long offset; - private final Deque list; + private final Deque<@Nullable Object[]> list; /** * Note that it only works for batch scenario. E.g. all data is known and there is no late data. @@ -1012,7 +1025,7 @@ private static class HopEnumerator implements Enumerator { * @param windowSize window size * @param offset indicates how much windows should off */ - HopEnumerator(Enumerator inputEnumerator, + HopEnumerator(Enumerator<@Nullable Object[]> inputEnumerator, int indexOfWatermarkedColumn, long slide, long windowSize, long offset) { this.inputEnumerator = inputEnumerator; this.indexOfWatermarkedColumn = indexOfWatermarkedColumn; @@ -1022,15 +1035,17 @@ private static class HopEnumerator implements Enumerator { list = new ArrayDeque<>(); } - @Override public Object[] current() { + @Override public @Nullable Object[] current() { if (list.size() > 0) { return takeOne(); } else { - Object[] current = inputEnumerator.current(); - List windows = hopWindows(SqlFunctions.toLong(current[indexOfWatermarkedColumn]), + @Nullable Object[] current = inputEnumerator.current(); + Object watermark = requireNonNull(current[indexOfWatermarkedColumn], + "element[indexOfWatermarkedColumn]"); + List> windows = hopWindows(SqlFunctions.toLong(watermark), emitFrequency, windowSize, offset); - for (Pair window : windows) { - Object[] curWithWindow = new Object[current.length + 2]; + for (Pair window : windows) { + @Nullable Object[] curWithWindow = new Object[current.length + 2]; System.arraycopy(current, 0, curWithWindow, 0, current.length); curWithWindow[current.length] = window.left; curWithWindow[current.length + 1] = window.right; @@ -1052,19 +1067,19 @@ private static class HopEnumerator implements Enumerator { @Override public void close() { } - private Object[] takeOne() { - return list.pollFirst(); + private @Nullable Object[] takeOne() { + return requireNonNull(list.pollFirst(), "list.pollFirst()"); } } - private static List hopWindows( + private static List> hopWindows( long tsMillis, long periodMillis, long sizeMillis, long offsetMillis) { - ArrayList ret = new ArrayList<>(Math.toIntExact(sizeMillis / periodMillis)); + ArrayList> ret = new ArrayList<>(Math.toIntExact(sizeMillis / periodMillis)); long lastStart = tsMillis - ((tsMillis + periodMillis - offsetMillis) % periodMillis); for (long start = lastStart; start > tsMillis - sizeMillis; start -= periodMillis) { - ret.add(new Pair(start, start + sizeMillis)); + ret.add(new Pair<>(start, start + sizeMillis)); } return ret; } @@ -1080,7 +1095,7 @@ public static Enumerable tumbling( // exactly one element for each input element. @Override public Enumerator enumerator() { return new Enumerator() { - Enumerator inputs = inputEnumerable.enumerator(); + final Enumerator inputs = inputEnumerable.enumerator(); @Override public TResult current() { return outSelector.apply(inputs.current()); @@ -1095,14 +1110,19 @@ public static Enumerable tumbling( } @Override public void close() { + inputs.close(); } }; } }; } - public static Expression generateCollatorExpression(SqlCollation collation) { - if (collation == null || collation.getCollator() == null) { + public static @Nullable Expression generateCollatorExpression(@Nullable SqlCollation collation) { + if (collation == null) { + return null; + } + Collator collator = collation.getCollator(); + if (collator == null) { return null; } @@ -1113,7 +1133,7 @@ public static Expression generateCollatorExpression(SqlCollation collation) { // collation.getLocale().getVariant()), // collation.getCollator().getStrength()); final Locale locale = collation.getLocale(); - final int strength = collation.getCollator().getStrength(); + final int strength = collator.getStrength(); return Expressions.call( Utilities.class, "generateCollator", diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java index 8a4d49921063..14a9c8e0df46 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java @@ -36,10 +36,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Aggregate} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableAggregate extends EnumerableAggregateBase implements EnumerableRel { @@ -48,7 +52,7 @@ public EnumerableAggregate( RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) throws InvalidRelException { super(cluster, traitSet, ImmutableList.of(), input, groupSet, groupSets, aggCalls); @@ -79,7 +83,7 @@ public EnumerableAggregate(RelOptCluster cluster, RelTraitSet traitSet, @Override public EnumerableAggregate copy(RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, List aggCalls) { + @Nullable List groupSets, List aggCalls) { try { return new EnumerableAggregate(getCluster(), traitSet, input, groupSet, groupSets, aggCalls); @@ -253,8 +257,9 @@ public EnumerableAggregate(RelOptCluster cluster, RelTraitSet traitSet, } for (final AggImpState agg : aggs) { results.add( - agg.implementor.implementResult(agg.context, - new AggResultContextImpl(resultBlock, agg.call, agg.state, key_, + agg.implementor.implementResult(requireNonNull(agg.context, "agg.context"), + new AggResultContextImpl(resultBlock, agg.call, + requireNonNull(agg.state, "agg.state"), key_, keyPhysType))); } resultBlock.add(physType.record(results)); @@ -273,7 +278,7 @@ public EnumerableAggregate(RelOptCluster cluster, RelTraitSet traitSet, builder.append("resultSelector", Expressions.lambda(Function2.class, resultBlock.toBlock(), - key_, + requireNonNull(key_, "key_"), acc_)); builder.add( Expressions.return_(null, @@ -335,7 +340,7 @@ public EnumerableAggregate(RelOptCluster cluster, RelTraitSet traitSet, builder.append("resultSelector", Expressions.lambda(Function2.class, resultBlock.toBlock(), - key_, + requireNonNull(key_, "key_"), acc_)); builder.add( Expressions.return_(null, diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateBase.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateBase.java index da6741128eaf..4ce42218a753 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateBase.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateBase.java @@ -46,12 +46,16 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** Base class for EnumerableAggregate and EnumerableSortedAggregate. */ public abstract class EnumerableAggregateBase extends Aggregate { protected EnumerableAggregateBase( @@ -60,7 +64,7 @@ protected EnumerableAggregateBase( List hints, RelNode input, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) { super(cluster, traitSet, hints, input, groupSet, groupSets, aggCalls); } @@ -139,7 +143,7 @@ protected void implementLambdaFactory(BlockBuilder builder, Expressions.call(pe, BuiltInMethod.COLLECTION_ADD.method, Expressions.new_(BuiltInMethod.BASIC_LAZY_ACCUMULATOR.constructor, - agg.accumulatorAdder)))); + requireNonNull(agg.accumulatorAdder, "agg.accumulatorAdder"))))); continue; } final Pair pair = @@ -150,7 +154,8 @@ protected void implementLambdaFactory(BlockBuilder builder, Expressions.call(pe, BuiltInMethod.COLLECTION_ADD.method, Expressions.new_(BuiltInMethod.SOURCE_SORTER.constructor, - agg.accumulatorAdder, pair.left, pair.right)))); + requireNonNull(agg.accumulatorAdder, "agg.accumulatorAdder"), + pair.left, pair.right)))); } builder.add( Expressions.declare(0, lambdaFactory, @@ -168,7 +173,7 @@ protected void implementLambdaFactory(BlockBuilder builder, builder.add( Expressions.statement( Expressions.call(pe, BuiltInMethod.COLLECTION_ADD.method, - agg.accumulatorAdder))); + requireNonNull(agg.accumulatorAdder, "agg.accumulatorAdder")))); } builder.add( Expressions.declare(0, lambdaFactory, @@ -242,7 +247,7 @@ protected void createAccumulatorAdders( final BlockBuilder builder2 = new BlockBuilder(); final AggImpState agg = aggs.get(i); - final int stateSize = agg.state.size(); + final int stateSize = requireNonNull(agg.state, "agg.state").size(); final List accumulator = new ArrayList<>(stateSize); for (int j = 0; j < stateSize; j++) { accumulator.add(accPhysType.fieldReference(accExpr, j + stateOffset)); @@ -263,7 +268,7 @@ protected void createAccumulatorAdders( return args; } - @Override public RexNode rexFilterArgument() { + @Override public @Nullable RexNode rexFilterArgument() { return agg.call.filterArg < 0 ? null : RexInputRef.of(agg.call.filterArg, @@ -280,7 +285,7 @@ protected void createAccumulatorAdders( } }; - agg.implementor.implementAdd(agg.context, addContext); + agg.implementor.implementAdd(requireNonNull(agg.context, "agg.context"), addContext); builder2.add(accExpr); agg.accumulatorAdder = builder.append("accumulatorAdder", Expressions.lambda(Function2.class, builder2.toBlock(), accExpr, diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateRule.java index 78612aaba4ff..6e85a116f4af 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateRule.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregateRule.java @@ -23,6 +23,8 @@ import org.apache.calcite.rel.convert.ConverterRule; import org.apache.calcite.rel.logical.LogicalAggregate; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalAggregate} * to an {@link EnumerableAggregate}. @@ -41,7 +43,7 @@ protected EnumerableAggregateRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final LogicalAggregate agg = (LogicalAggregate) rel; final RelTraitSet traitSet = rel.getCluster() .traitSet().replace(EnumerableConvention.INSTANCE); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBatchNestedLoopJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBatchNestedLoopJoin.java index e2e7e13ed13a..845936d3ff2a 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBatchNestedLoopJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBatchNestedLoopJoin.java @@ -42,6 +42,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; @@ -90,13 +92,13 @@ public static EnumerableBatchNestedLoopJoin create( joinType); } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( final RelTraitSet required) { return EnumerableTraitsUtils.passThroughTraitsForJoin( required, joinType, getLeft().getRowType().getFieldCount(), traitSet); } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { return EnumerableTraitsUtils.deriveTraitsForJoin( childTraits, childId, joinType, traitSet, right.getTraitSet()); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBindable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBindable.java index 9ee7ec81aad8..0a04893de37c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBindable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableBindable.java @@ -35,6 +35,8 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -59,7 +61,7 @@ protected EnumerableBindable(RelOptCluster cluster, RelNode input) { return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { final ImmutableMap map = ImmutableMap.of(); final Bindable bindable = EnumerableInterpretable.toBindable(map, null, (EnumerableRel) getInput(), EnumerableRel.Prefer.ARRAY); @@ -71,8 +73,8 @@ protected EnumerableBindable(RelOptCluster cluster, RelNode input) { return () -> { final Sink sink = implementor.relSinks.get(EnumerableBindable.this).get(0); - final Enumerable enumerable = bind(implementor.dataContext); - final Enumerator enumerator = enumerable.enumerator(); + final Enumerable<@Nullable Object[]> enumerable = bind(implementor.dataContext); + final Enumerator<@Nullable Object[]> enumerator = enumerable.enumerator(); while (enumerator.moveNext()) { sink.send(Row.asCopy(enumerator.current())); } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java index fbf0b0a054b5..d6bcad7bf31b 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java @@ -51,6 +51,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.Collections; @@ -266,7 +268,7 @@ public static EnumerableCalc create(final RelNode input, return implementor.result(physType, builder.toBlock()); } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( final RelTraitSet required) { final List exps = Util.transform(program.getProjectList(), program::expandLocalRef); @@ -275,7 +277,7 @@ public static EnumerableCalc create(final RelNode input, input.getRowType(), input.getCluster().getTypeFactory(), traitSet); } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { final List exps = Util.transform(program.getProjectList(), program::expandLocalRef); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java index 3cbffaf0c1ee..f2dcbc5e77bd 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java @@ -28,6 +28,10 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.RelFactories; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * Family of calling conventions that return results as an * {@link org.apache.calcite.linq4j.Enumerable}. @@ -51,7 +55,7 @@ public enum EnumerableConvention implements Convention { return "ENUMERABLE"; } - @Override public RelNode enforce( + @Override public @Nullable RelNode enforce( final RelNode input, final RelTraitSet required) { RelNode rel = input; @@ -59,6 +63,8 @@ public enum EnumerableConvention implements Convention { rel = ConventionTraitDef.INSTANCE.convert( input.getCluster().getPlanner(), input, INSTANCE, true); + requireNonNull(rel, + () -> "Unable to convert input to " + INSTANCE + ", input = " + input); } RelCollation collation = required.getCollation(); if (collation != null && collation != RelCollations.EMPTY) { diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java index 666d84417b02..6f883723e21c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCorrelate.java @@ -37,6 +37,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.List; @@ -84,7 +86,7 @@ public static EnumerableCorrelate create( traitSet, left, right, correlationId, requiredColumns, joinType); } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( final RelTraitSet required) { // EnumerableCorrelate traits passdown shall only pass through collation to left input. // This is because for EnumerableCorrelate always uses left input as the outer loop, @@ -93,7 +95,7 @@ public static EnumerableCorrelate create( required, joinType, left.getRowType().getFieldCount(), getTraitSet()); } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { // should only derive traits (limited to collation for now) from left input. return EnumerableTraitsUtils.deriveTraitsForJoin( diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java index 4a2075ca81cb..f4943ce8917c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** Implementation of {@link org.apache.calcite.rel.core.Filter} in @@ -76,7 +78,7 @@ public static EnumerableFilter create(final RelNode input, throw new UnsupportedOperationException(); } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( RelTraitSet required) { RelCollation collation = required.getCollation(); if (collation == null || collation == RelCollations.EMPTY) { @@ -86,7 +88,7 @@ public static EnumerableFilter create(final RelNode input, return Pair.of(traits, ImmutableList.of(traits)); } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { RelCollation collation = childTraits.getCollation(); if (collation == null || collation == RelCollations.EMPTY) { diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java index 918919ed2537..297ab00e11a3 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableHashJoin.java @@ -42,6 +42,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.util.List; import java.util.Set; @@ -103,13 +105,13 @@ public static EnumerableHashJoin create( condition, variablesSet, joinType); } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( final RelTraitSet required) { return EnumerableTraitsUtils.passThroughTraitsForJoin( required, joinType, left.getRowType().getFieldCount(), getTraitSet()); } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { // should only derive traits (limited to collation for now) from left join input. return EnumerableTraitsUtils.deriveTraitsForJoin( diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpretable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpretable.java index 44c992e5720f..616ef22f15eb 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpretable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpretable.java @@ -48,6 +48,7 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import org.checkerframework.checker.nullness.qual.Nullable; import org.codehaus.commons.compiler.CompileException; import org.codehaus.commons.compiler.CompilerFactoryFactory; import org.codehaus.commons.compiler.IClassBodyEvaluator; @@ -84,7 +85,7 @@ protected EnumerableInterpretable(RelOptCluster cluster, RelNode input) { implementor.spark, (EnumerableRel) getInput(), EnumerableRel.Prefer.ARRAY); final ArrayBindable arrayBindable = box(bindable); - final Enumerable enumerable = + final Enumerable<@Nullable Object[]> enumerable = arrayBindable.bind(implementor.dataContext); return new EnumerableNode(enumerable, implementor.compiler, this); } @@ -103,7 +104,7 @@ protected EnumerableInterpretable(RelOptCluster cluster, RelNode input) { .build(); public static Bindable toBindable(Map parameters, - CalcitePrepare.SparkHandler spark, EnumerableRel rel, + CalcitePrepare.@Nullable SparkHandler spark, EnumerableRel rel, EnumerableRel.Prefer prefer) { EnumerableRelImplementor relImplementor = new EnumerableRelImplementor(rel.getCluster().getRexBuilder(), @@ -185,13 +186,13 @@ static ArrayBindable box(final Bindable bindable) { return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { final Enumerable enumerable = bindable.bind(dataContext); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { final Enumerator enumerator = enumerable.enumerator(); - return new Enumerator() { - @Override public Object[] current() { + return new Enumerator<@Nullable Object[]>() { + @Override public @Nullable Object[] current() { return new Object[] {enumerator.current()}; } @@ -217,19 +218,19 @@ static ArrayBindable box(final Bindable bindable) { * *

From the interpreter's perspective, it is a leaf node. */ private static class EnumerableNode implements Node { - private final Enumerable enumerable; + private final Enumerable<@Nullable Object[]> enumerable; private final Sink sink; - EnumerableNode(Enumerable enumerable, Compiler compiler, + EnumerableNode(Enumerable<@Nullable Object[]> enumerable, Compiler compiler, EnumerableInterpretable rel) { this.enumerable = enumerable; this.sink = compiler.sink(rel); } @Override public void run() throws InterruptedException { - final Enumerator enumerator = enumerable.enumerator(); + final Enumerator<@Nullable Object[]> enumerator = enumerable.enumerator(); while (enumerator.moveNext()) { - Object[] values = enumerator.current(); + @Nullable Object[] values = enumerator.current(); sink.send(Row.of(values)); } } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableIntersect.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableIntersect.java index ab95359709ec..e20a8ad765ea 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableIntersect.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableIntersect.java @@ -28,6 +28,8 @@ import java.util.List; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Intersect} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableIntersect extends Intersect implements EnumerableRel { @@ -68,7 +70,7 @@ public EnumerableIntersect(RelOptCluster cluster, RelTraitSet traitSet, pref = pref.of(result.format); } - builder.add(intersectExp); + builder.add(requireNonNull(intersectExp, "intersectExp")); final PhysType physType = PhysTypeImpl.of( implementor.getTypeFactory(), diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java index fb0836eb2379..3b1694f7f2e1 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java @@ -35,12 +35,14 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** Relational expression that applies a limit and/or offset to its input. */ public class EnumerableLimit extends SingleRel implements EnumerableRel { - public final RexNode offset; - public final RexNode fetch; + public final @Nullable RexNode offset; + public final @Nullable RexNode fetch; /** Creates an EnumerableLimit. * @@ -49,8 +51,8 @@ public EnumerableLimit( RelOptCluster cluster, RelTraitSet traitSet, RelNode input, - RexNode offset, - RexNode fetch) { + @Nullable RexNode offset, + @Nullable RexNode fetch) { super(cluster, traitSet, input); this.offset = offset; this.fetch = fetch; @@ -59,8 +61,8 @@ public EnumerableLimit( } /** Creates an EnumerableLimit. */ - public static EnumerableLimit create(final RelNode input, RexNode offset, - RexNode fetch) { + public static EnumerableLimit create(final RelNode input, @Nullable RexNode offset, + @Nullable RexNode fetch) { final RelOptCluster cluster = input.getCluster(); final RelMetadataQuery mq = cluster.getMetadataQuery(); final RelTraitSet traitSet = diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java index 95313034c4f2..d1060bc9a12f 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java @@ -33,6 +33,8 @@ import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.apache.calcite.adapter.enumerable.EnumerableLimit.getExpression; /** @@ -52,8 +54,8 @@ public EnumerableLimitSort( RelTraitSet traitSet, RelNode input, RelCollation collation, - RexNode offset, - RexNode fetch) { + @Nullable RexNode offset, + @Nullable RexNode fetch) { super(cluster, traitSet, input, collation, offset, fetch); assert this.getConvention() instanceof EnumerableConvention; assert this.getConvention() == input.getConvention(); @@ -63,8 +65,8 @@ public EnumerableLimitSort( public static EnumerableLimitSort create( RelNode input, RelCollation collation, - RexNode offset, - RexNode fetch) { + @Nullable RexNode offset, + @Nullable RexNode fetch) { final RelOptCluster cluster = input.getCluster(); final RelTraitSet traitSet = cluster.traitSetOf(EnumerableConvention.INSTANCE).replace( collation); @@ -75,8 +77,8 @@ public static EnumerableLimitSort create( RelTraitSet traitSet, RelNode newInput, RelCollation newCollation, - RexNode offset, - RexNode fetch) { + @Nullable RexNode offset, + @Nullable RexNode fetch) { return new EnumerableLimitSort( this.getCluster(), traitSet, @@ -146,7 +148,7 @@ public static EnumerableLimitSort create( return cost; } - private double getValue(RexNode r, double defaultValue) { + private double getValue(@Nullable RexNode r, double defaultValue) { if (r == null || r instanceof RexDynamicParam) { return defaultValue; } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java index aeacf4817aa5..13c39a1c7cd8 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java @@ -48,6 +48,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; @@ -75,7 +77,7 @@ public EnumerableMatch(RelOptCluster cluster, RelTraitSet traitSet, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval) { + @Nullable RexNode interval) { super(cluster, traitSet, input, rowType, pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); @@ -87,7 +89,7 @@ public static EnumerableMatch create(RelNode input, RelDataType rowType, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval) { + @Nullable RexNode interval) { final RelOptCluster cluster = input.getCluster(); final RelTraitSet traitSet = cluster.traitSetOf(EnumerableConvention.INSTANCE); @@ -97,7 +99,7 @@ public static EnumerableMatch create(RelNode input, RelDataType rowType, } @Override public RelNode copy(RelTraitSet traitSet, List inputs) { - return new EnumerableMatch(getCluster(), traitSet, inputs.get(0), rowType, + return new EnumerableMatch(getCluster(), traitSet, inputs.get(0), getRowType(), pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); } @@ -452,7 +454,7 @@ public int getFuture() { * A special Getter that is able to return a field from a list of objects. */ static class PassedRowsInputGetter implements RexToLixTranslator.InputGetter { - private Expression index; + private @Nullable Expression index; private final ParameterExpression row; private final ParameterExpression passedRows; private final Function generator; @@ -468,12 +470,12 @@ static class PassedRowsInputGetter implements RexToLixTranslator.InputGetter { this.physType = physType; } - void setIndex(Expression index) { + void setIndex(@Nullable Expression index) { this.index = index; } @Override public Expression field(BlockBuilder list, int index, - Type storageType) { + @Nullable Type storageType) { if (this.index == null) { return generator.apply(this.row).field(list, index, storageType); } @@ -494,7 +496,7 @@ void setIndex(Expression index) { * A special Getter that "interchanges" the PREV and the field call. */ static class PrevInputGetter implements RexToLixTranslator.InputGetter { - private Expression offset; + private @Nullable Expression offset; private final ParameterExpression row; private final Function generator; private final PhysType physType; @@ -507,12 +509,12 @@ static class PrevInputGetter implements RexToLixTranslator.InputGetter { this.physType = physType; } - void setOffset(Expression offset) { + void setOffset(@Nullable Expression offset) { this.offset = offset; } @Override public Expression field(BlockBuilder list, int index, - Type storageType) { + @Nullable Type storageType) { final ParameterExpression row = Expressions.parameter(physType.getJavaRowType()); final ParameterExpression tmp = diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java index 10c253b1a387..00c84bda054b 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java @@ -50,6 +50,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; @@ -59,6 +61,8 @@ import static org.apache.calcite.rel.RelCollations.containsOrderless; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Join} in * {@link EnumerableConvention enumerable calling convention} using * a merge algorithm. */ @@ -73,10 +77,8 @@ protected EnumerableMergeJoin( JoinRelType joinType) { super(cluster, traits, ImmutableList.of(), left, right, condition, variablesSet, joinType); assert getConvention() instanceof EnumerableConvention; - final List leftCollations = - left.getTraitSet().getTraits(RelCollationTraitDef.INSTANCE); - final List rightCollations = - right.getTraitSet().getTraits(RelCollationTraitDef.INSTANCE); + final List leftCollations = getCollations(left.getTraitSet()); + final List rightCollations = getCollations(right.getTraitSet()); // If the join keys are not distinct, the sanity check doesn't apply. // e.g. t1.a=t2.b and t1.a=t2.c @@ -118,6 +120,16 @@ public static boolean isMergeJoinSupported(JoinRelType joinType) { return EnumerableDefaults.isMergeJoinSupported(EnumUtils.toLinq4jJoinType(joinType)); } + private static RelCollation getCollation(RelTraitSet traits) { + return requireNonNull(traits.getCollation(), + () -> "no collation trait in " + traits); + } + + private static List getCollations(RelTraitSet traits) { + return requireNonNull(traits.getTraits(RelCollationTraitDef.INSTANCE), + () -> "no collation trait in " + traits); + } + @Deprecated // to be removed before 2.0 EnumerableMergeJoin(RelOptCluster cluster, RelTraitSet traits, RelNode left, RelNode right, RexNode condition, ImmutableIntList leftKeys, @@ -174,10 +186,10 @@ public static boolean isMergeJoinSupported(JoinRelType joinType) { * join * (select * from bar order by bar.b desc, bar.d desc) */ - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( final RelTraitSet required) { // Required collation keys can be subset or superset of merge join keys. - RelCollation collation = required.getCollation(); + RelCollation collation = getCollation(required); int leftInputFieldCount = left.getRowType().getFieldCount(); List reqKeys = RelCollations.ordinals(collation); @@ -257,10 +269,10 @@ public static boolean isMergeJoinSupported(JoinRelType joinType) { return null; } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { final int keyCount = joinInfo.leftKeys.size(); - RelCollation collation = childTraits.getCollation(); + RelCollation collation = getCollation(childTraits); final int colCount = collation.getFieldCollations().size(); if (colCount < keyCount || keyCount == 0) { return null; @@ -327,7 +339,7 @@ private RelCollation extendCollation(RelCollation collation, List keys) ImmutableBitSet keysBitset = ImmutableBitSet.of(keys); ImmutableBitSet colKeysBitset = ImmutableBitSet.of(collation.getKeys()); ImmutableBitSet exceptBitset = keysBitset.except(colKeysBitset); - for (Integer i : exceptBitset.toList()) { + for (Integer i : exceptBitset) { fieldsForNewCollation.add(new RelFieldCollation(i)); } return RelCollations.of(fieldsForNewCollation); @@ -368,7 +380,7 @@ public static EnumerableMergeJoin create(RelNode left, RelNode right, final RelMetadataQuery mq = cluster.getMetadataQuery(); final List collations = RelMdCollation.mergeJoin(mq, left, right, leftKeys, rightKeys, joinType); - traitSet = traitSet.replace(collations); + traitSet = traitSet.replaceIfs(RelCollationTraitDef.INSTANCE, () -> collations); } return new EnumerableMergeJoin(cluster, traitSet, left, right, condition, ImmutableSet.of(), joinType); @@ -413,11 +425,11 @@ public static EnumerableMergeJoin create(RelNode left, RelNode right, final List leftExpressions = new ArrayList<>(); final List rightExpressions = new ArrayList<>(); for (Pair pair : Pair.zip(joinInfo.leftKeys, joinInfo.rightKeys)) { - final RelDataType keyType = - typeFactory.leastRestrictive( - ImmutableList.of( - left.getRowType().getFieldList().get(pair.left).getType(), - right.getRowType().getFieldList().get(pair.right).getType())); + RelDataType leftType = left.getRowType().getFieldList().get(pair.left).getType(); + RelDataType rightType = right.getRowType().getFieldList().get(pair.right).getType(); + final RelDataType keyType = requireNonNull( + typeFactory.leastRestrictive(ImmutableList.of(leftType, rightType)), + () -> "leastRestrictive returns null for " + leftType + " and " + rightType); final Type keyClass = typeFactory.getJavaClass(keyType); leftExpressions.add( EnumUtils.convert( diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java index 0a0ad07908a3..36deb59d3b77 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java @@ -31,6 +31,8 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -54,7 +56,7 @@ protected EnumerableMergeJoinRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { LogicalJoin join = (LogicalJoin) rel; final JoinInfo info = join.analyzeCondition(); if (!EnumerableMergeJoin.isMergeJoinSupported(join.getJoinType())) { diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMinus.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMinus.java index 656c9a437154..e9c45c9641b0 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMinus.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMinus.java @@ -28,6 +28,8 @@ import java.util.List; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Minus} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableMinus extends Minus implements EnumerableRel { @@ -51,6 +53,7 @@ public EnumerableMinus(RelOptCluster cluster, RelTraitSet traitSet, builder.append( "child" + ord.i, result.block); + assert childExp != null : "childExp must not be null"; if (minusExp == null) { minusExp = childExp; @@ -68,7 +71,8 @@ public EnumerableMinus(RelOptCluster cluster, RelTraitSet traitSet, pref = pref.of(result.format); } - builder.add(minusExp); + builder.add( + requireNonNull(minusExp, () -> "minusExp is null, inputs=" + inputs + ", rel=" + this)); final PhysType physType = PhysTypeImpl.of( implementor.getTypeFactory(), diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java index fce87e51e0e0..440d0ff67cf9 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableNestedLoopJoin.java @@ -39,6 +39,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Set; @@ -121,7 +123,7 @@ public static EnumerableNestedLoopJoin create( return cost; } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( final RelTraitSet required) { // EnumerableNestedLoopJoin traits passdown shall only pass through collation to // left input. It is because for EnumerableNestedLoopJoin always @@ -133,7 +135,7 @@ public static EnumerableNestedLoopJoin create( required, joinType, getLeft().getRowType().getFieldCount(), traitSet); } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { return EnumerableTraitsUtils.deriveTraitsForJoin( childTraits, childId, joinType, traitSet, right.getTraitSet() diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java index 4c718833dd35..b3c04c37aa55 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java @@ -30,6 +30,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; @@ -89,13 +91,13 @@ public static EnumerableProject create(final RelNode input, throw new UnsupportedOperationException(); } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( RelTraitSet required) { return EnumerableTraitsUtils.passThroughTraitsForProject(required, exps, input.getRowType(), input.getCluster().getTypeFactory(), traitSet); } - @Override public Pair> deriveTraits( + @Override public @Nullable Pair> deriveTraits( final RelTraitSet childTraits, final int childId) { return EnumerableTraitsUtils.deriveTraitsForProject(childTraits, childId, exps, input.getRowType(), input.getCluster().getTypeFactory(), traitSet); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java index f4a27c0d40e7..2136b1f727ca 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java @@ -22,6 +22,8 @@ import org.apache.calcite.rel.PhysicalNode; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -34,12 +36,12 @@ public interface EnumerableRel //~ Methods ---------------------------------------------------------------- - @Override default Pair> passThroughTraits( + @Override default @Nullable Pair> passThroughTraits( RelTraitSet required) { return null; } - @Override default Pair> deriveTraits( + @Override default @Nullable Pair> deriveTraits( RelTraitSet childTraits, int childId) { return null; } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelFactories.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelFactories.java index 8dcff99be323..ad109c44d0b4 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelFactories.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelFactories.java @@ -26,6 +26,8 @@ import org.apache.calcite.rex.RexUtil; import org.apache.calcite.sql.validate.SqlValidatorUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Set; @@ -65,7 +67,8 @@ private static class TableScanFactoryImpl private static class ProjectFactoryImpl implements org.apache.calcite.rel.core.RelFactories.ProjectFactory { @Override public RelNode createProject(RelNode input, List hints, - List childExprs, List fieldNames) { + List childExprs, + @Nullable List fieldNames) { final RelDataType rowType = RexUtil.createStructType(input.getCluster().getTypeFactory(), childExprs, fieldNames, SqlValidatorUtil.F_SUGGESTER); @@ -92,7 +95,7 @@ private static class FilterFactoryImpl private static class SortFactoryImpl implements org.apache.calcite.rel.core.RelFactories.SortFactory { @Override public RelNode createSort(RelNode input, RelCollation collation, - RexNode offset, RexNode fetch) { + @Nullable RexNode offset, @Nullable RexNode fetch) { return EnumerableSort.create(input, collation, offset, fetch); } } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java index 8e688d94ab7a..5dda33f771e9 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java @@ -70,6 +70,8 @@ import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Subclass of {@link org.apache.calcite.plan.RelImplementor} for relational * operators of {@link EnumerableConvention} calling convention. @@ -81,6 +83,7 @@ public class EnumerableRelImplementor extends JavaRelImplementor { private final Map stashedParameters = new IdentityHashMap<>(); + @SuppressWarnings("methodref.receiver.bound.invalid") protected final Function1 allCorrelateVariables = this::getCorrelVariableGetter; @@ -120,7 +123,8 @@ public ClassDeclaration implementRoot(EnumerableRel rootRel, Expression e = null; for (Statement statement : result.block.statements) { if (statement instanceof GotoStatement) { - e = bb.append("v", ((GotoStatement) statement).expression); + e = bb.append("v", + requireNonNull(((GotoStatement) statement).expression, "expression")); } else { bb.add(statement); } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java index 559e1c89a25c..be23b6602e89 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java @@ -28,7 +28,7 @@ import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Pair; -import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; /** Implementation of {@link org.apache.calcite.rel.core.Sort} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ @@ -39,17 +39,17 @@ public class EnumerableSort extends Sort implements EnumerableRel { *

Use {@link #create} unless you know what you're doing. */ public EnumerableSort(RelOptCluster cluster, RelTraitSet traitSet, - RelNode input, RelCollation collation, RexNode offset, RexNode fetch) { + RelNode input, RelCollation collation, @Nullable RexNode offset, @Nullable RexNode fetch) { super(cluster, traitSet, input, collation, offset, fetch); assert getConvention() instanceof EnumerableConvention; assert getConvention() == input.getConvention(); - Preconditions.checkArgument(fetch == null); - Preconditions.checkArgument(offset == null); + assert fetch == null : "fetch must be null"; + assert offset == null : "offset must be null"; } /** Creates an EnumerableSort. */ public static EnumerableSort create(RelNode child, RelCollation collation, - RexNode offset, RexNode fetch) { + @Nullable RexNode offset, @Nullable RexNode fetch) { final RelOptCluster cluster = child.getCluster(); final RelTraitSet traitSet = cluster.traitSetOf(EnumerableConvention.INSTANCE) @@ -62,8 +62,8 @@ public static EnumerableSort create(RelNode child, RelCollation collation, RelTraitSet traitSet, RelNode newInput, RelCollation newCollation, - RexNode offset, - RexNode fetch) { + @Nullable RexNode offset, + @Nullable RexNode fetch) { return new EnumerableSort(getCluster(), traitSet, newInput, newCollation, offset, fetch); } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java index a449c8bec7ad..14a9c8671a01 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.convert.ConverterRule; import org.apache.calcite.rel.core.Sort; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Rule to convert an {@link org.apache.calcite.rel.core.Sort} to an * {@link EnumerableSort}. @@ -39,7 +41,7 @@ protected EnumerableSortRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Sort sort = (Sort) rel; if (sort.offset != null || sort.fetch != null) { return null; diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java index 77041dfccbe6..6b6562668ae3 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregate.java @@ -42,10 +42,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** Sort based physical implementation of {@link Aggregate} in * {@link EnumerableConvention enumerable calling convention}. */ public class EnumerableSortedAggregate extends EnumerableAggregateBase implements EnumerableRel { @@ -54,7 +58,7 @@ public EnumerableSortedAggregate( RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) { super(cluster, traitSet, ImmutableList.of(), input, groupSet, groupSets, aggCalls); assert getConvention() instanceof EnumerableConvention; @@ -62,19 +66,20 @@ public EnumerableSortedAggregate( @Override public EnumerableSortedAggregate copy(RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, List aggCalls) { + @Nullable List groupSets, List aggCalls) { return new EnumerableSortedAggregate(getCluster(), traitSet, input, groupSet, groupSets, aggCalls); } - @Override public Pair> passThroughTraits( + @Override public @Nullable Pair> passThroughTraits( final RelTraitSet required) { if (!isSimple(this)) { return null; } RelTraitSet inputTraits = getInput().getTraitSet(); - RelCollation collation = required.getCollation(); + RelCollation collation = requireNonNull(required.getCollation(), + () -> "collation trait is null, required traits are " + required); ImmutableBitSet requiredKeys = ImmutableBitSet.of(RelCollations.ordinals(collation)); ImmutableBitSet groupKeys = ImmutableBitSet.range(groupSet.cardinality()); @@ -206,7 +211,9 @@ public EnumerableSortedAggregate( // Generate the appropriate key Comparator. In the case of NULL values // in group keys, the comparator must be able to support NULL values by giving a // consistent sort ordering. - final Expression comparator = keyPhysType.generateComparator(getTraitSet().getCollation()); + final Expression comparator = keyPhysType.generateComparator( + requireNonNull(getTraitSet().getCollation(), + () -> "getTraitSet().getCollation() is null, current traits are " + getTraitSet())); final Expression resultSelector_ = builder.append("resultSelector", diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregateRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregateRule.java index cfc3660af433..4808b500e0b6 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregateRule.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortedAggregateRule.java @@ -25,6 +25,8 @@ import org.apache.calcite.rel.logical.LogicalAggregate; import org.apache.calcite.util.ImmutableIntList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Rule to convert a {@link LogicalAggregate} @@ -44,7 +46,7 @@ protected EnumerableSortedAggregateRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final LogicalAggregate agg = (LogicalAggregate) rel; if (!Aggregate.isSimple(agg)) { return null; diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java index 33febbcc348a..254d2d3b7f19 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java @@ -36,6 +36,8 @@ import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.List; @@ -47,9 +49,9 @@ public class EnumerableTableFunctionScan extends TableFunctionScan implements EnumerableRel { public EnumerableTableFunctionScan(RelOptCluster cluster, - RelTraitSet traits, List inputs, Type elementType, + RelTraitSet traits, List inputs, @Nullable Type elementType, RelDataType rowType, RexNode call, - Set columnMappings) { + @Nullable Set columnMappings) { super(cluster, traits, inputs, call, elementType, rowType, columnMappings); } @@ -58,9 +60,9 @@ public EnumerableTableFunctionScan(RelOptCluster cluster, RelTraitSet traitSet, List inputs, RexNode rexCall, - Type elementType, + @Nullable Type elementType, RelDataType rowType, - Set columnMappings) { + @Nullable Set columnMappings) { return new EnumerableTableFunctionScan(getCluster(), traitSet, inputs, elementType, rowType, rexCall, columnMappings); } @@ -106,12 +108,13 @@ private Result defaultTableFunctionImplement( BlockBuilder bb = new BlockBuilder(); // Non-array user-specified types are not supported yet final JavaRowFormat format; - if (getElementType() == null) { + Type elementType = getElementType(); + if (elementType == null) { format = JavaRowFormat.ARRAY; - } else if (rowType.getFieldCount() == 1 && isQueryable()) { + } else if (getRowType().getFieldCount() == 1 && isQueryable()) { format = JavaRowFormat.SCALAR; - } else if (getElementType() instanceof Class - && Object[].class.isAssignableFrom((Class) getElementType())) { + } else if (elementType instanceof Class + && Object[].class.isAssignableFrom((Class) elementType)) { format = JavaRowFormat.ARRAY; } else { format = JavaRowFormat.CUSTOM; diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModify.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModify.java index b40c809f6799..d0084fc74725 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModify.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModify.java @@ -32,6 +32,8 @@ import org.apache.calcite.schema.ModifiableTable; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -44,8 +46,8 @@ public class EnumerableTableModify extends TableModify implements EnumerableRel { public EnumerableTableModify(RelOptCluster cluster, RelTraitSet traits, RelOptTable table, Prepare.CatalogReader catalogReader, RelNode child, - Operation operation, List updateColumnList, - List sourceExpressionList, boolean flattened) { + Operation operation, @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { super(cluster, traits, table, catalogReader, child, operation, updateColumnList, sourceExpressionList, flattened); assert child.getConvention() instanceof EnumerableConvention; diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModifyRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModifyRule.java index 0019d3ac36a8..768d0a0e8627 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModifyRule.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModifyRule.java @@ -23,6 +23,8 @@ import org.apache.calcite.rel.logical.LogicalTableModify; import org.apache.calcite.schema.ModifiableTable; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Planner rule that converts a * {@link org.apache.calcite.rel.logical.LogicalTableModify} to * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. @@ -40,7 +42,7 @@ protected EnumerableTableModifyRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final LogicalTableModify modify = (LogicalTableModify) rel; final ModifiableTable modifiableTable = diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java index bae77f9caaa7..2d0d2aec4c38 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java @@ -51,10 +51,16 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import static org.apache.calcite.linq4j.tree.Types.toClass; + +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.TableScan} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableTableScan @@ -80,7 +86,7 @@ assert canHandle(table) * * @return IndexScan if there is index available on collation keys */ - @Override public RelNode passThrough(final RelTraitSet required) { + @Override public @Nullable RelNode passThrough(final RelTraitSet required) { /* keys = required.getCollation().getKeys(); if (table has index on keys) { @@ -165,7 +171,7 @@ public static boolean canHandle(RelOptTable relOptTable) { return true; } - public static Class deduceElementType(Table table) { + public static Class deduceElementType(@Nullable Table table) { if (table instanceof QueryableTable) { final QueryableTable queryableTable = (QueryableTable) table; final Type type = queryableTable.getElementType(); @@ -185,7 +191,7 @@ public static Class deduceElementType(Table table) { } public static JavaRowFormat deduceFormat(RelOptTable table) { - final Class elementType = deduceElementType(table.unwrap(Table.class)); + final Class elementType = deduceElementType(table.unwrapOrThrow(Table.class)); return elementType == Object[].class ? JavaRowFormat.ARRAY : JavaRowFormat.CUSTOM; @@ -206,7 +212,7 @@ private Expression getExpression(PhysType physType) { private Expression toEnumerable(Expression expression) { final Type type = expression.getType(); if (Types.isArray(type)) { - if (Types.toClass(type).getComponentType().isPrimitive()) { + if (requireNonNull(toClass(type).getComponentType()).isPrimitive()) { expression = Expressions.call(BuiltInMethod.AS_LIST.method, expression); } @@ -235,7 +241,7 @@ && getRowType().getFieldCount() == 1 return Expressions.call(BuiltInMethod.SLICE0.method, expression); } JavaRowFormat oldFormat = format(); - if (physType.getFormat() == oldFormat && !hasCollectionField(rowType)) { + if (physType.getFormat() == oldFormat && !hasCollectionField(getRowType())) { return expression; } final ParameterExpression row_ = @@ -260,7 +266,8 @@ private Expression fieldExpression(ParameterExpression row_, int i, switch (relFieldType.getSqlTypeName()) { case ARRAY: case MULTISET: - final RelDataType fieldType = relFieldType.getComponentType(); + final RelDataType fieldType = requireNonNull(relFieldType.getComponentType(), + () -> "relFieldType.getComponentType() for " + relFieldType); if (fieldType.isStruct()) { // We can't represent a multiset or array as a List, because // the consumer does not know the element type. diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java index 1ecb22604272..9a4b3d8838fb 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java @@ -24,6 +24,8 @@ import org.apache.calcite.schema.QueryableTable; import org.apache.calcite.schema.Table; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Planner rule that converts a * {@link org.apache.calcite.rel.logical.LogicalTableScan} to * {@link EnumerableConvention enumerable calling convention}. @@ -43,7 +45,7 @@ protected EnumerableTableScanRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { LogicalTableScan scan = (LogicalTableScan) rel; final RelOptTable relOptTable = scan.getTable(); final Table table = relOptTable.unwrap(Table.class); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTraitsUtils.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTraitsUtils.java index 16dc2aa85575..3fddfbd2fd65 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTraitsUtils.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTraitsUtils.java @@ -39,6 +39,7 @@ import com.google.common.collect.ImmutableList; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; import java.util.List; @@ -82,7 +83,7 @@ private static boolean isCollationOnTrivialExpr( return true; } - static Pair> passThroughTraitsForProject( + static @Nullable Pair> passThroughTraitsForProject( RelTraitSet required, List exps, RelDataType inputRowType, @@ -108,7 +109,7 @@ static Pair> passThroughTraitsForProject( ImmutableList.of(currentTraits.replace(newCollation))); } - static Pair> deriveTraitsForProject( + static @Nullable Pair> deriveTraitsForProject( RelTraitSet childTraits, int childId, List exps, RelDataType inputRowType, RelDataTypeFactory typeFactory, RelTraitSet currentTraits) { final RelCollation collation = childTraits.getCollation(); @@ -159,7 +160,7 @@ static Pair> deriveTraitsForProject( * @param leftInputFieldCount number of field count of left join input * @param joinTraitSet trait set of the join */ - static Pair> passThroughTraitsForJoin( + static @Nullable Pair> passThroughTraitsForJoin( RelTraitSet required, JoinRelType joinType, int leftInputFieldCount, RelTraitSet joinTraitSet) { RelCollation collation = required.getCollation(); @@ -194,7 +195,7 @@ static Pair> passThroughTraitsForJoin( * @param joinTraitSet trait set of the join * @param rightTraitSet trait set of the right join input */ - static Pair> deriveTraitsForJoin( + static @Nullable Pair> deriveTraitsForJoin( RelTraitSet childTraits, int childId, JoinRelType joinType, RelTraitSet joinTraitSet, RelTraitSet rightTraitSet) { // should only derive traits (limited to collation for now) from left join input. diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUnion.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUnion.java index 701cbd2a051d..5e53fc2c163f 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUnion.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUnion.java @@ -28,6 +28,8 @@ import java.util.List; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Union} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableUnion extends Union implements EnumerableRel { @@ -64,7 +66,7 @@ public EnumerableUnion(RelOptCluster cluster, RelTraitSet traitSet, } } - builder.add(unionExp); + builder.add(requireNonNull(unionExp, "unionExp")); final PhysType physType = PhysTypeImpl.of( implementor.getTypeFactory(), diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java index 4a624a8a4dad..c732d14319dd 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java @@ -42,10 +42,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Values} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableValues extends Values implements EnumerableRel { @@ -71,10 +75,10 @@ public static EnumerableValues create(RelOptCluster cluster, @Override public RelNode copy(RelTraitSet traitSet, List inputs) { assert inputs.isEmpty(); - return new EnumerableValues(getCluster(), rowType, tuples, traitSet); + return new EnumerableValues(getCluster(), getRowType(), tuples, traitSet); } - @Override public RelNode passThrough(final RelTraitSet required) { + @Override public @Nullable RelNode passThrough(final RelTraitSet required) { RelCollation collation = required.getCollation(); if (collation == null || collation.isDefault()) { return null; @@ -93,7 +97,7 @@ public static EnumerableValues create(RelOptCluster cluster, } } // Check whether the tuples are sorted by required collations. - if (!ordering.isOrdered(tuples)) { + if (!requireNonNull(ordering, "ordering").isOrdered(tuples)) { return null; } } @@ -126,7 +130,7 @@ public static EnumerableValues create(RelOptCluster cluster, final Type rowClass = physType.getJavaRowType(); final List expressions = new ArrayList<>(); - final List fields = rowType.getFieldList(); + final List fields = getRowType().getFieldList(); for (List tuple : tuples) { final List literals = new ArrayList<>(); for (Pair pair diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java index a8aa7fda5b30..1b6cac78cd17 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java @@ -56,14 +56,21 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.function.Function; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Window} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableWindow extends Window implements EnumerableRel { @@ -75,7 +82,7 @@ public class EnumerableWindow extends Window implements EnumerableRel { @Override public RelNode copy(RelTraitSet traitSet, List inputs) { return new EnumerableWindow(getCluster(), traitSet, sole(inputs), - constants, rowType, groups); + constants, getRowType(), groups); } @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { @@ -102,7 +109,7 @@ private WindowRelInputGetter(Expression row, this.constants = constants; } - @Override public Expression field(BlockBuilder list, int index, Type storageType) { + @Override public Expression field(BlockBuilder list, int index, @Nullable Type storageType) { if (index < actualInputFieldCount) { Expression current = list.append("current", row); return rowPhysType.fieldReference(current, index, storageType); @@ -111,7 +118,7 @@ private WindowRelInputGetter(Expression row, } } - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "nullness"}) private void sampleOfTheGeneratedWindowedAggregate() { // Here's overview of the generated code // For each list of rows that have the same partitioning key, evaluate @@ -221,7 +228,10 @@ private void sampleOfTheGeneratedWindowedAggregate() { final RelDataTypeFactory.Builder typeBuilder = typeFactory.builder(); typeBuilder.addAll(inputPhysType.getRowType().getFieldList()); for (AggImpState agg : aggs) { - typeBuilder.add(agg.call.name, agg.call.type); + // CALCITE-4326 + String name = requireNonNull(agg.call.name, + () -> "agg.call.name for " + agg.call); + typeBuilder.add(name, agg.call.type); } RelDataType outputRowType = typeBuilder.build(); final PhysType outputPhysType = @@ -237,7 +247,7 @@ private void sampleOfTheGeneratedWindowedAggregate() { collectionExpr, BuiltInMethod.COLLECTION_SIZE.method)), false); - Pair collationKey = + Pair<@Nullable Expression, @Nullable Expression> collationKey = getRowCollationKey(builder, inputPhysType, group, windowIdx); Expression keySelector = collationKey.left; Expression keyComparator = collationKey.right; @@ -383,8 +393,9 @@ private void sampleOfTheGeneratedWindowedAggregate() { Expressions.statement(Expressions.assign(actualStart, startX))); for (final AggImpState agg : aggs) { - agg.implementor.implementReset(agg.context, - new WinAggResetContextImpl(builder6, agg.state, i_, startX, endX, + List aggState = requireNonNull(agg.state, "agg.state"); + agg.implementor.implementReset(requireNonNull(agg.context, "agg.context"), + new WinAggResetContextImpl(builder6, aggState, i_, startX, endX, hasRows, frameRowCount, partitionRowCount)); } @@ -739,7 +750,7 @@ private Pair getPartitionIterator( comparator_))); } - private Pair getRowCollationKey( + private Pair<@Nullable Expression, @Nullable Expression> getRowCollationKey( BlockBuilder builder, PhysType inputPhysType, Group group, int windowIdx) { if (!(group.isRows @@ -830,15 +841,17 @@ private void declareAndResetState(final JavaTypeFactory typeFactory, builder.add( Expressions.declare(0, aggRes, - Expressions.constant(Primitive.is(aggRes.getType()) - ? Primitive.of(aggRes.getType()).defaultValue - : null, + Expressions.constant( + Optional.ofNullable(Primitive.of(aggRes.getType())) + .map(x -> x.defaultValue) + .orElse(null), aggRes.getType()))); agg.result = aggRes; outputRow.add(aggRes); agg.implementor.implementReset(agg.context, new WinAggResetContextImpl(builder, agg.state, - null, null, null, null, null, null)); + castNonNull(null), castNonNull(null), castNonNull(null), castNonNull(null), + castNonNull(null), castNonNull(null))); } } @@ -849,7 +862,7 @@ private void implementAdd(List aggs, final DeclarationStatement jDecl) { for (final AggImpState agg : aggs) { final WinAggAddContext addContext = - new WinAggAddContextImpl(builder7, agg.state, frame) { + new WinAggAddContextImpl(builder7, requireNonNull(agg.state, "agg.state"), frame) { @Override public Expression currentPosition() { return jDecl.parameter; } @@ -858,11 +871,11 @@ private void implementAdd(List aggs, return rexArguments.apply(agg); } - @Override public RexNode rexFilterArgument() { + @Override public @Nullable RexNode rexFilterArgument() { return null; // REVIEW } }; - agg.implementor.implementAdd(agg.context, addContext); + agg.implementor.implementAdd(requireNonNull(agg.context, "agg.context"), addContext); } } @@ -884,17 +897,19 @@ private boolean implementResult(List aggs, continue; } nonEmpty = true; - Expression res = agg.implementor.implementResult(agg.context, - new WinAggResultContextImpl(builder, agg.state, frame) { + Expression res = agg.implementor.implementResult(requireNonNull(agg.context, "agg.context"), + new WinAggResultContextImpl(builder, requireNonNull(agg.state, "agg.state"), frame) { @Override public List rexArguments() { return rexArguments.apply(agg); } }); // Several count(a) and count(b) might share the result + Expression result = requireNonNull(agg.result, + () -> "agg.result for " + agg.call); Expression aggRes = builder.append("a" + agg.aggIdx + "res", - EnumUtils.convert(res, agg.result.getType())); + EnumUtils.convert(res, result.getType())); builder.add( - Expressions.statement(Expressions.assign(agg.result, aggRes))); + Expressions.statement(Expressions.assign(result, aggRes))); } return nonEmpty; } @@ -905,7 +920,7 @@ private Expression translateBound(RexToLixTranslator translator, boolean lower, PhysType physType, @SuppressWarnings("unused") Expression rowComparator, // TODO: remove or use - Expression keySelector, Expression keyComparator) { + @Nullable Expression keySelector, @Nullable Expression keyComparator) { RexWindowBound bound = lower ? group.lowerBound : group.upperBound; if (bound.isUnbounded()) { return bound.isPreceding() ? min_ : max_; @@ -945,7 +960,9 @@ private Expression translateBound(RexToLixTranslator translator, (lower ? BuiltInMethod.BINARY_SEARCH5_LOWER : BuiltInMethod.BINARY_SEARCH5_UPPER).method, - rows_, row_, searchLower, searchUpper, keySelector, keyComparator); + rows_, row_, searchLower, searchUpper, + requireNonNull(keySelector, "keySelector"), + requireNonNull(keyComparator, "keyComparator")); } assert fieldCollations.size() == 1 : "When using range window specification, ORDER BY should have" @@ -976,6 +993,8 @@ private Expression translateBound(RexToLixTranslator translator, (lower ? BuiltInMethod.BINARY_SEARCH6_LOWER : BuiltInMethod.BINARY_SEARCH6_UPPER).method, - rows_, val, searchLower, searchUpper, keySelector, keyComparator); + rows_, val, searchLower, searchUpper, + requireNonNull(keySelector, "keySelector"), + requireNonNull(keyComparator, "keyComparator")); } } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java index cebe0a6228e2..8b76e592ea86 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java @@ -29,6 +29,8 @@ import org.apache.calcite.runtime.Unit; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; @@ -61,7 +63,7 @@ public enum JavaRowFormat { } @Override public MemberExpression field(Expression expression, int field, - Type fromType, Type fieldType) { + @Nullable Type fromType, Type fieldType) { final Type type = expression.getType(); if (type instanceof Types.RecordType) { Types.RecordType recordType = (Types.RecordType) type; @@ -94,7 +96,7 @@ public enum JavaRowFormat { return expressions.get(0); } - @Override public Expression field(Expression expression, int field, Type fromType, + @Override public Expression field(Expression expression, int field, @Nullable Type fromType, Type fieldType) { assert field == 0; return expression; @@ -177,7 +179,7 @@ public enum JavaRowFormat { } } - @Override public Expression field(Expression expression, int field, Type fromType, + @Override public Expression field(Expression expression, int field, @Nullable Type fromType, Type fieldType) { final MethodCallExpression e = Expressions.call(expression, BuiltInMethod.LIST_GET.method, Expressions.constant(field)); @@ -206,7 +208,7 @@ public enum JavaRowFormat { return Expressions.call(BuiltInMethod.ROW_AS_COPY.method, expressions); } - @Override public Expression field(Expression expression, int field, Type fromType, + @Override public Expression field(Expression expression, int field, @Nullable Type fromType, Type fieldType) { final Expression e = Expressions.call(expression, BuiltInMethod.ROW_VALUE.method, Expressions.constant(field)); @@ -237,7 +239,7 @@ public enum JavaRowFormat { return Expressions.call(BuiltInMethod.ARRAY_COMPARER.method); } - @Override public Expression field(Expression expression, int field, Type fromType, + @Override public Expression field(Expression expression, int field, @Nullable Type fromType, Type fieldType) { final IndexExpression e = Expressions.arrayIndex(expression, Expressions.constant(field)); @@ -280,7 +282,7 @@ abstract Type javaFieldClass(JavaTypeFactory typeFactory, RelDataType type, public abstract Expression record( Type javaRowClass, List expressions); - public Expression comparer() { + public @Nullable Expression comparer() { return null; } @@ -290,5 +292,5 @@ public Expression comparer() { * field. */ public abstract Expression field(Expression expression, int field, - Type fromType, Type fieldType); + @Nullable Type fromType, Type fieldType); } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/NestedBlockBuilderImpl.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/NestedBlockBuilderImpl.java index 036943c5f816..581f84049606 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/NestedBlockBuilderImpl.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/NestedBlockBuilderImpl.java @@ -33,6 +33,7 @@ public class NestedBlockBuilderImpl implements NestedBlockBuilder { * Constructs nested block builders starting of a given code block. * @param block root code block */ + @SuppressWarnings("method.invocation.invalid") public NestedBlockBuilderImpl(BlockBuilder block) { nestBlock(block); } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysType.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysType.java index 460ad8874eee..4f4eeaf649c8 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysType.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysType.java @@ -23,6 +23,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; @@ -92,7 +94,7 @@ public interface PhysType { * @return Expression to access the field of the expression */ Expression fieldReference(Expression expression, int field, - Type storageType); + @Nullable Type storageType); /** Generates an accessor function for a given list of fields. The resulting * object is a {@link List} (implementing {@link Object#hashCode()} and @@ -177,7 +179,7 @@ Expression generateComparator( /** Returns a expression that yields a comparer, or null if this type * is comparable. */ - Expression comparer(); + @Nullable Expression comparer(); /** Generates an expression that creates a record for a row, initializing * its fields with the given expressions. There must be one expression per diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java index b96ebf6d75e8..ad57cf3dc710 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java @@ -40,6 +40,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.AbstractList; @@ -50,6 +52,8 @@ import static org.apache.calcite.adapter.enumerable.EnumUtils.generateCollatorExpression; import static org.apache.calcite.adapter.enumerable.EnumUtils.overridingMethodDecl; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link PhysType}. */ public class PhysTypeImpl implements PhysType { private final JavaTypeFactory typeFactory; @@ -518,8 +522,10 @@ static PhysType of( @Override public PhysType component(int fieldOrdinal) { final RelDataTypeField field = rowType.getFieldList().get(fieldOrdinal); + RelDataType componentType = requireNonNull(field.getType().getComponentType(), + () -> "field.getType().getComponentType() for " + field); return PhysTypeImpl.of(typeFactory, - toStruct(field.getType().getComponentType()), format, false); + toStruct(componentType), format, false); } @Override public PhysType field(int ordinal) { @@ -537,7 +543,7 @@ private RelDataType toStruct(RelDataType type) { .build(); } - @Override public Expression comparer() { + @Override public @Nullable Expression comparer() { return format.comparer(); } @@ -669,7 +675,7 @@ private List fieldReferences( } @Override public Expression fieldReference( - Expression expression, int field, Type storageType) { + Expression expression, int field, @Nullable Type storageType) { Type fieldType; if (storageType == null) { storageType = fieldClass(field); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java index 184c5dfbd33a..fdea9bb6e1c7 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java @@ -71,6 +71,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -306,6 +308,8 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.UPPER; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.USER; +import static java.util.Objects.requireNonNull; + /** * Contains implementations of Rex operators as Java code. */ @@ -335,6 +339,7 @@ public class RexImpTable { private final Map> tvfImplementorMap = new HashMap<>(); + @SuppressWarnings("method.invocation.invalid") RexImpTable() { defineMethod(ROW, BuiltInMethod.ARRAY.method, NullPolicy.NONE); defineMethod(UPPER, BuiltInMethod.UPPER.method, NullPolicy.STRICT); @@ -723,7 +728,7 @@ private void defineMethod(SqlOperator operator, Method method, } private void defineUnary(SqlOperator operator, ExpressionType expressionType, - NullPolicy nullPolicy, String backupMethodName) { + NullPolicy nullPolicy, @Nullable String backupMethodName) { map.put(operator, new UnaryImplementor(expressionType, nullPolicy, backupMethodName)); } @@ -764,7 +769,7 @@ private static RexCallImplementor wrapAsRexCallImplementor( }; } - public RexCallImplementor get(final SqlOperator operator) { + public @Nullable RexCallImplementor get(final SqlOperator operator) { if (operator instanceof SqlUserDefinedFunction) { org.apache.calcite.schema.Function udf = ((SqlUserDefinedFunction) operator).getFunction(); @@ -781,7 +786,7 @@ public RexCallImplementor get(final SqlOperator operator) { return map.get(operator); } - public AggImplementor get(final SqlAggFunction aggregation, + public @Nullable AggImplementor get(final SqlAggFunction aggregation, boolean forWindowAggregate) { if (aggregation instanceof SqlUserDefinedAggFunction) { final SqlUserDefinedAggFunction udaf = @@ -926,7 +931,7 @@ public Expression handle(Expression x) { switch (this) { case NOT_POSSIBLE: return EnumUtils.convert(x, - Primitive.ofBox(x.getType()).primitiveClass); + Primitive.unbox(x.getType())); default: break; } @@ -953,8 +958,8 @@ public Expression handle(Expression x) { } static Expression getDefaultValue(Type type) { - if (Primitive.is(type)) { - Primitive p = Primitive.of(type); + Primitive p = Primitive.of(type); + if (p != null) { return Expressions.constant(p.defaultValue, type); } return Expressions.constant(null, type); @@ -1390,7 +1395,10 @@ public UserDefinedAggReflectiveImplementor(AggregateFunctionImpl afi) { AggResultContext result) { List acc = result.accumulator(); return Expressions.call( - afi.isStatic ? null : acc.get(1), afi.resultMethod, acc.get(0)); + afi.isStatic ? null : acc.get(1), + requireNonNull(afi.resultMethod, + () -> "resultMethod is null. Does " + afi.declaringClass + " declare result method?"), + acc.get(0)); } } @@ -1404,8 +1412,9 @@ static class RankImplementor extends StrictWinAggImplementor { new Object() { int curentPosition; // position in for-win-agg-loop int startIndex; // index of start of window - Comparable[] rows; // accessed via WinAggAddContext.compareRows - { + Comparable @Nullable [] rows; // accessed via WinAggAddContext.compareRows + @SuppressWarnings("nullness") + void sample() { if (curentPosition > startIndex) { if (rows[curentPosition - 1].compareTo(rows[curentPosition]) > 0) { @@ -1925,7 +1934,8 @@ private static class FloorImplementor extends MethodNameImplementor { preFloor = false; } final TimeUnitRange timeUnitRange = - (TimeUnitRange) translator.getLiteralValue(argValueList.get(1)); + (TimeUnitRange) requireNonNull(translator.getLiteralValue(argValueList.get(1)), + "timeUnitRange"); switch (timeUnitRange) { case YEAR: case QUARTER: @@ -1959,7 +1969,7 @@ private Expression call(Expression operand, Type type, private static class MethodImplementor extends AbstractRexCallImplementor { protected final Method method; - MethodImplementor(Method method, NullPolicy nullPolicy, boolean harmonize) { + MethodImplementor(Method method, @Nullable NullPolicy nullPolicy, boolean harmonize) { super(nullPolicy, harmonize); this.method = method; } @@ -2204,10 +2214,10 @@ private Expression maybeBox(Expression expression) { /** Implementor for unary operators. */ private static class UnaryImplementor extends AbstractRexCallImplementor { private final ExpressionType expressionType; - private final String backupMethodName; + private final @Nullable String backupMethodName; UnaryImplementor(ExpressionType expressionType, NullPolicy nullPolicy, - String backupMethodName) { + @Nullable String backupMethodName) { super(nullPolicy, false); this.expressionType = expressionType; this.backupMethodName = backupMethodName; @@ -2255,7 +2265,7 @@ private static class ExtractImplementor extends AbstractRexCallImplementor { final RexCall call, final List argValueList) { final TimeUnitRange timeUnitRange = (TimeUnitRange) translator.getLiteralValue(argValueList.get(0)); - final TimeUnit unit = timeUnitRange.startUnit; + final TimeUnit unit = requireNonNull(timeUnitRange, "timeUnitRange").startUnit; Expression operand = argValueList.get(1); final SqlTypeName sqlTypeName = call.operands.get(1).getType().getSqlTypeName(); @@ -2475,10 +2485,10 @@ private static class CastImplementor extends AbstractRexCallImplementor { private RelDataType nullifyType(JavaTypeFactory typeFactory, final RelDataType type, final boolean nullable) { if (type instanceof RelDataTypeFactoryImpl.JavaType) { - final Primitive primitive = Primitive.ofBox( - ((RelDataTypeFactoryImpl.JavaType) type).getJavaClass()); - if (primitive != null) { - return typeFactory.createJavaType(primitive.primitiveClass); + Class javaClass = ((RelDataTypeFactoryImpl.JavaType) type).getJavaClass(); + final Class primitive = Primitive.unbox(javaClass); + if (primitive != javaClass) { + return typeFactory.createJavaType(primitive); } } return typeFactory.createTypeWithNullability(type, nullable); @@ -2812,7 +2822,7 @@ private static class LastImplementor implements MatchImplementor { // Just take the last one, if exists if ("*".equals(alpha)) { - ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex(i); + setInputGetterIndex(translator, i); // Important, unbox the node / expression to avoid NullAs.NOT_POSSIBLE final RexPatternFieldRef ref = (RexPatternFieldRef) node; final RexPatternFieldRef newRef = @@ -2821,11 +2831,11 @@ private static class LastImplementor implements MatchImplementor { translator.typeFactory.createTypeWithNullability(ref.getType(), true)); final Expression expression = translator.translate(newRef, NullAs.NULL); - ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex(null); + setInputGetterIndex(translator, null); return expression; } else { // Alpha != "*" so we have to search for a specific one to find and use that, if found - ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex( + setInputGetterIndex(translator, Expressions.call(BuiltInMethod.MATCH_UTILS_LAST_WITH_SYMBOL.method, Expressions.constant(alpha), rows, symbols, i)); @@ -2837,10 +2847,15 @@ private static class LastImplementor implements MatchImplementor { translator.typeFactory.createTypeWithNullability(ref.getType(), true)); final Expression expression = translator.translate(newRef, NullAs.NULL); - ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex(null); + setInputGetterIndex(translator, null); return expression; } } + + private void setInputGetterIndex(RexToLixTranslator translator, @Nullable Expression o) { + requireNonNull((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter, + "inputGetter").setIndex(o); + } } /** Null-safe implementor of {@code RexCall}s. */ @@ -2869,10 +2884,10 @@ RexToLixTranslator.Result implement( */ private abstract static class AbstractRexCallImplementor implements RexCallImplementor { - final NullPolicy nullPolicy; + final @Nullable NullPolicy nullPolicy; private final boolean harmonize; - AbstractRexCallImplementor(NullPolicy nullPolicy, boolean harmonize) { + AbstractRexCallImplementor(@Nullable NullPolicy nullPolicy, boolean harmonize) { this.nullPolicy = nullPolicy; this.harmonize = harmonize; } @@ -3102,7 +3117,8 @@ private static class LogicalAndImplementor extends AbstractRexCallImplementor { @Override Expression implementSafe(final RexToLixTranslator translator, final RexCall call, final List argValueList) { - return null; + throw new IllegalStateException("This implementSafe should not be called," + + " please call implement(...)"); } } @@ -3158,7 +3174,8 @@ private static class LogicalOrImplementor extends AbstractRexCallImplementor { @Override Expression implementSafe(final RexToLixTranslator translator, final RexCall call, final List argValueList) { - return null; + throw new IllegalStateException("This implementSafe should not be called," + + " please call implement(...)"); } } @@ -3193,7 +3210,7 @@ private static class LogicalNotImplementor extends AbstractRexCallImplementor { private static class ReflectiveImplementor extends AbstractRexCallImplementor { protected final Method method; - ReflectiveImplementor(Method method, NullPolicy nullPolicy) { + ReflectiveImplementor(Method method, @Nullable NullPolicy nullPolicy) { super(nullPolicy, false); this.method = method; } @@ -3242,7 +3259,6 @@ private static class RandImplementor extends AbstractRexCallImplementor { /** Implementor for the {@code RAND_INTEGER} function. */ private static class RandIntegerImplementor extends AbstractRexCallImplementor { private final AbstractRexCallImplementor[] implementors = { - null, new ReflectiveImplementor(BuiltInMethod.RAND_INTEGER.method, nullPolicy), new ReflectiveImplementor(BuiltInMethod.RAND_INTEGER_SEED.method, nullPolicy) }; @@ -3257,7 +3273,7 @@ private static class RandIntegerImplementor extends AbstractRexCallImplementor { @Override Expression implementSafe(final RexToLixTranslator translator, final RexCall call, final List argValueList) { - return implementors[call.getOperands().size()] + return implementors[call.getOperands().size() - 1] .implementSafe(translator, call, argValueList); } } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java index 695a1a92a1f7..ee78362efd58 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java @@ -64,6 +64,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -72,7 +74,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import static org.apache.calcite.sql.fun.SqlLibraryOperators.TRANSLATE3; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CASE; @@ -83,6 +84,8 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.SUBSTRING; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.UPPER; +import static java.util.Objects.requireNonNull; + /** * Translates {@link org.apache.calcite.rex.RexNode REX expressions} to * {@link Expression linq4j expressions}. @@ -103,12 +106,12 @@ public class RexToLixTranslator implements RexVisitor final JavaTypeFactory typeFactory; final RexBuilder builder; - private final RexProgram program; + private final @Nullable RexProgram program; final SqlConformance conformance; private final Expression root; - final RexToLixTranslator.InputGetter inputGetter; + final RexToLixTranslator.@Nullable InputGetter inputGetter; private final BlockBuilder list; - private final Function1 correlates; + private final @Nullable Function1 correlates; /** * Map from RexLiteral's variable name to its literal, which is often a @@ -129,14 +132,14 @@ public class RexToLixTranslator implements RexVisitor /** Map from RexNode under specific storage type to its Result, to avoid * generating duplicate code. For {@code RexInputRef}, {@code RexDynamicParam} * and {@code RexFieldAccess}. */ - private final Map, Result> rexWithStorageTypeResultMap = + private final Map, Result> rexWithStorageTypeResultMap = new HashMap<>(); /** Map from RexNode to its Result, to avoid generating duplicate code. * For {@code RexLiteral} and {@code RexCall}. */ private final Map rexResultMap = new HashMap<>(); - private Type currentStorageType; + private @Nullable Type currentStorageType; private static Method findMethod( Class clazz, String name, Class... parameterTypes) { @@ -147,21 +150,21 @@ private static Method findMethod( } } - private RexToLixTranslator(RexProgram program, + private RexToLixTranslator(@Nullable RexProgram program, JavaTypeFactory typeFactory, Expression root, - InputGetter inputGetter, + @Nullable InputGetter inputGetter, BlockBuilder list, RexBuilder builder, SqlConformance conformance, - Function1 correlates) { + @Nullable Function1 correlates) { this.program = program; // may be null - this.typeFactory = Objects.requireNonNull(typeFactory); - this.conformance = Objects.requireNonNull(conformance); - this.root = Objects.requireNonNull(root); + this.typeFactory = requireNonNull(typeFactory); + this.conformance = requireNonNull(conformance); + this.root = requireNonNull(root); this.inputGetter = inputGetter; - this.list = Objects.requireNonNull(list); - this.builder = Objects.requireNonNull(builder); + this.list = requireNonNull(list); + this.builder = requireNonNull(builder); this.correlates = correlates; // may be null } @@ -182,8 +185,8 @@ private RexToLixTranslator(RexProgram program, */ public static List translateProjects(RexProgram program, JavaTypeFactory typeFactory, SqlConformance conformance, - BlockBuilder list, PhysType outputPhysType, Expression root, - InputGetter inputGetter, Function1 correlates) { + BlockBuilder list, @Nullable PhysType outputPhysType, Expression root, + InputGetter inputGetter, @Nullable Function1 correlates) { List storageTypes = null; if (outputPhysType != null) { final RelDataType rowType = outputPhysType.getRowType(); @@ -209,7 +212,7 @@ blockBuilder, new RexBuilder(typeFactory), conformance, null) /** Creates a translator for translating aggregate functions. */ public static RexToLixTranslator forAggregation(JavaTypeFactory typeFactory, - BlockBuilder list, InputGetter inputGetter, SqlConformance conformance) { + BlockBuilder list, @Nullable InputGetter inputGetter, SqlConformance conformance) { final ParameterExpression root = DataContext.ROOT; return new RexToLixTranslator(null, typeFactory, root, inputGetter, list, new RexBuilder(typeFactory), conformance, null); @@ -225,14 +228,14 @@ Expression translate(RexNode expr, RexImpTable.NullAs nullAs) { return translate(expr, nullAs, null); } - Expression translate(RexNode expr, Type storageType) { + Expression translate(RexNode expr, @Nullable Type storageType) { final RexImpTable.NullAs nullAs = RexImpTable.NullAs.of(isNullable(expr)); return translate(expr, nullAs, storageType); } Expression translate(RexNode expr, RexImpTable.NullAs nullAs, - Type storageType) { + @Nullable Type storageType) { currentStorageType = storageType; final Result result = expr.accept(this); final Expression translated = @@ -467,7 +470,7 @@ Expression translateCast( Expressions.call( BuiltInMethod.INTERVAL_YEAR_MONTH_TO_STRING.method, operand, - Expressions.constant(interval.timeUnitRange))); + Expressions.constant(requireNonNull(interval, "interval").timeUnitRange))); break; case INTERVAL_DAY: case INTERVAL_DAY_HOUR: @@ -484,7 +487,7 @@ Expression translateCast( Expressions.call( BuiltInMethod.INTERVAL_DAY_TIME_TO_STRING.method, operand, - Expressions.constant(interval.timeUnitRange), + Expressions.constant(requireNonNull(interval, "interval").timeUnitRange), Expressions.constant( interval.getFractionalSecondPrecision( typeFactory.getTypeSystem())))); @@ -576,7 +579,9 @@ Expression translateCast( case INTERVAL_MINUTE: case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: - switch (sourceType.getSqlTypeName().getFamily()) { + switch (requireNonNull(sourceType.getSqlTypeName().getFamily(), + () -> "null SqlTypeFamily for " + sourceType + ", SqlTypeName " + + sourceType.getSqlTypeName())) { case NUMERIC: final BigDecimal multiplier = targetType.getSqlTypeName().getEndUnit().multiplier; final BigDecimal divider = BigDecimal.ONE; @@ -592,7 +597,7 @@ Expression translateCast( return scaleIntervalToNumber(sourceType, targetType, convert); } - private Expression translateCastToTime(RelDataType sourceType, Expression operand) { + private @Nullable Expression translateCastToTime(RelDataType sourceType, Expression operand) { Expression convert = null; switch (sourceType.getSqlTypeName()) { case CHAR: @@ -630,7 +635,7 @@ private Expression translateCastToTime(RelDataType sourceType, Expression operan return convert; } - private Expression translateCastToDate(RelDataType sourceType, Expression operand) { + private @Nullable Expression translateCastToDate(RelDataType sourceType, Expression operand) { Expression convert = null; switch (sourceType.getSqlTypeName()) { case CHAR: @@ -689,7 +694,8 @@ Expression handleMethodCheckedExceptions(Expression callExpr) { public RexNode deref(RexNode expr) { if (expr instanceof RexLocalRef) { RexLocalRef ref = (RexLocalRef) expr; - final RexNode e2 = program.getExprList().get(ref.getIndex()); + final RexNode e2 = requireNonNull(program, "program") + .getExprList().get(ref.getIndex()); assert ref.getType().equals(e2.getType()); return e2; } else { @@ -743,7 +749,9 @@ public static Expression translateLiteral( } assert javaClass == BigDecimal.class; return Expressions.new_(BigDecimal.class, - Expressions.constant(bd.toString())); + Expressions.constant( + requireNonNull(bd, + () -> "value for " + literal).toString())); case DATE: case TIME: case TIME_WITH_LOCAL_TIME_ZONE: @@ -780,12 +788,14 @@ public static Expression translateLiteral( literal.getValueAs(byte[].class), byte[].class)); case GEOMETRY: - final Geometries.Geom geom = literal.getValueAs(Geometries.Geom.class); + final Geometries.Geom geom = requireNonNull(literal.getValueAs(Geometries.Geom.class), + () -> "getValueAs(Geometries.Geom) for " + literal); final String wkt = GeoFunctions.ST_AsWKT(geom); return Expressions.call(null, BuiltInMethod.ST_GEOM_FROM_TEXT.method, Expressions.constant(wkt)); case SYMBOL: - value2 = literal.getValueAs(Enum.class); + value2 = requireNonNull(literal.getValueAs(Enum.class), + () -> "getValueAs(Enum.class) for " + literal); javaClass = value2.getClass(); break; default: @@ -810,9 +820,9 @@ public List translateList( public List translateList( List operandList, RexImpTable.NullAs nullAs, - List storageTypes) { + List storageTypes) { final List list = new ArrayList<>(); - for (Pair e : Pair.zip(operandList, storageTypes)) { + for (Pair e : Pair.zip(operandList, storageTypes)) { list.add(translate(e.left, nullAs, e.right)); } return list; @@ -849,7 +859,7 @@ public List translateList(List operandList) { * @return translated expressions */ public List translateList(List operandList, - List storageTypes) { + @Nullable List storageTypes) { final List list = new ArrayList<>(operandList.size()); for (int i = 0; i < operandList.size(); i++) { @@ -887,7 +897,8 @@ private Expression translateTableFunction(RexCall rexCall, Expression inputEnume public static Expression translateCondition(RexProgram program, JavaTypeFactory typeFactory, BlockBuilder list, InputGetter inputGetter, Function1 correlates, SqlConformance conformance) { - if (program.getCondition() == null) { + RexLocalRef condition = program.getCondition(); + if (condition == null) { return RexImpTable.TRUE_EXPR; } final ParameterExpression root = DataContext.ROOT; @@ -896,7 +907,7 @@ public static Expression translateCondition(RexProgram program, new RexBuilder(typeFactory), conformance, null); translator = translator.setCorrelates(correlates); return translator.translate( - program.getCondition(), + condition, RexImpTable.NullAs.FALSE); } @@ -917,7 +928,7 @@ public RexToLixTranslator setBlock(BlockBuilder block) { } public RexToLixTranslator setCorrelates( - Function1 correlates) { + @Nullable Function1 correlates) { if (this.correlates == correlates) { return this; } @@ -933,7 +944,8 @@ private static Expression scaleIntervalToNumber( RelDataType sourceType, RelDataType targetType, Expression operand) { - switch (targetType.getSqlTypeName().getFamily()) { + switch (requireNonNull(targetType.getSqlTypeName().getFamily(), + () -> "SqlTypeFamily for " + targetType)) { case NUMERIC: switch (sourceType.getSqlTypeName()) { case INTERVAL_YEAR: @@ -977,7 +989,7 @@ private static Expression scaleIntervalToNumber( * } */ @Override public Result visitInputRef(RexInputRef inputRef) { - final Pair key = Pair.of(inputRef, currentStorageType); + final Pair key = Pair.of(inputRef, currentStorageType); // If the RexInputRef has been visited under current storage type already, // it is not necessary to visit it again, just return the result. if (rexWithStorageTypeResultMap.containsKey(key)) { @@ -985,7 +997,7 @@ private static Expression scaleIntervalToNumber( } // Generate one line of code to get the input, e.g., // "final Employee current =(Employee) inputEnumerator.current();" - final Expression valueExpression = inputGetter.field( + final Expression valueExpression = requireNonNull(inputGetter, "inputGetter").field( list, inputRef.getIndex(), currentStorageType); // Generate one line of code for the value of RexInputRef, e.g., @@ -1123,7 +1135,7 @@ private ConstantExpression getTypedNullLiteral(RexLiteral literal) { throw new RuntimeException("cannot translate call " + call); } final List operandList = call.getOperands(); - final List storageTypes = EnumUtils.internalTypes(operandList); + final List<@Nullable Type> storageTypes = EnumUtils.internalTypes(operandList); final List operandResults = new ArrayList<>(); for (int i = 0; i < operandList.size(); i++) { final Result operandResult = @@ -1137,7 +1149,7 @@ private ConstantExpression getTypedNullLiteral(RexLiteral literal) { } private static Result implementCallOperand(final RexNode operand, - final Type storageType, final RexToLixTranslator translator) { + final @Nullable Type storageType, final RexToLixTranslator translator) { final Type originalStorageType = translator.currentStorageType; translator.currentStorageType = storageType; Result operandResult = operand.accept(translator); @@ -1149,7 +1161,7 @@ private static Result implementCallOperand(final RexNode operand, } private static Expression implementCallOperand2(final RexNode operand, - final Type storageType, final RexToLixTranslator translator) { + final @Nullable Type storageType, final RexToLixTranslator translator) { final Type originalStorageType = translator.currentStorageType; translator.currentStorageType = storageType; final Expression result = translator.translate(operand); @@ -1166,7 +1178,8 @@ private Result implementPrev(RexCall call) { final RexNode offset = call.getOperands().get(1); final Expression offs = Expressions.multiply(translate(offset), Expressions.constant(-1)); - ((EnumerableMatch.PrevInputGetter) inputGetter).setOffset(offs); + requireNonNull((EnumerableMatch.PrevInputGetter) inputGetter, "inputGetter") + .setOffset(offs); return node.accept(this); } @@ -1228,7 +1241,7 @@ private Result implementCaseWhen(RexCall call) { private void implementRecursively(final RexToLixTranslator currentTranslator, final List operandList, final ParameterExpression valueVariable, int pos) { final BlockBuilder currentBlockBuilder = currentTranslator.getBlockBuilder(); - final List storageTypes = EnumUtils.internalTypes(operandList); + final List<@Nullable Type> storageTypes = EnumUtils.internalTypes(operandList); // [ELSE] clause if (pos == operandList.size() - 1) { Expression res = implementCallOperand2(operandList.get(pos), @@ -1293,7 +1306,7 @@ private Result toInnerStorageType(final Result result, final Type storageType) { } @Override public Result visitDynamicParam(RexDynamicParam dynamicParam) { - final Pair key = Pair.of(dynamicParam, currentStorageType); + final Pair key = Pair.of(dynamicParam, currentStorageType); if (rexWithStorageTypeResultMap.containsKey(key)) { return rexWithStorageTypeResultMap.get(key); } @@ -1315,7 +1328,7 @@ private Result toInnerStorageType(final Result result, final Type storageType) { } @Override public Result visitFieldAccess(RexFieldAccess fieldAccess) { - final Pair key = Pair.of(fieldAccess, currentStorageType); + final Pair key = Pair.of(fieldAccess, currentStorageType); if (rexWithStorageTypeResultMap.containsKey(key)) { return rexWithStorageTypeResultMap.get(key); } @@ -1406,11 +1419,12 @@ BlockBuilder getBlockBuilder() { } Expression getLiteral(Expression literalVariable) { - return literalMap.get(literalVariable); + return requireNonNull(literalMap.get(literalVariable), + () -> "literalMap.get(literalVariable) for " + literalVariable); } /** Returns the value of a literal. */ - Object getLiteralValue(Expression expr) { + @Nullable Object getLiteralValue(@Nullable Expression expr) { if (expr instanceof ParameterExpression) { final Expression constantExpr = literalMap.get(expr); return getLiteralValue(constantExpr); @@ -1422,12 +1436,13 @@ Object getLiteralValue(Expression expr) { } List getCallOperandResult(RexCall call) { - return callOperandResultMap.get(call); + return requireNonNull(callOperandResultMap.get(call), + () -> "callOperandResultMap.get(call) for " + call); } /** Translates a field of an input to an expression. */ public interface InputGetter { - Expression field(BlockBuilder list, int index, Type storageType); + Expression field(BlockBuilder list, int index, @Nullable Type storageType); } /** Implementation of {@link InputGetter} that calls @@ -1439,7 +1454,7 @@ public InputGetterImpl(List> inputs) { this.inputs = inputs; } - @Override public Expression field(BlockBuilder list, int index, Type storageType) { + @Override public Expression field(BlockBuilder list, int index, @Nullable Type storageType) { int offset = 0; for (Pair input : inputs) { final PhysType physType = input.right; diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java index 3b77c3c50f87..4531b607d33c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java @@ -119,9 +119,10 @@ protected void implementNotNullReset(AggContext info, final List conditions = new ArrayList<>(); conditions.addAll( translator.translateList(args, RexImpTable.NullAs.IS_NOT_NULL)); - if (add.rexFilterArgument() != null) { + RexNode filterArgument = add.rexFilterArgument(); + if (filterArgument != null) { conditions.add( - translator.translate(add.rexFilterArgument(), + translator.translate(filterArgument, RexImpTable.NullAs.FALSE)); } Expression condition = Expressions.foldAnd(conditions); diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/impl/AggResultContextImpl.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/impl/AggResultContextImpl.java index c146706f6fa7..b36d670f1365 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/impl/AggResultContextImpl.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/impl/AggResultContextImpl.java @@ -23,17 +23,21 @@ import org.apache.calcite.linq4j.tree.ParameterExpression; import org.apache.calcite.rel.core.AggregateCall; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Implementation of * {@link org.apache.calcite.adapter.enumerable.AggResultContext}. */ public class AggResultContextImpl extends AggResetContextImpl implements AggResultContext { - private final AggregateCall call; - private final ParameterExpression key; - private final PhysType keyPhysType; + private final @Nullable AggregateCall call; + private final @Nullable ParameterExpression key; + private final @Nullable PhysType keyPhysType; /** * Creates aggregate result context. @@ -44,24 +48,25 @@ public class AggResultContextImpl extends AggResetContextImpl * aggregate state * @param key Key */ - public AggResultContextImpl(BlockBuilder block, AggregateCall call, - List accumulator, ParameterExpression key, - PhysType keyPhysType) { + public AggResultContextImpl(BlockBuilder block, @Nullable AggregateCall call, + List accumulator, @Nullable ParameterExpression key, + @Nullable PhysType keyPhysType) { super(block, accumulator); - this.call = call; + this.call = call; // null for AggAddContextImpl this.key = key; - this.keyPhysType = keyPhysType; + this.keyPhysType = keyPhysType; // null for AggAddContextImpl } - @Override public Expression key() { + @Override public @Nullable Expression key() { return key; } @Override public Expression keyField(int i) { - return keyPhysType.fieldReference(key, i); + return requireNonNull(keyPhysType, "keyPhysType") + .fieldReference(requireNonNull(key, "key"), i); } @Override public AggregateCall call() { - return call; + return requireNonNull(call, "call"); } } diff --git a/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java b/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java index a2d5c9766978..e9c5ce5c5943 100644 --- a/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java @@ -53,6 +53,9 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -62,6 +65,8 @@ import java.util.List; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link org.apache.calcite.schema.Schema} that exposes the * public fields and methods in a Java object. @@ -69,9 +74,9 @@ public class ReflectiveSchema extends AbstractSchema { private final Class clazz; - private Object target; - private Map tableMap; - private Multimap functionMap; + private final Object target; + private @MonotonicNonNull Map tableMap; + private @MonotonicNonNull Multimap functionMap; /** * Creates a ReflectiveSchema. @@ -126,6 +131,7 @@ private Map createTableMap() { throw new RuntimeException( "Error while accessing field " + field, e); } + requireNonNull(rc, () -> "field must not be null: " + field); FieldTable table = (FieldTable) tableMap.get(Util.last(rc.getSourceQualifiedName())); assert table != null; @@ -172,7 +178,7 @@ private Multimap createFunctionMap() { /** Returns an expression for the object wrapped by this schema (not the * schema itself). */ - Expression getTargetExpression(SchemaPlus parentSchema, String name) { + Expression getTargetExpression(@Nullable SchemaPlus parentSchema, String name) { return EnumUtils.convert( Expressions.call( Schemas.unwrap( @@ -184,7 +190,7 @@ Expression getTargetExpression(SchemaPlus parentSchema, String name) { /** Returns a table based on a particular field of this schema. If the * field is not of the right type to be a relation, returns null. */ - private Table fieldRelation(final Field field) { + private @Nullable Table fieldRelation(final Field field) { final Type elementType = getElementType(field.getType()); if (elementType == null) { return null; @@ -196,6 +202,7 @@ private Table fieldRelation(final Field field) { throw new RuntimeException( "Error while accessing field " + field, e); } + requireNonNull(o, () -> "field " + field + " is null for " + target); @SuppressWarnings("unchecked") final Enumerable enumerable = toEnumerable(o); return new FieldTable<>(field, elementType, enumerable); @@ -203,7 +210,7 @@ private Table fieldRelation(final Field field) { /** Deduces the element type of a collection; * same logic as {@link #toEnumerable}. */ - private static Type getElementType(Class clazz) { + private static @Nullable Type getElementType(Class clazz) { if (clazz.isArray()) { return clazz.getComponentType(); } @@ -247,7 +254,7 @@ private static class ReflectiveTable return Statistics.UNKNOWN; } - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { if (elementType == Object[].class) { //noinspection unchecked return enumerable; @@ -319,6 +326,7 @@ public static class Factory implements SchemaFactory { //noinspection unchecked Method method = clazz.getMethod((String) methodName); target = method.invoke(null); + requireNonNull(target, () -> "method " + method + " returns null"); } catch (Exception e) { throw new RuntimeException("Error invoking method " + methodName, e); } @@ -354,7 +362,9 @@ private static class MethodTableMacro extends ReflectiveFunctionBase @Override public TranslatableTable apply(final List arguments) { try { - final Object o = method.invoke(schema.getTarget(), arguments.toArray()); + final Object o = requireNonNull( + method.invoke(schema.getTarget(), arguments.toArray()), + () -> "method " + method + " returned null for arguments " + arguments); return (TranslatableTable) o; } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); @@ -390,23 +400,26 @@ private static class FieldTable extends ReflectiveTable { @Override public Expression getExpression(SchemaPlus schema, String tableName, Class clazz) { + ReflectiveSchema reflectiveSchema = requireNonNull( + schema.unwrap(ReflectiveSchema.class), + () -> "schema.unwrap(ReflectiveSchema.class) for " + schema); return Expressions.field( - schema.unwrap(ReflectiveSchema.class).getTargetExpression( + reflectiveSchema.getTargetExpression( schema.getParentSchema(), schema.getName()), field); } } /** Function that returns an array of a given object's field values. */ - private static class FieldSelector implements Function1 { + private static class FieldSelector implements Function1 { private final Field[] fields; FieldSelector(Class elementType) { this.fields = elementType.getFields(); } - @Override public Object[] apply(Object o) { + @Override public @Nullable Object[] apply(Object o) { try { - final Object[] objects = new Object[fields.length]; + final @Nullable Object[] objects = new Object[fields.length]; for (int i = 0; i < fields.length; i++) { objects[i] = fields[i].get(o); } diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java index feecda495d6b..95a4f61edbf2 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java @@ -32,13 +32,16 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; -import java.util.Objects; import javax.sql.DataSource; +import static java.util.Objects.requireNonNull; + /** * Schema based upon a JDBC catalog (database). * @@ -58,20 +61,21 @@ public class JdbcCatalogSchema extends AbstractSchema { final String catalog; /** Sub-schemas by name, lazily initialized. */ + @SuppressWarnings("method.invocation.invalid") final Supplier subSchemaMapSupplier = Suppliers.memoize(() -> computeSubSchemaMap()); /** Creates a JdbcCatalogSchema. */ public JdbcCatalogSchema(DataSource dataSource, SqlDialect dialect, JdbcConvention convention, String catalog) { - this.dataSource = Objects.requireNonNull(dataSource); - this.dialect = Objects.requireNonNull(dialect); - this.convention = Objects.requireNonNull(convention); + this.dataSource = requireNonNull(dataSource); + this.dialect = requireNonNull(dialect); + this.convention = requireNonNull(convention); this.catalog = catalog; } public static JdbcCatalogSchema create( - SchemaPlus parentSchema, + @Nullable SchemaPlus parentSchema, String name, DataSource dataSource, String catalog) { @@ -80,7 +84,7 @@ public static JdbcCatalogSchema create( } public static JdbcCatalogSchema create( - SchemaPlus parentSchema, + @Nullable SchemaPlus parentSchema, String name, DataSource dataSource, SqlDialectFactory dialectFactory, @@ -107,7 +111,9 @@ private SubSchemaMap computeSubSchemaMap() { connection.getMetaData().getSchemas(catalog, null)) { defaultSchemaName = connection.getSchema(); while (resultSet.next()) { - final String schemaName = resultSet.getString(1); + final String schemaName = requireNonNull( + resultSet.getString(1), + () -> "got null schemaName from the database"); builder.put(schemaName, new JdbcSchema(dataSource, dialect, convention, catalog, schemaName)); } diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcQueryProvider.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcQueryProvider.java index f58e25015e3a..c816f7d4573f 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcQueryProvider.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcQueryProvider.java @@ -21,6 +21,8 @@ import org.apache.calcite.linq4j.QueryProviderImpl; import org.apache.calcite.linq4j.Queryable; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Implementation of {@link QueryProvider} that talks to JDBC databases. */ @@ -31,6 +33,6 @@ private JdbcQueryProvider() { } @Override public Enumerator executeQuery(Queryable queryable) { - return null; + return castNonNull(null); } } diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java index 0f22db345622..a12d01c8dd7c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java @@ -76,6 +76,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.ArrayList; @@ -83,6 +84,8 @@ import java.util.Set; import java.util.function.Consumer; +import static java.util.Objects.requireNonNull; + /** * Rules and relational operators for * {@link JdbcConvention} @@ -115,7 +118,8 @@ private JdbcRules() { static final RelFactories.JoinFactory JOIN_FACTORY = (left, right, hints, condition, variablesSet, joinType, semiJoinDone) -> { final RelOptCluster cluster = left.getCluster(); - final RelTraitSet traitSet = cluster.traitSetOf(left.getConvention()); + final RelTraitSet traitSet = cluster.traitSetOf( + requireNonNull(left.getConvention(), "left.getConvention()")); try { return new JdbcJoin(cluster, traitSet, left, right, condition, variablesSet, joinType); @@ -147,7 +151,8 @@ private JdbcRules() { public static final RelFactories.AggregateFactory AGGREGATE_FACTORY = (input, hints, groupSet, groupSets, aggCalls) -> { final RelOptCluster cluster = input.getCluster(); - final RelTraitSet traitSet = cluster.traitSetOf(input.getConvention()); + final RelTraitSet traitSet = cluster.traitSetOf( + requireNonNull(input.getConvention(), "input.getConvention()")); try { return new JdbcAggregate(cluster, traitSet, input, groupSet, groupSets, aggCalls); @@ -167,7 +172,8 @@ private JdbcRules() { (kind, inputs, all) -> { RelNode input = inputs.get(0); RelOptCluster cluster = input.getCluster(); - final RelTraitSet traitSet = cluster.traitSetOf(input.getConvention()); + final RelTraitSet traitSet = cluster.traitSetOf( + requireNonNull(input.getConvention(), "input.getConvention()")); switch (kind) { case UNION: return new JdbcUnion(cluster, traitSet, inputs, all); @@ -267,7 +273,7 @@ protected JdbcJoinRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Join join = (Join) rel; switch (join.getJoinType()) { case SEMI: @@ -288,7 +294,7 @@ protected JdbcJoinRule(Config config) { * JDBC convention * @return A new JdbcJoin */ - public RelNode convert(Join join, boolean convertInputTraits) { + public @Nullable RelNode convert(Join join, boolean convertInputTraits) { final List newInputs = new ArrayList<>(); for (RelNode input : join.getInputs()) { if (convertInputTraits && input.getConvention() != getOutTrait()) { @@ -432,7 +438,7 @@ protected JdbcCalcRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Calc calc = (Calc) rel; // If there's a multiset, let FarragoMultisetSplitter work on it @@ -529,7 +535,7 @@ private static boolean userDefinedFunctionInProject(Project project) { return false; } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Project project = (Project) rel; return new JdbcProject( @@ -606,7 +612,7 @@ private static boolean userDefinedFunctionInFilter(Filter filter) { return visitor.containsUserDefinedFunction(); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Filter filter = (Filter) rel; return new JdbcFilter( @@ -659,7 +665,7 @@ protected JdbcAggregateRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Aggregate agg = (Aggregate) rel; if (agg.getGroupSets().size() != 1) { // GROUPING SETS not supported; see @@ -692,7 +698,7 @@ public JdbcAggregate( RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) throws InvalidRelException { super(cluster, traitSet, ImmutableList.of(), input, groupSet, groupSets, aggCalls); @@ -722,7 +728,7 @@ public JdbcAggregate(RelOptCluster cluster, RelTraitSet traitSet, @Override public JdbcAggregate copy(RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, List aggCalls) { + @Nullable List groupSets, List aggCalls) { try { return new JdbcAggregate(getCluster(), traitSet, input, groupSet, groupSets, aggCalls); @@ -756,7 +762,7 @@ protected JdbcSortRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { return convert((Sort) rel, true); } @@ -793,15 +799,15 @@ public JdbcSort( RelTraitSet traitSet, RelNode input, RelCollation collation, - RexNode offset, - RexNode fetch) { + @Nullable RexNode offset, + @Nullable RexNode fetch) { super(cluster, traitSet, input, collation, offset, fetch); assert getConvention() instanceof JdbcConvention; assert getConvention() == input.getConvention(); } @Override public JdbcSort copy(RelTraitSet traitSet, RelNode newInput, - RelCollation newCollation, RexNode offset, RexNode fetch) { + RelCollation newCollation, @Nullable RexNode offset, @Nullable RexNode fetch) { return new JdbcSort(getCluster(), traitSet, newInput, newCollation, offset, fetch); } @@ -834,7 +840,7 @@ protected JdbcUnionRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Union union = (Union) rel; final RelTraitSet traitSet = union.getTraitSet().replace(out); @@ -887,7 +893,7 @@ protected JdbcIntersectRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Intersect intersect = (Intersect) rel; if (intersect.all) { return null; // INTERSECT ALL not implemented @@ -940,7 +946,7 @@ protected JdbcMinusRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final Minus minus = (Minus) rel; if (minus.all) { return null; // EXCEPT ALL not implemented @@ -986,7 +992,7 @@ protected JdbcTableModificationRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final TableModify modify = (TableModify) rel; final ModifiableTable modifiableTable = @@ -1010,16 +1016,14 @@ protected JdbcTableModificationRule(Config config) { /** Table-modification operator implemented in JDBC convention. */ public static class JdbcTableModify extends TableModify implements JdbcRel { - private final Expression expression; - public JdbcTableModify(RelOptCluster cluster, RelTraitSet traitSet, RelOptTable table, Prepare.CatalogReader catalogReader, RelNode input, Operation operation, - List updateColumnList, - List sourceExpressionList, + @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { super(cluster, traitSet, table, catalogReader, input, operation, updateColumnList, sourceExpressionList, flattened); @@ -1030,7 +1034,7 @@ public JdbcTableModify(RelOptCluster cluster, if (modifiableTable == null) { throw new AssertionError(); // TODO: user error in validator } - this.expression = table.getExpression(Queryable.class); + Expression expression = table.getExpression(Queryable.class); if (expression == null) { throw new AssertionError(); // TODO: user error in validator } @@ -1068,7 +1072,7 @@ protected JdbcValuesRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { Values values = (Values) rel; return new JdbcValues(values.getCluster(), values.getRowType(), values.getTuples(), values.getTraitSet().replace(out)); diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java index c28a1f0d2edf..fe4565d3907b 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java @@ -48,6 +48,8 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -58,11 +60,12 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.function.BiFunction; import javax.sql.DataSource; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link Schema} that is backed by a JDBC data source. * @@ -72,15 +75,15 @@ */ public class JdbcSchema implements Schema { final DataSource dataSource; - final String catalog; - final String schema; + final @Nullable String catalog; + final @Nullable String schema; public final SqlDialect dialect; final JdbcConvention convention; - private ImmutableMap tableMap; + private @Nullable ImmutableMap tableMap; private final boolean snapshot; @Experimental - public static final ThreadLocal THREAD_METADATA = new ThreadLocal<>(); + public static final ThreadLocal<@Nullable Foo> THREAD_METADATA = new ThreadLocal<>(); private static final Ordering> VERSION_ORDERING = Ordering.natural().lexicographical(); @@ -95,15 +98,15 @@ public class JdbcSchema implements Schema { * @param schema Schema name pattern */ public JdbcSchema(DataSource dataSource, SqlDialect dialect, - JdbcConvention convention, String catalog, String schema) { + JdbcConvention convention, @Nullable String catalog, @Nullable String schema) { this(dataSource, dialect, convention, catalog, schema, null); } private JdbcSchema(DataSource dataSource, SqlDialect dialect, - JdbcConvention convention, String catalog, String schema, - ImmutableMap tableMap) { - this.dataSource = Objects.requireNonNull(dataSource); - this.dialect = Objects.requireNonNull(dialect); + JdbcConvention convention, @Nullable String catalog, @Nullable String schema, + @Nullable ImmutableMap tableMap) { + this.dataSource = requireNonNull(dataSource, "dataSource"); + this.dialect = requireNonNull(dialect, "dialect"); this.convention = convention; this.catalog = catalog; this.schema = schema; @@ -115,8 +118,8 @@ public static JdbcSchema create( SchemaPlus parentSchema, String name, DataSource dataSource, - String catalog, - String schema) { + @Nullable String catalog, + @Nullable String schema) { return create(parentSchema, name, dataSource, SqlDialectFactoryImpl.INSTANCE, catalog, schema); } @@ -126,8 +129,8 @@ public static JdbcSchema create( String name, DataSource dataSource, SqlDialectFactory dialectFactory, - String catalog, - String schema) { + @Nullable String catalog, + @Nullable String schema) { final Expression expression = Schemas.subSchemaExpression(parentSchema, name, JdbcSchema.class); final SqlDialect dialect = createDialect(dialectFactory, dataSource); @@ -198,8 +201,8 @@ public static SqlDialect createDialect(SqlDialectFactory dialectFactory, } /** Creates a JDBC data source with the given specification. */ - public static DataSource dataSource(String url, String driverClassName, - String username, String password) { + public static DataSource dataSource(String url, @Nullable String driverClassName, + @Nullable String username, @Nullable String password) { if (url.startsWith("jdbc:hsqldb:")) { // Prevent hsqldb from screwing up java.util.logging. System.setProperty("hsqldb.reconfig_logging", "false"); @@ -222,7 +225,7 @@ public DataSource getDataSource() { return dataSource; } - @Override public Expression getExpression(SchemaPlus parentSchema, String name) { + @Override public Expression getExpression(@Nullable SchemaPlus parentSchema, String name) { return Schemas.subSchemaExpression(parentSchema, name, JdbcSchema.class); } @@ -244,12 +247,13 @@ private ImmutableMap computeTables() { ResultSet resultSet = null; try { connection = dataSource.getConnection(); - final Pair catalogSchema = getCatalogSchema(connection); + final Pair<@Nullable String, @Nullable String> catalogSchema = getCatalogSchema(connection); final String catalog = catalogSchema.left; final String schema = catalogSchema.right; final Iterable tableDefs; - if (THREAD_METADATA.get() != null) { - tableDefs = THREAD_METADATA.get().apply(catalog, schema); + Foo threadMetadata = THREAD_METADATA.get(); + if (threadMetadata != null) { + tableDefs = threadMetadata.apply(catalog, schema); } else { final List tableDefList = new ArrayList<>(); final DatabaseMetaData metaData = connection.getMetaData(); @@ -308,7 +312,7 @@ private List version(DatabaseMetaData metaData) throws SQLException { } /** Returns a pair of (catalog, schema) for the current connection. */ - private Pair getCatalogSchema(Connection connection) + private Pair<@Nullable String, @Nullable String> getCatalogSchema(Connection connection) throws SQLException { final DatabaseMetaData metaData = connection.getMetaData(); final List version41 = ImmutableList.of(4, 1); // JDBC 4.1 @@ -341,7 +345,7 @@ private Pair getCatalogSchema(Connection connection) return Pair.of(catalog, schema); } - @Override public Table getTable(String name) { + @Override public @Nullable Table getTable(String name) { return getTableMap(false).get(name); } @@ -377,7 +381,7 @@ RelProtoDataType getRelDataType(DatabaseMetaData metaData, String catalogName, new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); final RelDataTypeFactory.Builder fieldInfo = typeFactory.builder(); while (resultSet.next()) { - final String columnName = resultSet.getString(4); + final String columnName = requireNonNull(resultSet.getString(4), "columnName"); final int dataType = resultSet.getInt(5); final String typeString = resultSet.getString(6); final int precision; @@ -403,7 +407,7 @@ RelProtoDataType getRelDataType(DatabaseMetaData metaData, String catalogName, } private RelDataType sqlType(RelDataTypeFactory typeFactory, int dataType, - int precision, int scale, String typeString) { + int precision, int scale, @Nullable String typeString) { // Fall back to ANY if type is unknown final SqlTypeName sqlTypeName = Util.first(SqlTypeName.getNameForJdbcType(dataType), SqlTypeName.ANY); @@ -483,15 +487,16 @@ protected Map getTypes() { return ImmutableMap.of(); } - @Override public RelProtoDataType getType(String name) { + @Override public @Nullable RelProtoDataType getType(String name) { return getTypes().get(name); } @Override public Set getTypeNames() { - return getTypes().keySet(); + //noinspection RedundantCast + return (Set) getTypes().keySet(); } - @Override public Schema getSubSchema(String name) { + @Override public @Nullable Schema getSubSchema(String name) { // JDBC does not support sub-schemas. return null; } @@ -501,7 +506,9 @@ protected Map getTypes() { } private static void close( - Connection connection, Statement statement, ResultSet resultSet) { + @Nullable Connection connection, + @Nullable Statement statement, + @Nullable ResultSet resultSet) { if (resultSet != null) { try { resultSet.close(); @@ -566,6 +573,6 @@ private Factory() {} /** Do not use. */ @Experimental public interface Foo - extends BiFunction> { + extends BiFunction<@Nullable String, @Nullable String, Iterable> { } } diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java index 71d58c52962f..6b2ab112be4d 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java @@ -54,11 +54,14 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Objects; + +import static java.util.Objects.requireNonNull; /** * Queryable that gets its data from a table within a JDBC connection. @@ -72,7 +75,7 @@ */ public class JdbcTable extends AbstractQueryableTable implements TranslatableTable, ScannableTable, ModifiableTable { - private RelProtoDataType protoRowType; + private @Nullable RelProtoDataType protoRowType; public final JdbcSchema jdbcSchema; public final String jdbcCatalogName; public final String jdbcSchemaName; @@ -83,11 +86,11 @@ public class JdbcTable extends AbstractQueryableTable String jdbcSchemaName, String jdbcTableName, Schema.TableType jdbcTableType) { super(Object[].class); - this.jdbcSchema = Objects.requireNonNull(jdbcSchema); + this.jdbcSchema = requireNonNull(jdbcSchema); this.jdbcCatalogName = jdbcCatalogName; this.jdbcSchemaName = jdbcSchemaName; - this.jdbcTableName = Objects.requireNonNull(jdbcTableName); - this.jdbcTableType = Objects.requireNonNull(jdbcTableType); + this.jdbcTableName = requireNonNull(jdbcTableName); + this.jdbcTableType = requireNonNull(jdbcTableType); } @Override public String toString() { @@ -98,7 +101,7 @@ public class JdbcTable extends AbstractQueryableTable return jdbcTableType; } - @Override public C unwrap(Class aClass) { + @Override public @Nullable C unwrap(Class aClass) { if (aClass.isInstance(jdbcSchema.getDataSource())) { return aClass.cast(jdbcSchema.getDataSource()); } else if (aClass.isInstance(jdbcSchema.dialect)) { @@ -127,7 +130,7 @@ public class JdbcTable extends AbstractQueryableTable private List> fieldClasses( final JavaTypeFactory typeFactory) { - final RelDataType rowType = protoRowType.apply(typeFactory); + final RelDataType rowType = requireNonNull(protoRowType, "protoRowType").apply(typeFactory); return Util.transform(rowType.getFieldList(), f -> { final RelDataType type = f.getType(); final Class clazz = (Class) typeFactory.getJavaClass(type); @@ -176,21 +179,21 @@ public SqlIdentifier tableName() { return new JdbcTableQueryable<>(queryProvider, schema, tableName); } - @Override public Enumerable scan(DataContext root) { - final JavaTypeFactory typeFactory = root.getTypeFactory(); + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); final SqlString sql = generateSql(); return ResultSetEnumerable.of(jdbcSchema.getDataSource(), sql.getSql(), JdbcUtils.ObjectArrayRowBuilder.factory(fieldClasses(typeFactory))); } - @Override public Collection getModifiableCollection() { + @Override public @Nullable Collection getModifiableCollection() { return null; } @Override public TableModify toModificationRel(RelOptCluster cluster, RelOptTable table, CatalogReader catalogReader, RelNode input, - Operation operation, List updateColumnList, - List sourceExpressionList, boolean flattened) { + Operation operation, @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { jdbcSchema.convention.register(cluster.getPlanner()); return new LogicalTableModify(cluster, cluster.traitSetOf(Convention.NONE), diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java index 25d8a145bca1..ee201f32de48 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Relational expression representing a scan of a table in a JDBC data source. */ @@ -45,7 +47,7 @@ protected JdbcTableScan( @Override public RelNode copy(RelTraitSet traitSet, List inputs) { assert inputs.isEmpty(); return new JdbcTableScan( - getCluster(), table, jdbcTable, (JdbcConvention) getConvention()); + getCluster(), table, jdbcTable, (JdbcConvention) castNonNull(getConvention())); } @Override public JdbcImplementor.Result implement(JdbcImplementor implementor) { diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java index d2e138dd5762..7d4c8d688681 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java @@ -48,6 +48,8 @@ import org.apache.calcite.sql.util.SqlString; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.sql.ResultSet; @@ -59,6 +61,8 @@ import java.util.stream.Collectors; import javax.sql.DataSource; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Relational expression representing a scan of a table in a JDBC data source. */ @@ -212,7 +216,7 @@ private UnaryExpression getTimeZoneExpression( private void generateGet(EnumerableRelImplementor implementor, PhysType physType, BlockBuilder builder, ParameterExpression resultSet_, - int i, Expression target, Expression calendar_, + int i, Expression target, @Nullable Expression calendar_, SqlDialect.CalendarPolicy calendarPolicy) { final Primitive primitive = Primitive.ofBoxOr(physType.fieldClass(i)); final RelDataType fieldType = @@ -223,6 +227,7 @@ private void generateGet(EnumerableRelImplementor implementor, boolean offset = false; switch (calendarPolicy) { case LOCAL: + assert calendar_ != null : "calendar must not be null"; dateTimeArgs.add(calendar_); break; case NULL: @@ -324,10 +329,10 @@ private Method getMethod2(SqlTypeName sqlTypeName) { } /** E,g, {@code jdbcGetMethod(int)} returns "getInt". */ - private String jdbcGetMethod(Primitive primitive) { + private String jdbcGetMethod(@Nullable Primitive primitive) { return primitive == null ? "getObject" - : "get" + SqlFunctions.initcap(primitive.primitiveName); + : "get" + SqlFunctions.initcap(castNonNull(primitive.primitiveName)); } private SqlString generateSql(SqlDialect dialect) { diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java index 3446ba47db5a..d676f9e1550a 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.convert.ConverterRule; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Rule to convert a relational expression from * {@link JdbcConvention} to @@ -41,7 +43,7 @@ protected JdbcToEnumerableConverterRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { RelTraitSet newTraitSet = rel.getTraitSet().replace(getOutTrait()); return new JdbcToEnumerableConverter(rel.getCluster(), newTraitSet, rel); } diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java index 41018ec08e74..6bc960c41f76 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java @@ -32,6 +32,9 @@ import com.google.common.cache.LoadingCache; import com.google.common.primitives.Ints; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Date; @@ -42,7 +45,6 @@ import java.sql.Types; import java.util.List; import java.util.TimeZone; -import javax.annotation.Nonnull; import javax.sql.DataSource; /** @@ -61,8 +63,8 @@ static class DialectPool { CacheBuilder.newBuilder().softValues() .build(CacheLoader.from(DialectPool::dialect)); - private static @Nonnull SqlDialect dialect( - @Nonnull Pair key) { + private static SqlDialect dialect( + Pair key) { SqlDialectFactory dialectFactory = key.left; DataSource dataSource = key.right; Connection connection = null; @@ -96,7 +98,7 @@ public SqlDialect get(SqlDialectFactory dialectFactory, DataSource dataSource) { /** Builder that calls {@link ResultSet#getObject(int)} for every column, * or {@code getXxx} if the result type is a primitive {@code xxx}, * and returns an array of objects for each row. */ - static class ObjectArrayRowBuilder implements Function0 { + static class ObjectArrayRowBuilder implements Function0<@Nullable Object[]> { private final ResultSet resultSet; private final int columnCount; private final ColumnMetaData.Rep[] reps; @@ -111,13 +113,13 @@ static class ObjectArrayRowBuilder implements Function0 { this.columnCount = resultSet.getMetaData().getColumnCount(); } - public static Function1> factory( + public static Function1> factory( final List> list) { return resultSet -> { try { return new ObjectArrayRowBuilder( resultSet, - Pair.left(list).toArray(new ColumnMetaData.Rep[list.size()]), + Pair.left(list).toArray(new ColumnMetaData.Rep[0]), Ints.toArray(Pair.right(list))); } catch (SQLException e) { throw new RuntimeException(e); @@ -125,9 +127,9 @@ public static Function1> factory( }; } - @Override public Object[] apply() { + @Override public @Nullable Object[] apply() { try { - final Object[] values = new Object[columnCount]; + final @Nullable Object[] values = new Object[columnCount]; for (int i = 0; i < columnCount; i++) { values[i] = value(i); } @@ -142,7 +144,7 @@ public static Function1> factory( * * @param i Ordinal of column (1-based, per JDBC) */ - private Object value(int i) throws SQLException { + private @Nullable Object value(int i) throws SQLException { // MySQL returns timestamps shifted into local time. Using // getTimestamp(int, Calendar) with a UTC calendar should prevent this, // but does not. So we shift explicitly. @@ -159,7 +161,7 @@ private Object value(int i) throws SQLException { return reps[i].jdbcGet(resultSet, i + 1); } - private static Timestamp shift(Timestamp v) { + private static @PolyNull Timestamp shift(@PolyNull Timestamp v) { if (v == null) { return null; } @@ -168,7 +170,7 @@ private static Timestamp shift(Timestamp v) { return new Timestamp(time + offset); } - private static Time shift(Time v) { + private static @PolyNull Time shift(@PolyNull Time v) { if (v == null) { return null; } @@ -177,7 +179,7 @@ private static Time shift(Time v) { return new Time((time + offset) % DateTimeUtils.MILLIS_PER_DAY); } - private static Date shift(Date v) { + private static @PolyNull Date shift(@PolyNull Date v) { if (v == null) { return null; } @@ -197,12 +199,12 @@ private static Date shift(Date v) { static class DataSourcePool { public static final DataSourcePool INSTANCE = new DataSourcePool(); - private final LoadingCache, BasicDataSource> cache = + private final LoadingCache, BasicDataSource> cache = CacheBuilder.newBuilder().softValues() .build(CacheLoader.from(DataSourcePool::dataSource)); - private static @Nonnull BasicDataSource dataSource( - @Nonnull List key) { + private static BasicDataSource dataSource( + List key) { BasicDataSource dataSource = new BasicDataSource(); dataSource.setUrl(key.get(0)); dataSource.setUsername(key.get(1)); @@ -211,11 +213,11 @@ static class DataSourcePool { return dataSource; } - public DataSource get(String url, String driverClassName, - String username, String password) { + public DataSource get(String url, @Nullable String driverClassName, + @Nullable String username, @Nullable String password) { // Get data source objects from a cache, so that we don't have to sniff // out what kind of database they are quite as often. - final List key = + final List<@Nullable String> key = ImmutableNullableList.of(url, username, password, driverClassName); return cache.getUnchecked(key); } diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java index 73f8c90628db..cea5c6a17eb1 100644 --- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java +++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java @@ -22,6 +22,9 @@ import org.apache.calcite.model.JsonSchema; import org.apache.calcite.sql.validate.SqlConformance; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.Properties; /** Interface for reading connection properties within Calcite code. There is @@ -56,9 +59,9 @@ public interface CalciteConnectionConfig extends ConnectionConfig { * {@link CalciteConnectionProperty#DEFAULT_NULL_COLLATION}. */ NullCollation defaultNullCollation(); /** Returns the value of {@link CalciteConnectionProperty#FUN}. */ - T fun(Class operatorTableClass, T defaultOperatorTable); + @PolyNull T fun(Class operatorTableClass, @PolyNull T defaultOperatorTable); /** Returns the value of {@link CalciteConnectionProperty#MODEL}. */ - String model(); + @Nullable String model(); /** Returns the value of {@link CalciteConnectionProperty#LEX}. */ Lex lex(); /** Returns the value of {@link CalciteConnectionProperty#QUOTING}. */ @@ -70,9 +73,9 @@ public interface CalciteConnectionConfig extends ConnectionConfig { /** Returns the value of {@link CalciteConnectionProperty#CASE_SENSITIVE}. */ boolean caseSensitive(); /** Returns the value of {@link CalciteConnectionProperty#PARSER_FACTORY}. */ - T parserFactory(Class parserFactoryClass, T defaultParserFactory); + @PolyNull T parserFactory(Class parserFactoryClass, @PolyNull T defaultParserFactory); /** Returns the value of {@link CalciteConnectionProperty#SCHEMA_FACTORY}. */ - T schemaFactory(Class schemaFactoryClass, T defaultSchemaFactory); + @PolyNull T schemaFactory(Class schemaFactoryClass, @PolyNull T defaultSchemaFactory); /** Returns the value of {@link CalciteConnectionProperty#SCHEMA_TYPE}. */ JsonSchema.Type schemaType(); /** Returns the value of {@link CalciteConnectionProperty#SPARK}. */ @@ -81,7 +84,7 @@ public interface CalciteConnectionConfig extends ConnectionConfig { * {@link CalciteConnectionProperty#FORCE_DECORRELATE}. */ boolean forceDecorrelate(); /** Returns the value of {@link CalciteConnectionProperty#TYPE_SYSTEM}. */ - T typeSystem(Class typeSystemClass, T defaultTypeSystem); + @PolyNull T typeSystem(Class typeSystemClass, @PolyNull T defaultTypeSystem); /** Returns the value of {@link CalciteConnectionProperty#CONFORMANCE}. */ SqlConformance conformance(); /** Returns the value of {@link CalciteConnectionProperty#TIME_ZONE}. */ diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java index d7dc2f84d76a..204568b9ca30 100644 --- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java +++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java @@ -27,6 +27,9 @@ import org.apache.calcite.sql.validate.SqlConformance; import org.apache.calcite.sql.validate.SqlConformanceEnum; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.List; import java.util.Properties; @@ -103,7 +106,8 @@ public boolean isSet(CalciteConnectionProperty property) { .getEnum(NullCollation.class, NullCollation.HIGH); } - @Override public T fun(Class operatorTableClass, T defaultOperatorTable) { + @Override public @PolyNull T fun(Class operatorTableClass, + @PolyNull T defaultOperatorTable) { final String fun = CalciteConnectionProperty.FUN.wrap(properties).getString(); if (fun == null || fun.equals("") || fun.equals("standard")) { @@ -116,7 +120,7 @@ public boolean isSet(CalciteConnectionProperty property) { return operatorTableClass.cast(operatorTable); } - @Override public String model() { + @Override public @Nullable String model() { return CalciteConnectionProperty.MODEL.wrap(properties).getString(); } @@ -144,14 +148,14 @@ public boolean isSet(CalciteConnectionProperty property) { .getBoolean(lex().caseSensitive); } - @Override public T parserFactory(Class parserFactoryClass, - T defaultParserFactory) { + @Override public @PolyNull T parserFactory(Class parserFactoryClass, + @PolyNull T defaultParserFactory) { return CalciteConnectionProperty.PARSER_FACTORY.wrap(properties) .getPlugin(parserFactoryClass, defaultParserFactory); } - @Override public T schemaFactory(Class schemaFactoryClass, - T defaultSchemaFactory) { + @Override public @PolyNull T schemaFactory(Class schemaFactoryClass, + @PolyNull T defaultSchemaFactory) { return CalciteConnectionProperty.SCHEMA_FACTORY.wrap(properties) .getPlugin(schemaFactoryClass, defaultSchemaFactory); } @@ -170,7 +174,8 @@ public boolean isSet(CalciteConnectionProperty property) { .getBoolean(); } - @Override public T typeSystem(Class typeSystemClass, T defaultTypeSystem) { + @Override public @PolyNull T typeSystem(Class typeSystemClass, + @PolyNull T defaultTypeSystem) { return CalciteConnectionProperty.TYPE_SYSTEM.wrap(properties) .getPlugin(typeSystemClass, defaultTypeSystem); } diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java index 316edc991f53..6d0b8c40f67e 100644 --- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java +++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java @@ -22,6 +22,8 @@ import org.apache.calcite.model.JsonSchema; import org.apache.calcite.sql.validate.SqlConformanceEnum; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -166,9 +168,9 @@ public enum CalciteConnectionProperty implements ConnectionProperty { private final String camelName; private final Type type; @SuppressWarnings("ImmutableEnumChecker") - private final Object defaultValue; + private final @Nullable Object defaultValue; private final boolean required; - private final Class valueClass; + private final @Nullable Class valueClass; private static final Map NAME_TO_PROPS; @@ -184,13 +186,13 @@ public enum CalciteConnectionProperty implements ConnectionProperty { } } - CalciteConnectionProperty(String camelName, Type type, Object defaultValue, + CalciteConnectionProperty(String camelName, Type type, @Nullable Object defaultValue, boolean required) { this(camelName, type, defaultValue, required, null); } - CalciteConnectionProperty(String camelName, Type type, Object defaultValue, - boolean required, Class valueClass) { + CalciteConnectionProperty(String camelName, Type type, @Nullable Object defaultValue, + boolean required, @Nullable Class valueClass) { this.camelName = camelName; this.type = type; this.defaultValue = defaultValue; @@ -205,7 +207,7 @@ public enum CalciteConnectionProperty implements ConnectionProperty { return camelName; } - @Override public Object defaultValue() { + @Override public @Nullable Object defaultValue() { return defaultValue; } @@ -213,7 +215,7 @@ public enum CalciteConnectionProperty implements ConnectionProperty { return type; } - @Override public Class valueClass() { + @Override public @Nullable Class valueClass() { return valueClass; } diff --git a/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java b/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java index ac27b01c2fd6..4fd3a75645ee 100644 --- a/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java +++ b/core/src/main/java/org/apache/calcite/config/CalciteSystemProperty.java @@ -19,6 +19,8 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -248,7 +250,7 @@ public final class CalciteSystemProperty { // The name of the property is not appropriate. A better alternative would be // calcite.test.foodmart.queries.ids. Moreover, I am not in favor of using system properties for // parameterized tests. - public static final CalciteSystemProperty TEST_FOODMART_QUERY_IDS = + public static final CalciteSystemProperty<@Nullable String> TEST_FOODMART_QUERY_IDS = new CalciteSystemProperty<>("calcite.ids", Function.identity()); /** @@ -441,7 +443,8 @@ private static Properties loadProperties() { private final T value; - private CalciteSystemProperty(String key, Function valueParser) { + private CalciteSystemProperty(String key, + Function valueParser) { this.value = valueParser.apply(PROPERTIES.getProperty(key)); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java b/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java index 1f1e042374ed..9c2917b9b730 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java @@ -44,6 +44,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; @@ -55,6 +57,10 @@ import java.util.function.BiFunction; import java.util.function.Supplier; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Interpreter node that implements an * {@link org.apache.calcite.rel.core.Aggregate}. @@ -85,7 +91,9 @@ public AggregateNode(Compiler compiler, Aggregate rel) { ImmutableList.Builder builder = ImmutableList.builder(); for (AggregateCall aggregateCall : rel.getAggCallList()) { - builder.add(getAccumulator(aggregateCall, false)); + @SuppressWarnings("method.invocation.invalid") + AccumulatorFactory accumulator = getAccumulator(aggregateCall, false); + builder.add(accumulator); } accumulatorFactories = builder.build(); } @@ -136,10 +144,10 @@ private AccumulatorFactory getAccumulator(final AggregateCall call, } if (call.getAggregation() == SqlStdOperatorTable.SUM) { return new UdaAccumulatorFactory( - AggregateFunctionImpl.create(clazz), call, true); + getAggFunction(clazz), call, true); } else { return new UdaAccumulatorFactory( - AggregateFunctionImpl.create(clazz), call, false); + getAggFunction(clazz), call, false); } } else if (call.getAggregation() == SqlStdOperatorTable.MIN) { final Class clazz; @@ -165,7 +173,7 @@ private AccumulatorFactory getAccumulator(final AggregateCall call, break; } return new UdaAccumulatorFactory( - AggregateFunctionImpl.create(clazz), call, true); + getAggFunction(clazz), call, true); } else if (call.getAggregation() == SqlStdOperatorTable.MAX) { final Class clazz; switch (call.getType().getSqlTypeName()) { @@ -187,13 +195,13 @@ private AccumulatorFactory getAccumulator(final AggregateCall call, break; } return new UdaAccumulatorFactory( - AggregateFunctionImpl.create(clazz), call, true); + getAggFunction(clazz), call, true); } else { final JavaTypeFactory typeFactory = (JavaTypeFactory) rel.getCluster().getTypeFactory(); int stateOffset = 0; final AggImpState agg = new AggImpState(0, call, false); - int stateSize = agg.state.size(); + int stateSize = requireNonNull(agg.state, "agg.state").size(); final BlockBuilder builder2 = new BlockBuilder(); final PhysType inputPhysType = @@ -227,7 +235,7 @@ private AccumulatorFactory getAccumulator(final AggregateCall call, return args; } - @Override public RexNode rexFilterArgument() { + @Override public @Nullable RexNode rexFilterArgument() { return agg.call.filterArg < 0 ? null : RexInputRef.of(agg.call.filterArg, @@ -246,7 +254,7 @@ private AccumulatorFactory getAccumulator(final AggregateCall call, } }; - agg.implementor.implementAdd(agg.context, addContext); + agg.implementor.implementAdd(requireNonNull(agg.context, "agg.context"), addContext); final ParameterExpression context_ = Expressions.parameter(Context.class, "context"); @@ -254,11 +262,17 @@ private AccumulatorFactory getAccumulator(final AggregateCall call, Expressions.parameter(Object[].class, "outputValues"); Scalar addScalar = JaninoRexCompiler.baz(context_, outputValues_, builder2.toBlock()); - return new ScalarAccumulatorDef(null, addScalar, null, + return new ScalarAccumulatorDef(castNonNull(null), addScalar, castNonNull(null), rel.getInput().getRowType().getFieldCount(), stateSize, dataContext); } } + private AggregateFunctionImpl getAggFunction(Class clazz) { + return requireNonNull( + AggregateFunctionImpl.create(clazz), + () -> "Unable to create AggregateFunctionImpl for " + clazz); + } + /** Accumulator for calls to the COUNT function. */ private static class CountAccumulator implements Accumulator { private final AggregateCall call; @@ -331,16 +345,20 @@ private ScalarAccumulator(ScalarAccumulatorDef def, Object[] values) { } @Override public void send(Row row) { - System.arraycopy(row.getValues(), 0, def.sendContext.values, 0, + @Nullable Object[] sendValues = requireNonNull(def.sendContext.values, + "def.sendContext.values"); + System.arraycopy(row.getValues(), 0, sendValues, 0, def.rowLength); - System.arraycopy(values, 0, def.sendContext.values, def.rowLength, - values.length); - def.addScalar.execute(def.sendContext, values); + System.arraycopy(this.values, 0, sendValues, def.rowLength, + this.values.length); + def.addScalar.execute(def.sendContext, this.values); } - @Override public Object end() { - System.arraycopy(values, 0, def.endContext.values, 0, values.length); - return def.endScalar.execute(def.endContext); + @Override public @Nullable Object end() { + Context endContext = requireNonNull(def.endContext, "def.endContext"); + @Nullable Object[] values = requireNonNull(endContext.values, "endContext.values"); + System.arraycopy(this.values, 0, values, 0, this.values.length); + return def.endScalar.execute(endContext); } } @@ -422,7 +440,7 @@ public void end(RowBuilder r) { */ private interface Accumulator { void send(Row row); - Object end(); + @Nullable Object end(); } /** Implementation of {@code SUM} over INTEGER values as a user-defined @@ -660,7 +678,7 @@ public static BigDecimal max(BigDecimal a, BigDecimal b) { private static class UdaAccumulatorFactory implements AccumulatorFactory { final AggregateFunctionImpl aggFunction; final int argOrdinal; - public final Object instance; + public final @Nullable Object instance; public final boolean nullIfEmpty; UdaAccumulatorFactory(AggregateFunctionImpl aggFunction, @@ -694,7 +712,7 @@ private static class UdaAccumulatorFactory implements AccumulatorFactory { /** Accumulator based upon a user-defined aggregate. */ private static class UdaAccumulator implements Accumulator { private final UdaAccumulatorFactory factory; - private Object value; + private @Nullable Object value; private boolean empty; UdaAccumulator(UdaAccumulatorFactory factory) { @@ -708,7 +726,7 @@ private static class UdaAccumulator implements Accumulator { } @Override public void send(Row row) { - final Object[] args = {value, row.getValues()[factory.argOrdinal]}; + final @Nullable Object[] args = {value, row.getValues()[factory.argOrdinal]}; for (int i = 1; i < args.length; i++) { if (args[i] == null) { return; // one of the arguments is null; don't add to the total @@ -722,13 +740,16 @@ private static class UdaAccumulator implements Accumulator { empty = false; } - @Override public Object end() { + @Override public @Nullable Object end() { if (factory.nullIfEmpty && empty) { return null; } - final Object[] args = {value}; + final @Nullable Object[] args = {value}; try { - return factory.aggFunction.resultMethod.invoke(factory.instance, args); + AggregateFunctionImpl aggFunction = requireNonNull(factory.aggFunction, + "factory.aggFunction"); + return requireNonNull(aggFunction.resultMethod, "aggFunction.resultMethod") + .invoke(factory.instance, args); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } @@ -752,7 +773,7 @@ private static class FilterAccumulator implements Accumulator { } } - @Override public Object end() { + @Override public @Nullable Object end() { return accumulator.end(); } } diff --git a/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java b/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java index 1c66e4de36a9..eea419d82f89 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java +++ b/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java @@ -24,6 +24,8 @@ import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Calling convention that returns results as an * {@link org.apache.calcite.linq4j.Enumerable} of object arrays. @@ -52,7 +54,7 @@ public enum BindableConvention implements Convention { return "BINDABLE"; } - @Override public RelNode enforce(RelNode input, RelTraitSet required) { + @Override public @Nullable RelNode enforce(RelNode input, RelTraitSet required) { return null; } diff --git a/core/src/main/java/org/apache/calcite/interpreter/Bindables.java b/core/src/main/java/org/apache/calcite/interpreter/Bindables.java index 25329f37df67..8c02dfa64735 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Bindables.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Bindables.java @@ -79,6 +79,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Map; import java.util.Objects; @@ -154,7 +156,7 @@ private Bindables() {} *

Any bindable can be compiled; if its input is also bindable, it becomes * part of the same compilation unit. */ - private static Enumerable help(DataContext dataContext, + private static Enumerable<@Nullable Object[]> help(DataContext dataContext, BindableRel rel) { return new Interpreter(dataContext, rel); } @@ -289,7 +291,7 @@ public static boolean canHandle(RelOptTable table) { || table.unwrap(ProjectableFilterableTable.class) != null; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -354,7 +356,7 @@ public static BindableFilter create(final RelNode input, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -413,7 +415,7 @@ public BindableProject(RelOptCluster cluster, RelTraitSet traitSet, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -456,13 +458,13 @@ protected BindableSortRule(Config config) { * bindable calling convention. */ public static class BindableSort extends Sort implements BindableRel { public BindableSort(RelOptCluster cluster, RelTraitSet traitSet, - RelNode input, RelCollation collation, RexNode offset, RexNode fetch) { + RelNode input, RelCollation collation, @Nullable RexNode offset, @Nullable RexNode fetch) { super(cluster, traitSet, input, collation, offset, fetch); assert getConvention() instanceof BindableConvention; } @Override public BindableSort copy(RelTraitSet traitSet, RelNode newInput, - RelCollation newCollation, RexNode offset, RexNode fetch) { + RelCollation newCollation, @Nullable RexNode offset, @Nullable RexNode fetch) { return new BindableSort(getCluster(), traitSet, newInput, newCollation, offset, fetch); } @@ -471,7 +473,7 @@ public BindableSort(RelOptCluster cluster, RelTraitSet traitSet, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -543,7 +545,7 @@ protected BindableJoin(RelOptCluster cluster, RelTraitSet traitSet, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -604,7 +606,7 @@ public BindableUnion(RelOptCluster cluster, RelTraitSet traitSet, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -630,7 +632,7 @@ public BindableIntersect(RelOptCluster cluster, RelTraitSet traitSet, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -655,7 +657,7 @@ public BindableMinus(RelOptCluster cluster, RelTraitSet traitSet, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -674,14 +676,14 @@ public static class BindableValues extends Values implements BindableRel { @Override public RelNode copy(RelTraitSet traitSet, List inputs) { assert inputs.isEmpty(); - return new BindableValues(getCluster(), rowType, tuples, traitSet); + return new BindableValues(getCluster(), getRowType(), tuples, traitSet); } @Override public Class getElementType() { return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -722,7 +724,7 @@ public BindableAggregate( RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) throws InvalidRelException { super(cluster, traitSet, ImmutableList.of(), input, groupSet, groupSets, aggCalls); @@ -753,7 +755,7 @@ public BindableAggregate(RelOptCluster cluster, RelTraitSet traitSet, @Override public BindableAggregate copy(RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, List aggCalls) { + @Nullable List groupSets, List aggCalls) { try { return new BindableAggregate(getCluster(), traitSet, input, groupSet, groupSets, aggCalls); @@ -768,7 +770,7 @@ public BindableAggregate(RelOptCluster cluster, RelTraitSet traitSet, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -792,7 +794,7 @@ protected BindableAggregateRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { final LogicalAggregate agg = (LogicalAggregate) rel; final RelTraitSet traitSet = agg.getTraitSet().replace(BindableConvention.INSTANCE); @@ -818,7 +820,7 @@ public static class BindableWindow extends Window implements BindableRel { @Override public RelNode copy(RelTraitSet traitSet, List inputs) { return new BindableWindow(getCluster(), traitSet, sole(inputs), - constants, rowType, groups); + constants, getRowType(), groups); } @Override public RelOptCost computeSelfCost(RelOptPlanner planner, @@ -831,7 +833,7 @@ public static class BindableWindow extends Window implements BindableRel { return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } @@ -879,14 +881,14 @@ public static class BindableMatch extends Match implements BindableRel { Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval) { + @Nullable RexNode interval) { super(cluster, traitSet, input, rowType, pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); } @Override public RelNode copy(RelTraitSet traitSet, List inputs) { - return new BindableMatch(getCluster(), traitSet, inputs.get(0), rowType, + return new BindableMatch(getCluster(), traitSet, inputs.get(0), getRowType(), pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); } @@ -895,7 +897,7 @@ public static class BindableMatch extends Match implements BindableRel { return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return help(dataContext, this); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/CollectNode.java b/core/src/main/java/org/apache/calcite/interpreter/CollectNode.java index 3954c9bb6aca..a72bae3fe58b 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/CollectNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/CollectNode.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -35,7 +37,7 @@ public CollectNode(Compiler compiler, Collect rel) { @Override public void run() throws InterruptedException { Row row; - List values = new ArrayList<>(); + List<@Nullable Object[]> values = new ArrayList<>(); while ((row = source.receive()) != null) { values.add(row.getValues()); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/Compiler.java b/core/src/main/java/org/apache/calcite/interpreter/Compiler.java index 5eae988c75de..41a5d5d73766 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Compiler.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Compiler.java @@ -22,6 +22,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -31,7 +33,7 @@ public interface Compiler { /** Compiles an expression to an executable form. */ - Scalar compile(List nodes, RelDataType inputRowType); + Scalar compile(List nodes, @Nullable RelDataType inputRowType); RelDataType combinedRowType(List inputs); diff --git a/core/src/main/java/org/apache/calcite/interpreter/Context.java b/core/src/main/java/org/apache/calcite/interpreter/Context.java index d44d9c215c04..09a7efdf66f2 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Context.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Context.java @@ -18,6 +18,9 @@ import org.apache.calcite.DataContext; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Context for executing a scalar expression in an interpreter. */ @@ -25,7 +28,7 @@ public class Context { public final DataContext root; /** Values of incoming columns from all inputs. */ - public Object[] values; + public @Nullable Object @MonotonicNonNull [] values; Context(DataContext root) { this.root = root; diff --git a/core/src/main/java/org/apache/calcite/interpreter/InterpretableConverter.java b/core/src/main/java/org/apache/calcite/interpreter/InterpretableConverter.java index a7c84ba427e5..26e431828a3f 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/InterpretableConverter.java +++ b/core/src/main/java/org/apache/calcite/interpreter/InterpretableConverter.java @@ -25,6 +25,8 @@ import org.apache.calcite.rel.convert.ConverterImpl; import org.apache.calcite.runtime.ArrayBindable; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -47,7 +49,7 @@ protected InterpretableConverter(RelOptCluster cluster, RelTraitSet traits, return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return new Interpreter(dataContext, getInput()); } } diff --git a/core/src/main/java/org/apache/calcite/interpreter/InterpretableRel.java b/core/src/main/java/org/apache/calcite/interpreter/InterpretableRel.java index 275c8a49265b..e0e8dd745f5a 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/InterpretableRel.java +++ b/core/src/main/java/org/apache/calcite/interpreter/InterpretableRel.java @@ -20,6 +20,8 @@ import org.apache.calcite.jdbc.CalcitePrepare; import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -38,12 +40,12 @@ class InterpreterImplementor { public final Compiler compiler; public final Map internalParameters = new LinkedHashMap<>(); - public final CalcitePrepare.SparkHandler spark; + public final CalcitePrepare.@Nullable SparkHandler spark; public final DataContext dataContext; public final Map> relSinks = new HashMap<>(); public InterpreterImplementor(Compiler compiler, - CalcitePrepare.SparkHandler spark, + CalcitePrepare.@Nullable SparkHandler spark, DataContext dataContext) { this.compiler = compiler; this.spark = spark; diff --git a/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java b/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java index 939b57083628..6658c89b43e5 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java @@ -17,6 +17,7 @@ package org.apache.calcite.interpreter; import org.apache.calcite.DataContext; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.config.CalciteSystemProperty; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; @@ -51,6 +52,10 @@ import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayDeque; import java.util.ArrayList; @@ -62,7 +67,10 @@ import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Objects; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; /** * Interpreter. @@ -71,7 +79,7 @@ * particular it holds working state while the data flow graph is being * assembled. */ -public class Interpreter extends AbstractEnumerable +public class Interpreter extends AbstractEnumerable<@Nullable Object[]> implements AutoCloseable { private final Map nodes; private final DataContext dataContext; @@ -79,16 +87,17 @@ public class Interpreter extends AbstractEnumerable /** Creates an Interpreter. */ public Interpreter(DataContext dataContext, RelNode rootRel) { - this.dataContext = Objects.requireNonNull(dataContext); + this.dataContext = requireNonNull(dataContext); final RelNode rel = optimize(rootRel); final CompilerImpl compiler = new Nodes.CoreCompiler(this, rootRel.getCluster()); + @SuppressWarnings("method.invocation.invalid") Pair> pair = compiler.visitRoot(rel); this.rootRel = pair.left; this.nodes = ImmutableMap.copyOf(pair.right); } - private RelNode optimize(RelNode rootRel) { + private static RelNode optimize(RelNode rootRel) { final HepProgram hepProgram = new HepProgramBuilder() .addRuleInstance(CoreRules.CALC_SPLIT) .addRuleInstance(CoreRules.FILTER_SCAN) @@ -103,9 +112,10 @@ private RelNode optimize(RelNode rootRel) { return rootRel; } - @Override public Enumerator enumerator() { + @Override public Enumerator<@Nullable Object[]> enumerator() { start(); - final NodeInfo nodeInfo = nodes.get(rootRel); + final NodeInfo nodeInfo = requireNonNull(nodes.get(rootRel), + () -> "nodeInfo for " + rootRel); final Enumerator rows; if (nodeInfo.rowEnumerable != null) { rows = nodeInfo.rowEnumerable.enumerator(); @@ -115,8 +125,8 @@ private RelNode optimize(RelNode rootRel) { rows = Linq4j.iterableEnumerator(queue); } - return new TransformedEnumerator(rows) { - @Override protected Object[] transform(Row row) { + return new TransformedEnumerator(rows) { + @Override protected @Nullable Object[] transform(Row row) { return row.getValues(); } }; @@ -128,6 +138,7 @@ private void start() { for (Map.Entry entry : nodes.entrySet()) { final NodeInfo nodeInfo = entry.getValue(); try { + assert nodeInfo.node != null : "node must not be null for nodeInfo, rel=" + nodeInfo.rel; nodeInfo.node.run(); } catch (InterruptedException e) { e.printStackTrace(); @@ -149,11 +160,11 @@ private class FooCompiler implements ScalarCompiler { return new Scalar() { final Object[] args = new Object[call.getOperands().size()]; - @Override public void execute(final Context context, Object[] results) { + @Override public void execute(final Context context, @Nullable Object[] results) { results[0] = execute(context); } - @Override public Object execute(Context context) { + @Override public @Nullable Object execute(Context context) { Comparable o0; Comparable o1; switch (call.getKind()) { @@ -229,16 +240,17 @@ private class FooCompiler implements ScalarCompiler { }; } return new Scalar() { - @Override public void execute(Context context, Object[] results) { + @Override public void execute(Context context, @Nullable Object[] results) { results[0] = execute(context); } - @Override public Object execute(Context context) { + @Override public @Nullable Object execute(Context context) { switch (node.getKind()) { case LITERAL: return ((RexLiteral) node).getValueAs(Comparable.class); case INPUT_REF: - return context.values[((RexInputRef) node).getIndex()]; + @Nullable Object[] values = requireNonNull(context.values, "context.values"); + return values[((RexInputRef) node).getIndex()]; default: throw new RuntimeException("unknown expression type " + node); } @@ -251,10 +263,10 @@ private class FooCompiler implements ScalarCompiler { private static class NodeInfo { final RelNode rel; final Map sinks = new LinkedHashMap<>(); - final Enumerable rowEnumerable; - Node node; + final @Nullable Enumerable rowEnumerable; + @Nullable Node node; - NodeInfo(RelNode rel, Enumerable rowEnumerable) { + NodeInfo(RelNode rel, @Nullable Enumerable rowEnumerable) { this.rel = rel; this.rowEnumerable = rowEnumerable; } @@ -268,10 +280,10 @@ private static class EnumeratorSource implements Source { private final Enumerator enumerator; EnumeratorSource(final Enumerator enumerator) { - this.enumerator = Objects.requireNonNull(enumerator); + this.enumerator = requireNonNull(enumerator); } - @Override public Row receive() { + @Override public @Nullable Row receive() { if (enumerator.moveNext()) { return enumerator.current(); } @@ -315,13 +327,13 @@ private ListSink(ArrayDeque list) { /** Implementation of {@link Source} using a {@link java.util.ArrayDeque}. */ private static class ListSource implements Source { private final ArrayDeque list; - private Iterator iterator = null; + private @Nullable Iterator iterator; ListSource(ArrayDeque list) { this.list = list; } - @Override public Row receive() { + @Override public @Nullable Row receive() { try { if (iterator == null) { iterator = list.iterator(); @@ -385,10 +397,11 @@ static class CompilerImpl extends RelVisitor final ScalarCompiler scalarCompiler; private final ReflectiveVisitDispatcher dispatcher = ReflectUtil.createDispatcher(CompilerImpl.class, RelNode.class); + @NotOnlyInitialized protected final Interpreter interpreter; - protected RelNode rootRel; - protected RelNode rel; - protected Node node; + protected @Nullable RelNode rootRel; + protected @Nullable RelNode rel; + protected @Nullable Node node; final Map nodes = new LinkedHashMap<>(); final Map> relInputs = new HashMap<>(); final Multimap outEdges = LinkedHashMultimap.create(); @@ -396,7 +409,7 @@ static class CompilerImpl extends RelVisitor private static final String REWRITE_METHOD_NAME = "rewrite"; private static final String VISIT_METHOD_NAME = "visit"; - CompilerImpl(Interpreter interpreter, RelOptCluster cluster) { + CompilerImpl(@UnknownInitialization Interpreter interpreter, RelOptCluster cluster) { this.interpreter = interpreter; this.scalarCompiler = new JaninoRexCompiler(cluster.getRexBuilder()); } @@ -405,10 +418,10 @@ static class CompilerImpl extends RelVisitor Pair> visitRoot(RelNode p) { rootRel = p; visit(p, 0, null); - return Pair.of(rootRel, nodes); + return Pair.of(requireNonNull(rootRel, "rootRel"), nodes); } - @Override public void visit(RelNode p, int ordinal, RelNode parent) { + @Override public void visit(RelNode p, int ordinal, @Nullable RelNode parent) { for (;;) { rel = null; boolean found = dispatcher.invokeVisitor(this, p, REWRITE_METHOD_NAME); @@ -422,7 +435,7 @@ Pair> visitRoot(RelNode p) { if (CalciteSystemProperty.DEBUG.value()) { System.out.println("Interpreter: rewrite " + p + " to " + rel); } - p = rel; + p = requireNonNull(rel, "rel"); if (parent != null) { List inputs = relInputs.get(parent); if (inputs == null) { @@ -454,8 +467,10 @@ Pair> visitRoot(RelNode p) { if (!found) { if (p instanceof InterpretableRel) { InterpretableRel interpretableRel = (InterpretableRel) p; + // TODO: analyze if null is permissible argument for dataContext node = interpretableRel.implement( - new InterpretableRel.InterpreterImplementor(this, null, null)); + new InterpretableRel.InterpreterImplementor(this, null, + castNonNull(null))); } else { // Probably need to add a visit(XxxRel) method to CoreCompiler. throw new AssertionError("interpreter: no implementation for " @@ -481,17 +496,22 @@ Pair> visitRoot(RelNode p) { public void rewrite(RelNode r) { } - @Override public Scalar compile(List nodes, RelDataType inputRowType) { + @Override public Scalar compile(List nodes, @Nullable RelDataType inputRowType) { if (inputRowType == null) { - inputRowType = interpreter.dataContext.getTypeFactory().builder() + inputRowType = getTypeFactory().builder() .build(); } return scalarCompiler.compile(nodes, inputRowType); } + private JavaTypeFactory getTypeFactory() { + return requireNonNull(interpreter.dataContext.getTypeFactory(), + () -> "no typeFactory in dataContext"); + } + @Override public RelDataType combinedRowType(List inputs) { final RelDataTypeFactory.Builder builder = - interpreter.dataContext.getTypeFactory().builder(); + getTypeFactory().builder(); for (RelNode input : inputs) { builder.addAll(input.getRowType().getFieldList()); } @@ -572,8 +592,8 @@ private RelNode getInput(RelNode rel, int ordinal) { } /** Edge between a {@link RelNode} and one of its inputs. */ - static class Edge extends Pair { - Edge(RelNode parent, int ordinal) { + static class Edge extends Pair<@Nullable RelNode, Integer> { + Edge(@Nullable RelNode parent, int ordinal) { super(parent, ordinal); } } diff --git a/core/src/main/java/org/apache/calcite/interpreter/Interpreters.java b/core/src/main/java/org/apache/calcite/interpreter/Interpreters.java index 2ae81a3ca665..9bb42719d8a7 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Interpreters.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Interpreters.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.runtime.ArrayBindable; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Utilities relating to {@link org.apache.calcite.interpreter.Interpreter} * and {@link org.apache.calcite.interpreter.InterpretableConvention}. @@ -36,7 +38,7 @@ public static ArrayBindable bindable(final RelNode rel) { return (ArrayBindable) rel; } return new ArrayBindable() { - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return new Interpreter(dataContext, rel); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java b/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java index 980bb772b9a6..6d88213f3b34 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java @@ -21,11 +21,15 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Interpreter node that implements a * {@link org.apache.calcite.rel.core.Join}. @@ -82,7 +86,8 @@ public JoinNode(Compiler compiler, Join rel) { if (rel.getJoinType() == JoinRelType.FULL) { // send un-match rows for full join on right source List empty = new ArrayList<>(); - for (Row row: innerRows) { + // TODO: CALCITE-4308, JointNode in Interpreter might fail with NPE for FULL join + for (Row row: requireNonNull(innerRows, "innerRows")) { if (matchRowSet.contains(row)) { continue; } @@ -124,6 +129,7 @@ private void doSend(Row outerRow, List matchInnerRows, case FULL: boolean outerRowOnLeft = joinRelType != JoinRelType.RIGHT; copyToContext(outerRow, outerRowOnLeft); + requireNonNull(context.values, "context.values"); for (Row row: matchInnerRows) { copyToContext(row, !outerRowOnLeft); sink.send(Row.asCopy(context.values)); @@ -140,6 +146,7 @@ private void doSend(Row outerRow, List matchInnerRows, case LEFT: case RIGHT: case FULL: + requireNonNull(context.values, "context.values"); int nullColumnNum = context.values.length - outerRow.size(); // for full join, use left source as outer source, // and send un-match rows in left source fist, @@ -163,7 +170,8 @@ private void doSend(Row outerRow, List matchInnerRows, * Copies the value of row into context values. */ private void copyToContext(Row row, boolean toLeftSide) { - Object[] values = row.getValues(); + @Nullable Object[] values = row.getValues(); + requireNonNull(context.values, "context.values"); if (toLeftSide) { System.arraycopy(values, 0, context.values, 0, values.length); } else { diff --git a/core/src/main/java/org/apache/calcite/interpreter/Nodes.java b/core/src/main/java/org/apache/calcite/interpreter/Nodes.java index 0f01a46aa67c..84224d075f99 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Nodes.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Nodes.java @@ -33,6 +33,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; + /** * Helper methods for {@link Node} and implementations for core relational * expressions. @@ -43,7 +45,7 @@ public class Nodes { * that knows how to handle the core logical * {@link org.apache.calcite.rel.RelNode}s. */ public static class CoreCompiler extends Interpreter.CompilerImpl { - CoreCompiler(Interpreter interpreter, RelOptCluster cluster) { + CoreCompiler(@UnknownInitialization Interpreter interpreter, RelOptCluster cluster) { super(interpreter, cluster); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/NoneToBindableConverterRule.java b/core/src/main/java/org/apache/calcite/interpreter/NoneToBindableConverterRule.java index bdf78ab6aa03..c4d1c555a0ca 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/NoneToBindableConverterRule.java +++ b/core/src/main/java/org/apache/calcite/interpreter/NoneToBindableConverterRule.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.convert.ConverterRule; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Rule to convert a relational expression from @@ -41,7 +43,7 @@ protected NoneToBindableConverterRule(Config config) { super(config); } - @Override public RelNode convert(RelNode rel) { + @Override public @Nullable RelNode convert(RelNode rel) { RelTraitSet newTraitSet = rel.getTraitSet().replace(getOutConvention()); return new InterpretableConverter(rel.getCluster(), newTraitSet, rel); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/Row.java b/core/src/main/java/org/apache/calcite/interpreter/Row.java index 365d5f7e6ac0..1d35308c7bdc 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Row.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Row.java @@ -16,17 +16,19 @@ */ package org.apache.calcite.interpreter; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; /** * Row. */ public class Row { - private final Object[] values; + private final @Nullable Object[] values; /** Creates a Row. */ // must stay package-protected, because does not copy - Row(Object[] values) { + Row(@Nullable Object[] values) { this.values = values; } @@ -36,27 +38,27 @@ public class Row { * (If you're worried about the extra copy, call {@link #of(Object)}. * But the JIT probably avoids the copy.) */ - public static Row asCopy(Object... values) { + public static Row asCopy(@Nullable Object... values) { return new Row(values.clone()); } /** Creates a Row with one column value. */ - public static Row of(Object value0) { + public static Row of(@Nullable Object value0) { return new Row(new Object[] {value0}); } /** Creates a Row with two column values. */ - public static Row of(Object value0, Object value1) { + public static Row of(@Nullable Object value0, @Nullable Object value1) { return new Row(new Object[] {value0, value1}); } /** Creates a Row with three column values. */ - public static Row of(Object value0, Object value1, Object value2) { + public static Row of(@Nullable Object value0, @Nullable Object value1, @Nullable Object value2) { return new Row(new Object[] {value0, value1, value2}); } /** Creates a Row with variable number of values. */ - public static Row of(Object...values) { + public static Row of(@Nullable Object...values) { return new Row(values); } @@ -64,7 +66,7 @@ public static Row of(Object...values) { return Arrays.hashCode(values); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof Row && Arrays.equals(values, ((Row) obj).values); @@ -74,17 +76,17 @@ public static Row of(Object...values) { return Arrays.toString(values); } - public Object getObject(int index) { + public @Nullable Object getObject(int index) { return values[index]; } // must stay package-protected - Object[] getValues() { + @Nullable Object[] getValues() { return values; } /** Returns a copy of the values. */ - public Object[] copyValues() { + public @Nullable Object[] copyValues() { return values.clone(); } @@ -106,7 +108,7 @@ public static RowBuilder newBuilder(int size) { * Utility class to build row objects. */ public static class RowBuilder { - Object[] values; + @Nullable Object[] values; private RowBuilder(int size) { values = new Object[size]; @@ -118,7 +120,7 @@ private RowBuilder(int size) { * @param index Zero-indexed position of value. * @param value Desired column value. */ - public void set(int index, Object value) { + public void set(int index, @Nullable Object value) { values[index] = value; } diff --git a/core/src/main/java/org/apache/calcite/interpreter/Scalar.java b/core/src/main/java/org/apache/calcite/interpreter/Scalar.java index 20e5bdd44d60..9146b398e497 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Scalar.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Scalar.java @@ -16,10 +16,12 @@ */ package org.apache.calcite.interpreter; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Compiled scalar expression. */ public interface Scalar { - Object execute(Context context); - void execute(Context context, Object[] results); + @Nullable Object execute(Context context); + void execute(Context context, @Nullable Object[] results); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/SortNode.java b/core/src/main/java/org/apache/calcite/interpreter/SortNode.java index 9c4dc3e4f00a..b7a85a252d84 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/SortNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/SortNode.java @@ -19,6 +19,7 @@ import org.apache.calcite.rel.RelFieldCollation; import org.apache.calcite.rel.core.Sort; import org.apache.calcite.rex.RexLiteral; +import org.apache.calcite.rex.RexNode; import org.apache.calcite.util.Util; import com.google.common.collect.Ordering; @@ -27,6 +28,8 @@ import java.util.Comparator; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Interpreter node that implements a * {@link org.apache.calcite.rel.core.Sort}. @@ -36,15 +39,20 @@ public SortNode(Compiler compiler, Sort rel) { super(compiler, rel); } + private static int getValueAsInt(RexNode node) { + return requireNonNull(((RexLiteral) node).getValueAs(Integer.class), + () -> "getValueAs(Integer.class) for " + node); + } + @Override public void run() throws InterruptedException { final int offset = rel.offset == null ? 0 - : ((RexLiteral) rel.offset).getValueAs(Integer.class); + : getValueAsInt(rel.offset); final int fetch = rel.fetch == null ? -1 - : ((RexLiteral) rel.fetch).getValueAs(Integer.class); + : getValueAsInt(rel.fetch); // In pure limit mode. No sort required. Row row; loop: diff --git a/core/src/main/java/org/apache/calcite/interpreter/Source.java b/core/src/main/java/org/apache/calcite/interpreter/Source.java index 4543e6206b42..f6c49e398e5b 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Source.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Source.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.interpreter; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Source of rows. * @@ -23,7 +25,7 @@ */ public interface Source extends AutoCloseable { /** Reads a row. Null means end of data. */ - Row receive(); + @Nullable Row receive(); @Override void close(); } diff --git a/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java b/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java index 427f0e6c47ed..75a5a930d51a 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java @@ -45,14 +45,17 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.List; -import java.util.Objects; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Interpreter node that implements a * {@link org.apache.calcite.rel.core.TableScan}. @@ -73,7 +76,7 @@ private TableScanNode(Compiler compiler, TableScan rel, * and projects it can implement. Adds to the Enumerable implementations of * any filters and projects that cannot be implemented by the table. */ static TableScanNode create(Compiler compiler, TableScan rel, - ImmutableList filters, ImmutableIntList projects) { + ImmutableList filters, @Nullable ImmutableIntList projects) { final RelOptTable relOptTable = rel.getTable(); final ProjectableFilterableTable pfTable = relOptTable.unwrap(ProjectableFilterableTable.class); @@ -110,7 +113,7 @@ static TableScanNode create(Compiler compiler, TableScan rel, } private static TableScanNode createScannable(Compiler compiler, TableScan rel, - ImmutableList filters, ImmutableIntList projects, + ImmutableList filters, @Nullable ImmutableIntList projects, ScannableTable scannableTable) { final Enumerable rowEnumerable = Enumerables.toRow(scannableTable.scan(compiler.getDataContext())); @@ -119,13 +122,15 @@ private static TableScanNode createScannable(Compiler compiler, TableScan rel, } private static TableScanNode createQueryable(Compiler compiler, - TableScan rel, ImmutableList filters, ImmutableIntList projects, + TableScan rel, ImmutableList filters, @Nullable ImmutableIntList projects, QueryableTable queryableTable) { final DataContext root = compiler.getDataContext(); final RelOptTable relOptTable = rel.getTable(); final Type elementType = queryableTable.getElementType(); SchemaPlus schema = root.getRootSchema(); for (String name : Util.skipLast(relOptTable.getQualifiedName())) { + requireNonNull(schema, () -> "schema is null while resolving " + name + " for table" + + relOptTable.getQualifiedName()); schema = schema.getSubSchema(name); } final Enumerable rowEnumerable; @@ -144,7 +149,7 @@ private static TableScanNode createQueryable(Compiler compiler, } final List fields = fieldBuilder.build(); rowEnumerable = queryable.select(o -> { - final Object[] values = new Object[fields.size()]; + final @Nullable Object[] values = new Object[fields.size()]; for (int i = 0; i < fields.size(); i++) { Field field = fields.get(i); try { @@ -164,11 +169,11 @@ private static TableScanNode createQueryable(Compiler compiler, } private static TableScanNode createFilterable(Compiler compiler, - TableScan rel, ImmutableList filters, ImmutableIntList projects, + TableScan rel, ImmutableList filters, @Nullable ImmutableIntList projects, FilterableTable filterableTable) { final DataContext root = compiler.getDataContext(); final List mutableFilters = Lists.newArrayList(filters); - final Enumerable enumerable = + final Enumerable<@Nullable Object[]> enumerable = filterableTable.scan(root, mutableFilters); for (RexNode filter : mutableFilters) { if (!filters.contains(filter)) { @@ -181,7 +186,7 @@ private static TableScanNode createFilterable(Compiler compiler, } private static TableScanNode createProjectableFilterable(Compiler compiler, - TableScan rel, ImmutableList filters, ImmutableIntList projects, + TableScan rel, ImmutableList filters, @Nullable ImmutableIntList projects, ProjectableFilterableTable pfTable) { final DataContext root = compiler.getDataContext(); final ImmutableIntList originalProjects = projects; @@ -194,7 +199,7 @@ private static TableScanNode createProjectableFilterable(Compiler compiler, } else { projectInts = projects.toIntArray(); } - final Enumerable enumerable1 = + final Enumerable<@Nullable Object[]> enumerable1 = pfTable.scan(root, mutableFilters, projectInts); for (RexNode filter : mutableFilters) { if (!filters.contains(filter)) { @@ -223,7 +228,7 @@ private static TableScanNode createProjectableFilterable(Compiler compiler, } final Enumerable rowEnumerable = Enumerables.toRow(enumerable1); final ImmutableIntList rejectedProjects; - if (Objects.equals(projects, originalProjects)) { + if (originalProjects == null || originalProjects.equals(projects)) { rejectedProjects = null; } else { // We projected extra columns because they were needed in filters. Now @@ -237,8 +242,8 @@ private static TableScanNode createProjectableFilterable(Compiler compiler, private static TableScanNode createEnumerable(Compiler compiler, TableScan rel, Enumerable enumerable, - final ImmutableIntList acceptedProjects, List rejectedFilters, - final ImmutableIntList rejectedProjects) { + final @Nullable ImmutableIntList acceptedProjects, List rejectedFilters, + final @Nullable ImmutableIntList rejectedProjects) { if (!rejectedFilters.isEmpty()) { final RexNode filter = RexUtil.composeConjunction(rel.getCluster().getRexBuilder(), @@ -274,9 +279,9 @@ private static TableScanNode createEnumerable(Compiler compiler, if (rejectedProjects != null) { enumerable = enumerable.select( new Function1() { - final Object[] values = new Object[rejectedProjects.size()]; + final @Nullable Object[] values = new Object[rejectedProjects.size()]; @Override public Row apply(Row row) { - final Object[] inValues = row.getValues(); + final @Nullable Object[] inValues = row.getValues(); for (int i = 0; i < rejectedProjects.size(); i++) { values[i] = inValues[rejectedProjects.get(i)]; } diff --git a/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java b/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java index ac399a6dfde8..6800f949b6ec 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java @@ -37,10 +37,11 @@ public class ValuesNode implements Node { public ValuesNode(Compiler compiler, Values rel) { this.sink = compiler.sink(rel); this.fieldCount = rel.getRowType().getFieldCount(); - this.rows = createRows(compiler, rel.getTuples()); + this.rows = createRows(compiler, fieldCount, rel.getTuples()); } - private ImmutableList createRows(Compiler compiler, + private static ImmutableList createRows(Compiler compiler, + int fieldCount, ImmutableList> tuples) { final List nodes = new ArrayList<>(); for (ImmutableList tuple : tuples) { diff --git a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java index 23e144552a54..1c62d913ab73 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java @@ -33,10 +33,14 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.List; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Concrete implementation of {@link CalciteSchema} that caches tables, * functions and sub-schemas. @@ -50,16 +54,21 @@ class CachingCalciteSchema extends CalciteSchema { private boolean cache = true; /** Creates a CachingCalciteSchema. */ - CachingCalciteSchema(CalciteSchema parent, Schema schema, String name) { + CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, String name) { this(parent, schema, name, null, null, null, null, null, null, null, null); } - private CachingCalciteSchema(CalciteSchema parent, Schema schema, - String name, NameMap subSchemaMap, - NameMap tableMap, NameMap latticeMap, NameMap typeMap, - NameMultimap functionMap, NameSet functionNames, - NameMap nullaryFunctionMap, - List> path) { + @SuppressWarnings({"argument.type.incompatible", "return.type.incompatible"}) + private CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, + String name, + @Nullable NameMap subSchemaMap, + @Nullable NameMap tableMap, + @Nullable NameMap latticeMap, + @Nullable NameMap typeMap, + @Nullable NameMultimap functionMap, + @Nullable NameSet functionNames, + @Nullable NameMap nullaryFunctionMap, + @Nullable List> path) { super(parent, schema, name, subSchemaMap, tableMap, latticeMap, typeMap, functionMap, functionNames, nullaryFunctionMap, path); this.implicitSubSchemaCache = @@ -107,7 +116,7 @@ private CachingCalciteSchema(CalciteSchema parent, Schema schema, return this.cache; } - @Override protected CalciteSchema getImplicitSubSchema(String schemaName, + @Override protected @Nullable CalciteSchema getImplicitSubSchema(String schemaName, boolean caseSensitive) { final long now = System.currentTimeMillis(); final SubSchemaCache subSchemaCache = @@ -128,7 +137,7 @@ private CachingCalciteSchema(CalciteSchema parent, Schema schema, return calciteSchema; } - @Override protected TableEntry getImplicitTable(String tableName, + @Override protected @Nullable TableEntry getImplicitTable(String tableName, boolean caseSensitive) { final long now = System.currentTimeMillis(); final NameSet implicitTableNames = implicitTableCache.get(now); @@ -142,7 +151,7 @@ private CachingCalciteSchema(CalciteSchema parent, Schema schema, return null; } - @Override protected TypeEntry getImplicitType(String name, boolean caseSensitive) { + @Override protected @Nullable TypeEntry getImplicitType(String name, boolean caseSensitive) { final long now = System.currentTimeMillis(); final NameSet implicitTypeNames = implicitTypeCache.get(now); for (String typeName @@ -228,7 +237,7 @@ private CachingCalciteSchema(CalciteSchema parent, Schema schema, } } - @Override protected TableEntry getImplicitTableBasedOnNullaryFunction(String tableName, + @Override protected @Nullable TableEntry getImplicitTableBasedOnNullaryFunction(String tableName, boolean caseSensitive) { final long now = System.currentTimeMillis(); final NameSet set = implicitFunctionCache.get(now); @@ -245,7 +254,8 @@ private CachingCalciteSchema(CalciteSchema parent, Schema schema, return null; } - @Override protected CalciteSchema snapshot(CalciteSchema parent, SchemaVersion version) { + @Override protected CalciteSchema snapshot(@Nullable CalciteSchema parent, + SchemaVersion version) { CalciteSchema snapshot = new CachingCalciteSchema(parent, schema.snapshot(version), name, null, tableMap, latticeMap, typeMap, functionMap, functionNames, nullaryFunctionMap, getPath()); @@ -295,7 +305,7 @@ private interface Cached { * * @param element type */ private abstract class AbstractCached implements Cached { - T t; + @Nullable T t; boolean built = false; @Override public T get(long now) { @@ -306,7 +316,7 @@ private abstract class AbstractCached implements Cached { t = build(); } built = true; - return t; + return castNonNull(t); } @Override public void enable(long now, boolean enabled) { diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java index 3bac79c1c6a2..a4d1dd275641 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java @@ -69,6 +69,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.Serializable; import java.lang.reflect.Type; import java.sql.ResultSet; @@ -84,6 +86,8 @@ import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Implementation of JDBC connection * in the Calcite engine. @@ -115,8 +119,8 @@ abstract class CalciteConnectionImpl * @param typeFactory Type factory, or null */ protected CalciteConnectionImpl(Driver driver, AvaticaFactory factory, - String url, Properties info, CalciteSchema rootSchema, - JavaTypeFactory typeFactory) { + String url, Properties info, @Nullable CalciteSchema rootSchema, + @Nullable JavaTypeFactory typeFactory) { super(driver, factory, url, info); CalciteConnectionConfig cfg = new CalciteConnectionConfigImpl(info); this.prepareFactory = driver.prepareFactory; @@ -267,11 +271,11 @@ CalcitePrepare.CalciteSignature parseQuery( } @Override public T execute(Expression expression, Type type) { - return null; // TODO: + return castNonNull(null); // TODO: } @Override public T execute(Expression expression, Class type) { - return null; // TODO: + return castNonNull(null); // TODO: } @Override public Enumerator executeQuery(Queryable queryable) { @@ -381,7 +385,7 @@ static class RootSchema extends AbstractSchema { super(); } - @Override public Expression getExpression(SchemaPlus parentSchema, + @Override public Expression getExpression(@Nullable SchemaPlus parentSchema, String name) { return Expressions.call( DataContext.ROOT, @@ -445,7 +449,7 @@ static class DataContextImpl implements DataContext { map = builder.build(); } - @Override public synchronized Object get(String name) { + @Override public synchronized @Nullable Object get(String name) { Object o = map.get(name); if (o == AvaticaSite.DUMMY_VALUE) { return null; @@ -484,7 +488,7 @@ private SqlAdvisor getSqlAdvisor() { return new SqlAdvisor(validator, parserConfig); } - @Override public SchemaPlus getRootSchema() { + @Override public @Nullable SchemaPlus getRootSchema() { return rootSchema == null ? null : rootSchema.plus(); } @@ -535,7 +539,7 @@ static class ContextImpl implements CalcitePrepare.Context { : ImmutableList.of(schemaName); } - @Override public List getObjectPath() { + @Override public @Nullable List getObjectPath() { return null; } @@ -570,19 +574,19 @@ static class ContextImpl implements CalcitePrepare.Context { /** Implementation of {@link DataContext} that has few variables and is * {@link Serializable}. For Spark. */ private static class SlimDataContext implements DataContext, Serializable { - @Override public SchemaPlus getRootSchema() { + @Override public @Nullable SchemaPlus getRootSchema() { return null; } - @Override public JavaTypeFactory getTypeFactory() { + @Override public @Nullable JavaTypeFactory getTypeFactory() { return null; } - @Override public QueryProvider getQueryProvider() { + @Override public @Nullable QueryProvider getQueryProvider() { return null; } - @Override public Object get(String name) { + @Override public @Nullable Object get(String name) { return null; } } @@ -591,8 +595,8 @@ private static class SlimDataContext implements DataContext, Serializable { static class CalciteServerStatementImpl implements CalciteServerStatement { private final CalciteConnectionImpl connection; - private Iterator iterator; - private Meta.Signature signature; + private @Nullable Iterator iterator; + private Meta.@Nullable Signature signature; private final AtomicBoolean cancelFlag = new AtomicBoolean(); CalciteServerStatementImpl(CalciteConnectionImpl connection) { @@ -611,11 +615,11 @@ static class CalciteServerStatementImpl this.signature = signature; } - @Override public Meta.Signature getSignature() { + @Override public Meta.@Nullable Signature getSignature() { return signature; } - @Override public Iterator getResultSet() { + @Override public @Nullable Iterator getResultSet() { return iterator; } diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java index 1485d118f9cb..9100da4acc09 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java @@ -21,6 +21,8 @@ import org.apache.calcite.avatica.AvaticaFactory; import org.apache.calcite.avatica.UnregisteredDriver; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Properties; /** @@ -56,5 +58,5 @@ protected CalciteFactory(int major, int minor) { /** Creates a connection with a root schema. */ public abstract AvaticaConnection newConnection(UnregisteredDriver driver, AvaticaFactory factory, String url, Properties info, - CalciteSchema rootSchema, JavaTypeFactory typeFactory); + @Nullable CalciteSchema rootSchema, @Nullable JavaTypeFactory typeFactory); } diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java index c71ae8423168..1707834f16d8 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java @@ -27,6 +27,8 @@ import org.apache.calcite.avatica.QueryState; import org.apache.calcite.avatica.UnregisteredDriver; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.InputStream; import java.io.Reader; import java.sql.NClob; @@ -55,7 +57,7 @@ protected CalciteJdbc41Factory(int major, int minor) { @Override public CalciteJdbc41Connection newConnection(UnregisteredDriver driver, AvaticaFactory factory, String url, Properties info, - CalciteSchema rootSchema, JavaTypeFactory typeFactory) { + @Nullable CalciteSchema rootSchema, @Nullable JavaTypeFactory typeFactory) { return new CalciteJdbc41Connection( (Driver) driver, factory, url, info, rootSchema, typeFactory); } @@ -110,8 +112,8 @@ protected CalciteJdbc41Factory(int major, int minor) { /** Implementation of connection for JDBC 4.1. */ private static class CalciteJdbc41Connection extends CalciteConnectionImpl { CalciteJdbc41Connection(Driver driver, AvaticaFactory factory, String url, - Properties info, CalciteSchema rootSchema, - JavaTypeFactory typeFactory) { + Properties info, @Nullable CalciteSchema rootSchema, + @Nullable JavaTypeFactory typeFactory) { super(driver, factory, url, info, rootSchema, typeFactory); } } @@ -139,18 +141,18 @@ private static class CalciteJdbc41PreparedStatement @Override public void setRowId( int parameterIndex, - RowId x) throws SQLException { + @Nullable RowId x) throws SQLException { getSite(parameterIndex).setRowId(x); } @Override public void setNString( - int parameterIndex, String value) throws SQLException { + int parameterIndex, @Nullable String value) throws SQLException { getSite(parameterIndex).setNString(value); } @Override public void setNCharacterStream( int parameterIndex, - Reader value, + @Nullable Reader value, long length) throws SQLException { getSite(parameterIndex) .setNCharacterStream(value, length); @@ -158,13 +160,13 @@ private static class CalciteJdbc41PreparedStatement @Override public void setNClob( int parameterIndex, - NClob value) throws SQLException { + @Nullable NClob value) throws SQLException { getSite(parameterIndex).setNClob(value); } @Override public void setClob( int parameterIndex, - Reader reader, + @Nullable Reader reader, long length) throws SQLException { getSite(parameterIndex) .setClob(reader, length); @@ -172,7 +174,7 @@ private static class CalciteJdbc41PreparedStatement @Override public void setBlob( int parameterIndex, - InputStream inputStream, + @Nullable InputStream inputStream, long length) throws SQLException { getSite(parameterIndex) .setBlob(inputStream, length); @@ -180,19 +182,19 @@ private static class CalciteJdbc41PreparedStatement @Override public void setNClob( int parameterIndex, - Reader reader, + @Nullable Reader reader, long length) throws SQLException { getSite(parameterIndex).setNClob(reader, length); } @Override public void setSQLXML( - int parameterIndex, SQLXML xmlObject) throws SQLException { + int parameterIndex, @Nullable SQLXML xmlObject) throws SQLException { getSite(parameterIndex).setSQLXML(xmlObject); } @Override public void setAsciiStream( int parameterIndex, - InputStream x, + @Nullable InputStream x, long length) throws SQLException { getSite(parameterIndex) .setAsciiStream(x, length); @@ -200,7 +202,7 @@ private static class CalciteJdbc41PreparedStatement @Override public void setBinaryStream( int parameterIndex, - InputStream x, + @Nullable InputStream x, long length) throws SQLException { getSite(parameterIndex) .setBinaryStream(x, length); @@ -208,48 +210,48 @@ private static class CalciteJdbc41PreparedStatement @Override public void setCharacterStream( int parameterIndex, - Reader reader, + @Nullable Reader reader, long length) throws SQLException { getSite(parameterIndex) .setCharacterStream(reader, length); } @Override public void setAsciiStream( - int parameterIndex, InputStream x) throws SQLException { + int parameterIndex, @Nullable InputStream x) throws SQLException { getSite(parameterIndex).setAsciiStream(x); } @Override public void setBinaryStream( - int parameterIndex, InputStream x) throws SQLException { + int parameterIndex, @Nullable InputStream x) throws SQLException { getSite(parameterIndex).setBinaryStream(x); } @Override public void setCharacterStream( - int parameterIndex, Reader reader) throws SQLException { + int parameterIndex, @Nullable Reader reader) throws SQLException { getSite(parameterIndex) .setCharacterStream(reader); } @Override public void setNCharacterStream( - int parameterIndex, Reader value) throws SQLException { + int parameterIndex, @Nullable Reader value) throws SQLException { getSite(parameterIndex) .setNCharacterStream(value); } @Override public void setClob( int parameterIndex, - Reader reader) throws SQLException { + @Nullable Reader reader) throws SQLException { getSite(parameterIndex).setClob(reader); } @Override public void setBlob( - int parameterIndex, InputStream inputStream) throws SQLException { + int parameterIndex, @Nullable InputStream inputStream) throws SQLException { getSite(parameterIndex) .setBlob(inputStream); } @Override public void setNClob( - int parameterIndex, Reader reader) throws SQLException { + int parameterIndex, @Nullable Reader reader) throws SQLException { getSite(parameterIndex) .setNClob(reader); } diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java index 772613961180..80389879ea88 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java @@ -62,6 +62,8 @@ import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Field; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -71,9 +73,10 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.regex.Pattern; +import static java.util.Objects.requireNonNull; + /** * Helper for implementing the {@code getXxx} methods such as * {@link org.apache.calcite.avatica.AvaticaDatabaseMetaData#getTables}. @@ -162,7 +165,7 @@ public static Pattern likeToRegex(Pat pattern) { private MetaResultSet createResultSet(Enumerable enumerable, Class clazz, String... names) { - Objects.requireNonNull(names); + requireNonNull(names); final List columns = new ArrayList<>(names.length); final List fields = new ArrayList<>(names.length); final List fieldNames = new ArrayList<>(names.length); @@ -381,8 +384,9 @@ Enumerable tables(final MetaSchema schema_) { final CalciteMetaSchema schema = (CalciteMetaSchema) schema_; return Linq4j.asEnumerable(schema.calciteSchema.getTableNames()) .select((Function1) name -> { - final Table table = - schema.calciteSchema.getTable(name, true).getTable(); + final Table table = requireNonNull( + schema.calciteSchema.getTable(name, true), + () -> "table " + name + " is not found (case sensitive)").getTable(); return new CalciteMetaTable(table, schema.tableCatalog, schema.tableSchem, @@ -499,13 +503,13 @@ public Enumerable columns(final MetaTable table_) { } @Override public Iterable createIterable(StatementHandle handle, QueryState state, - Signature signature, List parameterValues, Frame firstFrame) { + Signature signature, @Nullable List parameterValues, @Nullable Frame firstFrame) { // Drop QueryState return _createIterable(handle, signature, parameterValues, firstFrame); } Iterable _createIterable(StatementHandle handle, - Signature signature, List parameterValues, Frame firstFrame) { + Signature signature, @Nullable List parameterValues, @Nullable Frame firstFrame) { try { //noinspection unchecked final CalcitePrepare.CalciteSignature calciteSignature = @@ -676,7 +680,7 @@ private CalcitePrepare.Query toQuery( final Meta.PrepareCallback callback = new Meta.PrepareCallback() { long updateCount; - Signature signature; + @Nullable Signature signature; @Override public Object getMonitor() { return statement; @@ -691,6 +695,7 @@ private CalcitePrepare.Query toQuery( } @Override public void execute() throws SQLException { + Signature signature = requireNonNull(this.signature, "signature"); if (signature.statementType.canUpdate()) { final Iterable iterable = _createIterable(h, signature, ImmutableList.of(), @@ -718,7 +723,7 @@ public static DataContext createDataContext(CalciteConnection connection) { /** A trojan-horse method, subject to change without notice. */ @VisibleForTesting public static CalciteConnection connect(CalciteSchema schema, - JavaTypeFactory typeFactory) { + @Nullable JavaTypeFactory typeFactory) { return DRIVER.connect(schema, typeFactory); } @@ -744,7 +749,7 @@ private static class CalciteMetaTable extends MetaTable { String tableSchem, String tableName) { super(tableCat, tableSchem, tableName, calciteTable.getJdbcTableType().jdbcName); - this.calciteTable = Objects.requireNonNull(calciteTable); + this.calciteTable = requireNonNull(calciteTable); } } diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java index 26e9eeb8b9f0..866cf4293086 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java @@ -50,6 +50,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; @@ -58,12 +60,16 @@ import java.util.List; import java.util.Map; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * API for a service that prepares statements for execution. */ public interface CalcitePrepare { Function0 DEFAULT_FACTORY = CalcitePrepareImpl::new; - ThreadLocal> THREAD_CONTEXT_STACK = + ThreadLocal<@Nullable Deque> THREAD_CONTEXT_STACK = ThreadLocal.withInitial(ArrayDeque::new); ParseResult parse(Context context, String sql); @@ -123,7 +129,7 @@ interface Context { *

The object is being analyzed is typically a view. If it is already * being analyzed further up the stack, the view definition can be deduced * to be cyclic. */ - List getObjectPath(); + @Nullable List getObjectPath(); /** Gets a runner; it can execute a relational expression. */ RelRunner getRelRunner(); @@ -152,7 +158,7 @@ interface RuleSetBuilder { /** Namespace that allows us to define non-abstract methods inside an * interface. */ class Dummy { - private static SparkHandler sparkHandler; + private static @Nullable SparkHandler sparkHandler; private Dummy() {} @@ -172,7 +178,9 @@ private static SparkHandler createHandler() { final Class clazz = Class.forName("org.apache.calcite.adapter.spark.SparkHandlerImpl"); Method method = clazz.getMethod("instance"); - return (CalcitePrepare.SparkHandler) method.invoke(null); + return (CalcitePrepare.SparkHandler) requireNonNull( + method.invoke(null), + () -> "non-null SparkHandler expected from " + method); } catch (ClassNotFoundException e) { return new TrivialSparkHandler(); } catch (IllegalAccessException @@ -184,7 +192,7 @@ private static SparkHandler createHandler() { } public static void push(Context context) { - final Deque stack = THREAD_CONTEXT_STACK.get(); + final Deque stack = castNonNull(THREAD_CONTEXT_STACK.get()); final List path = context.getObjectPath(); if (path != null) { for (Context context1 : stack) { @@ -198,11 +206,11 @@ public static void push(Context context) { } public static Context peek() { - return THREAD_CONTEXT_STACK.get().peek(); + return castNonNull(castNonNull(THREAD_CONTEXT_STACK.get()).peek()); } public static void pop(Context context) { - Context x = THREAD_CONTEXT_STACK.get().pop(); + Context x = castNonNull(THREAD_CONTEXT_STACK.get()).pop(); assert x == context; } @@ -287,17 +295,17 @@ public ConvertResult(CalcitePrepareImpl prepare, SqlValidator validator, /** The result of analyzing a view. */ class AnalyzeViewResult extends ConvertResult { /** Not null if and only if the view is modifiable. */ - public final Table table; - public final ImmutableList tablePath; - public final RexNode constraint; - public final ImmutableIntList columnMapping; + public final @Nullable Table table; + public final @Nullable ImmutableList tablePath; + public final @Nullable RexNode constraint; + public final @Nullable ImmutableIntList columnMapping; public final boolean modifiable; public AnalyzeViewResult(CalcitePrepareImpl prepare, SqlValidator validator, String sql, SqlNode sqlNode, - RelDataType rowType, RelRoot root, Table table, - ImmutableList tablePath, RexNode constraint, - ImmutableIntList columnMapping, boolean modifiable) { + RelDataType rowType, RelRoot root, @Nullable Table table, + @Nullable ImmutableList tablePath, @Nullable RexNode constraint, + @Nullable ImmutableIntList columnMapping, boolean modifiable) { super(prepare, validator, sql, sqlNode, rowType, root); this.table = table; this.tablePath = tablePath; @@ -314,11 +322,11 @@ public AnalyzeViewResult(CalcitePrepareImpl prepare, * * @param element type */ class CalciteSignature extends Meta.Signature { - @JsonIgnore public final RelDataType rowType; - @JsonIgnore public final CalciteSchema rootSchema; + @JsonIgnore public final @Nullable RelDataType rowType; + @JsonIgnore public final @Nullable CalciteSchema rootSchema; @JsonIgnore private final List collationList; private final long maxRowCount; - private final Bindable bindable; + private final @Nullable Bindable bindable; @Deprecated // to be removed before 2.0 public CalciteSignature(String sql, List parameterList, @@ -328,19 +336,19 @@ public CalciteSignature(String sql, List parameterList, long maxRowCount, Bindable bindable) { this(sql, parameterList, internalParameters, rowType, columns, cursorFactory, rootSchema, collationList, maxRowCount, bindable, - null); + castNonNull(null)); } - public CalciteSignature(String sql, + public CalciteSignature(@Nullable String sql, List parameterList, Map internalParameters, - RelDataType rowType, + @Nullable RelDataType rowType, List columns, Meta.CursorFactory cursorFactory, - CalciteSchema rootSchema, + @Nullable CalciteSchema rootSchema, List collationList, long maxRowCount, - Bindable bindable, + @Nullable Bindable bindable, Meta.StatementType statementType) { super(columns, sql, parameterList, internalParameters, cursorFactory, statementType); @@ -352,7 +360,7 @@ public CalciteSignature(String sql, } public Enumerable enumerable(DataContext dataContext) { - Enumerable enumerable = bindable.bind(dataContext); + Enumerable enumerable = castNonNull(bindable).bind(dataContext); if (maxRowCount >= 0) { // Apply limit. In JDBC 0 means "no limit". But for us, -1 means // "no limit", and 0 is a valid limit. @@ -372,11 +380,11 @@ public List getCollationList() { * * @param element type */ class Query { - public final String sql; - public final Queryable queryable; - public final RelNode rel; + public final @Nullable String sql; + public final @Nullable Queryable queryable; + public final @Nullable RelNode rel; - private Query(String sql, Queryable queryable, RelNode rel) { + private Query(@Nullable String sql, @Nullable Queryable queryable, @Nullable RelNode rel) { this.sql = sql; this.queryable = queryable; this.rel = rel; diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java index f3bc02f8bee2..fd0f2b76dc1a 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java @@ -41,6 +41,8 @@ import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -58,7 +60,7 @@ */ public abstract class CalciteSchema { - private final CalciteSchema parent; + private final @Nullable CalciteSchema parent; public final Schema schema; public final String name; /** Tables explicitly defined in this schema. Does not include tables in @@ -70,14 +72,18 @@ public abstract class CalciteSchema { protected final NameSet functionNames; protected final NameMap nullaryFunctionMap; protected final NameMap subSchemaMap; - private List> path; - - protected CalciteSchema(CalciteSchema parent, Schema schema, - String name, NameMap subSchemaMap, - NameMap tableMap, NameMap latticeMap, NameMap typeMap, - NameMultimap functionMap, NameSet functionNames, - NameMap nullaryFunctionMap, - List> path) { + private @Nullable List> path; + + protected CalciteSchema(@Nullable CalciteSchema parent, Schema schema, + String name, + @Nullable NameMap subSchemaMap, + @Nullable NameMap tableMap, + @Nullable NameMap latticeMap, + @Nullable NameMap typeMap, + @Nullable NameMultimap functionMap, + @Nullable NameSet functionNames, + @Nullable NameMap nullaryFunctionMap, + @Nullable List> path) { this.parent = parent; this.schema = schema; this.name = name; @@ -118,25 +124,25 @@ protected CalciteSchema(CalciteSchema parent, Schema schema, /** Returns a sub-schema with a given name that is defined implicitly * (that is, by the underlying {@link Schema} object, not explicitly * by a call to {@link #add(String, Schema)}), or null. */ - protected abstract CalciteSchema getImplicitSubSchema(String schemaName, + protected abstract @Nullable CalciteSchema getImplicitSubSchema(String schemaName, boolean caseSensitive); /** Returns a table with a given name that is defined implicitly * (that is, by the underlying {@link Schema} object, not explicitly * by a call to {@link #add(String, Table)}), or null. */ - protected abstract TableEntry getImplicitTable(String tableName, + protected abstract @Nullable TableEntry getImplicitTable(String tableName, boolean caseSensitive); /** Returns a type with a given name that is defined implicitly * (that is, by the underlying {@link Schema} object, not explicitly * by a call to {@link #add(String, RelProtoDataType)}), or null. */ - protected abstract TypeEntry getImplicitType(String name, + protected abstract @Nullable TypeEntry getImplicitType(String name, boolean caseSensitive); /** Returns table function with a given name and zero arguments that is * defined implicitly (that is, by the underlying {@link Schema} object, * not explicitly by a call to {@link #add(String, Function)}), or null. */ - protected abstract TableEntry getImplicitTableBasedOnNullaryFunction(String tableName, + protected abstract @Nullable TableEntry getImplicitTableBasedOnNullaryFunction(String tableName, boolean caseSensitive); /** Adds implicit sub-schemas to a builder. */ @@ -166,7 +172,7 @@ protected abstract void addImplicitTablesBasedOnNullaryFunctionsToBuilder( /** Returns a snapshot representation of this CalciteSchema. */ protected abstract CalciteSchema snapshot( - CalciteSchema parent, SchemaVersion version); + @Nullable CalciteSchema parent, SchemaVersion version); protected abstract boolean isCacheEnabled(); @@ -239,7 +245,7 @@ public boolean isRoot() { } /** Returns the path of an object in this schema. */ - public List path(String name) { + public List path(@Nullable String name) { final List list = new ArrayList<>(); if (name != null) { list.add(name); @@ -254,7 +260,7 @@ public List path(String name) { return ImmutableList.copyOf(Lists.reverse(list)); } - public final CalciteSchema getSubSchema(String schemaName, + public final @Nullable CalciteSchema getSubSchema(String schemaName, boolean caseSensitive) { // Check explicit schemas. //noinspection LoopStatementThatDoesntLoop @@ -269,7 +275,7 @@ public final CalciteSchema getSubSchema(String schemaName, public abstract CalciteSchema add(String name, Schema schema); /** Returns a table that materializes the given SQL statement. */ - public final TableEntry getTableBySql(String sql) { + public final @Nullable TableEntry getTableBySql(String sql) { for (TableEntry tableEntry : tableMap.map().values()) { if (tableEntry.sqls.contains(sql)) { return tableEntry; @@ -279,7 +285,7 @@ public final TableEntry getTableBySql(String sql) { } /** Returns a table with the given name. Does not look for views. */ - public final TableEntry getTable(String tableName, boolean caseSensitive) { + public final @Nullable TableEntry getTable(String tableName, boolean caseSensitive) { // Check explicit tables. //noinspection LoopStatementThatDoesntLoop for (Map.Entry entry @@ -364,7 +370,7 @@ public final NavigableSet getTypeNames() { /** Returns a type, explicit and implicit, with a given * name. Never null. */ - public final TypeEntry getType(String name, boolean caseSensitive) { + public final @Nullable TypeEntry getType(String name, boolean caseSensitive) { for (Map.Entry entry : typeMap.range(name, caseSensitive).entrySet()) { return entry.getValue(); @@ -419,7 +425,7 @@ public final NavigableMap getTablesBasedOnNullaryFunctions() { /** Returns a tables derived from explicit and implicit functions * that take zero parameters. */ - public final TableEntry getTableBasedOnNullaryFunction(String tableName, + public final @Nullable TableEntry getTableBasedOnNullaryFunction(String tableName, boolean caseSensitive) { for (Map.Entry entry : nullaryFunctionMap.range(tableName, caseSensitive).entrySet()) { @@ -625,7 +631,7 @@ CalciteSchema calciteSchema() { return CalciteSchema.this; } - @Override public SchemaPlus getParentSchema() { + @Override public @Nullable SchemaPlus getParentSchema() { return parent == null ? null : parent.plus(); } @@ -649,11 +655,11 @@ CalciteSchema calciteSchema() { throw new UnsupportedOperationException(); } - @Override public Expression getExpression(SchemaPlus parentSchema, String name) { + @Override public Expression getExpression(@Nullable SchemaPlus parentSchema, String name) { return schema.getExpression(parentSchema, name); } - @Override public Table getTable(String name) { + @Override public @Nullable Table getTable(String name) { final TableEntry entry = CalciteSchema.this.getTable(name, true); return entry == null ? null : entry.getTable(); } @@ -662,7 +668,7 @@ CalciteSchema calciteSchema() { return CalciteSchema.this.getTableNames(); } - @Override public RelProtoDataType getType(String name) { + @Override public @Nullable RelProtoDataType getType(String name) { final TypeEntry entry = CalciteSchema.this.getType(name, true); return entry == null ? null : entry.getType(); } @@ -679,14 +685,15 @@ CalciteSchema calciteSchema() { return CalciteSchema.this.getFunctionNames(); } - @Override public SchemaPlus getSubSchema(String name) { + @Override public @Nullable SchemaPlus getSubSchema(String name) { final CalciteSchema subSchema = CalciteSchema.this.getSubSchema(name, true); return subSchema == null ? null : subSchema.plus(); } @Override public Set getSubSchemaNames() { - return CalciteSchema.this.getSubSchemaMap().keySet(); + //noinspection RedundantCast + return (Set) CalciteSchema.this.getSubSchemaMap().keySet(); } @Override public SchemaPlus add(String name, Schema schema) { @@ -694,7 +701,7 @@ CalciteSchema calciteSchema() { return calciteSchema.plus(); } - @Override public T unwrap(Class clazz) { + @Override public T unwrap(Class clazz) { if (clazz.isInstance(this)) { return clazz.cast(this); } diff --git a/core/src/main/java/org/apache/calcite/jdbc/Driver.java b/core/src/main/java/org/apache/calcite/jdbc/Driver.java index ce7c350755c5..06aa637b68e1 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/Driver.java +++ b/core/src/main/java/org/apache/calcite/jdbc/Driver.java @@ -35,6 +35,8 @@ import org.apache.calcite.util.JsonBuilder; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; @@ -56,6 +58,7 @@ public class Driver extends UnregisteredDriver { new Driver().register(); } + @SuppressWarnings("method.invocation.invalid") public Driver() { super(); this.prepareFactory = createPrepareFactory(); @@ -103,7 +106,7 @@ protected Function0 createPrepareFactory() { connection.init(); } - String model(CalciteConnectionImpl connection) { + @Nullable String model(CalciteConnectionImpl connection) { String model = connection.config().model(); if (model != null) { return model; @@ -129,17 +132,17 @@ String model(CalciteConnectionImpl connection) { } if (schemaFactory != null) { final JsonBuilder json = new JsonBuilder(); - final Map root = json.map(); + final Map root = json.map(); root.put("version", "1.0"); root.put("defaultSchema", schemaName); - final List schemaList = json.list(); + final List<@Nullable Object> schemaList = json.list(); root.put("schemas", schemaList); - final Map schema = json.map(); + final Map schema = json.map(); schemaList.add(schema); schema.put("type", "custom"); schema.put("name", schemaName); schema.put("factory", schemaFactory.getClass().getName()); - final Map operandMap = json.map(); + final Map operandMap = json.map(); schema.put("operand", operandMap); for (Map.Entry entry : Util.toMap(info).entrySet()) { if (entry.getKey().startsWith("schema.")) { @@ -167,7 +170,7 @@ String model(CalciteConnectionImpl connection) { /** Creates an internal connection. */ CalciteConnection connect(CalciteSchema rootSchema, - JavaTypeFactory typeFactory) { + @Nullable JavaTypeFactory typeFactory) { return (CalciteConnection) ((CalciteFactory) factory) .newConnection(this, factory, CONNECT_STRING_PREFIX, new Properties(), rootSchema, typeFactory); @@ -175,7 +178,7 @@ CalciteConnection connect(CalciteSchema rootSchema, /** Creates an internal connection. */ CalciteConnection connect(CalciteSchema rootSchema, - JavaTypeFactory typeFactory, Properties properties) { + @Nullable JavaTypeFactory typeFactory, Properties properties) { return (CalciteConnection) ((CalciteFactory) factory) .newConnection(this, factory, CONNECT_STRING_PREFIX, properties, rootSchema, typeFactory); diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaCollation.java b/core/src/main/java/org/apache/calcite/jdbc/JavaCollation.java index 468c40fe4647..421d6a840439 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/JavaCollation.java +++ b/core/src/main/java/org/apache/calcite/jdbc/JavaCollation.java @@ -18,6 +18,9 @@ import org.apache.calcite.sql.SqlCollation; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.charset.Charset; import java.text.Collator; import java.util.Locale; @@ -55,11 +58,13 @@ private static String getStrengthString(int strengthValue) { } } - @Override protected String generateCollationName(Charset charset) { + @Override protected String generateCollationName( + @UnderInitialization JavaCollation this, + Charset charset) { return super.generateCollationName(charset) + "$JAVA_COLLATOR"; } - @Override public Collator getCollator() { + @Override public @Nullable Collator getCollator() { return collator; } } diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java b/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java index 80049cd87c20..43aaa1cade93 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java +++ b/core/src/main/java/org/apache/calcite/jdbc/JavaRecordType.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rel.type.RelRecordType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -37,10 +39,10 @@ public JavaRecordType(List fields, Class clazz) { this.clazz = Objects.requireNonNull(clazz); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof JavaRecordType - && fieldList.equals(((JavaRecordType) obj).fieldList) + && Objects.equals(fieldList, ((JavaRecordType) obj).fieldList) && clazz == ((JavaRecordType) obj).clazz; } diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java index 35cc7ad8441b..d0c9908d5639 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java @@ -38,6 +38,8 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -47,9 +49,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link JavaTypeFactory}. * @@ -115,7 +118,9 @@ private Type fieldType(Field field) { if (type instanceof SyntheticRecordType) { final SyntheticRecordType syntheticRecordType = (SyntheticRecordType) type; - return syntheticRecordType.relType; + return requireNonNull( + syntheticRecordType.relType, + () -> "relType for " + syntheticRecordType); } if (type instanceof Types.ArrayType) { final Types.ArrayType arrayType = (Types.ArrayType) type; @@ -141,7 +146,7 @@ private Type fieldType(Field field) { case PRIMITIVE: return createJavaType(clazz); case BOX: - return createJavaType(Primitive.ofBox(clazz).boxClass); + return createJavaType(Primitive.box(clazz)); default: break; } @@ -238,7 +243,7 @@ private Type fieldType(Field field) { default: break; } - return null; + return Object.class; } @Override public RelDataType toSql(RelDataType type) { @@ -283,7 +288,7 @@ public static RelDataType toSql(final RelDataTypeFactory typeFactory, } private static RelDataType toSqlTypeWithNullToAny( - final RelDataTypeFactory typeFactory, RelDataType type) { + final RelDataTypeFactory typeFactory, @Nullable RelDataType type) { if (type == null) { return typeFactory.createSqlType(SqlTypeName.ANY); } @@ -359,10 +364,10 @@ private Type createSyntheticType(RelRecordType type) { /** Synthetic record type. */ public static class SyntheticRecordType implements Types.RecordType { final List fields = new ArrayList<>(); - final RelDataType relType; + final @Nullable RelDataType relType; private final String name; - private SyntheticRecordType(RelDataType relType, String name) { + private SyntheticRecordType(@Nullable RelDataType relType, String name) { this.relType = relType; this.name = name; assert relType == null @@ -397,9 +402,9 @@ private static class RecordFieldImpl implements Types.RecordField { Type type, boolean nullable, int modifiers) { - this.syntheticType = Objects.requireNonNull(syntheticType); - this.name = Objects.requireNonNull(name); - this.type = Objects.requireNonNull(type); + this.syntheticType = requireNonNull(syntheticType); + this.name = requireNonNull(name); + this.type = requireNonNull(type); this.nullable = nullable; this.modifiers = modifiers; assert !(nullable && Primitive.is(type)) @@ -422,7 +427,7 @@ private static class RecordFieldImpl implements Types.RecordField { return nullable; } - @Override public Object get(Object o) { + @Override public @Nullable Object get(@Nullable Object o) { throw new UnsupportedOperationException(); } diff --git a/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java index ab08915b408d..564240e22163 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java +++ b/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java @@ -30,6 +30,8 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.List; @@ -42,16 +44,21 @@ class SimpleCalciteSchema extends CalciteSchema { * *

Use {@link CalciteSchema#createRootSchema(boolean)} * or {@link #add(String, Schema)}. */ - SimpleCalciteSchema(CalciteSchema parent, Schema schema, String name) { + SimpleCalciteSchema(@Nullable CalciteSchema parent, Schema schema, String name) { this(parent, schema, name, null, null, null, null, null, null, null, null); } - private SimpleCalciteSchema(CalciteSchema parent, Schema schema, - String name, NameMap subSchemaMap, - NameMap tableMap, NameMap latticeMap, NameMap typeMap, - NameMultimap functionMap, NameSet functionNames, - NameMap nullaryFunctionMap, - List> path) { + private SimpleCalciteSchema(@Nullable CalciteSchema parent, + Schema schema, + String name, + @Nullable NameMap subSchemaMap, + @Nullable NameMap tableMap, + @Nullable NameMap latticeMap, + @Nullable NameMap typeMap, + @Nullable NameMultimap functionMap, + @Nullable NameSet functionNames, + @Nullable NameMap nullaryFunctionMap, + @Nullable List> path) { super(parent, schema, name, subSchemaMap, tableMap, latticeMap, typeMap, functionMap, functionNames, nullaryFunctionMap, path); } @@ -67,7 +74,7 @@ private SimpleCalciteSchema(CalciteSchema parent, Schema schema, return calciteSchema; } - @Override protected CalciteSchema getImplicitSubSchema(String schemaName, + @Override protected @Nullable CalciteSchema getImplicitSubSchema(String schemaName, boolean caseSensitive) { // Check implicit schemas. Schema s = schema.getSubSchema(schemaName); @@ -77,7 +84,7 @@ private SimpleCalciteSchema(CalciteSchema parent, Schema schema, return null; } - @Override protected TableEntry getImplicitTable(String tableName, + @Override protected @Nullable TableEntry getImplicitTable(String tableName, boolean caseSensitive) { // Check implicit tables. Table table = schema.getTable(tableName); @@ -87,7 +94,7 @@ private SimpleCalciteSchema(CalciteSchema parent, Schema schema, return null; } - @Override protected TypeEntry getImplicitType(String name, boolean caseSensitive) { + @Override protected @Nullable TypeEntry getImplicitType(String name, boolean caseSensitive) { // Check implicit types. RelProtoDataType type = schema.getType(name); if (type != null) { @@ -154,7 +161,7 @@ private SimpleCalciteSchema(CalciteSchema parent, Schema schema, } } - @Override protected TableEntry getImplicitTableBasedOnNullaryFunction(String tableName, + @Override protected @Nullable TableEntry getImplicitTableBasedOnNullaryFunction(String tableName, boolean caseSensitive) { Collection functions = schema.getFunctions(tableName); if (functions != null) { @@ -169,7 +176,8 @@ private SimpleCalciteSchema(CalciteSchema parent, Schema schema, return null; } - @Override protected CalciteSchema snapshot(CalciteSchema parent, SchemaVersion version) { + @Override protected CalciteSchema snapshot(@Nullable CalciteSchema parent, + SchemaVersion version) { CalciteSchema snapshot = new SimpleCalciteSchema(parent, schema.snapshot(version), name, null, tableMap, latticeMap, typeMap, functionMap, functionNames, nullaryFunctionMap, getPath()); diff --git a/core/src/main/java/org/apache/calcite/materialize/Lattice.java b/core/src/main/java/org/apache/calcite/materialize/Lattice.java index 5fb86353c586..29383a07285f 100644 --- a/core/src/main/java/org/apache/calcite/materialize/Lattice.java +++ b/core/src/main/java/org/apache/calcite/materialize/Lattice.java @@ -65,6 +65,11 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Ordering; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -80,15 +85,15 @@ import java.util.function.Function; import java.util.function.IntFunction; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; /** * Structure that allows materialized views based upon a star schema to be * recognized and recommended. */ -@ParametersAreNonnullByDefault public class Lattice { public final CalciteSchema rootSchema; public final LatticeRootNode rootNode; @@ -109,13 +114,13 @@ private Lattice(CalciteSchema rootSchema, LatticeRootNode rootNode, ImmutableSortedSet defaultMeasures, ImmutableList tiles, ImmutableListMultimap columnUses) { this.rootSchema = rootSchema; - this.rootNode = Objects.requireNonNull(rootNode); - this.columns = Objects.requireNonNull(columns); + this.rootNode = requireNonNull(rootNode); + this.columns = requireNonNull(columns); this.auto = auto; this.algorithm = algorithm; this.algorithmMaxMillis = algorithmMaxMillis; this.defaultMeasures = defaultMeasures.asList(); // unique and sorted - this.tiles = Objects.requireNonNull(tiles); + this.tiles = requireNonNull(tiles); this.columnUses = columnUses; assert isValid(Litmus.THROW); @@ -127,8 +132,10 @@ private Lattice(CalciteSchema rootSchema, LatticeRootNode rootNode, } Preconditions.checkArgument(rowCountEstimate > 0d); this.rowCountEstimate = rowCountEstimate; - this.statisticProvider = - Objects.requireNonNull(statisticProviderFactory.apply(this)); + @SuppressWarnings("argument.type.incompatible") + LatticeStatisticProvider statisticProvider = + requireNonNull(statisticProviderFactory.apply(this)); + this.statisticProvider = statisticProvider; } /** Creates a Lattice. */ @@ -136,7 +143,10 @@ public static Lattice create(CalciteSchema schema, String sql, boolean auto) { return builder(schema, sql).auto(auto).build(); } - private boolean isValid(Litmus litmus) { + @RequiresNonNull({"rootNode", "defaultMeasures", "columns"}) + private boolean isValid( + @UnknownInitialization Lattice this, + Litmus litmus) { if (!rootNode.isValid(litmus)) { return false; } @@ -151,7 +161,7 @@ private boolean isValid(Litmus litmus) { return litmus.succeed(); } - private static void populateAliases(SqlNode from, List aliases, + private static void populateAliases(SqlNode from, List<@Nullable String> aliases, @Nullable String current) { if (from instanceof SqlJoin) { SqlJoin join = (SqlJoin) from; @@ -168,13 +178,13 @@ private static void populateAliases(SqlNode from, List aliases, } } - private static boolean populate(List nodes, List tempLinks, + private static boolean populate(List nodes, List tempLinks, RelNode rel) { if (nodes.isEmpty() && rel instanceof LogicalProject) { return populate(nodes, tempLinks, ((LogicalProject) rel).getInput()); } if (rel instanceof TableScan) { - nodes.add(rel); + nodes.add((TableScan) rel); return true; } if (rel instanceof LogicalJoin) { @@ -195,7 +205,7 @@ private static boolean populate(List nodes, List tempLinks, } /** Converts an "t1.c1 = t2.c2" expression into two (input, field) pairs. */ - private static int[][] grab(List leaves, RexNode rex) { + private static int[][] grab(List leaves, RexNode rex) { switch (rex.getKind()) { case EQUALS: break; @@ -209,7 +219,7 @@ private static int[][] grab(List leaves, RexNode rex) { } /** Converts an expression into an (input, field) pair. */ - private static int[] inputField(List leaves, RexNode rex) { + private static int[] inputField(List leaves, RexNode rex) { if (!(rex instanceof RexInputRef)) { throw new RuntimeException("only equi-join of columns allowed: " + rex); } @@ -325,8 +335,11 @@ public String sql(ImmutableBitSet groupSet, boolean group, buf.append("\nJOIN "); } dialect.quoteIdentifier(buf, node.table.t.getQualifiedName()); - buf.append(" AS "); - dialect.quoteIdentifier(buf, node.alias); + String alias = node.alias; + if (alias != null) { + buf.append(" AS "); + dialect.quoteIdentifier(buf, alias); + } if (node instanceof LatticeChildNode) { final LatticeChildNode node1 = (LatticeChildNode) node; buf.append(" ON "); @@ -374,7 +387,10 @@ public String countSql(ImmutableBitSet groupSet) { public StarTable createStarTable() { final List tables = new ArrayList<>(); for (LatticeNode node : rootNode.descendants) { - tables.add(node.table.t.unwrap(Table.class)); + tables.add( + requireNonNull( + node.table.t.unwrap(Table.class), + () -> "can't get table for " + node.table.t)); } return StarTable.of(this, tables); } @@ -456,7 +472,7 @@ public List uniqueColumnNames() { Pair columnToPathOffset(BaseColumn c) { for (Pair p : Pair.zip(rootNode.descendants, rootNode.paths)) { - if (p.left.alias.equals(c.table)) { + if (Objects.equals(p.left.alias, c.table)) { return Pair.of(p.right, c.ordinal - p.left.startCol); } } @@ -523,9 +539,9 @@ Vertex getSource() { /** Vertex in the temporary graph. */ private static class Vertex { final LatticeTable table; - final String alias; + final @Nullable String alias; - private Vertex(LatticeTable table, String alias) { + private Vertex(LatticeTable table, @Nullable String alias) { this.table = table; this.alias = alias; } @@ -541,13 +557,13 @@ private Vertex(LatticeTable table, String alias) { public static class Measure implements Comparable { public final SqlAggFunction agg; public final boolean distinct; - @Nullable public final String name; + public final @Nullable String name; public final ImmutableList args; public final String digest; public Measure(SqlAggFunction agg, boolean distinct, @Nullable String name, Iterable args) { - this.agg = Objects.requireNonNull(agg); + this.agg = requireNonNull(agg); this.distinct = distinct; this.name = name; this.args = ImmutableList.copyOf(args); @@ -571,7 +587,7 @@ public Measure(SqlAggFunction agg, boolean distinct, @Nullable String name, this.digest = b.toString(); } - @Override public int compareTo(@Nonnull Measure measure) { + @Override public int compareTo(Measure measure) { int c = compare(args, measure.args); if (c == 0) { c = agg.getName().compareTo(measure.agg.getName()); @@ -590,7 +606,7 @@ public Measure(SqlAggFunction agg, boolean distinct, @Nullable String name, return Objects.hash(agg, args); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof Measure && this.agg.equals(((Measure) obj).agg) @@ -643,7 +659,7 @@ public abstract static class Column implements Comparable { private Column(int ordinal, String alias) { this.ordinal = ordinal; - this.alias = Objects.requireNonNull(alias); + this.alias = requireNonNull(alias); } /** Converts a list of columns to a bit set of their ordinals. */ @@ -663,7 +679,7 @@ static ImmutableBitSet toBitSet(List columns) { return ordinal; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof Column && this.ordinal == ((Column) obj).ordinal; @@ -672,7 +688,7 @@ static ImmutableBitSet toBitSet(List columns) { public abstract void toSql(SqlWriter writer); /** The alias that SQL would give to this expression. */ - public abstract String defaultAlias(); + public abstract @Nullable String defaultAlias(); } /** Column in a lattice. Columns are identified by table alias and @@ -684,12 +700,12 @@ public static class BaseColumn extends Column { /** Name of the column. Unique within the table reference, but not * necessarily within the lattice. */ - @Nonnull public final String column; + public final String column; private BaseColumn(int ordinal, String table, String column, String alias) { super(ordinal, alias); - this.table = Objects.requireNonNull(table); - this.column = Objects.requireNonNull(column); + this.table = requireNonNull(table); + this.column = requireNonNull(column); } @Override public String toString() { @@ -711,8 +727,8 @@ public List identifiers() { /** Column in a lattice that is based upon a SQL expression. */ public static class DerivedColumn extends Column { - @Nonnull public final RexNode e; - @Nonnull final List tables; + public final RexNode e; + final List tables; private DerivedColumn(int ordinal, String alias, RexNode e, List tables) { @@ -729,7 +745,7 @@ private DerivedColumn(int ordinal, String alias, RexNode e, writer.write(e); } - @Override public String defaultAlias() { + @Override public @Nullable String defaultAlias() { // there is no default alias for an expression return null; } @@ -778,32 +794,34 @@ public static class Builder { private boolean algorithm = false; private long algorithmMaxMillis = -1; private boolean auto = true; - private Double rowCountEstimate; - private String statisticProvider; - private Map derivedColumnsByName = + private @MonotonicNonNull Double rowCountEstimate; + private @Nullable String statisticProvider; + private final Map derivedColumnsByName = new LinkedHashMap<>(); public Builder(LatticeSpace space, CalciteSchema schema, String sql) { - this.rootSchema = Objects.requireNonNull(schema.root()); + this.rootSchema = requireNonNull(schema.root()); Preconditions.checkArgument(rootSchema.isRoot(), "must be root schema"); CalcitePrepare.ConvertResult parsed = Schemas.convert(MaterializedViewTable.MATERIALIZATION_CONNECTION, schema, schema.path(null), sql); // Walk the join tree. - List relNodes = new ArrayList<>(); + List relNodes = new ArrayList<>(); List tempLinks = new ArrayList<>(); populate(relNodes, tempLinks, parsed.root.rel); // Get aliases. - List aliases = new ArrayList<>(); - populateAliases(((SqlSelect) parsed.sqlNode).getFrom(), aliases, null); + List<@Nullable String> aliases = new ArrayList<>(); + SqlNode from = ((SqlSelect) parsed.sqlNode).getFrom(); + assert from != null : "from must not be null"; + populateAliases(from, aliases, null); // Build a graph. final DirectedGraph graph = DefaultDirectedGraph.create(Edge.FACTORY); final List vertices = new ArrayList<>(); - for (Pair p : Pair.zip(relNodes, aliases)) { + for (Pair p : Pair.zip(relNodes, aliases)) { final LatticeTable table = space.register(p.left.getTable()); final Vertex vertex = new Vertex(table, p.right); graph.addVertex(vertex); @@ -814,7 +832,7 @@ public Builder(LatticeSpace space, CalciteSchema schema, String sql) { final Vertex target = vertices.get(tempLink[1][0]); Edge edge = graph.getEdge(source, target); if (edge == null) { - edge = graph.addEdge(source, target); + edge = castNonNull(graph.addEdge(source, target)); } edge.pairs.add(IntPair.of(tempLink[0][1], tempLink[1][1])); } @@ -906,7 +924,7 @@ public Builder rowCountEstimate(double rowCountEstimate) { /** Sets the "statisticProvider" attribute. * *

If not set, the lattice will use {@link Lattices#CACHED_SQL}. */ - public Builder statisticProvider(String statisticProvider) { + public Builder statisticProvider(@Nullable String statisticProvider) { this.statisticProvider = statisticProvider; return this; } @@ -1109,7 +1127,7 @@ void fixUp(MutableNode node) { final String alias = SqlValidatorUtil.uniquify(name, columnAliases, SqlValidatorUtil.ATTEMPT_SUGGESTER); final BaseColumn column = - new BaseColumn(c++, node.alias, name, alias); + new BaseColumn(c++, castNonNull(node.alias), name, alias); columnList.add(column); columnAliasList.put(name, column); // name before it is made unique } @@ -1132,8 +1150,8 @@ public static class Tile { public Tile(ImmutableList measures, ImmutableList dimensions) { - this.measures = Objects.requireNonNull(measures); - this.dimensions = Objects.requireNonNull(dimensions); + this.measures = requireNonNull(measures); + this.dimensions = requireNonNull(dimensions); assert Ordering.natural().isStrictlyOrdered(dimensions); assert Ordering.natural().isStrictlyOrdered(measures); bitSet = Column.toBitSet(dimensions); diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeChildNode.java b/core/src/main/java/org/apache/calcite/materialize/LatticeChildNode.java index 2782cb81cde6..a55c8cd2d869 100644 --- a/core/src/main/java/org/apache/calcite/materialize/LatticeChildNode.java +++ b/core/src/main/java/org/apache/calcite/materialize/LatticeChildNode.java @@ -21,7 +21,8 @@ import com.google.common.collect.ImmutableList; import java.util.List; -import java.util.Objects; + +import static java.util.Objects.requireNonNull; /** Non-root node in a {@link Lattice}. */ public class LatticeChildNode extends LatticeNode { @@ -31,8 +32,8 @@ public class LatticeChildNode extends LatticeNode { LatticeChildNode(LatticeSpace space, LatticeNode parent, MutableNode mutableNode) { super(space, parent, mutableNode); - this.parent = Objects.requireNonNull(parent); - this.link = ImmutableList.copyOf(mutableNode.step.keys); + this.parent = requireNonNull(parent, "parent"); + this.link = ImmutableList.copyOf(requireNonNull(mutableNode.step, "step").keys); } @Override void use(List usedNodes) { diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeNode.java b/core/src/main/java/org/apache/calcite/materialize/LatticeNode.java index 4b739cc9391f..1e4e458e457b 100644 --- a/core/src/main/java/org/apache/calcite/materialize/LatticeNode.java +++ b/core/src/main/java/org/apache/calcite/materialize/LatticeNode.java @@ -22,8 +22,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.initialization.qual.Initialized; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; -import java.util.Objects; + +import static java.util.Objects.requireNonNull; /** Source relation of a lattice. * @@ -34,7 +38,7 @@ public abstract class LatticeNode { public final LatticeTable table; final int startCol; final int endCol; - public final String alias; + public final @Nullable String alias; private final ImmutableList children; public final String digest; @@ -42,8 +46,8 @@ public abstract class LatticeNode { * *

The {@code parent} and {@code mutableNode} arguments are used only * during construction. */ - LatticeNode(LatticeSpace space, LatticeNode parent, MutableNode mutableNode) { - this.table = Objects.requireNonNull(mutableNode.table); + LatticeNode(LatticeSpace space, @Nullable LatticeNode parent, MutableNode mutableNode) { + this.table = requireNonNull(mutableNode.table); this.startCol = mutableNode.startCol; this.endCol = mutableNode.endCol; this.alias = mutableNode.alias; @@ -55,7 +59,7 @@ public abstract class LatticeNode { if (parent != null) { sb.append(':'); int i = 0; - for (IntPair p : mutableNode.step.keys) { + for (IntPair p : requireNonNull(mutableNode.step, "mutableNode.step").keys) { if (i++ > 0) { sb.append(","); } @@ -72,7 +76,8 @@ public abstract class LatticeNode { if (i++ > 0) { sb.append(' '); } - final LatticeChildNode node = + @SuppressWarnings({"argument.type.incompatible", "assignment.type.incompatible"}) + final @Initialized LatticeChildNode node = new LatticeChildNode(space, this, mutableChild); sb.append(node.digest); b.add(node); diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeRootNode.java b/core/src/main/java/org/apache/calcite/materialize/LatticeRootNode.java index 607b882de7bb..ecf244ad2903 100644 --- a/core/src/main/java/org/apache/calcite/materialize/LatticeRootNode.java +++ b/core/src/main/java/org/apache/calcite/materialize/LatticeRootNode.java @@ -29,6 +29,7 @@ public class LatticeRootNode extends LatticeNode { public final ImmutableList descendants; final ImmutableList paths; + @SuppressWarnings("method.invocation.invalid") LatticeRootNode(LatticeSpace space, MutableNode mutableNode) { super(space, null, mutableNode); diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeSpace.java b/core/src/main/java/org/apache/calcite/materialize/LatticeSpace.java index e58641088785..1cbcaa1e47b0 100644 --- a/core/src/main/java/org/apache/calcite/materialize/LatticeSpace.java +++ b/core/src/main/java/org/apache/calcite/materialize/LatticeSpace.java @@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; + import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -38,7 +40,8 @@ class LatticeSpace { final SqlStatisticProvider statisticProvider; private final Map, LatticeTable> tableMap = new HashMap<>(); - final AttributedDirectedGraph g = + @SuppressWarnings("assignment.type.incompatible") + final @NotOnlyInitialized AttributedDirectedGraph g = new AttributedDirectedGraph<>(new Step.Factory(this)); private final Map, String> simpleTableNames = new HashMap<>(); private final Set simpleNames = new HashSet<>(); @@ -164,7 +167,9 @@ public String fieldName(LatticeTable table, int field) { if (field < fieldCount) { return fieldList.get(field).getName(); } else { - return tableExpressions.get(table).get(field - fieldCount).toString(); + List rexNodes = tableExpressions.get(table); + assert rexNodes != null : "no expressions found for table " + table; + return rexNodes.get(field - fieldCount).toString(); } } } diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java b/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java index bae862e88e49..aa2bf80e1831 100644 --- a/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java +++ b/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java @@ -35,6 +35,7 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.runtime.FlatLists; import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.tools.FrameworkConfig; import org.apache.calcite.util.CompositeList; @@ -53,6 +54,8 @@ import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -62,11 +65,10 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.function.Function; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; + +import static java.util.Objects.requireNonNull; /** * Algorithm that suggests a set of lattices. @@ -111,7 +113,9 @@ public RexNode toRex(LatticeTable table, int column) { if (column < fieldList.size()) { return new RexInputRef(column, fieldList.get(column).getType()); } else { - return space.tableExpressions.get(table).get(column - fieldList.size()); + return requireNonNull(space.tableExpressions.get(table), + () -> "space.tableExpressions.get(table) is null for " + table) + .get(column - fieldList.size()); } } @@ -171,7 +175,7 @@ private void addFrame(Query q, Frame frame, List lattices) { } // Translate the query graph to mutable nodes - final Map nodes = new IdentityHashMap<>(); + final Map nodes = new IdentityHashMap<>(); final Map nodesByParent = new HashMap<>(); final List rootNodes = new ArrayList<>(); for (TableRef tableRef : TopologicalOrderIterator.of(g)) { @@ -186,7 +190,7 @@ private void addFrame(Query q, Frame frame, List lattices) { final StepRef edge = edges.get(0); final MutableNode parent = nodes.get(edge.source()); final List key = - ImmutableList.of(parent, tableRef.table, edge.step.keys); + FlatLists.of(parent, tableRef.table, edge.step.keys); final MutableNode existingNode = nodesByParent.get(key); if (existingNode == null) { node = new MutableNode(tableRef.table, parent, edge.step); @@ -198,6 +202,9 @@ private void addFrame(Query q, Frame frame, List lattices) { default: for (StepRef edge2 : edges) { final MutableNode parent2 = nodes.get(edge2.source()); + requireNonNull( + parent2, + () -> "parent for " + edge2.source()); final MutableNode node2 = new MutableNode(tableRef.table, parent2, edge2.step); parent2.children.add(node2); @@ -279,7 +286,7 @@ private static String deriveAlias(MutableMeasure measure, // User specified an alias. Use that. return derivedColRef.alias; } - String alias = measure.name; + String alias = requireNonNull(measure.name, "measure.name"); if (alias.contains("$")) { // User did not specify an alias for the aggregate function, and it got a // system-generated name like 'EXPR$2'. Don't try to derive anything from @@ -412,7 +419,7 @@ private void frames(List frames, final Query q, RelNode r) { } } - private Frame frame(final Query q, RelNode r) { + private @Nullable Frame frame(final Query q, RelNode r) { if (r instanceof Sort) { final Sort sort = (Sort) r; return frame(q, sort.getInput()); @@ -429,11 +436,12 @@ private Frame frame(final Query q, RelNode r) { for (AggregateCall call : aggregate.getAggCallList()) { measures.add( new MutableMeasure(call.getAggregation(), call.isDistinct(), - Util.transform(call.getArgList(), h::column), call.name)); + Util.transform(call.getArgList(), h::column), + call.name)); } final int fieldCount = r.getRowType().getFieldCount(); return new Frame(fieldCount, h.hops, measures, ImmutableList.of(h)) { - @Override ColRef column(int offset) { + @Override @Nullable ColRef column(int offset) { if (offset < aggregate.getGroupSet().cardinality()) { return h.column(aggregate.getGroupSet().nth(offset)); } @@ -448,25 +456,27 @@ private Frame frame(final Query q, RelNode r) { } final int fieldCount = r.getRowType().getFieldCount(); return new Frame(fieldCount, h.hops, h.measures, ImmutableList.of(h)) { - final List columns; + final List<@Nullable ColRef> columns; { - final ImmutableNullableList.Builder columnBuilder = + final ImmutableNullableList.Builder<@Nullable ColRef> columnBuilder = ImmutableNullableList.builder(); for (Pair p : project.getNamedProjects()) { - columnBuilder.add(toColRef(p.left, p.right)); + @SuppressWarnings("method.invocation.invalid") + ColRef colRef = toColRef(p.left, p.right); + columnBuilder.add(colRef); } columns = columnBuilder.build(); } - @Override ColRef column(int offset) { + @Override @Nullable ColRef column(int offset) { return columns.get(offset); } /** Converts an expression to a base or derived column reference. * The alias is optional, but if the derived column reference becomes * a dimension or measure, the alias will be used to choose a name. */ - private ColRef toColRef(RexNode e, String alias) { + private @Nullable ColRef toColRef(RexNode e, String alias) { if (e instanceof RexInputRef) { return h.column(((RexInputRef) e).getIndex()); } @@ -515,7 +525,7 @@ private ColRef toColRef(RexNode e, String alias) { return new Frame(fieldCount, builder.build(), CompositeList.of(left.measures, right.measures), ImmutableList.of(left, right)) { - @Override ColRef column(int offset) { + @Override @Nullable ColRef column(int offset) { if (offset < leftCount) { return left.column(offset); } else { @@ -598,7 +608,7 @@ abstract static class Frame { this(columnCount, hops, measures, collectTableRefs(inputs, hops)); } - abstract ColRef column(int offset); + abstract @Nullable ColRef column(int offset); @Override public String toString() { return "Frame(" + hops + ")"; @@ -623,7 +633,7 @@ private static class TableRef { private final int ordinalInQuery; private TableRef(LatticeTable table, int ordinalInQuery) { - this.table = Objects.requireNonNull(table); + this.table = requireNonNull(table); this.ordinalInQuery = ordinalInQuery; } @@ -631,7 +641,7 @@ private TableRef(LatticeTable table, int ordinalInQuery) { return ordinalInQuery; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof TableRef && ordinalInQuery == ((TableRef) obj).ordinalInQuery; @@ -649,7 +659,7 @@ private static class StepRef extends DefaultEdge { StepRef(TableRef source, TableRef target, Step step, int ordinalInQuery) { super(source, target); - this.step = Objects.requireNonNull(step); + this.step = requireNonNull(step); this.ordinalInQuery = ordinalInQuery; } @@ -657,7 +667,7 @@ private static class StepRef extends DefaultEdge { return ordinalInQuery; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof StepRef && ((StepRef) obj).ordinalInQuery == ordinalInQuery; @@ -764,8 +774,8 @@ private BaseColRef(TableRef t, int c) { /** Reference to a derived column (that is, an expression). */ private static class DerivedColRef extends ColRef { - @Nonnull final List tableRefs; - @Nonnull final RexNode e; + final List tableRefs; + final RexNode e; final String alias; DerivedColRef(Iterable tableRefs, RexNode e, String alias) { @@ -800,11 +810,11 @@ private static class SingleTableDerivedColRef extends DerivedColRef private static class MutableMeasure { final SqlAggFunction aggregate; final boolean distinct; - final List arguments; - final String name; + final List arguments; + final @Nullable String name; private MutableMeasure(SqlAggFunction aggregate, boolean distinct, - List arguments, @Nullable String name) { + List arguments, @Nullable String name) { this.aggregate = aggregate; this.arguments = arguments; this.distinct = distinct; diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeTable.java b/core/src/main/java/org/apache/calcite/materialize/LatticeTable.java index 0ea30a40b7a8..fb2bd357ccda 100644 --- a/core/src/main/java/org/apache/calcite/materialize/LatticeTable.java +++ b/core/src/main/java/org/apache/calcite/materialize/LatticeTable.java @@ -20,13 +20,14 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; -import javax.annotation.Nonnull; /** Table registered in the graph. */ public class LatticeTable { - @Nonnull public final RelOptTable t; - @Nonnull public final String alias; + public final RelOptTable t; + public final String alias; LatticeTable(RelOptTable table) { t = Objects.requireNonNull(table); @@ -37,7 +38,7 @@ public class LatticeTable { return t.getQualifiedName().hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof LatticeTable && t.getQualifiedName().equals( diff --git a/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java b/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java index 7183747f92be..839c576228ee 100644 --- a/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java +++ b/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java @@ -23,6 +23,8 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,10 +54,10 @@ class MaterializationActor { static class Materialization { final MaterializationKey key; final CalciteSchema rootSchema; - CalciteSchema.TableEntry materializedTable; + CalciteSchema.@Nullable TableEntry materializedTable; final String sql; final RelDataType rowType; - final List viewSchemaPath; + final @Nullable List viewSchemaPath; /** Creates a materialization. * @@ -70,10 +72,10 @@ static class Materialization { */ Materialization(MaterializationKey key, CalciteSchema rootSchema, - CalciteSchema.TableEntry materializedTable, + CalciteSchema.@Nullable TableEntry materializedTable, String sql, RelDataType rowType, - List viewSchemaPath) { + @Nullable List viewSchemaPath) { this.key = key; this.rootSchema = Objects.requireNonNull(rootSchema); Preconditions.checkArgument(rootSchema.isRoot(), "must be root schema"); @@ -89,20 +91,20 @@ static class Materialization { static class QueryKey { final String sql; final CalciteSchema schema; - final List path; + final @Nullable List path; - QueryKey(String sql, CalciteSchema schema, List path) { + QueryKey(String sql, CalciteSchema schema, @Nullable List path) { this.sql = sql; this.schema = schema; this.path = path; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof QueryKey && sql.equals(((QueryKey) obj).sql) && schema.equals(((QueryKey) obj).schema) - && path.equals(((QueryKey) obj).path); + && Objects.equals(path, ((QueryKey) obj).path); } @Override public int hashCode() { diff --git a/core/src/main/java/org/apache/calcite/materialize/MaterializationKey.java b/core/src/main/java/org/apache/calcite/materialize/MaterializationKey.java index 7f6b61a4af1c..3aa39d0007ab 100644 --- a/core/src/main/java/org/apache/calcite/materialize/MaterializationKey.java +++ b/core/src/main/java/org/apache/calcite/materialize/MaterializationKey.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.materialize; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.Serializable; import java.util.UUID; @@ -32,7 +34,7 @@ public class MaterializationKey implements Serializable { return uuid.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof MaterializationKey && uuid.equals(((MaterializationKey) obj).uuid); diff --git a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java index 17f9a169d9f1..39f1df232df4 100644 --- a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java +++ b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java @@ -40,6 +40,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Comparator; @@ -50,6 +52,10 @@ import java.util.PriorityQueue; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Manages the collection of materialized tables known to the system, * and the process by which they become valid and invalid. @@ -59,7 +65,7 @@ public class MaterializationService { new MaterializationService(); /** For testing. */ - private static final ThreadLocal THREAD_INSTANCE = + private static final ThreadLocal<@Nullable MaterializationService> THREAD_INSTANCE = ThreadLocal.withInitial(MaterializationService::new); private static final Comparator> C = @@ -67,10 +73,19 @@ public class MaterializationService { // We prefer rolling up from the table with the fewest rows. final Table t0 = o0.left.getTable(); final Table t1 = o1.left.getTable(); - int c = Double.compare(t0.getStatistic().getRowCount(), - t1.getStatistic().getRowCount()); - if (c != 0) { - return c; + Double rowCount0 = t0.getStatistic().getRowCount(); + Double rowCount1 = t1.getStatistic().getRowCount(); + if (rowCount0 != null && rowCount1 != null) { + int c = Double.compare(rowCount0, rowCount1); + if (c != 0) { + return c; + } + } else if (rowCount0 == null) { + // Unknown is worse than known + return 1; + } else { + // rowCount1 == null => Unknown is worse than known + return -1; } // Tie-break based on table name. return o0.left.name.compareTo(o1.left.name); @@ -83,17 +98,17 @@ private MaterializationService() { } /** Defines a new materialization. Returns its key. */ - public MaterializationKey defineMaterialization(final CalciteSchema schema, - TileKey tileKey, String viewSql, List viewSchemaPath, - final String suggestedTableName, boolean create, boolean existing) { + public @Nullable MaterializationKey defineMaterialization(final CalciteSchema schema, + @Nullable TileKey tileKey, String viewSql, @Nullable List viewSchemaPath, + final @Nullable String suggestedTableName, boolean create, boolean existing) { return defineMaterialization(schema, tileKey, viewSql, viewSchemaPath, suggestedTableName, tableFactory, create, existing); } /** Defines a new materialization. Returns its key. */ - public MaterializationKey defineMaterialization(final CalciteSchema schema, - TileKey tileKey, String viewSql, List viewSchemaPath, - String suggestedTableName, TableFactory tableFactory, boolean create, + public @Nullable MaterializationKey defineMaterialization(final CalciteSchema schema, + @Nullable TileKey tileKey, String viewSql, @Nullable List viewSchemaPath, + @Nullable String suggestedTableName, TableFactory tableFactory, boolean create, boolean existing) { final MaterializationActor.QueryKey queryKey = new MaterializationActor.QueryKey(viewSql, schema, viewSchemaPath); @@ -111,6 +126,7 @@ public MaterializationKey defineMaterialization(final CalciteSchema schema, // If the user says the materialization exists, first try to find a table // with the name and if none can be found, lookup a view in the schema if (existing) { + requireNonNull(suggestedTableName, "suggestedTableName"); tableEntry = schema.getTable(suggestedTableName, true); if (tableEntry == null) { tableEntry = schema.getTableBasedOnNullaryFunction(suggestedTableName, true); @@ -152,7 +168,7 @@ public MaterializationKey defineMaterialization(final CalciteSchema schema, /** Checks whether a materialization is valid, and if so, returns the table * where the data are stored. */ - public CalciteSchema.TableEntry checkValid(MaterializationKey key) { + public CalciteSchema.@Nullable TableEntry checkValid(MaterializationKey key) { final MaterializationActor.Materialization materialization = actor.keyMap.get(key); if (materialization != null) { @@ -169,14 +185,14 @@ public CalciteSchema.TableEntry checkValid(MaterializationKey key) { * during the recursive SQL that populates a materialization. Otherwise a * materialization would try to create itself to populate itself! */ - public Pair defineTile(Lattice lattice, + public @Nullable Pair defineTile(Lattice lattice, ImmutableBitSet groupSet, List measureList, CalciteSchema schema, boolean create, boolean exact) { return defineTile(lattice, groupSet, measureList, schema, create, exact, "m" + groupSet, tableFactory); } - public Pair defineTile(Lattice lattice, + public @Nullable Pair defineTile(Lattice lattice, ImmutableBitSet groupSet, List measureList, CalciteSchema schema, boolean create, boolean exact, String suggestedTableName, TableFactory tableFactory) { @@ -314,7 +330,10 @@ public List query(CalciteSchema rootSchema) { && materialization.materializedTable != null) { list.add( new Prepare.Materialization(materialization.materializedTable, - materialization.sql, materialization.viewSchemaPath)); + materialization.sql, + requireNonNull(materialization.viewSchemaPath, + () -> "materialization.viewSchemaPath is null for " + + materialization.materializedTable))); } } return list; @@ -350,7 +369,7 @@ public void removeMaterialization(MaterializationKey key) { */ public interface TableFactory { Table createTable(CalciteSchema schema, String viewSql, - List viewSchemaPath); + @Nullable List viewSchemaPath); } /** @@ -359,7 +378,7 @@ Table createTable(CalciteSchema schema, String viewSql, */ public static class DefaultTableFactory implements TableFactory { @Override public Table createTable(CalciteSchema schema, String viewSql, - List viewSchemaPath) { + @Nullable List viewSchemaPath) { final CalciteConnection connection = CalciteMetaImpl.connect(schema.root(), null); final ImmutableMap map = @@ -368,14 +387,14 @@ public static class DefaultTableFactory implements TableFactory { final CalcitePrepare.CalciteSignature calciteSignature = Schemas.prepare(connection, schema, viewSchemaPath, viewSql, map); return CloneSchema.createCloneTable(connection.getTypeFactory(), - RelDataTypeImpl.proto(calciteSignature.rowType), + RelDataTypeImpl.proto(castNonNull(calciteSignature.rowType)), calciteSignature.getCollationList(), Util.transform(calciteSignature.columns, column -> column.type.rep), new AbstractQueryable() { @Override public Enumerator enumerator() { final DataContext dataContext = Schemas.createDataContext(connection, - calciteSignature.rootSchema.plus()); + requireNonNull(calciteSignature.rootSchema, "rootSchema").plus()); return calciteSignature.enumerable(dataContext).enumerator(); } @@ -394,7 +413,7 @@ public static class DefaultTableFactory implements TableFactory { @Override public Iterator iterator() { final DataContext dataContext = Schemas.createDataContext(connection, - calciteSignature.rootSchema.plus()); + requireNonNull(calciteSignature.rootSchema, "rootSchema").plus()); return calciteSignature.enumerable(dataContext).iterator(); } }); diff --git a/core/src/main/java/org/apache/calcite/materialize/MutableNode.java b/core/src/main/java/org/apache/calcite/materialize/MutableNode.java index cc7b971b1a92..302c1a285284 100644 --- a/core/src/main/java/org/apache/calcite/materialize/MutableNode.java +++ b/core/src/main/java/org/apache/calcite/materialize/MutableNode.java @@ -20,6 +20,8 @@ import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -32,11 +34,11 @@ * built. */ class MutableNode { final LatticeTable table; - final MutableNode parent; - final Step step; + final @Nullable MutableNode parent; + final @Nullable Step step; int startCol; int endCol; - String alias; + @Nullable String alias; final List children = new ArrayList<>(); /** Comparator for sorting children within a parent. */ @@ -46,7 +48,7 @@ class MutableNode { @Override public int compare(MutableNode o1, MutableNode o2) { int c = Ordering.natural().lexicographical().compare( o1.table.t.getQualifiedName(), o2.table.t.getQualifiedName()); - if (c == 0) { + if (c == 0 && o1.step != null && o2.step != null) { // The nodes have the same table. Now compare them based on the // columns they use as foreign key. c = Ordering.natural().lexicographical().compare( @@ -62,7 +64,8 @@ class MutableNode { } /** Creates a non-root node. */ - MutableNode(LatticeTable table, MutableNode parent, Step step) { + @SuppressWarnings("argument.type.incompatible") + MutableNode(LatticeTable table, @Nullable MutableNode parent, @Nullable Step step) { this.table = Objects.requireNonNull(table); this.parent = parent; this.step = step; @@ -99,7 +102,7 @@ private boolean isCyclicRecurse(Set descendants) { return false; } - void addPath(Path path, String alias) { + void addPath(Path path, @Nullable String alias) { MutableNode n = this; for (Step step1 : path.steps) { MutableNode n2 = n.findChild(step1); @@ -113,10 +116,10 @@ void addPath(Path path, String alias) { } } - private MutableNode findChild(Step step) { + private @Nullable MutableNode findChild(Step step) { for (MutableNode child : children) { - if (child.table.equals(step.target()) - && child.step.equals(step)) { + if (Objects.equals(child.table, step.target()) + && Objects.equals(child.step, step)) { return child; } } diff --git a/core/src/main/java/org/apache/calcite/materialize/Path.java b/core/src/main/java/org/apache/calcite/materialize/Path.java index 07a66f279633..68ee4f5fc053 100644 --- a/core/src/main/java/org/apache/calcite/materialize/Path.java +++ b/core/src/main/java/org/apache/calcite/materialize/Path.java @@ -18,6 +18,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** A sequence of {@link Step}s from a root node (fact table) to another node @@ -35,7 +37,7 @@ class Path { return id; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof Path && id == ((Path) obj).id; diff --git a/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java b/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java index 21802ad38286..e1733435a227 100644 --- a/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java +++ b/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java @@ -17,7 +17,9 @@ package org.apache.calcite.materialize; import org.apache.calcite.schema.ScannableTable; +import org.apache.calcite.schema.Schemas; import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.impl.MaterializedViewTable; import org.apache.calcite.util.ImmutableBitSet; import com.google.common.collect.ImmutableList; @@ -60,7 +62,10 @@ private double cardinality(Lattice lattice, Lattice.Column column) { new MaterializationService.DefaultTableFactory() .createTable(lattice.rootSchema, sql, ImmutableList.of()); final Object[] values = - Iterables.getOnlyElement(((ScannableTable) table).scan(null)); + Iterables.getOnlyElement( + ((ScannableTable) table).scan( + Schemas.createDataContext(MaterializedViewTable.MATERIALIZATION_CONNECTION, + lattice.rootSchema.plus()))); return ((Number) values[0]).doubleValue(); } } diff --git a/core/src/main/java/org/apache/calcite/materialize/Step.java b/core/src/main/java/org/apache/calcite/materialize/Step.java index 170937fb1451..1ab6759acd4c 100644 --- a/core/src/main/java/org/apache/calcite/materialize/Step.java +++ b/core/src/main/java/org/apache/calcite/materialize/Step.java @@ -24,6 +24,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -68,7 +72,7 @@ static Step create(LatticeTable source, LatticeTable target, return Objects.hash(source, target, keys); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof Step && ((Step) obj).source.equals(source) @@ -139,9 +143,10 @@ private double cardinality(SqlStatisticProvider statisticProvider, /** Creates {@link Step} instances. */ static class Factory implements AttributedDirectedGraph.AttributedEdgeFactory< LatticeTable, Step> { - private final LatticeSpace space; + private final @NotOnlyInitialized LatticeSpace space; - Factory(LatticeSpace space) { + @SuppressWarnings("type.argument.type.incompatible") + Factory(@UnderInitialization LatticeSpace space) { this.space = Objects.requireNonNull(space); } diff --git a/core/src/main/java/org/apache/calcite/materialize/TileKey.java b/core/src/main/java/org/apache/calcite/materialize/TileKey.java index 476886f996a0..6a16ecb2244c 100644 --- a/core/src/main/java/org/apache/calcite/materialize/TileKey.java +++ b/core/src/main/java/org/apache/calcite/materialize/TileKey.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Definition of a particular combination of dimensions and measures of a @@ -45,7 +47,7 @@ public TileKey(Lattice lattice, ImmutableBitSet dimensions, return Objects.hash(lattice, dimensions); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof TileKey && lattice == ((TileKey) obj).lattice diff --git a/core/src/main/java/org/apache/calcite/materialize/TileSuggester.java b/core/src/main/java/org/apache/calcite/materialize/TileSuggester.java index 5ab233cbb454..9cab6f5fcc95 100644 --- a/core/src/main/java/org/apache/calcite/materialize/TileSuggester.java +++ b/core/src/main/java/org/apache/calcite/materialize/TileSuggester.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.pentaho.aggdes.algorithm.Algorithm; import org.pentaho.aggdes.algorithm.Progress; import org.pentaho.aggdes.algorithm.Result; @@ -139,7 +140,7 @@ private static class TableImpl implements Table { return "TABLE"; } - @Override public Table getParent() { + @Override public @Nullable Table getParent() { return null; } } @@ -170,11 +171,11 @@ private AttributeImpl(Lattice.Column column, TableImpl table) { return 0; } - @Override public String getCandidateColumnName() { + @Override public @Nullable String getCandidateColumnName() { return null; } - @Override public String getDatatype(Dialect dialect) { + @Override public @Nullable String getDatatype(Dialect dialect) { return null; } diff --git a/core/src/main/java/org/apache/calcite/model/JsonColumn.java b/core/src/main/java/org/apache/calcite/model/JsonColumn.java index f76050a1aadc..a89827cf0825 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonColumn.java +++ b/core/src/main/java/org/apache/calcite/model/JsonColumn.java @@ -16,6 +16,11 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import static java.util.Objects.requireNonNull; + /** * JSON object representing a column. * @@ -28,7 +33,12 @@ public class JsonColumn { * *

Required, and must be unique within the table. */ - public String name; + public final String name; + + @JsonCreator + public JsonColumn(@JsonProperty(value = "name", required = true) String name) { + this.name = requireNonNull(name, "name"); + } public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonCustomSchema.java b/core/src/main/java/org/apache/calcite/model/JsonCustomSchema.java index 1491a02656e9..dee32b6ec80b 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonCustomSchema.java +++ b/core/src/main/java/org/apache/calcite/model/JsonCustomSchema.java @@ -16,8 +16,16 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * JSON schema element that represents a custom schema. * @@ -33,13 +41,26 @@ public class JsonCustomSchema extends JsonMapSchema { * {@link org.apache.calcite.schema.SchemaFactory} and have a public default * constructor. */ - public String factory; + public final String factory; /** Contains attributes to be passed to the factory. * *

May be a JSON object (represented as Map) or null. */ - public Map operand; + public final @Nullable Map operand; + + @JsonCreator + public JsonCustomSchema( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty("path") @Nullable List path, + @JsonProperty("cache") @Nullable Boolean cache, + @JsonProperty("autoLattice") @Nullable Boolean autoLattice, + @JsonProperty(value = "factory", required = true) String factory, + @JsonProperty("operand") @Nullable Map operand) { + super(name, path, cache, autoLattice); + this.factory = requireNonNull(factory, "factory"); + this.operand = operand; + } @Override public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonCustomTable.java b/core/src/main/java/org/apache/calcite/model/JsonCustomTable.java index 2bbab55c6138..ef70381da75e 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonCustomTable.java +++ b/core/src/main/java/org/apache/calcite/model/JsonCustomTable.java @@ -16,8 +16,15 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * Custom table schema element. * @@ -33,13 +40,25 @@ public class JsonCustomTable extends JsonTable { * {@link org.apache.calcite.schema.TableFactory} and have a public default * constructor. */ - public String factory; + public final String factory; /** Contains attributes to be passed to the factory. * *

May be a JSON object (represented as Map) or null. */ - public Map operand; + public final @Nullable Map operand; + + @JsonCreator + public JsonCustomTable( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty("stream") JsonStream stream, + @JsonProperty(value = "factory", required = true) String factory, + @JsonProperty("operand") @Nullable Map operand) { + super(name, stream); + this.factory = requireNonNull(factory, "factory"); + this.operand = operand; + } + @Override public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonFunction.java b/core/src/main/java/org/apache/calcite/model/JsonFunction.java index 716723f578cf..0adf4f886961 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonFunction.java +++ b/core/src/main/java/org/apache/calcite/model/JsonFunction.java @@ -16,8 +16,15 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Function schema element. * @@ -28,13 +35,13 @@ public class JsonFunction { * *

Required. */ - public String name; + public final String name; /** Name of the class that implements this function. * *

Required. */ - public String className; + public final String className; /** Name of the method that implements this function. * @@ -52,13 +59,25 @@ public class JsonFunction { * It also looks for methods "init", "add", "merge", "result", and * if found, creates an aggregate function. */ - public String methodName; + public final @Nullable String methodName; /** Path for resolving this function. * *

Optional. */ - public List path; + public final @Nullable List path; + + @JsonCreator + public JsonFunction( + @JsonProperty("name") String name, + @JsonProperty(value = "className", required = true) String className, + @JsonProperty("methodName") @Nullable String methodName, + @JsonProperty("path") @Nullable List path) { + this.name = name; + this.className = requireNonNull(className, "className"); + this.methodName = methodName; + this.path = path; + } public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonJdbcSchema.java b/core/src/main/java/org/apache/calcite/model/JsonJdbcSchema.java index 036155465dcc..99edce2b22e3 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonJdbcSchema.java +++ b/core/src/main/java/org/apache/calcite/model/JsonJdbcSchema.java @@ -16,6 +16,15 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; + +import static java.util.Objects.requireNonNull; + /** * JSON object representing a schema that maps to a JDBC database. * @@ -30,44 +39,65 @@ public class JsonJdbcSchema extends JsonSchema { *

Optional. If not specified, uses whichever class the JDBC * {@link java.sql.DriverManager} chooses. */ - public String jdbcDriver; + public final @Nullable String jdbcDriver; /** The FQN of the {@link org.apache.calcite.sql.SqlDialectFactory} implementation. * *

Optional. If not specified, uses whichever class the JDBC * {@link java.sql.DriverManager} chooses. */ - public String sqlDialectFactory; + public final @Nullable String sqlDialectFactory; /** JDBC connect string, for example "jdbc:mysql://localhost/foodmart". - * - *

Optional. */ - public String jdbcUrl; + public final String jdbcUrl; /** JDBC user name. * *

Optional. */ - public String jdbcUser; + public final @Nullable String jdbcUser; /** JDBC connect string, for example "jdbc:mysql://localhost/foodmart". * *

Optional. */ - public String jdbcPassword; + public final @Nullable String jdbcPassword; /** Name of the initial catalog in the JDBC data source. * *

Optional. */ - public String jdbcCatalog; + public final @Nullable String jdbcCatalog; /** Name of the initial schema in the JDBC data source. * *

Optional. */ - public String jdbcSchema; + public final @Nullable String jdbcSchema; + + @JsonCreator + public JsonJdbcSchema( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty("path") @Nullable List path, + @JsonProperty("cache") @Nullable Boolean cache, + @JsonProperty("autoLattice") @Nullable Boolean autoLattice, + @JsonProperty("jdbcDriver") @Nullable String jdbcDriver, + @JsonProperty("sqlDialectFactory") @Nullable String sqlDialectFactory, + @JsonProperty(value = "jdbcUrl", required = true) String jdbcUrl, + @JsonProperty("jdbcUser") @Nullable String jdbcUser, + @JsonProperty("jdbcPassword") @Nullable String jdbcPassword, + @JsonProperty("jdbcCatalog") @Nullable String jdbcCatalog, + @JsonProperty("jdbcSchema") @Nullable String jdbcSchema) { + super(name, path, cache, autoLattice); + this.jdbcDriver = jdbcDriver; + this.sqlDialectFactory = sqlDialectFactory; + this.jdbcUrl = requireNonNull(jdbcUrl, "jdbcUrl"); + this.jdbcUser = jdbcUser; + this.jdbcPassword = jdbcPassword; + this.jdbcCatalog = jdbcCatalog; + this.jdbcSchema = jdbcSchema; + } @Override public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonLattice.java b/core/src/main/java/org/apache/calcite/model/JsonLattice.java index 20bc638dc30f..0f98143d8d20 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonLattice.java +++ b/core/src/main/java/org/apache/calcite/model/JsonLattice.java @@ -16,8 +16,17 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; +import java.util.StringJoiner; + +import static java.util.Objects.requireNonNull; /** * Element that describes a star schema and provides a framework for defining, @@ -33,7 +42,7 @@ public class JsonLattice { * *

Required. */ - public String name; + public final String name; /** SQL query that defines the lattice. * @@ -44,20 +53,20 @@ public class JsonLattice { * items in the FROM clause, defines the fact table, dimension tables, and * join paths for this lattice. */ - public Object sql; + public final Object sql; /** Whether to materialize tiles on demand as queries are executed. * *

Optional; default is true. */ - public boolean auto = true; + public final boolean auto; /** Whether to use an optimization algorithm to suggest and populate an * initial set of tiles. * *

Optional; default is false. */ - public boolean algorithm = false; + public final boolean algorithm; /** Maximum time (in milliseconds) to run the algorithm. * @@ -66,12 +75,12 @@ public class JsonLattice { *

When the timeout is reached, Calcite uses the best result that has * been obtained so far. */ - public long algorithmMaxMillis = -1; + public final long algorithmMaxMillis; /** Estimated number of rows. * *

If null, Calcite will a query to find the real value. */ - public Double rowCountEstimate; + public final @Nullable Double rowCountEstimate; /** Name of a class that provides estimates of the number of distinct values * in each column. @@ -84,7 +93,7 @@ public class JsonLattice { * *

If not set, Calcite will generate and execute a SQL query to find the * real value, and cache the results. */ - public String statisticProvider; + public final @Nullable String statisticProvider; /** List of materialized aggregates to create up front. */ public final List tiles = new ArrayList<>(); @@ -95,7 +104,28 @@ public class JsonLattice { * *

Optional. The default list is just "count(*)". */ - public List defaultMeasures; + public final List defaultMeasures; + + @JsonCreator + public JsonLattice( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty(value = "sql", required = true) Object sql, + @JsonProperty("auto") @Nullable Boolean auto, + @JsonProperty("algorithm") @Nullable Boolean algorithm, + @JsonProperty("algorithmMaxMillis") @Nullable Long algorithmMaxMillis, + @JsonProperty("rowCountEstimate") @Nullable Double rowCountEstimate, + @JsonProperty("statisticProvider") @Nullable String statisticProvider, + @JsonProperty("defaultMeasures") @Nullable List defaultMeasures) { + this.name = requireNonNull(name, "name"); + this.sql = requireNonNull(sql, "sql"); + this.auto = auto == null || auto; + this.algorithm = algorithm != null && algorithm; + this.algorithmMaxMillis = algorithmMaxMillis == null ? -1 : algorithmMaxMillis; + this.rowCountEstimate = rowCountEstimate; + this.statisticProvider = statisticProvider; + this.defaultMeasures = defaultMeasures == null + ? ImmutableList.of(new JsonMeasure("count", null)) : defaultMeasures; + } public void accept(ModelHandler handler) { handler.visit(this); @@ -114,21 +144,21 @@ public String getSql() { /** Converts a string or a list of strings to a string. The list notation * is a convenient way of writing long multi-line strings in JSON. */ static String toString(Object o) { - return o == null ? null - : o instanceof String ? (String) o - : concatenate((List) o); + requireNonNull(o, "argument must not be null"); + //noinspection unchecked + return o instanceof String ? (String) o + : concatenate((List) o); } /** Converts a list of strings into a multi-line string. */ - private static String concatenate(List list) { - final StringBuilder buf = new StringBuilder(); + private static String concatenate(List list) { + final StringJoiner buf = new StringJoiner("\n", "", "\n"); for (Object o : list) { if (!(o instanceof String)) { throw new RuntimeException( "each element of a string list must be a string; found: " + o); } - buf.append((String) o); - buf.append("\n"); + buf.add((String) o); } return buf.toString(); } diff --git a/core/src/main/java/org/apache/calcite/model/JsonMapSchema.java b/core/src/main/java/org/apache/calcite/model/JsonMapSchema.java index 1b8300df65a8..98d06754ae1b 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonMapSchema.java +++ b/core/src/main/java/org/apache/calcite/model/JsonMapSchema.java @@ -16,6 +16,11 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -46,6 +51,15 @@ public class JsonMapSchema extends JsonSchema { */ public final List functions = new ArrayList<>(); + @JsonCreator + public JsonMapSchema( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty("path") @Nullable List path, + @JsonProperty("cache") @Nullable Boolean cache, + @JsonProperty("autoLattice") @Nullable Boolean autoLattice) { + super(name, path, cache, autoLattice); + } + @Override public void accept(ModelHandler handler) { handler.visit(this); } diff --git a/core/src/main/java/org/apache/calcite/model/JsonMaterialization.java b/core/src/main/java/org/apache/calcite/model/JsonMaterialization.java index 59c0c2f422b9..55969be47f5a 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonMaterialization.java +++ b/core/src/main/java/org/apache/calcite/model/JsonMaterialization.java @@ -16,8 +16,15 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Element that describes how a table is a materialization of a query. * @@ -26,17 +33,29 @@ * @see JsonRoot Description of schema elements */ public class JsonMaterialization { - public String view; - public String table; + public final @Nullable String view; + public final @Nullable String table; /** SQL query that defines the materialization. * *

Must be a string or a list of strings (which are concatenated into a * multi-line SQL string, separated by newlines). */ - public Object sql; + public final Object sql; - public List viewSchemaPath; + public final @Nullable List viewSchemaPath; + + @JsonCreator + public JsonMaterialization( + @JsonProperty("view") @Nullable String view, + @JsonProperty("table") @Nullable String table, + @JsonProperty(value = "sql", required = true) Object sql, + @JsonProperty("viewSchemaPath") @Nullable List viewSchemaPath) { + this.view = view; + this.table = table; + this.sql = requireNonNull(sql, "sql"); + this.viewSchemaPath = viewSchemaPath; + } public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonMeasure.java b/core/src/main/java/org/apache/calcite/model/JsonMeasure.java index 37737cc97486..8485eb35f127 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonMeasure.java +++ b/core/src/main/java/org/apache/calcite/model/JsonMeasure.java @@ -16,6 +16,13 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * An aggregate function applied to a column (or columns) of a lattice. * @@ -31,7 +38,7 @@ public class JsonMeasure { *

Required. Usually {@code count}, {@code sum}, * {@code min}, {@code max}. */ - public String agg; + public final String agg; /** Arguments to the measure. * @@ -49,7 +56,15 @@ public class JsonMeasure { * that each column you intend to use as a measure has a unique name within * the lattice (using "{@code AS alias}" if necessary). */ - public Object args; + public final @Nullable Object args; + + @JsonCreator + public JsonMeasure( + @JsonProperty(value = "agg", required = true) String agg, + @JsonProperty("args") @Nullable Object args) { + this.agg = requireNonNull(agg, "agg"); + this.args = args; + } public void accept(ModelHandler modelHandler) { modelHandler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonRoot.java b/core/src/main/java/org/apache/calcite/model/JsonRoot.java index ca2924f5e80d..f35790c6a495 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonRoot.java +++ b/core/src/main/java/org/apache/calcite/model/JsonRoot.java @@ -16,9 +16,16 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Root schema element. * @@ -50,7 +57,7 @@ */ public class JsonRoot { /** Schema model version number. Required, must have value "1.0". */ - public String version; + public final String version; /** Name of the schema that will become the default schema for connections * to Calcite that use this model. @@ -58,11 +65,19 @@ public class JsonRoot { *

Optional, case-sensitive. If specified, there must be a schema in this * model with this name. */ - public String defaultSchema; + public final @Nullable String defaultSchema; /** List of schema elements. * *

The list may be empty. */ public final List schemas = new ArrayList<>(); + + @JsonCreator + public JsonRoot( + @JsonProperty(value = "version", required = true) String version, + @JsonProperty("defaultSchema") @Nullable String defaultSchema) { + this.version = requireNonNull(version, "version"); + this.defaultSchema = defaultSchema; + } } diff --git a/core/src/main/java/org/apache/calcite/model/JsonSchema.java b/core/src/main/java/org/apache/calcite/model/JsonSchema.java index 3a9afcc351c8..9b1a7c0396dc 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonSchema.java +++ b/core/src/main/java/org/apache/calcite/model/JsonSchema.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -44,7 +46,7 @@ public abstract class JsonSchema { * * @see JsonRoot#defaultSchema */ - public String name; + public final String name; /** SQL path that is used to resolve functions used in this schema. * @@ -59,7 +61,7 @@ public abstract class JsonSchema { * '/lib'. Most schemas are at the top level, and for these you can use a * string. */ - public List path; + public final @Nullable List path; /** * List of tables in this schema that are materializations of queries. @@ -86,11 +88,19 @@ public abstract class JsonSchema { * not affected by this caching mechanism. They always appear in the schema * immediately, and are never flushed.

*/ - public Boolean cache; + public final @Nullable Boolean cache; /** Whether to create lattices in this schema based on queries occurring in * other schemas. Default value is {@code false}. */ - public Boolean autoLattice; + public final @Nullable Boolean autoLattice; + + protected JsonSchema(String name, @Nullable List path, @Nullable Boolean cache, + @Nullable Boolean autoLattice) { + this.name = name; + this.path = path; + this.cache = cache; + this.autoLattice = autoLattice; + } public abstract void accept(ModelHandler handler); diff --git a/core/src/main/java/org/apache/calcite/model/JsonStream.java b/core/src/main/java/org/apache/calcite/model/JsonStream.java index bc9ee484d273..886fd9143d79 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonStream.java +++ b/core/src/main/java/org/apache/calcite/model/JsonStream.java @@ -16,6 +16,11 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Information about whether a table allows streaming. * @@ -29,11 +34,19 @@ public class JsonStream { * *

Optional; default true. */ - public boolean stream = true; + public final boolean stream; /** Whether the history of the table is available. * *

Optional; default false. */ - public boolean history = false; + public final boolean history; + + @JsonCreator + public JsonStream( + @JsonProperty("stream") @Nullable Boolean stream, + @JsonProperty("history") @Nullable Boolean history) { + this.stream = stream == null || stream; + this.history = history != null && history; + } } diff --git a/core/src/main/java/org/apache/calcite/model/JsonTable.java b/core/src/main/java/org/apache/calcite/model/JsonTable.java index 3fb80be18b2a..f6d977b54c0a 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonTable.java +++ b/core/src/main/java/org/apache/calcite/model/JsonTable.java @@ -19,9 +19,13 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Table schema element. * @@ -41,7 +45,7 @@ public abstract class JsonTable { * *

Required. Must be unique within the schema. */ - public String name; + public final String name; /** Definition of the columns of this table. * @@ -52,7 +56,12 @@ public abstract class JsonTable { /** Information about whether the table can be streamed, and if so, whether * the history of the table is also available. */ - public JsonStream stream; + public final @Nullable JsonStream stream; + + protected JsonTable(String name, @Nullable JsonStream stream) { + this.name = requireNonNull(name, "name"); + this.stream = stream; + } public abstract void accept(ModelHandler handler); } diff --git a/core/src/main/java/org/apache/calcite/model/JsonTile.java b/core/src/main/java/org/apache/calcite/model/JsonTile.java index c007f05d9fb2..33e087096f1d 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonTile.java +++ b/core/src/main/java/org/apache/calcite/model/JsonTile.java @@ -16,6 +16,12 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -46,7 +52,13 @@ public class JsonTile { * *

If not specified, uses {@link JsonLattice#defaultMeasures}. */ - public List measures; + public final List measures; + + @JsonCreator + public JsonTile(@JsonProperty("measures") @Nullable List measures) { + this.measures = measures == null + ? ImmutableList.of(new JsonMeasure("count", null)) : measures; + } public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/JsonType.java b/core/src/main/java/org/apache/calcite/model/JsonType.java index 31290384386b..c7475a7686df 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonType.java +++ b/core/src/main/java/org/apache/calcite/model/JsonType.java @@ -16,9 +16,16 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Type schema element. * @@ -31,16 +38,24 @@ public class JsonType { * *

Required. */ - public String name; + public final String name; /** Type if this is not a struct. */ - public String type; + public final @Nullable String type; /** Definition of the attributes of this type. */ public final List attributes = new ArrayList<>(); + @JsonCreator + public JsonType( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty("type") @Nullable String type) { + this.name = requireNonNull(name, "name"); + this.type = type; + } + public void accept(ModelHandler handler) { handler.visit(this); } diff --git a/core/src/main/java/org/apache/calcite/model/JsonTypeAttribute.java b/core/src/main/java/org/apache/calcite/model/JsonTypeAttribute.java index 8ce2a2883c8a..b5003ea55e90 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonTypeAttribute.java +++ b/core/src/main/java/org/apache/calcite/model/JsonTypeAttribute.java @@ -16,6 +16,11 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import static java.util.Objects.requireNonNull; + /** * JSON object representing a type attribute. */ @@ -24,11 +29,19 @@ public class JsonTypeAttribute { * *

Required. */ - public String name; + public final String name; /** Type of this attribute. * *

Required. */ - public String type; + public final String type; + + @JsonCreator + public JsonTypeAttribute( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty(value = "type", required = true) String type) { + this.name = requireNonNull(name, "name"); + this.type = requireNonNull(type, "type"); + } } diff --git a/core/src/main/java/org/apache/calcite/model/JsonView.java b/core/src/main/java/org/apache/calcite/model/JsonView.java index c2547bd66760..ff4e98b1bbdd 100644 --- a/core/src/main/java/org/apache/calcite/model/JsonView.java +++ b/core/src/main/java/org/apache/calcite/model/JsonView.java @@ -16,7 +16,13 @@ */ package org.apache.calcite.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import java.util.Objects; /** * View schema element. @@ -60,13 +66,13 @@ public class JsonView extends JsonTable { *

Must be a string or a list of strings (which are concatenated into a * multi-line SQL string, separated by newlines). */ - public Object sql; + public final Object sql; /** Schema name(s) to use when resolving query. * *

If not specified, defaults to current schema. */ - public List path; + public final @Nullable List path; /** Whether this view should allow INSERT requests. * @@ -80,7 +86,20 @@ public class JsonView extends JsonTable { * *

The default value is {@code null}. */ - public Boolean modifiable; + public final @Nullable Boolean modifiable; + + @JsonCreator + public JsonView( + @JsonProperty(value = "name", required = true) String name, + @JsonProperty("steram") JsonStream stream, + @JsonProperty(value = "sql", required = true) Object sql, + @JsonProperty("path") @Nullable List path, + @JsonProperty("modifiable") @Nullable Boolean modifiable) { + super(name, stream); + this.sql = Objects.requireNonNull(sql, "sql"); + this.path = path; + this.modifiable = modifiable; + } @Override public void accept(ModelHandler handler) { handler.visit(this); diff --git a/core/src/main/java/org/apache/calcite/model/ModelHandler.java b/core/src/main/java/org/apache/calcite/model/ModelHandler.java index 7563f74ce6fb..04758640c751 100644 --- a/core/src/main/java/org/apache/calcite/model/ModelHandler.java +++ b/core/src/main/java/org/apache/calcite/model/ModelHandler.java @@ -51,9 +51,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.io.IOException; -import java.lang.reflect.Field; import java.sql.SQLException; import java.util.ArrayDeque; import java.util.Collections; @@ -63,6 +64,8 @@ import java.util.Map; import javax.sql.DataSource; +import static java.util.Objects.requireNonNull; + /** * Reads a model and creates schema objects accordingly. */ @@ -74,11 +77,13 @@ public class ModelHandler { private static final ObjectMapper YAML_MAPPER = new YAMLMapper(); private final CalciteConnection connection; - private final Deque> schemaStack = new ArrayDeque<>(); + private final Deque> schemaStack = + new ArrayDeque<>(); private final String modelUri; - Lattice.Builder latticeBuilder; - Lattice.TileBuilder tileBuilder; + Lattice.@Nullable Builder latticeBuilder; + Lattice.@Nullable TileBuilder tileBuilder; + @SuppressWarnings("method.invocation.invalid") public ModelHandler(CalciteConnection connection, String uri) throws IOException { super(); @@ -123,8 +128,8 @@ public static void create(SchemaPlus schema, String functionName, * @param upCase Whether to convert method names to upper case, so that they * can be called without using quotes */ - public static void addFunctions(SchemaPlus schema, String functionName, - List path, String className, String methodName, boolean upCase) { + public static void addFunctions(SchemaPlus schema, @Nullable String functionName, + List path, String className, @Nullable String methodName, boolean upCase) { final Class clazz; try { clazz = Class.forName(className); @@ -132,17 +137,28 @@ public static void addFunctions(SchemaPlus schema, String functionName, throw new RuntimeException("UDF class '" + className + "' not found"); } + String methodNameOrDefault = Util.first(methodName, "eval"); + String actualFunctionName; + if (functionName != null) { + actualFunctionName = functionName; + } else { + actualFunctionName = methodNameOrDefault; + } + if (upCase) { + actualFunctionName = actualFunctionName.toUpperCase(Locale.ROOT); + } final TableFunction tableFunction = - TableFunctionImpl.create(clazz, Util.first(methodName, "eval")); + TableFunctionImpl.create(clazz, methodNameOrDefault); if (tableFunction != null) { - schema.add(functionName, tableFunction); + schema.add(Util.first(functionName, methodNameOrDefault), + tableFunction); return; } // Must look for TableMacro before ScalarFunction. Both have an "eval" // method. final TableMacro macro = TableMacroImpl.create(clazz); if (macro != null) { - schema.add(functionName, macro); + schema.add(actualFunctionName, macro); return; } if (methodName != null && methodName.equals("*")) { @@ -157,24 +173,16 @@ public static void addFunctions(SchemaPlus schema, String functionName, return; } else { final ScalarFunction function = - ScalarFunctionImpl.create(clazz, Util.first(methodName, "eval")); + ScalarFunctionImpl.create(clazz, methodNameOrDefault); if (function != null) { - final String name; - if (functionName != null) { - name = functionName; - } else if (upCase) { - name = methodName.toUpperCase(Locale.ROOT); - } else { - name = methodName; - } - schema.add(name, function); + schema.add(actualFunctionName, function); return; } } if (methodName == null) { final AggregateFunction aggFunction = AggregateFunctionImpl.create(clazz); if (aggFunction != null) { - schema.add(functionName, aggFunction); + schema.add(actualFunctionName, aggFunction); return; } } @@ -184,32 +192,14 @@ public static void addFunctions(SchemaPlus schema, String functionName, + "'initAdd', 'merge' and 'result' methods."); } - private void checkRequiredAttributes(Object json, String... attributeNames) { - for (String attributeName : attributeNames) { - try { - final Class c = json.getClass(); - final Field f = c.getField(attributeName); - final Object o = f.get(json); - if (o == null) { - throw new RuntimeException("Field '" + attributeName - + "' is required in " + c.getSimpleName()); - } - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException("while accessing field " + attributeName, - e); - } - } - } - public void visit(JsonRoot jsonRoot) { - checkRequiredAttributes(jsonRoot, "version"); - final Pair pair = + final Pair<@Nullable String, SchemaPlus> pair = Pair.of(null, connection.getRootSchema()); schemaStack.push(pair); for (JsonSchema schema : jsonRoot.schemas) { schema.accept(this); } - final Pair p = schemaStack.pop(); + final Pair p = schemaStack.pop(); assert p == pair; if (jsonRoot.defaultSchema != null) { try { @@ -221,7 +211,6 @@ public void visit(JsonRoot jsonRoot) { } public void visit(JsonMapSchema jsonSchema) { - checkRequiredAttributes(jsonSchema, "name"); final SchemaPlus parentSchema = currentMutableSchema("schema"); final SchemaPlus schema = parentSchema.add(jsonSchema.name, new AbstractSchema()); @@ -269,14 +258,13 @@ private void populateSchema(JsonSchema jsonSchema, SchemaPlus schema) { final Pair pair = Pair.of(jsonSchema.name, schema); schemaStack.push(pair); jsonSchema.visitChildren(this); - final Pair p = schemaStack.pop(); + final Pair p = schemaStack.pop(); assert p == pair; } public void visit(JsonCustomSchema jsonSchema) { try { final SchemaPlus parentSchema = currentMutableSchema("sub-schema"); - checkRequiredAttributes(jsonSchema, "name", "factory"); final SchemaFactory schemaFactory = AvaticaUtils.instantiatePlugin(SchemaFactory.class, jsonSchema.factory); @@ -291,8 +279,8 @@ public void visit(JsonCustomSchema jsonSchema) { } /** Adds extra entries to an operand to a custom schema. */ - protected Map operandMap(JsonSchema jsonSchema, - Map operand) { + protected Map operandMap(@Nullable JsonSchema jsonSchema, + @Nullable Map operand) { if (operand == null) { return ImmutableMap.of(); } @@ -330,7 +318,6 @@ protected Map operandMap(JsonSchema jsonSchema, } public void visit(JsonJdbcSchema jsonSchema) { - checkRequiredAttributes(jsonSchema, "name"); final SchemaPlus parentSchema = currentMutableSchema("jdbc schema"); final DataSource dataSource = JdbcSchema.dataSource(jsonSchema.jdbcUrl, @@ -355,7 +342,6 @@ public void visit(JsonJdbcSchema jsonSchema) { public void visit(JsonMaterialization jsonMaterialization) { try { - checkRequiredAttributes(jsonMaterialization, "sql"); final SchemaPlus schema = currentSchema(); if (!schema.isMutable()) { throw new RuntimeException( @@ -389,7 +375,6 @@ public void visit(JsonMaterialization jsonMaterialization) { public void visit(JsonLattice jsonLattice) { try { - checkRequiredAttributes(jsonLattice, "name", "sql"); final SchemaPlus schema = currentSchema(); if (!schema.isMutable()) { throw new RuntimeException("Cannot define lattice; parent schema '" @@ -416,12 +401,6 @@ public void visit(JsonLattice jsonLattice) { private void populateLattice(JsonLattice jsonLattice, Lattice.Builder latticeBuilder) { - // By default, the default measure list is just {count(*)}. - if (jsonLattice.defaultMeasures == null) { - final JsonMeasure countMeasure = new JsonMeasure(); - countMeasure.agg = "count"; - jsonLattice.defaultMeasures = ImmutableList.of(countMeasure); - } assert this.latticeBuilder == null; this.latticeBuilder = latticeBuilder; jsonLattice.visitChildren(this); @@ -430,7 +409,6 @@ private void populateLattice(JsonLattice jsonLattice, public void visit(JsonCustomTable jsonTable) { try { - checkRequiredAttributes(jsonTable, "name", "factory"); final SchemaPlus schema = currentMutableSchema("table"); final TableFactory tableFactory = AvaticaUtils.instantiatePlugin(TableFactory.class, @@ -448,12 +426,10 @@ public void visit(JsonCustomTable jsonTable) { } public void visit(JsonColumn jsonColumn) { - checkRequiredAttributes(jsonColumn, "name"); } public void visit(JsonView jsonView) { try { - checkRequiredAttributes(jsonView, "name"); final SchemaPlus schema = currentMutableSchema("view"); final List path = Util.first(jsonView.path, currentSchemaPath()); final List viewPath = ImmutableList.builder().addAll(path) @@ -467,15 +443,19 @@ public void visit(JsonView jsonView) { } private List currentSchemaPath() { - return Collections.singletonList(schemaStack.peek().left); + return Collections.singletonList(currentSchemaName()); + } + + private Pair nameAndSchema() { + return requireNonNull(schemaStack.peek(), "schemaStack.peek()"); } private SchemaPlus currentSchema() { - return schemaStack.peek().right; + return nameAndSchema().right; } private String currentSchemaName() { - return schemaStack.peek().left; + return requireNonNull(nameAndSchema().left, "currentSchema.name"); } private SchemaPlus currentMutableSchema(String elementType) { @@ -488,20 +468,24 @@ private SchemaPlus currentMutableSchema(String elementType) { } public void visit(final JsonType jsonType) { - checkRequiredAttributes(jsonType, "name"); try { final SchemaPlus schema = currentMutableSchema("type"); schema.add(jsonType.name, typeFactory -> { if (jsonType.type != null) { - return typeFactory.createSqlType(SqlTypeName.get(jsonType.type)); + return typeFactory.createSqlType( + requireNonNull(SqlTypeName.get(jsonType.type), + () -> "SqlTypeName.get for " + jsonType.type)); } else { final RelDataTypeFactory.Builder builder = typeFactory.builder(); for (JsonTypeAttribute jsonTypeAttribute : jsonType.attributes) { - final SqlTypeName typeName = - SqlTypeName.get(jsonTypeAttribute.type); + final SqlTypeName typeName = requireNonNull( + SqlTypeName.get(jsonTypeAttribute.type), + () -> "SqlTypeName.get for " + jsonTypeAttribute.type); RelDataType type = typeFactory.createSqlType(typeName); if (type == null) { - type = currentSchema().getType(jsonTypeAttribute.type) + type = requireNonNull(currentSchema().getType(jsonTypeAttribute.type), + () -> "type " + jsonTypeAttribute.type + " is not found in schema " + + currentSchemaName()) .apply(typeFactory); } builder.add(jsonTypeAttribute.name, type); @@ -516,7 +500,6 @@ public void visit(final JsonType jsonType) { public void visit(JsonFunction jsonFunction) { // "name" is not required - a class can have several functions - checkRequiredAttributes(jsonFunction, "className"); try { final SchemaPlus schema = currentMutableSchema("function"); final List path = @@ -529,7 +512,6 @@ public void visit(JsonFunction jsonFunction) { } public void visit(JsonMeasure jsonMeasure) { - checkRequiredAttributes(jsonMeasure, "agg"); assert latticeBuilder != null; final boolean distinct = false; // no distinct field in JsonMeasure.yet final Lattice.Measure measure = @@ -546,16 +528,17 @@ public void visit(JsonMeasure jsonMeasure) { public void visit(JsonTile jsonTile) { assert tileBuilder == null; - tileBuilder = Lattice.Tile.builder(); + Lattice.TileBuilder tileBuilder = this.tileBuilder = Lattice.Tile.builder(); for (JsonMeasure jsonMeasure : jsonTile.measures) { jsonMeasure.accept(this); } + Lattice.Builder latticeBuilder = requireNonNull(this.latticeBuilder, "latticeBuilder"); for (Object dimension : jsonTile.dimensions) { final Lattice.Column column = latticeBuilder.resolveColumn(dimension); tileBuilder.addDimension(column); } latticeBuilder.addTile(tileBuilder.build()); - tileBuilder = null; + this.tileBuilder = null; } /** Extra operands automatically injected into a diff --git a/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java b/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java index 0bc7306c924e..7074de57370e 100644 --- a/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java +++ b/core/src/main/java/org/apache/calcite/plan/AbstractRelOptPlanner.java @@ -28,6 +28,10 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import org.slf4j.Logger; import java.text.NumberFormat; @@ -65,11 +69,11 @@ public abstract class AbstractRelOptPlanner implements RelOptPlanner { protected final RelOptCostFactory costFactory; - private MulticastRelOptListener listener; + private @MonotonicNonNull MulticastRelOptListener listener; - private RuleAttemptsListener ruleAttemptsListener; + private @MonotonicNonNull RuleAttemptsListener ruleAttemptsListener; - private Pattern ruleDescExclusionFilter; + private @Nullable Pattern ruleDescExclusionFilter; protected final AtomicBoolean cancelFlag; @@ -80,7 +84,7 @@ public abstract class AbstractRelOptPlanner implements RelOptPlanner { /** External context. Never null. */ protected final Context context; - private RexExecutor executor; + private @Nullable RexExecutor executor; //~ Constructors ----------------------------------------------------------- @@ -88,7 +92,7 @@ public abstract class AbstractRelOptPlanner implements RelOptPlanner { * Creates an AbstractRelOptPlanner. */ protected AbstractRelOptPlanner(RelOptCostFactory costFactory, - Context context) { + @Nullable Context context) { assert costFactory != null; this.costFactory = costFactory; if (context == null) { @@ -174,11 +178,11 @@ public void checkCancel() { * @param description Description * @return Rule with given description, or null if not found */ - protected RelOptRule getRuleByDescription(String description) { + protected @Nullable RelOptRule getRuleByDescription(String description) { return mapDescToRule.get(description); } - @Override public void setRuleDescExclusionFilter(Pattern exclusionFilter) { + @Override public void setRuleDescExclusionFilter(@Nullable Pattern exclusionFilter) { ruleDescExclusionFilter = exclusionFilter; } @@ -209,7 +213,7 @@ public boolean isRuleExcluded(RelOptRule rule) { // ignore - this planner does not support lattices } - @Override public RelOptLattice getLattice(RelOptTable table) { + @Override public @Nullable RelOptLattice getLattice(RelOptTable table) { // this planner does not support lattices return null; } @@ -229,8 +233,9 @@ public boolean isRuleExcluded(RelOptRule rule) { if (classes.add(clazz)) { onNewClass(node); } - if (conventions.add(node.getConvention())) { - node.getConvention().register(this); + Convention convention = node.getConvention(); + if (convention != null && conventions.add(convention)) { + convention.register(this); } } @@ -243,17 +248,19 @@ protected void onNewClass(RelNode node) { return RelTraitSet.createEmpty(); } - @Override public RelOptCost getCost(RelNode rel, RelMetadataQuery mq) { + @Override public @Nullable RelOptCost getCost(RelNode rel, RelMetadataQuery mq) { return mq.getCumulativeCost(rel); } - @Override @SuppressWarnings("deprecation") - public RelOptCost getCost(RelNode rel) { + @SuppressWarnings("deprecation") + @Override public @Nullable RelOptCost getCost(RelNode rel) { final RelMetadataQuery mq = rel.getCluster().getMetadataQuery(); return getCost(rel, mq); } - @Override public void addListener(RelOptListener newListener) { + @Override public void addListener( + @UnknownInitialization AbstractRelOptPlanner this, + RelOptListener newListener) { if (listener == null) { listener = new MulticastRelOptListener(); } @@ -273,11 +280,11 @@ public RelOptCost getCost(RelNode rel) { return ImmutableList.of(); } - @Override public void setExecutor(RexExecutor executor) { + @Override public void setExecutor(@Nullable RexExecutor executor) { this.executor = executor; } - @Override public RexExecutor getExecutor() { + @Override public @Nullable RexExecutor getExecutor() { return executor; } @@ -425,7 +432,8 @@ protected void notifyDiscard(RelNode rel) { } } - public RelOptListener getListener() { + @Pure + public @Nullable RelOptListener getListener() { return listener; } diff --git a/core/src/main/java/org/apache/calcite/plan/Contexts.java b/core/src/main/java/org/apache/calcite/plan/Contexts.java index ebcd34ad3415..bf7adb13d9e4 100644 --- a/core/src/main/java/org/apache/calcite/plan/Contexts.java +++ b/core/src/main/java/org/apache/calcite/plan/Contexts.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -57,7 +59,7 @@ public static Context of(Object o) { } /** Returns a context that wraps an array of objects, ignoring any nulls. */ - public static Context of(Object... os) { + public static Context of(@Nullable Object... os) { final List contexts = new ArrayList<>(); for (Object o : os) { if (o != null) { @@ -118,7 +120,7 @@ private static class WrapContext implements Context { this.target = Objects.requireNonNull(target); } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz.isInstance(target)) { return clazz.cast(target); } @@ -128,7 +130,7 @@ private static class WrapContext implements Context { /** Empty context. */ static class EmptyContext implements Context { - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { return null; } } @@ -144,7 +146,7 @@ private static final class ChainContext implements Context { } } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { for (Context context : contexts) { final T t = context.unwrap(clazz); if (t != null) { diff --git a/core/src/main/java/org/apache/calcite/plan/Convention.java b/core/src/main/java/org/apache/calcite/plan/Convention.java index 75915ae0f3ad..797ef063757a 100644 --- a/core/src/main/java/org/apache/calcite/plan/Convention.java +++ b/core/src/main/java/org/apache/calcite/plan/Convention.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.RelFactories; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Calling convention trait. */ @@ -48,7 +50,7 @@ public interface Convention extends RelTrait { * or {@code null} if trait enforcement is not allowed or the * required traitSet can't be satisfied. */ - default RelNode enforce(RelNode input, RelTraitSet required) { + default @Nullable RelNode enforce(RelNode input, RelTraitSet required) { throw new RuntimeException(getClass().getName() + "#enforce() is not implemented."); } @@ -122,7 +124,7 @@ public Impl(String name, Class relClass) { return ConventionTraitDef.INSTANCE; } - @Override public RelNode enforce(final RelNode input, + @Override public @Nullable RelNode enforce(final RelNode input, final RelTraitSet required) { return null; } diff --git a/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java b/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java index 218ca09a42cd..785703f21f46 100644 --- a/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java +++ b/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java @@ -31,8 +31,13 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Definition of the convention trait. * A new set of conversion information is created for @@ -122,7 +127,7 @@ private ConventionTraitDef() { } // implement RelTraitDef - @Override public RelNode convert( + @Override public @Nullable RelNode convert( RelOptPlanner planner, RelNode rel, Convention toConvention, @@ -130,7 +135,8 @@ private ConventionTraitDef() { final RelMetadataQuery mq = rel.getCluster().getMetadataQuery(); final ConversionData conversionData = getConversionData(planner); - final Convention fromConvention = rel.getConvention(); + final Convention fromConvention = requireNonNull(rel.getConvention(), + () -> "convention is null for rel " + rel); List> conversionPaths = conversionData.getPaths(fromConvention, toConvention); @@ -143,7 +149,8 @@ private ConventionTraitDef() { RelNode converted = rel; Convention previous = null; for (Convention arc : conversionPath) { - if (planner.getCost(converted, mq).isInfinite() + RelOptCost cost = planner.getCost(converted, mq); + if ((cost == null || cost.isInfinite()) && !allowInfiniteCostConverters) { continue loop; } @@ -169,7 +176,7 @@ private ConventionTraitDef() { * Tries to convert a relational expression to the target convention of an * arc. */ - private RelNode changeConvention( + private @Nullable RelNode changeConvention( RelNode rel, Convention source, Convention target, @@ -219,7 +226,7 @@ private static final class ConversionData { final Multimap, ConverterRule> mapArcToConverterRule = HashMultimap.create(); - private Graphs.FrozenGraph pathMap; + private Graphs.@MonotonicNonNull FrozenGraph pathMap; public List> getPaths( Convention fromConvention, diff --git a/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java b/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java index 8bb4efdd52d1..b62847ee5958 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java +++ b/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java @@ -19,6 +19,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -78,7 +80,7 @@ static RelTrait of(RelTraitDef def, return Arrays.hashCode(traits); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RelCompositeTrait && Arrays.equals(traits, ((RelCompositeTrait) obj).traits); diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java b/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java index aae8ee425457..efd9aec68ef0 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java @@ -31,6 +31,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.List; @@ -78,15 +80,15 @@ public String getName() { } // Override to define collations. - @Override public List getCollationList() { + @Override public @Nullable List getCollationList() { return Collections.emptyList(); } - @Override public RelDistribution getDistribution() { + @Override public @Nullable RelDistribution getDistribution() { return RelDistributions.BROADCAST_DISTRIBUTED; } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { return clazz.isInstance(this) ? clazz.cast(this) : null; @@ -98,12 +100,12 @@ public String getName() { } // Override to get unique keys - @Override public List getKeys() { + @Override public @Nullable List getKeys() { return Collections.emptyList(); } // Override to define foreign keys - @Override public List getReferentialConstraints() { + @Override public @Nullable List getReferentialConstraints() { return Collections.emptyList(); } @@ -112,7 +114,7 @@ public String getName() { context.getTableHints()); } - @Override public Expression getExpression(Class clazz) { + @Override public @Nullable Expression getExpression(Class clazz) { return null; } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java index b4991982094b..98bd4ddc8d64 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java @@ -30,12 +30,18 @@ import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * An environment for related relational expressions during the * optimization of a query. @@ -44,16 +50,16 @@ public class RelOptCluster { //~ Instance fields -------------------------------------------------------- private final RelDataTypeFactory typeFactory; - private RelOptPlanner planner; + private final RelOptPlanner planner; private final AtomicInteger nextCorrel; private final Map mapCorrelToRel; private RexNode originalExpression; private final RexBuilder rexBuilder; private RelMetadataProvider metadataProvider; private MetadataFactory metadataFactory; - private HintStrategyTable hintStrategies; + private @Nullable HintStrategyTable hintStrategies; private final RelTraitSet emptyTraitSet; - private RelMetadataQuery mq; + private @Nullable RelMetadataQuery mq; private Supplier mqSupplier; //~ Constructors ----------------------------------------------------------- @@ -105,7 +111,7 @@ public static RelOptCluster create(RelOptPlanner planner, @Deprecated // to be removed before 2.0 public RelOptQuery getQuery() { - return new RelOptQuery(planner, nextCorrel, mapCorrelToRel); + return new RelOptQuery(castNonNull(planner), nextCorrel, mapCorrelToRel); } @Deprecated // to be removed before 2.0 @@ -130,7 +136,7 @@ public RexBuilder getRexBuilder() { return rexBuilder; } - public RelMetadataProvider getMetadataProvider() { + public @Nullable RelMetadataProvider getMetadataProvider() { return metadataProvider; } @@ -139,7 +145,10 @@ public RelMetadataProvider getMetadataProvider() { * * @param metadataProvider custom provider */ - public void setMetadataProvider(RelMetadataProvider metadataProvider) { + @EnsuresNonNull({"this.metadataProvider", "this.metadataFactory"}) + public void setMetadataProvider( + @UnknownInitialization RelOptCluster this, + RelMetadataProvider metadataProvider) { this.metadataProvider = metadataProvider; this.metadataFactory = new MetadataFactoryImpl(metadataProvider); // Wrap the metadata provider as a JaninoRelMetadataProvider @@ -162,7 +171,10 @@ public MetadataFactory getMetadataFactory() { * cached in this cluster, and we may invalidate and re-generate it * for each {@link RelOptRuleCall} cycle. */ - public void setMetadataQuerySupplier(Supplier mqSupplier) { + @EnsuresNonNull("this.mqSupplier") + public void setMetadataQuerySupplier( + @UnknownInitialization RelOptCluster this, + Supplier mqSupplier) { this.mqSupplier = mqSupplier; } @@ -175,7 +187,7 @@ public void setMetadataQuerySupplier(Supplier mqSupplier) { * method, then use {@link RelOptRuleCall#getMetadataQuery()} instead. */ public RelMetadataQuery getMetadataQuery() { if (mq == null) { - mq = this.mqSupplier.get(); + mq = castNonNull(mqSupplier).get(); } return mq; } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptCostImpl.java b/core/src/main/java/org/apache/calcite/plan/RelOptCostImpl.java index 202a639e91a0..7e3cc1e7a4c4 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptCostImpl.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptCostImpl.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.plan; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelOptCostImpl provides a default implementation for the {@link RelOptCost} * interface. It it defined in terms of a single scalar quantity; somewhat @@ -77,7 +79,7 @@ public RelOptCostImpl(double value) { return getRows() == other.getRows(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (obj instanceof RelOptCostImpl) { return equals((RelOptCost) obj); } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptLattice.java b/core/src/main/java/org/apache/calcite/plan/RelOptLattice.java index 2b823e7d0576..fd5286aa8c8e 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptLattice.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptLattice.java @@ -25,6 +25,8 @@ import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -50,7 +52,7 @@ public RelOptTable rootTable() { * @param node Relational expression * @return Rewritten query */ - public RelNode rewrite(RelNode node) { + public @Nullable RelNode rewrite(RelNode node) { return RelOptMaterialization.tryUseStar(node, starRelOptTable); } @@ -69,7 +71,7 @@ public RelNode rewrite(RelNode node) { * @param measureList Calls to aggregate functions * @return Materialized table */ - public Pair getAggregate( + public @Nullable Pair getAggregate( RelOptPlanner planner, ImmutableBitSet groupSet, List measureList) { final CalciteConnectionConfig config = @@ -80,6 +82,7 @@ public Pair getAggregate( final MaterializationService service = MaterializationService.instance(); boolean create = lattice.auto && config.createMaterializations(); final CalciteSchema schema = starRelOptTable.unwrap(CalciteSchema.class); + assert schema != null : "Can't get CalciteSchema from " + starRelOptTable; return service.defineTile(lattice, groupSet, measureList, schema, create, false); } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptListener.java b/core/src/main/java/org/apache/calcite/plan/RelOptListener.java index 64d6203e565d..92c5e6226c7d 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptListener.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptListener.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.EventListener; import java.util.EventObject; @@ -86,21 +88,21 @@ public interface RelOptListener extends EventListener { * source of an event is typically the RelOptPlanner which initiated it. */ abstract class RelEvent extends EventObject { - private final RelNode rel; + private final @Nullable RelNode rel; - protected RelEvent(Object eventSource, RelNode rel) { + protected RelEvent(Object eventSource, @Nullable RelNode rel) { super(eventSource); this.rel = rel; } - public RelNode getRel() { + public @Nullable RelNode getRel() { return rel; } } /** Event indicating that a relational expression has been chosen. */ class RelChosenEvent extends RelEvent { - public RelChosenEvent(Object eventSource, RelNode rel) { + public RelChosenEvent(Object eventSource, @Nullable RelNode rel) { super(eventSource, rel); } } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java index a8823e36d139..5b190fbee2b8 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java @@ -38,17 +38,21 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Records that a particular query is materialized by a particular table. */ public class RelOptMaterialization { public final RelNode tableRel; - public final RelOptTable starRelOptTable; - public final StarTable starTable; + public final @Nullable RelOptTable starRelOptTable; + public final @Nullable StarTable starTable; public final List qualifiedTableName; public final RelNode queryRel; @@ -56,9 +60,12 @@ public class RelOptMaterialization { * Creates a RelOptMaterialization. */ public RelOptMaterialization(RelNode tableRel, RelNode queryRel, - RelOptTable starRelOptTable, List qualifiedTableName) { + @Nullable RelOptTable starRelOptTable, List qualifiedTableName) { this.tableRel = - RelOptUtil.createCastRel(tableRel, queryRel.getRowType(), false); + RelOptUtil.createCastRel( + Objects.requireNonNull(tableRel, "tableRel"), + Objects.requireNonNull(queryRel, "queryRel").getRowType(), + false); this.starRelOptTable = starRelOptTable; if (starRelOptTable == null) { this.starTable = null; @@ -80,7 +87,7 @@ public RelOptMaterialization(RelNode tableRel, RelNode queryRel, * @return Rewritten expression, or null if expression cannot be rewritten * to use the star */ - public static RelNode tryUseStar(RelNode rel, + public static @Nullable RelNode tryUseStar(RelNode rel, final RelOptTable starRelOptTable) { final StarTable starTable = starRelOptTable.unwrap(StarTable.class); assert starTable != null; @@ -89,7 +96,7 @@ public static RelNode tryUseStar(RelNode rel, @Override public RelNode visit(TableScan scan) { RelOptTable relOptTable = scan.getTable(); final Table table = relOptTable.unwrap(Table.class); - if (table.equals(starTable.tables.get(0))) { + if (Objects.equals(table, starTable.tables.get(0))) { Mappings.TargetMapping mapping = Mappings.createShiftMapping( starRelOptTable.getRowType().getFieldCount(), @@ -99,7 +106,7 @@ public static RelNode tryUseStar(RelNode rel, final RelNode scan2 = starRelOptTable.toRel(ViewExpanders.simpleContext(cluster)); return RelOptUtil.createProject(scan2, - Mappings.asList(mapping.inverse())); + Mappings.asListNonNull(mapping.inverse())); } return scan; } @@ -120,7 +127,7 @@ public static RelNode tryUseStar(RelNode rel, try { match(left, right, join.getCluster()); } catch (Util.FoundOne e) { - return (RelNode) e.getNode(); + return (RelNode) Objects.requireNonNull(e.getNode(), "FoundOne.getNode"); } } } @@ -140,6 +147,7 @@ private void match(ProjectFilterTable left, ProjectFilterTable right, final RelOptTable rightRelOptTable = right.getTable(); final Table rightTable = rightRelOptTable.unwrap(Table.class); if (leftTable instanceof StarTable + && rightTable != null && ((StarTable) leftTable).tables.contains(rightTable)) { final int offset = ((StarTable) leftTable).columnOffset(rightTable); @@ -150,7 +158,7 @@ private void match(ProjectFilterTable left, ProjectFilterTable right, leftMapping.getTargetCount())); final RelNode project = RelOptUtil.createProject( leftRelOptTable.toRel(ViewExpanders.simpleContext(cluster)), - Mappings.asList(mapping.inverse())); + Mappings.asListNonNull(mapping.inverse())); final List conditions = new ArrayList<>(); if (left.condition != null) { conditions.add(left.condition); @@ -165,6 +173,7 @@ private void match(ProjectFilterTable left, ProjectFilterTable right, throw new Util.FoundOne(filter); } if (rightTable instanceof StarTable + && leftTable != null && ((StarTable) rightTable).tables.contains(leftTable)) { final int offset = ((StarTable) rightTable).columnOffset(leftTable); @@ -174,7 +183,7 @@ private void match(ProjectFilterTable left, ProjectFilterTable right, Mappings.offsetTarget(rightMapping, leftCount)); final RelNode project = RelOptUtil.createProject( rightRelOptTable.toRel(ViewExpanders.simpleContext(cluster)), - Mappings.asList(mapping.inverse())); + Mappings.asListNonNull(mapping.inverse())); final List conditions = new ArrayList<>(); if (left.condition != null) { conditions.add( @@ -200,25 +209,25 @@ private void match(ProjectFilterTable left, ProjectFilterTable right, CoreRules.AGGREGATE_FILTER_TRANSPOSE), false, DefaultRelMetadataProvider.INSTANCE); - return program.run(null, rel2, null, + return program.run(castNonNull(null), rel2, castNonNull(null), ImmutableList.of(), ImmutableList.of()); } /** A table scan and optional project mapping and filter condition. */ private static class ProjectFilterTable { - final RexNode condition; - final Mappings.TargetMapping mapping; + final @Nullable RexNode condition; + final Mappings.@Nullable TargetMapping mapping; final TableScan scan; - private ProjectFilterTable(RexNode condition, - Mappings.TargetMapping mapping, TableScan scan) { + private ProjectFilterTable(@Nullable RexNode condition, + Mappings.@Nullable TargetMapping mapping, TableScan scan) { this.condition = condition; this.mapping = mapping; this.scan = Objects.requireNonNull(scan); } - static ProjectFilterTable of(RelNode node) { + static @Nullable ProjectFilterTable of(RelNode node) { if (node instanceof Filter) { final Filter filter = (Filter) node; return of2(filter.getCondition(), filter.getInput()); @@ -227,7 +236,7 @@ static ProjectFilterTable of(RelNode node) { } } - private static ProjectFilterTable of2(RexNode condition, RelNode node) { + private static @Nullable ProjectFilterTable of2(@Nullable RexNode condition, RelNode node) { if (node instanceof Project) { final Project project = (Project) node; return of3(condition, project.getMapping(), project.getInput()); @@ -236,8 +245,8 @@ private static ProjectFilterTable of2(RexNode condition, RelNode node) { } } - private static ProjectFilterTable of3(RexNode condition, - Mappings.TargetMapping mapping, RelNode node) { + private static @Nullable ProjectFilterTable of3(@Nullable RexNode condition, + Mappings.@Nullable TargetMapping mapping, RelNode node) { if (node instanceof TableScan) { return new ProjectFilterTable(condition, mapping, (TableScan) node); @@ -276,7 +285,7 @@ public static RelNode toLeafJoinForm(RelNode rel) { RelOptUtil.dumpPlan("before", rel, SqlExplainFormat.TEXT, SqlExplainLevel.DIGEST_ATTRIBUTES)); } - final RelNode rel2 = program.run(null, rel, null, + final RelNode rel2 = program.run(castNonNull(null), rel, castNonNull(null), ImmutableList.of(), ImmutableList.of()); if (CalciteSystemProperty.DEBUG.value()) { diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java index 9e3dd13aa04f..abe2fbda2da8 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java @@ -173,7 +173,7 @@ private static List substitute( RelNode root, RelOptMaterialization materialization) { // First, if the materialization is in terms of a star table, rewrite // the query in terms of the star table. - if (materialization.starTable != null) { + if (materialization.starRelOptTable != null) { RelNode newRoot = RelOptMaterialization.tryUseStar(root, materialization.starRelOptTable); if (newRoot != null) { diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java b/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java index 2114f73de34d..b3ec59e067c5 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java @@ -24,6 +24,7 @@ import org.apache.calcite.util.CancelFlag; import org.apache.calcite.util.trace.CalciteTrace; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.List; @@ -53,7 +54,7 @@ public interface RelOptPlanner { * * @return Root node */ - RelNode getRoot(); + @Nullable RelNode getRoot(); /** * Registers a rel trait definition. If the {@link RelTraitDef} has already @@ -124,7 +125,7 @@ public interface RelOptPlanner { * @param exclusionFilter pattern to match for exclusion; null to disable * filtering */ - void setRuleDescExclusionFilter(Pattern exclusionFilter); + void setRuleDescExclusionFilter(@Nullable Pattern exclusionFilter); /** * Does nothing. @@ -187,7 +188,7 @@ public interface RelOptPlanner { /** * Retrieves a lattice, given its star table. */ - RelOptLattice getLattice(RelOptTable table); + @Nullable RelOptLattice getLattice(RelOptTable table); /** * Finds the most efficient expression to implement this query. @@ -210,13 +211,13 @@ public interface RelOptPlanner { * @param mq Metadata query * @return estimated cost */ - RelOptCost getCost(RelNode rel, RelMetadataQuery mq); + @Nullable RelOptCost getCost(RelNode rel, RelMetadataQuery mq); // CHECKSTYLE: IGNORE 2 /** @deprecated Use {@link #getCost(RelNode, RelMetadataQuery)} * or, better, call {@link RelMetadataQuery#getCumulativeCost(RelNode)}. */ @Deprecated // to be removed before 2.0 - RelOptCost getCost(RelNode rel); + @Nullable RelOptCost getCost(RelNode rel); /** * Registers a relational expression in the expression bank. @@ -234,7 +235,7 @@ public interface RelOptPlanner { */ RelNode register( RelNode rel, - RelNode equivRel); + @Nullable RelNode equivRel); /** * Registers a relational expression if it is not already registered. @@ -249,7 +250,7 @@ RelNode register( * @param equivRel Relational expression it is equivalent to (may be null) * @return Registered relational expression */ - RelNode ensureRegistered(RelNode rel, RelNode equivRel); + RelNode ensureRegistered(RelNode rel, @Nullable RelNode equivRel); /** * Determines whether a relational expression has been registered. @@ -325,10 +326,10 @@ RelNode register( RelTraitSet emptyTraitSet(); /** Sets the object that can execute scalar expressions. */ - void setExecutor(RexExecutor executor); + void setExecutor(@Nullable RexExecutor executor); /** Returns the executor used to evaluate constant expressions. */ - RexExecutor getExecutor(); + @Nullable RexExecutor getExecutor(); /** Called when a relational expression is copied to a similar expression. */ void onCopy(RelNode rel, RelNode newRel); diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java index 8d06739ffcc1..0342ca08c38d 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java @@ -26,6 +26,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.Objects; @@ -120,6 +122,21 @@ public static RelOptPredicateList of(RexBuilder rexBuilder, return of(rexBuilder, pulledUpPredicatesList, EMPTY_LIST, EMPTY_LIST); } + /** + * Returns true if given predicate list is empty. + * @param value input predicate list + * @return true if all the predicates are empty or if the argument is null + */ + public static boolean isEmpty(@Nullable RelOptPredicateList value) { + if (value == null || value == EMPTY) { + return true; + } + return value.constantMap.isEmpty() + && value.leftInferredPredicates.isEmpty() + && value.rightInferredPredicates.isEmpty() + && value.pulledUpPredicates.isEmpty(); + } + /** Creates a RelOptPredicateList for a join. * * @param rexBuilder Rex builder diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java b/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java index d0951615b5d2..03efd0175f6d 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptQuery.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexBuilder; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -114,7 +116,7 @@ public String createCorrel() { /** * Returns the relational expression which populates a correlating variable. */ - public RelNode lookupCorrel(String name) { + public @Nullable RelNode lookupCorrel(String name) { return mapCorrelToRel.get(name); } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptRule.java b/core/src/main/java/org/apache/calcite/plan/RelOptRule.java index 44fad9d7ab8f..4c12f909005f 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptRule.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptRule.java @@ -26,11 +26,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Predicate; -import javax.annotation.Nonnull; /** * A RelOptRule transforms an expression into another. It has a @@ -96,7 +98,7 @@ protected RelOptRule(RelOptRuleOperand operand, String description) { * @param relBuilderFactory Builder for relational expressions */ protected RelOptRule(RelOptRuleOperand operand, - RelBuilderFactory relBuilderFactory, String description) { + RelBuilderFactory relBuilderFactory, @Nullable String description) { this.operand = Objects.requireNonNull(operand); this.relBuilderFactory = Objects.requireNonNull(relBuilderFactory); if (description == null) { @@ -108,7 +110,7 @@ protected RelOptRule(RelOptRuleOperand operand, } this.description = description; this.operands = flattenOperands(operand); - assignSolveOrder(); + assignSolveOrder(operands); } //~ Methods for creating operands ------------------------------------------ @@ -375,6 +377,7 @@ public static RelOptRuleOperandChildren any() { * @return Flattened list of operands */ private List flattenOperands( + @UnderInitialization RelOptRule this, RelOptRuleOperand rootOperand) { final List operandList = new ArrayList<>(); @@ -395,6 +398,7 @@ private List flattenOperands( * @param parentOperand Parent of this operand */ private void flattenRecurse( + @UnderInitialization RelOptRule this, List operandList, RelOptRuleOperand parentOperand) { int k = 0; @@ -412,7 +416,7 @@ private void flattenRecurse( * Builds each operand's solve-order. Start with itself, then its parent, up * to the root, then the remaining operands in prefix order. */ - private void assignSolveOrder() { + private static void assignSolveOrder(List operands) { for (RelOptRuleOperand operand : operands) { operand.solveOrder = new int[operands.size()]; int m = 0; @@ -463,7 +467,7 @@ public List getOperands() { return description.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return (obj instanceof RelOptRule) && equals((RelOptRule) obj); } @@ -478,7 +482,7 @@ public List getOperands() { * @return Whether this rule is equal to another rule */ @SuppressWarnings("NonOverridingEquals") - protected boolean equals(@Nonnull RelOptRule that) { + protected boolean equals(RelOptRule that) { // Include operands and class in the equality criteria just in case // they have chosen a poor description. return this == that @@ -550,7 +554,7 @@ public boolean matches(RelOptRuleCall call) { * @return Convention of the result of firing this rule, null if * not known */ - public Convention getOutConvention() { + public @Nullable Convention getOutConvention() { return null; } @@ -561,7 +565,7 @@ public Convention getOutConvention() { * @return Trait which will be modified as a result of firing this rule, * or null if the rule is not a converter rule */ - public RelTrait getOutTrait() { + public @Nullable RelTrait getOutTrait() { return null; } @@ -609,7 +613,7 @@ public static RelNode convert(RelNode rel, RelTraitSet toTraits) { * @param toTrait Desired trait * @return a relational expression with the desired trait; never null */ - public static RelNode convert(RelNode rel, RelTrait toTrait) { + public static RelNode convert(RelNode rel, @Nullable RelTrait toTrait) { RelOptPlanner planner = rel.getCluster().getPlanner(); RelTraitSet outTraits = rel.getTraitSet(); if (toTrait != null) { diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java b/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java index c0dfdfa57c51..fa4c4773028b 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptRuleCall.java @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.HashMap; @@ -54,7 +55,7 @@ public abstract class RelOptRuleCall { public final RelOptRule rule; public final RelNode[] rels; private final RelOptPlanner planner; - private final List parents; + private final @Nullable List parents; //~ Constructors ----------------------------------------------------------- @@ -77,7 +78,7 @@ protected RelOptRuleCall( RelOptRuleOperand operand, RelNode[] rels, Map> nodeInputs, - List parents) { + @Nullable List parents) { this.id = nextId++; this.planner = planner; this.operand0 = operand; @@ -171,7 +172,7 @@ public T rel(int ordinal) { * @param rel Relational expression * @return Children of relational expression */ - public List getChildRels(RelNode rel) { + public @Nullable List getChildRels(RelNode rel) { return nodeInputs.get(rel); } @@ -221,7 +222,7 @@ public RelMetadataQuery getMetadataQuery() { /** * Returns a list of parents of the first relational expression. */ - public List getParents() { + public @Nullable List getParents() { return parents; } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptRuleOperand.java b/core/src/main/java/org/apache/calcite/plan/RelOptRuleOperand.java index 3db199922fb3..71620af4a3e3 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptRuleOperand.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptRuleOperand.java @@ -20,6 +20,11 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; import java.util.function.Predicate; @@ -38,16 +43,16 @@ public class RelOptRuleOperand { //~ Instance fields -------------------------------------------------------- - private RelOptRuleOperand parent; - private RelOptRule rule; + private @Nullable RelOptRuleOperand parent; + private @NotOnlyInitialized RelOptRule rule; private final Predicate predicate; // REVIEW jvs 29-Aug-2004: some of these are Volcano-specific and should be // factored out - public int[] solveOrder; + public int @MonotonicNonNull [] solveOrder; public int ordinalInParent; public int ordinalInRule; - public final RelTrait trait; + public final @Nullable RelTrait trait; private final Class clazz; private final ImmutableList children; @@ -97,9 +102,11 @@ protected RelOptRuleOperand( * and add constructor parameters for them. See * [CALCITE-1166] * Disallow sub-classes of RelOptRuleOperand. */ + @SuppressWarnings({"initialization.fields.uninitialized", + "initialization.invalid.field.write.initialized"}) RelOptRuleOperand( Class clazz, - RelTrait trait, + @Nullable RelTrait trait, Predicate predicate, RelOptRuleOperandChildPolicy childPolicy, ImmutableList children) { @@ -135,7 +142,7 @@ RelOptRuleOperand( * * @return parent operand */ - public RelOptRuleOperand getParent() { + public @Nullable RelOptRuleOperand getParent() { return parent; } @@ -144,7 +151,7 @@ public RelOptRuleOperand getParent() { * * @param parent Parent operand */ - public void setParent(RelOptRuleOperand parent) { + public void setParent(@Nullable RelOptRuleOperand parent) { this.parent = parent; } @@ -162,7 +169,8 @@ public RelOptRule getRule() { * * @param rule containing rule */ - public void setRule(RelOptRule rule) { + @SuppressWarnings("initialization.invalid.field.write.initialized") + public void setRule(@UnknownInitialization RelOptRule rule) { this.rule = rule; } @@ -170,7 +178,7 @@ public void setRule(RelOptRule rule) { return Objects.hash(clazz, trait, children); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptSchema.java b/core/src/main/java/org/apache/calcite/plan/RelOptSchema.java index fdf151cde4b9..c130d6854fd5 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptSchema.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptSchema.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -38,7 +40,7 @@ public interface RelOptSchema { * * @param names Qualified name */ - RelOptTable getTableForMember(List names); + @Nullable RelOptTable getTableForMember(List names); /** * Returns the {@link RelDataTypeFactory type factory} used to generate diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptSchemaWithSampling.java b/core/src/main/java/org/apache/calcite/plan/RelOptSchemaWithSampling.java index f080f977cffd..a4cc4e29b3b2 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptSchemaWithSampling.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptSchemaWithSampling.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.plan; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -37,8 +39,8 @@ public interface RelOptSchemaWithSampling extends RelOptSchema { * dataset is found; may be null * @return Table, or null if not found */ - RelOptTable getTableForMember( + @Nullable RelOptTable getTableForMember( List names, - String datasetName, - boolean[] usedDataset); + @Nullable String datasetName, + boolean @Nullable [] usedDataset); } diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptTable.java b/core/src/main/java/org/apache/calcite/plan/RelOptTable.java index e80bf4067018..1e9929973e75 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptTable.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptTable.java @@ -30,6 +30,8 @@ import org.apache.calcite.schema.Wrapper; import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -60,7 +62,7 @@ public interface RelOptTable extends Wrapper { /** * Returns the {@link RelOptSchema} this table belongs to. */ - RelOptSchema getRelOptSchema(); + @Nullable RelOptSchema getRelOptSchema(); /** * Converts this table into a {@link RelNode relational expression}. @@ -81,7 +83,7 @@ public interface RelOptTable extends Wrapper { * * @see RelMetadataQuery#collations(RelNode) */ - List getCollationList(); + @Nullable List getCollationList(); /** * Returns a description of the physical distribution of the rows @@ -89,7 +91,7 @@ public interface RelOptTable extends Wrapper { * * @see RelMetadataQuery#distribution(RelNode) */ - RelDistribution getDistribution(); + @Nullable RelDistribution getDistribution(); /** * Returns whether the given columns are a key or a superset of a unique key @@ -104,13 +106,13 @@ public interface RelOptTable extends Wrapper { * Returns a list of unique keys, empty list if no key exist, * the result should be consistent with {@code isKey}. */ - List getKeys(); + @Nullable List getKeys(); /** * Returns the referential constraints existing for this table. These constraints * are represented over other tables using {@link RelReferentialConstraint} nodes. */ - List getReferentialConstraints(); + @Nullable List getReferentialConstraints(); /** * Generates code for this table. @@ -119,7 +121,7 @@ public interface RelOptTable extends Wrapper { * * @return the code for the table, or null if code generation is not supported */ - Expression getExpression(Class clazz); + @Nullable Expression getExpression(Class clazz); /** Returns a table with the given extra fields. * @@ -146,7 +148,7 @@ interface ViewExpander { * @return Relational expression */ RelRoot expandView(RelDataType rowType, String queryString, - List schemaPath, List viewPath); + List schemaPath, @Nullable List viewPath); } /** Contains the context needed to convert a a table into a relational diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java index 54575d7c9c9a..c5164736203e 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java @@ -108,6 +108,11 @@ import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.io.PrintWriter; import java.io.StringWriter; import java.util.AbstractList; @@ -124,12 +129,11 @@ import java.util.List; import java.util.Map; import java.util.NavigableSet; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * RelOptUtil defines static utility methods for use in optimizing @@ -200,10 +204,14 @@ public static List findAllTables(RelNode rel) { final Multimap, RelNode> nodes = rel.getCluster().getMetadataQuery().getNodeTypes(rel); final List usedTables = new ArrayList<>(); + if (nodes == null) { + return usedTables; + } for (Map.Entry, Collection> e : nodes.asMap().entrySet()) { if (TableScan.class.isAssignableFrom(e.getKey())) { for (RelNode node : e.getValue()) { - usedTables.add(node.getTable()); + TableScan scan = (TableScan) node; + usedTables.add(scan.getTable()); } } } @@ -562,9 +570,9 @@ public static Mappings.TargetMapping permutationPushDownProject( public static RelNode createExistsPlan( RelOptCluster cluster, RelNode seekRel, - List conditions, - RexLiteral extraExpr, - String extraName) { + @Nullable List conditions, + @Nullable RexLiteral extraExpr, + @Nullable String extraName) { assert extraExpr == null || extraName != null; RelNode ret = seekRel; @@ -573,9 +581,11 @@ public static RelNode createExistsPlan( RexUtil.composeConjunction( cluster.getRexBuilder(), conditions, true); - final RelFactories.FilterFactory factory = - RelFactories.DEFAULT_FILTER_FACTORY; - ret = factory.createFilter(ret, conditionExp, ImmutableSet.of()); + if (conditionExp != null) { + final RelFactories.FilterFactory factory = + RelFactories.DEFAULT_FILTER_FACTORY; + ret = factory.createFilter(ret, conditionExp, ImmutableSet.of()); + } } if (extraExpr != null) { @@ -1015,13 +1025,13 @@ public static boolean analyzeSimpleEquiJoin( * @return remaining join filters that are not equijoins; may return a * {@link RexLiteral} true, but never null */ - public static @Nonnull RexNode splitJoinCondition( + public static RexNode splitJoinCondition( RelNode left, RelNode right, RexNode condition, List leftKeys, List rightKeys, - List filterNulls) { + @Nullable List filterNulls) { final List nonEquiList = new ArrayList<>(); splitJoinCondition(left, right, condition, leftKeys, rightKeys, @@ -1040,7 +1050,7 @@ public static void splitJoinCondition( RexNode condition, List leftKeys, List rightKeys, - List filterNulls, + @Nullable List filterNulls, List nonEquiList) { splitJoinCondition( left.getCluster().getRexBuilder(), @@ -1103,8 +1113,8 @@ public static RexNode splitJoinCondition( RexNode condition, List leftJoinKeys, List rightJoinKeys, - List filterNulls, - List rangeOp) { + @Nullable List filterNulls, + @Nullable List rangeOp) { return splitJoinCondition( sysFieldList, ImmutableList.of(leftRel, rightRel), @@ -1135,13 +1145,13 @@ public static RexNode splitJoinCondition( * returned * @return What's left, never null */ - public static @Nonnull RexNode splitJoinCondition( + public static RexNode splitJoinCondition( List sysFieldList, List inputs, RexNode condition, List> joinKeys, - List filterNulls, - List rangeOp) { + @Nullable List filterNulls, + @Nullable List rangeOp) { final List nonEquiList = new ArrayList<>(); splitJoinCondition( @@ -1159,7 +1169,7 @@ public static RexNode splitJoinCondition( } @Deprecated // to be removed before 2.0 - public static RexNode splitCorrelatedFilterCondition( + public static @Nullable RexNode splitCorrelatedFilterCondition( LogicalFilter filter, List joinKeys, List correlatedJoinKeys) { @@ -1177,7 +1187,7 @@ public static RexNode splitCorrelatedFilterCondition( filter.getCluster().getRexBuilder(), nonEquiList, true); } - public static RexNode splitCorrelatedFilterCondition( + public static @Nullable RexNode splitCorrelatedFilterCondition( LogicalFilter filter, List joinKeys, List correlatedJoinKeys, @@ -1189,7 +1199,7 @@ public static RexNode splitCorrelatedFilterCondition( extractCorrelatedFieldAccess); } - public static RexNode splitCorrelatedFilterCondition( + public static @Nullable RexNode splitCorrelatedFilterCondition( Filter filter, List joinKeys, List correlatedJoinKeys, @@ -1214,8 +1224,8 @@ private static void splitJoinCondition( List inputs, RexNode condition, List> joinKeys, - List filterNulls, - List rangeOp, + @Nullable List filterNulls, + @Nullable List rangeOp, List nonEquiList) { final int sysFieldCount = sysFieldList.size(); final RelOptCluster cluster = inputs.get(0).getCluster(); @@ -1434,7 +1444,7 @@ private static void splitJoinCondition( } /** Builds an equi-join condition from a set of left and right keys. */ - public static @Nonnull RexNode createEquiJoinCondition( + public static RexNode createEquiJoinCondition( final RelNode left, final List leftKeys, final RelNode right, final List rightKeys, final RexBuilder rexBuilder) { @@ -1625,7 +1635,7 @@ private static void splitJoinCondition( RexNode condition, List leftKeys, List rightKeys, - List filterNulls, + @Nullable List filterNulls, List nonEquiList) { if (condition instanceof RexCall) { RexCall call = (RexCall) condition; @@ -1858,10 +1868,10 @@ public static void projectJoinInputs( int origRightInputSize = rightRel.getRowType().getFieldCount(); final List newLeftFields = new ArrayList<>(); - final List newLeftFieldNames = new ArrayList<>(); + final List<@Nullable String> newLeftFieldNames = new ArrayList<>(); final List newRightFields = new ArrayList<>(); - final List newRightFieldNames = new ArrayList<>(); + final List<@Nullable String> newRightFieldNames = new ArrayList<>(); int leftKeyCount = leftJoinKeys.size(); int rightKeyCount = rightJoinKeys.size(); int i; @@ -2352,8 +2362,8 @@ public static String toString(final RelNode rel) { /** * Converts a relational expression to a string. */ - public static String toString( - final RelNode rel, + public static @PolyNull String toString( + final @PolyNull RelNode rel, SqlExplainLevel detailLevel) { if (rel == null) { return null; @@ -2426,7 +2436,7 @@ public static List deduplicateColumns( * @param rexList list of decomposed RexNodes */ public static void decomposeConjunction( - RexNode rexPredicate, + @Nullable RexNode rexPredicate, List rexList) { if (rexPredicate == null || rexPredicate.isAlwaysTrue()) { return; @@ -2459,7 +2469,7 @@ public static void decomposeConjunction( * @param notList list of decomposed RexNodes that were prefixed NOT */ public static void decomposeConjunction( - RexNode rexPredicate, + @Nullable RexNode rexPredicate, List rexList, List notList) { if (rexPredicate == null || rexPredicate.isAlwaysTrue()) { @@ -2514,7 +2524,7 @@ public static void decomposeConjunction( * @param rexList list of decomposed RexNodes */ public static void decomposeDisjunction( - RexNode rexPredicate, + @Nullable RexNode rexPredicate, List rexList) { if (rexPredicate == null || rexPredicate.isAlwaysFalse()) { return; @@ -2534,7 +2544,7 @@ public static void decomposeDisjunction( *

For example, {@code conjunctions(TRUE)} returns the empty list; * {@code conjunctions(FALSE)} returns list {@code {FALSE}}.

*/ - public static List conjunctions(RexNode rexPredicate) { + public static List conjunctions(@Nullable RexNode rexPredicate) { final List list = new ArrayList<>(); decomposeConjunction(rexPredicate, list); return list; @@ -2563,8 +2573,8 @@ public static List disjunctions(RexNode rexPredicate) { */ public static RexNode andJoinFilters( RexBuilder rexBuilder, - RexNode left, - RexNode right) { + @Nullable RexNode left, + @Nullable RexNode right) { // don't bother AND'ing in expressions that always evaluate to // true if ((left != null) && !left.isAlwaysTrue()) { @@ -2903,7 +2913,7 @@ private static RexNode shiftFilter( */ public static void splitFilters( ImmutableBitSet childBitmap, - RexNode predicate, + @Nullable RexNode predicate, List pushable, List notPushable) { // for each filter, if the filter only references the child inputs, @@ -3142,12 +3152,12 @@ public static RelNode replaceInput( public static RelNode createProject( RelNode child, Mappings.TargetMapping mapping) { - return createProject(child, Mappings.asList(mapping.inverse())); + return createProject(child, Mappings.asListNonNull(mapping.inverse())); } public static RelNode createProject(RelNode child, Mappings.TargetMapping mapping, RelFactories.ProjectFactory projectFactory) { - return createProject(projectFactory, child, Mappings.asList(mapping.inverse())); + return createProject(projectFactory, child, Mappings.asListNonNull(mapping.inverse())); } /** Returns whether relational expression {@code target} occurs within a @@ -3159,7 +3169,8 @@ public static boolean contains(RelNode ancestor, final RelNode target) { } try { new RelVisitor() { - @Override public void visit(RelNode node, int ordinal, RelNode parent) { + @Override public void visit(RelNode node, int ordinal, + @Nullable RelNode parent) { if (node == target) { throw Util.FoundOne.NULL; } @@ -3219,7 +3230,8 @@ public static int countJoins(RelNode rootRel) { class JoinCounter extends RelVisitor { int joinCount; - @Override public void visit(RelNode node, int ordinal, RelNode parent) { + @Override public void visit(RelNode node, int ordinal, + @org.checkerframework.checker.nullness.qual.Nullable RelNode parent) { if (node instanceof Join) { ++joinCount; } @@ -3257,7 +3269,7 @@ public static RelNode createProject( @Deprecated // to be removed before 2.0 public static RelNode createProject( RelNode child, - List> projectList, + List> projectList, boolean optimize) { final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(child.getCluster(), null); @@ -3287,7 +3299,7 @@ public static RelNode createProject(final RelNode child, public static RelNode createProject( RelNode child, List exprs, - List fieldNames, + List fieldNames, boolean optimize) { final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(child.getCluster(), null); @@ -3303,7 +3315,7 @@ public static RelNode createProject( public static RelNode createProject( RelNode child, List exprs, - List fieldNames, + List fieldNames, boolean optimize, RelBuilder relBuilder) { return relBuilder.push(child) @@ -3314,7 +3326,7 @@ public static RelNode createProject( @Deprecated // to be removed before 2.0 public static RelNode createRename( RelNode rel, - List fieldNames) { + List fieldNames) { final List fields = rel.getRowType().getFieldList(); assert fieldNames.size() == fields.size(); final List refs = @@ -3362,7 +3374,7 @@ public static RelNode createRename( public static RelNode permute( RelNode rel, Permutation permutation, - List fieldNames) { + @Nullable List fieldNames) { if (permutation.isIdentity()) { return rel; } @@ -3453,7 +3465,7 @@ public static RelNode createProject(final RelFactories.ProjectFactory factory, public static RelNode projectMapping( RelNode rel, Mapping mapping, - List fieldNames, + @Nullable List fieldNames, RelFactories.ProjectFactory projectFactory) { assert mapping.getMappingType().isSingleSource(); assert mapping.getMappingType().isMandatorySource(); @@ -3594,13 +3606,13 @@ public static RelNode pushDownJoinConditions(Join originalJoin, if (!extraLeftExprs.isEmpty()) { final List fields = relBuilder.peek().getRowType().getFieldList(); - final List> pairs = - new AbstractList>() { + final List> pairs = + new AbstractList>() { @Override public int size() { return leftCount + extraLeftExprs.size(); } - @Override public Pair get(int index) { + @Override public Pair get(int index) { if (index < leftCount) { RelDataTypeField field = fields.get(index); return Pair.of( @@ -3618,13 +3630,13 @@ public static RelNode pushDownJoinConditions(Join originalJoin, final List fields = relBuilder.peek().getRowType().getFieldList(); final int newLeftCount = leftCount + extraLeftExprs.size(); - final List> pairs = - new AbstractList>() { + final List> pairs = + new AbstractList>() { @Override public int size() { return rightCount + extraRightExprs.size(); } - @Override public Pair get(int index) { + @Override public Pair get(int index) { if (index < rightCount) { RelDataTypeField field = fields.get(index); return Pair.of( @@ -3834,7 +3846,7 @@ private static boolean containsNullableFields(RelNode r) { return false; } final RelOptPredicateList predicates = mq.getPulledUpPredicates(r); - if (predicates.pulledUpPredicates.isEmpty()) { + if (RelOptPredicateList.isEmpty(predicates)) { // We have no predicates, so cannot deduce that any of the fields // declared NULL are really NOT NULL. return true; @@ -4171,7 +4183,7 @@ private static class VariableSetVisitor extends RelVisitor { @Override public void visit( RelNode p, int ordinal, - RelNode parent) { + @org.checkerframework.checker.nullness.qual.Nullable RelNode parent) { super.visit(p, ordinal, parent); p.collectVariablesUsed(variables); @@ -4186,9 +4198,10 @@ public static class VariableUsedVisitor extends RexShuttle { public final Set variables = new LinkedHashSet<>(); public final Multimap variableFields = LinkedHashMultimap.create(); - private final RelShuttle relShuttle; + @NotOnlyInitialized + private final @Nullable RelShuttle relShuttle; - public VariableUsedVisitor(RelShuttle relShuttle) { + public VariableUsedVisitor(@Nullable @UnknownInitialization RelShuttle relShuttle) { this.relShuttle = relShuttle; } @@ -4254,7 +4267,11 @@ void accept(RelDataType type) { } } else if (type instanceof MultisetSqlType) { // E.g. "INTEGER NOT NULL MULTISET NOT NULL" - accept(type.getComponentType()); + RelDataType componentType = + Objects.requireNonNull( + type.getComponentType(), + () -> "type.getComponentType() for " + type); + accept(componentType); pw.print(" MULTISET"); if (!type.isNullable()) { pw.print(" NOT NULL"); @@ -4285,9 +4302,9 @@ private void acceptFields(final List fields) { */ public static class InputFinder extends RexVisitorImpl { private final ImmutableBitSet.Builder bitBuilder; - private final Set extraFields; + private final @Nullable Set extraFields; - private InputFinder(Set extraFields, + private InputFinder(@Nullable Set extraFields, ImmutableBitSet.Builder bitBuilder) { super(true); this.bitBuilder = bitBuilder; @@ -4298,11 +4315,11 @@ public InputFinder() { this(null); } - public InputFinder(Set extraFields) { + public InputFinder(@Nullable Set extraFields) { this(extraFields, ImmutableBitSet.builder()); } - public InputFinder(Set extraFields, + public InputFinder(@Nullable Set extraFields, ImmutableBitSet initialBits) { this(extraFields, initialBits.rebuild()); } @@ -4325,7 +4342,7 @@ public static ImmutableBitSet bits(RexNode node) { * Returns a bit set describing the inputs used by a collection of * project expressions and an optional condition. */ - public static ImmutableBitSet bits(List exprs, RexNode expr) { + public static ImmutableBitSet bits(List exprs, @Nullable RexNode expr) { final InputFinder inputFinder = new InputFinder(); RexUtil.apply(inputFinder, exprs, expr); return inputFinder.build(); @@ -4347,11 +4364,16 @@ public ImmutableBitSet build() { @Override public Void visitCall(RexCall call) { if (call.getOperator() == RexBuilder.GET_OPERATOR) { RexLiteral literal = (RexLiteral) call.getOperands().get(1); - extraFields.add( - new RelDataTypeFieldImpl( - (String) literal.getValue2(), - -1, - call.getType())); + if (extraFields != null) { + Objects.requireNonNull(literal, () -> "first operand in " + call); + String value2 = (String) literal.getValue2(); + Objects.requireNonNull(value2, () -> "value of the first operand in " + call); + extraFields.add( + new RelDataTypeFieldImpl( + value2, + -1, + call.getType())); + } } return super.visitCall(call); } @@ -4363,10 +4385,10 @@ public ImmutableBitSet build() { */ public static class RexInputConverter extends RexShuttle { protected final RexBuilder rexBuilder; - private final List srcFields; - protected final List destFields; - private final List leftDestFields; - private final List rightDestFields; + private final @Nullable List srcFields; + protected final @Nullable List destFields; + private final @Nullable List leftDestFields; + private final @Nullable List rightDestFields; private final int nLeftDestFields; private final int[] adjustments; @@ -4390,10 +4412,10 @@ public static class RexInputConverter extends RexShuttle { */ private RexInputConverter( RexBuilder rexBuilder, - List srcFields, - List destFields, - List leftDestFields, - List rightDestFields, + @Nullable List srcFields, + @Nullable List destFields, + @Nullable List leftDestFields, + @Nullable List rightDestFields, int[] adjustments) { this.rexBuilder = rexBuilder; this.srcFields = srcFields; @@ -4411,9 +4433,9 @@ private RexInputConverter( public RexInputConverter( RexBuilder rexBuilder, - List srcFields, - List leftDestFields, - List rightDestFields, + @Nullable List srcFields, + @Nullable List leftDestFields, + @Nullable List rightDestFields, int[] adjustments) { this( rexBuilder, @@ -4426,15 +4448,15 @@ public RexInputConverter( public RexInputConverter( RexBuilder rexBuilder, - List srcFields, - List destFields, + @Nullable List srcFields, + @Nullable List destFields, int[] adjustments) { this(rexBuilder, srcFields, destFields, null, null, adjustments); } public RexInputConverter( RexBuilder rexBuilder, - List srcFields, + @Nullable List srcFields, int[] adjustments) { this(rexBuilder, srcFields, null, null, null, adjustments); } @@ -4451,10 +4473,11 @@ public RexInputConverter( type = leftDestFields.get(destIndex).getType(); } else { type = - rightDestFields.get(destIndex - nLeftDestFields).getType(); + Objects.requireNonNull(rightDestFields, "rightDestFields") + .get(destIndex - nLeftDestFields).getType(); } } else { - type = srcFields.get(srcIndex).getType(); + type = Objects.requireNonNull(srcFields, "srcFields").get(srcIndex).getType(); } if ((adjustments[srcIndex] != 0) || (srcFields == null) @@ -4504,6 +4527,7 @@ public boolean opposite(Side side) { * expression, including those that are inside * {@link RexSubQuery sub-queries}. */ private static class CorrelationCollector extends RelHomogeneousShuttle { + @SuppressWarnings("assignment.type.incompatible") private final VariableUsedVisitor vuv = new VariableUsedVisitor(this); @Override public RelNode visit(RelNode other) { diff --git a/core/src/main/java/org/apache/calcite/plan/RelRule.java b/core/src/main/java/org/apache/calcite/plan/RelRule.java index bcaf358869bb..33388870fc78 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelRule.java +++ b/core/src/main/java/org/apache/calcite/plan/RelRule.java @@ -23,13 +23,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; -import javax.annotation.Nonnull; /** * Rule that is parameterized via a configuration. @@ -129,7 +130,7 @@ public interface Config { RelOptRule toRule(); /** Casts this configuration to another type, usually a sub-class. */ - default T as(Class class_) { + default T as(Class class_) { return ImmutableBeans.copy(class_, this); } @@ -187,7 +188,7 @@ public interface Done { * @param Type of relational expression */ public interface OperandDetailBuilder { /** Sets a trait of this operand. */ - OperandDetailBuilder trait(@Nonnull RelTrait trait); + OperandDetailBuilder trait(RelTrait trait); /** Sets the predicate of this operand. */ OperandDetailBuilder predicate(Predicate predicate); @@ -245,7 +246,7 @@ private static class OperandDetailBuilderImpl private final OperandBuilderImpl parent; private final Class relClass; final OperandBuilderImpl inputBuilder = new OperandBuilderImpl(); - private RelTrait trait; + private @Nullable RelTrait trait; private Predicate predicate = r -> true; OperandDetailBuilderImpl(OperandBuilderImpl parent, Class relClass) { @@ -253,7 +254,7 @@ private static class OperandDetailBuilderImpl this.relClass = Objects.requireNonNull(relClass); } - @Override public OperandDetailBuilderImpl trait(@Nonnull RelTrait trait) { + @Override public OperandDetailBuilderImpl trait(RelTrait trait) { this.trait = Objects.requireNonNull(trait); return this; } diff --git a/core/src/main/java/org/apache/calcite/plan/RelTrait.java b/core/src/main/java/org/apache/calcite/plan/RelTrait.java index aa70ddd9f8a5..c410a96d677a 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelTrait.java +++ b/core/src/main/java/org/apache/calcite/plan/RelTrait.java @@ -20,6 +20,8 @@ import org.apache.calcite.rel.core.Project; import org.apache.calcite.util.mapping.Mappings; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelTrait represents the manifestation of a relational expression trait within * a trait definition. For example, a {@code CallingConvention.JAVA} is a trait @@ -53,7 +55,7 @@ public interface RelTrait { /** * See note about equals() and hashCode(). */ - @Override boolean equals(Object o); + @Override boolean equals(@Nullable Object o); /** * Returns whether this trait satisfies a given trait. diff --git a/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java b/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java index c4ab3ff1ad07..74d46e9c1da6 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java +++ b/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java @@ -22,6 +22,8 @@ import com.google.common.collect.Interner; import com.google.common.collect.Interners; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelTraitDef represents a class of {@link RelTrait}s. Implementations of * RelTraitDef may be singletons under the following conditions: @@ -117,7 +119,7 @@ assert getTraitClass().isInstance(trait) * converters are allowed * @return a converted RelNode or null if conversion is not possible */ - public abstract RelNode convert( + public abstract @Nullable RelNode convert( RelOptPlanner planner, RelNode rel, T toTrait, diff --git a/core/src/main/java/org/apache/calcite/plan/RelTraitPropagationVisitor.java b/core/src/main/java/org/apache/calcite/plan/RelTraitPropagationVisitor.java index 1474ad002d61..c2280ae85c30 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelTraitPropagationVisitor.java +++ b/core/src/main/java/org/apache/calcite/plan/RelTraitPropagationVisitor.java @@ -20,6 +20,8 @@ import org.apache.calcite.rel.RelVisitor; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelTraitPropagationVisitor traverses a RelNode and its unregistered * children, making sure that each has a full complement of traits. When a @@ -55,7 +57,7 @@ public RelTraitPropagationVisitor( //~ Methods ---------------------------------------------------------------- - @Override public void visit(RelNode rel, int ordinal, RelNode parent) { + @Override public void visit(RelNode rel, int ordinal, @Nullable RelNode parent) { // REVIEW: SWZ: 1/31/06: We assume that any special RelNodes, such // as the VolcanoPlanner's RelSubset always have a full complement // of traits and that they either appear as registered or do nothing diff --git a/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java b/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java index 08bc1fb57c06..373437015e37 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java +++ b/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.Arrays; import java.util.HashMap; @@ -41,7 +43,7 @@ public final class RelTraitSet extends AbstractList { private final Cache cache; private final RelTrait[] traits; - private String string; + private @Nullable String string; /** Caches the hash code for the traits. */ private int hash; // Default to 0 @@ -122,7 +124,7 @@ public boolean isEnabled(RelTraitDef traitDef) { * @param traitDef the type of RelTrait to retrieve * @return the RelTrait, or null if not found */ - public T getTrait(RelTraitDef traitDef) { + public @Nullable T getTrait(RelTraitDef traitDef) { int index = findIndex(traitDef); if (index >= 0) { //noinspection unchecked @@ -140,7 +142,7 @@ public T getTrait(RelTraitDef traitDef) { * @param traitDef the type of RelTrait to retrieve * @return the RelTrait, or null if not found */ - public List getTraits( + public @Nullable List getTraits( RelTraitDef traitDef) { int index = findIndex(traitDef); if (index >= 0) { @@ -234,7 +236,7 @@ public RelTraitSet replace(RelTraitDef def, /** If a given multiple trait is enabled, replaces it by calling the given * function. */ public RelTraitSet replaceIfs(RelTraitDef def, - Supplier> traitSupplier) { + Supplier> traitSupplier) { int index = findIndex(def); if (index < 0) { return this; // trait is not enabled; ignore it @@ -362,7 +364,7 @@ public RelTraitSet getDefaultSansConvention() { * {@link ConventionTraitDef#INSTANCE} is not registered * in this traitSet. */ - public Convention getConvention() { + public @Nullable Convention getConvention() { return getTrait(ConventionTraitDef.INSTANCE); } @@ -372,9 +374,9 @@ public Convention getConvention() { * {@link RelDistributionTraitDef#INSTANCE} is not registered * in this traitSet. */ - public T getDistribution() { - //noinspection unchecked - return (T) getTrait(RelDistributionTraitDef.INSTANCE); + @SuppressWarnings("unchecked") + public @Nullable T getDistribution() { + return (@Nullable T) getTrait(RelDistributionTraitDef.INSTANCE); } /** @@ -383,9 +385,9 @@ public T getDistribution() { * {@link RelCollationTraitDef#INSTANCE} is not registered * in this traitSet. */ - public T getCollation() { - //noinspection unchecked - return (T) getTrait(RelCollationTraitDef.INSTANCE); + @SuppressWarnings("unchecked") + public @Nullable T getCollation() { + return (@Nullable T) getTrait(RelCollationTraitDef.INSTANCE); } /** @@ -407,7 +409,9 @@ public T getCollation() { */ public T canonize(T trait) { if (trait == null) { - return null; + // Return "trait" makes the input type to be the same as the output type, + // so checkerframework is happy + return trait; } if (trait instanceof RelCompositeTrait) { @@ -426,7 +430,7 @@ public T canonize(T trait) { * @param obj another RelTraitSet * @return true if traits are equal and in the same order, false otherwise */ - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java index 938059fbceb7..d1b8fc64aa8a 100644 --- a/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java +++ b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java @@ -36,6 +36,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.LoggerFactory; import java.util.ArrayList; @@ -211,14 +212,14 @@ private boolean implies2(RexNode first, RexNode second) { return false; } - ImmutableList.Builder>> usagesBuilder = + ImmutableList.Builder>> usagesBuilder = ImmutableList.builder(); - for (Map.Entry> entry + for (Map.Entry> entry : firstUsageFinder.usageMap.entrySet()) { - ImmutableSet.Builder> usageBuilder = + ImmutableSet.Builder> usageBuilder = ImmutableSet.builder(); if (entry.getValue().usageList.size() > 0) { - for (final Pair pair + for (final Pair pair : entry.getValue().usageList) { usageBuilder.add(Pair.of(entry.getKey(), pair.getValue())); } @@ -226,10 +227,10 @@ private boolean implies2(RexNode first, RexNode second) { } } - final Set>> usages = + final Set>> usages = Sets.cartesianProduct(usagesBuilder.build()); - for (List> usageList : usages) { + for (List> usageList : usages) { // Get the literals from first conjunction and executes second conjunction // using them. // @@ -250,7 +251,7 @@ private boolean implies2(RexNode first, RexNode second) { return true; } - private boolean isSatisfiable(RexNode second, DataContext dataValues) { + private boolean isSatisfiable(RexNode second, @Nullable DataContext dataValues) { if (dataValues == null) { return false; } @@ -306,22 +307,22 @@ private boolean isSatisfiable(RexNode second, DataContext dataValues) { */ private boolean checkSupport(InputUsageFinder firstUsageFinder, InputUsageFinder secondUsageFinder) { - final Map> firstUsageMap = + final Map> firstUsageMap = firstUsageFinder.usageMap; - final Map> secondUsageMap = + final Map> secondUsageMap = secondUsageFinder.usageMap; - for (Map.Entry> entry + for (Map.Entry> entry : secondUsageMap.entrySet()) { - final InputRefUsage secondUsage = entry.getValue(); - final List> secondUsageList = secondUsage.usageList; + final InputRefUsage secondUsage = entry.getValue(); + final List> secondUsageList = secondUsage.usageList; final int secondLen = secondUsageList.size(); if (secondUsage.usageCount != secondLen || secondLen > 2) { return false; } - final InputRefUsage firstUsage = + final InputRefUsage firstUsage = firstUsageMap.get(entry.getKey()); if (firstUsage == null @@ -336,10 +337,12 @@ private boolean checkSupport(InputUsageFinder firstUsageFinder, final SqlKind fKind = firstUsageList.get(0).getKey().getKind(); final SqlKind sKind = secondUsageList.get(0).getKey().getKind(); final SqlKind fKind2 = - (firstUsageList.size() == 2) ? firstUsageList.get(1).getKey().getKind() : null; + firstLen == 2 ? firstUsageList.get(1).getKey().getKind() : null; final SqlKind sKind2 = - (secondUsageList.size() == 2) ? secondUsageList.get(1).getKey().getKind() : null; + secondLen == 2 ? secondUsageList.get(1).getKey().getKind() : null; + // Note: arguments to isEquivalentOp are never null, however checker-framework's + // dataflow is not strong enough, so the first parameter is marked as nullable if (firstLen == 2 && secondLen == 2 && !(isEquivalentOp(fKind, sKind) && isEquivalentOp(fKind2, sKind2)) && !(isEquivalentOp(fKind, sKind2) && isEquivalentOp(fKind2, sKind))) { @@ -376,7 +379,7 @@ private boolean isSupportedUnaryOperators(SqlKind kind) { } } - private boolean isEquivalentOp(SqlKind fKind, SqlKind sKind) { + private boolean isEquivalentOp(@Nullable SqlKind fKind, SqlKind sKind) { switch (sKind) { case GREATER_THAN: case GREATER_THAN_OR_EQUAL: @@ -435,7 +438,7 @@ private boolean validate(RexNode first, RexNode second) { * */ private static class InputUsageFinder extends RexVisitorImpl { - final Map> usageMap = + final Map> usageMap = new HashMap<>(); InputUsageFinder() { @@ -443,7 +446,7 @@ private static class InputUsageFinder extends RexVisitorImpl { } @Override public Void visitInputRef(RexInputRef inputRef) { - InputRefUsage inputRefUse = getUsageMap(inputRef); + InputRefUsage inputRefUse = getUsageMap(inputRef); inputRefUse.usageCount++; return null; } @@ -497,15 +500,15 @@ private SqlOperator reverse(SqlOperator op) { } private void updateUsage(SqlOperator op, RexInputRef inputRef, - RexNode literal) { - final InputRefUsage inputRefUse = + @Nullable RexNode literal) { + final InputRefUsage inputRefUse = getUsageMap(inputRef); - Pair use = Pair.of(op, literal); + Pair use = Pair.of(op, literal); inputRefUse.usageList.add(use); } - private InputRefUsage getUsageMap(RexInputRef rex) { - InputRefUsage inputRefUse = usageMap.get(rex); + private InputRefUsage getUsageMap(RexInputRef rex) { + InputRefUsage inputRefUse = usageMap.get(rex); if (inputRefUse == null) { inputRefUse = new InputRefUsage<>(); usageMap.put(rex, inputRefUse); diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java index 2fe5b8859462..02992cc66be2 100644 --- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java +++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java @@ -41,7 +41,6 @@ import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexCall; import org.apache.calcite.rex.RexExecutor; -import org.apache.calcite.rex.RexExecutorImpl; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexLocalRef; @@ -73,6 +72,8 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -81,13 +82,14 @@ import java.util.List; import java.util.Map; import java.util.NavigableMap; -import java.util.Objects; import java.util.Set; import java.util.TreeMap; import static org.apache.calcite.rex.RexUtil.andNot; import static org.apache.calcite.rex.RexUtil.removeAll; +import static java.util.Objects.requireNonNull; + /** * Substitutes part of a tree of relational expressions with another tree. * @@ -195,11 +197,12 @@ public SubstitutionVisitor(RelNode target_, RelNode query_, this.query = Holder.of(MutableRels.toMutable(query_)); this.target = MutableRels.toMutable(target_); this.relBuilder = relBuilderFactory.create(cluster, null); - final Set parents = Sets.newIdentityHashSet(); + final Set<@Nullable MutableRel> parents = Sets.newIdentityHashSet(); final List allNodes = new ArrayList<>(); final MutableRelVisitor visitor = new MutableRelVisitor() { - @Override public void visit(MutableRel node) { + @Override public void visit(@Nullable MutableRel node) { + requireNonNull(node, "node"); parents.add(node.getParent()); allNodes.add(node); super.visit(node); @@ -279,7 +282,7 @@ void register(MutableRel result, MutableRel query) { * problem.

*/ @VisibleForTesting - public static RexNode splitFilter(final RexSimplify simplify, + public static @Nullable RexNode splitFilter(final RexSimplify simplify, RexNode condition, RexNode target) { final RexBuilder rexBuilder = simplify.rexBuilder; RexNode condition2 = canonizeNode(rexBuilder, condition); @@ -350,7 +353,11 @@ private static RexNode canonizeNode(RexBuilder rexBuilder, RexNode condition) { if (left.toString().compareTo(right.toString()) <= 0) { return call; } - return RexUtil.invert(rexBuilder, call); + final RexNode result = RexUtil.invert(rexBuilder, call); + if (result == null) { + throw new NullPointerException("RexUtil.invert returned null for " + call); + } + return result; } case SEARCH: { final RexNode e = RexUtil.expandSearch(rexBuilder, null, condition); @@ -378,7 +385,7 @@ private static RexNode canonizeNode(RexBuilder rexBuilder, RexNode condition) { } } - private static RexNode splitOr( + private static @Nullable RexNode splitOr( final RexBuilder rexBuilder, RexNode condition, RexNode target) { List conditions = RelOptUtil.disjunctions(condition); int conditionsLength = conditions.size(); @@ -467,7 +474,7 @@ public static boolean mayBeSatisfiable(RexNode e) { return true; } - public RelNode go0(RelNode replacement_) { + public @Nullable RelNode go0(RelNode replacement_) { assert false; // not called MutableRel replacement = MutableRels.toMutable(replacement_); assert equalType( @@ -680,7 +687,7 @@ static class Replacement { * *

Assumes relational expressions (and their descendants) are not null. * Does not handle cycles. */ - public static Replacement replace(MutableRel query, MutableRel find, + public static @Nullable Replacement replace(MutableRel query, MutableRel find, MutableRel replace) { if (find.equals(replace)) { // Short-cut common case. @@ -691,7 +698,7 @@ public static Replacement replace(MutableRel query, MutableRel find, } /** Helper for {@link #replace}. */ - private static Replacement replaceRecurse(MutableRel query, + private static @Nullable Replacement replaceRecurse(MutableRel query, MutableRel find, MutableRel replace) { if (find.equals(query)) { query.replaceInParent(replace); @@ -735,7 +742,7 @@ private static void reverseSubstitute(RelBuilder relBuilder, Holder query, redoReplacement(matches.get(0)); } - private UnifyResult matchRecurse(MutableRel target) { + private @Nullable UnifyResult matchRecurse(MutableRel target) { assert false; // not called final List targetInputs = target.getInputs(); MutableRel queryParent = null; @@ -799,7 +806,7 @@ private UnifyResult matchRecurse(MutableRel target) { System.out.println( "Unify failed:" + "\nQuery:\n" - + queryParent.toString() + + queryParent + "\nTarget:\n" + target.toString() + "\n"); @@ -807,7 +814,7 @@ private UnifyResult matchRecurse(MutableRel target) { return null; } - private UnifyResult apply(UnifyRule rule, MutableRel query, + private @Nullable UnifyResult apply(UnifyRule rule, MutableRel query, MutableRel target) { final UnifyRuleCall call = new UnifyRuleCall(rule, query, target, ImmutableList.of()); @@ -891,9 +898,9 @@ protected UnifyRule(int slotCount, Operand queryOperand, * * @param call Input parameters */ - protected abstract UnifyResult apply(UnifyRuleCall call); + protected abstract @Nullable UnifyResult apply(UnifyRuleCall call); - protected UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query, + protected @Nullable UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query, MutableRel target) { if (queryOperand.matches(visitor, query)) { if (targetOperand.matches(visitor, target)) { @@ -928,10 +935,10 @@ protected class UnifyRuleCall { public UnifyRuleCall(UnifyRule rule, MutableRel query, MutableRel target, ImmutableList slots) { - this.rule = Objects.requireNonNull(rule); - this.query = Objects.requireNonNull(query); - this.target = Objects.requireNonNull(target); - this.slots = Objects.requireNonNull(slots); + this.rule = requireNonNull(rule); + this.query = requireNonNull(query); + this.target = requireNonNull(target); + this.slots = requireNonNull(slots); } public UnifyResult result(MutableRel result) { @@ -991,6 +998,7 @@ assert equalType("query", call.query, "result", result, /** Abstract base class for implementing {@link UnifyRule}. */ protected abstract static class AbstractUnifyRule extends UnifyRule { + @SuppressWarnings("method.invocation.invalid") protected AbstractUnifyRule(Operand queryOperand, Operand targetOperand, int slotCount) { super(slotCount, queryOperand, targetOperand); @@ -1047,7 +1055,7 @@ private TrivialRule() { super(any(MutableRel.class), any(MutableRel.class), 0); } - @Override public UnifyResult apply(UnifyRuleCall call) { + @Override public @Nullable UnifyResult apply(UnifyRuleCall call) { if (call.query.equals(call.target)) { return call.result(call.target); } @@ -1069,7 +1077,7 @@ private ScanToCalcUnifyRule() { operand(MutableCalc.class, any(MutableScan.class)), 0); } - @Override protected UnifyResult apply(UnifyRuleCall call) { + @Override protected @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableScan query = (MutableScan) call.query; @@ -1087,7 +1095,7 @@ private ScanToCalcUnifyRule() { final RexShuttle shuttle = getRexShuttle(targetProjs); final List compenProjs; try { - compenProjs = (List) shuttle.apply( + compenProjs = shuttle.apply( rexBuilder.identityProjects(query.rowType)); } catch (MatchFailed e) { return null; @@ -1120,7 +1128,7 @@ private CalcToCalcUnifyRule() { operand(MutableCalc.class, target(0)), 1); } - @Override public UnifyResult apply(UnifyRuleCall call) { + @Override public @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableCalc query = (MutableCalc) call.query; final Pair> queryExplained = explainCalc(query); final RexNode queryCond = queryExplained.left; @@ -1190,7 +1198,7 @@ private JoinOnLeftCalcToJoinUnifyRule() { operand(MutableJoin.class, target(0), target(1)), 2); } - @Override protected UnifyResult apply(UnifyRuleCall call) { + @Override protected @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableJoin query = (MutableJoin) call.query; final MutableCalc qInput0 = (MutableCalc) query.getLeft(); final MutableRel qInput1 = query.getRight(); @@ -1215,7 +1223,7 @@ private JoinOnLeftCalcToJoinUnifyRule() { } // Try pulling up MutableCalc only when Join condition references mapping. final List identityProjects = - (List) rexBuilder.identityProjects(qInput1.rowType); + rexBuilder.identityProjects(qInput1.rowType); if (!referenceByMapping(query.condition, qInput0Projs, identityProjects)) { return null; } @@ -1276,7 +1284,7 @@ private JoinOnRightCalcToJoinUnifyRule() { operand(MutableJoin.class, target(0), target(1)), 2); } - @Override protected UnifyResult apply(UnifyRuleCall call) { + @Override protected @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableJoin query = (MutableJoin) call.query; final MutableRel qInput0 = query.getLeft(); final MutableCalc qInput1 = (MutableCalc) query.getRight(); @@ -1301,7 +1309,7 @@ private JoinOnRightCalcToJoinUnifyRule() { } // Try pulling up MutableCalc only when Join condition references mapping. final List identityProjects = - (List) rexBuilder.identityProjects(qInput0.rowType); + rexBuilder.identityProjects(qInput0.rowType); if (!referenceByMapping(query.condition, identityProjects, qInput1Projs)) { return null; } @@ -1364,7 +1372,7 @@ private JoinOnCalcsToJoinUnifyRule() { operand(MutableJoin.class, target(0), target(1)), 2); } - @Override protected UnifyResult apply(UnifyRuleCall call) { + @Override protected @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableJoin query = (MutableJoin) call.query; final MutableCalc qInput0 = (MutableCalc) query.getLeft(); final MutableCalc qInput1 = (MutableCalc) query.getRight(); @@ -1455,7 +1463,7 @@ private AggregateOnCalcToAggregateUnifyRule() { operand(MutableAggregate.class, target(0)), 1); } - @Override protected UnifyResult apply(UnifyRuleCall call) { + @Override protected @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableAggregate query = (MutableAggregate) call.query; final MutableCalc qInput = (MutableCalc) query.getInput(); final Pair> qInputExplained = explainCalc(qInput); @@ -1529,6 +1537,9 @@ private AggregateOnCalcToAggregateUnifyRule() { if (unifiedAggregate instanceof MutableCalc) { final MutableCalc newCompenCalc = mergeCalc(rexBuilder, compenCalc, (MutableCalc) unifiedAggregate); + if (newCompenCalc == null) { + return null; + } return tryMergeParentCalcAndGenResult(call, newCompenCalc); } else { return tryMergeParentCalcAndGenResult(call, compenCalc); @@ -1552,7 +1563,7 @@ private AggregateToAggregateUnifyRule() { operand(MutableAggregate.class, target(0)), 1); } - @Override public UnifyResult apply(UnifyRuleCall call) { + @Override public @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableAggregate query = (MutableAggregate) call.query; final MutableAggregate target = (MutableAggregate) call.target; assert query != target; @@ -1588,7 +1599,7 @@ private UnionToUnionUnifyRule() { super(any(MutableUnion.class), any(MutableUnion.class), 0); } - @Override public UnifyResult apply(UnifyRuleCall call) { + @Override public @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableUnion query = (MutableUnion) call.query; final MutableUnion target = (MutableUnion) call.target; final List queryInputs = new ArrayList<>(query.getInputs()); @@ -1615,7 +1626,7 @@ private UnionOnCalcsToUnionUnifyRule() { super(any(MutableUnion.class), any(MutableUnion.class), 0); } - @Override public UnifyResult apply(UnifyRuleCall call) { + @Override public @Nullable UnifyResult apply(UnifyRuleCall call) { return setOpApply(call); } } @@ -1633,7 +1644,7 @@ private IntersectToIntersectUnifyRule() { super(any(MutableIntersect.class), any(MutableIntersect.class), 0); } - @Override public UnifyResult apply(UnifyRuleCall call) { + @Override public @Nullable UnifyResult apply(UnifyRuleCall call) { final MutableIntersect query = (MutableIntersect) call.query; final MutableIntersect target = (MutableIntersect) call.target; final List queryInputs = new ArrayList<>(query.getInputs()); @@ -1660,7 +1671,7 @@ private IntersectOnCalcsToIntersectUnifyRule() { super(any(MutableIntersect.class), any(MutableIntersect.class), 0); } - @Override public UnifyResult apply(UnifyRuleCall call) { + @Override public @Nullable UnifyResult apply(UnifyRuleCall call) { return setOpApply(call); } } @@ -1672,7 +1683,7 @@ private IntersectOnCalcsToIntersectUnifyRule() { * * @param call Input parameters */ - private static UnifyResult setOpApply(UnifyRuleCall call) { + private static @Nullable UnifyResult setOpApply(UnifyRuleCall call) { if (call.query instanceof MutableMinus && call.target instanceof MutableMinus) { return null; @@ -1702,8 +1713,9 @@ && sameRelCollectionNoOrderConsidered(queryGrandInputs, targetInputs)) { final Pair> queryInputExplained = explainCalc(queryInputs.get(i)); // Matching fails when filtering conditions are not equal or projects are not equal. - if (!splitFilter(call.getSimplify(), queryInputExplained0.left, - queryInputExplained.left).isAlwaysTrue()) { + RexNode residue = splitFilter(call.getSimplify(), queryInputExplained0.left, + queryInputExplained.left); + if (residue == null || !residue.isAlwaysTrue()) { return null; } for (Pair pair : Pair.zip( @@ -1782,7 +1794,7 @@ private static UnifyResult tryMergeParentCalcAndGenResult( } /** Merge two MutableCalc together. */ - private static MutableCalc mergeCalc( + private static @Nullable MutableCalc mergeCalc( RexBuilder rexBuilder, MutableCalc topCalc, MutableCalc bottomCalc) { RexProgram topProgram = topCalc.program; if (RexOver.containsOver(topProgram)) { @@ -1810,8 +1822,8 @@ private static RexShuttle getExpandShuttle(RexProgram rexProgram) { /** Check if condition cond0 implies cond1. */ private static boolean implies( RelOptCluster cluster, RexNode cond0, RexNode cond1, RelDataType rowType) { - RexExecutorImpl rexImpl = - (RexExecutorImpl) cluster.getPlanner().getExecutor(); + RexExecutor rexImpl = + Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR); RexImplicationChecker rexImplicationChecker = new RexImplicationChecker(cluster.getRexBuilder(), rexImpl, rowType); return rexImplicationChecker.implies(cond0, cond1); @@ -1841,7 +1853,7 @@ private static boolean referenceByMapping( return true; } - private static JoinRelType sameJoinType(JoinRelType type0, JoinRelType type1) { + private static @Nullable JoinRelType sameJoinType(JoinRelType type0, JoinRelType type1) { if (type0 == type1) { return type0; } else { @@ -1859,8 +1871,8 @@ public static MutableAggregate permute(MutableAggregate aggregate, return MutableAggregate.of(input, groupSet, groupSets, aggregateCalls); } - public static MutableRel unifyAggregates(MutableAggregate query, - RexNode targetCond, MutableAggregate target) { + public static @Nullable MutableRel unifyAggregates(MutableAggregate query, + @Nullable RexNode targetCond, MutableAggregate target) { MutableRel result; RexBuilder rexBuilder = query.cluster.getRexBuilder(); Map targetCondConstantMap = @@ -1890,7 +1902,7 @@ public static MutableRel unifyAggregates(MutableAggregate query, || (compenGroupSet != null && constantCondInputRefs.containsAll(compenGroupSet))) { int projOffset = 0; if (!query.groupSets.equals(target.groupSets)) { - projOffset = compenGroupSet.size(); + projOffset = requireNonNull(compenGroupSet, "compenGroupSet").size(); } // Same level of aggregation. Generate a project. final List projects = new ArrayList<>(); @@ -1969,7 +1981,7 @@ public static MutableRel unifyAggregates(MutableAggregate query, return result; } - public static SqlAggFunction getRollup(SqlAggFunction aggregation) { + public static @Nullable SqlAggFunction getRollup(SqlAggFunction aggregation) { if (aggregation == SqlStdOperatorTable.SUM || aggregation == SqlStdOperatorTable.MIN || aggregation == SqlStdOperatorTable.MAX diff --git a/core/src/main/java/org/apache/calcite/plan/TableAccessMap.java b/core/src/main/java/org/apache/calcite/plan/TableAccessMap.java index fd9d6f4120fe..98a4fa52abb0 100644 --- a/core/src/main/java/org/apache/calcite/plan/TableAccessMap.java +++ b/core/src/main/java/org/apache/calcite/plan/TableAccessMap.java @@ -20,6 +20,8 @@ import org.apache.calcite.rel.RelVisitor; import org.apache.calcite.rel.core.TableModify; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -106,6 +108,7 @@ public TableAccessMap(List table, Mode mode) { /** * Returns a set of qualified names for all tables accessed. */ + @SuppressWarnings("return.type.incompatible") public Set> getTablesAccessed() { return accessMap.keySet(); } @@ -173,7 +176,7 @@ private class TableRelVisitor extends RelVisitor { @Override public void visit( RelNode p, int ordinal, - RelNode parent) { + @Nullable RelNode parent) { super.visit(p, ordinal, parent); RelOptTable table = p.getTable(); if (table == null) { diff --git a/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java b/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java index f025489a1814..47f482a61433 100644 --- a/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java +++ b/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java @@ -22,14 +22,16 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; -import javax.annotation.Nonnull; /** * Utilities for {@link RelOptTable.ViewExpander} and * {@link RelOptTable.ToRelContext}. */ -@Nonnull +@NonNull public abstract class ViewExpanders { private ViewExpanders() {} @@ -48,7 +50,7 @@ public static RelOptTable.ToRelContext toRelContext( } @Override public RelRoot expandView(RelDataType rowType, String queryString, - List schemaPath, List viewPath) { + List schemaPath, @Nullable List viewPath) { return viewExpander.expandView(rowType, queryString, schemaPath, viewPath); } @@ -77,7 +79,7 @@ public static RelOptTable.ToRelContext simpleContext( } @Override public RelRoot expandView(RelDataType rowType, String queryString, - List schemaPath, List viewPath) { + List schemaPath, @Nullable List viewPath) { throw new UnsupportedOperationException(); } diff --git a/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java index 55c9be8ca9de..758695b9b444 100644 --- a/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java +++ b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java @@ -32,6 +32,7 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.trace.CalciteLogger; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.LoggerFactory; import java.math.BigDecimal; @@ -44,9 +45,9 @@ public class VisitorDataContext implements DataContext { private static final CalciteLogger LOGGER = new CalciteLogger(LoggerFactory.getLogger(VisitorDataContext.class.getName())); - private final Object[] values; + private final @Nullable Object[] values; - public VisitorDataContext(Object[] values) { + public VisitorDataContext(@Nullable Object[] values) { this.values = values; } @@ -62,25 +63,25 @@ public VisitorDataContext(Object[] values) { throw new RuntimeException("Unsupported"); } - @Override public Object get(String name) { + @Override public @Nullable Object get(String name) { if (name.equals("inputRecord")) { return values; } else { return null; } } - public static DataContext of(RelNode targetRel, LogicalFilter queryRel) { + public static @Nullable DataContext of(RelNode targetRel, LogicalFilter queryRel) { return of(targetRel.getRowType(), queryRel.getCondition()); } - public static DataContext of(RelDataType rowType, RexNode rex) { + public static @Nullable DataContext of(RelDataType rowType, RexNode rex) { final int size = rowType.getFieldList().size(); - final Object[] values = new Object[size]; final List operands = ((RexCall) rex).getOperands(); final RexNode firstOperand = operands.get(0); final RexNode secondOperand = operands.get(1); final Pair value = getValue(firstOperand, secondOperand); if (value != null) { + final @Nullable Object[] values = new Object[size]; int index = value.getKey(); values[index] = value.getValue(); return new VisitorDataContext(values); @@ -89,11 +90,11 @@ public static DataContext of(RelDataType rowType, RexNode rex) { } } - public static DataContext of(RelDataType rowType, - List> usageList) { + public static @Nullable DataContext of(RelDataType rowType, + List> usageList) { final int size = rowType.getFieldList().size(); - final Object[] values = new Object[size]; - for (Pair elem : usageList) { + final @Nullable Object[] values = new Object[size]; + for (Pair elem : usageList) { Pair value = getValue(elem.getKey(), elem.getValue()); if (value == null) { LOGGER.warn("{} is not handled for {} for checking implication", @@ -106,7 +107,8 @@ public static DataContext of(RelDataType rowType, return new VisitorDataContext(values); } - public static Pair getValue(RexNode inputRef, RexNode literal) { + public static @Nullable Pair getValue( + @Nullable RexNode inputRef, @Nullable RexNode literal) { inputRef = inputRef == null ? null : RexUtil.removeCast(inputRef); literal = literal == null ? null : RexUtil.removeCast(literal); @@ -147,12 +149,13 @@ public static DataContext of(RelDataType rowType, return Pair.of(index, rexLiteral.getValueAs(String.class)); default: // TODO: Support few more supported cases + Comparable value = rexLiteral.getValue(); LOGGER.warn("{} for value of class {} is being handled in default way", - type.getSqlTypeName(), rexLiteral.getValue().getClass()); - if (rexLiteral.getValue() instanceof NlsString) { - return Pair.of(index, ((NlsString) rexLiteral.getValue()).getValue()); + type.getSqlTypeName(), value == null ? null : value.getClass()); + if (value instanceof NlsString) { + return Pair.of(index, ((NlsString) value).getValue()); } else { - return Pair.of(index, rexLiteral.getValue()); + return Pair.of(index, value); } } } diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepInstruction.java b/core/src/main/java/org/apache/calcite/plan/hep/HepInstruction.java index 11eaf2718327..46f1a729cd3f 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepInstruction.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepInstruction.java @@ -18,6 +18,9 @@ import org.apache.calcite.plan.RelOptRule; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -42,13 +45,13 @@ void initialize(boolean clearCache) { * * @param rule type */ static class RuleClass extends HepInstruction { - Class ruleClass; + @Nullable Class ruleClass; /** * Actual rule set instantiated during planning by filtering all of the * planner's rules through ruleClass. */ - Set ruleSet; + @Nullable Set ruleSet; @Override void initialize(boolean clearCache) { if (!clearCache) { @@ -68,7 +71,7 @@ static class RuleCollection extends HepInstruction { /** * Collection of rules to apply. */ - Collection rules; + @Nullable Collection rules; @Override void execute(HepPlanner planner) { planner.executeInstruction(this); @@ -83,7 +86,7 @@ static class ConverterRules extends HepInstruction { * Actual rule set instantiated during planning by filtering all of the * planner's rules, looking for the desired converters. */ - Set ruleSet; + @MonotonicNonNull Set ruleSet; @Override void execute(HepPlanner planner) { planner.executeInstruction(this); @@ -92,7 +95,7 @@ static class ConverterRules extends HepInstruction { /** Instruction that finds common relational sub-expressions. */ static class CommonRelSubExprRules extends HepInstruction { - Set ruleSet; + @Nullable Set ruleSet; @Override void execute(HepPlanner planner) { planner.executeInstruction(this); @@ -104,13 +107,13 @@ static class RuleInstance extends HepInstruction { /** * Description to look for, or null if rule specified explicitly. */ - String ruleDescription; + @Nullable String ruleDescription; /** * Explicitly specified rule, or rule looked up by planner from * description. */ - RelOptRule rule; + @Nullable RelOptRule rule; @Override void initialize(boolean clearCache) { if (!clearCache) { @@ -130,7 +133,7 @@ static class RuleInstance extends HepInstruction { /** Instruction that sets match order. */ static class MatchOrder extends HepInstruction { - HepMatchOrder order; + @Nullable HepMatchOrder order; @Override void execute(HepPlanner planner) { planner.executeInstruction(this); @@ -148,10 +151,12 @@ static class MatchLimit extends HepInstruction { /** Instruction that executes a sub-program. */ static class Subprogram extends HepInstruction { - HepProgram subprogram; + @Nullable HepProgram subprogram; @Override void initialize(boolean clearCache) { - subprogram.initialize(clearCache); + if (subprogram != null) { + subprogram.initialize(clearCache); + } } @Override void execute(HepPlanner planner) { @@ -161,7 +166,7 @@ static class Subprogram extends HepInstruction { /** Instruction that begins a group. */ static class BeginGroup extends HepInstruction { - EndGroup endGroup; + @Nullable EndGroup endGroup; @Override void initialize(boolean clearCache) { } @@ -177,7 +182,7 @@ static class EndGroup extends HepInstruction { * Actual rule set instantiated during planning by collecting grouped * rules. */ - Set ruleSet; + @Nullable Set ruleSet; boolean collecting; diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java index 32a6b5b56f1f..c0af0fdafd3b 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java @@ -53,6 +53,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -66,6 +68,10 @@ import java.util.Queue; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * HepPlanner is a heuristic implementation of the {@link RelOptPlanner} * interface. @@ -75,11 +81,11 @@ public class HepPlanner extends AbstractRelOptPlanner { private final HepProgram mainProgram; - private HepProgram currentProgram; + private @Nullable HepProgram currentProgram; - private HepRelVertex root; + private @Nullable HepRelVertex root; - private RelTraitSet requestedRootTraits; + private @Nullable RelTraitSet requestedRootTraits; /** * {@link RelDataType} is represented with its field types as {@code List}. @@ -126,7 +132,7 @@ public HepPlanner(HepProgram program) { * @param program program controlling rule application * @param context to carry while planning */ - public HepPlanner(HepProgram program, Context context) { + public HepPlanner(HepProgram program, @Nullable Context context) { this(program, context, false, null, RelOptCostImpl.FACTORY); } @@ -141,9 +147,9 @@ public HepPlanner(HepProgram program, Context context) { */ public HepPlanner( HepProgram program, - Context context, + @Nullable Context context, boolean noDag, - Function2 onCopyHook, + @Nullable Function2 onCopyHook, RelOptCostFactory costFactory) { super(costFactory, context); this.mainProgram = program; @@ -160,7 +166,7 @@ public HepPlanner( } // implement RelOptPlanner - @Override public RelNode getRoot() { + @Override public @Nullable RelNode getRoot() { return root; } @@ -176,7 +182,7 @@ public HepPlanner( @Override public RelNode changeTraits(RelNode rel, RelTraitSet toTraits) { // Ignore traits, except for the root, where we remember // what the final conversion should be. - if ((rel == root) || (rel == root.getCurrentRel())) { + if ((rel == root) || (rel == requireNonNull(root, "root").getCurrentRel())) { requestedRootTraits = toTraits; } return rel; @@ -191,7 +197,7 @@ public HepPlanner( // Get rid of everything except what's in the final plan. collectGarbage(); dumpRuleAttemptsInfo(); - return buildFinalPlan(root); + return buildFinalPlan(requireNonNull(root, "root")); } private void executeProgram(HepProgram program) { @@ -218,12 +224,14 @@ private void executeProgram(HepProgram program) { void executeInstruction( HepInstruction.MatchLimit instruction) { LOGGER.trace("Setting match limit to {}", instruction.limit); + assert currentProgram != null : "currentProgram must not be null"; currentProgram.matchLimit = instruction.limit; } void executeInstruction( HepInstruction.MatchOrder instruction) { LOGGER.trace("Setting match order to {}", instruction.order); + assert currentProgram != null : "currentProgram must not be null"; currentProgram.matchOrder = instruction.order; } @@ -252,15 +260,17 @@ void executeInstruction( return; } LOGGER.trace("Applying rule class {}", instruction.ruleClass); - if (instruction.ruleSet == null) { - instruction.ruleSet = new LinkedHashSet<>(); + Set ruleSet = instruction.ruleSet; + if (ruleSet == null) { + instruction.ruleSet = ruleSet = new LinkedHashSet<>(); + Class ruleClass = requireNonNull(instruction.ruleClass, "instruction.ruleClass"); for (RelOptRule rule : mapDescToRule.values()) { - if (instruction.ruleClass.isInstance(rule)) { - instruction.ruleSet.add(rule); + if (ruleClass.isInstance(rule)) { + ruleSet.add(rule); } } } - applyRules(instruction.ruleSet, true); + applyRules(ruleSet, true); } void executeInstruction( @@ -268,11 +278,12 @@ void executeInstruction( if (skippingGroup()) { return; } + assert instruction.rules != null : "instruction.rules must not be null"; applyRules(instruction.rules, true); } private boolean skippingGroup() { - if (currentProgram.group != null) { + if (currentProgram != null && currentProgram.group != null) { // Skip if we've already collected the ruleset. return !currentProgram.group.collecting; } else { @@ -283,6 +294,7 @@ private boolean skippingGroup() { void executeInstruction( HepInstruction.ConverterRules instruction) { + assert currentProgram != null : "currentProgram must not be null"; assert currentProgram.group == null; if (instruction.ruleSet == null) { instruction.ruleSet = new LinkedHashSet<>(); @@ -309,17 +321,19 @@ void executeInstruction( } void executeInstruction(HepInstruction.CommonRelSubExprRules instruction) { + assert currentProgram != null : "currentProgram must not be null"; assert currentProgram.group == null; - if (instruction.ruleSet == null) { - instruction.ruleSet = new LinkedHashSet<>(); + Set ruleSet = instruction.ruleSet; + if (ruleSet == null) { + instruction.ruleSet = ruleSet = new LinkedHashSet<>(); for (RelOptRule rule : mapDescToRule.values()) { if (!(rule instanceof CommonRelSubExprRule)) { continue; } - instruction.ruleSet.add(rule); + ruleSet.add(rule); } } - applyRules(instruction.ruleSet, true); + applyRules(ruleSet, true); } void executeInstruction( @@ -327,7 +341,7 @@ void executeInstruction( LOGGER.trace("Entering subprogram"); for (;;) { int nTransformationsBefore = nTransformations; - executeProgram(instruction.subprogram); + executeProgram(requireNonNull(instruction.subprogram, "instruction.subprogram")); if (nTransformations == nTransformationsBefore) { // Nothing happened this time around. break; @@ -338,6 +352,7 @@ void executeInstruction( void executeInstruction( HepInstruction.BeginGroup instruction) { + assert currentProgram != null : "currentProgram must not be null"; assert currentProgram.group == null; currentProgram.group = instruction.endGroup; LOGGER.trace("Entering group"); @@ -345,10 +360,11 @@ void executeInstruction( void executeInstruction( HepInstruction.EndGroup instruction) { + assert currentProgram != null : "currentProgram must not be null"; assert currentProgram.group == instruction; currentProgram.group = null; instruction.collecting = false; - applyRules(instruction.ruleSet, true); + applyRules(requireNonNull(instruction.ruleSet, "instruction.ruleSet"), true); LOGGER.trace("Leaving group"); } @@ -363,6 +379,7 @@ private int depthFirstApply(Iterator iter, if (newVertex == null || newVertex == vertex) { continue; } + assert currentProgram != null : "currentProgram must not be null"; ++nMatches; if (nMatches >= currentProgram.matchLimit) { return nMatches; @@ -382,14 +399,18 @@ private int depthFirstApply(Iterator iter, private void applyRules( Collection rules, boolean forceConversions) { + assert currentProgram != null : "currentProgram must not be null"; if (currentProgram.group != null) { assert currentProgram.group.collecting; - currentProgram.group.ruleSet.addAll(rules); + Set ruleSet = requireNonNull(currentProgram.group.ruleSet, + "currentProgram.group.ruleSet"); + ruleSet.addAll(rules); return; } LOGGER.trace("Applying rule set {}", rules); + requireNonNull(currentProgram, "currentProgram"); boolean fullRestartAfterTransformation = currentProgram.matchOrder != HepMatchOrder.ARBITRARY && currentProgram.matchOrder != HepMatchOrder.DEPTH_FIRST; @@ -398,7 +419,7 @@ private void applyRules( boolean fixedPoint; do { - Iterator iter = getGraphIterator(root); + Iterator iter = getGraphIterator(requireNonNull(root, "root")); fixedPoint = true; while (iter.hasNext()) { HepRelVertex vertex = iter.next(); @@ -409,20 +430,21 @@ private void applyRules( continue; } ++nMatches; - if (nMatches >= currentProgram.matchLimit) { + if (nMatches >= requireNonNull(currentProgram, "currentProgram").matchLimit) { return; } if (fullRestartAfterTransformation) { - iter = getGraphIterator(root); + iter = getGraphIterator(requireNonNull(root, "root")); } else { // To the extent possible, pick up where we left // off; have to create a new iterator because old // one was invalidated by transformation. iter = getGraphIterator(newVertex); - if (currentProgram.matchOrder == HepMatchOrder.DEPTH_FIRST) { + if (requireNonNull(currentProgram, "currentProgram").matchOrder + == HepMatchOrder.DEPTH_FIRST) { nMatches = depthFirstApply(iter, rules, forceConversions, nMatches); - if (nMatches >= currentProgram.matchLimit) { + if (nMatches >= requireNonNull(currentProgram, "currentProgram").matchLimit) { return; } } @@ -447,7 +469,8 @@ private Iterator getGraphIterator(HepRelVertex start) { // better optimizer performance. collectGarbage(); - switch (currentProgram.matchOrder) { + assert currentProgram != null : "currentProgram must not be null"; + switch (requireNonNull(currentProgram.matchOrder, "currentProgram.matchOrder")) { case ARBITRARY: case DEPTH_FIRST: return DepthFirstIterator.of(graph, start).iterator(); @@ -480,7 +503,7 @@ private Iterator getGraphIterator(HepRelVertex start) { } } - private HepRelVertex applyRule( + private @Nullable HepRelVertex applyRule( RelOptRule rule, HepRelVertex vertex, boolean forceConversions) { @@ -668,7 +691,7 @@ private boolean matchOperands( private HepRelVertex applyTransformationResults( HepRelVertex vertex, HepRuleCall call, - RelTrait parentTrait) { + @Nullable RelTrait parentTrait) { // TODO jvs 5-Apr-2006: Take the one that gives the best // global cost rather than the best local cost. That requires // "tentative" graph edits. @@ -691,7 +714,10 @@ private HepRelVertex applyTransformationResults( LOGGER.trace("considering {} with cumulative cost={} and rowcount={}", rel, thisCost, mq.getRowCount(rel)); } - if ((bestRel == null) || thisCost.isLt(bestCost)) { + if (thisCost == null) { + continue; + } + if (bestRel == null || thisCost.isLt(castNonNull(bestCost))) { bestRel = rel; bestCost = thisCost; } @@ -701,7 +727,7 @@ private HepRelVertex applyTransformationResults( ++nTransformations; notifyTransformation( call, - bestRel, + requireNonNull(bestRel, "bestRel"), true); // Before we add the result, make a copy of the list of vertex's @@ -763,7 +789,7 @@ private HepRelVertex applyTransformationResults( // implement RelOptPlanner @Override public RelNode register( RelNode rel, - RelNode equivRel) { + @Nullable RelNode equivRel) { // Ignore; this call is mostly to tell Volcano how to avoid // infinite loops. return rel; @@ -774,7 +800,7 @@ private HepRelVertex applyTransformationResults( } // implement RelOptPlanner - @Override public RelNode ensureRegistered(RelNode rel, RelNode equivRel) { + @Override public RelNode ensureRegistered(RelNode rel, @Nullable RelNode equivRel) { return rel; } @@ -957,6 +983,7 @@ private void collectGarbage() { // Yer basic mark-and-sweep. final Set rootSet = new HashSet<>(); + HepRelVertex root = requireNonNull(this.root, "this.root"); if (graph.vertexSet().contains(root)) { BreadthFirstIterator.reachable(rootSet, graph, root); } @@ -1008,6 +1035,11 @@ private void dumpGraph() { assertNoCycles(); + HepRelVertex root = this.root; + if (root == null) { + LOGGER.trace("dumpGraph: root is null"); + return; + } final RelMetadataQuery mq = root.getCluster().getMetadataQuery(); final StringBuilder sb = new StringBuilder(); sb.append("\nBreadth-first from root: {\n"); diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepProgram.java b/core/src/main/java/org/apache/calcite/plan/hep/HepProgram.java index 83e5c88d685d..893b2403061c 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepProgram.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepProgram.java @@ -18,6 +18,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -43,9 +45,9 @@ public class HepProgram { int matchLimit; - HepMatchOrder matchOrder; + @Nullable HepMatchOrder matchOrder; - HepInstruction.EndGroup group; + HepInstruction.@Nullable EndGroup group; //~ Constructors ----------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepProgramBuilder.java b/core/src/main/java/org/apache/calcite/plan/hep/HepProgramBuilder.java index f1bd87467d6b..8fe1f887c782 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepProgramBuilder.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepProgramBuilder.java @@ -20,10 +20,13 @@ import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.plan.RelOptRule; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Objects; + +import static java.util.Objects.requireNonNull; /** * HepProgramBuilder creates instances of {@link HepProgram}. @@ -33,7 +36,7 @@ public class HepProgramBuilder { private final List instructions = new ArrayList<>(); - private HepInstruction.BeginGroup group; + private HepInstruction.@Nullable BeginGroup group; //~ Constructors ----------------------------------------------------------- @@ -112,7 +115,7 @@ public HepProgramBuilder addRuleCollection(Collection rules) { public HepProgramBuilder addRuleInstance(RelOptRule rule) { HepInstruction.RuleInstance instruction = new HepInstruction.RuleInstance(); - instruction.rule = Objects.requireNonNull(rule); + instruction.rule = requireNonNull(rule); instructions.add(instruction); return this; } @@ -162,7 +165,7 @@ public HepProgramBuilder addGroupEnd() { assert group != null; HepInstruction.EndGroup instruction = new HepInstruction.EndGroup(); instructions.add(instruction); - group.endGroup = instruction; + requireNonNull(group, "group").endGroup = instruction; group = null; return this; } diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java index 960f660b73b7..825b4204ce57 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java @@ -26,8 +26,12 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; +import static java.util.Objects.requireNonNull; + /** * HepRelMetadataProvider implements the {@link RelMetadataProvider} interface * by combining metadata from the rels inside of a {@link HepRelVertex}. @@ -35,7 +39,7 @@ class HepRelMetadataProvider implements RelMetadataProvider { //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj instanceof HepRelMetadataProvider; } @@ -43,7 +47,7 @@ class HepRelMetadataProvider implements RelMetadataProvider { return 107; } - @Override public UnboundMetadata apply( + @Override public <@Nullable M extends @Nullable Metadata> UnboundMetadata apply( Class relClass, final Class metadataClass) { return (rel, mq) -> { @@ -53,9 +57,12 @@ class HepRelMetadataProvider implements RelMetadataProvider { HepRelVertex vertex = (HepRelVertex) rel; final RelNode rel2 = vertex.getCurrentRel(); UnboundMetadata function = - rel.getCluster().getMetadataProvider().apply(rel2.getClass(), - metadataClass); - return function.bind(rel2, mq); + requireNonNull(rel.getCluster().getMetadataProvider(), "metadataProvider") + .apply(rel2.getClass(), metadataClass); + return requireNonNull( + function, + () -> "no metadata provider for class " + metadataClass) + .bind(rel2, mq); }; } diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java b/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java index add516a3f10a..cd4ff1424475 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepRelVertex.java @@ -25,6 +25,8 @@ import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rel.type.RelDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -91,7 +93,7 @@ public RelNode getCurrentRel() { return currentRel; } - @Override public boolean deepEquals(Object obj) { + @Override public boolean deepEquals(@Nullable Object obj) { return this == obj || (obj instanceof HepRelVertex && currentRel == ((HepRelVertex) obj).currentRel); diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java b/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java index e02d595b6c77..3ed3822b41a6 100644 --- a/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java +++ b/core/src/main/java/org/apache/calcite/plan/hep/HepRuleCall.java @@ -23,6 +23,8 @@ import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -44,7 +46,7 @@ public class HepRuleCall extends RelOptRuleCall { RelOptRuleOperand operand, RelNode[] rels, Map> nodeChildren, - List parents) { + @Nullable List parents) { super(planner, operand, rels, nodeChildren, parents); results = new ArrayList<>(); diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/AbstractConverter.java b/core/src/main/java/org/apache/calcite/plan/volcano/AbstractConverter.java index 7d36ee0fb2a8..2844152402ed 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/AbstractConverter.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/AbstractConverter.java @@ -30,6 +30,8 @@ import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.tools.RelBuilderFactory; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -51,7 +53,7 @@ public class AbstractConverter extends ConverterImpl { public AbstractConverter( RelOptCluster cluster, RelSubset rel, - RelTraitDef traitDef, + @Nullable RelTraitDef traitDef, RelTraitSet traits) { super(cluster, traitDef, traits, rel); assert traits.allSimple(); diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/Dumpers.java b/core/src/main/java/org/apache/calcite/plan/volcano/Dumpers.java index 488bfcc632b5..0e25bce5b8e8 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/Dumpers.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/Dumpers.java @@ -26,6 +26,7 @@ import com.google.common.collect.Ordering; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; import java.io.PrintWriter; import java.io.StringWriter; @@ -38,6 +39,8 @@ import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Utility class to dump state of VolcanoPlanner. */ @@ -66,7 +69,7 @@ static String provenance( final PrintWriter pw = new PrintWriter(sw); final List nodes = new ArrayList<>(); new RelVisitor() { - @Override public void visit(RelNode node, int ordinal, RelNode parent) { + @Override public void visit(RelNode node, int ordinal, @Nullable RelNode parent) { nodes.add(node); super.visit(node, ordinal, parent); } @@ -140,6 +143,10 @@ static void dumpSets(VolcanoPlanner planner, PrintWriter pw) { planner.getSubset( input, input.getTraitSet()); + if (inputSubset == null) { + pw.append("no subset found for input ").print(input.getId()); + continue; + } RelSet inputSet = inputSubset.set; if (input instanceof RelSubset) { final Iterator rels = @@ -191,7 +198,12 @@ static void dumpGraphviz(VolcanoPlanner planner, PrintWriter pw) { // Note: rel traitset could be different from its subset.traitset // It can happen due to RelTraitset#simplify // If the traits are different, we want to keep them on a graph - String traits = "." + planner.getSubset(rel).getTraitSet().toString(); + RelSubset relSubset = planner.getSubset(rel); + if (relSubset == null) { + pw.append("no subset found for rel"); + continue; + } + String traits = "." + relSubset.getTraitSet().toString(); String title = rel.toString().replace(traits, ""); if (title.endsWith(")")) { int openParen = title.indexOf('('); @@ -208,7 +220,6 @@ static void dumpGraphviz(VolcanoPlanner planner, PrintWriter pw) { title + "\nrows=" + mq.getRowCount(rel) + ", cost=" + planner.getCost(rel, mq), false); - RelSubset relSubset = planner.getSubset(rel); if (!(rel instanceof AbstractConverter)) { nonEmptySubsets.add(relSubset); } @@ -249,7 +260,11 @@ static void dumpGraphviz(VolcanoPlanner planner, PrintWriter pw) { } for (RelSubset subset : subsetPoset) { - for (RelSubset parent : subsetPoset.getChildren(subset)) { + List children = subsetPoset.getChildren(subset); + if (children == null) { + continue; + } + for (RelSubset parent : children) { pw.print("\t\tsubset"); pw.print(subset.getId()); pw.print(" -> subset"); @@ -263,11 +278,15 @@ static void dumpGraphviz(VolcanoPlanner planner, PrintWriter pw) { // Note: it is important that all the links are declared AFTER declaration of the nodes // Otherwise Graphviz creates nodes implicitly, and puts them into a wrong cluster pw.print("\troot -> subset"); - pw.print(planner.root.getId()); + pw.print(requireNonNull(planner.root, "planner.root").getId()); pw.println(";"); for (RelSet set : ordering.immutableSortedCopy(planner.allSets)) { for (RelNode rel : set.rels) { RelSubset relSubset = planner.getSubset(rel); + if (relSubset == null) { + pw.append("no subset found for rel ").print(rel.getId()); + continue; + } pw.print("\tsubset"); pw.print(relSubset.getId()); pw.print(" -> rel"); diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleDriver.java b/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleDriver.java index 3883e48e2804..ed6a379ffbfc 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleDriver.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleDriver.java @@ -21,6 +21,8 @@ import org.slf4j.Logger; +import static java.util.Objects.requireNonNull; + /*** *

The algorithm executes repeatedly. The exact rules * that may be fired varies. @@ -46,7 +48,8 @@ class IterativeRuleDriver implements RuleDriver { @Override public void drive() { while (true) { - LOGGER.debug("PLANNER = {}; COST = {}", this, planner.root.bestCost); + LOGGER.debug("PLANNER = {}; COST = {}", this, + requireNonNull(planner.root, "planner.root").bestCost); VolcanoRuleMatch match = ruleQueue.popMatch(); if (match == null) { diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleQueue.java b/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleQueue.java index b682fee234ba..6b68b1bd38e4 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleQueue.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/IterativeRuleQueue.java @@ -16,12 +16,14 @@ */ package org.apache.calcite.plan.volcano; +import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.rules.SubstitutionRule; import org.apache.calcite.util.trace.CalciteTrace; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.io.PrintWriter; @@ -98,7 +100,7 @@ class IterativeRuleQueue extends RuleQueue { * obsolete set or has been pruned. * */ - public VolcanoRuleMatch popMatch() { + public @Nullable VolcanoRuleMatch popMatch() { dumpPlannerState(); VolcanoRuleMatch match; @@ -110,6 +112,9 @@ public VolcanoRuleMatch popMatch() { dumpRuleQueue(matchList); match = matchList.poll(); + if (match == null) { + return null; + } if (skipMatch(match)) { LOGGER.debug("Skip match: {}", match); @@ -158,7 +163,10 @@ private void dumpPlannerState() { planner.dump(pw); pw.flush(); LOGGER.trace(sw.toString()); - planner.getRoot().getCluster().invalidateMetadataQuery(); + RelNode root = planner.getRoot(); + if (root != null) { + root.getCluster().invalidateMetadataQuery(); + } } } @@ -197,7 +205,7 @@ int size() { return preQueue.size() + queue.size(); } - VolcanoRuleMatch poll() { + @Nullable VolcanoRuleMatch poll() { VolcanoRuleMatch match = preQueue.poll(); if (match == null) { match = queue.poll(); diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java b/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java index 06703f8e56d7..7142a68d5cdf 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/RelSet.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.ArrayList; @@ -40,6 +42,10 @@ import java.util.Set; import java.util.stream.Collectors; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * A RelSet is an equivalence-set of expressions; that is, a set of * expressions which have identical semantics. We are generally interested in @@ -68,13 +74,13 @@ class RelSet { * Set to the superseding set when this is found to be equivalent to another * set. */ - RelSet equivalentSet; - RelNode rel; + @MonotonicNonNull RelSet equivalentSet; + @MonotonicNonNull RelNode rel; /** * Exploring state of current RelSet. */ - ExploringState exploringState; + @Nullable ExploringState exploringState; /** * Records conversions / enforcements that have happened on the @@ -147,7 +153,7 @@ public List getRelsFromAllSubsets() { return rels; } - public RelSubset getSubset(RelTraitSet traits) { + public @Nullable RelSubset getSubset(RelTraitSet traits) { for (RelSubset subset : subsets) { if (subset.getTraitSet().equals(traits)) { return subset; @@ -203,6 +209,7 @@ void addConverters(RelSubset subset, boolean required, if (from == to || to.isEnforceDisabled() || useAbstractConverter + && from.getConvention() != null && !from.getConvention().useAbstractConvertersForConversion( from.getTraitSet(), to.getTraitSet())) { continue; @@ -238,7 +245,10 @@ void addConverters(RelSubset subset, boolean required, enforcer = new AbstractConverter( cluster, from, null, to.getTraitSet()); } else { - enforcer = subset.getConvention().enforce(from, to.getTraitSet()); + Convention convention = requireNonNull( + subset.getConvention(), + () -> "convention is null for " + subset); + enforcer = convention.enforce(from, to.getTraitSet()); } if (enforcer != null) { @@ -287,13 +297,17 @@ RelSubset getOrCreateSubset( } private void postEquivalenceEvent(VolcanoPlanner planner, RelNode rel) { + RelOptListener listener = planner.getListener(); + if (listener == null) { + return; + } RelOptListener.RelEquivalenceEvent event = new RelOptListener.RelEquivalenceEvent( planner, rel, "equivalence class " + id, false); - planner.getListener().relEquivalenceFound(event); + listener.relEquivalenceFound(event); } /** @@ -349,7 +363,7 @@ void mergeWith( assert otherSet.equivalentSet == null; LOGGER.trace("Merge set#{} into set#{}", otherSet.id, id); otherSet.equivalentSet = this; - RelOptCluster cluster = rel.getCluster(); + RelOptCluster cluster = castNonNull(rel).getCluster(); // remove from table boolean existed = planner.allSets.remove(otherSet); @@ -381,7 +395,7 @@ void mergeWith( } // collect RelSubset instances, whose best should be changed - if (otherSubset.bestCost.isLt(subset.bestCost)) { + if (otherSubset.bestCost.isLt(subset.bestCost) && otherSubset.best != null) { changedRels.add(otherSubset.best); } } diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java b/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java index b1e0b6322abe..394dad5a20a3 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/RelSubset.java @@ -41,6 +41,9 @@ import com.google.common.collect.Sets; import org.apiguardian.api.API; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.io.PrintWriter; @@ -57,6 +60,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + + /** * Subset of an equivalence class where all relational expressions have the * same physical properties. @@ -83,7 +89,7 @@ public class RelSubset extends AbstractRelNode { //~ Instance fields -------------------------------------------------------- /** Optimization task state. */ - OptimizeState taskState; + @Nullable OptimizeState taskState; /** Cost of best known plan (it may have improved since). */ RelOptCost bestCost; @@ -92,7 +98,7 @@ public class RelSubset extends AbstractRelNode { final RelSet set; /** Best known plan. */ - RelNode best; + @Nullable RelNode best; /** Timestamp for metadata validity. */ long timestamp; @@ -131,7 +137,7 @@ public class RelSubset extends AbstractRelNode { * A cache that recognize which RelNode has invoked the passThrough method * so as to avoid duplicate invocation. */ - Set passThroughCache; + @Nullable Set passThroughCache; //~ Constructors ----------------------------------------------------------- @@ -142,7 +148,7 @@ public class RelSubset extends AbstractRelNode { super(cluster, traits); this.set = set; assert traits.allSimple(); - computeBestCost(cluster.getPlanner()); + computeBestCost(cluster, cluster.getPlanner()); upperBound = bestCost; } @@ -161,11 +167,21 @@ public class RelSubset extends AbstractRelNode { * {@link RelSet#mergeWith(VolcanoPlanner, RelSet)}. * */ - private void computeBestCost(RelOptPlanner planner) { + @EnsuresNonNull("bestCost") + private void computeBestCost( + @UnderInitialization RelSubset this, + RelOptCluster cluster, + RelOptPlanner planner + ) { bestCost = planner.getCostFactory().makeInfiniteCost(); - final RelMetadataQuery mq = getCluster().getMetadataQuery(); - for (RelNode rel : getRels()) { + final RelMetadataQuery mq = cluster.getMetadataQuery(); + @SuppressWarnings("method.invocation.invalid") + Iterable rels = getRels(); + for (RelNode rel : rels) { final RelOptCost cost = planner.getCost(rel, mq); + if (cost == null) { + continue; + } if (cost.isLt(bestCost)) { bestCost = cost; best = rel; @@ -202,11 +218,11 @@ boolean isEnforceDisabled() { return enforceDisabled; } - public RelNode getBest() { + public @Nullable RelNode getBest() { return best; } - public RelNode getOriginal() { + public @Nullable RelNode getOriginal() { return set.rel; } @@ -229,7 +245,7 @@ public RelNode getOriginal() { if (best != null) { return mq.getRowCount(best); } else { - return mq.getRowCount(set.rel); + return mq.getRowCount(castNonNull(set.rel)); } } @@ -238,7 +254,7 @@ public RelNode getOriginal() { // values to be printed later. We actually do the work. pw.item("subset", toString()); final AbstractRelNode input = - (AbstractRelNode) Util.first(getBest(), getOriginal()); + (@Nullable AbstractRelNode) Util.first(getBest(), getOriginal()); if (input == null) { return; } @@ -246,7 +262,7 @@ public RelNode getOriginal() { pw.done(input); } - @Override public boolean deepEquals(Object obj) { + @Override public boolean deepEquals(@Nullable Object obj) { return this == obj; } @@ -255,7 +271,7 @@ public RelNode getOriginal() { } @Override protected RelDataType deriveRowType() { - return set.rel.getRowType(); + return castNonNull(set.rel).getRowType(); } /** @@ -284,7 +300,7 @@ Set getParentSubsets(VolcanoPlanner planner) { for (RelNode parent : set.getParentRels()) { for (RelSubset rel : inputSubsets(parent)) { if (rel.set == set && rel.getTraitSet().equals(traitSet)) { - list.add(planner.getSubset(parent)); + list.add(planner.getSubsetNonNull(parent)); } } } @@ -431,7 +447,7 @@ public Stream getSatisfyingSubsets() { * or null if the subset is not fully optimized. */ @API(since = "1.24", status = API.Status.INTERNAL) - public RelOptCost getWinnerCost() { + public @Nullable RelOptCost getWinnerCost() { if (taskState == OptimizeState.COMPLETED && bestCost.isLe(upperBound)) { return bestCost; } @@ -461,7 +477,7 @@ boolean resetTaskState() { return optimized; } - RelNode passThrough(RelNode rel) { + @Nullable RelNode passThrough(RelNode rel) { if (!(rel instanceof PhysicalNode)) { return null; } @@ -588,7 +604,7 @@ private static String traitDiff(RelTraitSet original, RelTraitSet desired) { public RelNode visit( RelNode p, int ordinal, - RelNode parent) { + @Nullable RelNode parent) { if (p instanceof RelSubset) { RelSubset subset = (RelSubset) p; RelNode cheapest = subset.best; @@ -617,8 +633,11 @@ public RelNode visit( Map problemCounts = finder.deadEnds.stream() .filter(deadSubset -> deadSubset.getOriginal() != null) - .map(x -> x.getOriginal().getClass().getSimpleName() - + traitDiff(x.getOriginal().getTraitSet(), x.getTraitSet())) + .map(x -> { + RelNode original = castNonNull(x.getOriginal()); + return original.getClass().getSimpleName() + + traitDiff(original.getTraitSet(), x.getTraitSet()); + }) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); // Sort problems from most often to less often ones String problems = problemCounts.entrySet().stream() @@ -647,8 +666,10 @@ public RelNode visit( pw.print(deadEnd); pw.println(", the relevant part of the original plan is as follows"); RelNode original = deadEnd.getOriginal(); - original.explain( - new RelWriterImpl(pw, SqlExplainLevel.EXPPLAN_ATTRIBUTES, true)); + if (original != null) { + original.explain( + new RelWriterImpl(pw, SqlExplainLevel.EXPPLAN_ATTRIBUTES, true)); + } i++; rest--; if (rest > 0) { diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java b/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java index ac683cbaf6aa..c608e6966de7 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java @@ -96,7 +96,7 @@ protected boolean skipMatch(VolcanoRuleMatch match) { */ private void checkDuplicateSubsets(Deque subsets, RelOptRuleOperand operand, RelNode[] rels) { - final RelSubset subset = planner.getSubset(rels[operand.ordinalInRule]); + final RelSubset subset = planner.getSubsetNonNull(rels[operand.ordinalInRule]); if (subsets.contains(subset)) { throw Util.FoundOne.NULL; } diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleDriver.java b/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleDriver.java index ce42d0348137..2330d7389671 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleDriver.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleDriver.java @@ -25,6 +25,7 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.trace.CalciteTrace; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.ArrayList; @@ -35,6 +36,8 @@ import java.util.Stack; import java.util.function.Predicate; +import static java.util.Objects.requireNonNull; + /** * A rule driver that apply rules in a Top-Down manner. * By ensuring rule applying orders, there could be ways for @@ -59,20 +62,20 @@ class TopDownRuleDriver implements RuleDriver { /** * All tasks waiting for execution. */ - private Stack tasks = new Stack<>(); // TODO: replace with Deque + private final Stack tasks = new Stack<>(); // TODO: replace with Deque /** * A task that is currently applying and may generate new RelNode. * It provides a callback to schedule tasks for new RelNodes that * are registered during task performing. */ - private GeneratorTask applying = null; + private @Nullable GeneratorTask applying = null; /** * RelNodes that are generated by passThrough or derive * these nodes will not takes part in another passThrough or derive. */ - private Set passThroughCache = new HashSet<>(); + private final Set passThroughCache = new HashSet<>(); //~ Constructors ----------------------------------------------------------- @@ -87,7 +90,10 @@ class TopDownRuleDriver implements RuleDriver { TaskDescriptor description = new TaskDescriptor(); // Starting from the root's OptimizeGroup task. - tasks.push(new OptimizeGroup(planner.root, planner.infCost)); + tasks.push( + new OptimizeGroup( + requireNonNull(planner.root, "planner.root"), + planner.infCost)); // ensure materialized view roots get explored. // Note that implementation rules or enforcement rules are not applied @@ -109,7 +115,8 @@ class TopDownRuleDriver implements RuleDriver { private void exploreMaterializationRoots() { for (RelSubset extraRoot : planner.explorationRoots) { RelSet rootSet = VolcanoPlanner.equivRoot(extraRoot.set); - if (rootSet == planner.root.set) { + RelSubset root = requireNonNull(planner.root, "planner.root"); + if (rootSet == root.set) { continue; } for (RelNode rel : extraRoot.set.rels) { @@ -136,7 +143,7 @@ private interface Procedure { void exec(); } - private void applyGenerator(GeneratorTask task, Procedure proc) { + private void applyGenerator(@Nullable GeneratorTask task, Procedure proc) { GeneratorTask applying = this.applying; this.applying = task; try { @@ -161,7 +168,9 @@ private void clearProcessed(RelSet set) { if (subset.resetTaskState() || explored) { Collection parentRels = subset.getParentRels(); for (RelNode parentRel : parentRels) { - clearProcessed(planner.getSet(parentRel)); + RelSet parentRelSet = + requireNonNull(planner.getSet(parentRel), () -> "no set found for " + parentRel); + clearProcessed(parentRelSet); } if (subset == planner.root) { tasks.push(new OptimizeGroup(subset, planner.infCost)); @@ -183,7 +192,7 @@ private void clearProcessed(RelSet set) { } // extra callback from each task - if (!applying.onProduce(node)) { + if (!requireNonNull(applying, "applying").onProduce(node)) { return; } @@ -232,6 +241,7 @@ private void clearProcessed(RelSet set) { } else { boolean optimizing = subset.set.subsets.stream() .anyMatch(s -> s.taskState == RelSubset.OptimizeState.OPTIMIZING); + GeneratorTask applying = requireNonNull(this.applying, "this.applying"); tasks.push( new OptimizeMExpr(node, applying.group(), applying.exploring() && !optimizing)); @@ -530,7 +540,7 @@ private class ApplyRule implements GeneratorTask { } // Decide how to optimize a physical node. - private Task getOptimizeInputTask(RelNode rel, RelSubset group) { + private @Nullable Task getOptimizeInputTask(RelNode rel, RelSubset group) { // If the physical does not in current optimizing RelSubset, it firstly tries to // convert the physical node either by converter rule or traits pass though. if (!rel.getTraitSet().satisfies(group.getTraitSet())) { @@ -566,7 +576,7 @@ private Task getOptimizeInputTask(RelNode rel, RelSubset group) { // Try to convert the physical node to another trait sets, // either by converter rule or traits pass through. - private RelNode convert(RelNode rel, RelSubset group) { + private @Nullable RelNode convert(RelNode rel, RelSubset group) { if (!passThroughCache.contains(rel)) { if (checkLowerBound(rel, group)) { RelNode passThrough = group.passThrough(rel); @@ -583,7 +593,9 @@ private RelNode convert(RelNode rel, RelSubset group) { VolcanoRuleMatch match = ruleQueue.popMatch( Pair.of(rel, m -> m.getRule() instanceof ConverterRule - && m.getRule().getOutTrait().satisfies(group.getTraitSet().getConvention()))); + && ((ConverterRule) m.getRule()).getOutTrait().satisfies( + requireNonNull(group.getTraitSet().getConvention(), + () -> "convention for " + group)))); if (match != null) { tasks.add(new ApplyRule(match, group, false)); } @@ -667,8 +679,8 @@ private class OptimizeInputs implements Task { .item("processingChild", processingChild); } - private List lowerBounds; - private RelOptCost lowerBoundSum; + private @Nullable List lowerBounds; + private @Nullable RelOptCost lowerBoundSum; @Override public void perform() { RelOptCost bestCost = group.bestCost; if (!bestCost.isInfinite()) { @@ -682,14 +694,14 @@ private class OptimizeInputs implements Task { if (upperForInput.isInfinite()) { upperForInput = planner.upperBoundForInputs(mExpr, upperBound); } - lowerBounds = new ArrayList<>(childCount); + List lowerBounds = this.lowerBounds = new ArrayList<>(childCount); for (RelNode input : mExpr.getInputs()) { RelOptCost lb = planner.getLowerBound(input); lowerBounds.add(lb); lowerBoundSum = lowerBoundSum == null ? lb : lowerBoundSum.plus(lb); } } - if (upperForInput.isLt(lowerBoundSum)) { + if (upperForInput.isLt(requireNonNull(lowerBoundSum, "lowerBoundSum"))) { LOGGER.debug( "Skip O_INPUT because of lower bound. LB = {}, UP = {}", lowerBoundSum, upperForInput); @@ -722,8 +734,8 @@ private class OptimizeInputs implements Task { // UB(one input) // = UB(current subset) - Parent's NonCumulativeCost - LB(other inputs) // = UB(current subset) - Parent's NonCumulativeCost - LB(all inputs) + LB(current input) - upper = upperForInput.minus(lowerBoundSum) - .plus(lowerBounds.get(processingChild)); + upper = upperForInput.minus(requireNonNull(lowerBoundSum, "lowerBoundSum")) + .plus(requireNonNull(lowerBounds, "lowerBounds").get(processingChild)); } if (input.taskState != null && upper.isLe(input.upperBound)) { LOGGER.debug("Failed to optimize because of upper bound. LB = {}, UP = {}", @@ -747,7 +759,7 @@ private class OptimizeInputs implements Task { */ private class CheckInput implements Task { - private final OptimizeInputs context; + private final @Nullable OptimizeInputs context; private final RelOptCost upper; private final RelNode parent; private RelSubset input; @@ -757,7 +769,7 @@ private class CheckInput implements Task { desc.item("parent", parent).item("i", i); } - CheckInput(OptimizeInputs context, + CheckInput(@Nullable OptimizeInputs context, RelNode parent, RelSubset input, int i, RelOptCost upper) { this.context = context; this.parent = parent; @@ -790,11 +802,13 @@ private class CheckInput implements Task { } // Update the context. - if (context.lowerBoundSum != null && context.lowerBoundSum != planner.infCost) { - context.lowerBoundSum = context. - lowerBoundSum.minus(context.lowerBounds.get(i)); - context.lowerBoundSum = context.lowerBoundSum.plus(winner); - context.lowerBounds.set(i, winner); + RelOptCost lowerBoundSum = context.lowerBoundSum; + if (lowerBoundSum != null && lowerBoundSum != planner.infCost) { + List lowerBounds = requireNonNull(context.lowerBounds, "context.lowerBounds"); + lowerBoundSum = lowerBoundSum.minus(lowerBounds.get(i)); + lowerBoundSum = lowerBoundSum.plus(winner); + context.lowerBoundSum = lowerBoundSum; + lowerBounds.set(i, winner); } } } @@ -914,7 +928,7 @@ private void derive() { subset.disableEnforcing(); } RelSubset relSubset = planner.register(newRel, rel); - assert relSubset.set == planner.getSubset(rel).set; + assert relSubset.set == planner.getSubsetNonNull(rel).set; } } } diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleQueue.java b/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleQueue.java index f4a611974264..43227e804cd3 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleQueue.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/TopDownRuleQueue.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; @@ -60,7 +62,7 @@ private void addMatch(VolcanoRuleMatch match, Deque queue) { } } - public VolcanoRuleMatch popMatch(Pair> category) { + public @Nullable VolcanoRuleMatch popMatch(Pair> category) { Deque queue = matches.get(category.left); if (queue == null) { return null; diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoCost.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoCost.java index e7d73d21965e..f5390282decd 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoCost.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoCost.java @@ -20,6 +20,8 @@ import org.apache.calcite.plan.RelOptCostFactory; import org.apache.calcite.plan.RelOptUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -132,7 +134,7 @@ class VolcanoCost implements RelOptCost { && (this.io == ((VolcanoCost) other).io); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (obj instanceof VolcanoCost) { return equals((VolcanoCost) obj); } diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java index 32d647d7253b..8b69fc3e3945 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java @@ -62,6 +62,12 @@ import com.google.common.collect.Multimap; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; +import org.checkerframework.dataflow.qual.Pure; import java.io.PrintWriter; import java.io.StringWriter; @@ -74,11 +80,16 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.PriorityQueue; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * VolcanoPlanner optimizes queries by transforming expressions selectively * according to a dynamic programming algorithm. @@ -87,7 +98,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { //~ Instance fields -------------------------------------------------------- - protected RelSubset root; + protected @MonotonicNonNull RelSubset root; /** * Operands that apply to a given class of {@link RelNode}. @@ -149,9 +160,9 @@ public class VolcanoPlanner extends AbstractRelOptPlanner { private int nextSetId = 0; - private RelNode originalRoot; + private @MonotonicNonNull RelNode originalRoot; - private Convention rootConvention; + private @Nullable Convention rootConvention; /** * Whether the planner can accept new rules. @@ -217,8 +228,9 @@ public VolcanoPlanner(Context externalContext) { /** * Creates a {@code VolcanoPlanner} with a given cost factory. */ - public VolcanoPlanner(RelOptCostFactory costFactory, - Context externalContext) { + @SuppressWarnings("method.invocation.invalid") + public VolcanoPlanner(@Nullable RelOptCostFactory costFactory, + @Nullable Context externalContext) { super(costFactory == null ? VolcanoCost.FACTORY : costFactory, externalContext); this.zeroCost = this.costFactory.makeZeroCost(); @@ -229,6 +241,7 @@ public VolcanoPlanner(RelOptCostFactory costFactory, initRuleQueue(); } + @EnsuresNonNull("ruleDriver") private void initRuleQueue() { if (topDownOpt) { ruleDriver = new TopDownRuleDriver(this); @@ -273,7 +286,8 @@ public void setTopDownOpt(boolean value) { ensureRootConverters(); } - @Override public RelNode getRoot() { + @Pure + @Override public @Nullable RelNode getRoot() { return root; } @@ -290,7 +304,7 @@ public void setTopDownOpt(boolean value) { latticeByName.put(lattice.starRelOptTable.getQualifiedName(), lattice); } - @Override public RelOptLattice getLattice(RelOptTable table) { + @Override public @Nullable RelOptLattice getLattice(RelOptTable table) { return latticeByName.get(table.getQualifiedName()); } @@ -302,6 +316,9 @@ protected void registerMaterializations() { return; } + assert root != null : "root"; + assert originalRoot != null : "originalRoot"; + // Register rels using materialized views. final List>> materializationUses = RelOptMaterializations.useMaterializedViews(originalRoot, materializations); @@ -350,7 +367,7 @@ protected void registerMaterializations() { * @return Equivalence set that expression belongs to, or null if it is not * registered */ - public RelSet getSet(RelNode rel) { + public @Nullable RelSet getSet(RelNode rel) { assert rel != null : "pre: rel != null"; final RelSubset subset = getSubset(rel); if (subset != null) { @@ -506,6 +523,7 @@ public RelSet getSet(RelNode rel) { * query */ @Override public RelNode findBestExp() { + assert root != null : "root must not be null"; ensureRootConverters(); registerMaterializations(); @@ -552,6 +570,7 @@ private void registerMetadataRels() { * in the plan where explicit converters are required; elsewhere, a consumer * will be asking for the result in a particular convention, but the root has * no consumers. */ + @RequiresNonNull("root") void ensureRootConverters() { final Set subsets = new HashSet<>(); for (RelNode rel : root.getRels()) { @@ -573,7 +592,7 @@ void ensureRootConverters() { @Override public RelSubset register( RelNode rel, - RelNode equivRel) { + @Nullable RelNode equivRel) { assert !isRegistered(rel) : "pre: isRegistered(rel)"; final RelSet set; if (equivRel == null) { @@ -593,12 +612,12 @@ void ensureRootConverters() { return registerImpl(rel, set); } - @Override public RelSubset ensureRegistered(RelNode rel, RelNode equivRel) { + @Override public RelSubset ensureRegistered(RelNode rel, @Nullable RelNode equivRel) { RelSubset result; final RelSubset subset = getSubset(rel); if (subset != null) { if (equivRel != null) { - final RelSubset equivSubset = getSubset(equivRel); + final RelSubset equivSubset = getSubsetNonNull(equivRel); if (subset.set != equivSubset.set) { merge(equivSubset.set, subset.set); } @@ -621,11 +640,12 @@ void ensureRootConverters() { * Checks internal consistency. */ protected boolean isValid(Litmus litmus) { - if (this.getRoot() == null) { + RelNode root = getRoot(); + if (root == null) { return true; } - RelMetadataQuery metaQuery = this.getRoot().getCluster().getMetadataQuerySupplier().get(); + RelMetadataQuery metaQuery = root.getCluster().getMetadataQuerySupplier().get(); for (RelSet set : allSets) { if (set.equivalentSet != null) { return litmus.fail("set [{}] has been merged: it should not be in the list", set); @@ -647,7 +667,7 @@ protected boolean isValid(Litmus litmus) { // Make sure bestCost is up-to-date try { RelOptCost bestCost = getCost(subset.best, metaQuery); - if (!subset.bestCost.equals(bestCost)) { + if (!Objects.equals(subset.bestCost, bestCost)) { return litmus.fail("RelSubset [" + subset + "] has wrong best cost " + subset.bestCost + ". Correct cost is " + bestCost); @@ -660,7 +680,7 @@ protected boolean isValid(Litmus litmus) { for (RelNode rel : subset.getRels()) { try { RelOptCost relCost = getCost(rel, metaQuery); - if (relCost.isLt(subset.bestCost)) { + if (relCost != null && relCost.isLt(subset.bestCost)) { return litmus.fail("rel [{}] has lower cost {} than " + "best cost {} of subset [{}]", rel, relCost, subset.bestCost, subset); @@ -697,7 +717,7 @@ public void setNoneConventionHasInfiniteCost(boolean infinite) { this.noneConventionHasInfiniteCost = infinite; } - @Override public RelOptCost getCost(RelNode rel, RelMetadataQuery mq) { + @Override public @Nullable RelOptCost getCost(RelNode rel, RelMetadataQuery mq) { assert rel != null : "pre-condition: rel != null"; if (rel instanceof RelSubset) { return ((RelSubset) rel).bestCost; @@ -707,12 +727,19 @@ public void setNoneConventionHasInfiniteCost(boolean infinite) { return costFactory.makeInfiniteCost(); } RelOptCost cost = mq.getNonCumulativeCost(rel); + if (cost == null) { + return null; + } if (!zeroCost.isLt(cost)) { // cost must be positive, so nudge it cost = costFactory.makeTinyCost(); } for (RelNode input : rel.getInputs()) { - cost = cost.plus(getCost(input, mq)); + RelOptCost inputCost = getCost(input, mq); + if (inputCost == null) { + return null; + } + cost = cost.plus(inputCost); } return cost; } @@ -723,7 +750,7 @@ public void setNoneConventionHasInfiniteCost(boolean infinite) { * @param rel Relational expression * @return Subset it belongs to, or null if it is not registered */ - public RelSubset getSubset(RelNode rel) { + public @Nullable RelSubset getSubset(RelNode rel) { assert rel != null : "pre: rel != null"; if (rel instanceof RelSubset) { return (RelSubset) rel; @@ -732,7 +759,19 @@ public RelSubset getSubset(RelNode rel) { } } - public RelSubset getSubset(RelNode rel, RelTraitSet traits) { + /** + * Returns the subset that a relational expression belongs to. + * + * @param rel Relational expression + * @return Subset it belongs to, or null if it is not registered + * @throws AssertionError in case subset is not found + */ + @API(since = "1.26", status = API.Status.EXPERIMENTAL) + public RelSubset getSubsetNonNull(RelNode rel) { + return requireNonNull(getSubset(rel), () -> "Subset is not found for " + rel); + } + + public @Nullable RelSubset getSubset(RelNode rel, RelTraitSet traits) { if ((rel instanceof RelSubset) && rel.getTraitSet().equals(traits)) { return (RelSubset) rel; } @@ -743,7 +782,7 @@ public RelSubset getSubset(RelNode rel, RelTraitSet traits) { return set.getSubset(traits); } - RelNode changeTraitsUsingConverters( + @Nullable RelNode changeTraitsUsingConverters( RelNode rel, RelTraitSet toTraits) { final RelTraitSet fromTraits = rel.getTraitSet(); @@ -776,18 +815,18 @@ RelNode changeTraitsUsingConverters( continue; } - rel = + RelNode convertedRel = traitDef.convert( this, converted, toTrait, allowInfiniteCostConverters); - if (rel != null) { - assert rel.getTraitSet().getTrait(traitDef).satisfies(toTrait); - register(rel, converted); + if (convertedRel != null) { + assert castNonNull(convertedRel.getTraitSet().getTrait(traitDef)).satisfies(toTrait); + register(convertedRel, converted); } - converted = rel; + converted = convertedRel; } // make sure final converted traitset subsumes what was required @@ -852,7 +891,7 @@ public String toDot() { * @param rel Relational expression */ void rename(RelNode rel) { - String oldDigest = null; + String oldDigest = ""; if (LOGGER.isTraceEnabled()) { oldDigest = rel.getDigest(); } @@ -871,7 +910,7 @@ void rename(RelNode rel) { mapDigestToRel.put(newDigest, equivRel); checkPruned(equivRel, rel); - RelSubset equivRelSubset = getSubset(equivRel); + RelSubset equivRelSubset = getSubsetNonNull(equivRel); // Remove back-links from children. for (RelNode input : rel.getInputs()) { @@ -885,7 +924,7 @@ void rename(RelNode rel) { assert subset != null; boolean existed = subset.set.rels.remove(rel); assert existed : "rel was not known to its set"; - final RelSubset equivSubset = getSubset(equivRel); + final RelSubset equivSubset = getSubsetNonNull(equivRel); for (RelSubset s : subset.set.subsets) { if (s.best == rel) { s.best = equivRel; @@ -1008,6 +1047,7 @@ private void checkPruned(RelNode rel, RelNode duplicateRel) { /** * Find the new root subset in case the root is merged with another subset. */ + @RequiresNonNull("root") void canonize() { root = canonize(root); } @@ -1021,10 +1061,10 @@ void canonize() { * @return Leader of subset's equivalence class */ private RelSubset canonize(final RelSubset subset) { - if (subset.set.equivalentSet == null) { + RelSet set = subset.set; + if (set.equivalentSet == null) { return subset; } - RelSet set = subset.set; do { set = set.equivalentSet; } while (set.equivalentSet != null); @@ -1106,6 +1146,10 @@ private RelSet merge(RelSet set, RelSet set2) { // Merge. set.mergeWith(this, set2); + if (root == null) { + throw new IllegalStateException("root must not be null"); + } + // Was the set we merged with the root? If so, the result is the new // root. if (set2 == getSet(root)) { @@ -1130,14 +1174,14 @@ static RelSet equivRoot(RelSet s) { } /** Moves forward two links, checking for a cycle at each. */ - private static RelSet forward2(RelSet s, RelSet p) { + private static @Nullable RelSet forward2(RelSet s, @Nullable RelSet p) { p = forward1(s, p); p = forward1(s, p); return p; } /** Moves forward one link, checking for a cycle. */ - private static RelSet forward1(RelSet s, RelSet p) { + private static @Nullable RelSet forward1(RelSet s, @Nullable RelSet p) { if (p != null) { p = p.equivalentSet; if (p == s) { @@ -1160,7 +1204,7 @@ private static RelSet forward1(RelSet s, RelSet p) { */ private RelSubset registerImpl( RelNode rel, - RelSet set) { + @Nullable RelSet set) { if (rel instanceof RelSubset) { return registerSubset(set, (RelSubset) rel); } @@ -1193,10 +1237,10 @@ private RelSubset registerImpl( rel = rel.onRegister(this); // Record its provenance. (Rule call may be null.) - if (ruleCallStack.isEmpty()) { + final VolcanoRuleCall ruleCall = ruleCallStack.peek(); + if (ruleCall == null) { provenanceMap.put(rel, Provenance.EMPTY); } else { - final VolcanoRuleCall ruleCall = ruleCallStack.peek(); provenanceMap.put( rel, new RuleProvenance( @@ -1212,7 +1256,8 @@ private RelSubset registerImpl( if (equivExp == null) { // do nothing } else if (equivExp == rel) { - return getSubset(rel); + // The same rel is already registered, so return its subset + return getSubsetNonNull(equivExp); } else { if (!RelOptUtil.areRowTypesEqual(equivExp.getRowType(), rel.getRowType(), false)) { @@ -1226,14 +1271,14 @@ private RelSubset registerImpl( if (equivSet != null) { LOGGER.trace( "Register: rel#{} is equivalent to {}", rel.getId(), equivExp); - return registerSubset(set, getSubset(equivExp)); + return registerSubset(set, getSubsetNonNull(equivExp)); } } // Converters are in the same set as their children. if (rel instanceof Converter) { final RelNode input = ((Converter) rel).getInput(); - final RelSet childSet = getSet(input); + final RelSet childSet = castNonNull(getSet(input)); if ((set != null) && (set != childSet) && (set.equivalentSet == null)) { @@ -1258,7 +1303,7 @@ private RelSubset registerImpl( // There is already an equivalent expression. Use that // one, and forget about this one. - return getSubset(equivRel); + return getSubsetNonNull(equivRel); } } } else { @@ -1340,7 +1385,7 @@ private RelSubset addRelToSet(RelNode rel, RelSet set) { } private RelSubset registerSubset( - RelSet set, + @Nullable RelSet set, RelSubset subset) { if ((set != subset.set) && (set != null) @@ -1403,7 +1448,7 @@ private RelSubset registerSubset( * @param plan Plan * @return Normalized plan */ - public static String normalizePlan(String plan) { + public static @PolyNull String normalizePlan(@PolyNull String plan) { if (plan == null) { return null; } @@ -1496,7 +1541,7 @@ protected RelOptCost upperBoundForInputs( if (!upperBound.isInfinite()) { RelOptCost rootCost = mExpr.getCluster() .getMetadataQuery().getNonCumulativeCost(mExpr); - if (!rootCost.isInfinite()) { + if (rootCost != null && !rootCost.isInfinite()) { return upperBound.minus(rootCost); } } diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java index 82223ce722e0..3a6ae005035a 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java @@ -26,7 +26,10 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; +import java.util.Objects; /** * VolcanoRelMetadataProvider implements the {@link RelMetadataProvider} @@ -35,7 +38,7 @@ public class VolcanoRelMetadataProvider implements RelMetadataProvider { //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj instanceof VolcanoRelMetadataProvider; } @@ -43,7 +46,7 @@ public class VolcanoRelMetadataProvider implements RelMetadataProvider { return 103; } - @Override public UnboundMetadata apply( + @Override public <@Nullable M extends @Nullable Metadata> @Nullable UnboundMetadata apply( Class relClass, final Class metadataClass) { if (relClass != RelSubset.class) { @@ -53,8 +56,9 @@ public class VolcanoRelMetadataProvider implements RelMetadataProvider { return (rel, mq) -> { final RelSubset subset = (RelSubset) rel; - final RelMetadataProvider provider = - rel.getCluster().getMetadataProvider(); + final RelMetadataProvider provider = Objects.requireNonNull( + rel.getCluster().getMetadataProvider(), + "metadataProvider"); // REVIEW jvs 29-Mar-2006: I'm not sure what the correct precedence // should be here. Letting the current best plan take the first shot is @@ -65,10 +69,11 @@ public class VolcanoRelMetadataProvider implements RelMetadataProvider { // First, try current best implementation. If it knows how to answer // this query, treat it as the most reliable. if (subset.best != null) { + RelNode best = subset.best; final UnboundMetadata function = - provider.apply(subset.best.getClass(), metadataClass); + provider.apply(best.getClass(), metadataClass); if (function != null) { - final M metadata = function.bind(subset.best, mq); + final M metadata = function.bind(best, mq); if (metadata != null) { return metadata; } diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java index c33463fd79d0..4376217b74f9 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java @@ -30,6 +30,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -39,6 +41,10 @@ import java.util.Set; import java.util.stream.Collectors; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * VolcanoRuleCall implements the {@link RelOptRuleCall} interface * for VolcanoPlanner. @@ -51,7 +57,7 @@ public class VolcanoRuleCall extends RelOptRuleCall { /** * List of {@link RelNode} generated by this call. For debugging purposes. */ - private List generatedRelList; + private @Nullable List generatedRelList; //~ Constructors ----------------------------------------------------------- @@ -231,7 +237,7 @@ protected void onMatch() { volcanoPlanner.ruleCallStack.pop(); } - if (LOGGER.isDebugEnabled()) { + if (generatedRelList != null) { if (generatedRelList.isEmpty()) { LOGGER.debug("call#{} generated 0 successors.", id); } else { @@ -263,7 +269,7 @@ protected void onMatch() { void match(RelNode rel) { assert getOperand0().matches(rel) : "precondition"; final int solve = 0; - int operandOrdinal = getOperand0().solveOrder[solve]; + int operandOrdinal = castNonNull(getOperand0().solveOrder)[solve]; this.rels[operandOrdinal] = rel; matchRecurse(solve + 1); } @@ -285,8 +291,9 @@ private void matchRecurse(int solve) { onMatch(); } } else { - final int operandOrdinal = operand0.solveOrder[solve]; - final int previousOperandOrdinal = operand0.solveOrder[solve - 1]; + final int[] solveOrder = castNonNull(operand0.solveOrder); + final int operandOrdinal = solveOrder[solve]; + final int previousOperandOrdinal = solveOrder[solve - 1]; boolean ascending = operandOrdinal < previousOperandOrdinal; final RelOptRuleOperand previousOperand = operands.get(previousOperandOrdinal); @@ -304,10 +311,12 @@ private void matchRecurse(int solve) { + previousOperand.getMatchedClass().getSimpleName()); } parentOperand = operand; - final RelSubset subset = volcanoPlanner.getSubset(previous); + final RelSubset subset = volcanoPlanner.getSubsetNonNull(previous); successors = subset.getParentRels(); } else { - parentOperand = operand.getParent(); + parentOperand = requireNonNull( + operand.getParent(), + () -> "operand.getParent() for " + operand); final RelNode parentRel = rels[parentOperand.ordinalInRule]; final List inputs = parentRel.getInputs(); // if the child is unordered, then add all rels in all input subsets to the successors list diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java index 01ccde743ecb..8a24ecd9c139 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleMatch.java @@ -42,6 +42,7 @@ class VolcanoRuleMatch extends VolcanoRuleCall { * can modify it later * @param nodeInputs Map from relational expressions to their inputs */ + @SuppressWarnings("method.invocation.invalid") VolcanoRuleMatch(VolcanoPlanner volcanoPlanner, RelOptRuleOperand operand0, RelNode[] rels, Map> nodeInputs) { super(volcanoPlanner, operand0, rels.clone(), nodeInputs); diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java index 4ea28b2c4fd3..1c90915a0033 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java @@ -65,6 +65,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; @@ -113,7 +115,7 @@ protected CalciteCatalogReader(CalciteSchema rootSchema, ImmutableList.of(schemaPath, ImmutableList.of()), typeFactory, config); } - @Override public Prepare.PreparingTable getTable(final List names) { + @Override public Prepare.@Nullable PreparingTable getTable(final List names) { // First look in the default schema, if any. // If not found, look in the root schema. CalciteSchema.TableEntry entry = SqlValidatorUtil.getTableEntry(this, names); @@ -171,7 +173,7 @@ private Collection getFunctionsFrom( return functions2; } - @Override public RelDataType getNamedType(SqlIdentifier typeName) { + @Override public @Nullable RelDataType getNamedType(SqlIdentifier typeName) { CalciteSchema.TypeEntry typeEntry = SqlValidatorUtil.getTypeEntry(getRootSchema(), typeName); if (typeEntry != null) { return typeEntry.getType().apply(typeFactory); @@ -210,7 +212,7 @@ private Collection getFunctionsFrom( return result.build(); } - private SqlMonikerImpl moniker(CalciteSchema schema, String name, + private SqlMonikerImpl moniker(CalciteSchema schema, @Nullable String name, SqlMonikerType type) { final List path = schema.path(name); if (path.size() == 1 @@ -225,12 +227,12 @@ private SqlMonikerImpl moniker(CalciteSchema schema, String name, return schemaPaths; } - @Override public Prepare.PreparingTable getTableForMember(List names) { + @Override public Prepare.@Nullable PreparingTable getTableForMember(List names) { return getTable(names); } - @Override @SuppressWarnings("deprecation") - public RelDataTypeField field(RelDataType rowType, String alias) { + @SuppressWarnings("deprecation") + @Override public @Nullable RelDataTypeField field(RelDataType rowType, String alias) { return nameMatcher.field(rowType, alias); } @@ -246,7 +248,7 @@ public RelDataTypeField field(RelDataType rowType, String alias) { } @Override public void lookupOperatorOverloads(final SqlIdentifier opName, - SqlFunctionCategory category, + @Nullable SqlFunctionCategory category, SqlSyntax syntax, List operatorList, SqlNameMatcher nameMatcher) { @@ -444,7 +446,7 @@ private static RelDataType toSql(RelDataTypeFactory typeFactory, return nameMatcher; } - @Override public C unwrap(Class aClass) { + @Override public @Nullable C unwrap(Class aClass) { if (aClass.isInstance(this)) { return aClass.cast(this); } diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java b/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java index ffd6b9f8ca07..220e39883142 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java @@ -58,6 +58,8 @@ import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Context for populating a {@link Prepare.Materialization}. */ @@ -99,8 +101,10 @@ void populate(Materialization materialization) { // take the best (whatever that means), or all of them? useStar(schema, materialization); - RelOptTable table = - this.catalogReader.getTable(materialization.materializedTable.path()); + List tableName = materialization.materializedTable.path(); + RelOptTable table = requireNonNull( + this.catalogReader.getTable(tableName), + () -> "table " + tableName + " is not found"); materialization.tableRel = sqlToRelConverter2.toRel(table, ImmutableList.of()); } @@ -108,14 +112,15 @@ void populate(Materialization materialization) { * {@link StarTable} defined in {@code schema}. * Uses the first star table that fits. */ private void useStar(CalciteSchema schema, Materialization materialization) { - for (Callback x : useStar(schema, materialization.queryRel)) { + RelNode queryRel = requireNonNull(materialization.queryRel, "materialization.queryRel"); + for (Callback x : useStar(schema, queryRel)) { // Success -- we found a star table that matches. materialization.materialize(x.rel, x.starRelOptTable); if (CalciteSystemProperty.DEBUG.value()) { System.out.println("Materialization " + materialization.materializedTable + " matched star table " + x.starTable + "; query after re-write: " - + RelOptUtil.toString(materialization.queryRel)); + + RelOptUtil.toString(queryRel)); } } } diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java index 5c493d7ca7d6..837a4cb8315c 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java @@ -47,6 +47,7 @@ import org.apache.calcite.linq4j.tree.MethodCallExpression; import org.apache.calcite.linq4j.tree.NewExpression; import org.apache.calcite.linq4j.tree.ParameterExpression; +import org.apache.calcite.linq4j.tree.PseudoField; import org.apache.calcite.materialize.MaterializationService; import org.apache.calcite.plan.Contexts; import org.apache.calcite.plan.Convention; @@ -77,6 +78,7 @@ import org.apache.calcite.runtime.Bindable; import org.apache.calcite.runtime.Hook; import org.apache.calcite.runtime.Typed; +import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.schema.Schemas; import org.apache.calcite.schema.Table; import org.apache.calcite.server.CalciteServerStatement; @@ -113,6 +115,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.math.BigDecimal; import java.sql.DatabaseMetaData; @@ -123,8 +127,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; /** @@ -429,8 +435,8 @@ protected RelOptPlanner createPlanner(CalcitePrepare.Context prepareContext) { * rules. */ protected RelOptPlanner createPlanner( final CalcitePrepare.Context prepareContext, - org.apache.calcite.plan.Context externalContext, - RelOptCostFactory costFactory) { + org.apache.calcite.plan.@Nullable Context externalContext, + @Nullable RelOptCostFactory costFactory) { if (externalContext == null) { externalContext = Contexts.of(prepareContext.config()); } @@ -484,7 +490,7 @@ CalciteSignature prepare_( Type elementType, long maxRowCount) { if (SIMPLE_SQLS.contains(query.sql)) { - return simplePrepare(context, query.sql); + return simplePrepare(context, castNonNull(query.sql)); } final JavaTypeFactory typeFactory = context.getTypeFactory(); CalciteCatalogReader catalogReader = @@ -525,7 +531,7 @@ private CalciteSignature simplePrepare(Context context, String sql) { @SuppressWarnings("unchecked") final List list = (List) ImmutableList.of(1); final List origin = null; - final List> origins = + final List<@Nullable List> origins = Collections.nCopies(x.getFieldCount(), origin); final List columns = getColumnMetaDataList(typeFactory, x, x, origins); @@ -680,7 +686,7 @@ CalciteSignature prepare2_( } RelDataType jdbcType = makeStruct(typeFactory, x); - final List> originList = preparedResult.getFieldOrigins(); + final List> originList = preparedResult.getFieldOrigins(); final List columns = getColumnMetaDataList(typeFactory, x, jdbcType, originList); Class resultClazz = null; @@ -731,7 +737,7 @@ private SqlValidator createSqlValidator(Context context, private List getColumnMetaDataList( JavaTypeFactory typeFactory, RelDataType x, RelDataType jdbcType, - List> originList) { + List> originList) { final List columns = new ArrayList<>(); for (Ord pair : Ord.zip(jdbcType.getFieldList())) { final RelDataTypeField field = pair.e; @@ -746,8 +752,8 @@ private List getColumnMetaDataList( } private ColumnMetaData metaData(JavaTypeFactory typeFactory, int ordinal, - String fieldName, RelDataType type, RelDataType fieldType, - List origins) { + String fieldName, RelDataType type, @Nullable RelDataType fieldType, + @Nullable List origins) { final ColumnMetaData.AvaticaType avaticaType = avaticaType(typeFactory, type, fieldType); return new ColumnMetaData( @@ -776,7 +782,7 @@ private ColumnMetaData metaData(JavaTypeFactory typeFactory, int ordinal, } private ColumnMetaData.AvaticaType avaticaType(JavaTypeFactory typeFactory, - RelDataType type, RelDataType fieldType) { + RelDataType type, @Nullable RelDataType fieldType) { final String typeName = getTypeName(type); if (type.getComponentType() != null) { final ColumnMetaData.AvaticaType componentType = @@ -809,7 +815,7 @@ private ColumnMetaData.AvaticaType avaticaType(JavaTypeFactory typeFactory, } } - private static String origin(List origins, int offsetFromEnd) { + private static @Nullable String origin(@Nullable List origins, int offsetFromEnd) { return origins == null || offsetFromEnd >= origins.size() ? null : origins.get(origins.size() - 1 - offsetFromEnd); @@ -908,9 +914,10 @@ public R perform(CalciteServerStatement statement, final CalcitePrepare.Context prepareContext = statement.createPrepareContext(); final JavaTypeFactory typeFactory = prepareContext.getTypeFactory(); + SchemaPlus defaultSchema = config.getDefaultSchema(); final CalciteSchema schema = - config.getDefaultSchema() != null - ? CalciteSchema.from(config.getDefaultSchema()) + defaultSchema != null + ? CalciteSchema.from(defaultSchema) : prepareContext.getRootSchema(); CalciteCatalogReader catalogReader = new CalciteCatalogReader(schema.root(), @@ -936,20 +943,20 @@ static class CalcitePreparingStmt extends Prepare protected final CalciteSchema schema; protected final RelDataTypeFactory typeFactory; protected final SqlRexConvertletTable convertletTable; - private final EnumerableRel.Prefer prefer; + private final EnumerableRel.@Nullable Prefer prefer; private final RelOptCluster cluster; private final Map internalParameters = new LinkedHashMap<>(); @SuppressWarnings("unused") private int expansionDepth; - private SqlValidator sqlValidator; + private @Nullable SqlValidator sqlValidator; CalcitePreparingStmt(CalcitePrepareImpl prepare, Context context, CatalogReader catalogReader, RelDataTypeFactory typeFactory, CalciteSchema schema, - EnumerableRel.Prefer prefer, + EnumerableRel.@Nullable Prefer prefer, RelOptCluster cluster, Convention resultConvention, SqlRexConvertletTable convertletTable) { @@ -1050,7 +1057,7 @@ private PreparedResult prepare_(Supplier fn, } @Override public RelRoot expandView(RelDataType rowType, String queryString, - List schemaPath, List viewPath) { + List schemaPath, @Nullable List viewPath) { expansionDepth++; SqlParser parser = prepare.createParser(queryString); @@ -1088,9 +1095,9 @@ protected SqlValidator createSqlValidator(CatalogReader catalogReader) { } @Override protected PreparedResult createPreparedExplanation( - RelDataType resultType, + @Nullable RelDataType resultType, RelDataType parameterRowType, - RelRoot root, + @Nullable RelRoot root, SqlExplainFormat format, SqlExplainLevel detailLevel) { return new CalcitePreparedExplain(resultType, parameterRowType, root, @@ -1122,7 +1129,8 @@ protected SqlValidator createSqlValidator(CatalogReader catalogReader) { final SqlConformance conformance = context.config().conformance(); internalParameters.put("_conformance", conformance); bindable = EnumerableInterpretable.toBindable(internalParameters, - context.spark(), enumerable, prefer); + context.spark(), enumerable, + Objects.requireNonNull(prefer, "EnumerableRel.Prefer prefer")); } finally { CatalogReader.THREAD_LOCAL.remove(); } @@ -1138,8 +1146,8 @@ protected SqlValidator createSqlValidator(CatalogReader catalogReader) { return new PreparedResultImpl( resultType, - parameterRowType, - fieldOrigins, + Objects.requireNonNull(parameterRowType, "parameterRowType"), + Objects.requireNonNull(fieldOrigins, "fieldOrigins"), root.collation.getFieldCollations().isEmpty() ? ImmutableList.of() : ImmutableList.of(root.collation), @@ -1179,9 +1187,9 @@ protected SqlValidator createSqlValidator(CatalogReader catalogReader) { /** An {@code EXPLAIN} statement, prepared and ready to execute. */ private static class CalcitePreparedExplain extends Prepare.PreparedExplain { CalcitePreparedExplain( - RelDataType resultType, + @Nullable RelDataType resultType, RelDataType parameterRowType, - RelRoot root, + @Nullable RelRoot root, SqlExplainFormat format, SqlExplainLevel detailLevel) { super(resultType, parameterRowType, root, format, detailLevel); @@ -1249,10 +1257,15 @@ private static List simpleList(BlockStatement statement) { switch (expression.getNodeType()) { case MemberAccess: // Case-sensitive name match because name was previously resolved. + MemberExpression memberExpression = (MemberExpression) expression; + PseudoField field = memberExpression.field; + Expression targetExpression = Objects.requireNonNull(memberExpression.expression, + () -> "static field access is not implemented yet." + + " field.name=" + field.getName() + + ", field.declaringClass=" + field.getDeclaringClass()); return rexBuilder.makeFieldAccess( - toRex( - ((MemberExpression) expression).expression), - ((MemberExpression) expression).field.getName(), + toRex(targetExpression), + field.getName(), true); case GreaterThan: return binary(expression, SqlStdOperatorTable.GREATER_THAN); diff --git a/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java b/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java index 6bdb9be2bd98..f7d02f3e2c76 100644 --- a/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java +++ b/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java @@ -18,12 +18,14 @@ import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.linq4j.Queryable; +import org.apache.calcite.linq4j.tree.BlockStatement; import org.apache.calcite.linq4j.tree.Blocks; import org.apache.calcite.linq4j.tree.ConstantExpression; import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.FunctionExpression; import org.apache.calcite.linq4j.tree.MethodCallExpression; import org.apache.calcite.linq4j.tree.NewExpression; +import org.apache.calcite.linq4j.tree.ParameterExpression; import org.apache.calcite.linq4j.tree.Types; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptTable; @@ -38,10 +40,13 @@ import com.google.common.collect.ImmutableList; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Translates a tree of linq4j {@link Queryable} nodes to a tree of * {@link RelNode} planner nodes. @@ -59,6 +64,19 @@ class LixToRelTranslator { this.typeFactory = (JavaTypeFactory) cluster.getTypeFactory(); } + private static BlockStatement getBody(FunctionExpression expression) { + return requireNonNull(expression.body, () -> "body in " + expression); + } + + private static List getParameterList(FunctionExpression expression) { + return requireNonNull(expression.parameterList, () -> "parameterList in " + expression); + } + + private static Expression getTargetExpression(MethodCallExpression call) { + return requireNonNull(call.targetExpression, + "translation of static calls is not supported yet"); + } + RelOptTable.ToRelContext toRelContext() { if (preparingStmt instanceof RelOptTable.ViewExpander) { final RelOptTable.ViewExpander viewExpander = @@ -86,14 +104,14 @@ public RelNode translate(Expression expression) { RelNode input; switch (method) { case SELECT: - input = translate(call.targetExpression); + input = translate(getTargetExpression(call)); return LogicalProject.create(input, ImmutableList.of(), toRex(input, (FunctionExpression) call.expressions.get(0)), (List) null); case WHERE: - input = translate(call.targetExpression); + input = translate(getTargetExpression(call)); return LogicalFilter.create(input, toRex((FunctionExpression) call.expressions.get(0), input)); @@ -102,18 +120,20 @@ public RelNode translate(Expression expression) { RelOptTableImpl.create(null, typeFactory.createJavaType( Types.toClass( - Types.getElementType(call.targetExpression.getType()))), + getElementType(call))), ImmutableList.of(), - call.targetExpression), + getTargetExpression(call)), ImmutableList.of()); case SCHEMA_GET_TABLE: return LogicalTableScan.create(cluster, RelOptTableImpl.create(null, typeFactory.createJavaType((Class) - ((ConstantExpression) call.expressions.get(1)).value), + requireNonNull( + ((ConstantExpression) call.expressions.get(1)).value, + "argument 1 (0-based) is null Class")), ImmutableList.of(), - call.targetExpression), + getTargetExpression(call)), ImmutableList.of()); default: @@ -125,6 +145,13 @@ public RelNode translate(Expression expression) { "unknown expression type " + expression.getNodeType()); } + private static Type getElementType(MethodCallExpression call) { + Type type = getTargetExpression(call).getType(); + return requireNonNull( + Types.getElementType(type), + () -> "unable to figure out element type from " + type); + } + private List toRex( RelNode child, FunctionExpression expression) { RexBuilder rexBuilder = cluster.getRexBuilder(); @@ -134,9 +161,9 @@ private List toRex( CalcitePrepareImpl.ScalarTranslator translator = CalcitePrepareImpl.EmptyScalarTranslator .empty(rexBuilder) - .bind(expression.parameterList, list); + .bind(getParameterList(expression), list); final List rexList = new ArrayList<>(); - final Expression simple = Blocks.simple(expression.body); + final Expression simple = Blocks.simple(getBody(expression)); for (Expression expression1 : fieldExpressions(simple)) { rexList.add(translator.toRex(expression1)); } @@ -162,8 +189,8 @@ List toRexList( list.add(rexBuilder.makeRangeReference(input)); } return CalcitePrepareImpl.EmptyScalarTranslator.empty(rexBuilder) - .bind(expression.parameterList, list) - .toRexList(expression.body); + .bind(getParameterList(expression), list) + .toRexList(getBody(expression)); } RexNode toRex( @@ -175,7 +202,7 @@ RexNode toRex( list.add(rexBuilder.makeRangeReference(input)); } return CalcitePrepareImpl.EmptyScalarTranslator.empty(rexBuilder) - .bind(expression.parameterList, list) - .toRex(expression.body); + .bind(getParameterList(expression), list) + .toRex(getBody(expression)); } } diff --git a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java index e9bdde465959..81e796fa7f87 100644 --- a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java @@ -62,19 +62,24 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.Reader; import java.util.List; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.tools.Planner}. */ public class PlannerImpl implements Planner, ViewExpander { private final SqlOperatorTable operatorTable; private final ImmutableList programs; - private final RelOptCostFactory costFactory; + private final @Nullable RelOptCostFactory costFactory; private final Context context; private final CalciteConnectionConfig connectionConfig; /** Holds the trait definitions to be registered with planner. May be null. */ - private final ImmutableList traitDefs; + private final @Nullable ImmutableList traitDefs; private final SqlParser.Config parserConfig; private final SqlValidator.Config sqlValidatorConfig; @@ -88,20 +93,18 @@ public class PlannerImpl implements Planner, ViewExpander { private boolean open; // set in STATE_2_READY - private SchemaPlus defaultSchema; - private JavaTypeFactory typeFactory; - private RelOptPlanner planner; - private RexExecutor executor; + private @Nullable SchemaPlus defaultSchema; + private @Nullable JavaTypeFactory typeFactory; + private @Nullable RelOptPlanner planner; + private @Nullable RexExecutor executor; // set in STATE_4_VALIDATE - private SqlValidator validator; - private SqlNode validatedSqlNode; - - // set in STATE_5_CONVERT - private RelRoot root; + private @Nullable SqlValidator validator; + private @Nullable SqlNode validatedSqlNode; /** Creates a planner. Not a public API; call * {@link org.apache.calcite.tools.Frameworks#getPlanner} instead. */ + @SuppressWarnings("method.invocation.invalid") public PlannerImpl(FrameworkConfig config) { this.costFactory = config.getCostFactory(); this.defaultSchema = config.getDefaultSchema(); @@ -115,12 +118,13 @@ public PlannerImpl(FrameworkConfig config) { this.convertletTable = config.getConvertletTable(); this.executor = config.getExecutor(); this.context = config.getContext(); - this.connectionConfig = connConfig(); + this.connectionConfig = connConfig(context, parserConfig); reset(); } /** Gets a user-defined config and appends default connection values. */ - private CalciteConnectionConfig connConfig() { + private static CalciteConnectionConfig connConfig(Context context, + SqlParser.Config parserConfig) { CalciteConnectionConfigImpl config = Util.first( context.unwrap(CalciteConnectionConfigImpl.class), CalciteConnectionConfig.DEFAULT); @@ -148,7 +152,7 @@ private void ensure(State state) { } @Override public RelTraitSet getEmptyTraitSet() { - return planner.emptyTraitSet(); + return requireNonNull(planner, "planner").emptyTraitSet(); } @Override public void close() { @@ -177,7 +181,7 @@ private void ready() { connectionConfig.typeSystem(RelDataTypeSystem.class, RelDataTypeSystem.DEFAULT); typeFactory = new JavaTypeFactoryImpl(typeSystem); - planner = new VolcanoPlanner(costFactory, context); + RelOptPlanner planner = this.planner = new VolcanoPlanner(costFactory, context); RelOptUtil.registerDefaultRules(planner, connectionConfig.materializationsEnabled(), Hook.ENABLE_BINDABLE.get(false)); @@ -215,6 +219,7 @@ private void ready() { return sqlNode; } + @EnsuresNonNull("validator") @Override public SqlNode validate(SqlNode sqlNode) throws ValidationException { ensure(State.STATE_3_PARSED); this.validator = createSqlValidator(createCatalogReader()); @@ -242,15 +247,18 @@ private void ready() { @Override public RelRoot rel(SqlNode sql) { ensure(State.STATE_4_VALIDATED); - assert validatedSqlNode != null; + SqlNode validatedSqlNode = requireNonNull(this.validatedSqlNode, + "validatedSqlNode is null. Need to call #validate() first"); final RexBuilder rexBuilder = createRexBuilder(); - final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder); + final RelOptCluster cluster = RelOptCluster.create( + requireNonNull(planner, "planner"), + rexBuilder); final SqlToRelConverter.Config config = sqlToRelConverterConfig.withTrimUnusedFields(false); final SqlToRelConverter sqlToRelConverter = new SqlToRelConverter(this, validator, createCatalogReader(), cluster, convertletTable, config); - root = + RelRoot root = sqlToRelConverter.convertQuery(validatedSqlNode, false, true); root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true)); final RelBuilder relBuilder = @@ -270,16 +278,18 @@ public class ViewExpanderImpl implements ViewExpander { } @Override public RelRoot expandView(RelDataType rowType, String queryString, - List schemaPath, List viewPath) { + List schemaPath, @Nullable List viewPath) { return PlannerImpl.this.expandView(rowType, queryString, schemaPath, viewPath); } } @Override public RelRoot expandView(RelDataType rowType, String queryString, - List schemaPath, List viewPath) { + List schemaPath, @Nullable List viewPath) { + RelOptPlanner planner = this.planner; if (planner == null) { ready(); + planner = requireNonNull(this.planner, "planner"); } SqlParser parser = SqlParser.create(queryString, parserConfig); SqlNode sqlNode; @@ -313,12 +323,13 @@ public class ViewExpanderImpl implements ViewExpander { // CalciteCatalogReader is stateless; no need to store one private CalciteCatalogReader createCatalogReader() { + SchemaPlus defaultSchema = requireNonNull(this.defaultSchema, "defaultSchema"); final SchemaPlus rootSchema = rootSchema(defaultSchema); return new CalciteCatalogReader( CalciteSchema.from(rootSchema), CalciteSchema.from(defaultSchema).path(null), - typeFactory, connectionConfig); + getTypeFactory(), connectionConfig); } private SqlValidator createSqlValidator(CalciteCatalogReader catalogReader) { @@ -326,7 +337,7 @@ private SqlValidator createSqlValidator(CalciteCatalogReader catalogReader) { SqlOperatorTables.chain(operatorTable, catalogReader); return new CalciteSqlValidator(opTab, catalogReader, - typeFactory, + getTypeFactory(), sqlValidatorConfig .withDefaultNullCollation(connectionConfig.defaultNullCollation()) .withLenientOperatorLookup(connectionConfig.lenientOperatorLookup()) @@ -336,20 +347,21 @@ private SqlValidator createSqlValidator(CalciteCatalogReader catalogReader) { private static SchemaPlus rootSchema(SchemaPlus schema) { for (;;) { - if (schema.getParentSchema() == null) { + SchemaPlus parentSchema = schema.getParentSchema(); + if (parentSchema == null) { return schema; } - schema = schema.getParentSchema(); + schema = parentSchema; } } // RexBuilder is stateless; no need to store one private RexBuilder createRexBuilder() { - return new RexBuilder(typeFactory); + return new RexBuilder(getTypeFactory()); } @Override public JavaTypeFactory getTypeFactory() { - return typeFactory; + return requireNonNull(typeFactory, "typeFactory"); } @Override public RelNode transform(int ruleSetIndex, RelTraitSet requiredOutputTraits, @@ -357,10 +369,11 @@ private RexBuilder createRexBuilder() { ensure(State.STATE_5_CONVERTED); rel.getCluster().setMetadataProvider( new CachingRelMetadataProvider( - rel.getCluster().getMetadataProvider(), + requireNonNull(rel.getCluster().getMetadataProvider(), "metadataProvider"), rel.getCluster().getPlanner())); Program program = programs.get(ruleSetIndex); - return program.run(planner, rel, requiredOutputTraits, ImmutableList.of(), + return program.run(requireNonNull(planner, "planner"), + rel, requiredOutputTraits, ImmutableList.of(), ImmutableList.of()); } diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java index e99b2d360a47..858b66ab13a0 100644 --- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java +++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java @@ -68,13 +68,18 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; /** * Abstract base for classes that implement @@ -89,12 +94,12 @@ public abstract class Prepare { * Convention via which results should be returned by execution. */ protected final Convention resultConvention; - protected CalciteTimingTracer timingTracer; - protected List> fieldOrigins; - protected RelDataType parameterRowType; + protected @Nullable CalciteTimingTracer timingTracer; + protected @MonotonicNonNull List<@Nullable List> fieldOrigins; + protected @MonotonicNonNull RelDataType parameterRowType; // temporary. for testing. - public static final TryThreadLocal THREAD_TRIM = + public static final TryThreadLocal<@Nullable Boolean> THREAD_TRIM = TryThreadLocal.of(false); /** Temporary, until @@ -104,7 +109,7 @@ public abstract class Prepare { *

The default is false, meaning do not expand queries during sql-to-rel, * but a few tests override and set it to true. After CALCITE-1045 * is fixed, remove those overrides and use false everywhere. */ - public static final TryThreadLocal THREAD_EXPAND = + public static final TryThreadLocal<@Nullable Boolean> THREAD_EXPAND = TryThreadLocal.of(false); protected Prepare(CalcitePrepare.Context context, CatalogReader catalogReader, @@ -116,9 +121,9 @@ protected Prepare(CalcitePrepare.Context context, CatalogReader catalogReader, } protected abstract PreparedResult createPreparedExplanation( - RelDataType resultType, + @Nullable RelDataType resultType, RelDataType parameterRowType, - RelRoot root, + @Nullable RelRoot root, SqlExplainFormat format, SqlExplainLevel detailLevel); @@ -143,8 +148,9 @@ protected RelRoot optimize(RelRoot root, for (Materialization materialization : materializations) { List qualifiedTableName = materialization.materializedTable.path(); materializationList.add( - new RelOptMaterialization(materialization.tableRel, - materialization.queryRel, + new RelOptMaterialization( + castNonNull(materialization.tableRel), + castNonNull(materialization.queryRel), materialization.starRelOptTable, qualifiedTableName)); } @@ -175,10 +181,11 @@ protected RelRoot optimize(RelRoot root, protected Program getProgram() { // Allow a test to override the default program. - final Holder holder = Holder.of(null); + final Holder<@Nullable Program> holder = Holder.of(null); Hook.PROGRAM.run(holder); - if (holder.get() != null) { - return holder.get(); + Program holderValue = holder.get(); + if (holderValue != null) { + return holderValue; } return Programs.standard(); @@ -224,7 +231,7 @@ public PreparedResult prepareSql( final SqlToRelConverter.Config config = SqlToRelConverter.config() .withTrimUnusedFields(true) - .withExpand(THREAD_EXPAND.get()) + .withExpand(castNonNull(THREAD_EXPAND.get())) .withExplain(sqlQuery.getKind() == SqlKind.EXPLAIN); final Holder configHolder = Holder.of(config); Hook.SQL2REL_CONVERTER_CONFIG_BUILDER.run(configHolder); @@ -311,7 +318,7 @@ public PreparedResult prepareSql( return implement(root); } - protected TableModify.Operation mapTableModOp( + protected TableModify.@Nullable Operation mapTableModOp( boolean isDml, SqlKind sqlKind) { if (!isDml) { return null; @@ -362,7 +369,7 @@ protected abstract RelNode decorrelate(SqlToRelConverter sqlToRelConverter, protected RelRoot trimUnusedFields(RelRoot root) { final SqlToRelConverter.Config config = SqlToRelConverter.config() .withTrimUnusedFields(shouldTrim(root.rel)) - .withExpand(THREAD_EXPAND.get()); + .withExpand(castNonNull(THREAD_EXPAND.get())); final SqlToRelConverter converter = getSqlToRelConverter(getSqlValidator(), catalogReader, config); final boolean ordered = !root.collation.getFieldCollations().isEmpty(); @@ -374,7 +381,7 @@ private boolean shouldTrim(RelNode rootRel) { // For now, don't trim if there are more than 3 joins. The projects // near the leaves created by trim migrate past joins and seem to // prevent join-reordering. - return THREAD_TRIM.get() || RelOptUtil.countJoins(rootRel) < 2; + return castNonNull(THREAD_TRIM.get()) || RelOptUtil.countJoins(rootRel) < 2; } protected abstract void init(Class runtimeContextClass); @@ -384,15 +391,15 @@ private boolean shouldTrim(RelNode rootRel) { /** Interface by which validator and planner can read table metadata. */ public interface CatalogReader extends RelOptSchema, SqlValidatorCatalogReader, SqlOperatorTable { - @Override PreparingTable getTableForMember(List names); + @Override @Nullable PreparingTable getTableForMember(List names); /** Returns a catalog reader the same as this one but with a possibly * different schema path. */ CatalogReader withSchemaPath(List schemaPath); - @Override PreparingTable getTable(List names); + @Override @Nullable PreparingTable getTable(List names); - ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); + ThreadLocal<@Nullable CatalogReader> THREAD_LOCAL = new ThreadLocal<>(); } /** Definition of a table, for the purposes of the validator and planner. */ @@ -444,7 +451,9 @@ public abstract static class AbstractPreparingTable (ModifiableViewTable) table; final ModifiableViewTable extendedView = modifiableViewTable.extend(dedupedExtendedFields, - getRelOptSchema().getTypeFactory()); + requireNonNull( + getRelOptSchema(), + () -> "relOptSchema for table " + getQualifiedName()).getTypeFactory()); return extend(extendedView); } throw new RuntimeException("Cannot extend " + table); @@ -465,16 +474,16 @@ public abstract static class AbstractPreparingTable */ public abstract static class PreparedExplain implements PreparedResult { - private final RelDataType rowType; + private final @Nullable RelDataType rowType; private final RelDataType parameterRowType; - private final RelRoot root; + private final @Nullable RelRoot root; private final SqlExplainFormat format; private final SqlExplainLevel detailLevel; protected PreparedExplain( - RelDataType rowType, + @Nullable RelDataType rowType, RelDataType parameterRowType, - RelRoot root, + @Nullable RelRoot root, SqlExplainFormat format, SqlExplainLevel detailLevel) { this.rowType = rowType; @@ -486,7 +495,7 @@ protected PreparedExplain( @Override public String getCode() { if (root == null) { - return RelOptUtil.dumpType(rowType); + return rowType == null ? "rowType is null" : RelOptUtil.dumpType(rowType); } else { return RelOptUtil.dumpPlan("", root.rel, format, detailLevel); } @@ -500,11 +509,11 @@ protected PreparedExplain( return false; } - @Override public TableModify.Operation getTableModOp() { + @Override public TableModify.@Nullable Operation getTableModOp() { return null; } - @Override public List> getFieldOrigins() { + @Override public List<@Nullable List> getFieldOrigins() { return Collections.singletonList( Collections.nCopies(4, null)); } @@ -530,13 +539,13 @@ public interface PreparedResult { * Returns the table modification operation corresponding to this * statement if it is a table modification statement; otherwise null. */ - TableModify.Operation getTableModOp(); + TableModify.@Nullable Operation getTableModOp(); /** * Returns a list describing, for each result field, the origin of the * field as a 4-element list of (database, schema, table, column). */ - List> getFieldOrigins(); + List> getFieldOrigins(); /** * Returns a record type whose fields are the parameters of this statement. @@ -561,23 +570,23 @@ public abstract static class PreparedResultImpl protected final RelDataType parameterRowType; protected final RelDataType rowType; protected final boolean isDml; - protected final TableModify.Operation tableModOp; - protected final List> fieldOrigins; + protected final TableModify.@Nullable Operation tableModOp; + protected final List> fieldOrigins; protected final List collations; protected PreparedResultImpl( RelDataType rowType, RelDataType parameterRowType, - List> fieldOrigins, + List> fieldOrigins, List collations, RelNode rootRel, - TableModify.Operation tableModOp, + TableModify.@Nullable Operation tableModOp, boolean isDml) { - this.rowType = Objects.requireNonNull(rowType); - this.parameterRowType = Objects.requireNonNull(parameterRowType); - this.fieldOrigins = Objects.requireNonNull(fieldOrigins); + this.rowType = requireNonNull(rowType); + this.parameterRowType = requireNonNull(parameterRowType); + this.fieldOrigins = requireNonNull(fieldOrigins); this.collations = ImmutableList.copyOf(collations); - this.rootRel = Objects.requireNonNull(rootRel); + this.rootRel = requireNonNull(rootRel); this.tableModOp = tableModOp; this.isDml = isDml; } @@ -586,11 +595,11 @@ protected PreparedResultImpl( return isDml; } - @Override public TableModify.Operation getTableModOp() { + @Override public TableModify.@Nullable Operation getTableModOp() { return tableModOp; } - @Override public List> getFieldOrigins() { + @Override public List> getFieldOrigins() { return fieldOrigins; } @@ -626,11 +635,11 @@ public static class Materialization { final List viewSchemaPath; /** Relational expression for the table. Usually a * {@link org.apache.calcite.rel.logical.LogicalTableScan}. */ - RelNode tableRel; + @Nullable RelNode tableRel; /** Relational expression for the query to populate the table. */ - RelNode queryRel; + @Nullable RelNode queryRel; /** Star table identified. */ - private RelOptTable starRelOptTable; + private @Nullable RelOptTable starRelOptTable; public Materialization(CalciteSchema.TableEntry materializedTable, String sql, List viewSchemaPath) { diff --git a/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java b/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java index 136ae6db1c25..261ae017de32 100644 --- a/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java +++ b/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java @@ -50,10 +50,17 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.math.BigDecimal; import java.util.Comparator; import java.util.List; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link QueryableFactory} * that builds a tree of {@link RelNode} planner nodes. Used by @@ -78,7 +85,7 @@ */ class QueryableRelBuilder implements QueryableFactory { private final LixToRelTranslator translator; - private RelNode rel; + private @Nullable RelNode rel; QueryableRelBuilder(LixToRelTranslator translator) { this.translator = translator; @@ -88,7 +95,7 @@ RelNode toRel(Queryable queryable) { if (queryable instanceof QueryableDefaults.Replayable) { //noinspection unchecked ((QueryableDefaults.Replayable) queryable).replay(this); - return rel; + return requireNonNull(rel, "rel"); } if (queryable instanceof AbstractTableQueryable) { final AbstractTableQueryable tableQueryable = @@ -107,7 +114,10 @@ RelNode toRel(Queryable queryable) { return LogicalTableScan.create(translator.cluster, relOptTable, ImmutableList.of()); } } - return translator.translate(queryable.getExpression()); + return translator.translate( + requireNonNull( + queryable.getExpression(), + () -> "null expression from " + queryable)); } /** Sets the output of this event. */ @@ -127,7 +137,7 @@ private void setRel(RelNode rel) { @Override public T aggregate( Queryable source, - FunctionExpression> selector) { + FunctionExpression> selector) { throw new UnsupportedOperationException(); } @@ -239,11 +249,11 @@ private void setRel(RelNode rel) { throw new UnsupportedOperationException(); } - @Override public Queryable defaultIfEmpty(Queryable source) { + @Override public Queryable<@Nullable T> defaultIfEmpty(Queryable source) { throw new UnsupportedOperationException(); } - @Override public Queryable defaultIfEmpty(Queryable source, T value) { + @Override public Queryable<@PolyNull T> defaultIfEmpty(Queryable source, @PolyNull T value) { throw new UnsupportedOperationException(); } @@ -539,7 +549,7 @@ private void setRel(RelNode rel) { List nodes = translator.toRexList(selector, child); setRel( LogicalProject.create(child, ImmutableList.of(), nodes, (List) null)); - return null; + return castNonNull(null); } @Override public Queryable selectN( diff --git a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java index 1f9245fc7c73..e9261f50321e 100644 --- a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java @@ -60,21 +60,24 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.Collection; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.function.Function; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link org.apache.calcite.plan.RelOptTable}. */ public class RelOptTableImpl extends Prepare.AbstractPreparingTable { - private final RelOptSchema schema; + private final @Nullable RelOptSchema schema; private final RelDataType rowType; - private final Table table; - private final Function expressionFunction; + private final @Nullable Table table; + private final @Nullable Function expressionFunction; private final ImmutableList names; /** Estimate for the row count, or null. @@ -84,17 +87,17 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable { *

Useful when a table that contains a materialized query result is being * used to replace a query expression that wildly underestimates the row * count. Now the materialized table can tell the same lie. */ - private final Double rowCount; + private final @Nullable Double rowCount; private RelOptTableImpl( - RelOptSchema schema, + @Nullable RelOptSchema schema, RelDataType rowType, List names, - Table table, - Function expressionFunction, - Double rowCount) { + @Nullable Table table, + @Nullable Function expressionFunction, + @Nullable Double rowCount) { this.schema = schema; - this.rowType = Objects.requireNonNull(rowType); + this.rowType = requireNonNull(rowType); this.names = ImmutableList.copyOf(names); this.table = table; // may be null this.expressionFunction = expressionFunction; // may be null @@ -102,7 +105,7 @@ private RelOptTableImpl( } public static RelOptTableImpl create( - RelOptSchema schema, + @Nullable RelOptSchema schema, RelDataType rowType, List names, Expression expression) { @@ -111,7 +114,7 @@ public static RelOptTableImpl create( } public static RelOptTableImpl create( - RelOptSchema schema, + @Nullable RelOptSchema schema, RelDataType rowType, List names, Table table, @@ -120,7 +123,7 @@ public static RelOptTableImpl create( c -> expression, table.getStatistic().getRowCount()); } - public static RelOptTableImpl create(RelOptSchema schema, RelDataType rowType, + public static RelOptTableImpl create(@Nullable RelOptSchema schema, RelDataType rowType, Table table, Path path) { final SchemaPlus schemaPlus = MySchemaPlus.create(path); return new RelOptTableImpl(schema, rowType, Pair.left(path), table, @@ -128,8 +131,8 @@ public static RelOptTableImpl create(RelOptSchema schema, RelDataType rowType, table.getStatistic().getRowCount()); } - public static RelOptTableImpl create(RelOptSchema schema, RelDataType rowType, - final CalciteSchema.TableEntry tableEntry, Double rowCount) { + public static RelOptTableImpl create(@Nullable RelOptSchema schema, RelDataType rowType, + final CalciteSchema.TableEntry tableEntry, @Nullable Double rowCount) { final Table table = tableEntry.getTable(); return new RelOptTableImpl(schema, rowType, tableEntry.path(), table, getClassExpressionFunction(tableEntry, table), rowCount); @@ -178,7 +181,7 @@ private static Function getClassExpressionFunction( } } - public static RelOptTableImpl create(RelOptSchema schema, + public static RelOptTableImpl create(@Nullable RelOptSchema schema, RelDataType rowType, Table table, ImmutableList names) { assert table instanceof TranslatableTable || table instanceof ScannableTable @@ -186,7 +189,7 @@ public static RelOptTableImpl create(RelOptSchema schema, return new RelOptTableImpl(schema, rowType, names, table, null, null); } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz.isInstance(this)) { return clazz.cast(this); } @@ -199,7 +202,7 @@ public static RelOptTableImpl create(RelOptSchema schema, return t; } } - if (clazz == CalciteSchema.class) { + if (clazz == CalciteSchema.class && schema != null) { return clazz.cast( Schemas.subSchema(((CalciteCatalogReader) schema).rootSchema, Util.skipLast(getQualifiedName()))); @@ -207,7 +210,7 @@ public static RelOptTableImpl create(RelOptSchema schema, return null; } - @Override public Expression getExpression(Class clazz) { + @Override public @Nullable Expression getExpression(Class clazz) { if (expressionFunction == null) { return null; } @@ -215,13 +218,14 @@ public static RelOptTableImpl create(RelOptSchema schema, } @Override protected RelOptTable extend(Table extendedTable) { + RelOptSchema schema = requireNonNull(getRelOptSchema(), "relOptSchema"); final RelDataType extendedRowType = - extendedTable.getRowType(getRelOptSchema().getTypeFactory()); - return new RelOptTableImpl(getRelOptSchema(), extendedRowType, getQualifiedName(), + extendedTable.getRowType(schema.getTypeFactory()); + return new RelOptTableImpl(schema, extendedRowType, getQualifiedName(), extendedTable, expressionFunction, getRowCount()); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj instanceof RelOptTableImpl && this.rowType.equals(((RelOptTableImpl) obj).getRowType()) && this.table == ((RelOptTableImpl) obj).table; @@ -244,7 +248,7 @@ public static RelOptTableImpl create(RelOptSchema schema, return 100d; } - @Override public RelOptSchema getRelOptSchema() { + @Override public @Nullable RelOptSchema getRelOptSchema() { return schema; } @@ -271,7 +275,7 @@ public static RelOptTableImpl create(RelOptSchema schema, final RelOptTable relOptTable = new RelOptTableImpl(this.schema, b.build(), this.names, this.table, this.expressionFunction, this.rowCount) { - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz.isAssignableFrom(InitializerExpressionFactory.class)) { return clazz.cast(NullInitializerExpressionFactory.INSTANCE); } @@ -287,14 +291,14 @@ public static RelOptTableImpl create(RelOptSchema schema, return LogicalTableScan.create(context.getCluster(), this, context.getTableHints()); } - @Override public List getCollationList() { + @Override public @Nullable List getCollationList() { if (table != null) { return table.getStatistic().getCollations(); } return ImmutableList.of(); } - @Override public RelDistribution getDistribution() { + @Override public @Nullable RelDistribution getDistribution() { if (table != null) { return table.getStatistic().getDistribution(); } @@ -308,11 +312,14 @@ public static RelOptTableImpl create(RelOptSchema schema, return false; } - @Override public List getKeys() { - return table.getStatistic().getKeys(); + @Override public @Nullable List getKeys() { + if (table != null) { + return table.getStatistic().getKeys(); + } + return ImmutableList.of(); } - @Override public List getReferentialConstraints() { + @Override public @Nullable List getReferentialConstraints() { if (table != null) { return table.getStatistic().getReferentialConstraints(); } @@ -341,9 +348,12 @@ public static RelOptTableImpl create(RelOptSchema schema, } @Override public SqlMonotonicity getMonotonicity(String columnName) { + if (table == null) { + return SqlMonotonicity.NOT_MONOTONIC; + } List collations = table.getStatistic().getCollations(); if (collations == null) { - return null; + return SqlMonotonicity.NOT_MONOTONIC; } for (RelCollation collation : collations) { final RelFieldCollation fieldCollation = @@ -405,7 +415,8 @@ public static RelDataType realRowType(RelOptTable table) { return rowType; } final RelDataTypeFactory.Builder builder = - table.getRelOptSchema().getTypeFactory().builder(); + requireNonNull(table.getRelOptSchema(), + () -> "relOptSchema for table " + table).getTypeFactory().builder(); for (RelDataTypeField field : rowType.getFieldList()) { if (strategies.get(field.getIndex()) != ColumnStrategy.VIRTUAL) { builder.add(field); @@ -420,11 +431,11 @@ public static RelDataType realRowType(RelOptTable table) { *

It is read-only, and functionality is limited in other ways, it but * allows table expressions to be generated. */ private static class MySchemaPlus implements SchemaPlus { - private final SchemaPlus parent; + private final @Nullable SchemaPlus parent; private final String name; private final Schema schema; - MySchemaPlus(SchemaPlus parent, String name, Schema schema) { + MySchemaPlus(@Nullable SchemaPlus parent, String name, Schema schema) { this.parent = parent; this.name = name; this.schema = schema; @@ -441,7 +452,7 @@ public static MySchemaPlus create(Path path) { return new MySchemaPlus(parent, pair.left, pair.right); } - @Override public SchemaPlus getParentSchema() { + @Override public @Nullable SchemaPlus getParentSchema() { return parent; } @@ -449,7 +460,7 @@ public static MySchemaPlus create(Path path) { return name; } - @Override public SchemaPlus getSubSchema(String name) { + @Override public @Nullable SchemaPlus getSubSchema(String name) { final Schema subSchema = schema.getSubSchema(name); return subSchema == null ? null : new MySchemaPlus(this, name, subSchema); } @@ -479,7 +490,7 @@ public static MySchemaPlus create(Path path) { return schema.isMutable(); } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { return null; } @@ -495,7 +506,7 @@ public static MySchemaPlus create(Path path) { return false; } - @Override public Table getTable(String name) { + @Override public @Nullable Table getTable(String name) { return schema.getTable(name); } @@ -503,7 +514,7 @@ public static MySchemaPlus create(Path path) { return schema.getTableNames(); } - @Override public RelProtoDataType getType(String name) { + @Override public @Nullable RelProtoDataType getType(String name) { return schema.getType(name); } @@ -524,7 +535,7 @@ public static MySchemaPlus create(Path path) { return schema.getSubSchemaNames(); } - @Override public Expression getExpression(SchemaPlus parentSchema, + @Override public Expression getExpression(@Nullable SchemaPlus parentSchema, String name) { return schema.getExpression(parentSchema, name); } diff --git a/core/src/main/java/org/apache/calcite/profile/Profiler.java b/core/src/main/java/org/apache/calcite/profile/Profiler.java index cd389854eaa7..32fcced511eb 100644 --- a/core/src/main/java/org/apache/calcite/profile/Profiler.java +++ b/core/src/main/java/org/apache/calcite/profile/Profiler.java @@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; @@ -34,7 +36,6 @@ import java.util.Map; import java.util.NavigableSet; import java.util.SortedSet; -import javax.annotation.Nonnull; /** * Analyzes data sets. @@ -81,13 +82,13 @@ static ImmutableBitSet toOrdinals(Iterable columns) { return ordinal; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof Column && ordinal == ((Column) o).ordinal; } - @Override public int compareTo(@Nonnull Column column) { + @Override public int compareTo(Column column) { return Integer.compare(ordinal, column.ordinal); } @@ -110,7 +111,7 @@ public RowCount(int rowCount) { } @Override public Object toMap(JsonBuilder jsonBuilder) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder.map(); map.put("type", "rowCount"); map.put("rowCount", rowCount); return map; @@ -126,7 +127,7 @@ public Unique(SortedSet columns) { } @Override public Object toMap(JsonBuilder jsonBuilder) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder.map(); map.put("type", "unique"); map.put("columns", FunctionalDependency.getObjects(jsonBuilder, columns)); return map; @@ -144,16 +145,16 @@ class FunctionalDependency implements Statistic { } @Override public Object toMap(JsonBuilder jsonBuilder) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder.map(); map.put("type", "fd"); map.put("columns", getObjects(jsonBuilder, columns)); map.put("dependentColumn", dependentColumn.name); return map; } - private static List getObjects(JsonBuilder jsonBuilder, + private static List<@Nullable Object> getObjects(JsonBuilder jsonBuilder, NavigableSet columns) { - final List list = jsonBuilder.list(); + final List<@Nullable Object> list = jsonBuilder.list(); for (Column column : columns) { list.add(column.name); } @@ -172,7 +173,7 @@ class Distribution implements Statistic { new MathContext(3, RoundingMode.HALF_EVEN); final NavigableSet columns; - final NavigableSet values; + final @Nullable NavigableSet values; final double cardinality; final int nullCount; final double expectedCardinality; @@ -188,7 +189,7 @@ class Distribution implements Statistic { * @param minimal Whether the distribution is not implied by a unique * or functional dependency */ - public Distribution(SortedSet columns, SortedSet values, + public Distribution(SortedSet columns, @Nullable SortedSet values, double cardinality, int nullCount, double expectedCardinality, boolean minimal) { this.columns = ImmutableSortedSet.copyOf(columns); @@ -200,11 +201,11 @@ public Distribution(SortedSet columns, SortedSet values, } @Override public Object toMap(JsonBuilder jsonBuilder) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder.map(); map.put("type", "distribution"); map.put("columns", FunctionalDependency.getObjects(jsonBuilder, columns)); if (values != null) { - List list = jsonBuilder.list(); + List<@Nullable Object> list = jsonBuilder.list(); for (Comparable value : values) { if (value instanceof java.sql.Date) { value = value.toString(); diff --git a/core/src/main/java/org/apache/calcite/profile/ProfilerImpl.java b/core/src/main/java/org/apache/calcite/profile/ProfilerImpl.java index 9ab14c185e08..1164bb3c49dc 100644 --- a/core/src/main/java/org/apache/calcite/profile/ProfilerImpl.java +++ b/core/src/main/java/org/apache/calcite/profile/ProfilerImpl.java @@ -34,6 +34,8 @@ import com.google.common.collect.Ordering; import com.yahoo.sketches.hll.HllSketch; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; @@ -55,6 +57,7 @@ import java.util.TreeSet; import java.util.function.Predicate; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.profile.ProfilerImpl.CompositeCollector.OF; /** @@ -290,7 +293,7 @@ void pass(int pass, List spaces, Iterable> rows) { for (final List row : rows) { ++rowCount; for (Space space : spaces) { - space.collector.add(row); + castNonNull(space.collector).add(row); } } @@ -447,14 +450,14 @@ static class Space { final BitSet dependencies = new BitSet(); final Set dependents = new HashSet<>(); double expectedCardinality; - Collector collector; + @Nullable Collector collector; /** Assigned by {@link Collector#finish()}. */ int nullCount; /** Number of distinct values. Null is counted as a value, if present. * Assigned by {@link Collector#finish()}. */ int cardinality; /** Assigned by {@link Collector#finish()}. */ - SortedSet valueSet; + @Nullable SortedSet valueSet; Space(Run run, ImmutableBitSet columnOrdinals, Iterable columns) { this.run = run; @@ -466,7 +469,7 @@ static class Space { return columnOrdinals.hashCode(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof Space && columnOrdinals.equals(((Space) o).columnOrdinals); @@ -474,7 +477,7 @@ static class Space { /** Returns the distribution created from this space, or null if no * distribution has been registered yet. */ - public Distribution distribution() { + public @Nullable Distribution distribution() { return run.distributions.get(columnOrdinals); } @@ -771,7 +774,7 @@ boolean isValid() { boolean offer(double d) { boolean b; - if (count++ < warmUpCount || d > priorityQueue.peek()) { + if (count++ < warmUpCount || d > castNonNull(priorityQueue.peek())) { if (priorityQueue.size() >= size) { priorityQueue.remove(deque.pop()); } diff --git a/core/src/main/java/org/apache/calcite/profile/SimpleProfiler.java b/core/src/main/java/org/apache/calcite/profile/SimpleProfiler.java index a4216eb082ee..d2af454f2b4e 100644 --- a/core/src/main/java/org/apache/calcite/profile/SimpleProfiler.java +++ b/core/src/main/java/org/apache/calcite/profile/SimpleProfiler.java @@ -27,6 +27,8 @@ import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; @@ -39,7 +41,6 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import javax.annotation.Nonnull; /** * Basic implementation of {@link Profiler}. @@ -304,13 +305,13 @@ static class Space implements Comparable { return columnOrdinals.hashCode(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof Space && columnOrdinals.equals(((Space) o).columnOrdinals); } - @Override public int compareTo(@Nonnull Space o) { + @Override public int compareTo(Space o) { return columnOrdinals.equals(o.columnOrdinals) ? 0 : columnOrdinals.contains(o.columnOrdinals) ? 1 : -1; diff --git a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java index 8ef74654e4ef..70833f663440 100644 --- a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java +++ b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java @@ -41,6 +41,10 @@ import com.google.common.collect.ImmutableSet; import org.apiguardian.api.API; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import java.util.ArrayList; import java.util.Collections; @@ -48,6 +52,8 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import static java.util.Objects.requireNonNull; + /** * Base class for every relational expression ({@link RelNode}). */ @@ -62,7 +68,7 @@ public abstract class AbstractRelNode implements RelNode { /** * Cached type of this relational expression. */ - protected RelDataType rowType; + protected @MonotonicNonNull RelDataType rowType; /** * The digest that uniquely identifies the node. @@ -118,15 +124,18 @@ protected static T sole(List collection) { return cluster; } - @Override public final Convention getConvention() { - return traitSet.getTrait(ConventionTraitDef.INSTANCE); + @Pure + @Override public final @Nullable Convention getConvention( + @UnknownInitialization AbstractRelNode this + ) { + return traitSet == null ? null : traitSet.getTrait(ConventionTraitDef.INSTANCE); } @Override public RelTraitSet getTraitSet() { return traitSet; } - @Override public String getCorrelVariable() { + @Override public @Nullable String getCorrelVariable() { return null; } @@ -155,7 +164,7 @@ protected static T sole(List collection) { return cn; } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { return litmus.succeed(); } @@ -247,7 +256,7 @@ protected RelDataType deriveRowType() { * Each node should call {@code super.explainTerms}, then call the * {@link org.apache.calcite.rel.externalize.RelWriterImpl#input(String, RelNode)} * and - * {@link org.apache.calcite.rel.externalize.RelWriterImpl#item(String, Object)} + * {@link RelWriter#item(String, Object)} * methods for each input and attribute. * * @param pw Plan writer @@ -306,7 +315,7 @@ public RelWriter explainTerms(RelWriter pw) { return digest; } - @Override public RelOptTable getTable() { + @Override public @Nullable RelOptTable getTable() { return null; } @@ -317,7 +326,7 @@ public RelWriter explainTerms(RelWriter pw) { * sub-classes of {@link RelNode} to redefine identity. Various algorithms * (e.g. visitors, planner) can define the identity as meets their needs. */ - @Override public final boolean equals(Object obj) { + @Override public final boolean equals(@Nullable Object obj) { return super.equals(obj); } @@ -345,12 +354,12 @@ public RelWriter explainTerms(RelWriter pw) { * @return Whether the 2 RelNodes are equivalent or have the same digest. * @see #deepHashCode() */ - @Override @API(since = "1.25", status = API.Status.MAINTAINED) - public boolean deepEquals(Object obj) { + @API(since = "1.25", status = API.Status.MAINTAINED) + @Override public boolean deepEquals(@Nullable Object obj) { if (this == obj) { return true; } - if (this.getClass() != obj.getClass()) { + if (obj == null || this.getClass() != obj.getClass()) { return false; } AbstractRelNode that = (AbstractRelNode) obj; @@ -359,14 +368,14 @@ public boolean deepEquals(Object obj) { if (!result) { return false; } - List> items1 = this.getDigestItems(); - List> items2 = that.getDigestItems(); + List> items1 = this.getDigestItems(); + List> items2 = that.getDigestItems(); if (items1.size() != items2.size()) { return false; } for (int i = 0; result && i < items1.size(); i++) { - Pair attr1 = items1.get(i); - Pair attr2 = items2.get(i); + Pair attr1 = items1.get(i); + Pair attr2 = items2.get(i); if (attr1.right instanceof RelNode) { result = ((RelNode) attr1.right).deepEquals(attr2.right); } else { @@ -379,13 +388,13 @@ public boolean deepEquals(Object obj) { /** * Compute hash code for RelNode digest. * - * @see #deepEquals(Object) + * @see RelNode#deepEquals(Object) */ @API(since = "1.25", status = API.Status.MAINTAINED) @Override public int deepHashCode() { int result = 31 + getTraitSet().hashCode(); - List> items = this.getDigestItems(); - for (Pair item : items) { + List> items = this.getDigestItems(); + for (Pair item : items) { Object value = item.right; final int h; if (value == null) { @@ -400,7 +409,7 @@ public boolean deepEquals(Object obj) { return result; } - private List> getDigestItems() { + private List> getDigestItems() { RelDigestWriter rdw = new RelDigestWriter(); explainTerms(rdw); if (this instanceof Hintable) { @@ -423,7 +432,7 @@ private class InnerRelDigest implements RelDigest { hash = 0; } - @Override public boolean equals(final Object o) { + @Override public boolean equals(final @Nullable Object o) { if (this == o) { return true; } @@ -444,7 +453,7 @@ private class InnerRelDigest implements RelDigest { @Override public String toString() { RelDigestWriter rdw = new RelDigestWriter(); explain(rdw); - return rdw.digest; + return requireNonNull(rdw.digest, "digest"); } } @@ -458,11 +467,12 @@ private class InnerRelDigest implements RelDigest { */ private static final class RelDigestWriter implements RelWriter { - private final List> attrs = new ArrayList<>(); + private final List> attrs = new ArrayList<>(); - String digest = null; + @Nullable String digest = null; - @Override public void explain(final RelNode rel, final List> valueList) { + @Override public void explain(final RelNode rel, + final List> valueList) { throw new IllegalStateException("Should not be called for computing digest"); } @@ -470,7 +480,7 @@ private static final class RelDigestWriter implements RelWriter { return SqlExplainLevel.DIGEST_ATTRIBUTES; } - @Override public RelWriter item(String term, Object value) { + @Override public RelWriter item(String term, @Nullable Object value) { if (value != null && value.getClass().isArray()) { // We can't call hashCode and equals on Array, so // convert it to String to keep the same behaviour. @@ -487,7 +497,7 @@ private static final class RelDigestWriter implements RelWriter { sb.append(node.getTraitSet()); sb.append('('); int j = 0; - for (Pair attr : attrs) { + for (Pair attr : attrs) { if (j++ > 0) { sb.append(','); } diff --git a/core/src/main/java/org/apache/calcite/rel/PhysicalNode.java b/core/src/main/java/org/apache/calcite/rel/PhysicalNode.java index a2839a404076..8e32b9509e34 100644 --- a/core/src/main/java/org/apache/calcite/rel/PhysicalNode.java +++ b/core/src/main/java/org/apache/calcite/rel/PhysicalNode.java @@ -23,6 +23,8 @@ import org.apache.calcite.rel.core.Sort; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -73,7 +75,7 @@ public interface PhysicalNode extends RelNode { * Pass required traitset from parent node to child nodes, * returns new node after traits is passed down. */ - default RelNode passThrough(RelTraitSet required) { + default @Nullable RelNode passThrough(RelTraitSet required) { Pair> p = passThroughTraits(required); if (p == null) { return null; @@ -95,7 +97,7 @@ default RelNode passThrough(RelTraitSet required) { *

Pair.left: the new traitset *

Pair.right: the list of required traitsets for child nodes */ - default Pair> passThroughTraits( + default @Nullable Pair> passThroughTraits( RelTraitSet required) { throw new RuntimeException(getClass().getName() + "#passThroughTraits() is not implemented."); @@ -105,7 +107,7 @@ default Pair> passThroughTraits( * Derive traitset from child node, returns new node after * traits derivation. */ - default RelNode derive(RelTraitSet childTraits, int childId) { + default @Nullable RelNode derive(RelTraitSet childTraits, int childId) { Pair> p = deriveTraits(childTraits, childId); if (p == null) { return null; @@ -128,7 +130,7 @@ default RelNode derive(RelTraitSet childTraits, int childId) { *

Pair.left: the new traitset *

Pair.right: the list of required traitsets for child nodes */ - default Pair> deriveTraits( + default @Nullable Pair> deriveTraits( RelTraitSet childTraits, int childId) { throw new RuntimeException(getClass().getName() + "#deriveTraits() is not implemented."); diff --git a/core/src/main/java/org/apache/calcite/rel/RelCollation.java b/core/src/main/java/org/apache/calcite/rel/RelCollation.java index 35e101d92e2b..faa2cf187a89 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelCollation.java +++ b/core/src/main/java/org/apache/calcite/rel/RelCollation.java @@ -20,7 +20,6 @@ import org.apache.calcite.util.ImmutableIntList; import java.util.List; -import javax.annotation.Nonnull; /** * Description of the physical ordering of a relational expression. @@ -34,12 +33,12 @@ public interface RelCollation extends RelMultipleTrait { /** * Returns the ordinals and directions of the columns in this ordering. */ - @Nonnull List getFieldCollations(); + List getFieldCollations(); /** * Returns the ordinals of the key columns. */ - default @Nonnull ImmutableIntList getKeys() { + default ImmutableIntList getKeys() { final List collations = getFieldCollations(); final int size = collations.size(); final int[] keys = new int[size]; diff --git a/core/src/main/java/org/apache/calcite/rel/RelCollationImpl.java b/core/src/main/java/org/apache/calcite/rel/RelCollationImpl.java index 3853f46b0d10..44a78ca64622 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelCollationImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/RelCollationImpl.java @@ -30,9 +30,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.UnmodifiableIterator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Iterator; import java.util.List; -import javax.annotation.Nonnull; /** * Simple implementation of {@link RelCollation}. @@ -83,7 +84,7 @@ public static RelCollation of(List fieldCollations) { return fieldCollations.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -98,7 +99,7 @@ public static RelCollation of(List fieldCollations) { return fieldCollations.isEmpty(); } - @Override public int compareTo(@Nonnull RelMultipleTrait o) { + @Override public int compareTo(RelMultipleTrait o) { final RelCollationImpl that = (RelCollationImpl) o; final UnmodifiableIterator iterator = that.fieldCollations.iterator(); diff --git a/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java b/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java index ad6d9fa2d7a0..7dce4d7c3288 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java +++ b/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java @@ -22,6 +22,8 @@ import org.apache.calcite.rel.core.Sort; import org.apache.calcite.rel.logical.LogicalSort; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Definition of the ordering trait. * @@ -59,7 +61,7 @@ private RelCollationTraitDef() { return RelCollations.EMPTY; } - @Override public RelNode convert( + @Override public @Nullable RelNode convert( RelOptPlanner planner, RelNode rel, RelCollation toCollation, diff --git a/core/src/main/java/org/apache/calcite/rel/RelDistribution.java b/core/src/main/java/org/apache/calcite/rel/RelDistribution.java index cc4d96b39f62..2c9320ffc72a 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelDistribution.java +++ b/core/src/main/java/org/apache/calcite/rel/RelDistribution.java @@ -20,7 +20,6 @@ import org.apache.calcite.util.mapping.Mappings; import java.util.List; -import javax.annotation.Nonnull; /** * Description of the physical distribution of a relational expression. @@ -37,7 +36,7 @@ */ public interface RelDistribution extends RelMultipleTrait { /** Returns the type of distribution. */ - @Nonnull Type getType(); + Type getType(); /** * Returns the ordinals of the key columns. @@ -46,7 +45,7 @@ public interface RelDistribution extends RelMultipleTrait { * it unimportant but impose an arbitrary order; other types (BROADCAST, * SINGLETON) never have keys. */ - @Nonnull List getKeys(); + List getKeys(); /** * Applies mapping to this distribution trait. diff --git a/core/src/main/java/org/apache/calcite/rel/RelDistributionTraitDef.java b/core/src/main/java/org/apache/calcite/rel/RelDistributionTraitDef.java index 1582e32e66fc..5fc2ed5bf576 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelDistributionTraitDef.java +++ b/core/src/main/java/org/apache/calcite/rel/RelDistributionTraitDef.java @@ -22,6 +22,8 @@ import org.apache.calcite.rel.core.Exchange; import org.apache.calcite.rel.logical.LogicalExchange; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Definition of the distribution trait. * @@ -48,7 +50,7 @@ private RelDistributionTraitDef() { return RelDistributions.ANY; } - @Override public RelNode convert(RelOptPlanner planner, RelNode rel, + @Override public @Nullable RelNode convert(RelOptPlanner planner, RelNode rel, RelDistribution toDistribution, boolean allowInfiniteCostConverters) { if (toDistribution == RelDistributions.ANY) { return rel; diff --git a/core/src/main/java/org/apache/calcite/rel/RelDistributions.java b/core/src/main/java/org/apache/calcite/rel/RelDistributions.java index 05f55392c04c..6984d20fe346 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelDistributions.java +++ b/core/src/main/java/org/apache/calcite/rel/RelDistributions.java @@ -26,10 +26,11 @@ import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; /** * Utilities concerning {@link org.apache.calcite.rel.RelDistribution}. @@ -110,7 +111,7 @@ private RelDistributionImpl(Type type, ImmutableIntList keys) { return Objects.hash(type, keys); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RelDistributionImpl && type == ((RelDistributionImpl) obj).type @@ -125,11 +126,11 @@ private RelDistributionImpl(Type type, ImmutableIntList keys) { } } - @Override @Nonnull public Type getType() { + @Override public Type getType() { return type; } - @Override @Nonnull public List getKeys() { + @Override public List getKeys() { return keys; } @@ -188,7 +189,7 @@ private RelDistributionImpl(Type type, ImmutableIntList keys) { return type == Type.ANY; } - @Override public int compareTo(@Nonnull RelMultipleTrait o) { + @Override public int compareTo(RelMultipleTrait o) { final RelDistribution distribution = (RelDistribution) o; if (type == distribution.getType() && (type == Type.HASH_DISTRIBUTED diff --git a/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java b/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java index 887e4b2a7164..6584d2f35dd5 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java +++ b/core/src/main/java/org/apache/calcite/rel/RelFieldCollation.java @@ -18,8 +18,9 @@ import org.apache.calcite.sql.validate.SqlMonotonicity; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; -import javax.annotation.Nonnull; /** * Definition of the ordering of one field of a {@link RelNode} whose @@ -30,7 +31,7 @@ public class RelFieldCollation { /** Utility method that compares values taking into account null * direction. */ - public static int compare(Comparable c1, Comparable c2, int nullComparison) { + public static int compare(@Nullable Comparable c1, @Nullable Comparable c2, int nullComparison) { if (c1 == c2) { return 0; } else if (c1 == null) { @@ -126,7 +127,7 @@ public static Direction of(SqlMonotonicity monotonicity) { /** Returns the null direction if not specified. Consistent with Oracle, * NULLS are sorted as if they were positive infinity. */ - public @Nonnull NullDirection defaultNullDirection() { + public NullDirection defaultNullDirection() { switch (this) { case ASCENDING: case STRICTLY_ASCENDING: @@ -156,7 +157,7 @@ public boolean isDescending() { * * @return reverse of the input direction */ - @Nonnull public Direction reverse() { + public Direction reverse() { switch (this) { case ASCENDING: case STRICTLY_ASCENDING: @@ -170,7 +171,7 @@ public boolean isDescending() { } /** Removes strictness. */ - @Nonnull public Direction lax() { + public Direction lax() { switch (this) { case STRICTLY_ASCENDING: return ASCENDING; @@ -278,7 +279,7 @@ public RelFieldCollation shift(int offset) { return withFieldIndex(fieldIndex + offset); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof RelFieldCollation && fieldIndex == ((RelFieldCollation) o).fieldIndex diff --git a/core/src/main/java/org/apache/calcite/rel/RelInput.java b/core/src/main/java/org/apache/calcite/rel/RelInput.java index 72015b009947..a360c1455602 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelInput.java +++ b/core/src/main/java/org/apache/calcite/rel/RelInput.java @@ -27,6 +27,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -51,20 +53,20 @@ public interface RelInput { /** * Returns an expression. */ - RexNode getExpression(String tag); + @Nullable RexNode getExpression(String tag); ImmutableBitSet getBitSet(String tag); - List getBitSetList(String tag); + @Nullable List getBitSetList(String tag); List getAggregateCalls(String tag); - Object get(String tag); + @Nullable Object get(String tag); /** * Returns a {@code string} value. Throws if wrong type. */ - String getString(String tag); + @Nullable String getString(String tag); /** * Returns a {@code float} value. Throws if not present or wrong type. @@ -74,15 +76,15 @@ public interface RelInput { /** * Returns an enum value. Throws if not a valid member. */ - > E getEnum(String tag, Class enumClass); + > @Nullable E getEnum(String tag, Class enumClass); - List getExpressionList(String tag); + @Nullable List getExpressionList(String tag); - List getStringList(String tag); + @Nullable List getStringList(String tag); - List getIntegerList(String tag); + @Nullable List getIntegerList(String tag); - List> getIntegerListList(String tag); + @Nullable List> getIntegerListList(String tag); RelDataType getRowType(String tag); diff --git a/core/src/main/java/org/apache/calcite/rel/RelNode.java b/core/src/main/java/org/apache/calcite/rel/RelNode.java index 284052f35201..42be83d5d935 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelNode.java +++ b/core/src/main/java/org/apache/calcite/rel/RelNode.java @@ -33,6 +33,9 @@ import org.apache.calcite.util.Litmus; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import java.util.List; import java.util.Set; @@ -87,7 +90,8 @@ public interface RelNode extends RelOptNode, Cloneable { * * @return this RelNode's CallingConvention */ - Convention getConvention(); + @Pure + @Nullable Convention getConvention(); /** * Returns the name of the variable which is to be implicitly set at runtime @@ -96,7 +100,7 @@ public interface RelNode extends RelOptNode, Cloneable { * * @return Name of correlating variable, or null */ - String getCorrelVariable(); + @Nullable String getCorrelVariable(); /** * Returns the ith input relational expression. @@ -216,7 +220,7 @@ public interface RelNode extends RelOptNode, Cloneable { * Each node should call {@code super.explain}, then call the * {@link org.apache.calcite.rel.externalize.RelWriterImpl#input(String, RelNode)} * and - * {@link org.apache.calcite.rel.externalize.RelWriterImpl#item(String, Object)} + * {@link RelWriter#item(String, Object)} * methods for each input and attribute. * * @param pw Plan writer @@ -293,7 +297,8 @@ default String explain() { * @return Whether the 2 RelNodes are equivalent or have the same digest. * @see #deepHashCode() */ - boolean deepEquals(Object obj); + @EnsuresNonNullIf(expression = "#1", result = true) + boolean deepEquals(@Nullable Object obj); /** * Compute deep hash code for RelNode digest. @@ -320,7 +325,7 @@ void replaceInput( * @return If this relational expression represents an access to a table, * returns that table, otherwise returns null */ - RelOptTable getTable(); + @Nullable RelOptTable getTable(); /** * Returns the name of this relational expression's class, sans package @@ -352,7 +357,7 @@ void replaceInput( * @throws AssertionError if this relational expression is invalid and * litmus is THROW */ - boolean isValid(Litmus litmus, Context context); + boolean isValid(Litmus litmus, @Nullable Context context); /** * Creates a copy of this relational expression, perhaps changing traits and diff --git a/core/src/main/java/org/apache/calcite/rel/RelVisitor.java b/core/src/main/java/org/apache/calcite/rel/RelVisitor.java index 82704bfb7026..c355eba747c8 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelVisitor.java +++ b/core/src/main/java/org/apache/calcite/rel/RelVisitor.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.rel; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A RelVisitor is a Visitor role in the * {@link org.apache.calcite.util.Glossary#VISITOR_PATTERN visitor pattern} and @@ -25,7 +27,7 @@ public abstract class RelVisitor { //~ Instance fields -------------------------------------------------------- - private RelNode root; + private @Nullable RelNode root; //~ Methods ---------------------------------------------------------------- @@ -40,7 +42,7 @@ public abstract class RelVisitor { public void visit( RelNode node, int ordinal, - RelNode parent) { + @Nullable RelNode parent) { node.childrenAccept(this); } @@ -49,14 +51,14 @@ public void visit( * * @param node The new root node */ - public void replaceRoot(RelNode node) { + public void replaceRoot(@Nullable RelNode node) { this.root = node; } /** * Starts an iteration. */ - public RelNode go(RelNode p) { + public @Nullable RelNode go(RelNode p) { this.root = p; visit(p, 0, null); return root; diff --git a/core/src/main/java/org/apache/calcite/rel/RelWriter.java b/core/src/main/java/org/apache/calcite/rel/RelWriter.java index f6be8698ad60..8caf38c4d46b 100644 --- a/core/src/main/java/org/apache/calcite/rel/RelWriter.java +++ b/core/src/main/java/org/apache/calcite/rel/RelWriter.java @@ -19,6 +19,8 @@ import org.apache.calcite.sql.SqlExplainLevel; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -40,7 +42,7 @@ public interface RelWriter { * @param rel Relational expression * @param valueList List of term-value pairs */ - void explain(RelNode rel, List> valueList); + void explain(RelNode rel, List> valueList); /** Returns detail level at which plan should be generated. */ SqlExplainLevel getDetailLevel(); @@ -61,13 +63,13 @@ default RelWriter input(String term, RelNode input) { * @param term Term for attribute, e.g. "joinType" * @param value Attribute value */ - RelWriter item(String term, Object value); + RelWriter item(String term, @Nullable Object value); /** * Adds an input to the explanation of the current node, if a condition * holds. */ - default RelWriter itemIf(String term, Object value, boolean condition) { + default RelWriter itemIf(String term, @Nullable Object value, boolean condition) { return condition ? item(term, value) : this; } diff --git a/core/src/main/java/org/apache/calcite/rel/convert/Converter.java b/core/src/main/java/org/apache/calcite/rel/convert/Converter.java index 2639f21e1f06..19b1c61bce0e 100644 --- a/core/src/main/java/org/apache/calcite/rel/convert/Converter.java +++ b/core/src/main/java/org/apache/calcite/rel/convert/Converter.java @@ -20,6 +20,8 @@ import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A relational expression implements the interface Converter to * indicate that it converts a physical attribute, or @@ -64,7 +66,7 @@ public interface Converter extends RelNode { * * @return trait which this converter modifies */ - RelTraitDef getTraitDef(); + @Nullable RelTraitDef getTraitDef(); /** * Returns the sole input relational expression. diff --git a/core/src/main/java/org/apache/calcite/rel/convert/ConverterImpl.java b/core/src/main/java/org/apache/calcite/rel/convert/ConverterImpl.java index b542b962087d..d65545b070c8 100644 --- a/core/src/main/java/org/apache/calcite/rel/convert/ConverterImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/convert/ConverterImpl.java @@ -25,6 +25,8 @@ import org.apache.calcite.rel.SingleRel; import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Abstract implementation of {@link Converter}. */ @@ -33,7 +35,7 @@ public abstract class ConverterImpl extends SingleRel //~ Instance fields -------------------------------------------------------- protected RelTraitSet inTraits; - protected final RelTraitDef traitDef; + protected final @Nullable RelTraitDef traitDef; //~ Constructors ----------------------------------------------------------- @@ -47,7 +49,7 @@ public abstract class ConverterImpl extends SingleRel */ protected ConverterImpl( RelOptCluster cluster, - RelTraitDef traitDef, + @Nullable RelTraitDef traitDef, RelTraitSet traits, RelNode child) { super(cluster, traits, child); @@ -75,7 +77,7 @@ protected Error cannotImplement() { return inTraits; } - @Override public RelTraitDef getTraitDef() { + @Override public @Nullable RelTraitDef getTraitDef() { return traitDef; } diff --git a/core/src/main/java/org/apache/calcite/rel/convert/ConverterRule.java b/core/src/main/java/org/apache/calcite/rel/convert/ConverterRule.java index d252c6d6f55d..0f7fdab14a7b 100644 --- a/core/src/main/java/org/apache/calcite/rel/convert/ConverterRule.java +++ b/core/src/main/java/org/apache/calcite/rel/convert/ConverterRule.java @@ -26,11 +26,15 @@ import org.apache.calcite.tools.RelBuilderFactory; import org.apache.calcite.util.ImmutableBeans; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Locale; import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Abstract base class for a rule which converts from one calling convention to * another without changing semantics. @@ -57,7 +61,7 @@ protected ConverterRule(Config config) { // Most sub-classes are concerned with converting one convention to // another, and for them, the "out" field is a convenient short-cut. this.out = outTrait instanceof Convention ? (Convention) outTrait - : null; + : castNonNull(null); } /** @@ -145,7 +149,7 @@ private static String createDescription(String descriptionPrefix, /** Converts a relational expression to the target trait(s) of this rule. * *

Returns null if conversion is not possible. */ - public abstract RelNode convert(RelNode rel); + public abstract @Nullable RelNode convert(RelNode rel); /** * Returns true if this rule can convert any relational expression diff --git a/core/src/main/java/org/apache/calcite/rel/convert/TraitMatchingRule.java b/core/src/main/java/org/apache/calcite/rel/convert/TraitMatchingRule.java index affb18615238..a07045ff9114 100644 --- a/core/src/main/java/org/apache/calcite/rel/convert/TraitMatchingRule.java +++ b/core/src/main/java/org/apache/calcite/rel/convert/TraitMatchingRule.java @@ -26,6 +26,8 @@ import org.apache.calcite.tools.RelBuilderFactory; import org.apache.calcite.util.ImmutableBeans; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * TraitMatchingRule adapts a converter rule, restricting it to fire only when * its input already matches the expected output trait. This can be used with @@ -73,7 +75,7 @@ public TraitMatchingRule(ConverterRule converterRule, //~ Methods ---------------------------------------------------------------- - @Override public Convention getOutConvention() { + @Override public @Nullable Convention getOutConvention() { return config.converterRule().getOutConvention(); } diff --git a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java index f728a523a5fd..9bbf1dc11270 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java @@ -50,6 +50,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.math.IntMath; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -141,13 +143,14 @@ public static void checkIndicator(boolean indicator) { * @param groupSets List of all grouping sets; null for just {@code groupSet} * @param aggCalls Collection of calls to aggregate functions */ + @SuppressWarnings("method.invocation.invalid") protected Aggregate( RelOptCluster cluster, RelTraitSet traitSet, List hints, RelNode input, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) { super(cluster, traitSet, input); this.hints = ImmutableList.copyOf(hints); @@ -204,7 +207,7 @@ public static boolean noIndicator(Aggregate aggregate) { return true; } - private boolean isPredicate(RelNode input, int index) { + private static boolean isPredicate(RelNode input, int index) { final RelDataType type = input.getRowType().getFieldList().get(index).getType(); return type.getSqlTypeName() == SqlTypeName.BOOLEAN @@ -242,7 +245,7 @@ protected Aggregate(RelInput input) { */ public abstract Aggregate copy(RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, List aggCalls); + @Nullable List groupSets, List aggCalls); @Deprecated // to be removed before 2.0 public Aggregate copy(RelTraitSet traitSet, RelNode input, @@ -382,7 +385,7 @@ public ImmutableList getGroupSets() { */ public static RelDataType deriveRowType(RelDataTypeFactory typeFactory, final RelDataType inputRowType, boolean indicator, - ImmutableBitSet groupSet, List groupSets, + ImmutableBitSet groupSet, @Nullable List groupSets, final List aggCalls) { final List groupList = groupSet.asList(); assert groupList.size() == groupSet.cardinality(); @@ -416,7 +419,7 @@ public static RelDataType deriveRowType(RelDataTypeFactory typeFactory, return builder.build(); } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { return super.isValid(litmus, context) && litmus.check(Util.isDistinct(getRowType().getFieldNames()), "distinct field names: {}", getRowType()); @@ -525,6 +528,7 @@ public static boolean isRollup(ImmutableBitSet groupSet, } g = bitSet; } + assert g != null : "groupSet must not be empty"; assert g.isEmpty(); return true; } diff --git a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java index 2eed5d7c4842..c001ad82f5e5 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java +++ b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java @@ -30,6 +30,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -46,7 +48,7 @@ public class AggregateCall { private final boolean approximate; private final boolean ignoreNulls; public final RelDataType type; - public final String name; + public final @Nullable String name; // We considered using ImmutableIntList but we would not save much memory: // since all values are small, ImmutableList uses cached Integer values. @@ -92,7 +94,7 @@ public AggregateCall( */ private AggregateCall(SqlAggFunction aggFunction, boolean distinct, boolean approximate, boolean ignoreNulls, List argList, - int filterArg, RelCollation collation, RelDataType type, String name) { + int filterArg, RelCollation collation, RelDataType type, @Nullable String name) { this.type = Objects.requireNonNull(type); this.name = name; this.aggFunction = Objects.requireNonNull(aggFunction); @@ -113,7 +115,7 @@ private AggregateCall(SqlAggFunction aggFunction, boolean distinct, @Deprecated // to be removed before 2.0 public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, List argList, int groupCount, RelNode input, - RelDataType type, String name) { + @Nullable RelDataType type, @Nullable String name) { return create(aggFunction, distinct, false, false, argList, -1, RelCollations.EMPTY, groupCount, input, type, name); } @@ -121,7 +123,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, @Deprecated // to be removed before 2.0 public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, List argList, int filterArg, int groupCount, - RelNode input, RelDataType type, String name) { + RelNode input, @Nullable RelDataType type, @Nullable String name) { return create(aggFunction, distinct, false, false, argList, filterArg, RelCollations.EMPTY, groupCount, input, type, name); } @@ -130,7 +132,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, boolean approximate, List argList, int filterArg, int groupCount, - RelNode input, RelDataType type, String name) { + RelNode input, @Nullable RelDataType type, @Nullable String name) { return create(aggFunction, distinct, approximate, false, argList, filterArg, RelCollations.EMPTY, groupCount, input, type, name); } @@ -139,7 +141,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, boolean approximate, List argList, int filterArg, RelCollation collation, int groupCount, - RelNode input, RelDataType type, String name) { + RelNode input, @Nullable RelDataType type, @Nullable String name) { return create(aggFunction, distinct, approximate, false, argList, filterArg, collation, groupCount, input, type, name); } @@ -149,7 +151,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, boolean approximate, boolean ignoreNulls, List argList, int filterArg, RelCollation collation, int groupCount, - RelNode input, RelDataType type, String name) { + RelNode input, @Nullable RelDataType type, @Nullable String name) { if (type == null) { final RelDataTypeFactory typeFactory = input.getCluster().getTypeFactory(); @@ -167,7 +169,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, @Deprecated // to be removed before 2.0 public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, List argList, int filterArg, RelDataType type, - String name) { + @Nullable String name) { return create(aggFunction, distinct, false, false, argList, filterArg, RelCollations.EMPTY, type, name); } @@ -175,7 +177,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, @Deprecated // to be removed before 2.0 public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, boolean approximate, List argList, - int filterArg, RelDataType type, String name) { + int filterArg, RelDataType type, @Nullable String name) { return create(aggFunction, distinct, approximate, false, argList, filterArg, RelCollations.EMPTY, type, name); } @@ -183,7 +185,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, @Deprecated // to be removed before 2.0 public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, boolean approximate, List argList, - int filterArg, RelCollation collation, RelDataType type, String name) { + int filterArg, RelCollation collation, RelDataType type, @Nullable String name) { return create(aggFunction, distinct, approximate, false, argList, filterArg, collation, type, name); } @@ -192,7 +194,7 @@ public static AggregateCall create(SqlAggFunction aggFunction, public static AggregateCall create(SqlAggFunction aggFunction, boolean distinct, boolean approximate, boolean ignoreNulls, List argList, int filterArg, RelCollation collation, - RelDataType type, String name) { + RelDataType type, @Nullable String name) { final boolean distinct2 = distinct && (aggFunction.getDistinctOptionality() != Optionality.IGNORED); return new AggregateCall(aggFunction, distinct2, approximate, ignoreNulls, @@ -272,7 +274,7 @@ public final RelDataType getType() { * * @return name */ - public String getName() { + public @Nullable String getName() { return name; } @@ -281,7 +283,7 @@ public String getName() { * * @param name New name (may be null) */ - public AggregateCall rename(String name) { + public AggregateCall rename(@Nullable String name) { if (Objects.equals(this.name, name)) { return this; } @@ -326,7 +328,7 @@ public boolean hasFilter() { return filterArg >= 0; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (!(o instanceof AggregateCall)) { return false; } diff --git a/core/src/main/java/org/apache/calcite/rel/core/Calc.java b/core/src/main/java/org/apache/calcite/rel/core/Calc.java index a85853ab3074..5ab502a560d4 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Calc.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Calc.java @@ -43,6 +43,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -66,6 +68,7 @@ public abstract class Calc extends SingleRel implements Hintable { * @param child Input relation * @param program Calc program */ + @SuppressWarnings("method.invocation.invalid") protected Calc( RelOptCluster cluster, RelTraitSet traits, @@ -136,7 +139,7 @@ public final boolean containsOver() { return RexOver.containsOver(program); } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { if (!RelOptUtil.equal( "program's input type", program.getInputRowType(), @@ -204,7 +207,7 @@ public RexProgram getProgram() { RexUtil.createStructType( rexBuilder.getTypeFactory(), projects, - this.rowType.getFieldNames(), + getRowType().getFieldNames(), null); final RexProgram newProgram = RexProgramBuilder.create( diff --git a/core/src/main/java/org/apache/calcite/rel/core/Collect.java b/core/src/main/java/org/apache/calcite/rel/core/Collect.java index d50b4d102342..d44613b13df4 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Collect.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Collect.java @@ -29,6 +29,8 @@ import java.util.List; +import static java.util.Objects.requireNonNull; + /** * A relational expression that collapses multiple rows into one. * @@ -69,7 +71,7 @@ public Collect( */ public Collect(RelInput input) { this(input.getCluster(), input.getTraitSet(), input.getInput(), - input.getString("field")); + requireNonNull(input.getString("field"), "field")); } //~ Methods ---------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java index f8b76c9945b3..502b9deba4aa 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Correlate.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Correlate.java @@ -34,10 +34,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; -import java.util.Objects; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * A relational operator that performs nested-loop joins. * @@ -94,9 +97,9 @@ protected Correlate( JoinRelType joinType) { super(cluster, traitSet, left, right); assert !joinType.generatesNullsOnLeft() : "Correlate has invalid join type " + joinType; - this.joinType = Objects.requireNonNull(joinType); - this.correlationId = Objects.requireNonNull(correlationId); - this.requiredColumns = Objects.requireNonNull(requiredColumns); + this.joinType = requireNonNull(joinType); + this.correlationId = requireNonNull(correlationId); + this.requiredColumns = requireNonNull(requiredColumns); } /** @@ -108,14 +111,15 @@ protected Correlate(RelInput input) { this( input.getCluster(), input.getTraitSet(), input.getInputs().get(0), input.getInputs().get(1), - new CorrelationId((Integer) input.get("correlation")), + new CorrelationId( + requireNonNull((Integer) input.get("correlation"), "correlation")), input.getBitSet("requiredColumns"), - input.getEnum("joinType", JoinRelType.class)); + requireNonNull(input.getEnum("joinType", JoinRelType.class), "joinType")); } //~ Methods ---------------------------------------------------------------- - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { return super.isValid(litmus, context) && RelOptUtil.notContainsCorrelation(left, correlationId, litmus); } @@ -210,9 +214,15 @@ public ImmutableBitSet getRequiredColumns() { } Double restartCount = mq.getRowCount(getLeft()); + if (restartCount == null) { + return planner.getCostFactory().makeInfiniteCost(); + } // RelMetadataQuery.getCumulativeCost(getRight()); does not work for // RelSubset, so we ask planner to cost-estimate right relation RelOptCost rightCost = planner.getCost(getRight(), mq); + if (rightCost == null) { + return planner.getCostFactory().makeInfiniteCost(); + } RelOptCost rescanCost = rightCost.multiplyBy(Math.max(1.0, restartCount - 1)); diff --git a/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java b/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java index f34f216052e0..86d9e3ec0f1c 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java +++ b/core/src/main/java/org/apache/calcite/rel/core/CorrelationId.java @@ -18,6 +18,8 @@ import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Set; /** @@ -93,7 +95,7 @@ public String getName() { return id; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof CorrelationId && this.id == ((CorrelationId) obj).id; diff --git a/core/src/main/java/org/apache/calcite/rel/core/Filter.java b/core/src/main/java/org/apache/calcite/rel/core/Filter.java index ef4d92761984..c3653353e3af 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Filter.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Filter.java @@ -36,10 +36,14 @@ import org.apache.calcite.util.Litmus; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.List; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * Relational expression that iterates over its input * and returns elements for which condition evaluates to @@ -66,15 +70,15 @@ public abstract class Filter extends SingleRel { * @param condition boolean expression which determines whether a row is * allowed to pass */ + @SuppressWarnings("method.invocation.invalid") protected Filter( RelOptCluster cluster, RelTraitSet traits, RelNode child, RexNode condition) { super(cluster, traits, child); - assert condition != null; - assert RexUtil.isFlat(condition) : condition; - this.condition = condition; + this.condition = requireNonNull(condition, "condition"); + assert RexUtil.isFlat(condition) : "RexUtil.isFlat should be true for condition " + condition; // Too expensive for everyday use: assert !CalciteSystemProperty.DEBUG.value() || isValid(Litmus.THROW, null); } @@ -84,7 +88,7 @@ protected Filter( */ protected Filter(RelInput input) { this(input.getCluster(), input.getTraitSet(), input.getInput(), - input.getExpression("condition")); + requireNonNull(input.getExpression("condition"), "condition")); } //~ Methods ---------------------------------------------------------------- @@ -114,7 +118,7 @@ public final boolean containsOver() { return RexOver.containsOver(condition); } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { if (RexUtil.isNullabilityCast(getCluster().getTypeFactory(), condition)) { return litmus.fail("Cast for just nullability not allowed"); } @@ -157,7 +161,8 @@ public static double estimateFilteredRows(RelNode child, RexNode condition) { } @API(since = "1.24", status = API.Status.INTERNAL) - protected boolean deepEquals0(Object obj) { + @EnsuresNonNullIf(expression = "#1", result = true) + protected boolean deepEquals0(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rel/core/Intersect.java b/core/src/main/java/org/apache/calcite/rel/core/Intersect.java index c111e63d99ad..c159353dad2b 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Intersect.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Intersect.java @@ -55,7 +55,12 @@ protected Intersect(RelInput input) { // REVIEW jvs 30-May-2005: I just pulled this out of a hat. double dRows = Double.MAX_VALUE; for (RelNode input : inputs) { - dRows = Math.min(dRows, mq.getRowCount(input)); + Double rowCount = mq.getRowCount(input); + if (rowCount == null) { + // Assume this input does not reduce row count + continue; + } + dRows = Math.min(dRows, rowCount); } dRows *= 0.25; return dRows; diff --git a/core/src/main/java/org/apache/calcite/rel/core/Join.java b/core/src/main/java/org/apache/calcite/rel/core/Join.java index be2d47bfecd5..f0881c7e1efc 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Join.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Join.java @@ -42,6 +42,8 @@ import com.google.common.collect.ImmutableSet; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collections; import java.util.List; @@ -144,7 +146,7 @@ public JoinRelType getJoinType() { return joinType; } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { if (!super.isValid(litmus, context)) { return false; } @@ -220,7 +222,8 @@ public static double estimateJoinedRows( } @API(since = "1.24", status = API.Status.INTERNAL) - protected boolean deepEquals0(Object obj) { + @EnsuresNonNullIf(expression = "#1", result = true) + protected boolean deepEquals0(@Nullable Object obj) { if (this == obj) { return true; } @@ -288,7 +291,7 @@ public static RelDataType deriveJoinRowType( RelDataType rightType, JoinRelType joinType, RelDataTypeFactory typeFactory, - List fieldNameList, + @Nullable List fieldNameList, List systemFieldList) { return SqlValidatorUtil.deriveJoinRowType(leftType, rightType, joinType, typeFactory, fieldNameList, systemFieldList); diff --git a/core/src/main/java/org/apache/calcite/rel/core/Match.java b/core/src/main/java/org/apache/calcite/rel/core/Match.java index fa7564f024b4..4a83c0f2f915 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Match.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Match.java @@ -40,6 +40,8 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashSet; import java.util.List; import java.util.Map; @@ -70,7 +72,7 @@ public abstract class Match extends SingleRel { protected final ImmutableMap> subsets; protected final ImmutableBitSet partitionKeys; protected final RelCollation orderKeys; - protected final RexNode interval; + protected final @Nullable RexNode interval; //~ Constructors ----------------------------------------------- @@ -99,7 +101,7 @@ protected Match(RelOptCluster cluster, RelTraitSet traitSet, RelNode input, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval) { + @Nullable RexNode interval) { super(cluster, traitSet, input); this.rowType = Objects.requireNonNull(rowType); this.pattern = Objects.requireNonNull(pattern); @@ -187,7 +189,7 @@ public RelCollation getOrderKeys() { return orderKeys; } - public RexNode getInterval() { + public @Nullable RexNode getInterval() { return interval; } @@ -334,7 +336,7 @@ public static final class RexMRAggCall extends RexCall return toString().compareTo(o.toString()); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof RexMRAggCall && toString().equals(obj.toString()); diff --git a/core/src/main/java/org/apache/calcite/rel/core/Project.java b/core/src/main/java/org/apache/calcite/rel/core/Project.java index 053a36721e6e..bf882079444a 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Project.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Project.java @@ -47,12 +47,16 @@ import com.google.common.collect.ImmutableList; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Relational expression that computes a set of * 'select expressions' from its input relational expression. @@ -78,6 +82,7 @@ public abstract class Project extends SingleRel implements Hintable { * @param projects List of expressions for the input columns * @param rowType Output row type */ + @SuppressWarnings("method.invocation.invalid") protected Project( RelOptCluster cluster, RelTraitSet traits, @@ -114,7 +119,7 @@ protected Project(RelInput input) { input.getTraitSet(), ImmutableList.of(), input.getInput(), - input.getExpressionList("exprs"), + requireNonNull(input.getExpressionList("exprs"), "exprs"), input.getRowType("exprs", "fields")); } @@ -122,7 +127,7 @@ protected Project(RelInput input) { @Override public final RelNode copy(RelTraitSet traitSet, List inputs) { - return copy(traitSet, sole(inputs), exps, rowType); + return copy(traitSet, sole(inputs), exps, getRowType()); } /** @@ -162,7 +167,7 @@ public boolean isBoxed() { RexUtil.createStructType( getInput().getCluster().getTypeFactory(), exps, - this.rowType.getFieldNames(), + getRowType().getFieldNames(), null); return copy(traitSet, getInput(), exps, rowType); } @@ -200,7 +205,7 @@ public final boolean containsOver() { return RexOver.containsOver(getProjects(), null); } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { if (!super.isValid(litmus, context)) { return litmus.fail(null); } @@ -217,7 +222,7 @@ public final boolean containsOver() { checker.getFailureCount(), exp); } } - if (!Util.isDistinct(rowType.getFieldNames())) { + if (!Util.isDistinct(getRowType().getFieldNames())) { return litmus.fail("field names not distinct: {}", rowType); } //CHECKSTYLE: IGNORE 1 @@ -277,10 +282,10 @@ private static int countTrivial(List refs) { } if (pw.nest()) { - pw.item("fields", rowType.getFieldNames()); + pw.item("fields", getRowType().getFieldNames()); pw.item("exprs", exps); } else { - for (Ord field : Ord.zip(rowType.getFieldList())) { + for (Ord field : Ord.zip(getRowType().getFieldList())) { String fieldName = field.e.getName(); if (fieldName == null) { fieldName = "field#" + field.i; @@ -293,7 +298,8 @@ private static int countTrivial(List refs) { } @API(since = "1.24", status = API.Status.INTERNAL) - protected boolean deepEquals0(Object obj) { + @EnsuresNonNullIf(expression = "#1", result = true) + protected boolean deepEquals0(@Nullable Object obj) { if (this == obj) { return true; } @@ -318,7 +324,7 @@ protected int deepHashCode0() { * * @return Mapping, or null if this projection is not a mapping */ - public Mappings.TargetMapping getMapping() { + public Mappings.@Nullable TargetMapping getMapping() { return getMapping(getInput().getRowType().getFieldCount(), exps); } @@ -336,7 +342,7 @@ public Mappings.TargetMapping getMapping() { * @return Mapping of a set of project expressions, or null if projection is * not a mapping */ - public static Mappings.TargetMapping getMapping(int inputFieldCount, + public static Mappings.@Nullable TargetMapping getMapping(int inputFieldCount, List projects) { if (inputFieldCount < projects.size()) { return null; // surjection is not possible @@ -391,7 +397,7 @@ public static Mappings.TargetMapping getPartialMapping(int inputFieldCount, * @return Permutation, if this projection is merely a permutation of its * input fields; otherwise null */ - public Permutation getPermutation() { + public @Nullable Permutation getPermutation() { return getPermutation(getInput().getRowType().getFieldCount(), exps); } @@ -399,7 +405,7 @@ public Permutation getPermutation() { * Returns a permutation, if this projection is merely a permutation of its * input fields; otherwise null. */ - public static Permutation getPermutation(int inputFieldCount, + public static @Nullable Permutation getPermutation(int inputFieldCount, List projects) { final int fieldCount = projects.size(); if (fieldCount != inputFieldCount) { diff --git a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java index e8098889ce4e..5a23ff22b62b 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java +++ b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java @@ -62,13 +62,15 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.SortedSet; -import javax.annotation.Nonnull; + +import static java.util.Objects.requireNonNull; /** * Contains factory interface and default implementation for creating various @@ -164,7 +166,7 @@ public interface ProjectFactory { * @return a project */ RelNode createProject(RelNode input, List hints, - List childExprs, List fieldNames); + List childExprs, @Nullable List fieldNames); } /** @@ -173,7 +175,7 @@ RelNode createProject(RelNode input, List hints, */ private static class ProjectFactoryImpl implements ProjectFactory { @Override public RelNode createProject(RelNode input, List hints, - List childExprs, List fieldNames) { + List childExprs, @Nullable List fieldNames) { return LogicalProject.create(input, hints, childExprs, fieldNames); } } @@ -184,12 +186,12 @@ private static class ProjectFactoryImpl implements ProjectFactory { */ public interface SortFactory { /** Creates a sort. */ - RelNode createSort(RelNode input, RelCollation collation, RexNode offset, - RexNode fetch); + RelNode createSort(RelNode input, RelCollation collation, @Nullable RexNode offset, + @Nullable RexNode fetch); @Deprecated // to be removed before 2.0 default RelNode createSort(RelTraitSet traitSet, RelNode input, - RelCollation collation, RexNode offset, RexNode fetch) { + RelCollation collation, @Nullable RexNode offset, @Nullable RexNode fetch) { return createSort(input, collation, offset, fetch); } } @@ -200,7 +202,7 @@ default RelNode createSort(RelTraitSet traitSet, RelNode input, */ private static class SortFactoryImpl implements SortFactory { @Override public RelNode createSort(RelNode input, RelCollation collation, - RexNode offset, RexNode fetch) { + @Nullable RexNode offset, @Nullable RexNode fetch) { return LogicalSort.create(input, collation, offset, fetch); } } @@ -488,8 +490,8 @@ private static class TableScanFactoryImpl implements TableScanFactory { public interface TableFunctionScanFactory { /** Creates a {@link TableFunctionScan}. */ RelNode createTableFunctionScan(RelOptCluster cluster, - List inputs, RexCall call, Type elementType, - Set columnMappings); + List inputs, RexCall call, @Nullable Type elementType, + @Nullable Set columnMappings); } /** @@ -500,8 +502,8 @@ RelNode createTableFunctionScan(RelOptCluster cluster, private static class TableFunctionScanFactoryImpl implements TableFunctionScanFactory { @Override public RelNode createTableFunctionScan(RelOptCluster cluster, - List inputs, RexCall call, Type elementType, - Set columnMappings) { + List inputs, RexCall call, @Nullable Type elementType, + @Nullable Set columnMappings) { final RelDataType rowType; // To deduce the return type: // 1. if the operator implements SqlTableFunction, @@ -521,7 +523,7 @@ private static class TableFunctionScanFactoryImpl } return LogicalTableFunctionScan.create(cluster, inputs, call, - elementType, rowType, columnMappings); + elementType, requireNonNull(rowType, "rowType"), columnMappings); } } @@ -557,7 +559,7 @@ RelNode createMatch(RelNode input, RexNode pattern, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval); + @Nullable RexNode interval); } /** @@ -570,7 +572,7 @@ private static class MatchFactoryImpl implements MatchFactory { Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval) { + @Nullable RexNode interval) { return LogicalMatch.create(input, rowType, pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); @@ -656,26 +658,26 @@ private Struct(FilterFactory filterFactory, MatchFactory matchFactory, SpoolFactory spoolFactory, RepeatUnionFactory repeatUnionFactory) { - this.filterFactory = Objects.requireNonNull(filterFactory); - this.projectFactory = Objects.requireNonNull(projectFactory); - this.aggregateFactory = Objects.requireNonNull(aggregateFactory); - this.sortFactory = Objects.requireNonNull(sortFactory); - this.exchangeFactory = Objects.requireNonNull(exchangeFactory); - this.sortExchangeFactory = Objects.requireNonNull(sortExchangeFactory); - this.setOpFactory = Objects.requireNonNull(setOpFactory); - this.joinFactory = Objects.requireNonNull(joinFactory); - this.correlateFactory = Objects.requireNonNull(correlateFactory); - this.valuesFactory = Objects.requireNonNull(valuesFactory); - this.scanFactory = Objects.requireNonNull(scanFactory); + this.filterFactory = requireNonNull(filterFactory); + this.projectFactory = requireNonNull(projectFactory); + this.aggregateFactory = requireNonNull(aggregateFactory); + this.sortFactory = requireNonNull(sortFactory); + this.exchangeFactory = requireNonNull(exchangeFactory); + this.sortExchangeFactory = requireNonNull(sortExchangeFactory); + this.setOpFactory = requireNonNull(setOpFactory); + this.joinFactory = requireNonNull(joinFactory); + this.correlateFactory = requireNonNull(correlateFactory); + this.valuesFactory = requireNonNull(valuesFactory); + this.scanFactory = requireNonNull(scanFactory); this.tableFunctionScanFactory = - Objects.requireNonNull(tableFunctionScanFactory); - this.snapshotFactory = Objects.requireNonNull(snapshotFactory); - this.matchFactory = Objects.requireNonNull(matchFactory); - this.spoolFactory = Objects.requireNonNull(spoolFactory); - this.repeatUnionFactory = Objects.requireNonNull(repeatUnionFactory); + requireNonNull(tableFunctionScanFactory); + this.snapshotFactory = requireNonNull(snapshotFactory); + this.matchFactory = requireNonNull(matchFactory); + this.spoolFactory = requireNonNull(spoolFactory); + this.repeatUnionFactory = requireNonNull(repeatUnionFactory); } - public static @Nonnull Struct fromContext(Context context) { + public static Struct fromContext(Context context) { Struct struct = context.unwrap(Struct.class); if (struct != null) { return struct; diff --git a/core/src/main/java/org/apache/calcite/rel/core/Sample.java b/core/src/main/java/org/apache/calcite/rel/core/Sample.java index bab6e2c805ca..3b5bff1c069a 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Sample.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Sample.java @@ -62,8 +62,8 @@ private static RelOptSamplingParameters getSamplingParameters( Object repeatableSeed = input.get("repeatableSeed"); boolean repeatable = repeatableSeed instanceof Number; return new RelOptSamplingParameters( - mode.equals("bernoulli"), percentage, repeatable, - repeatable ? ((Number) repeatableSeed).intValue() : 0); + "bernoulli".equals(mode), percentage, repeatable, + repeatable && repeatableSeed != null ? ((Number) repeatableSeed).intValue() : 0); } @Override public RelNode copy(RelTraitSet traitSet, List inputs) { diff --git a/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java b/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java index ae5c795ab896..7e48ad3f0cfb 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Snapshot.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Litmus; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -58,6 +60,7 @@ public abstract class Snapshot extends SingleRel { * @param period Timestamp expression which as the table was at the given * time in the past */ + @SuppressWarnings("method.invocation.invalid") protected Snapshot(RelOptCluster cluster, RelTraitSet traitSet, RelNode input, RexNode period) { super(cluster, traitSet, input); @@ -91,7 +94,7 @@ public RexNode getPeriod() { return period; } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { RelDataType dataType = period.getType(); if (dataType.getSqlTypeName() != SqlTypeName.TIMESTAMP) { return litmus.fail("The system time period specification expects Timestamp type but is '" diff --git a/core/src/main/java/org/apache/calcite/rel/core/Sort.java b/core/src/main/java/org/apache/calcite/rel/core/Sort.java index 55b3fe41f53f..b286951605e5 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Sort.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Sort.java @@ -33,6 +33,8 @@ import org.apache.calcite.rex.RexShuttle; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -44,8 +46,8 @@ public abstract class Sort extends SingleRel { //~ Instance fields -------------------------------------------------------- public final RelCollation collation; - public final RexNode offset; - public final RexNode fetch; + public final @Nullable RexNode offset; + public final @Nullable RexNode fetch; //~ Constructors ----------------------------------------------------------- @@ -81,8 +83,8 @@ protected Sort( RelTraitSet traits, RelNode child, RelCollation collation, - RexNode offset, - RexNode fetch) { + @Nullable RexNode offset, + @Nullable RexNode fetch) { super(cluster, traits, child); this.collation = collation; this.offset = offset; @@ -118,7 +120,7 @@ public final Sort copy(RelTraitSet traitSet, RelNode newInput, } public abstract Sort copy(RelTraitSet traitSet, RelNode newInput, - RelCollation newCollation, RexNode offset, RexNode fetch); + RelCollation newCollation, @Nullable RexNode offset, @Nullable RexNode fetch); @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) { diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java b/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java index b33ca3d790ac..987233b36efc 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java +++ b/core/src/main/java/org/apache/calcite/rel/core/TableFunctionScan.java @@ -32,11 +32,15 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Relational expression that calls a table-valued function. * @@ -51,11 +55,11 @@ public abstract class TableFunctionScan extends AbstractRelNode { private final RexNode rexCall; - private final Type elementType; + private final @Nullable Type elementType; private ImmutableList inputs; - protected final ImmutableSet columnMappings; + protected final @Nullable ImmutableSet columnMappings; //~ Constructors ----------------------------------------------------------- @@ -76,9 +80,9 @@ protected TableFunctionScan( RelTraitSet traitSet, List inputs, RexNode rexCall, - Type elementType, + @Nullable Type elementType, RelDataType rowType, - Set columnMappings) { + @Nullable Set columnMappings) { super(cluster, traitSet); this.rexCall = rexCall; this.elementType = elementType; @@ -94,7 +98,8 @@ protected TableFunctionScan( protected TableFunctionScan(RelInput input) { this( input.getCluster(), input.getTraitSet(), input.getInputs(), - input.getExpression("invocation"), (Type) input.get("elementType"), + requireNonNull(input.getExpression("invocation"), "invocation"), + (Type) input.get("elementType"), input.getRowType("rowType"), ImmutableSet.of()); } @@ -103,7 +108,7 @@ protected TableFunctionScan(RelInput input) { @Override public final TableFunctionScan copy(RelTraitSet traitSet, List inputs) { - return copy(traitSet, inputs, rexCall, elementType, rowType, + return copy(traitSet, inputs, rexCall, elementType, getRowType(), columnMappings); } @@ -125,9 +130,9 @@ public abstract TableFunctionScan copy( RelTraitSet traitSet, List inputs, RexNode rexCall, - Type elementType, + @Nullable Type elementType, RelDataType rowType, - Set columnMappings); + @Nullable Set columnMappings); @Override public List getInputs() { return inputs; @@ -138,7 +143,7 @@ public abstract TableFunctionScan copy( if (rexCall == this.rexCall) { return this; } - return copy(traitSet, inputs, rexCall, elementType, rowType, + return copy(traitSet, inputs, rexCall, elementType, getRowType(), columnMappings); } @@ -201,7 +206,7 @@ public RexNode getCall() { * @return set of mappings known for this table function, or null if unknown * (not the same as empty!) */ - public Set getColumnMappings() { + public @Nullable Set getColumnMappings() { return columnMappings; } @@ -210,7 +215,7 @@ public Set getColumnMappings() { * * @return element type of the collection that will implement this table */ - public Type getElementType() { + public @Nullable Type getElementType() { return elementType; } } diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java index 8a5744675c39..6bfe699b0955 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java +++ b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java @@ -19,6 +19,7 @@ import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptPlanner; +import org.apache.calcite.plan.RelOptSchema; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.RelTraitSet; @@ -37,8 +38,12 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; -import java.util.Objects; + +import static java.util.Objects.requireNonNull; /** * Relational expression that modifies a table. @@ -75,9 +80,9 @@ public enum Operation { */ protected final RelOptTable table; private final Operation operation; - private final List updateColumnList; - private final List sourceExpressionList; - private RelDataType inputRowType; + private final @Nullable List updateColumnList; + private final @Nullable List sourceExpressionList; + private @MonotonicNonNull RelDataType inputRowType; private final boolean flattened; //~ Constructors ----------------------------------------------------------- @@ -109,8 +114,8 @@ protected TableModify( Prepare.CatalogReader catalogReader, RelNode input, Operation operation, - List updateColumnList, - List sourceExpressionList, + @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { super(cluster, traitSet, input); this.table = table; @@ -119,20 +124,21 @@ protected TableModify( this.updateColumnList = updateColumnList; this.sourceExpressionList = sourceExpressionList; if (operation == Operation.UPDATE) { - Objects.requireNonNull(updateColumnList); - Objects.requireNonNull(sourceExpressionList); + requireNonNull(updateColumnList); + requireNonNull(sourceExpressionList); Preconditions.checkArgument(sourceExpressionList.size() == updateColumnList.size()); } else { if (operation == Operation.MERGE) { - Objects.requireNonNull(updateColumnList); + requireNonNull(updateColumnList); } else { Preconditions.checkArgument(updateColumnList == null); } Preconditions.checkArgument(sourceExpressionList == null); } - if (table.getRelOptSchema() != null) { - cluster.getPlanner().registerSchema(table.getRelOptSchema()); + RelOptSchema relOptSchema = table.getRelOptSchema(); + if (relOptSchema != null) { + cluster.getPlanner().registerSchema(relOptSchema); } this.flattened = flattened; } @@ -144,9 +150,11 @@ protected TableModify(RelInput input) { this(input.getCluster(), input.getTraitSet(), input.getTable("table"), - (Prepare.CatalogReader) input.getTable("table").getRelOptSchema(), + (Prepare.CatalogReader) requireNonNull( + input.getTable("table").getRelOptSchema(), + "relOptSchema"), input.getInput(), - input.getEnum("operation", Operation.class), + requireNonNull(input.getEnum("operation", Operation.class), "operation"), input.getStringList("updateColumnList"), input.getExpressionList("sourceExpressionList"), input.getBoolean("flattened", false)); @@ -162,11 +170,11 @@ public Prepare.CatalogReader getCatalogReader() { return table; } - public List getUpdateColumnList() { + public @Nullable List getUpdateColumnList() { return updateColumnList; } - public List getSourceExpressionList() { + public @Nullable List getSourceExpressionList() { return sourceExpressionList; } @@ -210,12 +218,14 @@ public boolean isMerge() { final RelDataType rowType = table.getRowType(); switch (operation) { case UPDATE: + assert updateColumnList != null : "updateColumnList must not be null for " + operation; inputRowType = typeFactory.createJoinType(rowType, getCatalogReader().createTypeFromProjection(rowType, updateColumnList)); break; case MERGE: + assert updateColumnList != null : "updateColumnList must not be null for " + operation; inputRowType = typeFactory.createJoinType( typeFactory.createJoinType(rowType, rowType), diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java index 7f6dd3599bc2..395a1358030d 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java +++ b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java @@ -19,6 +19,7 @@ import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptPlanner; +import org.apache.calcite.plan.RelOptSchema; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.AbstractRelNode; @@ -66,8 +67,9 @@ protected TableScan(RelOptCluster cluster, RelTraitSet traitSet, List hints, RelOptTable table) { super(cluster, traitSet); this.table = table; - if (table.getRelOptSchema() != null) { - cluster.getPlanner().registerSchema(table.getRelOptSchema()); + RelOptSchema relOptSchema = table.getRelOptSchema(); + if (relOptSchema != null) { + cluster.getPlanner().registerSchema(relOptSchema); } this.hints = ImmutableList.copyOf(hints); } diff --git a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java index cbf5b6a89e3a..6a4c15e0a869 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java @@ -69,6 +69,7 @@ public Uncollect(RelOptCluster cluster, RelTraitSet traitSet, /** Creates an Uncollect. * *

Use {@link #create} unless you know what you're doing. */ + @SuppressWarnings("method.invocation.invalid") public Uncollect(RelOptCluster cluster, RelTraitSet traitSet, RelNode input, boolean withOrdinality, List itemAliases) { super(cluster, traitSet, input); @@ -162,8 +163,9 @@ public static RelDataType deriveUncollectRowType(RelNode rel, for (int i = 0; i < fields.size(); i++) { RelDataTypeField field = fields.get(i); if (field.getType() instanceof MapSqlType) { - builder.add(SqlUnnestOperator.MAP_KEY_COLUMN_NAME, field.getType().getKeyType()); - builder.add(SqlUnnestOperator.MAP_VALUE_COLUMN_NAME, field.getType().getValueType()); + MapSqlType mapType = (MapSqlType) field.getType(); + builder.add(SqlUnnestOperator.MAP_KEY_COLUMN_NAME, mapType.getKeyType()); + builder.add(SqlUnnestOperator.MAP_VALUE_COLUMN_NAME, mapType.getValueType()); } else { RelDataType ret = field.getType().getComponentType(); assert null != ret; diff --git a/core/src/main/java/org/apache/calcite/rel/core/Values.java b/core/src/main/java/org/apache/calcite/rel/core/Values.java index 16b369ff0da5..f72f9be7feb1 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Values.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Values.java @@ -76,6 +76,7 @@ public abstract class Values extends AbstractRelNode { * list contains tuples; each inner list is one tuple; all * tuples must be of same length, conforming to rowType */ + @SuppressWarnings("method.invocation.invalid") protected Values( RelOptCluster cluster, RelDataType rowType, @@ -132,6 +133,7 @@ public ImmutableList> getTuples() { /** Returns true if all tuples match rowType; otherwise, assert on * mismatch. */ private boolean assertRowType() { + RelDataType rowType = getRowType(); for (List tuple : tuples) { assert tuple.size() == rowType.getFieldCount(); for (Pair pair @@ -152,6 +154,7 @@ private boolean assertRowType() { } @Override protected RelDataType deriveRowType() { + assert rowType != null : "rowType must not be null for " + this; return rowType; } @@ -175,6 +178,7 @@ private boolean assertRowType() { // A little adapter just to get the tuples to come out // with curly brackets instead of square brackets. Plus // more whitespace for readability. + RelDataType rowType = getRowType(); RelWriter relWriter = super.explainTerms(pw) // For rel digest, include the row type since a rendered // literal may leave the type ambiguous (e.g. "null"). diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java b/core/src/main/java/org/apache/calcite/rel/core/Window.java index f170b6b66d8d..460e6cdb0f06 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Window.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java @@ -45,6 +45,10 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; + import java.util.AbstractList; import java.util.List; import java.util.Objects; @@ -86,7 +90,7 @@ protected Window(RelOptCluster cluster, RelTraitSet traitSet, RelNode input, this.groups = ImmutableList.copyOf(groups); } - @Override public boolean isValid(Litmus litmus, Context context) { + @Override public boolean isValid(Litmus litmus, @Nullable Context context) { // In the window specifications, an aggregate call such as // 'SUM(RexInputRef #10)' refers to expression #10 of inputProgram. // (Not its projections.) @@ -243,7 +247,10 @@ public Group( return digest; } - private String computeString() { + @RequiresNonNull({"keys", "orderKeys", "lowerBound", "upperBound", "aggCalls"}) + private String computeString( + @UnderInitialization Group this + ) { final StringBuilder buf = new StringBuilder("window("); final int i = buf.length(); if (!keys.isEmpty()) { @@ -287,7 +294,7 @@ private String computeString() { return buf.toString(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof Group && this.digest.equals(((Group) obj).digest); @@ -394,7 +401,7 @@ public RexWinAggCall( this.ignoreNulls = ignoreNulls; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelDotWriter.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelDotWriter.java index 9e3acc883b15..65b61bacac48 100644 --- a/core/src/main/java/org/apache/calcite/rel/externalize/RelDotWriter.java +++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelDotWriter.java @@ -29,6 +29,8 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; @@ -36,7 +38,6 @@ import java.util.List; import java.util.Map; import java.util.function.Predicate; -import java.util.stream.Collectors; /** * Utility to dump a rel node plan in dot format. @@ -74,7 +75,7 @@ public RelDotWriter( //~ Methods ---------------------------------------------------------------- @Override protected void explain_(RelNode rel, - List> values) { + List> values) { // get inputs List inputs = getInputs(rel); outArcTable.put(rel, inputs); @@ -92,7 +93,7 @@ public RelDotWriter( protected String getRelNodeLabel( RelNode rel, - List> values) { + List> values) { List labels = new ArrayList<>(); StringBuilder sb = new StringBuilder(); @@ -105,7 +106,7 @@ protected String getRelNodeLabel( sb.setLength(0); if (detailLevel != SqlExplainLevel.NO_ATTRIBUTES) { - for (Pair value : values) { + for (Pair value : values) { if (value.right instanceof RelNode) { continue; } @@ -162,8 +163,8 @@ protected String getRelNodeLabel( return "\"" + String.join("\\n", newlabels) + "\""; } - private List getInputs(RelNode parent) { - return parent.getInputs().stream().map(child -> { + private List<@Nullable RelNode> getInputs(RelNode parent) { + return Util.transform(parent.getInputs(), child -> { if (child instanceof HepRelVertex) { return ((HepRelVertex) child).getCurrentRel(); } else if (child instanceof RelSubset) { @@ -172,10 +173,10 @@ private List getInputs(RelNode parent) { } else { return child; } - }).collect(Collectors.toList()); + }); } - private void explainInputs(List inputs) { + private void explainInputs(List inputs) { for (RelNode input : inputs) { if (input == null || nodeLabels.containsKey(input)) { continue; diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java index d386f13ce0e2..b16fc59044e3 100644 --- a/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java +++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java @@ -33,6 +33,8 @@ import com.google.common.collect.ImmutableMap; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** Registry of {@link Enum} classes that can be serialized to JSON. * *

Suppose you want to serialize the value @@ -76,7 +78,7 @@ private RelEnumTypes() {} private static void register(ImmutableMap.Builder> builder, Class aClass) { - for (Enum enumConstant : aClass.getEnumConstants()) { + for (Enum enumConstant : castNonNull(aClass.getEnumConstants())) { builder.put(enumConstant.name(), enumConstant); } } diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java index dce83f3a290d..49a1fc310aa8 100644 --- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java +++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java @@ -64,6 +64,9 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; @@ -76,13 +79,15 @@ import static org.apache.calcite.rel.RelDistributions.EMPTY; +import static java.util.Objects.requireNonNull; + /** * Utilities for converting {@link org.apache.calcite.rel.RelNode} * into JSON format. */ public class RelJson { private final Map constructorMap = new HashMap<>(); - private final JsonBuilder jsonBuilder; + private final @Nullable JsonBuilder jsonBuilder; public static final List PACKAGES = ImmutableList.of( @@ -92,12 +97,28 @@ public class RelJson { "org.apache.calcite.adapter.jdbc.", "org.apache.calcite.adapter.jdbc.JdbcRules$"); - public RelJson(JsonBuilder jsonBuilder) { + public RelJson(@Nullable JsonBuilder jsonBuilder) { this.jsonBuilder = jsonBuilder; } + private JsonBuilder jsonBuilder() { + return requireNonNull(jsonBuilder, "jsonBuilder"); + } + + @SuppressWarnings("unchecked") + private T get(Map map, String key) { + return (T) requireNonNull(map.get(key), () -> "entry for key " + key); + } + + private > T enumVal(Class clazz, Map map, String key) { + String textValue = get(map, key); + return requireNonNull( + Util.enumVal(clazz, textValue), + () -> "unable to find enum value " + textValue + " in class " + clazz); + } + public RelNode create(Map map) { - String type = (String) map.get("type"); + String type = get(map, "type"); Constructor constructor = getConstructor(type); try { return (RelNode) constructor.newInstance(map); @@ -164,7 +185,7 @@ public String classToTypeName(Class class_) { public Object toJson(RelCollationImpl node) { final List list = new ArrayList<>(); for (RelFieldCollation fieldCollation : node.getFieldCollations()) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder().map(); map.put("field", fieldCollation.getFieldIndex()); map.put("direction", fieldCollation.getDirection().name()); map.put("nulls", fieldCollation.nullDirection.name()); @@ -185,18 +206,18 @@ public RelCollation toCollation( public RelFieldCollation toFieldCollation(Map map) { final Integer field = (Integer) map.get("field"); final RelFieldCollation.Direction direction = - Util.enumVal(RelFieldCollation.Direction.class, - (String) map.get("direction")); + enumVal(RelFieldCollation.Direction.class, + map, "direction"); final RelFieldCollation.NullDirection nullDirection = - Util.enumVal(RelFieldCollation.NullDirection.class, - (String) map.get("nulls")); + enumVal(RelFieldCollation.NullDirection.class, + map, "nulls"); return new RelFieldCollation(field, direction, nullDirection); } public RelDistribution toDistribution(Map map) { final RelDistribution.Type type = - Util.enumVal(RelDistribution.Type.class, - (String) map.get("type")); + enumVal(RelDistribution.Type.class, + map, "type"); ImmutableIntList list = EMPTY; if (map.containsKey("keys")) { @@ -207,7 +228,7 @@ public RelDistribution toDistribution(Map map) { } private Object toJson(RelDistribution relDistribution) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder().map(); map.put("type", relDistribution.getType().name()); if (!relDistribution.getKeys().isEmpty()) { map.put("keys", relDistribution.getKeys()); @@ -233,7 +254,7 @@ public RelDataType toType(RelDataTypeFactory typeFactory, Object o) { return toType(typeFactory, fields); } else { final SqlTypeName sqlTypeName = - Util.enumVal(SqlTypeName.class, (String) map.get("type")); + enumVal(SqlTypeName.class, map, "type"); final Integer precision = (Integer) map.get("precision"); final Integer scale = (Integer) map.get("scale"); if (SqlTypeName.INTERVAL_TYPES.contains(sqlTypeName)) { @@ -252,18 +273,19 @@ public RelDataType toType(RelDataTypeFactory typeFactory, Object o) { } else { type = typeFactory.createSqlType(sqlTypeName, precision, scale); } - final boolean nullable = (Boolean) map.get("nullable"); + final boolean nullable = get(map, "nullable"); return typeFactory.createTypeWithNullability(type, nullable); } } else { - final SqlTypeName sqlTypeName = - Util.enumVal(SqlTypeName.class, (String) o); + final SqlTypeName sqlTypeName = requireNonNull( + Util.enumVal(SqlTypeName.class, (String) o), + () -> "unable to find enum value " + o + " in class " + SqlTypeName.class); return typeFactory.createSqlType(sqlTypeName); } } public Object toJson(AggregateCall node) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder().map(); final Map aggMap = toJson(node.getAggregation()); if (node.getAggregation().getFunctionType().isUserDefined()) { aggMap.put("class", node.getAggregation().getClass().getName()); @@ -276,7 +298,7 @@ public Object toJson(AggregateCall node) { return map; } - public Object toJson(Object value) { + public @Nullable Object toJson(@Nullable Object value) { if (value == null || value instanceof Number || value instanceof String @@ -293,13 +315,13 @@ public Object toJson(Object value) { } else if (value instanceof CorrelationId) { return toJson((CorrelationId) value); } else if (value instanceof List) { - final List list = jsonBuilder.list(); - for (Object o : (List) value) { + final List<@Nullable Object> list = jsonBuilder().list(); + for (Object o : (List) value) { list.add(toJson(o)); } return list; } else if (value instanceof ImmutableBitSet) { - final List list = jsonBuilder.list(); + final List<@Nullable Object> list = jsonBuilder().list(); for (Integer integer : (ImmutableBitSet) value) { list.add(toJson(integer)); } @@ -322,13 +344,13 @@ public Object toJson(Object value) { private Object toJson(RelDataType node) { if (node.isStruct()) { - final List list = jsonBuilder.list(); + final List<@Nullable Object> list = jsonBuilder().list(); for (RelDataTypeField field : node.getFieldList()) { list.add(toJson(field)); } return list; } else { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder().map(); map.put("type", node.getSqlTypeName().name()); map.put("nullable", node.isNullable()); if (node.getSqlTypeName().allowsPrec()) { @@ -344,7 +366,7 @@ private Object toJson(RelDataType node) { private Object toJson(RelDataTypeField node) { final Map map; if (node.getType().isStruct()) { - map = jsonBuilder.map(); + map = jsonBuilder().map(); map.put("fields", toJson(node.getType())); } else { map = (Map) toJson(node.getType()); @@ -361,7 +383,7 @@ private Object toJson(RexNode node) { final Map map; switch (node.getKind()) { case FIELD_ACCESS: - map = jsonBuilder.map(); + map = jsonBuilder().map(); final RexFieldAccess fieldAccess = (RexFieldAccess) node; map.put("field", fieldAccess.getField().getName()); map.put("expr", toJson(fieldAccess.getReferenceExpr())); @@ -369,32 +391,32 @@ private Object toJson(RexNode node) { case LITERAL: final RexLiteral literal = (RexLiteral) node; final Object value = literal.getValue3(); - map = jsonBuilder.map(); + map = jsonBuilder().map(); map.put("literal", RelEnumTypes.fromEnum(value)); map.put("type", toJson(node.getType())); return map; case INPUT_REF: - map = jsonBuilder.map(); + map = jsonBuilder().map(); map.put("input", ((RexSlot) node).getIndex()); map.put("name", ((RexSlot) node).getName()); return map; case LOCAL_REF: - map = jsonBuilder.map(); + map = jsonBuilder().map(); map.put("input", ((RexSlot) node).getIndex()); map.put("name", ((RexSlot) node).getName()); map.put("type", toJson(node.getType())); return map; case CORREL_VARIABLE: - map = jsonBuilder.map(); + map = jsonBuilder().map(); map.put("correl", ((RexCorrelVariable) node).getName()); map.put("type", toJson(node.getType())); return map; default: if (node instanceof RexCall) { final RexCall call = (RexCall) node; - map = jsonBuilder.map(); + map = jsonBuilder().map(); map.put("op", toJson(call.getOperator())); - final List list = jsonBuilder.list(); + final List<@Nullable Object> list = jsonBuilder().list(); for (RexNode operand : call.getOperands()) { list.add(toJson(operand)); } @@ -428,7 +450,7 @@ private Object toJson(RexNode node) { } private Object toJson(RexWindow window) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder().map(); if (window.partitionKeys.size() > 0) { map.put("partition", toJson(window.partitionKeys)); } @@ -456,7 +478,7 @@ private Object toJson(RexWindow window) { } private Object toJson(RexFieldCollation collation) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder().map(); map.put("expr", toJson(collation.left)); map.put("direction", collation.getDirection().name()); map.put("null-direction", collation.getNullDirection().name()); @@ -464,37 +486,40 @@ private Object toJson(RexFieldCollation collation) { } private Object toJson(RexWindowBound windowBound) { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder().map(); if (windowBound.isCurrentRow()) { map.put("type", "CURRENT_ROW"); } else if (windowBound.isUnbounded()) { map.put("type", windowBound.isPreceding() ? "UNBOUNDED_PRECEDING" : "UNBOUNDED_FOLLOWING"); } else { map.put("type", windowBound.isPreceding() ? "PRECEDING" : "FOLLOWING"); - map.put("offset", toJson(windowBound.getOffset())); + RexNode offset = requireNonNull(windowBound.getOffset(), + () -> "getOffset for window bound " + windowBound); + map.put("offset", toJson(offset)); } return map; } - RexNode toRex(RelInput relInput, Object o) { + @PolyNull RexNode toRex(RelInput relInput, @PolyNull Object o) { final RelOptCluster cluster = relInput.getCluster(); final RexBuilder rexBuilder = cluster.getRexBuilder(); if (o == null) { return null; } else if (o instanceof Map) { Map map = (Map) o; - final Map opMap = (Map) map.get("op"); + final Map opMap = (Map) map.get("op"); final RelDataTypeFactory typeFactory = cluster.getTypeFactory(); if (opMap != null) { if (map.containsKey("class")) { opMap.put("class", map.get("class")); } - final List operands = (List) map.get("operands"); + @SuppressWarnings("unchecked") + final List operands = get((Map) map, "operands"); final List rexOperands = toRexList(relInput, operands); final Object jsonType = map.get("type"); final Map window = (Map) map.get("window"); if (window != null) { - final SqlAggFunction operator = toAggregation(opMap); + final SqlAggFunction operator = requireNonNull(toAggregation(opMap), "operator"); final RelDataType type = toType(typeFactory, jsonType); List partitionKeys = new ArrayList<>(); if (window.containsKey("partition")) { @@ -502,7 +527,7 @@ RexNode toRex(RelInput relInput, Object o) { } List orderKeys = new ArrayList<>(); if (window.containsKey("order")) { - orderKeys = toRexFieldCollationList(relInput, (List) window.get("order")); + addRexFieldCollationList(orderKeys, relInput, (List) window.get("order")); } final RexWindowBound lowerBound; final RexWindowBound upperBound; @@ -517,16 +542,20 @@ RexNode toRex(RelInput relInput, Object o) { physical = false; } else { // No ROWS or RANGE clause + // Note: lower and upper bounds are non-nullable, so this branch is not reachable lowerBound = null; upperBound = null; physical = false; } - final boolean distinct = (Boolean) map.get("distinct"); + final boolean distinct = get((Map) map, "distinct"); return rexBuilder.makeOver(type, operator, rexOperands, partitionKeys, - ImmutableList.copyOf(orderKeys), lowerBound, upperBound, physical, + ImmutableList.copyOf(orderKeys), + requireNonNull(lowerBound, "lowerBound"), + requireNonNull(upperBound, "upperBound"), + physical, true, false, distinct, false); } else { - final SqlOperator operator = toOp(opMap); + final SqlOperator operator = requireNonNull(toOp(opMap), "operator"); final RelDataType type; if (jsonType != null) { type = toType(typeFactory, jsonType); @@ -604,15 +633,15 @@ RexNode toRex(RelInput relInput, Object o) { } } - private List toRexFieldCollationList( - RelInput relInput, List> order) { + private void addRexFieldCollationList( + List list, + RelInput relInput, @Nullable List> order) { if (order == null) { - return null; + return; } - List list = new ArrayList<>(); for (Map o : order) { - RexNode expr = toRex(relInput, o.get("expr")); + RexNode expr = requireNonNull(toRex(relInput, o.get("expr")), "expr"); Set directions = new HashSet<>(); if (Direction.valueOf((String) o.get("direction")) == Direction.DESCENDING) { directions.add(SqlKind.DESCENDING); @@ -624,15 +653,15 @@ private List toRexFieldCollationList( } list.add(new RexFieldCollation(expr, directions)); } - return list; } - private RexWindowBound toRexWindowBound(RelInput input, Map map) { + private @Nullable RexWindowBound toRexWindowBound(RelInput input, + @Nullable Map map) { if (map == null) { return null; } - final String type = (String) map.get("type"); + final String type = get(map, "type"); switch (type) { case "CURRENT_ROW": return RexWindowBounds.CURRENT_ROW; @@ -657,7 +686,7 @@ private List toRexList(RelInput relInput, List operands) { return list; } - SqlOperator toOp(Map map) { + @Nullable SqlOperator toOp(Map map) { // in case different operator has the same kind, check with both name and kind. String name = map.get("name").toString(); String kind = map.get("kind").toString(); @@ -683,13 +712,13 @@ SqlOperator toOp(Map map) { return null; } - SqlAggFunction toAggregation(Map map) { + @Nullable SqlAggFunction toAggregation(Map map) { return (SqlAggFunction) toOp(map); } - private Map toJson(SqlOperator operator) { + private Map toJson(SqlOperator operator) { // User-defined operators are not yet handled. - Map map = jsonBuilder.map(); + Map map = jsonBuilder().map(); map.put("name", operator.getName()); map.put("kind", operator.kind.toString()); map.put("syntax", operator.getSyntax().toString()); diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java index 1cede9c3fbd5..e7d47cc2b186 100644 --- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java +++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java @@ -41,6 +41,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -51,6 +53,8 @@ import java.util.Locale; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * Reads a JSON plan and converts it back to a tree of relational expressions. * @@ -65,7 +69,7 @@ public class RelJsonReader { private final RelOptSchema relOptSchema; private final RelJson relJson = new RelJson(null); private final Map relMap = new LinkedHashMap<>(); - private RelNode lastRel; + private @Nullable RelNode lastRel; public RelJsonReader(RelOptCluster cluster, RelOptSchema relOptSchema, Schema schema) { @@ -81,9 +85,9 @@ public RelNode read(String s) throws IOException { .configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true) .readValue(s, TYPE_REF); @SuppressWarnings("unchecked") - final List> rels = (List) o.get("rels"); + final List> rels = (List) requireNonNull(o.get("rels"), "rels"); readRels(rels); - return lastRel; + return requireNonNull(lastRel, "lastRel"); } private void readRels(List> jsonRels) { @@ -93,7 +97,7 @@ private void readRels(List> jsonRels) { } private void readRel(final Map jsonRel) { - String id = (String) jsonRel.get("id"); + String id = (String) requireNonNull(jsonRel.get("id"), "jsonRel.id"); String type = (String) jsonRel.get("relOp"); Constructor constructor = relJson.getConstructor(type); RelInput input = new RelInput() { @@ -106,8 +110,12 @@ private void readRel(final Map jsonRel) { } @Override public RelOptTable getTable(String table) { - final List list = getStringList(table); - return relOptSchema.getTableForMember(list); + final List list = requireNonNull( + getStringList(table), + () -> "getStringList for " + table); + return requireNonNull( + relOptSchema.getTableForMember(list), + () -> "table " + table + " is not found in schema " + relOptSchema.toString()); } @Override public RelNode getInput() { @@ -119,7 +127,7 @@ private void readRel(final Map jsonRel) { @Override public List getInputs() { final List jsonInputs = getStringList("inputs"); if (jsonInputs == null) { - return ImmutableList.of(lastRel); + return ImmutableList.of(requireNonNull(lastRel, "lastRel")); } final ImmutableList.Builder inputs = new ImmutableList.Builder<>(); for (String jsonInput : jsonInputs) { @@ -128,15 +136,15 @@ private void readRel(final Map jsonRel) { return inputs.build(); } - @Override public RexNode getExpression(String tag) { + @Override public @Nullable RexNode getExpression(String tag) { return relJson.toRex(this, jsonRel.get(tag)); } @Override public ImmutableBitSet getBitSet(String tag) { - return ImmutableBitSet.of(getIntegerList(tag)); + return ImmutableBitSet.of(requireNonNull(getIntegerList(tag), tag)); } - @Override public List getBitSetList(String tag) { + @Override public @Nullable List getBitSetList(String tag) { List> list = getIntegerListList(tag); if (list == null) { return null; @@ -149,24 +157,24 @@ private void readRel(final Map jsonRel) { return builder.build(); } - @Override public List getStringList(String tag) { + @Override public @Nullable List getStringList(String tag) { //noinspection unchecked return (List) jsonRel.get(tag); } - @Override public List getIntegerList(String tag) { + @Override public @Nullable List getIntegerList(String tag) { //noinspection unchecked return (List) jsonRel.get(tag); } - @Override public List> getIntegerListList(String tag) { + @Override public @Nullable List> getIntegerListList(String tag) { //noinspection unchecked return (List>) jsonRel.get(tag); } @Override public List getAggregateCalls(String tag) { @SuppressWarnings("unchecked") - final List> jsonAggs = (List) jsonRel.get(tag); + final List> jsonAggs = (List) getNonNull(tag); final List inputs = new ArrayList<>(); for (Map jsonAggCall : jsonAggs) { inputs.add(toAggCall(jsonAggCall)); @@ -174,29 +182,33 @@ private void readRel(final Map jsonRel) { return inputs; } - @Override public Object get(String tag) { + @Override public @Nullable Object get(String tag) { return jsonRel.get(tag); } - @Override public String getString(String tag) { - return (String) jsonRel.get(tag); + private Object getNonNull(String tag) { + return requireNonNull(get(tag), () -> "no entry for tag " + tag); + } + + @Override public @Nullable String getString(String tag) { + return (String) get(tag); } @Override public float getFloat(String tag) { - return ((Number) jsonRel.get(tag)).floatValue(); + return ((Number) getNonNull(tag)).floatValue(); } @Override public boolean getBoolean(String tag, boolean default_) { - final Boolean b = (Boolean) jsonRel.get(tag); + final Boolean b = (Boolean) get(tag); return b != null ? b : default_; } - @Override public > E getEnum(String tag, Class enumClass) { + @Override public > @Nullable E getEnum(String tag, Class enumClass) { return Util.enumVal(enumClass, - getString(tag).toUpperCase(Locale.ROOT)); + ((String) getNonNull(tag)).toUpperCase(Locale.ROOT)); } - @Override public List getExpressionList(String tag) { + @Override public @Nullable List getExpressionList(String tag) { @SuppressWarnings("unchecked") final List jsonNodes = (List) jsonRel.get(tag); if (jsonNodes == null) { @@ -210,19 +222,19 @@ private void readRel(final Map jsonRel) { } @Override public RelDataType getRowType(String tag) { - final Object o = jsonRel.get(tag); + final Object o = getNonNull(tag); return relJson.toType(cluster.getTypeFactory(), o); } @Override public RelDataType getRowType(String expressionsTag, String fieldsTag) { final List expressionList = getExpressionList(expressionsTag); @SuppressWarnings("unchecked") final List names = - (List) get(fieldsTag); + (List) getNonNull(fieldsTag); return cluster.getTypeFactory().createStructType( new AbstractList>() { @Override public Map.Entry get(int index) { return Pair.of(names.get(index), - expressionList.get(index).getType()); + requireNonNull(expressionList, "expressionList").get(index).getType()); } @Override public int size() { @@ -233,16 +245,17 @@ private void readRel(final Map jsonRel) { @Override public RelCollation getCollation() { //noinspection unchecked - return relJson.toCollation((List) get("collation")); + return relJson.toCollation((List) getNonNull("collation")); } @Override public RelDistribution getDistribution() { - return relJson.toDistribution((Map) get("distribution")); + //noinspection unchecked + return relJson.toDistribution((Map) getNonNull("distribution")); } @Override public ImmutableList> getTuples(String tag) { //noinspection unchecked - final List jsonTuples = (List) get(tag); + final List jsonTuples = (List) getNonNull(tag); final ImmutableList.Builder> builder = ImmutableList.builder(); for (List jsonTuple : jsonTuples) { @@ -276,15 +289,22 @@ public ImmutableList getTuple(List jsonTuple) { } private AggregateCall toAggCall(Map jsonAggCall) { - final Map aggMap = (Map) jsonAggCall.get("agg"); - final SqlAggFunction aggregation = - relJson.toAggregation(aggMap); + @SuppressWarnings("unchecked") + final Map aggMap = (Map) requireNonNull( + jsonAggCall.get("agg"), + "agg key is not found"); + final SqlAggFunction aggregation = requireNonNull( + relJson.toAggregation(aggMap), + () -> "relJson.toAggregation output for " + aggMap); final Boolean distinct = (Boolean) jsonAggCall.get("distinct"); @SuppressWarnings("unchecked") - final List operands = (List) jsonAggCall.get("operands"); + final List operands = (List) requireNonNull( + jsonAggCall.get("operands"), + "jsonAggCall.operands"); final Integer filterOperand = (Integer) jsonAggCall.get("filter"); + final Object jsonAggType = requireNonNull(jsonAggCall.get("type"), "jsonAggCall.type"); final RelDataType type = - relJson.toType(cluster.getTypeFactory(), jsonAggCall.get("type")); + relJson.toType(cluster.getTypeFactory(), jsonAggType); final String name = (String) jsonAggCall.get("name"); return AggregateCall.create(aggregation, distinct, false, false, operands, filterOperand == null ? -1 : filterOperand, diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonWriter.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonWriter.java index 7f631418f192..d293c8a9c4b0 100644 --- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonWriter.java +++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonWriter.java @@ -24,10 +24,13 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Callback for a relational expression to dump itself as JSON. @@ -40,9 +43,9 @@ public class RelJsonWriter implements RelWriter { protected final JsonBuilder jsonBuilder; protected final RelJson relJson; private final Map relIdMap = new IdentityHashMap<>(); - protected final List relList; - private final List> values = new ArrayList<>(); - private String previousId; + protected final List<@Nullable Object> relList; + private final List> values = new ArrayList<>(); + private @Nullable String previousId; //~ Constructors ------------------------------------------------------------- @@ -54,20 +57,20 @@ public RelJsonWriter() { //~ Methods ------------------------------------------------------------------ - protected void explain_(RelNode rel, List> values) { - final Map map = jsonBuilder.map(); + protected void explain_(RelNode rel, List> values) { + final Map map = jsonBuilder.map(); map.put("id", null); // ensure that id is the first attribute map.put("relOp", relJson.classToTypeName(rel.getClass())); - for (Pair value : values) { + for (Pair value : values) { if (value.right instanceof RelNode) { continue; } put(map, value.left, value.right); } // omit 'inputs: ["3"]' if "3" is the preceding rel - final List list = explainInputs(rel.getInputs()); - if (list.size() != 1 || !list.get(0).equals(previousId)) { + final List<@Nullable Object> list = explainInputs(rel.getInputs()); + if (list.size() != 1 || !Objects.equals(list.get(0), previousId)) { map.put("inputs", list); } @@ -79,12 +82,12 @@ protected void explain_(RelNode rel, List> values) { previousId = id; } - private void put(Map map, String name, Object value) { + private void put(Map map, String name, @Nullable Object value) { map.put(name, relJson.toJson(value)); } - private List explainInputs(List inputs) { - final List list = jsonBuilder.list(); + private List<@Nullable Object> explainInputs(List inputs) { + final List<@Nullable Object> list = jsonBuilder.list(); for (RelNode input : inputs) { String id = relIdMap.get(input); if (id == null) { @@ -96,7 +99,7 @@ private List explainInputs(List inputs) { return list; } - @Override public final void explain(RelNode rel, List> valueList) { + @Override public final void explain(RelNode rel, List> valueList) { explain_(rel, valueList); } @@ -104,13 +107,13 @@ private List explainInputs(List inputs) { return SqlExplainLevel.ALL_ATTRIBUTES; } - @Override public RelWriter item(String term, Object value) { + @Override public RelWriter item(String term, @Nullable Object value) { values.add(Pair.of(term, value)); return this; } @Override public RelWriter done(RelNode node) { - final List> valuesCopy = + final List> valuesCopy = ImmutableList.copyOf(values); values.clear(); explain_(node, valuesCopy); @@ -126,7 +129,7 @@ private List explainInputs(List inputs) { * explained. */ public String asString() { - final Map map = jsonBuilder.map(); + final Map map = jsonBuilder.map(); map.put("rels", relList); return jsonBuilder.toJsonString(map); } diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java index 4c0c328de351..1d242f21e9cc 100644 --- a/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelWriterImpl.java @@ -26,6 +26,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -40,7 +42,7 @@ public class RelWriterImpl implements RelWriter { protected final SqlExplainLevel detailLevel; protected final boolean withIdPrefix; protected final Spacer spacer = new Spacer(); - private final List> values = new ArrayList<>(); + private final List> values = new ArrayList<>(); //~ Constructors ----------------------------------------------------------- @@ -59,7 +61,7 @@ public RelWriterImpl( //~ Methods ---------------------------------------------------------------- protected void explain_(RelNode rel, - List> values) { + List> values) { List inputs = rel.getInputs(); final RelMetadataQuery mq = rel.getCluster().getMetadataQuery(); if (!mq.isVisibleInExplain(rel, detailLevel)) { @@ -76,7 +78,7 @@ protected void explain_(RelNode rel, s.append(rel.getRelTypeName()); if (detailLevel != SqlExplainLevel.NO_ATTRIBUTES) { int j = 0; - for (Pair value : values) { + for (Pair value : values) { if (value.right instanceof RelNode) { continue; } @@ -128,7 +130,7 @@ private void explainInputs(List inputs) { } } - @Override public final void explain(RelNode rel, List> valueList) { + @Override public final void explain(RelNode rel, List> valueList) { explain_(rel, valueList); } @@ -136,14 +138,14 @@ private void explainInputs(List inputs) { return detailLevel; } - @Override public RelWriter item(String term, Object value) { + @Override public RelWriter item(String term, @Nullable Object value) { values.add(Pair.of(term, value)); return this; } @Override public RelWriter done(RelNode node) { assert checkInputsPresentInExplain(node); - final List> valuesCopy = + final List> valuesCopy = ImmutableList.copyOf(values); values.clear(); explain_(node, valuesCopy); @@ -169,7 +171,7 @@ private boolean checkInputsPresentInExplain(RelNode node) { */ public String simple() { final StringBuilder buf = new StringBuilder("("); - for (Ord> ord : Ord.zip(values)) { + for (Ord> ord : Ord.zip(values)) { if (ord.i > 0) { buf.append(", "); } diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelXmlWriter.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelXmlWriter.java index 17bfc38bf996..7078f9add18d 100644 --- a/core/src/main/java/org/apache/calcite/rel/externalize/RelXmlWriter.java +++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelXmlWriter.java @@ -21,9 +21,12 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.XmlOutput; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Callback for a relational expression to dump in XML format. @@ -51,7 +54,7 @@ public RelXmlWriter(PrintWriter pw, SqlExplainLevel detailLevel) { @Override protected void explain_( RelNode rel, - List> values) { + List> values) { if (generic) { explainGeneric(rel, values); } else { @@ -88,7 +91,7 @@ public RelXmlWriter(PrintWriter pw, SqlExplainLevel detailLevel) { */ private void explainGeneric( RelNode rel, - List> values) { + List> values) { String relType = rel.getRelTypeName(); xmlOutput.beginBeginTag("RelNode"); xmlOutput.attribute("type", relType); @@ -96,7 +99,7 @@ private void explainGeneric( xmlOutput.endBeginTag("RelNode"); final List inputs = new ArrayList<>(); - for (Pair pair : values) { + for (Pair pair : values) { if (pair.right instanceof RelNode) { inputs.add((RelNode) pair.right); continue; @@ -136,18 +139,18 @@ private void explainGeneric( */ private void explainSpecific( RelNode rel, - List> values) { + List> values) { String tagName = rel.getRelTypeName(); xmlOutput.beginBeginTag(tagName); xmlOutput.attribute("id", rel.getId() + ""); - for (Pair value : values) { + for (Pair value : values) { if (value.right instanceof RelNode) { continue; } xmlOutput.attribute( value.left, - value.right.toString()); + Objects.toString(value.right)); } xmlOutput.endBeginTag(tagName); spacer.add(2); diff --git a/core/src/main/java/org/apache/calcite/rel/hint/HintStrategy.java b/core/src/main/java/org/apache/calcite/rel/hint/HintStrategy.java index 7670f783fee3..95c377f6d734 100644 --- a/core/src/main/java/org/apache/calcite/rel/hint/HintStrategy.java +++ b/core/src/main/java/org/apache/calcite/rel/hint/HintStrategy.java @@ -21,8 +21,9 @@ import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; -import javax.annotation.Nullable; /** * Represents a hint strategy entry of {@link HintStrategyTable}. @@ -47,7 +48,7 @@ public class HintStrategy { //~ Instance fields -------------------------------------------------------- public final HintPredicate predicate; - public final HintOptionChecker hintOptionChecker; + public final @Nullable HintOptionChecker hintOptionChecker; public final ImmutableSet excludedRules; public final ImmutableSet converterRules; @@ -55,7 +56,7 @@ public class HintStrategy { private HintStrategy( HintPredicate predicate, - HintOptionChecker hintOptionChecker, + @Nullable HintOptionChecker hintOptionChecker, ImmutableSet excludedRules, ImmutableSet converterRules) { this.predicate = predicate; @@ -79,8 +80,7 @@ public static Builder builder(HintPredicate hintPredicate) { /** Builder for {@link HintStrategy}. */ public static class Builder { private final HintPredicate predicate; - @Nullable - private HintOptionChecker optionChecker; + private @Nullable HintOptionChecker optionChecker; private ImmutableSet excludedRules; private ImmutableSet converterRules; diff --git a/core/src/main/java/org/apache/calcite/rel/hint/HintStrategyTable.java b/core/src/main/java/org/apache/calcite/rel/hint/HintStrategyTable.java index f011cefbb1af..c36e64b30c52 100644 --- a/core/src/main/java/org/apache/calcite/rel/hint/HintStrategyTable.java +++ b/core/src/main/java/org/apache/calcite/rel/hint/HintStrategyTable.java @@ -24,16 +24,18 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import static java.util.Objects.requireNonNull; + /** * A collection of {@link HintStrategy}s. * @@ -87,7 +89,7 @@ public List apply(List hints, RelNode rel) { private boolean canApply(RelHint hint, RelNode rel) { final Key key = Key.of(hint.hintName); - assert this.strategies.containsKey(key); + assert this.strategies.containsKey(key) : "hint " + hint.hintName + " must be present"; return this.strategies.get(key).predicate.apply(hint, rel); } @@ -107,7 +109,7 @@ public boolean validateHint(RelHint hint) { return false; } final HintStrategy strategy = strategies.get(key); - if (strategy.hintOptionChecker != null) { + if (strategy != null && strategy.hintOptionChecker != null) { return strategy.hintOptionChecker.checkOptions(hint, this.errorHandler); } return true; @@ -123,7 +125,7 @@ public boolean isRuleExcluded(Hintable hintable, RelOptRule rule) { for (RelHint hint : hints) { final Key key = Key.of(hint.hintName); - assert this.strategies.containsKey(key); + assert this.strategies.containsKey(key) : "hint " + hint.hintName + " must be present"; final HintStrategy strategy = strategies.get(key); if (strategy.excludedRules.contains(rule)) { return isDesiredConversionPossible(strategy.converterRules, hintable); @@ -167,7 +169,7 @@ static Key of(String name) { return new Key(name.toLowerCase(Locale.ROOT)); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -192,12 +194,12 @@ public static class Builder { public Builder hintStrategy(String hintName, HintPredicate strategy) { this.strategies.put(Key.of(hintName), - HintStrategy.builder(Objects.requireNonNull(strategy)).build()); + HintStrategy.builder(requireNonNull(strategy, "HintPredicate")).build()); return this; } public Builder hintStrategy(String hintName, HintStrategy entry) { - this.strategies.put(Key.of(hintName), Objects.requireNonNull(entry)); + this.strategies.put(Key.of(hintName), requireNonNull(entry, "HintStrategy")); return this; } @@ -227,8 +229,8 @@ public static class HintErrorLogger implements Litmus { public static final HintErrorLogger INSTANCE = new HintErrorLogger(); - @Override public boolean fail(String message, Object... args) { - LOGGER.warn(message, args); + @Override public boolean fail(@Nullable String message, @Nullable Object... args) { + LOGGER.warn(requireNonNull(message, "message"), args); return false; } @@ -236,7 +238,8 @@ public static class HintErrorLogger implements Litmus { return true; } - @Override public boolean check(boolean condition, String message, Object... args) { + @Override public boolean check(boolean condition, @Nullable String message, + @Nullable Object... args) { if (condition) { return succeed(); } else { diff --git a/core/src/main/java/org/apache/calcite/rel/hint/RelHint.java b/core/src/main/java/org/apache/calcite/rel/hint/RelHint.java index 169e1bdc5c1a..bb9460409831 100644 --- a/core/src/main/java/org/apache/calcite/rel/hint/RelHint.java +++ b/core/src/main/java/org/apache/calcite/rel/hint/RelHint.java @@ -20,13 +20,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import javax.annotation.Nullable; /** * Hint attached to a relation expression. @@ -128,7 +129,7 @@ public RelHint copy(List inheritPath) { return new RelHint(inheritPath, hintName, listOptions, kvOptions); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java index 115e423abdd9..d60cd228bcdb 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalAggregate.java @@ -29,6 +29,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -65,7 +67,7 @@ public LogicalAggregate( List hints, RelNode input, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) { super(cluster, traitSet, hints, input, groupSet, groupSets, aggCalls); } @@ -109,7 +111,7 @@ public LogicalAggregate(RelInput input) { public static LogicalAggregate create(final RelNode input, List hints, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) { return create_(input, hints, groupSet, groupSets, aggCalls); } @@ -135,7 +137,7 @@ public static LogicalAggregate create(final RelNode input, private static LogicalAggregate create_(final RelNode input, List hints, ImmutableBitSet groupSet, - List groupSets, + @Nullable List groupSets, List aggCalls) { final RelOptCluster cluster = input.getCluster(); final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE); @@ -147,7 +149,7 @@ private static LogicalAggregate create_(final RelNode input, @Override public LogicalAggregate copy(RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, List aggCalls) { + @Nullable List groupSets, List aggCalls) { assert traitSet.containsIfApplicable(Convention.NONE); return new LogicalAggregate(getCluster(), traitSet, hints, input, groupSet, groupSets, aggCalls); diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java index 031de6483f89..8a4a3f193d92 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java @@ -29,6 +29,8 @@ import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.Litmus; +import static java.util.Objects.requireNonNull; + /** * A relational operator that performs nested-loop joins. * @@ -80,9 +82,10 @@ public LogicalCorrelate( public LogicalCorrelate(RelInput input) { this(input.getCluster(), input.getTraitSet(), input.getInputs().get(0), input.getInputs().get(1), - new CorrelationId((Integer) input.get("correlation")), + new CorrelationId( + (Integer) requireNonNull(input.get("correlation"), "correlation")), input.getBitSet("requiredColumns"), - input.getEnum("joinType", JoinRelType.class)); + requireNonNull(input.getEnum("joinType", JoinRelType.class), "joinType")); } /** Creates a LogicalCorrelate. */ diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java index 8248fbc969ff..0b4d388c203c 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java @@ -35,6 +35,8 @@ import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; import java.util.Set; @@ -136,7 +138,7 @@ public static LogicalFilter create(final RelNode input, RexNode condition, .itemIf("variablesSet", variablesSet, !variablesSet.isEmpty()); } - @Override public boolean deepEquals(Object obj) { + @Override public boolean deepEquals(@Nullable Object obj) { return deepEquals0(obj) && variablesSet.equals(((LogicalFilter) obj).variablesSet); } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java index d8e50720b527..44704a534c0d 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalJoin.java @@ -33,11 +33,15 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Sub-class of {@link org.apache.calcite.rel.core.Join} * not targeted at any particular engine or calling convention. @@ -101,7 +105,7 @@ public LogicalJoin( ImmutableList systemFieldList) { super(cluster, traitSet, hints, left, right, condition, variablesSet, joinType); this.semiJoinDone = semiJoinDone; - this.systemFieldList = Objects.requireNonNull(systemFieldList); + this.systemFieldList = requireNonNull(systemFieldList); } @Deprecated // to be removed before 2.0 @@ -147,8 +151,10 @@ public LogicalJoin(RelInput input) { this(input.getCluster(), input.getCluster().traitSetOf(Convention.NONE), new ArrayList<>(), input.getInputs().get(0), input.getInputs().get(1), - input.getExpression("condition"), ImmutableSet.of(), - input.getEnum("joinType", JoinRelType.class), false, + requireNonNull(input.getExpression("condition"), "condition"), + ImmutableSet.of(), + requireNonNull(input.getEnum("joinType", JoinRelType.class), "joinType"), + false, ImmutableList.of()); } @@ -191,7 +197,7 @@ public static LogicalJoin create(RelNode left, RelNode right, List hint .itemIf("semiJoinDone", semiJoinDone, semiJoinDone); } - @Override public boolean deepEquals(Object obj) { + @Override public boolean deepEquals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalMatch.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalMatch.java index 8f370da29228..800a7bdee772 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalMatch.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalMatch.java @@ -27,6 +27,8 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Map; import java.util.SortedSet; @@ -64,7 +66,7 @@ public LogicalMatch(RelOptCluster cluster, RelTraitSet traitSet, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval) { + @Nullable RexNode interval) { super(cluster, traitSet, input, rowType, pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); @@ -77,7 +79,7 @@ public static LogicalMatch create(RelNode input, RelDataType rowType, RexNode pattern, boolean strictStart, boolean strictEnd, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, - ImmutableBitSet partitionKeys, RelCollation orderKeys, RexNode interval) { + ImmutableBitSet partitionKeys, RelCollation orderKeys, @Nullable RexNode interval) { final RelOptCluster cluster = input.getCluster(); final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE); return create(cluster, traitSet, input, rowType, pattern, @@ -94,7 +96,7 @@ public static LogicalMatch create(RelOptCluster cluster, Map patternDefinitions, Map measures, RexNode after, Map> subsets, boolean allRows, ImmutableBitSet partitionKeys, RelCollation orderKeys, - RexNode interval) { + @Nullable RexNode interval) { return new LogicalMatch(cluster, traitSet, input, rowType, pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); @@ -103,7 +105,7 @@ public static LogicalMatch create(RelOptCluster cluster, //~ Methods ------------------------------------------------------ @Override public RelNode copy(RelTraitSet traitSet, List inputs) { - return new LogicalMatch(getCluster(), traitSet, inputs.get(0), rowType, + return new LogicalMatch(getCluster(), traitSet, inputs.get(0), getRowType(), pattern, strictStart, strictEnd, patternDefinitions, measures, after, subsets, allRows, partitionKeys, orderKeys, interval); } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java index 87f5df225d8d..cb64b372700c 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalProject.java @@ -36,6 +36,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -84,7 +86,7 @@ public LogicalProject(RelOptCluster cluster, RelTraitSet traitSet, @Deprecated // to be removed before 2.0 public LogicalProject(RelOptCluster cluster, RelNode input, - List projects, List fieldNames, int flags) { + List projects, @Nullable List fieldNames, int flags) { this(cluster, cluster.traitSetOf(RelCollations.EMPTY), ImmutableList.of(), input, projects, RexUtil.createStructType(cluster.getTypeFactory(), projects, @@ -103,7 +105,8 @@ public LogicalProject(RelInput input) { /** Creates a LogicalProject. */ public static LogicalProject create(final RelNode input, List hints, - final List projects, List fieldNames) { + final List projects, + @Nullable List fieldNames) { final RelOptCluster cluster = input.getCluster(); final RelDataType rowType = RexUtil.createStructType(cluster.getTypeFactory(), projects, @@ -134,10 +137,10 @@ public static LogicalProject create(final RelNode input, List hints, @Override public RelNode withHints(List hintList) { return new LogicalProject(getCluster(), traitSet, hintList, - input, getProjects(), rowType); + input, getProjects(), getRowType()); } - @Override public boolean deepEquals(Object obj) { + @Override public boolean deepEquals(@Nullable Object obj) { return deepEquals0(obj); } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java index a53a4605cc69..c52acf80b45a 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java @@ -27,13 +27,15 @@ import org.apache.calcite.rel.core.Sort; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Sub-class of {@link org.apache.calcite.rel.core.Sort} not * targeted at any particular engine or calling convention. */ public final class LogicalSort extends Sort { private LogicalSort(RelOptCluster cluster, RelTraitSet traitSet, - RelNode input, RelCollation collation, RexNode offset, RexNode fetch) { + RelNode input, RelCollation collation, @Nullable RexNode offset, @Nullable RexNode fetch) { super(cluster, traitSet, input, collation, offset, fetch); assert traitSet.containsIfApplicable(Convention.NONE); } @@ -55,7 +57,7 @@ public LogicalSort(RelInput input) { * @param fetch Expression for number of rows to fetch */ public static LogicalSort create(RelNode input, RelCollation collation, - RexNode offset, RexNode fetch) { + @Nullable RexNode offset, @Nullable RexNode fetch) { RelOptCluster cluster = input.getCluster(); collation = RelCollationTraitDef.INSTANCE.canonize(collation); RelTraitSet traitSet = @@ -66,7 +68,7 @@ public static LogicalSort create(RelNode input, RelCollation collation, //~ Methods ---------------------------------------------------------------- @Override public Sort copy(RelTraitSet traitSet, RelNode newInput, - RelCollation newCollation, RexNode offset, RexNode fetch) { + RelCollation newCollation, @Nullable RexNode offset, @Nullable RexNode fetch) { return new LogicalSort(getCluster(), traitSet, newInput, newCollation, offset, fetch); } diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java index 9b5510ed55f6..e427d39b437e 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableFunctionScan.java @@ -29,6 +29,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; import java.util.Set; @@ -57,8 +59,8 @@ public LogicalTableFunctionScan( RelTraitSet traitSet, List inputs, RexNode rexCall, - Type elementType, RelDataType rowType, - Set columnMappings) { + @Nullable Type elementType, RelDataType rowType, + @Nullable Set columnMappings) { super(cluster, traitSet, inputs, rexCall, elementType, rowType, columnMappings); } @@ -68,8 +70,8 @@ public LogicalTableFunctionScan( RelOptCluster cluster, List inputs, RexNode rexCall, - Type elementType, RelDataType rowType, - Set columnMappings) { + @Nullable Type elementType, RelDataType rowType, + @Nullable Set columnMappings) { this(cluster, cluster.traitSetOf(Convention.NONE), inputs, rexCall, elementType, rowType, columnMappings); } @@ -86,8 +88,8 @@ public static LogicalTableFunctionScan create( RelOptCluster cluster, List inputs, RexNode rexCall, - Type elementType, RelDataType rowType, - Set columnMappings) { + @Nullable Type elementType, RelDataType rowType, + @Nullable Set columnMappings) { final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE); return new LogicalTableFunctionScan(cluster, traitSet, inputs, rexCall, elementType, rowType, columnMappings); @@ -99,9 +101,9 @@ public static LogicalTableFunctionScan create( RelTraitSet traitSet, List inputs, RexNode rexCall, - Type elementType, + @Nullable Type elementType, RelDataType rowType, - Set columnMappings) { + @Nullable Set columnMappings) { assert traitSet.containsIfApplicable(Convention.NONE); return new LogicalTableFunctionScan( getCluster(), diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java index 6a84e235e078..372bb0249d5b 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java @@ -26,6 +26,8 @@ import org.apache.calcite.rel.core.TableModify; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -42,8 +44,8 @@ public final class LogicalTableModify extends TableModify { */ public LogicalTableModify(RelOptCluster cluster, RelTraitSet traitSet, RelOptTable table, Prepare.CatalogReader schema, RelNode input, - Operation operation, List updateColumnList, - List sourceExpressionList, boolean flattened) { + Operation operation, @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { super(cluster, traitSet, table, schema, input, operation, updateColumnList, sourceExpressionList, flattened); } @@ -73,8 +75,8 @@ public LogicalTableModify(RelOptCluster cluster, RelOptTable table, /** Creates a LogicalTableModify. */ public static LogicalTableModify create(RelOptTable table, Prepare.CatalogReader schema, RelNode input, - Operation operation, List updateColumnList, - List sourceExpressionList, boolean flattened) { + Operation operation, @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { final RelOptCluster cluster = input.getCluster(); final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE); return new LogicalTableModify(cluster, traitSet, table, schema, input, diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java index 32b98505175b..11ea70f1b23c 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java @@ -94,7 +94,7 @@ public static LogicalValues create(RelOptCluster cluster, @Override public RelNode copy(RelTraitSet traitSet, List inputs) { assert traitSet.containsIfApplicable(Convention.NONE); assert inputs.isEmpty(); - return new LogicalValues(getCluster(), traitSet, rowType, tuples); + return new LogicalValues(getCluster(), traitSet, getRowType(), tuples); } /** Creates a LogicalValues that outputs no rows of a given row type. */ diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java index 6cbfeb0c0546..4e19c7127c67 100644 --- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java +++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java @@ -43,6 +43,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; @@ -78,7 +80,7 @@ public LogicalWindow(RelOptCluster cluster, RelTraitSet traitSet, @Override public LogicalWindow copy(RelTraitSet traitSet, List inputs) { return new LogicalWindow(getCluster(), traitSet, sole(inputs), constants, - rowType, groups); + getRowType(), groups); } /** @@ -324,7 +326,7 @@ private static class WindowKey { return Objects.hash(groupSet, orderKeys, isRows, lowerBound, upperBound); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof WindowKey && groupSet.equals(((WindowKey) obj).groupSet) diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java index aada5569d626..92e242a1dae3 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Set; @@ -55,11 +57,11 @@ public interface Selectivity extends Metadata { * @return estimated selectivity (between 0.0 and 1.0), or null if no * reliable estimate can be determined */ - Double getSelectivity(RexNode predicate); + @Nullable Double getSelectivity(@Nullable RexNode predicate); /** Handler API. */ interface Handler extends MetadataHandler { - Double getSelectivity(RelNode r, RelMetadataQuery mq, RexNode predicate); + @Nullable Double getSelectivity(RelNode r, RelMetadataQuery mq, @Nullable RexNode predicate); } } @@ -81,11 +83,11 @@ public interface UniqueKeys extends Metadata { * @return set of keys, or null if this information cannot be determined * (whereas empty set indicates definitely no keys at all) */ - Set getUniqueKeys(boolean ignoreNulls); + @Nullable Set getUniqueKeys(boolean ignoreNulls); /** Handler API. */ interface Handler extends MetadataHandler { - Set getUniqueKeys(RelNode r, RelMetadataQuery mq, + @Nullable Set getUniqueKeys(RelNode r, RelMetadataQuery mq, boolean ignoreNulls); } } @@ -180,11 +182,11 @@ public interface NodeTypes extends Metadata { * class. The default implementation for a node classifies it as a * {@link RelNode}. */ - Multimap, RelNode> getNodeTypes(); + @Nullable Multimap, RelNode> getNodeTypes(); /** Handler API. */ interface Handler extends MetadataHandler { - Multimap, RelNode> getNodeTypes(RelNode r, + @Nullable Multimap, RelNode> getNodeTypes(RelNode r, RelMetadataQuery mq); } } @@ -203,11 +205,11 @@ public interface RowCount extends Metadata { * @return estimated row count, or null if no reliable estimate can be * determined */ - Double getRowCount(); + @Nullable Double getRowCount(); /** Handler API. */ interface Handler extends MetadataHandler { - Double getRowCount(RelNode r, RelMetadataQuery mq); + @Nullable Double getRowCount(RelNode r, RelMetadataQuery mq); } } @@ -227,11 +229,11 @@ public interface MaxRowCount extends Metadata { * * @return upper bound on the number of rows returned */ - Double getMaxRowCount(); + @Nullable Double getMaxRowCount(); /** Handler API. */ interface Handler extends MetadataHandler { - Double getMaxRowCount(RelNode r, RelMetadataQuery mq); + @Nullable Double getMaxRowCount(RelNode r, RelMetadataQuery mq); } } @@ -250,11 +252,11 @@ public interface MinRowCount extends Metadata { * * @return lower bound on the number of rows returned */ - Double getMinRowCount(); + @Nullable Double getMinRowCount(); /** Handler API. */ interface Handler extends MetadataHandler { - Double getMinRowCount(RelNode r, RelMetadataQuery mq); + @Nullable Double getMinRowCount(RelNode r, RelMetadataQuery mq); } } @@ -276,12 +278,12 @@ public interface DistinctRowCount extends Metadata { * @return distinct row count for groupKey, filtered by predicate, or null * if no reliable estimate can be determined */ - Double getDistinctRowCount(ImmutableBitSet groupKey, RexNode predicate); + @Nullable Double getDistinctRowCount(ImmutableBitSet groupKey, @Nullable RexNode predicate); /** Handler API. */ interface Handler extends MetadataHandler { - Double getDistinctRowCount(RelNode r, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate); + @Nullable Double getDistinctRowCount(RelNode r, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate); } } @@ -301,11 +303,11 @@ public interface PercentageOriginalRows extends Metadata { * @return estimated percentage (between 0.0 and 1.0), or null if no * reliable estimate can be determined */ - Double getPercentageOriginalRows(); + @Nullable Double getPercentageOriginalRows(); /** Handler API. */ interface Handler extends MetadataHandler { - Double getPercentageOriginalRows(RelNode r, RelMetadataQuery mq); + @Nullable Double getPercentageOriginalRows(RelNode r, RelMetadataQuery mq); } } @@ -326,11 +328,11 @@ public interface PopulationSize extends Metadata { * @return distinct row count for the given groupKey, or null if no reliable * estimate can be determined */ - Double getPopulationSize(ImmutableBitSet groupKey); + @Nullable Double getPopulationSize(ImmutableBitSet groupKey); /** Handler API. */ interface Handler extends MetadataHandler { - Double getPopulationSize(RelNode r, RelMetadataQuery mq, + @Nullable Double getPopulationSize(RelNode r, RelMetadataQuery mq, ImmutableBitSet groupKey); } } @@ -347,7 +349,7 @@ public interface Size extends Metadata { * * @return average size of a row, in bytes, or null if not known */ - Double averageRowSize(); + @Nullable Double averageRowSize(); /** * Determines the average size (in bytes) of a value of a column in this @@ -364,12 +366,12 @@ public interface Size extends Metadata { * of a column value, in bytes. Each value or the entire list may be null if * the metadata is not available */ - List averageColumnSizes(); + List<@Nullable Double> averageColumnSizes(); /** Handler API. */ interface Handler extends MetadataHandler { - Double averageRowSize(RelNode r, RelMetadataQuery mq); - List averageColumnSizes(RelNode r, RelMetadataQuery mq); + @Nullable Double averageRowSize(RelNode r, RelMetadataQuery mq); + @Nullable List<@Nullable Double> averageColumnSizes(RelNode r, RelMetadataQuery mq); } } @@ -390,11 +392,11 @@ public interface ColumnOrigin extends Metadata { * determined (whereas empty set indicates definitely no origin columns at * all) */ - Set getColumnOrigins(int outputColumn); + @Nullable Set getColumnOrigins(int outputColumn); /** Handler API. */ interface Handler extends MetadataHandler { - Set getColumnOrigins(RelNode r, RelMetadataQuery mq, + @Nullable Set getColumnOrigins(RelNode r, RelMetadataQuery mq, int outputColumn); } } @@ -427,11 +429,11 @@ public interface ExpressionLineage extends Metadata { * cannot be determined (e.g. origin of an expression is an aggregation * in an {@link org.apache.calcite.rel.core.Aggregate} operator) */ - Set getExpressionLineage(RexNode expression); + @Nullable Set getExpressionLineage(RexNode expression); /** Handler API. */ interface Handler extends MetadataHandler { - Set getExpressionLineage(RelNode r, RelMetadataQuery mq, + @Nullable Set getExpressionLineage(RelNode r, RelMetadataQuery mq, RexNode expression); } } @@ -578,11 +580,11 @@ public interface AllPredicates extends Metadata { * @return predicate list, or null if the provider cannot infer the * lineage for any of the expressions contained in any of the predicates */ - RelOptPredicateList getAllPredicates(); + @Nullable RelOptPredicateList getAllPredicates(); /** Handler API. */ interface Handler extends MetadataHandler { - RelOptPredicateList getAllPredicates(RelNode r, RelMetadataQuery mq); + @Nullable RelOptPredicateList getAllPredicates(RelNode r, RelMetadataQuery mq); } } @@ -655,7 +657,7 @@ public interface Memory extends Metadata { * requires only {@code averageRowSize} bytes to maintain a single * accumulator for each aggregate function. */ - Double memory(); + @Nullable Double memory(); /** Returns the cumulative amount of memory, in bytes, required by the * physical operator implementing this relational expression, and all other @@ -663,7 +665,7 @@ public interface Memory extends Metadata { * * @see Parallelism#splitCount() */ - Double cumulativeMemoryWithinPhase(); + @Nullable Double cumulativeMemoryWithinPhase(); /** Returns the expected cumulative amount of memory, in bytes, required by * the physical operator implementing this relational expression, and all @@ -674,13 +676,13 @@ public interface Memory extends Metadata { *
cumulativeMemoryWithinPhaseSplit * = cumulativeMemoryWithinPhase / Parallelism.splitCount
*/ - Double cumulativeMemoryWithinPhaseSplit(); + @Nullable Double cumulativeMemoryWithinPhaseSplit(); /** Handler API. */ interface Handler extends MetadataHandler { - Double memory(RelNode r, RelMetadataQuery mq); - Double cumulativeMemoryWithinPhase(RelNode r, RelMetadataQuery mq); - Double cumulativeMemoryWithinPhaseSplit(RelNode r, RelMetadataQuery mq); + @Nullable Double memory(RelNode r, RelMetadataQuery mq); + @Nullable Double cumulativeMemoryWithinPhase(RelNode r, RelMetadataQuery mq); + @Nullable Double cumulativeMemoryWithinPhaseSplit(RelNode r, RelMetadataQuery mq); } } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java index d1a69285eb84..37dcff3d921b 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -31,6 +33,8 @@ import java.util.Map; import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Implementation of the {@link RelMetadataProvider} * interface that caches results from an underlying provider. @@ -55,7 +59,7 @@ public CachingRelMetadataProvider( //~ Methods ---------------------------------------------------------------- - @Override public UnboundMetadata apply( + @Override public <@Nullable M extends @Nullable Metadata> @Nullable UnboundMetadata apply( Class relClass, final Class metadataClass) { final UnboundMetadata function = @@ -89,7 +93,7 @@ public CachingRelMetadataProvider( private static class CacheEntry { long timestamp; - Object result; + @Nullable Object result; } /** Implementation of {@link InvocationHandler} for calls to a @@ -103,7 +107,7 @@ private class CachingInvocationHandler implements InvocationHandler { this.metadata = Objects.requireNonNull(metadata); } - @Override public Object invoke(Object proxy, Method method, Object[] args) + @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // Compute hash key. final ImmutableList.Builder builder = ImmutableList.builder(); @@ -138,7 +142,7 @@ private class CachingInvocationHandler implements InvocationHandler { } return result; } catch (InvocationTargetException e) { - throw e.getCause(); + throw castNonNull(e.getCause()); } } } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java index 2ece0454dfad..cfed31146d0e 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java @@ -23,6 +23,8 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -57,7 +59,7 @@ protected ChainedRelMetadataProvider( //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof ChainedRelMetadataProvider && providers.equals(((ChainedRelMetadataProvider) obj).providers); @@ -67,7 +69,7 @@ protected ChainedRelMetadataProvider( return providers.hashCode(); } - @Override public UnboundMetadata apply( + @Override public <@Nullable M extends @Nullable Metadata> @Nullable UnboundMetadata apply( Class relClass, final Class metadataClass) { final List> functions = new ArrayList<>(); @@ -125,7 +127,7 @@ private static class ChainedInvocationHandler implements InvocationHandler { this.metadataList = ImmutableList.copyOf(metadataList); } - @Override public Object invoke(Object proxy, Method method, Object[] args) + @Override public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for (Metadata metadata : metadataList) { try { diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java index d18cf07dd156..76106c9389a9 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java @@ -63,6 +63,7 @@ import com.google.common.collect.Multimap; import com.google.common.util.concurrent.UncheckedExecutionException; +import org.checkerframework.checker.nullness.qual.Nullable; import org.codehaus.commons.compiler.CompileException; import org.codehaus.commons.compiler.CompilerFactoryFactory; import org.codehaus.commons.compiler.ICompilerFactory; @@ -171,7 +172,7 @@ private static CacheBuilder maxSize(CacheBuilder builder, return builder; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof JaninoRelMetadataProvider && ((JaninoRelMetadataProvider) obj).provider.equals(provider); @@ -181,7 +182,7 @@ private static CacheBuilder maxSize(CacheBuilder builder, return 109 + provider.hashCode(); } - @Override public UnboundMetadata apply( + @Override public <@Nullable M extends @Nullable Metadata> UnboundMetadata apply( Class relClass, Class metadataClass) { throw new UnsupportedOperationException(); } @@ -533,7 +534,7 @@ private Key(MetadataDef def, RelMetadataProvider provider, + relClasses.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof Key && ((Key) obj).def.equals(def) diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java index 24b3e4f4c3ff..40a66f1a567a 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactory.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Source of metadata about relational expressions. * @@ -40,6 +42,6 @@ public interface MetadataFactory { * @param metadataClazz Metadata class * @return Metadata bound to {@code rel} and {@code query} */ - M query(RelNode rel, RelMetadataQuery mq, + M query(RelNode rel, RelMetadataQuery mq, Class metadataClazz); } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java index d92c267bd8c7..923c6d64b1fc 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java @@ -25,9 +25,12 @@ import com.google.common.cache.LoadingCache; import com.google.common.util.concurrent.UncheckedExecutionException; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.concurrent.ExecutionException; -/** Implementation of {@link MetadataFactory} that gets providers from a +/** + * Implementation of {@link MetadataFactory} that gets providers from a * {@link RelMetadataProvider} and stores them in a cache. * *

The cache does not store metadata. It remembers which providers can @@ -36,30 +39,31 @@ */ public class MetadataFactoryImpl implements MetadataFactory { @SuppressWarnings("unchecked") - public static final UnboundMetadata DUMMY = (rel, mq) -> null; + public static final UnboundMetadata<@Nullable Metadata> DUMMY = (rel, mq) -> null; private final LoadingCache< - Pair, Class>, UnboundMetadata> cache; + Pair, ? extends Class>, + UnboundMetadata<@Nullable Metadata>> cache; public MetadataFactoryImpl(RelMetadataProvider provider) { this.cache = CacheBuilder.newBuilder().build(loader(provider)); } - private static CacheLoader, Class>, - UnboundMetadata> loader(final RelMetadataProvider provider) { + private static CacheLoader, ? extends Class>, + UnboundMetadata<@Nullable Metadata>> loader(final RelMetadataProvider provider) { return CacheLoader.from(key -> { - final UnboundMetadata function = + final UnboundMetadata<@Nullable Metadata> function = provider.apply(key.left, key.right); // Return DUMMY, not null, so the cache knows to not ask again. return function != null ? function : DUMMY; }); } - @Override public M query(RelNode rel, RelMetadataQuery mq, + @Override public M query(RelNode rel, RelMetadataQuery mq, Class metadataClazz) { try { //noinspection unchecked - final Pair, Class> key = + final Pair, Class> key = (Pair) Pair.of(rel.getClass(), metadataClazz); final Metadata apply = cache.get(key).bind(rel, mq); return metadataClazz.cast(apply); diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java b/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java index a494f1a8119b..c559ef8a7c3a 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.rel.metadata; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Placeholder for null values. */ public enum NullSentinel { /** Placeholder for a null value. */ @@ -29,14 +31,14 @@ public enum NullSentinel { * therefore this request forms a cycle. */ ACTIVE; - public static Comparable mask(Comparable value) { + public static Comparable mask(@Nullable Comparable value) { if (value == null) { return INSTANCE; } return value; } - public static Object mask(Object value) { + public static Object mask(@Nullable Object value) { if (value == null) { return INSTANCE; } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java index aa2662e1569b..b9fdc5bb4b09 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java @@ -30,6 +30,8 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -228,7 +230,7 @@ private static boolean couldImplement(Method handlerMethod, Method method) { //~ Methods ---------------------------------------------------------------- - @Override public UnboundMetadata apply( + @Override public <@Nullable M extends @Nullable Metadata> @Nullable UnboundMetadata apply( Class relClass, Class metadataClass) { if (metadataClass == metadataClass0) { return apply(relClass); @@ -238,7 +240,7 @@ private static boolean couldImplement(Method handlerMethod, Method method) { } @SuppressWarnings({ "unchecked", "SuspiciousMethodCalls" }) - public UnboundMetadata apply( + public <@Nullable M extends @Nullable Metadata> @Nullable UnboundMetadata apply( Class relClass) { List> newSources = new ArrayList<>(); for (;;) { @@ -262,8 +264,9 @@ public UnboundMetadata apply( } } } - if (RelNode.class.isAssignableFrom(relClass.getSuperclass())) { - relClass = (Class) relClass.getSuperclass(); + Class superclass = relClass.getSuperclass(); + if (superclass != null && RelNode.class.isAssignableFrom(superclass)) { + relClass = (Class) superclass; } else { return null; } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelColumnOrigin.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelColumnOrigin.java index 2cdaaa5627b7..28aa144d5a4f 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelColumnOrigin.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelColumnOrigin.java @@ -18,6 +18,8 @@ import org.apache.calcite.plan.RelOptTable; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelColumnOrigin is a data structure describing one of the origins of an * output column produced by a relational expression. @@ -70,7 +72,7 @@ public boolean isDerived() { } // override Object - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof RelColumnOrigin)) { return false; } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java index 10ad7a04771a..effac5bb6b7c 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java @@ -47,6 +47,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; @@ -87,15 +89,15 @@ public class RelMdAllPredicates * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getAllPredicates(RelNode) */ - public RelOptPredicateList getAllPredicates(RelNode rel, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(RelNode rel, RelMetadataQuery mq) { return null; } - public RelOptPredicateList getAllPredicates(HepRelVertex rel, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(HepRelVertex rel, RelMetadataQuery mq) { return mq.getAllPredicates(rel.getCurrentRel()); } - public RelOptPredicateList getAllPredicates(RelSubset rel, + public @Nullable RelOptPredicateList getAllPredicates(RelSubset rel, RelMetadataQuery mq) { return mq.getAllPredicates(Util.first(rel.getBest(), rel.getOriginal())); } @@ -103,7 +105,7 @@ public RelOptPredicateList getAllPredicates(RelSubset rel, /** * Extract predicates for a table scan. */ - public RelOptPredicateList getAllPredicates(TableScan scan, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(TableScan scan, RelMetadataQuery mq) { final BuiltInMetadata.AllPredicates.Handler handler = scan.getTable().unwrap(BuiltInMetadata.AllPredicates.Handler.class); if (handler != null) { @@ -115,14 +117,14 @@ public RelOptPredicateList getAllPredicates(TableScan scan, RelMetadataQuery mq) /** * Extract predicates for a project. */ - public RelOptPredicateList getAllPredicates(Project project, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(Project project, RelMetadataQuery mq) { return mq.getAllPredicates(project.getInput()); } /** * Add the Filter condition to the list obtained from the input. */ - public RelOptPredicateList getAllPredicates(Filter filter, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(Filter filter, RelMetadataQuery mq) { final RelNode input = filter.getInput(); final RexBuilder rexBuilder = filter.getCluster().getRexBuilder(); final RexNode pred = filter.getCondition(); @@ -163,7 +165,7 @@ public RelOptPredicateList getAllPredicates(Filter filter, RelMetadataQuery mq) /** * Add the Join condition to the list obtained from the input. */ - public RelOptPredicateList getAllPredicates(Join join, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(Join join, RelMetadataQuery mq) { if (join.getJoinType().isOuterJoin()) { // We cannot map origin of this expression. return null; @@ -182,6 +184,9 @@ public RelOptPredicateList getAllPredicates(Join join, RelMetadataQuery mq) { } // Gather table references final Set tableRefs = mq.getTableReferences(input); + if (tableRefs == null) { + return null; + } if (input == join.getLeft()) { // Left input references remain unchanged for (RelTableRef leftRef : tableRefs) { @@ -248,21 +253,22 @@ public RelOptPredicateList getAllPredicates(Join join, RelMetadataQuery mq) { /** * Extract predicates for an Aggregate. */ - public RelOptPredicateList getAllPredicates(Aggregate agg, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(Aggregate agg, RelMetadataQuery mq) { return mq.getAllPredicates(agg.getInput()); } /** * Extract predicates for an TableModify. */ - public RelOptPredicateList getAllPredicates(TableModify tableModify, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(TableModify tableModify, + RelMetadataQuery mq) { return mq.getAllPredicates(tableModify.getInput()); } /** * Extract predicates for a Union. */ - public RelOptPredicateList getAllPredicates(Union union, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(Union union, RelMetadataQuery mq) { final RexBuilder rexBuilder = union.getCluster().getRexBuilder(); final Multimap, RelTableRef> qualifiedNamesToRefs = HashMultimap.create(); @@ -276,6 +282,9 @@ public RelOptPredicateList getAllPredicates(Union union, RelMetadataQuery mq) { } // Gather table references final Set tableRefs = mq.getTableReferences(input); + if (tableRefs == null) { + return null; + } if (i == 0) { // Left input references remain unchanged for (RelTableRef leftRef : tableRefs) { @@ -315,14 +324,14 @@ public RelOptPredicateList getAllPredicates(Union union, RelMetadataQuery mq) { /** * Extract predicates for a Sort. */ - public RelOptPredicateList getAllPredicates(Sort sort, RelMetadataQuery mq) { + public @Nullable RelOptPredicateList getAllPredicates(Sort sort, RelMetadataQuery mq) { return mq.getAllPredicates(sort.getInput()); } /** * Extract predicates for an Exchange. */ - public RelOptPredicateList getAllPredicates(Exchange exchange, + public @Nullable RelOptPredicateList getAllPredicates(Exchange exchange, RelMetadataQuery mq) { return mq.getAllPredicates(exchange.getInput()); } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java index dd54e86182b4..37a4fbd197e3 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java @@ -61,6 +61,8 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -111,21 +113,21 @@ private RelMdCollation() {} * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#collations(RelNode) */ - public ImmutableList collations(RelNode rel, + public @Nullable ImmutableList collations(RelNode rel, RelMetadataQuery mq) { return null; } - private static ImmutableList copyOf(Collection values) { + private static @Nullable ImmutableList copyOf(@Nullable Collection values) { return values == null ? null : ImmutableList.copyOf(values); } - public ImmutableList collations(Window rel, + public @Nullable ImmutableList collations(Window rel, RelMetadataQuery mq) { return copyOf(window(mq, rel.getInput(), rel.groups)); } - public ImmutableList collations(Match rel, + public @Nullable ImmutableList collations(Match rel, RelMetadataQuery mq) { return copyOf( match(mq, rel.getInput(), rel.getRowType(), rel.getPattern(), @@ -135,22 +137,22 @@ public ImmutableList collations(Match rel, rel.getOrderKeys(), rel.getInterval())); } - public ImmutableList collations(Filter rel, + public @Nullable ImmutableList collations(Filter rel, RelMetadataQuery mq) { return mq.collations(rel.getInput()); } - public ImmutableList collations(TableModify rel, + public @Nullable ImmutableList collations(TableModify rel, RelMetadataQuery mq) { return mq.collations(rel.getInput()); } - public ImmutableList collations(TableScan scan, + public @Nullable ImmutableList collations(TableScan scan, RelMetadataQuery mq) { return copyOf(table(scan.getTable())); } - public ImmutableList collations(EnumerableMergeJoin join, + public @Nullable ImmutableList collations(EnumerableMergeJoin join, RelMetadataQuery mq) { // In general a join is not sorted. But a merge join preserves the sort // order of the left and right sides. @@ -160,66 +162,66 @@ public ImmutableList collations(EnumerableMergeJoin join, join.getJoinType())); } - public ImmutableList collations(EnumerableHashJoin join, + public @Nullable ImmutableList collations(EnumerableHashJoin join, RelMetadataQuery mq) { return copyOf( RelMdCollation.enumerableHashJoin(mq, join.getLeft(), join.getRight(), join.getJoinType())); } - public ImmutableList collations(EnumerableNestedLoopJoin join, + public @Nullable ImmutableList collations(EnumerableNestedLoopJoin join, RelMetadataQuery mq) { return copyOf( RelMdCollation.enumerableNestedLoopJoin(mq, join.getLeft(), join.getRight(), join.getJoinType())); } - public ImmutableList collations(EnumerableCorrelate join, + public @Nullable ImmutableList collations(EnumerableCorrelate join, RelMetadataQuery mq) { return copyOf( RelMdCollation.enumerableCorrelate(mq, join.getLeft(), join.getRight(), join.getJoinType())); } - public ImmutableList collations(Sort sort, + public @Nullable ImmutableList collations(Sort sort, RelMetadataQuery mq) { return copyOf( RelMdCollation.sort(sort.getCollation())); } - public ImmutableList collations(SortExchange sort, + public @Nullable ImmutableList collations(SortExchange sort, RelMetadataQuery mq) { return copyOf( RelMdCollation.sort(sort.getCollation())); } - public ImmutableList collations(Project project, + public @Nullable ImmutableList collations(Project project, RelMetadataQuery mq) { return copyOf( project(mq, project.getInput(), project.getProjects())); } - public ImmutableList collations(Calc calc, + public @Nullable ImmutableList collations(Calc calc, RelMetadataQuery mq) { return copyOf(calc(mq, calc.getInput(), calc.getProgram())); } - public ImmutableList collations(Values values, + public @Nullable ImmutableList collations(Values values, RelMetadataQuery mq) { return copyOf( values(mq, values.getRowType(), values.getTuples())); } - public ImmutableList collations(JdbcToEnumerableConverter rel, + public @Nullable ImmutableList collations(JdbcToEnumerableConverter rel, RelMetadataQuery mq) { return mq.collations(rel.getInput()); } - public ImmutableList collations(HepRelVertex rel, + public @Nullable ImmutableList collations(HepRelVertex rel, RelMetadataQuery mq) { return mq.collations(rel.getCurrentRel()); } - public ImmutableList collations(RelSubset rel, + public @Nullable ImmutableList collations(RelSubset rel, RelMetadataQuery mq) { return copyOf( Objects.requireNonNull( @@ -230,13 +232,13 @@ public ImmutableList collations(RelSubset rel, /** Helper method to determine a * {@link org.apache.calcite.rel.core.TableScan}'s collation. */ - public static List table(RelOptTable table) { + public static @Nullable List table(RelOptTable table) { return table.getCollationList(); } /** Helper method to determine a * {@link org.apache.calcite.rel.core.Snapshot}'s collation. */ - public static List snapshot(RelMetadataQuery mq, RelNode input) { + public static @Nullable List snapshot(RelMetadataQuery mq, RelNode input) { return mq.collations(input); } @@ -248,19 +250,19 @@ public static List sort(RelCollation collation) { /** Helper method to determine a * {@link org.apache.calcite.rel.core.Filter}'s collation. */ - public static List filter(RelMetadataQuery mq, RelNode input) { + public static @Nullable List filter(RelMetadataQuery mq, RelNode input) { return mq.collations(input); } /** Helper method to determine a * limit's collation. */ - public static List limit(RelMetadataQuery mq, RelNode input) { + public static @Nullable List limit(RelMetadataQuery mq, RelNode input) { return mq.collations(input); } /** Helper method to determine a * {@link org.apache.calcite.rel.core.Calc}'s collation. */ - public static List calc(RelMetadataQuery mq, RelNode input, + public static @Nullable List calc(RelMetadataQuery mq, RelNode input, RexProgram program) { final List projects = program @@ -272,7 +274,7 @@ public static List calc(RelMetadataQuery mq, RelNode input, } /** Helper method to determine a {@link Project}'s collation. */ - public static List project(RelMetadataQuery mq, + public static @Nullable List project(RelMetadataQuery mq, RelNode input, List projects) { final NavigableSet collations = new TreeSet<>(); final List inputCollations = mq.collations(input); @@ -341,14 +343,14 @@ public static List project(RelMetadataQuery mq, * from each of its windows. Assuming (quite reasonably) that the * implementation does not re-order its input rows, then any collations of its * input are preserved. */ - public static List window(RelMetadataQuery mq, RelNode input, + public static @Nullable List window(RelMetadataQuery mq, RelNode input, ImmutableList groups) { return mq.collations(input); } /** Helper method to determine a * {@link org.apache.calcite.rel.core.Match}'s collation. */ - public static List match(RelMetadataQuery mq, RelNode input, + public static @Nullable List match(RelMetadataQuery mq, RelNode input, RelDataType rowType, RexNode pattern, boolean strictStart, boolean strictEnd, Map patternDefinitions, Map measures, @@ -441,7 +443,7 @@ public static Ordering> comparator( * key, the result preserves those collations too. * @deprecated Use {@link #mergeJoin(RelMetadataQuery, RelNode, RelNode, ImmutableIntList, ImmutableIntList, JoinRelType)} */ @Deprecated // to be removed before 2.0 - public static List mergeJoin(RelMetadataQuery mq, + public static @Nullable List mergeJoin(RelMetadataQuery mq, RelNode left, RelNode right, ImmutableIntList leftKeys, ImmutableIntList rightKeys) { return mergeJoin(mq, left, right, leftKeys, rightKeys, JoinRelType.INNER); @@ -452,7 +454,7 @@ public static List mergeJoin(RelMetadataQuery mq, * *

If the inputs are sorted on other keys in addition to the join * key, the result preserves those collations too. */ - public static List mergeJoin(RelMetadataQuery mq, + public static @Nullable List mergeJoin(RelMetadataQuery mq, RelNode left, RelNode right, ImmutableIntList leftKeys, ImmutableIntList rightKeys, JoinRelType joinType) { assert EnumerableMergeJoin.isMergeJoinSupported(joinType) @@ -462,11 +464,18 @@ public static List mergeJoin(RelMetadataQuery mq, if (!joinType.projectsRight()) { return leftCollations; } + if (leftCollations == null) { + return null; + } + + final ImmutableList rightCollations = mq.collations(right); + if (rightCollations == null) { + return leftCollations; + } final ImmutableList.Builder builder = ImmutableList.builder(); builder.addAll(leftCollations); - final ImmutableList rightCollations = mq.collations(right); final int leftFieldCount = left.getRowType().getFieldCount(); for (RelCollation collation : rightCollations) { builder.add(RelCollations.shift(collation, leftFieldCount)); @@ -477,7 +486,7 @@ public static List mergeJoin(RelMetadataQuery mq, /** * Returns the collation of {@link EnumerableHashJoin} based on its inputs and the join type. */ - public static List enumerableHashJoin(RelMetadataQuery mq, + public static @Nullable List enumerableHashJoin(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { if (joinType == JoinRelType.SEMI) { return enumerableSemiJoin(mq, left, right); @@ -490,32 +499,32 @@ public static List enumerableHashJoin(RelMetadataQuery mq, * Returns the collation of {@link EnumerableNestedLoopJoin} * based on its inputs and the join type. */ - public static List enumerableNestedLoopJoin(RelMetadataQuery mq, + public static @Nullable List enumerableNestedLoopJoin(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { return enumerableJoin0(mq, left, right, joinType); } - public static List enumerableCorrelate(RelMetadataQuery mq, + public static @Nullable List enumerableCorrelate(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { // The current implementation always preserve the sort order of the left input return mq.collations(left); } - public static List enumerableSemiJoin(RelMetadataQuery mq, + public static @Nullable List enumerableSemiJoin(RelMetadataQuery mq, RelNode left, RelNode right) { // The current implementation always preserve the sort order of the left input return mq.collations(left); } @SuppressWarnings("unused") - public static List enumerableBatchNestedLoopJoin(RelMetadataQuery mq, + public static @Nullable List enumerableBatchNestedLoopJoin(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { // The current implementation always preserve the sort order of the left input return mq.collations(left); } @SuppressWarnings("unused") - private static List enumerableJoin0(RelMetadataQuery mq, + private static @Nullable List enumerableJoin0(RelMetadataQuery mq, RelNode left, RelNode right, JoinRelType joinType) { // The current implementation can preserve the sort order of the left input if one of the // following conditions hold: diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java index dbe3aff4844c..dc7b4f306968 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java @@ -37,6 +37,9 @@ import org.apache.calcite.rex.RexVisitorImpl; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -62,7 +65,7 @@ private RelMdColumnOrigins() {} return BuiltInMetadata.ColumnOrigin.DEF; } - public Set getColumnOrigins(Aggregate rel, + public @Nullable Set getColumnOrigins(Aggregate rel, RelMetadataQuery mq, int iOutputColumn) { if (iOutputColumn < rel.getGroupCount()) { // get actual index of Group columns. @@ -86,7 +89,7 @@ public Set getColumnOrigins(Aggregate rel, return set; } - public Set getColumnOrigins(Join rel, RelMetadataQuery mq, + public @Nullable Set getColumnOrigins(Join rel, RelMetadataQuery mq, int iOutputColumn) { int nLeftColumns = rel.getLeft().getRowType().getFieldList().size(); Set set; @@ -110,7 +113,7 @@ public Set getColumnOrigins(Join rel, RelMetadataQuery mq, return set; } - public Set getColumnOrigins(SetOp rel, + public @Nullable Set getColumnOrigins(SetOp rel, RelMetadataQuery mq, int iOutputColumn) { final Set set = new HashSet<>(); for (RelNode input : rel.getInputs()) { @@ -123,7 +126,7 @@ public Set getColumnOrigins(SetOp rel, return set; } - public Set getColumnOrigins(Project rel, + public @Nullable Set getColumnOrigins(Project rel, final RelMetadataQuery mq, int iOutputColumn) { final RelNode input = rel.getInput(); RexNode rexNode = rel.getProjects().get(iOutputColumn); @@ -138,7 +141,7 @@ public Set getColumnOrigins(Project rel, return createDerivedColumnOrigins(set); } - public Set getColumnOrigins(Calc rel, + public @Nullable Set getColumnOrigins(Calc rel, final RelMetadataQuery mq, int iOutputColumn) { final RelNode input = rel.getInput(); final RexShuttle rexShuttle = new RexShuttle() { @@ -161,27 +164,27 @@ public Set getColumnOrigins(Calc rel, return createDerivedColumnOrigins(set); } - public Set getColumnOrigins(Filter rel, + public @Nullable Set getColumnOrigins(Filter rel, RelMetadataQuery mq, int iOutputColumn) { return mq.getColumnOrigins(rel.getInput(), iOutputColumn); } - public Set getColumnOrigins(Sort rel, RelMetadataQuery mq, + public @Nullable Set getColumnOrigins(Sort rel, RelMetadataQuery mq, int iOutputColumn) { return mq.getColumnOrigins(rel.getInput(), iOutputColumn); } - public Set getColumnOrigins(TableModify rel, RelMetadataQuery mq, + public @Nullable Set getColumnOrigins(TableModify rel, RelMetadataQuery mq, int iOutputColumn) { return mq.getColumnOrigins(rel.getInput(), iOutputColumn); } - public Set getColumnOrigins(Exchange rel, + public @Nullable Set getColumnOrigins(Exchange rel, RelMetadataQuery mq, int iOutputColumn) { return mq.getColumnOrigins(rel.getInput(), iOutputColumn); } - public Set getColumnOrigins(TableFunctionScan rel, + public @Nullable Set getColumnOrigins(TableFunctionScan rel, RelMetadataQuery mq, int iOutputColumn) { final Set set = new HashSet<>(); Set mappings = rel.getColumnMappings(); @@ -216,7 +219,7 @@ public Set getColumnOrigins(TableFunctionScan rel, } // Catch-all rule when none of the others apply. - public Set getColumnOrigins(RelNode rel, + public @Nullable Set getColumnOrigins(RelNode rel, RelMetadataQuery mq, int iOutputColumn) { // NOTE jvs 28-Mar-2006: We may get this wrong for a physical table // expression which supports projections. In that case, @@ -250,8 +253,8 @@ public Set getColumnOrigins(RelNode rel, return set; } - private Set createDerivedColumnOrigins( - Set inputSet) { + private @PolyNull Set createDerivedColumnOrigins( + @PolyNull Set inputSet) { if (inputSet == null) { return null; } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java index 77140d6dab1d..4be1e9eb4e0d 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java @@ -53,6 +53,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -83,7 +85,7 @@ public Boolean areColumnsUnique(TableScan rel, RelMetadataQuery mq, return rel.getTable().isKey(columns); } - public Boolean areColumnsUnique(Filter rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Filter rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls); @@ -105,7 +107,7 @@ public Boolean areColumnsUnique(Filter rel, RelMetadataQuery mq, * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#areColumnsUnique(RelNode, ImmutableBitSet, boolean) */ - public Boolean areColumnsUnique(RelNode rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(RelNode rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { // no information available return null; @@ -135,7 +137,7 @@ public Boolean areColumnsUnique(Intersect rel, RelMetadataQuery mq, return false; } - public Boolean areColumnsUnique(Minus rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Minus rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); if (areColumnsUnique((SetOp) rel, mq, columns, ignoreNulls)) { @@ -144,25 +146,25 @@ public Boolean areColumnsUnique(Minus rel, RelMetadataQuery mq, return mq.areColumnsUnique(rel.getInput(0), columns, ignoreNulls); } - public Boolean areColumnsUnique(Sort rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Sort rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls); } - public Boolean areColumnsUnique(TableModify rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(TableModify rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls); } - public Boolean areColumnsUnique(Exchange rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Exchange rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls); } - public Boolean areColumnsUnique(Correlate rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Correlate rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); switch (rel.getJoinType()) { @@ -199,7 +201,7 @@ public Boolean areColumnsUnique(Correlate rel, RelMetadataQuery mq, } } - public Boolean areColumnsUnique(Project rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Project rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); // LogicalProject maps a set of rows to a different set; @@ -213,7 +215,7 @@ public Boolean areColumnsUnique(Project rel, RelMetadataQuery mq, return areProjectColumnsUnique(rel, mq, columns, ignoreNulls, rel.getProjects()); } - public Boolean areColumnsUnique(Calc rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Calc rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); RexProgram program = rel.getProgram(); @@ -222,7 +224,7 @@ public Boolean areColumnsUnique(Calc rel, RelMetadataQuery mq, Util.transform(program.getProjectList(), program::expandLocalRef)); } - private Boolean areProjectColumnsUnique( + private @Nullable Boolean areProjectColumnsUnique( SingleRel rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls, List projExprs) { RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory(); @@ -270,7 +272,7 @@ private Boolean areProjectColumnsUnique( ignoreNulls); } - public Boolean areColumnsUnique(Join rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Join rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); if (columns.cardinality() == 0) { @@ -346,7 +348,7 @@ public Boolean areColumnsUnique(Join rel, RelMetadataQuery mq, throw new AssertionError(); } - public Boolean areColumnsUnique(Aggregate rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Aggregate rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { if (Aggregate.isSimple(rel) || ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); @@ -380,19 +382,19 @@ public Boolean areColumnsUnique(Values rel, RelMetadataQuery mq, return true; } - public Boolean areColumnsUnique(Converter rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(Converter rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); return mq.areColumnsUnique(rel.getInput(), columns, ignoreNulls); } - public Boolean areColumnsUnique(HepRelVertex rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(HepRelVertex rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); return mq.areColumnsUnique(rel.getCurrentRel(), columns, ignoreNulls); } - public Boolean areColumnsUnique(RelSubset rel, RelMetadataQuery mq, + public @Nullable Boolean areColumnsUnique(RelSubset rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) { columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq); for (RelNode rel2 : rel.getRels()) { @@ -463,8 +465,8 @@ private boolean simplyProjects(RelNode rel, ImmutableBitSet columns) { private static ImmutableBitSet decorateWithConstantColumnsFromPredicates( ImmutableBitSet checkingColumns, RelNode rel, RelMetadataQuery mq) { final RelOptPredicateList predicates = mq.getPulledUpPredicates(rel); - if (predicates != null) { - final Set constantIndexes = new HashSet(); + if (!RelOptPredicateList.isEmpty(predicates)) { + final Set constantIndexes = new HashSet<>(); predicates.constantMap.keySet().forEach(rex -> { if (rex instanceof RexInputRef) { constantIndexes.add(((RexInputRef) rex).getIndex()); diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java index ba6d4fa5db0e..12ff812ebb67 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java @@ -39,6 +39,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -71,8 +73,8 @@ protected RelMdDistinctRowCount() {} * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getDistinctRowCount(RelNode, ImmutableBitSet, RexNode) */ - public Double getDistinctRowCount(RelNode rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(RelNode rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { // REVIEW zfong 4/19/06 - Broadbase code does not take into // consideration selectivity of predicates passed in. Also, they // assume the rows are unique even if the table is not @@ -84,8 +86,8 @@ public Double getDistinctRowCount(RelNode rel, RelMetadataQuery mq, return null; } - public Double getDistinctRowCount(Union rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(Union rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { double rowCount = 0.0; int[] adjustments = new int[rel.getRowType().getFieldCount()]; RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); @@ -113,23 +115,23 @@ public Double getDistinctRowCount(Union rel, RelMetadataQuery mq, return rowCount; } - public Double getDistinctRowCount(Sort rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(Sort rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { return mq.getDistinctRowCount(rel.getInput(), groupKey, predicate); } - public Double getDistinctRowCount(TableModify rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(TableModify rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { return mq.getDistinctRowCount(rel.getInput(), groupKey, predicate); } - public Double getDistinctRowCount(Exchange rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(Exchange rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { return mq.getDistinctRowCount(rel.getInput(), groupKey, predicate); } - public Double getDistinctRowCount(Filter rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(Filter rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { if (predicate == null || predicate.isAlwaysTrue()) { if (groupKey.isEmpty()) { return 1D; @@ -147,14 +149,14 @@ public Double getDistinctRowCount(Filter rel, RelMetadataQuery mq, return mq.getDistinctRowCount(rel.getInput(), groupKey, unionPreds); } - public Double getDistinctRowCount(Join rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(Join rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { return RelMdUtil.getJoinDistinctRowCount(mq, rel, rel.getJoinType(), groupKey, predicate, false); } - public Double getDistinctRowCount(Aggregate rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(Aggregate rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { if (predicate == null || predicate.isAlwaysTrue()) { if (groupKey.isEmpty()) { return 1D; @@ -191,7 +193,7 @@ public Double getDistinctRowCount(Aggregate rel, RelMetadataQuery mq, } public Double getDistinctRowCount(Values rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + ImmutableBitSet groupKey, @Nullable RexNode predicate) { if (predicate == null || predicate.isAlwaysTrue()) { if (groupKey.isEmpty()) { return 1D; @@ -219,8 +221,8 @@ public Double getDistinctRowCount(Values rel, RelMetadataQuery mq, } } - public Double getDistinctRowCount(Project rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(Project rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { if (predicate == null || predicate.isAlwaysTrue()) { if (groupKey.isEmpty()) { return 1D; @@ -282,8 +284,8 @@ public Double getDistinctRowCount(Project rel, RelMetadataQuery mq, return RelMdUtil.numDistinctVals(distinctRowCount, mq.getRowCount(rel)); } - public Double getDistinctRowCount(RelSubset rel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public @Nullable Double getDistinctRowCount(RelSubset rel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { final RelNode best = rel.getBest(); if (best != null) { return mq.getDistinctRowCount(best, groupKey, predicate); diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java index 82b5479ad7a3..b524efbb7632 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java @@ -41,6 +41,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -90,7 +92,7 @@ public RelDistribution distribution(TableModify rel, RelMetadataQuery mq) { return mq.distribution(rel.getInput()); } - public RelDistribution distribution(TableScan scan, RelMetadataQuery mq) { + public @Nullable RelDistribution distribution(TableScan scan, RelMetadataQuery mq) { return table(scan.getTable()); } @@ -114,7 +116,7 @@ public RelDistribution distribution(HepRelVertex rel, RelMetadataQuery mq) { /** Helper method to determine a * {@link TableScan}'s distribution. */ - public static RelDistribution table(RelOptTable table) { + public static @Nullable RelDistribution table(RelOptTable table) { return table.getDistribution(); } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java index 6d73fad5165f..67955228a029 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlExplainLevel; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelMdExplainVisibility supplies a default implementation of * {@link RelMetadataQuery#isVisibleInExplain} for the standard logical algebra. @@ -47,7 +49,7 @@ private RelMdExplainVisibility() {} * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#isVisibleInExplain(RelNode, SqlExplainLevel) */ - public Boolean isVisibleInExplain(RelNode rel, RelMetadataQuery mq, + public @Nullable Boolean isVisibleInExplain(RelNode rel, RelMetadataQuery mq, SqlExplainLevel explainLevel) { // no information available return null; diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java index 41189a022479..8e47bc2d54f6 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java @@ -49,6 +49,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.KeyFor; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -59,7 +62,8 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; + +import static java.util.Objects.requireNonNull; /** * Default implementation of @@ -92,17 +96,17 @@ protected RelMdExpressionLineage() {} } // Catch-all rule when none of the others apply. - public Set getExpressionLineage(RelNode rel, + public @Nullable Set getExpressionLineage(RelNode rel, RelMetadataQuery mq, RexNode outputExpression) { return null; } - public Set getExpressionLineage(HepRelVertex rel, RelMetadataQuery mq, + public @Nullable Set getExpressionLineage(HepRelVertex rel, RelMetadataQuery mq, RexNode outputExpression) { return mq.getExpressionLineage(rel.getCurrentRel(), outputExpression); } - public Set getExpressionLineage(RelSubset rel, + public @Nullable Set getExpressionLineage(RelSubset rel, RelMetadataQuery mq, RexNode outputExpression) { return mq.getExpressionLineage(Util.first(rel.getBest(), rel.getOriginal()), outputExpression); @@ -114,7 +118,7 @@ public Set getExpressionLineage(RelSubset rel, *

We extract the fields referenced by the expression and we express them * using {@link RexTableInputRef}. */ - public Set getExpressionLineage(TableScan rel, + public @Nullable Set getExpressionLineage(TableScan rel, RelMetadataQuery mq, RexNode outputExpression) { final RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); @@ -141,7 +145,7 @@ public Set getExpressionLineage(TableScan rel, *

If the expression references grouping sets or aggregate function * results, we cannot extract the lineage and we return null. */ - public Set getExpressionLineage(Aggregate rel, + public @Nullable Set getExpressionLineage(Aggregate rel, RelMetadataQuery mq, RexNode outputExpression) { final RelNode input = rel.getInput(); final RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); @@ -179,7 +183,7 @@ public Set getExpressionLineage(Aggregate rel, * *

We only extract the lineage for INNER joins. */ - public Set getExpressionLineage(Join rel, RelMetadataQuery mq, + public @Nullable Set getExpressionLineage(Join rel, RelMetadataQuery mq, RexNode outputExpression) { final RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); final RelNode leftInput = rel.getLeft(); @@ -285,7 +289,7 @@ public Set getExpressionLineage(Join rel, RelMetadataQuery mq, *

For Union operator, we might be able to extract multiple origins for the * references in the given expression. */ - public Set getExpressionLineage(Union rel, RelMetadataQuery mq, + public @Nullable Set getExpressionLineage(Union rel, RelMetadataQuery mq, RexNode outputExpression) { final RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); @@ -349,7 +353,7 @@ public Set getExpressionLineage(Union rel, RelMetadataQuery mq, /** * Expression lineage from Project. */ - public Set getExpressionLineage(Project rel, + public @Nullable Set getExpressionLineage(Project rel, final RelMetadataQuery mq, RexNode outputExpression) { final RelNode input = rel.getInput(); final RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); @@ -377,7 +381,7 @@ public Set getExpressionLineage(Project rel, /** * Expression lineage from Filter. */ - public Set getExpressionLineage(Filter rel, + public @Nullable Set getExpressionLineage(Filter rel, RelMetadataQuery mq, RexNode outputExpression) { return mq.getExpressionLineage(rel.getInput(), outputExpression); } @@ -385,7 +389,7 @@ public Set getExpressionLineage(Filter rel, /** * Expression lineage from Sort. */ - public Set getExpressionLineage(Sort rel, RelMetadataQuery mq, + public @Nullable Set getExpressionLineage(Sort rel, RelMetadataQuery mq, RexNode outputExpression) { return mq.getExpressionLineage(rel.getInput(), outputExpression); } @@ -393,7 +397,7 @@ public Set getExpressionLineage(Sort rel, RelMetadataQuery mq, /** * Expression lineage from TableModify. */ - public Set getExpressionLineage(TableModify rel, RelMetadataQuery mq, + public @Nullable Set getExpressionLineage(TableModify rel, RelMetadataQuery mq, RexNode outputExpression) { return mq.getExpressionLineage(rel.getInput(), outputExpression); } @@ -401,7 +405,7 @@ public Set getExpressionLineage(TableModify rel, RelMetadataQuery mq, /** * Expression lineage from Exchange. */ - public Set getExpressionLineage(Exchange rel, + public @Nullable Set getExpressionLineage(Exchange rel, RelMetadataQuery mq, RexNode outputExpression) { return mq.getExpressionLineage(rel.getInput(), outputExpression); } @@ -438,7 +442,7 @@ public Set getExpressionLineage(Exchange rel, private static Set createAllPossibleExpressions(RexBuilder rexBuilder, RexNode expr, ImmutableBitSet predFieldsUsed, Map> mapping, Map singleMapping) { - final RexInputRef inputRef = mapping.keySet().iterator().next(); + final @KeyFor("mapping") RexInputRef inputRef = mapping.keySet().iterator().next(); final Set replacements = mapping.remove(inputRef); Set result = new HashSet<>(); assert !replacements.isEmpty(); @@ -484,7 +488,9 @@ private static class RexReplacer extends RexShuttle { } @Override public RexNode visitInputRef(RexInputRef inputRef) { - return replacementValues.get(inputRef); + return requireNonNull( + replacementValues.get(inputRef), + () -> "no replacement found for inputRef " + inputRef); } } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdLowerBoundCost.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdLowerBoundCost.java index 2a18df5a5aab..97f632ad54c6 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdLowerBoundCost.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdLowerBoundCost.java @@ -23,6 +23,8 @@ import org.apache.calcite.rel.metadata.BuiltInMetadata.LowerBoundCost; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Default implementations of the * {@link BuiltInMetadata.LowerBoundCost} @@ -44,7 +46,7 @@ protected RelMdLowerBoundCost() {} return BuiltInMetadata.LowerBoundCost.DEF; } - public RelOptCost getLowerBoundCost(RelSubset subset, + public @Nullable RelOptCost getLowerBoundCost(RelSubset subset, RelMetadataQuery mq, VolcanoPlanner planner) { if (planner.isLogical(subset)) { @@ -55,7 +57,7 @@ public RelOptCost getLowerBoundCost(RelSubset subset, return subset.getWinnerCost(); } - public RelOptCost getLowerBoundCost(RelNode node, + public @Nullable RelOptCost getLowerBoundCost(RelNode node, RelMetadataQuery mq, VolcanoPlanner planner) { if (planner.isLogical(node)) { // currently only support physical, will improve in the future diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java index dcf59a639c02..31c632b5830c 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java @@ -39,6 +39,8 @@ import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelMdMaxRowCount supplies a default implementation of * {@link RelMetadataQuery#getMaxRowCount} for the standard logical algebra. @@ -55,7 +57,7 @@ public class RelMdMaxRowCount return BuiltInMetadata.MaxRowCount.DEF; } - public Double getMaxRowCount(Union rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Union rel, RelMetadataQuery mq) { double rowCount = 0.0; for (RelNode input : rel.getInputs()) { Double partialRowCount = mq.getMaxRowCount(input); @@ -67,7 +69,7 @@ public Double getMaxRowCount(Union rel, RelMetadataQuery mq) { return rowCount; } - public Double getMaxRowCount(Intersect rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Intersect rel, RelMetadataQuery mq) { // max row count is the smallest of the inputs Double rowCount = null; for (RelNode input : rel.getInputs()) { @@ -80,26 +82,26 @@ public Double getMaxRowCount(Intersect rel, RelMetadataQuery mq) { return rowCount; } - public Double getMaxRowCount(Minus rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Minus rel, RelMetadataQuery mq) { return mq.getMaxRowCount(rel.getInput(0)); } - public Double getMaxRowCount(Filter rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Filter rel, RelMetadataQuery mq) { if (rel.getCondition().isAlwaysFalse()) { return 0D; } return mq.getMaxRowCount(rel.getInput()); } - public Double getMaxRowCount(Calc rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Calc rel, RelMetadataQuery mq) { return mq.getMaxRowCount(rel.getInput()); } - public Double getMaxRowCount(Project rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Project rel, RelMetadataQuery mq) { return mq.getMaxRowCount(rel.getInput()); } - public Double getMaxRowCount(Exchange rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Exchange rel, RelMetadataQuery mq) { return mq.getMaxRowCount(rel.getInput()); } @@ -137,7 +139,7 @@ public Double getMaxRowCount(EnumerableLimit rel, RelMetadataQuery mq) { return rowCount; } - public Double getMaxRowCount(Aggregate rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Aggregate rel, RelMetadataQuery mq) { if (rel.getGroupSet().isEmpty()) { // Aggregate with no GROUP BY always returns 1 row (even on empty table). return 1D; @@ -147,7 +149,7 @@ public Double getMaxRowCount(Aggregate rel, RelMetadataQuery mq) { if (rel.getGroupType() == Aggregate.Group.SIMPLE) { final RelOptPredicateList predicateList = mq.getPulledUpPredicates(rel.getInput()); - if (predicateList != null + if (!RelOptPredicateList.isEmpty(predicateList) && allGroupKeysAreConstant(rel, predicateList)) { return 1D; } @@ -171,7 +173,7 @@ private static boolean allGroupKeysAreConstant(Aggregate aggregate, return true; } - public Double getMaxRowCount(Join rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(Join rel, RelMetadataQuery mq) { Double left = mq.getMaxRowCount(rel.getLeft()); Double right = mq.getMaxRowCount(rel.getRight()); if (left == null || right == null) { @@ -197,7 +199,7 @@ public Double getMaxRowCount(Values values, RelMetadataQuery mq) { return (double) values.getTuples().size(); } - public Double getMaxRowCount(TableModify rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(TableModify rel, RelMetadataQuery mq) { return mq.getMaxRowCount(rel.getInput()); } @@ -218,7 +220,7 @@ public Double getMaxRowCount(RelSubset rel, RelMetadataQuery mq) { } // Catch-all rule when none of the others apply. - public Double getMaxRowCount(RelNode rel, RelMetadataQuery mq) { + public @Nullable Double getMaxRowCount(RelNode rel, RelMetadataQuery mq) { return null; } } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java index 5d2d215767fe..a1019f0d2027 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Default implementations of the * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory} @@ -52,7 +54,7 @@ protected RelMdMemory() {} * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#memory */ - public Double memory(RelNode rel, RelMetadataQuery mq) { + public @Nullable Double memory(RelNode rel, RelMetadataQuery mq) { return null; } @@ -62,7 +64,7 @@ public Double memory(RelNode rel, RelMetadataQuery mq) { * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#memory */ - public Double cumulativeMemoryWithinPhase(RelNode rel, RelMetadataQuery mq) { + public @Nullable Double cumulativeMemoryWithinPhase(RelNode rel, RelMetadataQuery mq) { Double nullable = mq.memory(rel); if (nullable == null) { return null; @@ -90,7 +92,7 @@ public Double cumulativeMemoryWithinPhase(RelNode rel, RelMetadataQuery mq) { * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#cumulativeMemoryWithinPhaseSplit */ - public Double cumulativeMemoryWithinPhaseSplit(RelNode rel, + public @Nullable Double cumulativeMemoryWithinPhaseSplit(RelNode rel, RelMetadataQuery mq) { final Double memoryWithinPhase = mq.cumulativeMemoryWithinPhase(rel); final Integer splitCount = mq.splitCount(rel); diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMinRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMinRowCount.java index 2bdce84dc5c2..eb4a1d9581c2 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMinRowCount.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMinRowCount.java @@ -37,6 +37,8 @@ import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelMdMinRowCount supplies a default implementation of * {@link RelMetadataQuery#getMinRowCount} for the standard logical algebra. @@ -81,7 +83,7 @@ public Double getMinRowCount(Filter rel, RelMetadataQuery mq) { return 0d; // no lower bound } - public Double getMinRowCount(Calc rel, RelMetadataQuery mq) { + public @Nullable Double getMinRowCount(Calc rel, RelMetadataQuery mq) { if (rel.getProgram().getCondition() != null) { // no lower bound return 0d; @@ -90,15 +92,15 @@ public Double getMinRowCount(Calc rel, RelMetadataQuery mq) { } } - public Double getMinRowCount(Project rel, RelMetadataQuery mq) { + public @Nullable Double getMinRowCount(Project rel, RelMetadataQuery mq) { return mq.getMinRowCount(rel.getInput()); } - public Double getMinRowCount(Exchange rel, RelMetadataQuery mq) { + public @Nullable Double getMinRowCount(Exchange rel, RelMetadataQuery mq) { return mq.getMinRowCount(rel.getInput()); } - public Double getMinRowCount(TableModify rel, RelMetadataQuery mq) { + public @Nullable Double getMinRowCount(TableModify rel, RelMetadataQuery mq) { return mq.getMinRowCount(rel.getInput()); } @@ -178,7 +180,7 @@ public Double getMinRowCount(RelSubset rel, RelMetadataQuery mq) { } // Catch-all rule when none of the others apply. - public Double getMinRowCount(RelNode rel, RelMetadataQuery mq) { + public @Nullable Double getMinRowCount(RelNode rel, RelMetadataQuery mq) { return null; } } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java index f47db4ac151f..e7e7a2add0f4 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java @@ -42,6 +42,8 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelMdNodeTypeCount supplies a default implementation of * {@link RelMetadataQuery#getNodeTypes} for the standard logical algebra. @@ -64,107 +66,107 @@ public class RelMdNodeTypes * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getNodeTypes(RelNode) */ - public Multimap, RelNode> getNodeTypes(RelNode rel, + public @Nullable Multimap, RelNode> getNodeTypes(RelNode rel, RelMetadataQuery mq) { return getNodeTypes(rel, RelNode.class, mq); } - public Multimap, RelNode> getNodeTypes(HepRelVertex rel, + public @Nullable Multimap, RelNode> getNodeTypes(HepRelVertex rel, RelMetadataQuery mq) { return mq.getNodeTypes(rel.getCurrentRel()); } - public Multimap, RelNode> getNodeTypes(RelSubset rel, + public @Nullable Multimap, RelNode> getNodeTypes(RelSubset rel, RelMetadataQuery mq) { return mq.getNodeTypes(Util.first(rel.getBest(), rel.getOriginal())); } - public Multimap, RelNode> getNodeTypes(Union rel, + public @Nullable Multimap, RelNode> getNodeTypes(Union rel, RelMetadataQuery mq) { return getNodeTypes(rel, Union.class, mq); } - public Multimap, RelNode> getNodeTypes(Intersect rel, + public @Nullable Multimap, RelNode> getNodeTypes(Intersect rel, RelMetadataQuery mq) { return getNodeTypes(rel, Intersect.class, mq); } - public Multimap, RelNode> getNodeTypes(Minus rel, + public @Nullable Multimap, RelNode> getNodeTypes(Minus rel, RelMetadataQuery mq) { return getNodeTypes(rel, Minus.class, mq); } - public Multimap, RelNode> getNodeTypes(Filter rel, + public @Nullable Multimap, RelNode> getNodeTypes(Filter rel, RelMetadataQuery mq) { return getNodeTypes(rel, Filter.class, mq); } - public Multimap, RelNode> getNodeTypes(Calc rel, + public @Nullable Multimap, RelNode> getNodeTypes(Calc rel, RelMetadataQuery mq) { return getNodeTypes(rel, Calc.class, mq); } - public Multimap, RelNode> getNodeTypes(Project rel, + public @Nullable Multimap, RelNode> getNodeTypes(Project rel, RelMetadataQuery mq) { return getNodeTypes(rel, Project.class, mq); } - public Multimap, RelNode> getNodeTypes(Sort rel, + public @Nullable Multimap, RelNode> getNodeTypes(Sort rel, RelMetadataQuery mq) { return getNodeTypes(rel, Sort.class, mq); } - public Multimap, RelNode> getNodeTypes(Join rel, + public @Nullable Multimap, RelNode> getNodeTypes(Join rel, RelMetadataQuery mq) { return getNodeTypes(rel, Join.class, mq); } - public Multimap, RelNode> getNodeTypes(Aggregate rel, + public @Nullable Multimap, RelNode> getNodeTypes(Aggregate rel, RelMetadataQuery mq) { return getNodeTypes(rel, Aggregate.class, mq); } - public Multimap, RelNode> getNodeTypes(TableScan rel, + public @Nullable Multimap, RelNode> getNodeTypes(TableScan rel, RelMetadataQuery mq) { return getNodeTypes(rel, TableScan.class, mq); } - public Multimap, RelNode> getNodeTypes(Values rel, + public @Nullable Multimap, RelNode> getNodeTypes(Values rel, RelMetadataQuery mq) { return getNodeTypes(rel, Values.class, mq); } - public Multimap, RelNode> getNodeTypes(TableModify rel, + public @Nullable Multimap, RelNode> getNodeTypes(TableModify rel, RelMetadataQuery mq) { return getNodeTypes(rel, TableModify.class, mq); } - public Multimap, RelNode> getNodeTypes(Exchange rel, + public @Nullable Multimap, RelNode> getNodeTypes(Exchange rel, RelMetadataQuery mq) { return getNodeTypes(rel, Exchange.class, mq); } - public Multimap, RelNode> getNodeTypes(Sample rel, + public @Nullable Multimap, RelNode> getNodeTypes(Sample rel, RelMetadataQuery mq) { return getNodeTypes(rel, Sample.class, mq); } - public Multimap, RelNode> getNodeTypes(Correlate rel, + public @Nullable Multimap, RelNode> getNodeTypes(Correlate rel, RelMetadataQuery mq) { return getNodeTypes(rel, Correlate.class, mq); } - public Multimap, RelNode> getNodeTypes(Window rel, + public @Nullable Multimap, RelNode> getNodeTypes(Window rel, RelMetadataQuery mq) { return getNodeTypes(rel, Window.class, mq); } - public Multimap, RelNode> getNodeTypes(Match rel, + public @Nullable Multimap, RelNode> getNodeTypes(Match rel, RelMetadataQuery mq) { return getNodeTypes(rel, Match.class, mq); } - private static Multimap, RelNode> getNodeTypes(RelNode rel, + private static @Nullable Multimap, RelNode> getNodeTypes(RelNode rel, Class c, RelMetadataQuery mq) { final Multimap, RelNode> nodeTypeCount = ArrayListMultimap.create(); for (RelNode input : rel.getInputs()) { diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java index 081bac0b9e5a..93a6581ce814 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java @@ -26,6 +26,9 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.List; /** @@ -58,7 +61,7 @@ private RelMdPercentageOriginalRows() {} return BuiltInMetadata.PercentageOriginalRows.DEF; } - public Double getPercentageOriginalRows(Aggregate rel, RelMetadataQuery mq) { + public @Nullable Double getPercentageOriginalRows(Aggregate rel, RelMetadataQuery mq) { // REVIEW jvs 28-Mar-2006: The assumption here seems to be that // aggregation does not apply any filtering, so it does not modify the // percentage. That's very much oversimplified. @@ -97,7 +100,7 @@ public Double getPercentageOriginalRows(Union rel, RelMetadataQuery mq) { return quotientForPercentage(numerator, denominator); } - public Double getPercentageOriginalRows(Join rel, RelMetadataQuery mq) { + public @Nullable Double getPercentageOriginalRows(Join rel, RelMetadataQuery mq) { // Assume any single-table filter conditions have already // been pushed down. @@ -118,7 +121,7 @@ public Double getPercentageOriginalRows(Join rel, RelMetadataQuery mq) { } // Catch-all rule when none of the others apply. - public Double getPercentageOriginalRows(RelNode rel, RelMetadataQuery mq) { + public @Nullable Double getPercentageOriginalRows(RelNode rel, RelMetadataQuery mq) { if (rel.getInputs().size() > 1) { // No generic formula available for multiple inputs. return null; @@ -155,7 +158,7 @@ public Double getPercentageOriginalRows(RelNode rel, RelMetadataQuery mq) { } // Ditto for getNonCumulativeCost - public RelOptCost getCumulativeCost(RelNode rel, RelMetadataQuery mq) { + public @Nullable RelOptCost getCumulativeCost(RelNode rel, RelMetadataQuery mq) { RelOptCost cost = mq.getNonCumulativeCost(rel); List inputs = rel.getInputs(); for (RelNode input : inputs) { @@ -164,7 +167,7 @@ public RelOptCost getCumulativeCost(RelNode rel, RelMetadataQuery mq) { return cost; } - public RelOptCost getCumulativeCost(EnumerableInterpreter rel, + public @Nullable RelOptCost getCumulativeCost(EnumerableInterpreter rel, RelMetadataQuery mq) { return mq.getNonCumulativeCost(rel); } @@ -174,9 +177,9 @@ public RelOptCost getNonCumulativeCost(RelNode rel, RelMetadataQuery mq) { return rel.computeSelfCost(rel.getCluster().getPlanner(), mq); } - private static Double quotientForPercentage( - Double numerator, - Double denominator) { + private static @PolyNull Double quotientForPercentage( + @PolyNull Double numerator, + @PolyNull Double denominator) { if ((numerator == null) || (denominator == null)) { return null; } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java index f725293bbf61..ca39cc869765 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java @@ -30,6 +30,8 @@ import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -52,27 +54,27 @@ private RelMdPopulationSize() {} return BuiltInMetadata.PopulationSize.DEF; } - public Double getPopulationSize(Filter rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(Filter rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { return mq.getPopulationSize(rel.getInput(), groupKey); } - public Double getPopulationSize(Sort rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(Sort rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { return mq.getPopulationSize(rel.getInput(), groupKey); } - public Double getPopulationSize(Exchange rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(Exchange rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { return mq.getPopulationSize(rel.getInput(), groupKey); } - public Double getPopulationSize(TableModify rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(TableModify rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { return mq.getPopulationSize(rel.getInput(), groupKey); } - public Double getPopulationSize(Union rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(Union rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { double population = 0.0; for (RelNode input : rel.getInputs()) { @@ -85,12 +87,12 @@ public Double getPopulationSize(Union rel, RelMetadataQuery mq, return population; } - public Double getPopulationSize(Join rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(Join rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { return RelMdUtil.getJoinPopulationSize(mq, rel, groupKey); } - public Double getPopulationSize(Aggregate rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(Aggregate rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { ImmutableBitSet.Builder childKey = ImmutableBitSet.builder(); RelMdUtil.setAggChildKeys(groupKey, rel, childKey); @@ -103,7 +105,7 @@ public Double getPopulationSize(Values rel, RelMetadataQuery mq, return rel.estimateRowCount(mq) / 2; } - public Double getPopulationSize(Project rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(Project rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { ImmutableBitSet.Builder baseCols = ImmutableBitSet.builder(); ImmutableBitSet.Builder projCols = ImmutableBitSet.builder(); @@ -143,7 +145,7 @@ public Double getPopulationSize(Project rel, RelMetadataQuery mq, * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getPopulationSize(RelNode, ImmutableBitSet) */ - public Double getPopulationSize(RelNode rel, RelMetadataQuery mq, + public @Nullable Double getPopulationSize(RelNode rel, RelMetadataQuery mq, ImmutableBitSet groupKey) { // if the keys are unique, return the row count; otherwise, we have // no further information on which to return any legitimate value diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java index bfb7ac3072ff..479fcb5494c4 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java @@ -64,6 +64,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -73,12 +75,12 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.stream.Collectors; -import javax.annotation.Nonnull; /** * Utility to infer Predicates that are applicable above a RelNode. @@ -543,13 +545,13 @@ static class JoinConditionBasedPredicateInference { final Map exprFields; final Set allExprs; final Set equalityPredicates; - final RexNode leftChildPredicates; - final RexNode rightChildPredicates; + final @Nullable RexNode leftChildPredicates; + final @Nullable RexNode rightChildPredicates; final RexSimplify simplify; @SuppressWarnings("JdkObsolete") - JoinConditionBasedPredicateInference(Join joinRel, RexNode leftPredicates, - RexNode rightPredicates, RexSimplify simplify) { + JoinConditionBasedPredicateInference(Join joinRel, @Nullable RexNode leftPredicates, + @Nullable RexNode rightPredicates, RexSimplify simplify) { super(); this.joinRel = joinRel; this.simplify = simplify; @@ -712,11 +714,11 @@ public RelOptPredicateList inferPredicates( } } - public RexNode left() { + public @Nullable RexNode left() { return leftChildPredicates; } - public RexNode right() { + public @Nullable RexNode right() { return rightChildPredicates; } @@ -770,7 +772,7 @@ private void markAsEquivalent(int p1, int p2) { b.set(p1); } - @Nonnull RexNode compose(RexBuilder rexBuilder, Iterable exprs) { + RexNode compose(RexBuilder rexBuilder, Iterable exprs) { exprs = Linq4j.asEnumerable(exprs).where(Objects::nonNull); return RexUtil.composeConjunction(rexBuilder, exprs); } @@ -834,7 +836,7 @@ class ExprsItr implements Iterator { final int[] columns; final BitSet[] columnSets; final int[] iterationIdx; - Mapping nextMapping; + @Nullable Mapping nextMapping; boolean firstCall; @SuppressWarnings("JdkObsolete") @@ -863,6 +865,9 @@ class ExprsItr implements Iterator { } @Override public Mapping next() { + if (nextMapping == null) { + throw new NoSuchElementException(); + } return nextMapping; } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java index 02c45cfc31fa..cdd35350807c 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java @@ -41,6 +41,8 @@ import org.apache.calcite.util.NumberUtil; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * RelMdRowCount supplies a default implementation of * {@link RelMetadataQuery#getRowCount} for the standard logical algebra. @@ -63,12 +65,12 @@ public class RelMdRowCount * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getRowCount(RelNode) */ - public Double getRowCount(RelNode rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(RelNode rel, RelMetadataQuery mq) { return rel.estimateRowCount(mq); } @SuppressWarnings("CatchAndPrintStackTrace") - public Double getRowCount(RelSubset subset, RelMetadataQuery mq) { + public @Nullable Double getRowCount(RelSubset subset, RelMetadataQuery mq) { if (!Bug.CALCITE_1048_FIXED) { return mq.getRowCount(Util.first(subset.getBest(), subset.getOriginal())); } @@ -85,7 +87,7 @@ public Double getRowCount(RelSubset subset, RelMetadataQuery mq) { return Util.first(v, 1e6d); // if set is empty, estimate large } - public Double getRowCount(Union rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(Union rel, RelMetadataQuery mq) { double rowCount = 0.0; for (RelNode input : rel.getInputs()) { Double partialRowCount = mq.getRowCount(input); @@ -100,7 +102,7 @@ public Double getRowCount(Union rel, RelMetadataQuery mq) { return rowCount; } - public Double getRowCount(Intersect rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(Intersect rel, RelMetadataQuery mq) { Double rowCount = null; for (RelNode input : rel.getInputs()) { Double partialRowCount = mq.getRowCount(input); @@ -116,7 +118,7 @@ public Double getRowCount(Intersect rel, RelMetadataQuery mq) { } } - public Double getRowCount(Minus rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(Minus rel, RelMetadataQuery mq) { Double rowCount = null; for (RelNode input : rel.getInputs()) { Double partialRowCount = mq.getRowCount(input); @@ -137,11 +139,11 @@ public Double getRowCount(Calc rel, RelMetadataQuery mq) { return RelMdUtil.estimateFilteredRows(rel.getInput(), rel.getProgram(), mq); } - public Double getRowCount(Project rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(Project rel, RelMetadataQuery mq) { return mq.getRowCount(rel.getInput()); } - public Double getRowCount(Sort rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(Sort rel, RelMetadataQuery mq) { Double rowCount = mq.getRowCount(rel.getInput()); if (rowCount == null) { return null; @@ -164,7 +166,7 @@ public Double getRowCount(Sort rel, RelMetadataQuery mq) { return rowCount; } - public Double getRowCount(EnumerableLimit rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(EnumerableLimit rel, RelMetadataQuery mq) { Double rowCount = mq.getRowCount(rel.getInput()); if (rowCount == null) { return null; @@ -188,11 +190,11 @@ public Double getRowCount(EnumerableLimit rel, RelMetadataQuery mq) { } // Covers Converter, Interpreter - public Double getRowCount(SingleRel rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(SingleRel rel, RelMetadataQuery mq) { return mq.getRowCount(rel.getInput()); } - public Double getRowCount(Join rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(Join rel, RelMetadataQuery mq) { return RelMdUtil.getJoinRowCount(mq, rel, rel.getCondition()); } @@ -220,11 +222,11 @@ public Double getRowCount(Values rel, RelMetadataQuery mq) { return rel.estimateRowCount(mq); } - public Double getRowCount(Exchange rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(Exchange rel, RelMetadataQuery mq) { return mq.getRowCount(rel.getInput()); } - public Double getRowCount(TableModify rel, RelMetadataQuery mq) { + public @Nullable Double getRowCount(TableModify rel, RelMetadataQuery mq) { return mq.getRowCount(rel.getInput()); } } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java index 192cca054752..b9c4b8c083d7 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java @@ -35,6 +35,8 @@ import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -59,8 +61,8 @@ protected RelMdSelectivity() { return BuiltInMetadata.Selectivity.DEF; } - public Double getSelectivity(Union rel, RelMetadataQuery mq, - RexNode predicate) { + public @Nullable Double getSelectivity(Union rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { if ((rel.getInputs().size() == 0) || (predicate == null)) { return 1.0; } @@ -95,18 +97,18 @@ public Double getSelectivity(Union rel, RelMetadataQuery mq, return sumSelectedRows / sumRows; } - public Double getSelectivity(Sort rel, RelMetadataQuery mq, - RexNode predicate) { + public @Nullable Double getSelectivity(Sort rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { return mq.getSelectivity(rel.getInput(), predicate); } - public Double getSelectivity(TableModify rel, RelMetadataQuery mq, - RexNode predicate) { + public @Nullable Double getSelectivity(TableModify rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { return mq.getSelectivity(rel.getInput(), predicate); } - public Double getSelectivity(Filter rel, RelMetadataQuery mq, - RexNode predicate) { + public @Nullable Double getSelectivity(Filter rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { // Take the difference between the predicate passed in and the // predicate in the filter's condition, so we don't apply the // selectivity of the filter twice. If no predicate is passed in, @@ -122,7 +124,8 @@ public Double getSelectivity(Filter rel, RelMetadataQuery mq, } } - public Double getSelectivity(Calc rel, RelMetadataQuery mq, RexNode predicate) { + public @Nullable Double getSelectivity(Calc rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { final RexProgram rexProgram = rel.getProgram(); final RexLocalRef programCondition = rexProgram.getCondition(); if (programCondition == null) { @@ -136,7 +139,8 @@ public Double getSelectivity(Calc rel, RelMetadataQuery mq, RexNode predicate) { } } - public Double getSelectivity(Join rel, RelMetadataQuery mq, RexNode predicate) { + public @Nullable Double getSelectivity(Join rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { if (!rel.isSemiJoin()) { return getSelectivity((RelNode) rel, mq, predicate); } @@ -155,8 +159,8 @@ public Double getSelectivity(Join rel, RelMetadataQuery mq, RexNode predicate) { return mq.getSelectivity(rel.getLeft(), newPred); } - public Double getSelectivity(Aggregate rel, RelMetadataQuery mq, - RexNode predicate) { + public @Nullable Double getSelectivity(Aggregate rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { final List notPushable = new ArrayList<>(); final List pushable = new ArrayList<>(); RelOptUtil.splitFilters( @@ -178,8 +182,8 @@ public Double getSelectivity(Aggregate rel, RelMetadataQuery mq, } } - public Double getSelectivity(Project rel, RelMetadataQuery mq, - RexNode predicate) { + public @Nullable Double getSelectivity(Project rel, RelMetadataQuery mq, + @Nullable RexNode predicate) { final List notPushable = new ArrayList<>(); final List pushable = new ArrayList<>(); RelOptUtil.splitFilters( @@ -209,7 +213,7 @@ public Double getSelectivity(Project rel, RelMetadataQuery mq, // Catch-all rule when none of the others apply. public Double getSelectivity(RelNode rel, RelMetadataQuery mq, - RexNode predicate) { + @Nullable RexNode predicate) { return RelMdUtil.guessSelectivity(predicate); } } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java index 55ff55ec7428..6093a28735e3 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java @@ -45,6 +45,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -84,14 +86,14 @@ protected RelMdSize() {} * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getAverageRowSize */ - public Double averageRowSize(RelNode rel, RelMetadataQuery mq) { - final List averageColumnSizes = mq.getAverageColumnSizes(rel); + public @Nullable Double averageRowSize(RelNode rel, RelMetadataQuery mq) { + final List<@Nullable Double> averageColumnSizes = mq.getAverageColumnSizes(rel); if (averageColumnSizes == null) { return null; } double d = 0d; final List fields = rel.getRowType().getFieldList(); - for (Pair p + for (Pair<@Nullable Double, RelDataTypeField> p : Pair.zip(averageColumnSizes, fields)) { if (p.left == null) { d += averageFieldValueSize(p.right); @@ -108,81 +110,78 @@ public Double averageRowSize(RelNode rel, RelMetadataQuery mq) { * * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getAverageColumnSizes */ - public List averageColumnSizes(RelNode rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(RelNode rel, RelMetadataQuery mq) { return null; // absolutely no idea } - public List averageColumnSizes(Filter rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Filter rel, RelMetadataQuery mq) { return mq.getAverageColumnSizes(rel.getInput()); } - public List averageColumnSizes(Sort rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Sort rel, RelMetadataQuery mq) { return mq.getAverageColumnSizes(rel.getInput()); } - public List averageColumnSizes(TableModify rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(TableModify rel, RelMetadataQuery mq) { return mq.getAverageColumnSizes(rel.getInput()); } - public List averageColumnSizes(Exchange rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Exchange rel, RelMetadataQuery mq) { return mq.getAverageColumnSizes(rel.getInput()); } - public List averageColumnSizes(Project rel, RelMetadataQuery mq) { - final List inputColumnSizes = + public @Nullable List<@Nullable Double> averageColumnSizes(Project rel, RelMetadataQuery mq) { + final List<@Nullable Double> inputColumnSizes = mq.getAverageColumnSizesNotNull(rel.getInput()); - final ImmutableNullableList.Builder sizes = - ImmutableNullableList.builder(); + final ImmutableNullableList.Builder<@Nullable Double> sizes = ImmutableNullableList.builder(); for (RexNode project : rel.getProjects()) { sizes.add(averageRexSize(project, inputColumnSizes)); } return sizes.build(); } - public List averageColumnSizes(Calc rel, RelMetadataQuery mq) { - final List inputColumnSizes = + public @Nullable List<@Nullable Double> averageColumnSizes(Calc rel, RelMetadataQuery mq) { + final List<@Nullable Double> inputColumnSizes = mq.getAverageColumnSizesNotNull(rel.getInput()); - final ImmutableNullableList.Builder sizes = - ImmutableNullableList.builder(); + final ImmutableNullableList.Builder<@Nullable Double> sizes = ImmutableNullableList.builder(); rel.getProgram().split().left.forEach( exp -> sizes.add(averageRexSize(exp, inputColumnSizes))); return sizes.build(); } - public List averageColumnSizes(Values rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Values rel, RelMetadataQuery mq) { final List fields = rel.getRowType().getFieldList(); - final ImmutableList.Builder list = ImmutableList.builder(); + final ImmutableNullableList.Builder<@Nullable Double> list = ImmutableNullableList.builder(); for (int i = 0; i < fields.size(); i++) { RelDataTypeField field = fields.get(i); - double d; if (rel.getTuples().isEmpty()) { - d = averageTypeValueSize(field.getType()); + list.add(averageTypeValueSize(field.getType())); } else { - d = 0; + double d = 0; for (ImmutableList literals : rel.getTuples()) { d += typeValueSize(field.getType(), literals.get(i).getValueAs(Comparable.class)); } d /= rel.getTuples().size(); + list.add(d); } - list.add(d); } return list.build(); } - public List averageColumnSizes(TableScan rel, RelMetadataQuery mq) { + public List<@Nullable Double> averageColumnSizes(TableScan rel, RelMetadataQuery mq) { final List fields = rel.getRowType().getFieldList(); - final ImmutableList.Builder list = ImmutableList.builder(); + final ImmutableNullableList.Builder<@Nullable Double> list = ImmutableNullableList.builder(); for (RelDataTypeField field : fields) { list.add(averageTypeValueSize(field.getType())); } return list.build(); } - public List averageColumnSizes(Aggregate rel, RelMetadataQuery mq) { - final List inputColumnSizes = + public List<@Nullable Double> averageColumnSizes(Aggregate rel, RelMetadataQuery mq) { + final List<@Nullable Double> inputColumnSizes = mq.getAverageColumnSizesNotNull(rel.getInput()); - final ImmutableList.Builder list = ImmutableList.builder(); + final ImmutableNullableList.Builder<@Nullable Double> list = ImmutableNullableList.builder(); for (int key : rel.getGroupSet()) { list.add(inputColumnSizes.get(key)); } @@ -192,22 +191,22 @@ public List averageColumnSizes(Aggregate rel, RelMetadataQuery mq) { return list.build(); } - public List averageColumnSizes(Join rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Join rel, RelMetadataQuery mq) { return averageJoinColumnSizes(rel, mq); } - private List averageJoinColumnSizes(Join rel, RelMetadataQuery mq) { + private @Nullable List<@Nullable Double> averageJoinColumnSizes(Join rel, RelMetadataQuery mq) { boolean semiOrAntijoin = !rel.getJoinType().projectsRight(); final RelNode left = rel.getLeft(); final RelNode right = rel.getRight(); - final List lefts = mq.getAverageColumnSizes(left); - final List rights = + final @Nullable List<@Nullable Double> lefts = mq.getAverageColumnSizes(left); + final @Nullable List<@Nullable Double> rights = semiOrAntijoin ? null : mq.getAverageColumnSizes(right); if (lefts == null && rights == null) { return null; } final int fieldCount = rel.getRowType().getFieldCount(); - Double[] sizes = new Double[fieldCount]; + @Nullable Double[] sizes = new Double[fieldCount]; if (lefts != null) { lefts.toArray(sizes); } @@ -220,19 +219,19 @@ private List averageJoinColumnSizes(Join rel, RelMetadataQuery mq) { return ImmutableNullableList.copyOf(sizes); } - public List averageColumnSizes(Intersect rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Intersect rel, RelMetadataQuery mq) { return mq.getAverageColumnSizes(rel.getInput(0)); } - public List averageColumnSizes(Minus rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Minus rel, RelMetadataQuery mq) { return mq.getAverageColumnSizes(rel.getInput(0)); } - public List averageColumnSizes(Union rel, RelMetadataQuery mq) { + public @Nullable List<@Nullable Double> averageColumnSizes(Union rel, RelMetadataQuery mq) { final int fieldCount = rel.getRowType().getFieldCount(); - List> inputColumnSizeList = new ArrayList<>(); + List> inputColumnSizeList = new ArrayList<>(); for (RelNode input : rel.getInputs()) { - final List inputSizes = mq.getAverageColumnSizes(input); + final List<@Nullable Double> inputSizes = mq.getAverageColumnSizes(input); if (inputSizes != null) { inputColumnSizeList.add(inputSizes); } @@ -245,13 +244,13 @@ public List averageColumnSizes(Union rel, RelMetadataQuery mq) { default: break; } - final ImmutableNullableList.Builder sizes = + final ImmutableNullableList.Builder<@Nullable Double> sizes = ImmutableNullableList.builder(); int nn = 0; for (int i = 0; i < fieldCount; i++) { double d = 0d; int n = 0; - for (List inputColumnSizes : inputColumnSizeList) { + for (List<@Nullable Double> inputColumnSizes : inputColumnSizeList) { Double d2 = inputColumnSizes.get(i); if (d2 != null) { d += d2; @@ -273,7 +272,7 @@ public List averageColumnSizes(Union rel, RelMetadataQuery mq) { *

We assume that the proportion of nulls is negligible, even if the field * is nullable. */ - protected Double averageFieldValueSize(RelDataTypeField field) { + protected @Nullable Double averageFieldValueSize(RelDataTypeField field) { return averageTypeValueSize(field.getType()); } @@ -282,7 +281,7 @@ protected Double averageFieldValueSize(RelDataTypeField field) { *

We assume that the proportion of nulls is negligible, even if the type * is nullable. */ - public Double averageTypeValueSize(RelDataType type) { + public @Nullable Double averageTypeValueSize(RelDataType type) { switch (type.getSqlTypeName()) { case BOOLEAN: case TINYINT: @@ -327,7 +326,10 @@ public Double averageTypeValueSize(RelDataType type) { case ROW: double average = 0.0; for (RelDataTypeField field : type.getFieldList()) { - average += averageTypeValueSize(field.getType()); + Double size = averageTypeValueSize(field.getType()); + if (size != null) { + average += size; + } } return average; default: @@ -339,7 +341,7 @@ public Double averageTypeValueSize(RelDataType type) { * *

Nulls count as 1 byte. */ - public double typeValueSize(RelDataType type, Comparable value) { + public double typeValueSize(RelDataType type, @Nullable Comparable value) { if (value == null) { return 1d; } @@ -385,7 +387,8 @@ public double typeValueSize(RelDataType type, Comparable value) { } } - public Double averageRexSize(RexNode node, List inputColumnSizes) { + public @Nullable Double averageRexSize(RexNode node, + List inputColumnSizes) { switch (node.getKind()) { case INPUT_REF: return inputColumnSizes.get(((RexInputRef) node).getIndex()); diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java index dd9e797dce84..553677e56b23 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java @@ -39,6 +39,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -76,15 +78,15 @@ protected RelMdTableReferences() {} } // Catch-all rule when none of the others apply. - public Set getTableReferences(RelNode rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(RelNode rel, RelMetadataQuery mq) { return null; } - public Set getTableReferences(HepRelVertex rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(HepRelVertex rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getCurrentRel()); } - public Set getTableReferences(RelSubset rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(RelSubset rel, RelMetadataQuery mq) { return mq.getTableReferences(Util.first(rel.getBest(), rel.getOriginal())); } @@ -98,14 +100,14 @@ public Set getTableReferences(TableScan rel, RelMetadataQuery mq) { /** * Table references from Aggregate. */ - public Set getTableReferences(Aggregate rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Aggregate rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from Join. */ - public Set getTableReferences(Join rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Join rel, RelMetadataQuery mq) { final RelNode leftInput = rel.getLeft(); final RelNode rightInput = rel.getRight(); final Set result = new HashSet<>(); @@ -152,7 +154,7 @@ public Set getTableReferences(Join rel, RelMetadataQuery mq) { *

For Union operator, we might be able to extract multiple table * references. */ - public Set getTableReferences(SetOp rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(SetOp rel, RelMetadataQuery mq) { final Set result = new HashSet<>(); // Infer column origin expressions for given references @@ -190,56 +192,56 @@ public Set getTableReferences(SetOp rel, RelMetadataQuery mq) { /** * Table references from Project. */ - public Set getTableReferences(Project rel, final RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Project rel, final RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from Filter. */ - public Set getTableReferences(Filter rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Filter rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from Calc. */ - public Set getTableReferences(Calc rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Calc rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from Sort. */ - public Set getTableReferences(Sort rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Sort rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from TableModify. */ - public Set getTableReferences(TableModify rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(TableModify rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from Exchange. */ - public Set getTableReferences(Exchange rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Exchange rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from Window. */ - public Set getTableReferences(Window rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Window rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } /** * Table references from Sample. */ - public Set getTableReferences(Sample rel, RelMetadataQuery mq) { + public @Nullable Set getTableReferences(Sample rel, RelMetadataQuery mq) { return mq.getTableReferences(rel.getInput()); } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java index 475cd9e27ed9..c058a24cec53 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java @@ -43,11 +43,15 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * RelMdUniqueKeys supplies a default implementation of * {@link RelMetadataQuery#getUniqueKeys} for the standard logical algebra. @@ -68,22 +72,22 @@ private RelMdUniqueKeys() {} return BuiltInMetadata.UniqueKeys.DEF; } - public Set getUniqueKeys(Filter rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(Filter rel, RelMetadataQuery mq, boolean ignoreNulls) { return mq.getUniqueKeys(rel.getInput(), ignoreNulls); } - public Set getUniqueKeys(Sort rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(Sort rel, RelMetadataQuery mq, boolean ignoreNulls) { return mq.getUniqueKeys(rel.getInput(), ignoreNulls); } - public Set getUniqueKeys(Correlate rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(Correlate rel, RelMetadataQuery mq, boolean ignoreNulls) { return mq.getUniqueKeys(rel.getLeft(), ignoreNulls); } - public Set getUniqueKeys(TableModify rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(TableModify rel, RelMetadataQuery mq, boolean ignoreNulls) { return mq.getUniqueKeys(rel.getInput(), ignoreNulls); } @@ -93,7 +97,7 @@ public Set getUniqueKeys(Project rel, RelMetadataQuery mq, return getProjectUniqueKeys(rel, mq, ignoreNulls, rel.getProjects()); } - public Set getUniqueKeys(Calc rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(Calc rel, RelMetadataQuery mq, boolean ignoreNulls) { RexProgram program = rel.getProgram(); return getProjectUniqueKeys(rel, mq, ignoreNulls, @@ -155,14 +159,17 @@ private Set getProjectUniqueKeys(SingleRel rel, RelMetadataQuer Iterable> product = Linq4j.product( Util.transform(colMask, - in -> Util.filter(mapInToOutPos.get(in).powerSet(), bs -> !bs.isEmpty()))); + in -> Util.filter( + requireNonNull(mapInToOutPos.get(in), + () -> "no entry for column " + in + " in mapInToOutPos: " + mapInToOutPos) + .powerSet(), bs -> !bs.isEmpty()))); resultBuilder.addAll(Util.transform(product, ImmutableBitSet::union)); } return resultBuilder.build(); } - public Set getUniqueKeys(Join rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(Join rel, RelMetadataQuery mq, boolean ignoreNulls) { if (!rel.getJoinType().projectsRight()) { // only return the unique keys from the LHS since a semijoin only @@ -301,9 +308,12 @@ public Set getUniqueKeys(Minus rel, return ImmutableSet.of(); } - public Set getUniqueKeys(TableScan rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(TableScan rel, RelMetadataQuery mq, boolean ignoreNulls) { final List keys = rel.getTable().getKeys(); + if (keys == null) { + return null; + } for (ImmutableBitSet key : keys) { assert rel.getTable().isKey(key); } @@ -311,7 +321,7 @@ public Set getUniqueKeys(TableScan rel, RelMetadataQuery mq, } // Catch-all rule when none of the others apply. - public Set getUniqueKeys(RelNode rel, RelMetadataQuery mq, + public @Nullable Set getUniqueKeys(RelNode rel, RelMetadataQuery mq, boolean ignoreNulls) { // no information available return null; diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java index 41b8539483f4..de577e33a9ff 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java @@ -48,6 +48,9 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.LinkedHashSet; @@ -202,7 +205,7 @@ public static boolean areColumnsDefinitelyUnique(RelMetadataQuery mq, return b != null && b; } - public static Boolean areColumnsUnique(RelMetadataQuery mq, RelNode rel, + public static @Nullable Boolean areColumnsUnique(RelMetadataQuery mq, RelNode rel, List columnRefs) { ImmutableBitSet.Builder colMask = ImmutableBitSet.builder(); for (RexInputRef columnRef : columnRefs) { @@ -235,7 +238,7 @@ public static boolean areColumnsDefinitelyUniqueWhenNullsFiltered( return b != null && b; } - public static Boolean areColumnsUniqueWhenNullsFiltered(RelMetadataQuery mq, + public static @Nullable Boolean areColumnsUniqueWhenNullsFiltered(RelMetadataQuery mq, RelNode rel, List columnRefs) { ImmutableBitSet.Builder colMask = ImmutableBitSet.builder(); @@ -291,11 +294,11 @@ public static void setLeftRightBitmaps( * @param numSelected the number of selections. * @return the expected number of distinct values. */ - public static Double numDistinctVals( - Double domainSize, - Double numSelected) { + public static @PolyNull Double numDistinctVals( + @PolyNull Double domainSize, + @PolyNull Double numSelected) { if ((domainSize == null) || (numSelected == null)) { - return null; + return domainSize; } // Cap the input sizes at MAX_VALUE to ensure that the calculations @@ -365,7 +368,7 @@ public static double capInfinity(Double d) { * means true, so gives selectity of 1.0 * @return estimated selectivity */ - public static double guessSelectivity(RexNode predicate) { + public static double guessSelectivity(@Nullable RexNode predicate) { return guessSelectivity(predicate, false); } @@ -379,7 +382,7 @@ public static double guessSelectivity(RexNode predicate) { * @return estimated selectivity */ public static double guessSelectivity( - RexNode predicate, + @Nullable RexNode predicate, boolean artificialOnly) { double sel = 1.0; if ((predicate == null) || predicate.isAlwaysTrue()) { @@ -421,10 +424,10 @@ public static double guessSelectivity( * @param pred2 second predicate * @return AND'd predicate or individual predicates if one is null */ - public static RexNode unionPreds( + public static @Nullable RexNode unionPreds( RexBuilder rexBuilder, - RexNode pred1, - RexNode pred2) { + @Nullable RexNode pred1, + @Nullable RexNode pred2) { final Set unionList = new LinkedHashSet<>(); unionList.addAll(RelOptUtil.conjunctions(pred1)); unionList.addAll(RelOptUtil.conjunctions(pred2)); @@ -440,10 +443,10 @@ public static RexNode unionPreds( * @param pred2 second predicate * @return MINUS'd predicate list */ - public static RexNode minusPreds( + public static @Nullable RexNode minusPreds( RexBuilder rexBuilder, - RexNode pred1, - RexNode pred2) { + @Nullable RexNode pred1, + @Nullable RexNode pred2) { final List minusList = new ArrayList<>(RelOptUtil.conjunctions(pred1)); minusList.removeAll(RelOptUtil.conjunctions(pred2)); @@ -510,7 +513,7 @@ public static void splitCols( * @param expr projection expression * @return cardinality */ - public static Double cardOfProjExpr(RelMetadataQuery mq, Project rel, + public static @Nullable Double cardOfProjExpr(RelMetadataQuery mq, Project rel, RexNode expr) { return expr.accept(new CardOfProjExpr(mq, rel)); } @@ -522,7 +525,7 @@ public static Double cardOfProjExpr(RelMetadataQuery mq, Project rel, * @param groupKey Keys to compute the population for * @return computed population size */ - public static Double getJoinPopulationSize(RelMetadataQuery mq, + public static @Nullable Double getJoinPopulationSize(RelMetadataQuery mq, RelNode join_, ImmutableBitSet groupKey) { Join join = (Join) join_; if (!join.getJoinType().projectsRight()) { @@ -577,8 +580,8 @@ public static double addEpsilon(double d) { * @param predicate join predicate * @return number of distinct rows */ - public static Double getSemiJoinDistinctRowCount(Join semiJoinRel, RelMetadataQuery mq, - ImmutableBitSet groupKey, RexNode predicate) { + public static @Nullable Double getSemiJoinDistinctRowCount(Join semiJoinRel, RelMetadataQuery mq, + ImmutableBitSet groupKey, @Nullable RexNode predicate) { if (predicate == null || predicate.isAlwaysTrue()) { if (groupKey.isEmpty()) { return 1D; @@ -611,9 +614,9 @@ public static Double getSemiJoinDistinctRowCount(Join semiJoinRel, RelMetadataQu * otherwise use left NDV * right NDV. * @return number of distinct rows */ - public static Double getJoinDistinctRowCount(RelMetadataQuery mq, + public static @Nullable Double getJoinDistinctRowCount(RelMetadataQuery mq, RelNode joinRel, JoinRelType joinType, ImmutableBitSet groupKey, - RexNode predicate, boolean useMaxNdv) { + @Nullable RexNode predicate, boolean useMaxNdv) { if (predicate == null || predicate.isAlwaysTrue()) { if (groupKey.isEmpty()) { return 1D; @@ -663,7 +666,7 @@ public static Double getJoinDistinctRowCount(RelMetadataQuery mq, } if (useMaxNdv) { - distRowCount = Math.max( + distRowCount = NumberUtil.max( mq.getDistinctRowCount(left, leftMask.build(), leftPred), mq.getDistinctRowCount(right, rightMask.build(), rightPred)); } else { @@ -701,7 +704,7 @@ public static double getMinusRowCount(RelMetadataQuery mq, Minus minus) { } /** Returns an estimate of the number of rows returned by a {@link Join}. */ - public static Double getJoinRowCount(RelMetadataQuery mq, Join join, + public static @Nullable Double getJoinRowCount(RelMetadataQuery mq, Join join, RexNode condition) { if (!join.getJoinType().projectsRight()) { // Create a RexNode representing the selectivity of the @@ -761,7 +764,7 @@ public static double estimateFilteredRows(RelNode child, RexProgram program, return estimateFilteredRows(child, condition, mq); } - public static double estimateFilteredRows(RelNode child, RexNode condition, + public static double estimateFilteredRows(RelNode child, @Nullable RexNode condition, RelMetadataQuery mq) { return mq.getRowCount(child) * mq.getSelectivity(child, condition); @@ -801,7 +804,7 @@ public static double linear(int x, int minX, int maxX, double minY, double /** Visitor that walks over a scalar expression and computes the * cardinality of its result. */ - private static class CardOfProjExpr extends RexVisitorImpl { + private static class CardOfProjExpr extends RexVisitorImpl<@Nullable Double> { private final RelMetadataQuery mq; private Project rel; @@ -811,7 +814,7 @@ private static class CardOfProjExpr extends RexVisitorImpl { this.rel = rel; } - @Override public Double visitInputRef(RexInputRef var) { + @Override public @Nullable Double visitInputRef(RexInputRef var) { int index = var.getIndex(); ImmutableBitSet col = ImmutableBitSet.of(index); Double distinctRowCount = @@ -823,11 +826,11 @@ private static class CardOfProjExpr extends RexVisitorImpl { } } - @Override public Double visitLiteral(RexLiteral literal) { + @Override public @Nullable Double visitLiteral(RexLiteral literal) { return numDistinctVals(1.0, mq.getRowCount(rel)); } - @Override public Double visitCall(RexCall call) { + @Override public @Nullable Double visitCall(RexCall call) { Double distinctRowCount; Double rowCount = mq.getRowCount(rel); if (call.isA(SqlKind.MINUS_PREFIX)) { @@ -869,7 +872,7 @@ private static class CardOfProjExpr extends RexVisitorImpl { *

If this is the case, it is safe to push down a * {@link org.apache.calcite.rel.core.Sort} with limit and optional offset. */ public static boolean checkInputForCollationAndLimit(RelMetadataQuery mq, - RelNode input, RelCollation collation, RexNode offset, RexNode fetch) { + RelNode input, RelCollation collation, @Nullable RexNode offset, @Nullable RexNode fetch) { return alreadySorted(mq, input, collation) && alreadySmaller(mq, input, offset, fetch); } @@ -893,7 +896,7 @@ private static boolean alreadySorted(RelMetadataQuery mq, RelNode input, RelColl // Checks if we are not reducing the number of tuples private static boolean alreadySmaller(RelMetadataQuery mq, RelNode input, - RexNode offset, RexNode fetch) { + @Nullable RexNode offset, @Nullable RexNode fetch) { if (fetch == null) { return true; } @@ -914,12 +917,12 @@ private static boolean alreadySmaller(RelMetadataQuery mq, RelNode input, * @return true if the {@code result} is a percentage number * @throws AssertionError if the validation fails */ - public static Double validatePercentage(Double result) { + public static @PolyNull Double validatePercentage(@PolyNull Double result) { assert isPercentage(result, true); return result; } - private static boolean isPercentage(Double result, boolean fail) { + private static boolean isPercentage(@Nullable Double result, boolean fail) { if (result != null) { final double d = result; if (d < 0.0) { @@ -945,7 +948,7 @@ private static boolean isPercentage(Double result, boolean fail) { * @return the corrected value from the {@code result} * @throws AssertionError if the {@code result} is negative */ - public static Double validateResult(Double result) { + public static @PolyNull Double validateResult(@PolyNull Double result) { if (result == null) { return null; } @@ -960,7 +963,7 @@ public static Double validateResult(Double result) { return result; } - private static boolean isNonNegative(Double result, boolean fail) { + private static boolean isNonNegative(@Nullable Double result, boolean fail) { if (result != null) { final double d = result; if (d < 0.0) { diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java index c6a64ccb03ec..1bd9a20bfeec 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java @@ -20,6 +20,8 @@ import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; /** @@ -62,7 +64,7 @@ public interface RelMetadataProvider { * @return Function that will field a metadata instance; or null if this * provider cannot supply metadata of this type */ - UnboundMetadata apply( + <@Nullable M extends @Nullable Metadata> @Nullable UnboundMetadata apply( Class relClass, Class metadataClass); Multimap> handlers( diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java index f57d3f0fd9e5..4abda11a2475 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java @@ -22,6 +22,7 @@ import org.apache.calcite.plan.volcano.VolcanoPlanner; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelDistribution; +import org.apache.calcite.rel.RelDistributions; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexTableInputRef.RelTableRef; @@ -32,11 +33,15 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * RelMetadataQuery provides a strongly-typed facade on top of * {@link RelMetadataProvider} for the set of relational expression metadata @@ -105,7 +110,7 @@ public class RelMetadataQuery extends RelMetadataQueryBase { * from {@link #THREAD_PROVIDERS} and {@link #EMPTY} as a prototype. */ protected RelMetadataQuery() { - this(THREAD_PROVIDERS.get(), EMPTY); + this(castNonNull(THREAD_PROVIDERS.get()), EMPTY); } /** Creates and initializes the instance that will serve as a prototype for @@ -185,7 +190,7 @@ public static RelMetadataQuery instance() { * * @param rel the relational expression */ - public Multimap, RelNode> getNodeTypes(RelNode rel) { + public @Nullable Multimap, RelNode> getNodeTypes(RelNode rel) { for (;;) { try { return nodeTypesHandler.getNodeTypes(rel, this); @@ -206,11 +211,11 @@ public Multimap, RelNode> getNodeTypes(RelNode rel) { * @return estimated row count, or null if no reliable estimate can be * determined */ - public Double getRowCount(RelNode rel) { + public /* @Nullable: CALCITE-4263 */ Double getRowCount(RelNode rel) { for (;;) { try { Double result = rowCountHandler.getRowCount(rel, this); - return RelMdUtil.validateResult(result); + return RelMdUtil.validateResult(castNonNull(result)); } catch (JaninoRelMetadataProvider.NoHandler e) { rowCountHandler = revise(e.relClass, BuiltInMetadata.RowCount.DEF); } @@ -225,7 +230,7 @@ public Double getRowCount(RelNode rel) { * @param rel the relational expression * @return max row count */ - public Double getMaxRowCount(RelNode rel) { + public @Nullable Double getMaxRowCount(RelNode rel) { for (;;) { try { return maxRowCountHandler.getMaxRowCount(rel, this); @@ -244,7 +249,7 @@ public Double getMaxRowCount(RelNode rel) { * @param rel the relational expression * @return max row count */ - public Double getMinRowCount(RelNode rel) { + public @Nullable Double getMinRowCount(RelNode rel) { for (;;) { try { return minRowCountHandler.getMinRowCount(rel, this); @@ -263,7 +268,7 @@ public Double getMinRowCount(RelNode rel) { * @param rel the relational expression * @return estimated cost, or null if no reliable estimate can be determined */ - public RelOptCost getCumulativeCost(RelNode rel) { + public @Nullable RelOptCost getCumulativeCost(RelNode rel) { for (;;) { try { return cumulativeCostHandler.getCumulativeCost(rel, this); @@ -282,7 +287,7 @@ public RelOptCost getCumulativeCost(RelNode rel) { * @param rel the relational expression * @return estimated cost, or null if no reliable estimate can be determined */ - public RelOptCost getNonCumulativeCost(RelNode rel) { + public @Nullable RelOptCost getNonCumulativeCost(RelNode rel) { for (;;) { try { return nonCumulativeCostHandler.getNonCumulativeCost(rel, this); @@ -302,7 +307,7 @@ public RelOptCost getNonCumulativeCost(RelNode rel) { * @return estimated percentage (between 0.0 and 1.0), or null if no * reliable estimate can be determined */ - public Double getPercentageOriginalRows(RelNode rel) { + public @Nullable Double getPercentageOriginalRows(RelNode rel) { for (;;) { try { Double result = @@ -326,7 +331,7 @@ public Double getPercentageOriginalRows(RelNode rel) { * determined (whereas empty set indicates definitely no origin columns at * all) */ - public Set getColumnOrigins(RelNode rel, int column) { + public @Nullable Set getColumnOrigins(RelNode rel, int column) { for (;;) { try { return columnOriginHandler.getColumnOrigins(rel, this, column); @@ -350,7 +355,7 @@ public Set getColumnOrigins(RelNode rel, int column) { * @return the origin of a column provided it's a simple column; otherwise, * returns null */ - public RelColumnOrigin getColumnOrigin(RelNode rel, int column) { + public @Nullable RelColumnOrigin getColumnOrigin(RelNode rel, int column) { final Set origins = getColumnOrigins(rel, column); if (origins == null || origins.size() != 1) { return null; @@ -362,7 +367,7 @@ public RelColumnOrigin getColumnOrigin(RelNode rel, int column) { /** * Determines the origin of a column. */ - public Set getExpressionLineage(RelNode rel, RexNode expression) { + public @Nullable Set getExpressionLineage(RelNode rel, RexNode expression) { for (;;) { try { return expressionLineageHandler.getExpressionLineage(rel, this, expression); @@ -376,7 +381,7 @@ public Set getExpressionLineage(RelNode rel, RexNode expression) { /** * Determines the tables used by a plan. */ - public Set getTableReferences(RelNode rel) { + public @Nullable Set getTableReferences(RelNode rel) { for (;;) { try { return tableReferencesHandler.getTableReferences(rel, this); @@ -395,7 +400,7 @@ public Set getTableReferences(RelNode rel) { * * @return the table, if the RelNode is a simple table; otherwise null */ - public RelOptTable getTableOrigin(RelNode rel) { + public @Nullable RelOptTable getTableOrigin(RelNode rel) { // Determine the simple origin of the first column in the // RelNode. If it's simple, then that means that the underlying // table is also simple, even if the column itself is derived. @@ -420,7 +425,7 @@ public RelOptTable getTableOrigin(RelNode rel) { * @return estimated selectivity (between 0.0 and 1.0), or null if no * reliable estimate can be determined */ - public Double getSelectivity(RelNode rel, RexNode predicate) { + public @Nullable Double getSelectivity(RelNode rel, @Nullable RexNode predicate) { for (;;) { try { Double result = selectivityHandler.getSelectivity(rel, this, predicate); @@ -441,7 +446,7 @@ public Double getSelectivity(RelNode rel, RexNode predicate) { * @return set of keys, or null if this information cannot be determined * (whereas empty set indicates definitely no keys at all) */ - public Set getUniqueKeys(RelNode rel) { + public @Nullable Set getUniqueKeys(RelNode rel) { return getUniqueKeys(rel, false); } @@ -457,7 +462,7 @@ public Set getUniqueKeys(RelNode rel) { * @return set of keys, or null if this information cannot be determined * (whereas empty set indicates definitely no keys at all) */ - public Set getUniqueKeys(RelNode rel, + public @Nullable Set getUniqueKeys(RelNode rel, boolean ignoreNulls) { for (;;) { try { @@ -480,7 +485,7 @@ public Set getUniqueKeys(RelNode rel, * @return true or false depending on whether the rows are unique, or * null if not enough information is available to make that determination */ - public Boolean areRowsUnique(RelNode rel) { + public @Nullable Boolean areRowsUnique(RelNode rel) { final ImmutableBitSet columns = ImmutableBitSet.range(rel.getRowType().getFieldCount()); return areColumnsUnique(rel, columns, false); @@ -498,7 +503,7 @@ public Boolean areRowsUnique(RelNode rel) { * @return true or false depending on whether the columns are unique, or * null if not enough information is available to make that determination */ - public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns) { + public @Nullable Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns) { return areColumnsUnique(rel, columns, false); } @@ -515,7 +520,7 @@ public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns) { * @return true or false depending on whether the columns are unique, or * null if not enough information is available to make that determination */ - public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns, + public @Nullable Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns, boolean ignoreNulls) { for (;;) { try { @@ -537,7 +542,7 @@ public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns, * @return List of sorted column combinations, or * null if not enough information is available to make that determination */ - public ImmutableList collations(RelNode rel) { + public @Nullable ImmutableList collations(RelNode rel) { for (;;) { try { return collationHandler.collations(rel, this); @@ -559,7 +564,12 @@ public ImmutableList collations(RelNode rel) { public RelDistribution distribution(RelNode rel) { for (;;) { try { - return distributionHandler.distribution(rel, this); + RelDistribution distribution = distributionHandler.distribution(rel, this); + //noinspection ConstantConditions + if (distribution == null) { + return RelDistributions.ANY; + } + return distribution; } catch (JaninoRelMetadataProvider.NoHandler e) { distributionHandler = revise(e.relClass, BuiltInMetadata.Distribution.DEF); @@ -579,7 +589,7 @@ public RelDistribution distribution(RelNode rel) { * estimate can be determined * */ - public Double getPopulationSize(RelNode rel, + public @Nullable Double getPopulationSize(RelNode rel, ImmutableBitSet groupKey) { for (;;) { try { @@ -601,7 +611,7 @@ public Double getPopulationSize(RelNode rel, * @param rel the relational expression * @return average size of a row, in bytes, or null if not known */ - public Double getAverageRowSize(RelNode rel) { + public @Nullable Double getAverageRowSize(RelNode rel) { for (;;) { try { return sizeHandler.averageRowSize(rel, this); @@ -621,7 +631,7 @@ public Double getAverageRowSize(RelNode rel) { * value, in bytes. Each value or the entire list may be null if the * metadata is not available */ - public List getAverageColumnSizes(RelNode rel) { + public @Nullable List<@Nullable Double> getAverageColumnSizes(RelNode rel) { for (;;) { try { return sizeHandler.averageColumnSizes(rel, this); @@ -633,8 +643,8 @@ public List getAverageColumnSizes(RelNode rel) { /** As {@link #getAverageColumnSizes(org.apache.calcite.rel.RelNode)} but * never returns a null list, only ever a list of nulls. */ - public List getAverageColumnSizesNotNull(RelNode rel) { - final List averageColumnSizes = getAverageColumnSizes(rel); + public List<@Nullable Double> getAverageColumnSizesNotNull(RelNode rel) { + final @Nullable List<@Nullable Double> averageColumnSizes = getAverageColumnSizes(rel); return averageColumnSizes == null ? Collections.nCopies(rel.getRowType().getFieldCount(), null) : averageColumnSizes; @@ -650,7 +660,7 @@ public List getAverageColumnSizesNotNull(RelNode rel) { * expression belongs to a different process than its inputs, or null if not * known */ - public Boolean isPhaseTransition(RelNode rel) { + public @Nullable Boolean isPhaseTransition(RelNode rel) { for (;;) { try { return parallelismHandler.isPhaseTransition(rel, this); @@ -669,7 +679,7 @@ public Boolean isPhaseTransition(RelNode rel) { * @param rel the relational expression * @return the number of distinct splits of the data, or null if not known */ - public Integer splitCount(RelNode rel) { + public @Nullable Integer splitCount(RelNode rel) { for (;;) { try { return parallelismHandler.splitCount(rel, this); @@ -690,7 +700,7 @@ public Integer splitCount(RelNode rel) { * operator implementing this relational expression, across all splits, * or null if not known */ - public Double memory(RelNode rel) { + public @Nullable Double memory(RelNode rel) { for (;;) { try { return memoryHandler.memory(rel, this); @@ -710,7 +720,7 @@ public Double memory(RelNode rel) { * physical operator implementing this relational expression, and all other * operators within the same phase, across all splits, or null if not known */ - public Double cumulativeMemoryWithinPhase(RelNode rel) { + public @Nullable Double cumulativeMemoryWithinPhase(RelNode rel) { for (;;) { try { return memoryHandler.cumulativeMemoryWithinPhase(rel, this); @@ -730,7 +740,7 @@ public Double cumulativeMemoryWithinPhase(RelNode rel) { * the physical operator implementing this relational expression, and all * operators within the same phase, within each split, or null if not known */ - public Double cumulativeMemoryWithinPhaseSplit(RelNode rel) { + public @Nullable Double cumulativeMemoryWithinPhaseSplit(RelNode rel) { for (;;) { try { return memoryHandler.cumulativeMemoryWithinPhaseSplit(rel, this); @@ -751,10 +761,10 @@ public Double cumulativeMemoryWithinPhaseSplit(RelNode rel) { * @return distinct row count for groupKey, filtered by predicate, or null * if no reliable estimate can be determined */ - public Double getDistinctRowCount( + public @Nullable Double getDistinctRowCount( RelNode rel, ImmutableBitSet groupKey, - RexNode predicate) { + @Nullable RexNode predicate) { for (;;) { try { Double result = @@ -779,7 +789,8 @@ public Double getDistinctRowCount( public RelOptPredicateList getPulledUpPredicates(RelNode rel) { for (;;) { try { - return predicatesHandler.getPredicates(rel, this); + RelOptPredicateList result = predicatesHandler.getPredicates(rel, this); + return result != null ? result : RelOptPredicateList.EMPTY; } catch (JaninoRelMetadataProvider.NoHandler e) { predicatesHandler = revise(e.relClass, BuiltInMetadata.Predicates.DEF); } @@ -794,7 +805,7 @@ public RelOptPredicateList getPulledUpPredicates(RelNode rel) { * @param rel the relational expression * @return All predicates within and below this RelNode */ - public RelOptPredicateList getAllPredicates(RelNode rel) { + public @Nullable RelOptPredicateList getAllPredicates(RelNode rel) { for (;;) { try { return allPredicatesHandler.getAllPredicates(rel, this); @@ -814,7 +825,7 @@ public RelOptPredicateList getAllPredicates(RelNode rel) { * @return true for visible, false for invisible; if no metadata is available, * defaults to true */ - public boolean isVisibleInExplain(RelNode rel, + public Boolean isVisibleInExplain(RelNode rel, SqlExplainLevel explainLevel) { for (;;) { try { @@ -838,7 +849,7 @@ public boolean isVisibleInExplain(RelNode rel, * @return description of how the rows in the relational expression are * physically distributed */ - public RelDistribution getDistribution(RelNode rel) { + public @Nullable RelDistribution getDistribution(RelNode rel) { for (;;) { try { return distributionHandler.distribution(rel, this); @@ -851,7 +862,7 @@ public RelDistribution getDistribution(RelNode rel) { /** * Returns the lower bound cost of a RelNode. */ - public RelOptCost getLowerBoundCost(RelNode rel, VolcanoPlanner planner) { + public @Nullable RelOptCost getLowerBoundCost(RelNode rel, VolcanoPlanner planner) { for (;;) { try { return lowerBoundCostHandler.getLowerBoundCost(rel, this, planner); diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQueryBase.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQueryBase.java index 0de898c33874..851a96b4b736 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQueryBase.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQueryBase.java @@ -21,11 +21,15 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Proxy; import java.util.List; import java.util.Map; import java.util.function.Supplier; +import static java.util.Objects.requireNonNull; + /** * Base class for the RelMetadataQuery that uses the metadata handler class * generated by the Janino. @@ -64,16 +68,16 @@ public class RelMetadataQueryBase { /** Set of active metadata queries, and cache of previous results. */ public final Table map = HashBasedTable.create(); - public final JaninoRelMetadataProvider metadataProvider; + public final @Nullable JaninoRelMetadataProvider metadataProvider; //~ Static fields/initializers --------------------------------------------- - public static final ThreadLocal THREAD_PROVIDERS = + public static final ThreadLocal<@Nullable JaninoRelMetadataProvider> THREAD_PROVIDERS = new ThreadLocal<>(); //~ Constructors ----------------------------------------------------------- - protected RelMetadataQueryBase(JaninoRelMetadataProvider metadataProvider) { + protected RelMetadataQueryBase(@Nullable JaninoRelMetadataProvider metadataProvider) { this.metadataProvider = metadataProvider; } @@ -81,7 +85,7 @@ protected static H initialHandler(Class handlerClass) { return handlerClass.cast( Proxy.newProxyInstance(RelMetadataQuery.class.getClassLoader(), new Class[] {handlerClass}, (proxy, method, args) -> { - final RelNode r = (RelNode) args[0]; + final RelNode r = requireNonNull((RelNode) args[0], "(RelNode) args[0]"); throw new JaninoRelMetadataProvider.NoHandler(r.getClass()); })); } @@ -92,6 +96,7 @@ protected static H initialHandler(Class handlerClass) { * {@code class_} if it is not already present. */ protected > H revise(Class class_, MetadataDef def) { + requireNonNull(metadataProvider, "metadataProvider"); return metadataProvider.revise(class_, def); } diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java index 0686ca868023..0f3131668644 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/UnboundMetadata.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Metadata that needs to be bound to a {@link RelNode} and * {@link RelMetadataQuery} before it can be used. @@ -25,6 +27,6 @@ * @param Metadata type */ @FunctionalInterface -public interface UnboundMetadata { +public interface UnboundMetadata { M bind(RelNode rel, RelMetadataQuery mq); } diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableAggregate.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableAggregate.java index 5c5a8b70c5b6..92593be2f11e 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableAggregate.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableAggregate.java @@ -23,6 +23,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -34,7 +36,7 @@ public class MutableAggregate extends MutableSingleRel { private MutableAggregate(MutableRel input, RelDataType rowType, ImmutableBitSet groupSet, - List groupSets, List aggCalls) { + @Nullable List groupSets, List aggCalls) { super(MutableRelType.AGGREGATE, rowType, input); this.groupSet = groupSet; this.groupSets = groupSets == null @@ -57,7 +59,7 @@ private MutableAggregate(MutableRel input, RelDataType rowType, * @param aggCalls Collection of calls to aggregate functions */ public static MutableAggregate of(MutableRel input, ImmutableBitSet groupSet, - ImmutableList groupSets, List aggCalls) { + @Nullable ImmutableList groupSets, List aggCalls) { RelDataType rowType = Aggregate.deriveRowType(input.cluster.getTypeFactory(), input.rowType, false, groupSet, groupSets, aggCalls); @@ -65,7 +67,7 @@ public static MutableAggregate of(MutableRel input, ImmutableBitSet groupSet, groupSets, aggCalls); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableAggregate && groupSet.equals(((MutableAggregate) obj).groupSet) diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableBiRel.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableBiRel.java index e7475226cc25..30c6ff02188f 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableBiRel.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableBiRel.java @@ -28,6 +28,7 @@ abstract class MutableBiRel extends MutableRel { protected MutableRel left; protected MutableRel right; + @SuppressWarnings("initialization.invalid.field.write.initialized") protected MutableBiRel(MutableRelType type, RelOptCluster cluster, RelDataType rowType, MutableRel left, MutableRel right) { super(cluster, rowType, type); diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableCalc.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableCalc.java index 5791bb9aac9a..1d172c5f2cb5 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableCalc.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableCalc.java @@ -18,6 +18,8 @@ import org.apache.calcite.rex.RexProgram; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Calc}. */ @@ -39,7 +41,7 @@ public static MutableCalc of(MutableRel input, RexProgram program) { return new MutableCalc(input, program); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableCalc && MutableRel.STRING_EQUIVALENCE.equivalent( diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableCollect.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableCollect.java index 3123c25b44dd..0782d2f89fa1 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableCollect.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableCollect.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.type.RelDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Collect}. */ @@ -42,7 +44,7 @@ public static MutableCollect of(RelDataType rowType, return new MutableCollect(rowType, input, fieldName); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableCollect && fieldName.equals(((MutableCollect) obj).fieldName) diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableCorrelate.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableCorrelate.java index 80302fa30d74..cba7890c8d45 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableCorrelate.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableCorrelate.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Correlate}. */ @@ -59,7 +61,7 @@ public static MutableCorrelate of(RelDataType rowType, MutableRel left, requiredColumns, joinType); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableCorrelate && correlationId.equals( diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableExchange.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableExchange.java index b9f482541698..72669a1cdbf7 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableExchange.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableExchange.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.RelDistribution; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Exchange}. */ @@ -39,7 +41,7 @@ public static MutableExchange of(MutableRel input, RelDistribution distribution) return new MutableExchange(input, distribution); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableExchange && distribution.equals(((MutableExchange) obj).distribution) diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java index 009674aaf1a5..783e8d9301f5 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableFilter.java @@ -18,6 +18,8 @@ import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Filter}. */ @@ -40,7 +42,7 @@ public static MutableFilter of(MutableRel input, RexNode condition) { return new MutableFilter(input, condition); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableFilter && condition.equals(((MutableFilter) obj).condition) diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java index ab87de80d024..ccc6a093fbec 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableJoin.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; import java.util.Set; @@ -62,7 +64,7 @@ public static MutableJoin of(RelDataType rowType, MutableRel left, variablesStopped); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableJoin && joinType == ((MutableJoin) obj).joinType diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableMatch.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableMatch.java index aeab05fd4eb2..b2f10529b6a1 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableMatch.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableMatch.java @@ -21,6 +21,8 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; import java.util.Objects; import java.util.SortedSet; @@ -74,7 +76,7 @@ public static MutableMatch of(RelDataType rowType, orderKeys, interval); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableMatch && pattern.equals(((MutableMatch) obj).pattern) diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableMultiRel.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableMultiRel.java index 417b21b6b9b5..7921f192549c 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableMultiRel.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableMultiRel.java @@ -28,6 +28,7 @@ abstract class MutableMultiRel extends MutableRel { protected final List inputs; + @SuppressWarnings("initialization.invalid.field.write.initialized") protected MutableMultiRel(RelOptCluster cluster, RelDataType rowType, MutableRelType type, List inputs) { super(cluster, rowType, type); diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableProject.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableProject.java index b781f1caa9a4..9254ce096d96 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableProject.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableProject.java @@ -25,6 +25,8 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.mapping.Mappings; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -66,7 +68,7 @@ public static MutableRel of(MutableRel input, List exprList, return of(rowType, input, exprList); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableProject && MutableRel.PAIRWISE_STRING_EQUIVALENCE.equivalent( @@ -88,7 +90,7 @@ public final List> getNamedProjects() { return Pair.zip(projects, rowType.getFieldNames()); } - public Mappings.TargetMapping getMapping() { + public Mappings.@Nullable TargetMapping getMapping() { return Project.getMapping(input.rowType.getFieldCount(), projects); } diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRel.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRel.java index fca5377e8912..4a08d121c748 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRel.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRel.java @@ -24,6 +24,8 @@ import com.google.common.base.Equivalence; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -64,7 +66,7 @@ public abstract class MutableRel { public final RelDataType rowType; protected final MutableRelType type; - protected MutableRel parent; + protected @Nullable MutableRel parent; protected int ordinalInParent; protected MutableRel(RelOptCluster cluster, @@ -74,7 +76,7 @@ protected MutableRel(RelOptCluster cluster, this.type = Objects.requireNonNull(type); } - public MutableRel getParent() { + public @Nullable MutableRel getParent() { return parent; } @@ -94,7 +96,7 @@ public MutableRel getParent() { * * @return The parent */ - public MutableRel replaceInParent(MutableRel child) { + public @Nullable MutableRel replaceInParent(MutableRel child) { final MutableRel parent = this.parent; if (this != child) { if (parent != null) { @@ -124,7 +126,7 @@ private static class MutableRelDumper extends MutableRelVisitor { private final StringBuilder buf = new StringBuilder(); private int level; - @Override public void visit(MutableRel node) { + @Override public void visit(@Nullable MutableRel node) { Spaces.append(buf, level * 2); if (node == null) { buf.append("null"); diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRelVisitor.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRelVisitor.java index 85532a0e9786..9fe5020ae969 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRelVisitor.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRelVisitor.java @@ -16,17 +16,19 @@ */ package org.apache.calcite.rel.mutable; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Visitor over {@link MutableRel}. */ public class MutableRelVisitor { - private MutableRel root; - public void visit(MutableRel node) { - node.childrenAccept(this); + public void visit(@Nullable MutableRel node) { + if (node != null) { + node.childrenAccept(this); + } } public MutableRel go(MutableRel p) { - this.root = p; visit(p); - return root; + return p; } } diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java index 996e44c2ee1a..6be8716e7eae 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java @@ -60,10 +60,13 @@ import org.apache.calcite.util.mapping.MappingType; import org.apache.calcite.util.mapping.Mappings; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** Utilities for dealing with {@link MutableRel}s. */ @@ -77,8 +80,8 @@ public static boolean contains(MutableRel ancestor, } try { new MutableRelVisitor() { - @Override public void visit(MutableRel node) { - if (node.equals(target)) { + @Override public void visit(@Nullable MutableRel node) { + if (Objects.equals(node, target)) { throw Util.FoundOne.NULL; } super.visit(node); @@ -91,7 +94,7 @@ public static boolean contains(MutableRel ancestor, } } - public static MutableRel preOrderTraverseNext(MutableRel node) { + public static @Nullable MutableRel preOrderTraverseNext(MutableRel node) { MutableRel parent = node.getParent(); int ordinal = node.ordinalInParent + 1; while (parent != null) { diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSample.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSample.java index 84bdf7f4b650..1fe63f6cf903 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSample.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSample.java @@ -18,6 +18,8 @@ import org.apache.calcite.plan.RelOptSamplingParameters; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Sample}. */ @@ -40,7 +42,7 @@ public static MutableSample of( return new MutableSample(input, params); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableSample && params.equals(((MutableSample) obj).params) diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableScan.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableScan.java index 4efc2d141708..e2450e725835 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableScan.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableScan.java @@ -16,8 +16,14 @@ */ package org.apache.calcite.rel.mutable; +import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.rel.core.TableScan; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; +import java.util.Objects; + /** Mutable equivalent of {@link org.apache.calcite.rel.core.TableScan}. */ public class MutableScan extends MutableLeafRel { private MutableScan(TableScan rel) { @@ -33,20 +39,27 @@ public static MutableScan of(TableScan scan) { return new MutableScan(scan); } - @Override public boolean equals(Object obj) { + private @Nullable List tableQualifiedName() { + RelOptTable table = rel.getTable(); + return table == null ? null : table.getQualifiedName(); + } + + @Override public boolean equals(@Nullable Object obj) { + if (!(obj instanceof MutableScan)) { + return false; + } + MutableScan other = (MutableScan) obj; return obj == this - || obj instanceof MutableScan - && rel.getTable().getQualifiedName().equals(((MutableScan) obj).rel - .getTable().getQualifiedName()); + || Objects.equals(tableQualifiedName(), other.tableQualifiedName()); } @Override public int hashCode() { - return rel.getTable().getQualifiedName().hashCode(); + return Objects.hashCode(tableQualifiedName()); } @Override public StringBuilder digest(StringBuilder buf) { return buf.append("Scan(table: ") - .append(rel.getTable().getQualifiedName()).append(")"); + .append(tableQualifiedName()).append(")"); } @Override public MutableRel clone() { diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSetOp.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSetOp.java index c6d15d19bce4..500a56bcfbb5 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSetOp.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSetOp.java @@ -19,6 +19,8 @@ import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.rel.type.RelDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -36,7 +38,7 @@ public boolean isAll() { return all; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableSetOp && type == ((MutableSetOp) obj).type diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSingleRel.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSingleRel.java index 3b7e4788c649..995dc889c26e 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSingleRel.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSingleRel.java @@ -26,6 +26,7 @@ abstract class MutableSingleRel extends MutableRel { protected MutableRel input; + @SuppressWarnings("initialization.invalid.field.write.initialized") protected MutableSingleRel(MutableRelType type, RelDataType rowType, MutableRel input) { super(input.cluster, rowType, type); diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSort.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSort.java index 2aa5c17fe3f2..904c6ce64bb0 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableSort.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableSort.java @@ -19,16 +19,18 @@ import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Sort}. */ public class MutableSort extends MutableSingleRel { public final RelCollation collation; - public final RexNode offset; - public final RexNode fetch; + public final @Nullable RexNode offset; + public final @Nullable RexNode fetch; private MutableSort(MutableRel input, RelCollation collation, - RexNode offset, RexNode fetch) { + @Nullable RexNode offset, @Nullable RexNode fetch) { super(MutableRelType.SORT, input.rowType, input); this.collation = collation; this.offset = offset; @@ -45,11 +47,11 @@ private MutableSort(MutableRel input, RelCollation collation, * @param fetch Expression for number of rows to fetch */ public static MutableSort of(MutableRel input, RelCollation collation, - RexNode offset, RexNode fetch) { + @Nullable RexNode offset, @Nullable RexNode fetch) { return new MutableSort(input, collation, offset, fetch); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableSort && collation.equals(((MutableSort) obj).collation) diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableFunctionScan.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableFunctionScan.java index 3e671608072a..7135c962a23a 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableFunctionScan.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableFunctionScan.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; import java.util.Objects; @@ -30,12 +32,12 @@ * {@link org.apache.calcite.rel.core.TableFunctionScan}. */ public class MutableTableFunctionScan extends MutableMultiRel { public final RexNode rexCall; - public final Type elementType; - public final Set columnMappings; + public final @Nullable Type elementType; + public final @Nullable Set columnMappings; private MutableTableFunctionScan(RelOptCluster cluster, RelDataType rowType, List inputs, RexNode rexCall, - Type elementType, Set columnMappings) { + @Nullable Type elementType, @Nullable Set columnMappings) { super(cluster, rowType, MutableRelType.TABLE_FUNCTION_SCAN, inputs); this.rexCall = rexCall; this.elementType = elementType; @@ -55,12 +57,12 @@ private MutableTableFunctionScan(RelOptCluster cluster, */ public static MutableTableFunctionScan of(RelOptCluster cluster, RelDataType rowType, List inputs, RexNode rexCall, - Type elementType, Set columnMappings) { + @Nullable Type elementType, @Nullable Set columnMappings) { return new MutableTableFunctionScan( cluster, rowType, inputs, rexCall, elementType, columnMappings); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableTableFunctionScan && STRING_EQUIVALENCE.equivalent(rexCall, diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableModify.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableModify.java index 1103cad04f84..d84047356b05 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableModify.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableTableModify.java @@ -22,6 +22,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -30,14 +32,14 @@ public class MutableTableModify extends MutableSingleRel { public final Prepare.CatalogReader catalogReader; public final RelOptTable table; public final Operation operation; - public final List updateColumnList; - public final List sourceExpressionList; + public final @Nullable List updateColumnList; + public final @Nullable List sourceExpressionList; public final boolean flattened; private MutableTableModify(RelDataType rowType, MutableRel input, RelOptTable table, Prepare.CatalogReader catalogReader, - Operation operation, List updateColumnList, - List sourceExpressionList, boolean flattened) { + Operation operation, @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { super(MutableRelType.TABLE_MODIFY, rowType, input); this.table = table; this.catalogReader = catalogReader; @@ -64,13 +66,13 @@ private MutableTableModify(RelDataType rowType, MutableRel input, public static MutableTableModify of(RelDataType rowType, MutableRel input, RelOptTable table, Prepare.CatalogReader catalogReader, - Operation operation, List updateColumnList, - List sourceExpressionList, boolean flattened) { + Operation operation, @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { return new MutableTableModify(rowType, input, table, catalogReader, operation, updateColumnList, sourceExpressionList, flattened); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableTableModify && table.getQualifiedName().equals( diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableUncollect.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableUncollect.java index 49d65efab341..594d109b5d69 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableUncollect.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableUncollect.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.type.RelDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** Mutable equivalent of {@link org.apache.calcite.rel.core.Uncollect}. */ @@ -43,7 +45,7 @@ public static MutableUncollect of(RelDataType rowType, return new MutableUncollect(rowType, input, withOrdinality); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableUncollect && withOrdinality == ((MutableUncollect) obj).withOrdinality diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableValues.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableValues.java index 53e0223327f9..2f921bc1b696 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableValues.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableValues.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.core.Values; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Mutable equivalent of {@link org.apache.calcite.rel.core.Values}. */ public class MutableValues extends MutableLeafRel { private MutableValues(Values rel) { @@ -33,7 +35,7 @@ public static MutableValues of(Values values) { return new MutableValues(values); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableValues && rel == ((MutableValues) obj).rel; diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableWindow.java b/core/src/main/java/org/apache/calcite/rel/mutable/MutableWindow.java index b098b5758b52..013d9534764d 100644 --- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableWindow.java +++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableWindow.java @@ -20,6 +20,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexLiteral; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -48,7 +50,7 @@ public static MutableWindow of(RelDataType rowType, return new MutableWindow(rowType, input, groups, constants); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof MutableWindow && groups.equals(((MutableWindow) obj).groups) diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java index 4c0393640b79..ae4da35cfc33 100644 --- a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java +++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java @@ -19,6 +19,7 @@ import org.apache.calcite.adapter.jdbc.JdbcTable; import org.apache.calcite.linq4j.Ord; import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelCollations; import org.apache.calcite.rel.RelFieldCollation; @@ -92,6 +93,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -119,6 +122,7 @@ public class RelToSqlConverter extends SqlImplementor private final Deque stack = new ArrayDeque<>(); /** Creates a RelToSqlConverter. */ + @SuppressWarnings("argument.type.incompatible") public RelToSqlConverter(SqlDialect dialect) { super(dialect); dispatcher = ReflectUtil.createMethodDispatcher(Result.class, this, "visit", @@ -147,7 +151,7 @@ protected Result dispatch(RelNode e) { } @Override protected Result result(SqlNode node, Collection clauses, - String neededAlias, RelDataType neededType, + @Nullable String neededAlias, @Nullable RelDataType neededType, Map aliases) { final Frame frame = Objects.requireNonNull(stack.peek()); return super.result(node, clauses, neededAlias, neededType, aliases) @@ -675,7 +679,7 @@ dual, createAlwaysFalseCondition(), null, return result(query, clauses, e, null); } - private SqlIdentifier getDual() { + private @Nullable SqlIdentifier getDual() { final List names = dialect.getSingleRowTableName(); if (names == null) { return null; @@ -784,13 +788,15 @@ public boolean hasTrickyRollup(Sort e, Aggregate aggregate) { private SqlIdentifier getSqlTargetTable(RelNode e) { final SqlIdentifier sqlTargetTable; - final JdbcTable jdbcTable = e.getTable().unwrap(JdbcTable.class); + RelOptTable table = e.getTable(); + assert table != null : "e.getTable() must not be null for " + e; + final JdbcTable jdbcTable = table.unwrap(JdbcTable.class); if (jdbcTable != null) { // Use the foreign catalog, schema and table names, if they exist, // rather than the qualified name of the shadow table in Calcite. sqlTargetTable = jdbcTable.tableName(); } else { - final List qualifiedName = e.getTable().getQualifiedName(); + final List qualifiedName = table.getQualifiedName(); sqlTargetTable = new SqlIdentifier(qualifiedName, SqlParserPos.ZERO); } diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java index b61e4b8daa45..d06a465617e0 100644 --- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java +++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java @@ -92,6 +92,9 @@ import com.google.common.collect.Lists; import com.google.common.collect.Range; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.AbstractList; import java.util.ArrayDeque; @@ -107,12 +110,13 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.function.IntFunction; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; /** * State for generating a SQL statement. @@ -146,7 +150,7 @@ public abstract class SqlImplementor { .build(); protected SqlImplementor(SqlDialect dialect) { - this.dialect = Objects.requireNonNull(dialect); + this.dialect = requireNonNull(dialect); } /** Visits a relational expression that has no parent. */ @@ -264,6 +268,8 @@ public Result setOpToSql(SqlSetOperator operator, RelNode rel) { node = operator.createCall(POS, node, result.asSelect()); } } + assert node != null : "set op must have at least one input, operator = " + operator + + ", rel = " + rel; final List clauses = Expressions.list(Clause.SET_OP); return result(node, clauses, rel, null); @@ -458,7 +464,7 @@ public static JoinType joinType(JoinRelType joinType) { /** Creates a result based on a single relational expression. */ public Result result(SqlNode node, Collection clauses, - RelNode rel, Map aliases) { + RelNode rel, @Nullable Map aliases) { assert aliases == null || aliases.size() < 2 || aliases instanceof LinkedHashMap @@ -493,7 +499,7 @@ public Result result(SqlNode node, Collection clauses, *

Call this method rather than creating a {@code Result} directly, * because sub-classes may override. */ protected Result result(SqlNode node, Collection clauses, - String neededAlias, RelDataType neededType, + @Nullable String neededAlias, @Nullable RelDataType neededType, Map aliases) { return new Result(node, clauses, neededAlias, neededType, aliases); } @@ -679,14 +685,14 @@ public SqlNode orderField(int ordinal) { * @param program Required only if {@code rex} contains {@link RexLocalRef} * @param rex Expression to convert */ - public SqlNode toSql(RexProgram program, RexNode rex) { + public SqlNode toSql(@Nullable RexProgram program, RexNode rex) { final RexSubQuery subQuery; final SqlNode sqlSubQuery; final RexLiteral literal; switch (rex.getKind()) { case LOCAL_REF: final int index = ((RexLocalRef) rex).getIndex(); - return toSql(program, program.getExprList().get(index)); + return toSql(program, requireNonNull(program, "program").getExprList().get(index)); case INPUT_REF: return field(((RexInputRef) rex).getIndex()); @@ -785,7 +791,7 @@ public SqlNode toSql(RexProgram program, RexNode rex) { case SEARCH: final RexCall search = (RexCall) rex; literal = (RexLiteral) search.operands.get(1); - final Sarg sarg = literal.getValueAs(Sarg.class); + final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class)); //noinspection unchecked return toSql(program, search.operands.get(0), literal.getType(), sarg); @@ -846,7 +852,7 @@ public SqlNode toSql(RexProgram program, RexNode rex) { assert nodeList.size() == 1; return nodeList.get(0); } else { - nodeList.add(dialect.getCastSpec(call.getType())); + nodeList.add(castNonNull(dialect.getCastSpec(call.getType()))); } break; default: @@ -859,7 +865,7 @@ public SqlNode toSql(RexProgram program, RexNode rex) { /** Converts a Sarg to SQL, generating "operand IN (c1, c2, ...)" if the * ranges are all points. */ @SuppressWarnings({"BetaApi", "UnstableApiUsage"}) - private > SqlNode toSql(RexProgram program, + private > SqlNode toSql(@Nullable RexProgram program, RexNode operand, RelDataType type, Sarg sarg) { final List orList = new ArrayList<>(); final SqlNode operandSql = toSql(program, operand); @@ -969,7 +975,7 @@ protected Context getAliasContext(RexCorrelVariable variable) { throw new UnsupportedOperationException(); } - private SqlCall toSql(RexProgram program, RexOver rexOver) { + private SqlCall toSql(@Nullable RexProgram program, RexOver rexOver) { final RexWindow rexWindow = rexOver.getWindow(); final SqlNodeList partitionList = new SqlNodeList( toSql(program, rexWindow.partitionKeys), POS); @@ -1024,7 +1030,7 @@ private SqlCall createOverCall(SqlAggFunction op, List operands, window); } - private SqlNode toSql(RexProgram program, RexFieldCollation rfc) { + private SqlNode toSql(@Nullable RexProgram program, RexFieldCollation rfc) { SqlNode node = toSql(program, rfc.left); switch (rfc.getDirection()) { case DESCENDING: @@ -1075,7 +1081,7 @@ private SqlNode createSqlWindowBound(RexWindowBound rexWindowBound) { + rexWindowBound); } - private List toSql(RexProgram program, List operandList) { + private List toSql(@Nullable RexProgram program, List operandList) { final List list = new ArrayList<>(); for (RexNode rex : operandList) { list.add(toSql(program, rex)); @@ -1114,7 +1120,7 @@ void addOrderItem(List orderByList, RelFieldCollation field) { /** Converts a RexFieldCollation to an ORDER BY item. */ private void addOrderItem(List orderByList, - RexProgram program, RexFieldCollation field) { + @Nullable RexProgram program, RexFieldCollation field) { SqlNode node = toSql(program, field.left); SqlNode nullDirectionNode = null; if (field.getNullDirection() != RelFieldCollation.NullDirection.UNSPECIFIED) { @@ -1320,7 +1326,7 @@ private SqlNode op(SqlOperator op, C value) { /** Converts a {@link RexLiteral} in the context of a {@link RexProgram} * to a {@link SqlNode}. */ - public static SqlNode toSql(RexProgram program, RexLiteral literal) { + public static SqlNode toSql(@Nullable RexProgram program, RexLiteral literal) { switch (literal.getTypeName()) { case SYMBOL: final Enum symbol = (Enum) literal.getValue(); @@ -1328,7 +1334,7 @@ public static SqlNode toSql(RexProgram program, RexLiteral literal) { case ROW: //noinspection unchecked - final List list = literal.getValueAs(List.class); + final List list = castNonNull(literal.getValueAs(List.class)); return SqlStdOperatorTable.ROW.createCall(POS, list.stream().map(e -> toSql(program, e)) .collect(Util.toImmutableList())); @@ -1345,14 +1351,15 @@ public static SqlNode toSql(RexProgram program, RexLiteral literal) { /** Converts a {@link RexLiteral} to a {@link SqlLiteral}. */ public static SqlNode toSql(RexLiteral literal) { - switch (literal.getTypeName()) { + SqlTypeName typeName = literal.getTypeName(); + switch (typeName) { case SYMBOL: final Enum symbol = (Enum) literal.getValue(); return SqlLiteral.createSymbol(symbol, POS); case ROW: //noinspection unchecked - final List list = literal.getValueAs(List.class); + final List list = castNonNull(literal.getValueAs(List.class)); return SqlStdOperatorTable.ROW.createCall(POS, list.stream().map(e -> toSql(e)) .collect(Util.toImmutableList())); @@ -1364,38 +1371,40 @@ public static SqlNode toSql(RexLiteral literal) { default: break; } - switch (literal.getTypeName().getFamily()) { + SqlTypeFamily family = requireNonNull(typeName.getFamily(), + () -> "literal " + literal + " has null SqlTypeFamily, and is SqlTypeName is " + typeName); + switch (family) { case CHARACTER: - return SqlLiteral.createCharString((String) literal.getValue2(), POS); + return SqlLiteral.createCharString((String) castNonNull(literal.getValue2()), POS); case NUMERIC: case EXACT_NUMERIC: return SqlLiteral.createExactNumeric( - literal.getValueAs(BigDecimal.class).toPlainString(), POS); + castNonNull(literal.getValueAs(BigDecimal.class)).toPlainString(), POS); case APPROXIMATE_NUMERIC: return SqlLiteral.createApproxNumeric( - literal.getValueAs(BigDecimal.class).toPlainString(), POS); + castNonNull(literal.getValueAs(BigDecimal.class)).toPlainString(), POS); case BOOLEAN: - return SqlLiteral.createBoolean(literal.getValueAs(Boolean.class), + return SqlLiteral.createBoolean(castNonNull(literal.getValueAs(Boolean.class)), POS); case INTERVAL_YEAR_MONTH: case INTERVAL_DAY_TIME: - final boolean negative = literal.getValueAs(Boolean.class); + final boolean negative = castNonNull(literal.getValueAs(Boolean.class)); return SqlLiteral.createInterval(negative ? -1 : 1, - literal.getValueAs(String.class), - literal.getType().getIntervalQualifier(), POS); + castNonNull(literal.getValueAs(String.class)), + castNonNull(literal.getType().getIntervalQualifier()), POS); case DATE: - return SqlLiteral.createDate(literal.getValueAs(DateString.class), + return SqlLiteral.createDate(castNonNull(literal.getValueAs(DateString.class)), POS); case TIME: - return SqlLiteral.createTime(literal.getValueAs(TimeString.class), + return SqlLiteral.createTime(castNonNull(literal.getValueAs(TimeString.class)), literal.getType().getPrecision(), POS); case TIMESTAMP: return SqlLiteral.createTimestamp( - literal.getValueAs(TimestampString.class), + castNonNull(literal.getValueAs(TimestampString.class)), literal.getType().getPrecision(), POS); case ANY: case NULL: - switch (literal.getTypeName()) { + switch (typeName) { case NULL: return SqlLiteral.createNull(POS); default: @@ -1403,7 +1412,7 @@ public static SqlNode toSql(RexLiteral literal) { } // fall through default: - throw new AssertionError(literal + ": " + literal.getTypeName()); + throw new AssertionError(literal + ": " + typeName); } } @@ -1412,7 +1421,7 @@ public static SqlNode toSql(RexLiteral literal) { * {@link SqlImplementor} or {@link org.apache.calcite.tools.RelBuilder} * to use it. It is a good way to convert a {@link RexNode} to SQL text. */ public static class SimpleContext extends Context { - @Nonnull private final IntFunction field; + private final IntFunction field; public SimpleContext(SqlDialect dialect, IntFunction field) { super(dialect, 0, false); @@ -1436,7 +1445,9 @@ protected abstract class BaseContext extends Context { } @Override protected Context getAliasContext(RexCorrelVariable variable) { - return correlTableMap.get(variable.id); + return requireNonNull( + correlTableMap.get(variable.id), + () -> "variable " + variable.id + " is not found"); } @Override public SqlImplementor implementor() { @@ -1477,11 +1488,11 @@ protected MatchRecognizeContext(SqlDialect dialect, super(dialect, aliases, false); } - @Override public SqlNode toSql(RexProgram program, RexNode rex) { + @Override public SqlNode toSql(@Nullable RexProgram program, RexNode rex) { if (rex.getKind() == SqlKind.LITERAL) { final RexLiteral literal = (RexLiteral) rex; if (literal.getTypeName().getFamily() == SqlTypeFamily.CHARACTER) { - return new SqlIdentifier(RexLiteral.stringValue(literal), POS); + return new SqlIdentifier(castNonNull(RexLiteral.stringValue(literal)), POS); } } return super.toSql(program, rex); @@ -1559,8 +1570,8 @@ class TableFunctionScanContext extends BaseContext { /** Result of implementing a node. */ public class Result { final SqlNode node; - final String neededAlias; - private final RelDataType neededType; + final @Nullable String neededAlias; + private final @Nullable RelDataType neededType; private final Map aliases; final List clauses; private final boolean anon; @@ -1570,19 +1581,19 @@ public class Result { /** Clauses that will be generated to implement current relational * expression. */ private final ImmutableSet expectedClauses; - private final RelNode expectedRel; + private final @Nullable RelNode expectedRel; private final boolean needNew; - public Result(SqlNode node, Collection clauses, String neededAlias, - RelDataType neededType, Map aliases) { + public Result(SqlNode node, Collection clauses, @Nullable String neededAlias, + @Nullable RelDataType neededType, Map aliases) { this(node, clauses, neededAlias, neededType, aliases, false, false, ImmutableSet.of(), null); } - private Result(SqlNode node, Collection clauses, String neededAlias, - RelDataType neededType, Map aliases, boolean anon, + private Result(SqlNode node, Collection clauses, @Nullable String neededAlias, + @Nullable RelDataType neededType, Map aliases, boolean anon, boolean ignoreClauses, Set expectedClauses, - RelNode expectedRel) { + @Nullable RelNode expectedRel) { this.node = node; this.neededAlias = neededAlias; this.neededType = neededType; @@ -1727,7 +1738,9 @@ private Builder builder(RelNode rel, Set clauses) { } /** Returns whether a new sub-query is required. */ - private boolean needNewSubQuery(RelNode rel, List clauses, + private boolean needNewSubQuery( + @UnknownInitialization Result this, + RelNode rel, List clauses, Set expectedClauses) { if (clauses.isEmpty()) { return false; @@ -1775,7 +1788,9 @@ private boolean needNewSubQuery(RelNode rel, List clauses, return false; } - private boolean hasNestedAggregations(Aggregate rel) { + private boolean hasNestedAggregations( + @UnknownInitialization Result this, + Aggregate rel) { if (node instanceof SqlSelect) { final SqlNodeList selectList = ((SqlSelect) node).getSelectList(); if (selectList != null) { @@ -1816,7 +1831,9 @@ public SqlNode asFrom() { // If we already have an AS node, we need to replace the alias // This is especially relevant for the VALUES clause rendering SqlCall sqlCall = (SqlCall) node; - SqlNode[] operands = sqlCall.getOperandList().toArray(SqlNode.EMPTY_ARRAY); + @SuppressWarnings({"assignment.type.incompatible", + "toArray.nullable.elements.not.newarray"}) + SqlNode[] operands = sqlCall.getOperandList().toArray(new SqlNode[0]); operands[1] = new SqlIdentifier(neededAlias, POS); return SqlStdOperatorTable.AS.createCall(POS, operands); } else { @@ -1938,7 +1955,7 @@ public Result resetAlias() { return this; } else { return new Result(node, clauses, neededAlias, neededType, - ImmutableMap.of(neededAlias, neededType), anon, ignoreClauses, + ImmutableMap.of(neededAlias, castNonNull(neededType)), anon, ignoreClauses, expectedClauses, expectedRel); } } @@ -1982,15 +1999,15 @@ public class Builder { final SqlSelect select; public final Context context; final boolean anon; - private final Map aliases; + private final @Nullable Map aliases; public Builder(RelNode rel, List clauses, SqlSelect select, Context context, boolean anon, @Nullable Map aliases) { - this.rel = Objects.requireNonNull(rel); + this.rel = requireNonNull(rel); this.clauses = ImmutableList.copyOf(clauses); - this.select = Objects.requireNonNull(select); - this.context = Objects.requireNonNull(context); + this.select = requireNonNull(select); + this.context = requireNonNull(context); this.anon = anon; this.aliases = aliases; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java index 2c52c003d65b..c38146ecd442 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java @@ -39,9 +39,10 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; /** * Rule that converts CASE-style filtered aggregates into true filtered diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java index 71abd2b1cd27..f7eb570ac993 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java @@ -48,6 +48,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -212,7 +214,7 @@ public AggregateExpandDistinctAggregatesRule( // Initially, the expressions point to the input field. final List aggFields = aggregate.getRowType().getFieldList(); - final List refs = new ArrayList<>(); + final List<@Nullable RexInputRef> refs = new ArrayList<>(); final List fieldNames = aggregate.getRowType().getFieldNames(); final ImmutableBitSet groupSet = aggregate.getGroupSet(); final int groupCount = aggregate.getGroupCount(); @@ -629,7 +631,7 @@ private RelBuilder convertMonopole(RelBuilder relBuilder, Aggregate aggregate, * be modified @return Relational expression */ private void doRewrite(RelBuilder relBuilder, Aggregate aggregate, int n, - List argList, int filterArg, List refs) { + List argList, int filterArg, List<@Nullable RexInputRef> refs) { final RexBuilder rexBuilder = aggregate.getCluster().getRexBuilder(); final List leftFields; if (n == 0) { diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java index d65d611f049d..455d773d3d62 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java @@ -48,6 +48,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; @@ -449,7 +451,7 @@ private static SqlSplittableAggFunction.Registry registry( /** Work space for an input to a join. */ private static class Side { final Map split = new HashMap<>(); - RelNode newInput; + @Nullable RelNode newInput; boolean aggregate; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java index cca5867f0dd1..c23cb71ac6e3 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java @@ -34,12 +34,16 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Planner rule that recognizes a {@link org.apache.calcite.rel.core.Aggregate} * on top of a {@link org.apache.calcite.rel.core.Project} and if possible @@ -82,7 +86,7 @@ public AggregateProjectMergeRule( } } - public static RelNode apply(RelOptRuleCall call, Aggregate aggregate, + public static @Nullable RelNode apply(RelOptRuleCall call, Aggregate aggregate, Project project) { // Find all fields which we need to be straightforward field projections. final Set interestingFields = RelOptUtil.getAllFields(aggregate); @@ -125,7 +129,9 @@ public static RelNode apply(RelOptRuleCall call, Aggregate aggregate, final RelBuilder relBuilder = call.builder(); relBuilder.push(newAggregate); final List newKeys = - Util.transform(aggregate.getGroupSet().asList(), map::get); + Util.transform(aggregate.getGroupSet().asList(), + key -> requireNonNull(map.get(key), + () -> "no value found for key " + key + " in " + map)); if (!newKeys.equals(newGroupSet.asList())) { final List posList = new ArrayList<>(); for (int newKey : newKeys) { diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java index b044c6dbc928..7460d8505952 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java @@ -94,7 +94,7 @@ public AggregateProjectPullUpConstantsRule( final RelMetadataQuery mq = call.getMetadataQuery(); final RelOptPredicateList predicates = mq.getPulledUpPredicates(aggregate.getInput()); - if (predicates == null) { + if (RelOptPredicateList.isEmpty(predicates)) { return; } final NavigableMap map = new TreeMap<>(); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java index ef83d498290d..b058868018cd 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java @@ -45,6 +45,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; @@ -54,7 +56,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; /** * Planner rule that reduces aggregate functions in @@ -873,14 +874,14 @@ public interface Config extends RelRule.Config { } @ImmutableBeans.Property - Set functionsToReduce(); + @Nullable Set functionsToReduce(); /** Sets {@link #functionsToReduce}. */ - Config withFunctionsToReduce(Set functionSet); + Config withFunctionsToReduce(@Nullable Set functionSet); /** Returns the validated set of functions to reduce, or the default set * if not specified. */ - @Nonnull default Set actualFunctionsToReduce() { + @NonNull default Set actualFunctionsToReduce() { final Set set = Util.first(functionsToReduce(), DEFAULT_FUNCTIONS_TO_REDUCE); set.forEach(AggregateReduceFunctionsRule::validateFunction); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java index d7ee66cc38ed..caec9d6a7e1a 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java @@ -47,6 +47,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -86,7 +88,7 @@ public AggregateStarTableRule(RelOptRuleOperand operand, apply(call, null, aggregate, scan); } - protected void apply(RelOptRuleCall call, Project postProject, + protected void apply(RelOptRuleCall call, @Nullable Project postProject, final Aggregate aggregate, StarTable.StarTableScan scan) { final RelOptPlanner planner = call.getPlanner(); final CalciteConnectionConfig config = @@ -186,7 +188,7 @@ protected void apply(RelOptRuleCall call, Project postProject, call.transformTo(relBuilder.build()); } - private static AggregateCall rollUp(int groupCount, RelBuilder relBuilder, + private static @Nullable AggregateCall rollUp(int groupCount, RelBuilder relBuilder, AggregateCall aggregateCall, TileKey tileKey) { if (aggregateCall.isDistinct()) { return null; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java index 5bd165e03ccb..251dc1e988c2 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java @@ -43,6 +43,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; @@ -157,7 +159,7 @@ public AggregateUnionTransposeRule(Class aggregateClass, call.transformTo(relBuilder.build()); } - private List transformAggCalls(RelNode input, int groupCount, + private @Nullable List transformAggCalls(RelNode input, int groupCount, List origCalls) { final List newCalls = new ArrayList<>(); for (Ord ord : Ord.zip(origCalls)) { diff --git a/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java b/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java index 45b89a458feb..f66e210bb9eb 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/CalcRelSplitter.java @@ -46,6 +46,7 @@ import com.google.common.base.Preconditions; import com.google.common.primitives.Ints; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.io.PrintWriter; @@ -468,7 +469,7 @@ private List computeTopologicalOrdering( * @param ordinal Integer to search for * @return Cohort that contains the integer, or null if not found */ - private static Set findCohort( + private static @Nullable Set findCohort( List> cohorts, int ordinal) { for (Set cohort : cohorts) { @@ -521,7 +522,7 @@ private RexProgram createProgramForLevel( int[] inputExprOrdinals, final int[] projectExprOrdinals, int conditionExprOrdinal, - RelDataType outputRowType) { + @Nullable RelDataType outputRowType) { // Build a list of expressions to form the calc. List exprs = new ArrayList<>(); @@ -972,7 +973,8 @@ private static class HighestUsageFinder extends RexVisitorImpl { continue; } currentLevel = exprLevels[i]; - exprs[i].accept(this); + @SuppressWarnings("argument.type.incompatible") + final Void unused = exprs[i].accept(this); } } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/CoerceInputsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/CoerceInputsRule.java index ea5b5ca2bd4a..4d8d057166b3 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/CoerceInputsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/CoerceInputsRule.java @@ -25,6 +25,8 @@ import org.apache.calcite.tools.RelBuilderFactory; import org.apache.calcite.util.ImmutableBeans; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -66,7 +68,7 @@ public CoerceInputsRule(Class consumerRelClass, //~ Methods ---------------------------------------------------------------- - @Override public Convention getOutConvention() { + @Override public @Nullable Convention getOutConvention() { return Convention.NONE; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java index 0b4e31a81fe6..141f3d899fe4 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java @@ -51,6 +51,8 @@ import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayDeque; import java.util.ArrayList; @@ -63,7 +65,8 @@ import java.util.Objects; import java.util.Set; import java.util.TimeZone; -import javax.annotation.Nonnull; + +import static java.util.Objects.requireNonNull; /** * Collection of planner rules that convert @@ -119,6 +122,12 @@ private DateRangeRules() {} .put(TimeUnitRange.MICROSECOND, TimeUnitRange.SECOND) .build(); + private static int calendarUnitFor(TimeUnitRange timeUnitRange) { + return requireNonNull(TIME_UNIT_CODES.get(timeUnitRange), + () -> "unexpected timeUnitRange: " + timeUnitRange + + ", the following are supported: " + TIME_UNIT_CODES); + } + /** Tests whether an expression contains one or more calls to the * {@code EXTRACT} function, and if so, returns the time units used. * @@ -194,7 +203,7 @@ private static boolean containsRoundingExpression(Filter filter) { final Filter filter = call.rel(0); final RexBuilder rexBuilder = filter.getCluster().getRexBuilder(); final String timeZone = filter.getCluster().getPlanner().getContext() - .unwrap(CalciteConnectionConfig.class).timeZone(); + .unwrapOrThrow(CalciteConnectionConfig.class).timeZone(); final RexNode condition = replaceTimeUnits(rexBuilder, filter.getCondition(), timeZone); if (condition.equals(filter.getCondition())) { @@ -230,7 +239,7 @@ private static class ExtractFinder extends RexVisitorImpl EnumSet.noneOf(TimeUnitRange.class); private final Set opKinds = EnumSet.noneOf(SqlKind.class); - private static final ThreadLocal THREAD_INSTANCES = + private static final ThreadLocal<@Nullable ExtractFinder> THREAD_INSTANCES = ThreadLocal.withInitial(ExtractFinder::new); private ExtractFinder() { @@ -278,10 +287,10 @@ static class ExtractShuttle extends RexShuttle { ExtractShuttle(RexBuilder rexBuilder, TimeUnitRange timeUnit, Map> operandRanges, ImmutableSortedSet timeUnitRanges, String timeZone) { - this.rexBuilder = Objects.requireNonNull(rexBuilder); - this.timeUnit = Objects.requireNonNull(timeUnit); - this.operandRanges = Objects.requireNonNull(operandRanges); - this.timeUnitRanges = Objects.requireNonNull(timeUnitRanges); + this.rexBuilder = requireNonNull(rexBuilder); + this.timeUnit = requireNonNull(timeUnit); + this.operandRanges = requireNonNull(operandRanges); + this.timeUnitRanges = requireNonNull(timeUnitRanges); this.timeZone = timeZone; } @@ -381,7 +390,7 @@ private boolean canRewriteExtract(RexNode operand) { } @Override protected List visitList(List exprs, - boolean[] update) { + boolean @Nullable [] update) { if (exprs.isEmpty()) { return ImmutableList.of(); // a bit more efficient } @@ -488,7 +497,7 @@ RexNode compareExtract(SqlKind comparison, RexNode operand, private boolean next(Calendar c, TimeUnitRange timeUnit, int v, Range r, boolean strict) { final Calendar original = (Calendar) c.clone(); - final int code = TIME_UNIT_CODES.get(timeUnit); + final int code = calendarUnitFor(timeUnit); for (;;) { c.set(code, v); int v2 = c.get(code); @@ -498,7 +507,7 @@ private boolean next(Calendar c, TimeUnitRange timeUnit, int v, continue; } if (strict && original.compareTo(c) == 0) { - c.add(TIME_UNIT_CODES.get(TIME_UNIT_PARENTS.get(timeUnit)), 1); + c.add(calendarUnitFor(TIME_UNIT_PARENTS.get(timeUnit)), 1); continue; } if (!r.contains(c)) { @@ -526,7 +535,7 @@ private static boolean isValid(int v, TimeUnitRange timeUnit) { } } - private @Nonnull RexNode toRex(RexNode operand, Range r) { + private RexNode toRex(RexNode operand, Range r) { final List nodes = new ArrayList<>(); if (r.hasLowerBound()) { final SqlBinaryOperator op = r.lowerBoundType() == BoundType.CLOSED @@ -597,7 +606,7 @@ private Range extractRange(TimeUnitRange timeUnit, SqlKind comparison, private Calendar round(Calendar c, TimeUnitRange timeUnit, boolean down) { c = (Calendar) c.clone(); if (!down) { - final Integer code = TIME_UNIT_CODES.get(timeUnit); + final Integer code = calendarUnitFor(timeUnit); final int v = c.get(code); c.set(code, v + 1); } @@ -696,13 +705,13 @@ boolean isFloorCeilCall(RexNode e) { private Calendar increment(Calendar c, TimeUnitRange timeUnit) { c = (Calendar) c.clone(); - c.add(TIME_UNIT_CODES.get(timeUnit), 1); + c.add(calendarUnitFor(timeUnit), 1); return c; } private Calendar decrement(Calendar c, TimeUnitRange timeUnit) { c = (Calendar) c.clone(); - c.add(TIME_UNIT_CODES.get(timeUnit), -1); + c.add(calendarUnitFor(timeUnit), -1); return c; } @@ -720,22 +729,22 @@ private Calendar floor(Calendar c, TimeUnitRange timeUnit) { c = (Calendar) c.clone(); switch (timeUnit) { case YEAR: - c.set(TIME_UNIT_CODES.get(TimeUnitRange.MONTH), Calendar.JANUARY); + c.set(calendarUnitFor(TimeUnitRange.MONTH), Calendar.JANUARY); // fall through; need to zero out lower time units case MONTH: - c.set(TIME_UNIT_CODES.get(TimeUnitRange.DAY), 1); + c.set(calendarUnitFor(TimeUnitRange.DAY), 1); // fall through; need to zero out lower time units case DAY: - c.set(TIME_UNIT_CODES.get(TimeUnitRange.HOUR), 0); + c.set(calendarUnitFor(TimeUnitRange.HOUR), 0); // fall through; need to zero out lower time units case HOUR: - c.set(TIME_UNIT_CODES.get(TimeUnitRange.MINUTE), 0); + c.set(calendarUnitFor(TimeUnitRange.MINUTE), 0); // fall through; need to zero out lower time units case MINUTE: - c.set(TIME_UNIT_CODES.get(TimeUnitRange.SECOND), 0); + c.set(calendarUnitFor(TimeUnitRange.SECOND), 0); // fall through; need to zero out lower time units case SECOND: - c.set(TIME_UNIT_CODES.get(TimeUnitRange.MILLISECOND), 0); + c.set(calendarUnitFor(TimeUnitRange.MILLISECOND), 0); break; default: break; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ExchangeRemoveConstantKeysRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ExchangeRemoveConstantKeysRule.java index 096162f787ee..f09c5c117a42 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ExchangeRemoveConstantKeysRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ExchangeRemoveConstantKeysRule.java @@ -80,7 +80,7 @@ private static void matchExchange(ExchangeRemoveConstantKeysRule rule, final RelMetadataQuery mq = call.getMetadataQuery(); final RelNode input = exchange.getInput(); final RelOptPredicateList predicates = mq.getPulledUpPredicates(input); - if (predicates == null) { + if (RelOptPredicateList.isEmpty(predicates)) { return; } @@ -115,7 +115,7 @@ private static void matchSortExchange(ExchangeRemoveConstantKeysRule rule, final RelMetadataQuery mq = call.getMetadataQuery(); final RelNode input = sortExchange.getInput(); final RelOptPredicateList predicates = mq.getPulledUpPredicates(input); - if (predicates == null) { + if (RelOptPredicateList.isEmpty(predicates)) { return; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java index 77804cd45025..eb49083d1036 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java @@ -36,6 +36,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -63,7 +65,7 @@ protected FilterJoinRule(C config) { //~ Methods ---------------------------------------------------------------- - protected void perform(RelOptRuleCall call, Filter filter, + protected void perform(RelOptRuleCall call, @Nullable Filter filter, Join join) { final List joinFilters = RelOptUtil.conjunctions(join.getCondition()); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java index b92881e954f4..2b086f04bf07 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java @@ -24,6 +24,8 @@ import org.apache.calcite.rex.RexUtil; import org.apache.calcite.tools.RelBuilderFactory; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.List; @@ -66,7 +68,7 @@ public FilterMultiJoinMergeRule(Class filterClass, // Create a new post-join filter condition // Conditions are nullable, so ImmutableList can't be used here - List filters = Arrays.asList( + List<@Nullable RexNode> filters = Arrays.asList( filter.getCondition(), multiJoin.getPostJoinFilter()); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java index bec08e053f0b..ee4752e2dfbc 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinCommuteRule.java @@ -36,6 +36,8 @@ import org.apache.calcite.tools.RelBuilderFactory; import org.apache.calcite.util.ImmutableBeans; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -84,13 +86,13 @@ public JoinCommuteRule(Class clazz, //~ Methods ---------------------------------------------------------------- @Deprecated // to be removed before 2.0 - public static RelNode swap(Join join) { + public static @Nullable RelNode swap(Join join) { return swap(join, false, RelFactories.LOGICAL_BUILDER.create(join.getCluster(), null)); } @Deprecated // to be removed before 2.0 - public static RelNode swap(Join join, boolean swapOuterJoins) { + public static @Nullable RelNode swap(Join join, boolean swapOuterJoins) { return swap(join, swapOuterJoins, RelFactories.LOGICAL_BUILDER.create(join.getCluster(), null)); } @@ -105,7 +107,7 @@ public static RelNode swap(Join join, boolean swapOuterJoins) { * @param relBuilder Builder for relational expressions * @return swapped join if swapping possible; else null */ - public static RelNode swap(Join join, boolean swapOuterJoins, + public static @Nullable RelNode swap(Join join, boolean swapOuterJoins, RelBuilder relBuilder) { final JoinRelType joinType = join.getJoinType(); if (!swapOuterJoins && joinType != JoinRelType.INNER) { diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinProjectTransposeRule.java index 2edd53e36bb1..70a0ec347433 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/JoinProjectTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinProjectTransposeRule.java @@ -42,6 +42,8 @@ import org.apache.calcite.util.ImmutableBeans; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -331,7 +333,7 @@ protected RelNode getProjectChild( * @param projects Projection expressions & names to be created */ protected void createProjectExprs( - Project project, + @Nullable Project project, RelNode joinChild, int adjustmentAmount, RexBuilder rexBuilder, diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinToMultiJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinToMultiJoinRule.java index 6edc34e71d09..1cce6bf3e1f9 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/JoinToMultiJoinRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinToMultiJoinRule.java @@ -36,11 +36,15 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * Planner rule to flatten a tree of * {@link org.apache.calcite.rel.logical.LogicalJoin}s @@ -138,7 +142,7 @@ public JoinToMultiJoinRule(Class joinClass, // combine the children MultiJoin inputs into an array of inputs // for the new MultiJoin - final List projFieldsList = new ArrayList<>(); + final List<@Nullable ImmutableBitSet> projFieldsList = new ArrayList<>(); final List joinFieldRefCountsList = new ArrayList<>(); final List newInputs = combineInputs( @@ -151,7 +155,7 @@ public JoinToMultiJoinRule(Class joinClass, // combine the outer join information from the left and right // inputs, and include the outer join information from the current // join, if it's a left/right outer join - final List> joinSpecs = new ArrayList<>(); + final List> joinSpecs = new ArrayList<>(); combineOuterJoins( origJoin, newInputs, @@ -162,7 +166,7 @@ public JoinToMultiJoinRule(Class joinClass, // pull up the join filters from the children MultiJoinRels and // combine them with the join filter associated with this LogicalJoin to // form the join filter for the new MultiJoin - List newJoinFilters = combineJoinFilters(origJoin, left, right); + List<@Nullable RexNode> newJoinFilters = combineJoinFilters(origJoin, left, right); // add on the join field reference counts for the join condition // associated with this LogicalJoin @@ -172,7 +176,7 @@ public JoinToMultiJoinRule(Class joinClass, origJoin.getCondition(), joinFieldRefCountsList); - List newPostJoinFilters = + List<@Nullable RexNode> newPostJoinFilters = combinePostJoinFilters(origJoin, left, right); final RexBuilder rexBuilder = origJoin.getCluster().getRexBuilder(); @@ -208,7 +212,7 @@ private List combineInputs( Join join, RelNode left, RelNode right, - List projFieldsList, + List<@Nullable ImmutableBitSet> projFieldsList, List joinFieldRefCountsList) { final List newInputs = new ArrayList<>(); @@ -267,7 +271,7 @@ private void combineOuterJoins( @SuppressWarnings("unused") List combinedInputs, RelNode left, RelNode right, - List> joinSpecs) { + List> joinSpecs) { JoinRelType joinType = joinRel.getJoinType(); boolean leftCombined = canCombine(left, joinType.generatesNullsOnLeft()); @@ -283,7 +287,7 @@ private void combineOuterJoins( null, null); } else { - joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null)); + joinSpecs.add(Pair.of(JoinRelType.INNER, (@Nullable RexNode) null)); } joinSpecs.add(Pair.of(joinType, joinRel.getCondition())); break; @@ -340,11 +344,11 @@ private void combineOuterJoins( */ private void copyOuterJoinInfo( MultiJoin multiJoin, - List> destJoinSpecs, + List> destJoinSpecs, int adjustmentAmount, - List srcFields, - List destFields) { - final List> srcJoinSpecs = + @Nullable List srcFields, + @Nullable List destFields) { + final List> srcJoinSpecs = Pair.zip( multiJoin.getJoinTypes(), multiJoin.getOuterJoinConditions()); @@ -385,7 +389,7 @@ private void copyOuterJoinInfo( * @param right Right input of the join * @return combined join filters AND-ed together */ - private List combineJoinFilters( + private List<@Nullable RexNode> combineJoinFilters( Join join, RelNode left, RelNode right) { @@ -394,7 +398,7 @@ private List combineJoinFilters( // AND the join condition if this isn't a left or right outer join; // in those cases, the outer join condition is already tracked // separately - final List filters = new ArrayList<>(); + final List<@Nullable RexNode> filters = new ArrayList<>(); if ((joinType != JoinRelType.LEFT) && (joinType != JoinRelType.RIGHT)) { filters.add(join.getCondition()); } @@ -439,11 +443,11 @@ private boolean canCombine(RelNode input, boolean nullGenerating) { * @param rightFilter the filter originating from the right child * @return the adjusted right filter */ - private RexNode shiftRightFilter( + private @Nullable RexNode shiftRightFilter( Join joinRel, RelNode left, MultiJoin right, - RexNode rightFilter) { + @Nullable RexNode rightFilter) { if (rightFilter == null) { return null; } @@ -511,7 +515,9 @@ private ImmutableMap addOnJoinFieldRefCounts( nFields = multiJoinInputs.get(currInput).getRowType().getFieldCount(); } - int[] refCounts = refCountsMap.get(currInput); + final int key = currInput; + int[] refCounts = requireNonNull(refCountsMap.get(key), + () -> "refCountsMap.get(currInput) for " + key); refCounts[i - startField] += joinCondRefCounts[i]; } @@ -532,11 +538,11 @@ private ImmutableMap addOnJoinFieldRefCounts( * @param right right child of the LogicalJoin * @return combined post-join filters AND'd together */ - private List combinePostJoinFilters( + private List<@Nullable RexNode> combinePostJoinFilters( Join joinRel, RelNode left, RelNode right) { - final List filters = new ArrayList<>(); + final List<@Nullable RexNode> filters = new ArrayList<>(); if (right instanceof MultiJoin) { final MultiJoin multiRight = (MultiJoin) right; filters.add( diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptJoinTree.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptJoinTree.java index e73c5e27e0b2..21efbc91493e 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/LoptJoinTree.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptJoinTree.java @@ -19,6 +19,9 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.Join; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnderInitialization; + import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -36,6 +39,7 @@ public class LoptJoinTree { //~ Instance fields -------------------------------------------------------- + @NotOnlyInitialized private final BinaryTree factorTree; private final RelNode joinTree; private final boolean removableSelfJoin; @@ -48,6 +52,7 @@ public class LoptJoinTree { * @param joinTree RelNode corresponding to the single node * @param factorId factor id of the node */ + @SuppressWarnings("argument.type.incompatible") public LoptJoinTree(RelNode joinTree, int factorId) { this.joinTree = joinTree; this.factorTree = new Leaf(factorId, this); @@ -153,9 +158,10 @@ public boolean isRemovableSelfJoin() { * track of the parent LoptJoinTree object associated with the binary tree. */ protected abstract static class BinaryTree { + @NotOnlyInitialized private final LoptJoinTree parent; - protected BinaryTree(LoptJoinTree parent) { + protected BinaryTree(@UnderInitialization LoptJoinTree parent) { this.parent = Objects.requireNonNull(parent); } @@ -190,7 +196,7 @@ protected static class Node extends BinaryTree { private final BinaryTree left; private final BinaryTree right; - public Node(BinaryTree left, BinaryTree right, LoptJoinTree parent) { + public Node(BinaryTree left, BinaryTree right, @UnderInitialization LoptJoinTree parent) { super(parent); this.left = Objects.requireNonNull(left); this.right = Objects.requireNonNull(right); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java index 4614b2fcee54..b5a8821c63c9 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java @@ -35,6 +35,12 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; + import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; @@ -44,6 +50,8 @@ import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Utility class that keeps track of the join factors that * make up a {@link MultiJoin}. @@ -94,7 +102,7 @@ public class LoptMultiJoin { * row scan processing has completed. This excludes fields referenced in * join conditions, unless the field appears in the final projection list. */ - private List projFields; + private List<@Nullable ImmutableBitSet> projFields; /** * Map containing reference counts of the fields referenced in join @@ -109,13 +117,13 @@ public class LoptMultiJoin { * For each join filter, associates a bitmap indicating all factors * referenced by the filter. */ - private Map factorsRefByJoinFilter; + private final Map factorsRefByJoinFilter = new HashMap<>(); /** * For each join filter, associates a bitmap indicating all fields * referenced by the filter. */ - private Map fieldsRefByJoinFilter; + private final Map fieldsRefByJoinFilter = new HashMap<>(); /** * Starting RexInputRef index corresponding to each join factor. @@ -131,12 +139,12 @@ public class LoptMultiJoin { * Bitmap indicating which factors each factor references in join filters * that correspond to comparisons. */ - ImmutableBitSet [] factorsRefByFactor; + ImmutableBitSet @MonotonicNonNull [] factorsRefByFactor; /** * Weights of each factor combination. */ - int [][] factorWeights; + int @MonotonicNonNull [][] factorWeights; /** * Type factory. @@ -150,7 +158,7 @@ public class LoptMultiJoin { * semijoin that allows the factor to be removed. If the factor cannot be * removed, the entry corresponding to the factor is null. */ - Integer [] joinRemovalFactors; + @Nullable Integer [] joinRemovalFactors; /** * The semijoins that allow the join of a dimension table to be removed. @@ -184,7 +192,7 @@ public LoptMultiJoin(MultiJoin multiJoin) { Lists.newArrayList(RelOptUtil.conjunctions(multiJoin.getJoinFilter())); allJoinFilters = new ArrayList<>(joinFilters); - List outerJoinFilters = multiJoin.getOuterJoinConditions(); + List<@Nullable RexNode> outerJoinFilters = multiJoin.getOuterJoinConditions(); for (int i = 0; i < nJoinFactors; i++) { allJoinFilters.addAll(RelOptUtil.conjunctions(outerJoinFilters.get(i))); } @@ -204,15 +212,16 @@ public LoptMultiJoin(MultiJoin multiJoin) { // of outer join and the factors that a null-generating factor is dependent // upon. joinTypes = ImmutableList.copyOf(multiJoin.getJoinTypes()); - List outerJoinConds = this.multiJoin.getOuterJoinConditions(); + List<@Nullable RexNode> outerJoinConds = this.multiJoin.getOuterJoinConditions(); outerJoinFactors = new ImmutableBitSet[nJoinFactors]; for (int i = 0; i < nJoinFactors; i++) { - if (outerJoinConds.get(i) != null) { + RexNode outerJoinCond = outerJoinConds.get(i); + if (outerJoinCond != null) { // set a bitmap containing the factors referenced in the // ON condition of the outer join; mask off the factor // corresponding to the factor itself ImmutableBitSet dependentFactors = - getJoinFilterFactorBitmap(outerJoinConds.get(i), false); + getJoinFilterFactorBitmap(outerJoinCond, false); dependentFactors = dependentFactors.clear(i); outerJoinFactors[i] = dependentFactors; } @@ -285,7 +294,9 @@ public List getJoinFilters() { * @param joinFilter Filter for which information will be returned */ public ImmutableBitSet getFactorsRefByJoinFilter(RexNode joinFilter) { - return factorsRefByJoinFilter.get(joinFilter); + return requireNonNull( + factorsRefByJoinFilter.get(joinFilter), + () -> "joinFilter is not found in factorsRefByJoinFilter: " + joinFilter); } /** @@ -301,13 +312,15 @@ public List getMultiJoinFields() { * @param joinFilter the filter for which information will be returned */ public ImmutableBitSet getFieldsRefByJoinFilter(RexNode joinFilter) { - return fieldsRefByJoinFilter.get(joinFilter); + return requireNonNull( + fieldsRefByJoinFilter.get(joinFilter), + () -> "joinFilter is not found in fieldsRefByJoinFilter: " + joinFilter); } /** * Returns weights of the different factors relative to one another. */ - public int [][] getFactorWeights() { + public int @Nullable [][] getFactorWeights() { return factorWeights; } @@ -318,7 +331,7 @@ public ImmutableBitSet getFieldsRefByJoinFilter(RexNode joinFilter) { * @param factIdx Factor for which information will be returned */ public ImmutableBitSet getFactorsRefByFactor(int factIdx) { - return factorsRefByFactor[factIdx]; + return requireNonNull(factorsRefByFactor, "factorsRefByFactor")[factIdx]; } /** @@ -357,7 +370,7 @@ public ImmutableBitSet getOuterJoinFactors(int factIdx) { * * @param factIdx Factor for which information will be returned */ - public RexNode getOuterJoinCond(int factIdx) { + public @Nullable RexNode getOuterJoinCond(int factIdx) { return multiJoin.getOuterJoinConditions().get(factIdx); } @@ -366,7 +379,7 @@ public RexNode getOuterJoinCond(int factIdx) { * * @param factIdx Factor for which information will be returned */ - public ImmutableBitSet getProjFields(int factIdx) { + public @Nullable ImmutableBitSet getProjFields(int factIdx) { return projFields.get(factIdx); } @@ -376,7 +389,9 @@ public ImmutableBitSet getProjFields(int factIdx) { * @param factIdx Factor for which information will be returned */ public int [] getJoinFieldRefCounts(int factIdx) { - return joinFieldRefCountsMap.get(factIdx); + return requireNonNull( + joinFieldRefCountsMap.get(factIdx), + () -> "no entry in joinFieldRefCountsMap found for " + factIdx); } /** @@ -386,7 +401,7 @@ public ImmutableBitSet getProjFields(int factIdx) { * * @param dimIdx Dimension factor for which information will be returned */ - public Integer getJoinRemovalFactor(int dimIdx) { + public @Nullable Integer getJoinRemovalFactor(int dimIdx) { return joinRemovalFactors[dimIdx]; } @@ -431,7 +446,9 @@ public void setJoinRemovalSemiJoin(int dimIdx, LogicalJoin semiJoin) { * * @return the bitmap containing the factor references */ + @RequiresNonNull({"joinStart", "nFieldsInJoinFactor"}) ImmutableBitSet getJoinFilterFactorBitmap( + @UnderInitialization LoptMultiJoin this, RexNode joinFilter, boolean setFields) { ImmutableBitSet fieldRefBitmap = fieldBitmap(joinFilter); @@ -442,7 +459,7 @@ ImmutableBitSet getJoinFilterFactorBitmap( return factorBitmap(fieldRefBitmap); } - private ImmutableBitSet fieldBitmap(RexNode joinFilter) { + private static ImmutableBitSet fieldBitmap(RexNode joinFilter) { final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(); joinFilter.accept(inputFinder); return inputFinder.build(); @@ -452,9 +469,9 @@ private ImmutableBitSet fieldBitmap(RexNode joinFilter) { * Sets bitmaps indicating which factors and fields each join filter * references. */ - private void setJoinFilterRefs() { - fieldsRefByJoinFilter = new HashMap<>(); - factorsRefByJoinFilter = new HashMap<>(); + @RequiresNonNull({"allJoinFilters", "joinStart", "nFieldsInJoinFactor"}) + private void setJoinFilterRefs( + @UnderInitialization LoptMultiJoin this) { ListIterator filterIter = allJoinFilters.listIterator(); while (filterIter.hasNext()) { RexNode joinFilter = filterIter.next(); @@ -478,7 +495,10 @@ private void setJoinFilterRefs() { * @return bitmap representing factors referenced that will * be set by this method */ - private ImmutableBitSet factorBitmap(ImmutableBitSet fieldRefBitmap) { + @RequiresNonNull({"joinStart", "nFieldsInJoinFactor"}) + private ImmutableBitSet factorBitmap( + @UnknownInitialization LoptMultiJoin this, + ImmutableBitSet fieldRefBitmap) { ImmutableBitSet.Builder factorRefBitmap = ImmutableBitSet.builder(); for (int field : fieldRefBitmap) { int factor = findRef(field); @@ -494,7 +514,10 @@ private ImmutableBitSet factorBitmap(ImmutableBitSet fieldRefBitmap) { * * @return index corresponding to join factor */ - public int findRef(int rexInputRef) { + @RequiresNonNull({"joinStart", "nFieldsInJoinFactor"}) + public int findRef( + @UnknownInitialization LoptMultiJoin this, + int rexInputRef) { for (int i = 0; i < nJoinFactors; i++) { if ((rexInputRef >= joinStart[i]) && (rexInputRef < (joinStart[i] + nFieldsInJoinFactor[i]))) { @@ -588,6 +611,7 @@ public void setFactorWeights() { * @param leftFactor index of left factor * @param rightFactor index of right factor */ + @RequiresNonNull("factorWeights") private void setFactorWeight(int weight, int leftFactor, int rightFactor) { if (factorWeights[leftFactor][rightFactor] < weight) { factorWeights[leftFactor][rightFactor] = weight; @@ -734,7 +758,7 @@ > getNumFieldsInJoinFactor(factor2)) { * * @param factIdx one of the factors in a self-join pair */ - public Integer getOtherSelfJoinFactor(int factIdx) { + public @Nullable Integer getOtherSelfJoinFactor(int factIdx) { RemovableSelfJoin selfJoin = removableSelfJoinPairs.get(factIdx); if (selfJoin == null) { return null; @@ -782,7 +806,7 @@ public boolean isRightFactorInRemovableSelfJoin(int factIdx) { * @return the offset of the corresponding column in the left factor, if * such a column mapping exists; otherwise, null is returned */ - public Integer getRightColumnMapping(int rightFactor, int rightOffset) { + public @Nullable Integer getRightColumnMapping(int rightFactor, int rightOffset) { RemovableSelfJoin selfJoin = removableSelfJoinPairs.get(rightFactor); assert selfJoin.rightFactor == rightFactor; return selfJoin.columnMapping.get(rightOffset); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java index 996b4cf61625..3c6424d6a4b7 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java @@ -47,6 +47,8 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.mapping.IntPair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; @@ -403,7 +405,7 @@ private boolean isSelfJoinFilterUnique( RelNode leftRel = multiJoin.getJoinFactor(leftFactor); RelNode rightRel = multiJoin.getJoinFactor(rightFactor); RexNode joinFilters = - RexUtil.composeConjunction(rexBuilder, joinFilterList, true); + RexUtil.composeConjunction(rexBuilder, joinFilterList); // Adjust the offsets in the filter by shifting the left factor // to the left and shifting the right factor to the left and then back @@ -568,7 +570,7 @@ private RelNode createTopProject( * * @return computed cardinality */ - private Double computeJoinCardinality( + private @Nullable Double computeJoinCardinality( RelMetadataQuery mq, LoptMultiJoin multiJoin, LoptSemiJoinOptimizer semiJoinOpt, @@ -672,7 +674,7 @@ private void setFactorJoinKeys( * @return constructed join tree or null if it is not possible for * firstFactor to appear as the first factor in the join */ - private LoptJoinTree createOrdering( + private @Nullable LoptJoinTree createOrdering( RelMetadataQuery mq, RelBuilder relBuilder, LoptMultiJoin multiJoin, @@ -870,12 +872,12 @@ private boolean isJoinTree(RelNode rel) { * @return optimal join tree with the new factor added if it is possible to * add the factor; otherwise, null is returned */ - private LoptJoinTree addFactorToTree( + private @Nullable LoptJoinTree addFactorToTree( RelMetadataQuery mq, RelBuilder relBuilder, LoptMultiJoin multiJoin, LoptSemiJoinOptimizer semiJoinOpt, - LoptJoinTree joinTree, + @Nullable LoptJoinTree joinTree, int factorToAdd, BitSet factorsNeeded, List filtersToAdd, @@ -1025,7 +1027,7 @@ private int rowWidthCost(RelNode tree) { * join tree if it is possible to do the pushdown; otherwise, null is * returned */ - private LoptJoinTree pushDownFactor( + private @Nullable LoptJoinTree pushDownFactor( RelMetadataQuery mq, RelBuilder relBuilder, LoptMultiJoin multiJoin, @@ -1177,7 +1179,7 @@ private LoptJoinTree pushDownFactor( * * @return new join tree */ - private LoptJoinTree addToTop( + private @Nullable LoptJoinTree addToTop( RelMetadataQuery mq, RelBuilder relBuilder, LoptMultiJoin multiJoin, @@ -1541,11 +1543,11 @@ private boolean remapJoinReferences( * @return created join tree or null if the corresponding fact table has not * been joined in yet */ - private LoptJoinTree createReplacementSemiJoin( + private @Nullable LoptJoinTree createReplacementSemiJoin( RelBuilder relBuilder, LoptMultiJoin multiJoin, LoptSemiJoinOptimizer semiJoinOpt, - LoptJoinTree factTree, + @Nullable LoptJoinTree factTree, int dimIdx, List filtersToAdd) { // if the current join tree doesn't contain the fact table, then @@ -1620,7 +1622,7 @@ private LoptJoinTree createReplacementJoin( int leftIdx, int factorToAdd, ImmutableIntList newKeys, - Integer [] replacementKeys, + Integer @Nullable [] replacementKeys, List filtersToAdd) { // create a projection, projecting the fields from the join tree // containing the current joinRel and the new factor; for fields diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java index 6517586c65dc..dde06cd83510 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java @@ -42,6 +42,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -78,7 +80,7 @@ public class LoptSemiJoinOptimizer { * corresponds to the dimension table and a SemiJoin that captures all * the necessary semijoin data between that fact and dimension table */ - private Map> possibleSemiJoins; + private @Nullable Map> possibleSemiJoins; private final Ordering factorCostOrdering = Ordering.from(new FactorCostComparator()); @@ -236,14 +238,14 @@ private int isSuitableFilter( * @return SemiJoin containing information regarding the semijoin that * can be used to filter the fact table */ - private LogicalJoin findSemiJoinIndexByCost( + private @Nullable LogicalJoin findSemiJoinIndexByCost( LoptMultiJoin multiJoin, List joinFilters, int factIdx, int dimIdx) { // create a SemiJoin with the semi-join condition and keys RexNode semiJoinCondition = - RexUtil.composeConjunction(rexBuilder, joinFilters, true); + RexUtil.composeConjunction(rexBuilder, joinFilters); int leftAdjustment = 0; for (int i = 0; i < factIdx; i++) { @@ -399,7 +401,7 @@ private RexNode adjustSemiJoinCondition( * @return the underlying fact table if the semijoin keys are valid; * otherwise null */ - private LcsTable validateKeys( + private @Nullable LcsTable validateKeys( RelNode factRel, List leftKeys, List rightKeys, @@ -464,7 +466,7 @@ private LcsTable validateKeys( * @return modified expression with filters that don't reference specified * keys removed */ - private RexNode removeExtraFilters( + private @Nullable RexNode removeExtraFilters( List keys, int nFields, RexNode condition) { @@ -829,7 +831,7 @@ private static class LcsTableScan { private static class LcsIndexOptimizer { LcsIndexOptimizer(LcsTableScan rel) {} - public FemLocalIndex findSemiJoinIndexByCost(RelNode dimRel, + public @Nullable FemLocalIndex findSemiJoinIndexByCost(RelNode dimRel, List actualLeftKeys, List rightKeys, List bestKeyOrder) { return null; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java index cfeb734e7271..b7dd3a78336f 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoin.java @@ -35,6 +35,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -52,11 +54,11 @@ public final class MultiJoin extends AbstractRelNode { @SuppressWarnings("HidingField") private final RelDataType rowType; private final boolean isFullOuterJoin; - private final List outerJoinConditions; + private final List<@Nullable RexNode> outerJoinConditions; private final ImmutableList joinTypes; - private final List projFields; + private final List<@Nullable ImmutableBitSet> projFields; public final ImmutableMap joinFieldRefCountsMap; - private final RexNode postJoinFilter; + private final @Nullable RexNode postJoinFilter; //~ Constructors ----------------------------------------------------------- @@ -91,11 +93,11 @@ public MultiJoin( RexNode joinFilter, RelDataType rowType, boolean isFullOuterJoin, - List outerJoinConditions, + List outerJoinConditions, List joinTypes, - List projFields, + List projFields, ImmutableMap joinFieldRefCountsMap, - RexNode postJoinFilter) { + @Nullable RexNode postJoinFilter) { super(cluster, cluster.traitSetOf(Convention.NONE)); this.inputs = Lists.newArrayList(inputs); this.joinFilter = joinFilter; @@ -149,15 +151,17 @@ private Map cloneJoinFieldRefCountsMap() { List projFieldObjects = new ArrayList<>(); for (int i = 0; i < inputs.size(); i++) { joinTypeNames.add(joinTypes.get(i).name()); - if (outerJoinConditions.get(i) == null) { + RexNode outerJoinCondition = outerJoinConditions.get(i); + if (outerJoinCondition == null) { outerJoinConds.add("NULL"); } else { - outerJoinConds.add(outerJoinConditions.get(i).toString()); + outerJoinConds.add(outerJoinCondition.toString()); } - if (projFields.get(i) == null) { + ImmutableBitSet projField = projFields.get(i); + if (projField == null) { projFieldObjects.add("ALL"); } else { - projFieldObjects.add(projFields.get(i).toString()); + projFieldObjects.add(projField.toString()); } } @@ -183,7 +187,7 @@ private Map cloneJoinFieldRefCountsMap() { @Override public RelNode accept(RexShuttle shuttle) { RexNode joinFilter = shuttle.apply(this.joinFilter); - List outerJoinConditions = shuttle.apply(this.outerJoinConditions); + List<@Nullable RexNode> outerJoinConditions = shuttle.apply(this.outerJoinConditions); RexNode postJoinFilter = shuttle.apply(this.postJoinFilter); if (joinFilter == this.joinFilter @@ -222,7 +226,7 @@ public boolean isFullOuterJoin() { /** * Returns outer join conditions for null-generating inputs. */ - public List getOuterJoinConditions() { + public List<@Nullable RexNode> getOuterJoinConditions() { return outerJoinConditions; } @@ -237,7 +241,7 @@ public List getJoinTypes() { * Returns bitmaps representing the fields projected from each input; if an * entry is null, all fields are projected. */ - public List getProjFields() { + public List<@Nullable ImmutableBitSet> getProjFields() { return projFields; } @@ -260,7 +264,7 @@ public Map getCopyJoinFieldRefCountsMap() { /** * Returns post-join filter associated with this MultiJoin. */ - public RexNode getPostJoinFilter() { + public @Nullable RexNode getPostJoinFilter() { return postJoinFilter; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java index 6ec18352c607..0dd5e49f83d0 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java @@ -38,6 +38,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Comparator; @@ -75,7 +77,7 @@ public class MultiJoinOptimizeBushyRule extends RelRule implements TransformationRule { - private final PrintWriter pw = CalciteSystemProperty.DEBUG.value() + private final @Nullable PrintWriter pw = CalciteSystemProperty.DEBUG.value() ? Util.printWriter(System.out) : null; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java index 92d496847de0..1fda70c1b691 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java @@ -41,6 +41,7 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** @@ -102,7 +103,7 @@ && kindCount(project.getProjects(), SqlKind.CASE) == 0) { final RexInputRef ref1 = (RexInputRef) cast.operands.get(0); final RexLiteral literal = (RexLiteral) operands.get(2); if (ref0.getIndex() == ref1.getIndex() - && literal.getValueAs(BigDecimal.class).equals(BigDecimal.ZERO)) { + && Objects.equals(literal.getValueAs(BigDecimal.class), BigDecimal.ZERO)) { final int aggCallIndex = ref1.getIndex() - aggregate.getGroupCount(); if (aggCallIndex >= 0) { diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinJoinRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinJoinRemoveRule.java index ce515860687a..191d3178a60a 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinJoinRemoveRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectJoinJoinRemoveRule.java @@ -117,7 +117,7 @@ public ProjectJoinJoinRemoveRule( final ImmutableBitSet.Builder columns = ImmutableBitSet.builder(); rightChildKeys.forEach(columns::set); final RelMetadataQuery mq = call.getMetadataQuery(); - if (!mq.areColumnsUnique(bottomJoin.getRight(), columns.build())) { + if (!Boolean.TRUE.equals(mq.areColumnsUnique(bottomJoin.getRight(), columns.build()))) { return; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java b/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java index 1d67f829bebe..c4a59ce035ea 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/PushProjector.java @@ -43,15 +43,18 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.BitSet; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.IntStream; +import static java.util.Objects.requireNonNull; + /** * PushProjector is a utility class used to perform operations used in push * projection rules. @@ -69,8 +72,8 @@ public class PushProjector { //~ Instance fields -------------------------------------------------------- - private final Project origProj; - private final RexNode origFilter; + private final @Nullable Project origProj; + private final @Nullable RexNode origFilter; private final RelNode childRel; private final ExprCondition preserveExprCondition; private final RelBuilder relBuilder; @@ -107,13 +110,13 @@ public class PushProjector { * case where the projection is being pushed past a join. Not used * otherwise. */ - final ImmutableBitSet rightBitmap; + final @Nullable ImmutableBitSet rightBitmap; /** * Bitmap containing the fields that should be strong, i.e. when preserving expressions * we can only preserve them if the expressions if it is null when these fields are null. */ - final ImmutableBitSet strongBitmap; + final @Nullable ImmutableBitSet strongBitmap; /** * Number of fields in the RelNode that the projection is being pushed past, @@ -204,8 +207,8 @@ public class PushProjector { * be preserved in the projection */ public PushProjector( - Project origProj, - RexNode origFilter, + @Nullable Project origProj, + @Nullable RexNode origFilter, RelNode childRel, ExprCondition preserveExprCondition, RelBuilder relBuilder) { @@ -213,7 +216,7 @@ public PushProjector( this.origFilter = origFilter; this.childRel = childRel; this.preserveExprCondition = preserveExprCondition; - this.relBuilder = Objects.requireNonNull(relBuilder); + this.relBuilder = requireNonNull(relBuilder); if (origProj == null) { origProjExprs = ImmutableList.of(); } else { @@ -330,7 +333,7 @@ public PushProjector( * @return the converted projection if it makes sense to push elements of * the projection; otherwise returns null */ - public RelNode convertProject(RexNode defaultExpr) { + public @Nullable RelNode convertProject(@Nullable RexNode defaultExpr) { // locate all fields referenced in the projection and filter locateAllRefs(); @@ -415,7 +418,7 @@ public boolean locateAllRefs() { projRefs, childBitmap, rightBitmap, - strongBitmap, + requireNonNull(strongBitmap, "strongBitmap"), preserveExprCondition, childPreserveExprs, rightPreserveExprs), @@ -664,7 +667,7 @@ public RelNode createNewProject(RelNode projChild, int[] adjustments) { private static class InputSpecialOpFinder extends RexVisitorImpl { private final BitSet rexRefs; private final ImmutableBitSet leftFields; - private final ImmutableBitSet rightFields; + private final @Nullable ImmutableBitSet rightFields; private final ImmutableBitSet strongFields; private final ExprCondition preserveExprCondition; private final List preserveLeft; @@ -721,7 +724,8 @@ private boolean preserve(RexNode call) { preserveLeft.add(call); } return true; - } else if (rightFields.contains(exprArgs) && isStrong(exprArgs, call)) { + } else if (requireNonNull(rightFields, "rightFields").contains(exprArgs) + && isStrong(exprArgs, call)) { assert preserveRight != null; if (!preserveRight.contains(call)) { preserveRight.add(call); @@ -783,7 +787,7 @@ private static class RefAndExprConverter extends RelOptUtil.RexInputConverter { firstRightRef); if (match >= 0) { return rexBuilder.makeInputRef( - destFields.get(match).getType(), + requireNonNull(destFields, "destFields").get(match).getType(), match); } return super.visitCall(call); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java index f7903c3bff5f..5351c182d173 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java @@ -42,6 +42,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; @@ -84,7 +86,7 @@ public ReduceDecimalsRule(RelBuilderFactory relBuilderFactory) { //~ Methods ---------------------------------------------------------------- - @Override public Convention getOutConvention() { + @Override public @Nullable Convention getOutConvention() { return Convention.NONE; } @@ -177,7 +179,7 @@ private void register(RexNode node, RexNode reducedNode) { /** * Looks up a registered node. */ - private RexNode lookup(RexNode node) { + private @Nullable RexNode lookup(RexNode node) { Pair key = RexUtil.makeKey(node); if (irreducible.get(key) != null) { return node; @@ -218,10 +220,12 @@ private static class ExpanderMap { private ExpanderMap(RexBuilder rexBuilder) { map = new HashMap<>(); - registerExpanders(rexBuilder); + defaultExpander = new CastArgAsDoubleExpander(rexBuilder); + registerExpanders(map, rexBuilder); } - private void registerExpanders(RexBuilder rexBuilder) { + private static void registerExpanders(Map map, + RexBuilder rexBuilder) { RexExpander cast = new CastExpander(rexBuilder); map.put(SqlStdOperatorTable.CAST, cast); @@ -257,8 +261,6 @@ private void registerExpanders(RexBuilder rexBuilder) { RexExpander caseExpander = new CaseExpander(rexBuilder); map.put(SqlStdOperatorTable.CASE, caseExpander); - - defaultExpander = new CastArgAsDoubleExpander(rexBuilder); } RexExpander getExpander(RexCall call) { @@ -814,8 +816,8 @@ private CastExpander(RexBuilder builder) { * Expands a decimal arithmetic expression. */ private static class BinaryArithmeticExpander extends RexExpander { - RelDataType typeA; - RelDataType typeB; + @Nullable RelDataType typeA; + @Nullable RelDataType typeB; int scaleA; int scaleB; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java index c53275c68ce2..e733f04fad9d 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java @@ -71,6 +71,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -576,12 +578,12 @@ public WindowReduceExpressionsRule(Class windowClass, } final ImmutableBitSet.Builder keyBuilder = ImmutableBitSet.builder(); - group.keys.asList().stream() - .filter(key -> - !predicates.constantMap.containsKey( - rexBuilder.makeInputRef(window.getInput(), key))) - .collect(Collectors.toList()) - .forEach(keyBuilder::set); + for (Integer key : group.keys) { + if (!predicates.constantMap.containsKey( + rexBuilder.makeInputRef(window.getInput(), key))) { + keyBuilder.set(key); + } + } final ImmutableBitSet keys = keyBuilder.build(); reduced |= keys.cardinality() != group.keys.cardinality(); @@ -929,7 +931,7 @@ protected static class RexReplacer extends RexShuttle { return node; } - private RexNode visit(final RexNode call) { + private @Nullable RexNode visit(final RexNode call) { int i = reducibleExps.indexOf(call); if (i == -1) { return null; @@ -1030,7 +1032,7 @@ private void addResult(RexNode exp) { } } - private Boolean isUdf(@SuppressWarnings("unused") SqlOperator operator) { + private Boolean isUdf(@SuppressWarnings("unused") @Nullable SqlOperator operator) { // return operator instanceof UserDefinedRoutine return false; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinRule.java index c2b5b7c530ce..16422d33bbb3 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinRule.java @@ -32,6 +32,8 @@ import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.ImmutableIntList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -59,7 +61,7 @@ protected SemiJoinRule(Config config) { super(config); } - protected void perform(RelOptRuleCall call, Project project, + protected void perform(RelOptRuleCall call, @Nullable Project project, Join join, RelNode left, Aggregate aggregate) { final RelOptCluster cluster = join.getCluster(); final RexBuilder rexBuilder = cluster.getRexBuilder(); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveConstantKeysRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveConstantKeysRule.java index 65f727ab0a38..6555b254d4c9 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveConstantKeysRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveConstantKeysRule.java @@ -51,7 +51,7 @@ protected SortRemoveConstantKeysRule(Config config) { final RelMetadataQuery mq = call.getMetadataQuery(); final RelNode input = sort.getInput(); final RelOptPredicateList predicates = mq.getPulledUpPredicates(input); - if (predicates == null) { + if (RelOptPredicateList.isEmpty(predicates)) { return; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveRule.java index 32d64eeb5043..c96e8537ff81 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SortRemoveRule.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.rel.rules; +import org.apache.calcite.plan.ConventionTraitDef; import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.plan.RelRule; import org.apache.calcite.plan.RelTraitSet; @@ -65,7 +66,7 @@ public SortRemoveRule(RelBuilderFactory relBuilderFactory) { assert collation == sort.getTraitSet() .getTrait(RelCollationTraitDef.INSTANCE); final RelTraitSet traits = sort.getInput().getTraitSet() - .replace(collation).replace(sort.getConvention()); + .replace(collation).replaceIf(ConventionTraitDef.INSTANCE, sort::getConvention); call.transformTo(convert(sort.getInput(), traits)); } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SpatialRules.java b/core/src/main/java/org/apache/calcite/rel/rules/SpatialRules.java index f9885dfc0b0a..91078125ca29 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SpatialRules.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SpatialRules.java @@ -40,6 +40,8 @@ import com.esri.core.geometry.Point; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.EnumSet; @@ -93,7 +95,7 @@ private SpatialRules() {} FilterHilbertRule.Config.DEFAULT.toRule(); /** Returns a geometry if an expression is constant, null otherwise. */ - private static Geometries.Geom constantGeom(RexNode e) { + private static Geometries.@Nullable Geom constantGeom(RexNode e) { switch (e.getKind()) { case CAST: return constantGeom(((RexCall) e).getOperands().get(0)); @@ -183,7 +185,7 @@ protected FilterHilbertRule(Config config) { * * @return List containing rewritten predicate and original, or null */ - static List replaceSpatial(RexNode conjunction, RelBuilder builder, + static @Nullable List replaceSpatial(RexNode conjunction, RelBuilder builder, RexInputRef ref, RexCall hilbert) { final RexNode op0; final RexNode op1; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java index 5f89ed0f1e49..6528ac3be3d6 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java @@ -67,7 +67,7 @@ public UnionPullUpConstantsRule(Class unionClass, final RexBuilder rexBuilder = union.getCluster().getRexBuilder(); final RelMetadataQuery mq = call.getMetadataQuery(); final RelOptPredicateList predicates = mq.getPulledUpPredicates(union); - if (predicates == null) { + if (RelOptPredicateList.isEmpty(predicates)) { return; } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java index 6550a73d84a7..70b0cf09a546 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java @@ -40,11 +40,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Planner rule that folds projections and filters into an underlying * {@link org.apache.calcite.rel.logical.LogicalValues}. @@ -124,8 +127,8 @@ private static void matchFilter(ValuesReduceRule rule, RelOptRuleCall call) { * @param filter Filter, may be null * @param values Values rel to be reduced */ - protected void apply(RelOptRuleCall call, LogicalProject project, - LogicalFilter filter, LogicalValues values) { + protected void apply(RelOptRuleCall call, @Nullable LogicalProject project, + @Nullable LogicalFilter filter, LogicalValues values) { assert values != null; assert filter != null || project != null; final RexNode conditionExpr = @@ -144,6 +147,7 @@ protected void apply(RelOptRuleCall call, LogicalProject project, reducibleExps.add(c); } if (projectExprs != null) { + requireNonNull(project, "project"); int k = -1; for (RexNode projectExpr : projectExprs) { ++k; @@ -209,7 +213,7 @@ protected void apply(RelOptRuleCall call, LogicalProject project, if (changeCount > 0) { final RelDataType rowType; if (projectExprs != null) { - rowType = project.getRowType(); + rowType = requireNonNull(project, "project").getRowType(); } else { rowType = values.getRowType(); } @@ -235,9 +239,10 @@ protected void apply(RelOptRuleCall call, LogicalProject project, /** Shuttle that converts inputs to literals. */ private static class MyRexShuttle extends RexShuttle { - private List literalList; + private @Nullable List literalList; @Override public RexNode visitInputRef(RexInputRef inputRef) { + requireNonNull(literalList, "literalList"); return literalList.get(inputRef.getIndex()); } } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java index 912a70551e06..9caa8be134d3 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java @@ -20,6 +20,7 @@ import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.hep.HepPlanner; +import org.apache.calcite.plan.hep.HepProgram; import org.apache.calcite.plan.hep.HepProgramBuilder; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.Aggregate; @@ -68,6 +69,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -76,6 +79,8 @@ import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** Materialized view rewriting for aggregate. * * @param Configuration type @@ -93,7 +98,7 @@ public abstract class MaterializedViewAggregateRule queryTableRefs, EquivalenceClasses queryEC, - Project topViewProject, + @Nullable Project topViewProject, RelNode viewNode, Set viewTableRefs) { // Modify view to join with missing tables and add Project on top to reorder columns. @@ -139,7 +144,8 @@ public abstract class MaterializedViewAggregateRule queryToViewTableMapping, EquivalenceClasses queryEC) { @@ -713,7 +719,7 @@ public abstract class MaterializedViewAggregateRuleIf any of the expressions cannot be mapped, we return null. */ - protected Multimap generateMapping( + protected @Nullable Multimap generateMapping( RexBuilder rexBuilder, RexSimplify simplify, RelMetadataQuery mq, @@ -847,7 +853,7 @@ protected SqlFunction getFloorSqlFunction(TimeUnitRange flag) { /** * Get rollup aggregation function. */ - protected SqlAggFunction getRollup(SqlAggFunction aggregation) { + protected @Nullable SqlAggFunction getRollup(SqlAggFunction aggregation) { if (aggregation == SqlStdOperatorTable.SUM || aggregation == SqlStdOperatorTable.SUM0 || aggregation instanceof SqlMinMaxAggFunction @@ -860,8 +866,8 @@ protected SqlAggFunction getRollup(SqlAggFunction aggregation) { } } - @Override public Pair pushFilterToOriginalViewPlan(RelBuilder builder, - RelNode topViewProject, RelNode viewNode, RexNode cond) { + @Override public Pair<@Nullable RelNode, RelNode> pushFilterToOriginalViewPlan(RelBuilder builder, + @Nullable RelNode topViewProject, RelNode viewNode, RexNode cond) { // We add (and push) the filter to the view plan before triggering the rewriting. // This is useful in case some of the columns can be folded to same value after // filter is added. @@ -898,7 +904,7 @@ protected SqlAggFunction getRollup(SqlAggFunction aggregation) { topNode = topNode.getInput(0); } } - return Pair.of(resultTopViewProject, resultViewNode); + return Pair.of(resultTopViewProject, requireNonNull(resultViewNode, "resultViewNode")); } /** Rule configuration. */ diff --git a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java index af4295356f02..6f468fab929a 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewJoinRule.java @@ -34,6 +34,8 @@ import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -52,21 +54,21 @@ public abstract class MaterializedViewJoinRule queryTableRefs, EquivalenceClasses queryEC, - Project topViewProject, + @Nullable Project topViewProject, RelNode viewNode, Set viewTableRefs) { // We only create the rewriting in the minimal subtree of plan operators. @@ -105,7 +107,8 @@ public abstract class MaterializedViewJoinRule queryToViewTableMapping, EquivalenceClasses queryEC) { @@ -273,8 +276,8 @@ public abstract class MaterializedViewJoinRule pushFilterToOriginalViewPlan(RelBuilder builder, - RelNode topViewProject, RelNode viewNode, RexNode cond) { + @Override public Pair<@Nullable RelNode, RelNode> pushFilterToOriginalViewPlan(RelBuilder builder, + @Nullable RelNode topViewProject, RelNode viewNode, RexNode cond) { // Nothing to do return Pair.of(topViewProject, viewNode); } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewRule.java index 5cf3b44cdb0e..e465b67c88d4 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewRule.java @@ -66,6 +66,8 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -77,6 +79,8 @@ import java.util.Map; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Planner rule that converts a {@link org.apache.calcite.rel.core.Project} * followed by {@link org.apache.calcite.rel.core.Aggregate} or an @@ -138,7 +142,7 @@ public abstract class MaterializedViewRuleRules implementing the method should follow different approaches depending on the * operators they rewrite. */ - protected abstract ViewPartialRewriting compensateViewPartial( + protected abstract @Nullable ViewPartialRewriting compensateViewPartial( RelBuilder relBuilder, RexBuilder rexBuilder, RelMetadataQuery mq, RelNode input, - Project topProject, RelNode node, Set queryTableRefs, EquivalenceClasses queryEC, - Project topViewProject, RelNode viewNode, Set viewTableRefs); + @Nullable Project topProject, RelNode node, Set queryTableRefs, + EquivalenceClasses queryEC, + @Nullable Project topViewProject, RelNode viewNode, Set viewTableRefs); /** * If the view will be used in a union rewriting, this method is responsible for @@ -501,7 +506,7 @@ protected abstract ViewPartialRewriting compensateViewPartial( *

If a rewriting can be produced, we return that rewriting. If it cannot * be produced, we will return null. */ - protected abstract RelNode rewriteQuery( + protected abstract @Nullable RelNode rewriteQuery( RelBuilder relBuilder, RexBuilder rexBuilder, RexSimplify simplify, RelMetadataQuery mq, RexNode compensationColumnsEquiPred, RexNode otherCompensationPred, Project topProject, RelNode node, @@ -513,8 +518,8 @@ protected abstract RelNode rewriteQuery( * generating the union and any other operator needed on top of it, e.g., a Project * operator. */ - protected abstract RelNode createUnion(RelBuilder relBuilder, RexBuilder rexBuilder, - RelNode topProject, RelNode unionInputQuery, RelNode unionInputView); + protected abstract @Nullable RelNode createUnion(RelBuilder relBuilder, RexBuilder rexBuilder, + @Nullable RelNode topProject, RelNode unionInputQuery, RelNode unionInputView); /** * Rewrites the query using the given view query. @@ -523,11 +528,11 @@ protected abstract RelNode createUnion(RelBuilder relBuilder, RexBuilder rexBuil * on top. If a rewriting can be produced, we return that rewriting. If it cannot * be produced, we will return null. */ - protected abstract RelNode rewriteView(RelBuilder relBuilder, RexBuilder rexBuilder, + protected abstract @Nullable RelNode rewriteView(RelBuilder relBuilder, RexBuilder rexBuilder, RexSimplify simplify, RelMetadataQuery mq, MatchModality matchModality, boolean unionRewriting, RelNode input, - Project topProject, RelNode node, - Project topViewProject, RelNode viewNode, + @Nullable Project topProject, RelNode node, + @Nullable Project topViewProject, RelNode viewNode, BiMap queryToViewTableMapping, EquivalenceClasses queryEC); @@ -540,8 +545,9 @@ protected abstract RelNode rewriteView(RelBuilder relBuilder, RexBuilder rexBuil *

The method will return a pair of nodes: the new top project on the left and * the new node on the right. */ - protected abstract Pair pushFilterToOriginalViewPlan(RelBuilder builder, - RelNode topViewProject, RelNode viewNode, RexNode cond); + protected abstract Pair<@Nullable RelNode, RelNode> pushFilterToOriginalViewPlan( + RelBuilder builder, + @Nullable RelNode topViewProject, RelNode viewNode, RexNode cond); //~ Methods ---------------------------------------------------------------- @@ -695,7 +701,7 @@ protected boolean compensatePartial( Set sourceTableRefs, EquivalenceClasses sourceEC, Set targetTableRefs, - Multimap compensationEquiColumns) { + @Nullable Multimap compensationEquiColumns) { // Create UK-FK graph with view tables final DirectedGraph graph = DefaultDirectedGraph.create(Edge::new); @@ -754,7 +760,7 @@ protected boolean compensatePartial( if (edge == null) { edge = graph.addEdge(tRef, parentTRef); } - edge.equiColumns.putAll(equiColumns); + castNonNull(edge).equiColumns.putAll(equiColumns); } } } @@ -803,7 +809,7 @@ protected boolean compensatePartial( * *

In turn, if containment cannot be confirmed, the method returns null. */ - protected Pair computeCompensationPredicates( + protected @Nullable Pair computeCompensationPredicates( RexBuilder rexBuilder, RexSimplify simplify, EquivalenceClasses sourceEC, @@ -852,7 +858,7 @@ protected Pair computeCompensationPredicates( * the target to make the equivalences classes match, it returns that * compensation predicate. */ - protected RexNode generateEquivalenceClasses(RexBuilder rexBuilder, + protected @Nullable RexNode generateEquivalenceClasses(RexBuilder rexBuilder, EquivalenceClasses sourceEC, EquivalenceClasses targetEC) { if (sourceEC.getEquivalenceClasses().isEmpty() && targetEC.getEquivalenceClasses().isEmpty()) { // No column equality predicates in query and view @@ -912,7 +918,7 @@ protected RexNode generateEquivalenceClasses(RexBuilder rexBuilder, *

If any of the source equivalence classes cannot be mapped to a target equivalence * class, it returns null. */ - protected Multimap extractPossibleMapping( + protected @Nullable Multimap extractPossibleMapping( List> sourceEquivalenceClasses, List> targetEquivalenceClasses) { Multimap mapping = ArrayListMultimap.create(); @@ -949,7 +955,7 @@ protected Multimap extractPossibleMapping( *

The method will return the rewritten expression. If any of the expressions in the input * expression cannot be mapped, it will return null. */ - protected RexNode rewriteExpression( + protected @Nullable RexNode rewriteExpression( RexBuilder rexBuilder, RelMetadataQuery mq, RelNode targetNode, @@ -980,7 +986,7 @@ protected RexNode rewriteExpression( *

The method will return the rewritten expressions. If any of the subexpressions in the input * expressions cannot be mapped, it will return null. */ - protected List rewriteExpressions( + protected @Nullable List rewriteExpressions( RexBuilder rexBuilder, RelMetadataQuery mq, RelNode targetNode, @@ -1086,7 +1092,7 @@ protected NodeLineage generateSwapColumnTableReferencesLineage( * digest of the expression and the index that the replacement input ref should * point to. */ - protected RexNode replaceWithOriginalReferences(final RexBuilder rexBuilder, + protected @Nullable RexNode replaceWithOriginalReferences(final RexBuilder rexBuilder, final RelNode node, final NodeLineage nodeLineage, final RexNode exprToRewrite) { // Currently we allow the following: // 1) compensation pred can be directly map to expression @@ -1104,7 +1110,7 @@ protected RexNode replaceWithOriginalReferences(final RexBuilder rexBuilder, return rw != null ? rw : super.visitTableInputRef(inputRef); } - private RexNode replace(RexNode e) { + private @Nullable RexNode replace(RexNode e) { Integer pos = nodeLineage.exprsLineage.get(e); if (pos != null) { // Found it @@ -1127,7 +1133,7 @@ private RexNode replace(RexNode e) { * input column set. If a reference index cannot be found in * the input set, then we return null. */ - protected RexNode shuttleReferences(final RexBuilder rexBuilder, + protected @Nullable RexNode shuttleReferences(final RexBuilder rexBuilder, final RexNode node, final Mapping mapping) { try { RexShuttle visitor = @@ -1152,7 +1158,7 @@ protected RexNode shuttleReferences(final RexBuilder rexBuilder, * Replaces all the possible sub-expressions by input references * to the input node. */ - protected RexNode shuttleReferences(final RexBuilder rexBuilder, + protected @Nullable RexNode shuttleReferences(final RexBuilder rexBuilder, final RexNode expr, final Multimap exprsLineage) { return shuttleReferences(rexBuilder, expr, exprsLineage, null, null); @@ -1164,9 +1170,9 @@ protected RexNode shuttleReferences(final RexBuilder rexBuilder, * to change the position to reference. Takes the reference type * from the input node. */ - protected RexNode shuttleReferences(final RexBuilder rexBuilder, + protected @Nullable RexNode shuttleReferences(final RexBuilder rexBuilder, final RexNode expr, final Multimap exprsLineage, - final RelNode node, final Multimap rewritingMapping) { + final @Nullable RelNode node, final @Nullable Multimap rewritingMapping) { try { RexShuttle visitor = new RexShuttle() { @@ -1243,8 +1249,8 @@ protected RexNode shuttleReferences(final RexBuilder rexBuilder, protected static class EquivalenceClasses { private final Map> nodeToEquivalenceClass; - private Map> cacheEquivalenceClassesMap; - private List> cacheEquivalenceClasses; + private @Nullable Map> cacheEquivalenceClassesMap; + private @Nullable List> cacheEquivalenceClasses; protected EquivalenceClasses() { nodeToEquivalenceClass = new HashMap<>(); @@ -1390,10 +1396,10 @@ public interface Config extends RelRule.Config { /** If we generate union rewriting, we might want to pull up projections * from the query itself to maximize rewriting opportunities. */ @ImmutableBeans.Property - HepProgram unionRewritingPullProgram(); + @Nullable HepProgram unionRewritingPullProgram(); /** Sets {@link #unionRewritingPullProgram()}. */ - Config withUnionRewritingPullProgram(HepProgram program); + Config withUnionRewritingPullProgram(@Nullable HepProgram program); /** Whether we should create the rewriting in the minimal subtree of plan * operators. */ diff --git a/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java b/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java index 74b02c52fcdf..76af9dfd761b 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java +++ b/core/src/main/java/org/apache/calcite/rel/type/DelegatingTypeSystem.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Implementation of {@link org.apache.calcite.rel.type.RelDataTypeSystem} * that sends all methods to an underlying object. */ public class DelegatingTypeSystem implements RelDataTypeSystem { @@ -48,7 +50,7 @@ protected DelegatingTypeSystem(RelDataTypeSystem typeSystem) { return typeSystem.getMaxNumericPrecision(); } - @Override public String getLiteral(SqlTypeName typeName, boolean isPrefix) { + @Override public @Nullable String getLiteral(SqlTypeName typeName, boolean isPrefix) { return typeSystem.getLiteral(typeName, isPrefix); } diff --git a/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java b/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java index 7ef18134d0aa..22084195b4ac 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/type/DynamicRecordTypeImpl.java @@ -17,11 +17,14 @@ package org.apache.calcite.rel.type; import org.apache.calcite.sql.type.SqlTypeExplicitPrecedenceList; +import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Pair; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -36,6 +39,7 @@ public class DynamicRecordTypeImpl extends DynamicRecordType { private final RelDataTypeHolder holder; /** Creates a DynamicRecordTypeImpl. */ + @SuppressWarnings("method.invocation.invalid") public DynamicRecordTypeImpl(RelDataTypeFactory typeFactory) { this.holder = new RelDataTypeHolder(typeFactory); computeDigest(); @@ -49,7 +53,7 @@ public DynamicRecordTypeImpl(RelDataTypeFactory typeFactory) { return holder.getFieldCount(); } - @Override public RelDataTypeField getField(String fieldName, + @Override public @Nullable RelDataTypeField getField(String fieldName, boolean caseSensitive, boolean elideRecord) { final Pair pair = holder.getFieldOrInsert(fieldName, caseSensitive); @@ -82,7 +86,8 @@ public DynamicRecordTypeImpl(RelDataTypeFactory typeFactory) { } @Override public RelDataTypeFamily getFamily() { - return getSqlTypeName().getFamily(); + SqlTypeFamily family = getSqlTypeName().getFamily(); + return family != null ? family : this; } } diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java b/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java index 5c8fcd8fe93b..31445cc3d3a8 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java @@ -39,6 +39,7 @@ public class RelCrossType extends RelDataTypeImpl { * Creates a cartesian product type. This should only be called from a * factory method. */ + @SuppressWarnings("method.invocation.invalid") public RelCrossType( List types, List fields) { diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java index 21fe0e134454..60df7f16e70a 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java @@ -22,6 +22,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import java.nio.charset.Charset; import java.util.List; @@ -46,6 +48,7 @@ public interface RelDataType { * @return whether this type has fields; examples include rows and * user-defined structured types in SQL, and classes in Java */ + @Pure boolean isStruct(); // NOTE jvs 17-Dec-2004: once we move to Java generics, getFieldList() @@ -103,7 +106,7 @@ public interface RelDataType { * @param elideRecord Whether to find fields nested within records * @return named field, or null if not found */ - RelDataTypeField getField(String fieldName, boolean caseSensitive, + @Nullable RelDataTypeField getField(String fieldName, boolean caseSensitive, boolean elideRecord); /** @@ -111,6 +114,7 @@ RelDataTypeField getField(String fieldName, boolean caseSensitive, * * @return whether type allows null values */ + @Pure boolean isNullable(); /** @@ -118,21 +122,22 @@ RelDataTypeField getField(String fieldName, boolean caseSensitive, * * @return canonical type descriptor for components */ - RelDataType getComponentType(); + @Pure + @Nullable RelDataType getComponentType(); /** * Gets the key type if this type is a map, otherwise null. * * @return canonical type descriptor for key */ - RelDataType getKeyType(); + @Nullable RelDataType getKeyType(); /** * Gets the value type if this type is a map, otherwise null. * * @return canonical type descriptor for value */ - RelDataType getValueType(); + @Nullable RelDataType getValueType(); /** * Gets this type's character set, or null if this type cannot carry a @@ -140,7 +145,8 @@ RelDataTypeField getField(String fieldName, boolean caseSensitive, * * @return charset of type */ - Charset getCharset(); + @Pure + @Nullable Charset getCharset(); /** * Gets this type's collation, or null if this type cannot carry a collation @@ -148,7 +154,8 @@ RelDataTypeField getField(String fieldName, boolean caseSensitive, * * @return collation of type */ - SqlCollation getCollation(); + @Pure + @Nullable SqlCollation getCollation(); /** * Gets this type's interval qualifier, or null if this is not an interval @@ -156,7 +163,8 @@ RelDataTypeField getField(String fieldName, boolean caseSensitive, * * @return interval qualifier */ - SqlIntervalQualifier getIntervalQualifier(); + @Pure + @Nullable SqlIntervalQualifier getIntervalQualifier(); /** * Gets the JDBC-defined precision for values of this type. Note that this @@ -199,7 +207,8 @@ RelDataTypeField getField(String fieldName, boolean caseSensitive, * * @return SqlIdentifier, or null if this is not an SQL type */ - SqlIdentifier getSqlIdentifier(); + @Pure + @Nullable SqlIdentifier getSqlIdentifier(); /** * Gets a string representation of this type without detail such as @@ -242,7 +251,7 @@ RelDataTypeField getField(String fieldName, boolean caseSensitive, * field names. If it is not a struct, just return the result of {@code * #equals(Object)}. */ @API(since = "1.24", status = API.Status.INTERNAL) - default boolean equalsSansFieldNames(RelDataType that) { + default boolean equalsSansFieldNames(@Nullable RelDataType that) { if (this == that) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java index f4cf07405ec3..70af1b014fc5 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java @@ -23,6 +23,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SqlValidatorUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -196,7 +198,7 @@ RelDataType createTypeWithCharsetAndCollation( * @param types input types to be combined using union (not null, not empty) * @return canonical union type descriptor */ - RelDataType leastRestrictive(List types); + @Nullable RelDataType leastRestrictive(List types); /** * Creates a SQL type with no precision or scale. @@ -272,7 +274,7 @@ RelDataType createSqlIntervalType( * {@link RelDataTypeSystem#deriveDecimalMultiplyType(RelDataTypeFactory, RelDataType, RelDataType)} */ @Deprecated // to be removed before 2.0 - RelDataType createDecimalProduct( + @Nullable RelDataType createDecimalProduct( RelDataType type1, RelDataType type2); @@ -304,7 +306,7 @@ boolean useDoubleMultiplication( * {@link RelDataTypeSystem#deriveDecimalDivideType(RelDataTypeFactory, RelDataType, RelDataType)} */ @Deprecated // to be removed before 2.0 - RelDataType createDecimalQuotient( + @Nullable RelDataType createDecimalQuotient( RelDataType type1, RelDataType type2); diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java index 8bbbc5b76183..7904ab712804 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java @@ -32,6 +32,8 @@ import com.google.common.collect.Interner; import com.google.common.collect.Interners; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.charset.Charset; @@ -42,7 +44,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import javax.annotation.Nonnull; /** * Abstract base for implementations of {@link RelDataTypeFactory}. @@ -65,7 +66,7 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory { private static final Interner DATATYPE_CACHE = Interners.newWeakInterner(); - private static RelDataType keyToType(@Nonnull Key key) { + private static RelDataType keyToType(Key key) { final ImmutableList.Builder list = ImmutableList.builder(); for (int i = 0; i < key.names.size(); i++) { @@ -208,7 +209,7 @@ private RelDataType createStructType( }, nullable); } - @Override public RelDataType leastRestrictive(List types) { + @Override public @Nullable RelDataType leastRestrictive(List types) { assert types != null; assert types.size() >= 1; RelDataType type0 = types.get(0); @@ -218,7 +219,7 @@ private RelDataType createStructType( return null; } - protected RelDataType leastRestrictiveStructuredType( + protected @Nullable RelDataType leastRestrictiveStructuredType( final List types) { final RelDataType type0 = types.get(0); final int fieldCount = type0.getFieldCount(); @@ -242,18 +243,16 @@ protected RelDataType leastRestrictiveStructuredType( // REVIEW jvs 22-Jan-2004: Always use the field name from the // first type? final int k = j; + + RelDataType type = leastRestrictive( + Util.transform(types, t -> t.getFieldList().get(k).getType()) + ); + if (type == null) { + return null; + } builder.add( type0.getFieldList().get(j).getName(), - leastRestrictive( - new AbstractList() { - @Override public RelDataType get(int index) { - return types.get(index).getFieldList().get(k).getType(); - } - - @Override public int size() { - return types.size(); - } - })); + type); } return createTypeWithNullability(builder.build(), isNullable); } @@ -437,7 +436,7 @@ public static boolean isJavaType(RelDataType t) { return t instanceof JavaType; } - private List fieldsOf(Class clazz) { + private @Nullable List fieldsOf(Class clazz) { final List list = new ArrayList<>(); for (Field field : clazz.getFields()) { if (Modifier.isStatic(field.getModifiers())) { @@ -462,8 +461,8 @@ private List fieldsOf(Class clazz) { * {@link RelDataTypeSystem#deriveDecimalMultiplyType(RelDataTypeFactory, RelDataType, RelDataType)} * to get the return type for the operation. */ - @Override @Deprecated - public RelDataType createDecimalProduct( + @Deprecated + @Override public @Nullable RelDataType createDecimalProduct( RelDataType type1, RelDataType type2) { return typeSystem.deriveDecimalMultiplyType(this, type1, type2); @@ -486,8 +485,8 @@ public RelDataType createDecimalProduct( * {@link RelDataTypeSystem#deriveDecimalDivideType(RelDataTypeFactory, RelDataType, RelDataType)} * to get the return type for the operation. */ - @Override @Deprecated - public RelDataType createDecimalQuotient( + @Deprecated + @Override public @Nullable RelDataType createDecimalQuotient( RelDataType type1, RelDataType type2) { return typeSystem.deriveDecimalDivideType(this, type1, type2); @@ -551,8 +550,8 @@ private RelDataType decimalOf2(RelDataType type) { public class JavaType extends RelDataTypeImpl { private final Class clazz; private final boolean nullable; - private SqlCollation collation; - private Charset charset; + private @Nullable SqlCollation collation; + private @Nullable Charset charset; public JavaType(Class clazz) { this(clazz, !clazz.isPrimitive()); @@ -564,11 +563,12 @@ public JavaType( this(clazz, nullable, null, null); } + @SuppressWarnings("argument.type.incompatible") public JavaType( Class clazz, boolean nullable, - Charset charset, - SqlCollation collation) { + @Nullable Charset charset, + @Nullable SqlCollation collation) { super(fieldsOf(clazz)); this.clazz = clazz; this.nullable = nullable; @@ -598,7 +598,7 @@ public Class getJavaClass() { sb.append(")"); } - @Override public RelDataType getComponentType() { + @Override public @Nullable RelDataType getComponentType() { final Class componentType = clazz.getComponentType(); if (componentType == null) { return null; @@ -611,7 +611,7 @@ public Class getJavaClass() { * For {@link JavaType} created with {@link Map} class, * we cannot get the key type. Use ANY as key type. */ - @Override public RelDataType getKeyType() { + @Override public @Nullable RelDataType getKeyType() { if (Map.class.isAssignableFrom(clazz)) { // Need to return a SQL type because the type inference needs SqlTypeName. return createSqlType(SqlTypeName.ANY); @@ -624,7 +624,7 @@ public Class getJavaClass() { * For {@link JavaType} created with {@link Map} class, * we cannot get the value type. Use ANY as value type. */ - @Override public RelDataType getValueType() { + @Override public @Nullable RelDataType getValueType() { if (Map.class.isAssignableFrom(clazz)) { // Need to return a SQL type because the type inference needs SqlTypeName. return createSqlType(SqlTypeName.ANY); @@ -633,11 +633,11 @@ public Class getJavaClass() { } } - @Override public Charset getCharset() { + @Override public @Nullable Charset getCharset() { return this.charset; } - @Override public SqlCollation getCollation() { + @Override public @Nullable SqlCollation getCollation() { return this.collation; } @@ -669,7 +669,7 @@ private static class Key { return Objects.hash(kind, names, types, nullable); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof Key && kind == ((Key) obj).kind diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeField.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeField.java index 7fceec70d5d0..da4e079a7aef 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeField.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeField.java @@ -36,6 +36,7 @@ public interface RelDataTypeField extends Map.Entry { * @deprecated Use {@code RelDataTypeField::getIndex} */ @Deprecated // to be removed before 2.0 + @SuppressWarnings("nullability") class ToFieldIndex implements com.google.common.base.Function { @Override public Integer apply(RelDataTypeField o) { @@ -50,6 +51,7 @@ class ToFieldIndex * @deprecated Use {@code RelDataTypeField::getName} */ @Deprecated // to be removed before 2.0 + @SuppressWarnings("nullability") class ToFieldName implements com.google.common.base.Function { @Override public String apply(RelDataTypeField o) { diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFieldImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFieldImpl.java index 909da959a4c2..8ea986bdc39e 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFieldImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFieldImpl.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.Serializable; import java.util.Objects; @@ -53,7 +55,7 @@ public RelDataTypeFieldImpl( return Objects.hash(index, name, type); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java index c880d9884453..f5c85fc9e9e1 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeImpl.java @@ -28,13 +28,19 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.Serializable; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static org.apache.calcite.linq4j.Nullness.castNonNull; +import static java.util.Objects.requireNonNull; + /** * RelDataTypeImpl is an abstract base for implementations of * {@link RelDataType}. @@ -46,8 +52,8 @@ public abstract class RelDataTypeImpl implements RelDataType, RelDataTypeFamily { //~ Instance fields -------------------------------------------------------- - protected final List fieldList; - protected String digest; + protected final @Nullable List fieldList; + protected @Nullable String digest; //~ Constructors ----------------------------------------------------------- @@ -56,7 +62,7 @@ public abstract class RelDataTypeImpl * * @param fieldList List of fields */ - protected RelDataTypeImpl(List fieldList) { + protected RelDataTypeImpl(@Nullable List fieldList) { if (fieldList != null) { // Create a defensive copy of the list. this.fieldList = ImmutableList.copyOf(fieldList); @@ -79,9 +85,9 @@ protected RelDataTypeImpl() { //~ Methods ---------------------------------------------------------------- - @Override public RelDataTypeField getField(String fieldName, boolean caseSensitive, + @Override public @Nullable RelDataTypeField getField(String fieldName, boolean caseSensitive, boolean elideRecord) { - for (RelDataTypeField field : fieldList) { + for (RelDataTypeField field : requireNonNull(fieldList, "fieldList")) { if (Util.matches(caseSensitive, field.getName(), fieldName)) { return field; } @@ -163,17 +169,17 @@ private static void getFieldRecurse(List slots, RelDataType type, return isStruct() ? StructKind.FULLY_QUALIFIED : StructKind.NONE; } - @Override public RelDataType getComponentType() { + @Override public @Nullable RelDataType getComponentType() { // this is not a collection type return null; } - @Override public RelDataType getKeyType() { + @Override public @Nullable RelDataType getKeyType() { // this is not a map type return null; } - @Override public RelDataType getValueType() { + @Override public @Nullable RelDataType getValueType() { // this is not a map type return null; } @@ -182,33 +188,33 @@ private static void getFieldRecurse(List slots, RelDataType type, return fieldList != null; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RelDataTypeImpl - && this.digest.equals(((RelDataTypeImpl) obj).digest); + && Objects.equals(this.digest, ((RelDataTypeImpl) obj).digest); } @Override public int hashCode() { - return digest.hashCode(); + return Objects.hashCode(digest); } @Override public String getFullTypeString() { - return digest; + return requireNonNull(digest, "digest"); } @Override public boolean isNullable() { return false; } - @Override public Charset getCharset() { + @Override public @Nullable Charset getCharset() { return null; } - @Override public SqlCollation getCollation() { + @Override public @Nullable SqlCollation getCollation() { return null; } - @Override public SqlIntervalQualifier getIntervalQualifier() { + @Override public @Nullable SqlIntervalQualifier getIntervalQualifier() { return null; } @@ -231,7 +237,7 @@ private static void getFieldRecurse(List slots, RelDataType type, return castNonNull(null); } - @Override public SqlIdentifier getSqlIdentifier() { + @Override public @Nullable SqlIdentifier getSqlIdentifier() { SqlTypeName typeName = getSqlTypeName(); if (typeName == null) { return null; @@ -262,7 +268,10 @@ protected abstract void generateTypeString( * Computes the digest field. This should be called in every non-abstract * subclass constructor once the type is fully defined. */ - protected void computeDigest() { + @SuppressWarnings("method.invocation.invalid") + protected void computeDigest( + @UnknownInitialization RelDataTypeImpl this + ) { StringBuilder sb = new StringBuilder(); generateTypeString(sb, true); if (!isNullable()) { @@ -376,7 +385,7 @@ public static RelProtoDataType proto(final SqlTypeName typeName, * @param rowType Row type * @return The "extra" field, or null */ - public static RelDataTypeField extra(RelDataType rowType) { + public static @Nullable RelDataTypeField extra(RelDataType rowType) { // Even in a case-insensitive connection, the name must be precisely // "_extra". return rowType.getField("_extra", true, false); @@ -389,6 +398,6 @@ public static RelDataTypeField extra(RelDataType rowType) { /** Work space for {@link RelDataTypeImpl#getFieldRecurse}. */ private static class Slot { int count; - RelDataTypeField field; + @Nullable RelDataTypeField field; } } diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java index 03189fe895d4..5f6095da7632 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystem.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.util.Glossary; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Type system. * @@ -59,7 +61,7 @@ public interface RelDataTypeSystem { int getMaxNumericPrecision(); /** Returns the LITERAL string for the type, either PREFIX/SUFFIX. */ - String getLiteral(SqlTypeName typeName, boolean isPrefix); + @Nullable String getLiteral(SqlTypeName typeName, boolean isPrefix); /** Returns whether the type is case sensitive. */ boolean isCaseSensitive(SqlTypeName typeName); @@ -145,7 +147,7 @@ default boolean shouldUseDoubleMultiplication( * @param type2 Type of the second operand * @return Result type for a decimal addition */ - default RelDataType deriveDecimalPlusType(RelDataTypeFactory typeFactory, + default @Nullable RelDataType deriveDecimalPlusType(RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) { if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) { @@ -214,7 +216,7 @@ default RelDataType deriveDecimalPlusType(RelDataTypeFactory typeFactory, * @return Result type for a decimal multiplication, or null if decimal * multiplication should not be applied to the operands */ - default RelDataType deriveDecimalMultiplyType(RelDataTypeFactory typeFactory, + default @Nullable RelDataType deriveDecimalMultiplyType(RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) { if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) { @@ -286,7 +288,7 @@ default RelDataType deriveDecimalMultiplyType(RelDataTypeFactory typeFactory, * @return Result type for a decimal division, or null if decimal * division should not be applied to the operands */ - default RelDataType deriveDecimalDivideType(RelDataTypeFactory typeFactory, + default @Nullable RelDataType deriveDecimalDivideType(RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) { if (SqlTypeUtil.isExactNumeric(type1) @@ -368,7 +370,7 @@ default RelDataType deriveDecimalDivideType(RelDataTypeFactory typeFactory, * @return Result type for a decimal modulus, or null if decimal * modulus should not be applied to the operands */ - default RelDataType deriveDecimalModType(RelDataTypeFactory typeFactory, + default @Nullable RelDataType deriveDecimalModType(RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) { if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) { diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java index 65c32aaedb75..d6954c66c3bd 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Default implementation of * {@link org.apache.calcite.rel.type.RelDataTypeSystem}, * providing parameters from the SQL standard. @@ -154,7 +156,7 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem { return 19; } - @Override public String getLiteral(SqlTypeName typeName, boolean isPrefix) { + @Override public @Nullable String getLiteral(SqlTypeName typeName, boolean isPrefix) { switch (typeName) { case VARBINARY: case VARCHAR: diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java b/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java index 932959f23fc8..210e4a1c0e19 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelRecordType.java @@ -21,7 +21,8 @@ import java.io.Serializable; import java.util.List; -import java.util.Objects; + +import static java.util.Objects.requireNonNull; /** * RelRecordType represents a structured type having named fields. @@ -43,7 +44,7 @@ public class RelRecordType extends RelDataTypeImpl implements Serializable { public RelRecordType(StructKind kind, List fields, boolean nullable) { super(fields); this.nullable = nullable; - this.kind = Objects.requireNonNull(kind); + this.kind = requireNonNull(kind); computeDigest(); } @@ -100,7 +101,7 @@ public RelRecordType(List fields) { break; } sb.append("("); - for (Ord ord : Ord.zip(fieldList)) { + for (Ord ord : Ord.zip(requireNonNull(fieldList, "fieldList"))) { if (ord.i > 0) { sb.append(", "); } @@ -125,7 +126,7 @@ public RelRecordType(List fields) { * it back to a RelRecordType during deserialization. */ private Object writeReplace() { - return new SerializableRelRecordType(fieldList); + return new SerializableRelRecordType(requireNonNull(fieldList, "fieldList")); } //~ Inner Classes ---------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java index 881994a160a1..95d78211dfda 100644 --- a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java +++ b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java @@ -20,16 +20,20 @@ import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Visitor pattern for traversing a tree of {@link RexNode} objects. */ -public class LogicVisitor extends RexUnaryBiVisitor { +public class LogicVisitor extends RexUnaryBiVisitor<@Nullable Logic> { private final RexNode seek; private final Collection logicCollection; @@ -78,7 +82,7 @@ public static void collect(RexNode node, RexNode seek, Logic logic, Collections.replaceAll(logicList, Logic.FALSE, Logic.UNKNOWN_AS_TRUE); } - @Override public Logic visitCall(RexCall call, Logic logic) { + @Override public @Nullable Logic visitCall(RexCall call, @Nullable Logic logic) { final Logic arg0 = logic; switch (call.getKind()) { case IS_NOT_NULL: @@ -94,7 +98,7 @@ public static void collect(RexNode node, RexNode seek, Logic logic, logic = Logic.UNKNOWN_AS_TRUE; break; case NOT: - logic = logic.negate2(); + logic = requireNonNull(logic, "logic").negate2(); break; case CASE: logic = Logic.TRUE_FALSE_UNKNOWN; @@ -120,22 +124,23 @@ public static void collect(RexNode node, RexNode seek, Logic logic, return end(call, arg0); } - @Override protected Logic end(RexNode node, Logic arg) { + @Override protected @Nullable Logic end(RexNode node, @Nullable Logic arg) { if (node.equals(seek)) { logicCollection.add(arg); } return arg; } - @Override public Logic visitOver(RexOver over, Logic arg) { + @Override public @Nullable Logic visitOver(RexOver over, @Nullable Logic arg) { return end(over, arg); } - @Override public Logic visitFieldAccess(RexFieldAccess fieldAccess, Logic arg) { + @Override public @Nullable Logic visitFieldAccess(RexFieldAccess fieldAccess, + @Nullable Logic arg) { return end(fieldAccess, arg); } - @Override public Logic visitSubQuery(RexSubQuery subQuery, Logic arg) { + @Override public @Nullable Logic visitSubQuery(RexSubQuery subQuery, @Nullable Logic arg) { if (!subQuery.getType().isNullable()) { if (arg == Logic.TRUE_FALSE_UNKNOWN) { arg = Logic.TRUE_FALSE; diff --git a/core/src/main/java/org/apache/calcite/rex/RexBiVisitorImpl.java b/core/src/main/java/org/apache/calcite/rex/RexBiVisitorImpl.java index 83a834308e7e..afdd5b5bc539 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexBiVisitorImpl.java +++ b/core/src/main/java/org/apache/calcite/rex/RexBiVisitorImpl.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.rex; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Default implementation of {@link RexBiVisitor}, which visits each node but * does nothing while it's there. @@ -23,7 +25,7 @@ * @param Return type from each {@code visitXxx} method * @param

Payload type */ -public class RexBiVisitorImpl implements RexBiVisitor { +public class RexBiVisitorImpl<@Nullable R, P> implements RexBiVisitor { //~ Instance fields -------------------------------------------------------- protected final boolean deep; diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java index 304a33765e9f..6fe22c6cfbc6 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java +++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java @@ -58,9 +58,13 @@ import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -68,7 +72,8 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; -import javax.annotation.Nonnull; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; /** * Factory for row expressions. @@ -109,6 +114,7 @@ public class RexBuilder { * * @param typeFactory Type factory */ + @SuppressWarnings("method.invocation.invalid") public RexBuilder(RelDataTypeFactory typeFactory) { this.typeFactory = typeFactory; this.booleanTrue = @@ -135,7 +141,7 @@ public RexBuilder(RelDataTypeFactory typeFactory) { /** Creates a list of {@link org.apache.calcite.rex.RexInputRef} expressions, * projecting the fields of a given record type. */ - public List identityProjects(final RelDataType rowType) { + public List identityProjects(final RelDataType rowType) { return Util.transform(rowType.getFieldList(), input -> new RexInputRef(input.getIndex(), input.getType())); } @@ -302,10 +308,11 @@ public RelDataType deriveReturnType( public RexNode addAggCall(AggregateCall aggCall, int groupCount, List aggCalls, Map aggCallMapping, - final List aggArgTypes) { + final @Nullable List aggArgTypes) { if (aggCall.getAggregation() instanceof SqlCountAggFunction && !aggCall.isDistinct()) { final List args = aggCall.getArgList(); + Objects.requireNonNull(aggArgTypes, "aggArgTypes"); final List nullableArgs = nullableArgs(args, aggArgTypes); if (!nullableArgs.equals(args)) { aggCall = aggCall.copy(nullableArgs, aggCall.filterArg, @@ -329,7 +336,7 @@ public RexNode addAggCall(AggregateCall aggCall, int groupCount, public RexNode addAggCall(AggregateCall aggCall, int groupCount, boolean indicator, List aggCalls, Map aggCallMapping, - final List aggArgTypes) { + final @Nullable List aggArgTypes) { Preconditions.checkArgument(!indicator, "indicator is deprecated, use GROUPING function instead"); return addAggCall(aggCall, groupCount, aggCalls, @@ -363,13 +370,13 @@ public RexNode makeOver(RelDataType type, SqlAggFunction operator, * Creates a call to a windowed agg. */ public RexNode makeOver( - @Nonnull RelDataType type, - @Nonnull SqlAggFunction operator, - @Nonnull List exprs, - @Nonnull List partitionKeys, - @Nonnull ImmutableList orderKeys, - @Nonnull RexWindowBound lowerBound, - @Nonnull RexWindowBound upperBound, + RelDataType type, + SqlAggFunction operator, + List exprs, + List partitionKeys, + ImmutableList orderKeys, + RexWindowBound lowerBound, + RexWindowBound upperBound, boolean rows, boolean allowPartial, boolean nullWhenCountZero, @@ -630,7 +637,7 @@ protected static TimeUnit baseUnit(SqlTypeName unit) { } } - boolean canRemoveCastFromLiteral(RelDataType toType, Comparable value, + boolean canRemoveCastFromLiteral(RelDataType toType, @Nullable Comparable value, SqlTypeName fromTypeName) { final SqlTypeName sqlType = toType.getSqlTypeName(); if (!RexLiteral.valueMatchesType(value, sqlType, false)) { @@ -927,7 +934,7 @@ public RexLiteral makeFlag(Enum flag) { * @return Literal */ protected RexLiteral makeLiteral( - Comparable o, + @Nullable Comparable o, RelDataType type, SqlTypeName typeName) { // All literals except NULL have NOT NULL types. @@ -941,15 +948,16 @@ protected RexLiteral makeLiteral( NlsString nlsString = (NlsString) o; if (nlsString.getCollation() == null || nlsString.getCharset() == null - || !nlsString.getCharset().equals(type.getCharset()) - || !nlsString.getCollation().equals(type.getCollation())) { + || !Objects.equals(nlsString.getCharset(), type.getCharset()) + || !Objects.equals(nlsString.getCollation(), type.getCollation())) { assert type.getSqlTypeName() == SqlTypeName.CHAR || type.getSqlTypeName() == SqlTypeName.VARCHAR; - assert type.getCharset().name() != null; - assert type.getCollation() != null; + Charset charset = type.getCharset(); + assert charset != null : "type.getCharset() must not be null"; + assert type.getCollation() != null : "type.getCollation() must not be null"; o = new NlsString( nlsString.getValue(), - type.getCharset().name(), + charset.name(), type.getCollation()); } break; @@ -1031,7 +1039,7 @@ public RexLiteral makeBigintLiteral(BigDecimal bd) { /** * Creates a numeric literal. */ - public RexLiteral makeExactLiteral(BigDecimal bd, RelDataType type) { + public RexLiteral makeExactLiteral(@Nullable BigDecimal bd, RelDataType type) { return makeLiteral(bd, type, SqlTypeName.DECIMAL); } @@ -1372,7 +1380,7 @@ && areAssignable(arg, Arrays.asList(lower, upper))) { /** Converts a list of expressions to a search argument, or returns null if * not possible. */ @SuppressWarnings({"BetaApi", "UnstableApiUsage"}) - private static > Sarg toSarg(Class clazz, + private static > @Nullable Sarg toSarg(Class clazz, List ranges, boolean containsNull) { if (ranges.isEmpty()) { // Cannot convert an empty list to a Sarg (by this interface, at least) @@ -1390,7 +1398,7 @@ private static > Sarg toSarg(Class clazz, return Sarg.of(containsNull, rangeSet); } - private static > C toComparable(Class clazz, + private static > @Nullable C toComparable(Class clazz, RexNode point) { switch (point.getKind()) { case LITERAL: @@ -1518,7 +1526,7 @@ public RexNode makeLiteral(Object value, RelDataType type, * to CHAR(3) 'foo' * @return Simple literal, or cast simple literal */ - public RexNode makeLiteral(Object value, RelDataType type, + public RexNode makeLiteral(@Nullable Object value, RelDataType type, boolean allowCast, boolean trim) { if (value == null) { return makeCast(type, constantNull); @@ -1594,7 +1602,7 @@ public RexNode makeLiteral(Object value, RelDataType type, case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: return makeIntervalLiteral((BigDecimal) value, - type.getIntervalQualifier()); + castNonNull(type.getIntervalQualifier())); case SYMBOL: return makeFlag((Enum) value); case MAP: @@ -1659,9 +1667,9 @@ public RexNode makeLiteral(Object value, RelDataType type, /** Converts the type of a value to comply with * {@link org.apache.calcite.rex.RexLiteral#valueMatchesType}. */ - private static Object clean(Object o, RelDataType type) { + private static @PolyNull Object clean(@PolyNull Object o, RelDataType type) { if (o == null) { - return null; + return o; } switch (type.getSqlTypeName()) { case TINYINT: @@ -1709,6 +1717,7 @@ private static Object clean(Object o, RelDataType type) { if (o instanceof NlsString) { return o; } + assert type.getCharset() != null : type + ".getCharset() must not be null"; return new NlsString((String) o, type.getCharset().name(), type.getCollation()); case TIME: @@ -1761,7 +1770,7 @@ private static Object clean(Object o, RelDataType type) { } } - private RelDataType guessType(Object value) { + private RelDataType guessType(@Nullable Object value) { if (value == null) { return typeFactory.createSqlType(SqlTypeName.NULL); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexCall.java b/core/src/main/java/org/apache/calcite/rex/RexCall.java index 5bea803685a4..70fee1a89c69 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexCall.java +++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java @@ -28,10 +28,11 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; /** * An expression formed by a call to an operator with zero or more expressions @@ -66,7 +67,7 @@ public class RexCall extends RexNode { /** * Cache of normalized variables used for #equals and #hashCode. */ - private Pair> normalized; + private @Nullable Pair> normalized; //~ Constructors ----------------------------------------------------------- @@ -141,7 +142,7 @@ protected final void appendOperands(StringBuilder sb) { } } - protected @Nonnull String computeDigest(boolean withType) { + protected String computeDigest(boolean withType) { final StringBuilder sb = new StringBuilder(op.getName()); if ((operands.size() == 0) && (op.getSyntax() == SqlSyntax.FUNCTION_ID)) { @@ -162,7 +163,7 @@ protected final void appendOperands(StringBuilder sb) { return sb.toString(); } - @Override public final @Nonnull String toString() { + @Override public final String toString() { return computeDigest(digestWithType()); } @@ -252,7 +253,7 @@ private Pair> getNormalized() { return this.normalized; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rex/RexCallBinding.java b/core/src/main/java/org/apache/calcite/rex/RexCallBinding.java index 8883b4dfda21..1e3ccdf91078 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexCallBinding.java +++ b/core/src/main/java/org/apache/calcite/rex/RexCallBinding.java @@ -31,6 +31,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -74,7 +76,7 @@ public static RexCallBinding create(RelDataTypeFactory typeFactory, //~ Methods ---------------------------------------------------------------- @SuppressWarnings("deprecation") - @Override public String getStringLiteralOperand(int ordinal) { + @Override public @Nullable String getStringLiteralOperand(int ordinal) { return RexLiteral.stringValue(operands.get(ordinal)); } @@ -83,7 +85,7 @@ public static RexCallBinding create(RelDataTypeFactory typeFactory, return RexLiteral.intValue(operands.get(ordinal)); } - @Override public T getOperandLiteralValue(int ordinal, Class clazz) { + @Override public @Nullable T getOperandLiteralValue(int ordinal, Class clazz) { final RexNode node = operands.get(ordinal); if (node instanceof RexLiteral) { return ((RexLiteral) node).getValueAs(clazz); diff --git a/core/src/main/java/org/apache/calcite/rex/RexChecker.java b/core/src/main/java/org/apache/calcite/rex/RexChecker.java index d35299241653..af6e4adc2f71 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexChecker.java +++ b/core/src/main/java/org/apache/calcite/rex/RexChecker.java @@ -22,8 +22,12 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.util.Litmus; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Visitor which checks the validity of a {@link RexNode} expression. * @@ -54,10 +58,10 @@ * * @see RexNode */ -public class RexChecker extends RexVisitorImpl { +public class RexChecker extends RexVisitorImpl<@Nullable Boolean> { //~ Instance fields -------------------------------------------------------- - protected final RelNode.Context context; + protected final RelNode.@Nullable Context context; protected final Litmus litmus; protected final List inputTypeList; protected int failCount; @@ -77,7 +81,7 @@ public class RexChecker extends RexVisitorImpl { * @param context Context of the enclosing {@link RelNode}, or null * @param litmus What to do if an invalid node is detected */ - public RexChecker(final RelDataType inputRowType, RelNode.Context context, + public RexChecker(final RelDataType inputRowType, RelNode.@Nullable Context context, Litmus litmus) { this(RelOptUtil.getFieldTypeList(inputRowType), context, litmus); } @@ -95,7 +99,7 @@ public RexChecker(final RelDataType inputRowType, RelNode.Context context, * @param context Context of the enclosing {@link RelNode}, or null * @param litmus What to do if an error is detected */ - public RexChecker(List inputTypeList, RelNode.Context context, + public RexChecker(List inputTypeList, RelNode.@Nullable Context context, Litmus litmus) { super(true); this.inputTypeList = inputTypeList; @@ -181,6 +185,7 @@ public int getFailureCount() { * Returns whether an expression is valid. */ public final boolean isValid(RexNode expr) { - return expr.accept(this); + return requireNonNull(expr.accept(this), + () -> "expr.accept(RexChecker) for expr=" + expr); } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java b/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java index 2e2b35ae4494..c7cdd820cfe9 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java +++ b/core/src/main/java/org/apache/calcite/rex/RexCorrelVariable.java @@ -20,6 +20,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlKind; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -55,10 +57,10 @@ public class RexCorrelVariable extends RexVariable { return SqlKind.CORREL_VARIABLE; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RexCorrelVariable - && digest.equals(((RexCorrelVariable) obj).digest) + && Objects.equals(digest, ((RexCorrelVariable) obj).digest) && type.equals(((RexCorrelVariable) obj).type) && id.equals(((RexCorrelVariable) obj).id); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java b/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java index 6477566ff32a..105b65324cfc 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java +++ b/core/src/main/java/org/apache/calcite/rex/RexDynamicParam.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlKind; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -62,7 +64,7 @@ public int getIndex() { return visitor.visitDynamicParam(this, arg); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RexDynamicParam && type.equals(((RexDynamicParam) obj).type) diff --git a/core/src/main/java/org/apache/calcite/rex/RexExecutable.java b/core/src/main/java/org/apache/calcite/rex/RexExecutable.java index a3eda5cc05b1..53f336cd3ede 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexExecutable.java +++ b/core/src/main/java/org/apache/calcite/rex/RexExecutable.java @@ -22,6 +22,7 @@ import org.apache.calcite.runtime.Utilities; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; import org.codehaus.commons.compiler.CompileException; import org.codehaus.janino.ClassBodyEvaluator; import org.codehaus.janino.Scanner; @@ -40,16 +41,16 @@ public class RexExecutable { private static final String GENERATED_CLASS_NAME = "Reducer"; - private final Function1 compiledFunction; + private final Function1 compiledFunction; private final String code; - private DataContext dataContext; + private @Nullable DataContext dataContext; public RexExecutable(String code, Object reason) { this.code = code; this.compiledFunction = compile(code, reason); } - private static Function1 compile(String code, + private static Function1 compile(String code, Object reason) { try { final ClassBodyEvaluator cbe = new ClassBodyEvaluator(); @@ -60,7 +61,7 @@ private static Function1 compile(String code, cbe.cook(new Scanner(null, new StringReader(code))); Class c = cbe.getClazz(); //noinspection unchecked - final Constructor> constructor = + final Constructor> constructor = c.getConstructor(); return constructor.newInstance(); } catch (CompileException | IOException | InstantiationException @@ -94,11 +95,11 @@ public void reduce(RexBuilder rexBuilder, List constExps, Hook.EXPRESSION_REDUCER.run(Pair.of(code, values)); } - public Function1 getFunction() { + public Function1 getFunction() { return compiledFunction; } - public Object[] execute() { + public Object @Nullable [] execute() { return compiledFunction.apply(dataContext); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java b/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java index 605c019771b2..060495478496 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java +++ b/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java @@ -39,6 +39,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.List; @@ -151,7 +153,7 @@ private static class DataContextInputGetter implements InputGetter { this.typeFactory = typeFactory; } - @Override public Expression field(BlockBuilder list, int index, Type storageType) { + @Override public Expression field(BlockBuilder list, int index, @Nullable Type storageType) { MethodCallExpression recFromCtx = Expressions.call( DataContext.ROOT, BuiltInMethod.DATA_CONTEXT_GET.method, diff --git a/core/src/main/java/org/apache/calcite/rex/RexFieldAccess.java b/core/src/main/java/org/apache/calcite/rex/RexFieldAccess.java index f8fc8f14db34..442bafe033b0 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexFieldAccess.java +++ b/core/src/main/java/org/apache/calcite/rex/RexFieldAccess.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Access to a field of a row-expression. * @@ -103,7 +105,7 @@ public RexNode getReferenceExpr() { return expr; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rex/RexInputRef.java b/core/src/main/java/org/apache/calcite/rex/RexInputRef.java index d7124bd933c7..c67e82ee5ad1 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexInputRef.java +++ b/core/src/main/java/org/apache/calcite/rex/RexInputRef.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.SqlKind; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -68,7 +70,7 @@ public RexInputRef(int index, RelDataType type) { //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RexInputRef && index == ((RexInputRef) obj).index; diff --git a/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java b/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java index 70d98df1daa7..ca969beb96ee 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java +++ b/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java @@ -26,6 +26,8 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.math.BigInteger; import java.util.Comparator; @@ -70,7 +72,7 @@ private RexInterpreter(Map environment) { } /** Evaluates an expression in an environment. */ - public static Comparable evaluate(RexNode e, Map map) { + public static @Nullable Comparable evaluate(RexNode e, Map map) { final Comparable v = e.accept(new RexInterpreter(map)); if (false) { System.out.println("evaluate " + e + " on " + map + " returns " + v); diff --git a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java index 21f9de3052c3..ebe119b7c471 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java +++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java @@ -46,6 +46,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; +import org.checkerframework.dataflow.qual.Pure; + import java.io.PrintWriter; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -58,6 +64,10 @@ import java.util.Objects; import java.util.TimeZone; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Constant value in a row-expression. * @@ -183,7 +193,7 @@ public class RexLiteral extends RexNode { * represented by a {@link BigDecimal}. But since this field is private, it * doesn't really matter how the values are stored. */ - private final Comparable value; + private final @Nullable Comparable value; /** * The real type of this literal, as reported by {@link #getType}. @@ -211,12 +221,12 @@ public class RexLiteral extends RexNode { * Creates a RexLiteral. */ RexLiteral( - Comparable value, + @Nullable Comparable value, RelDataType type, SqlTypeName typeName) { this.value = value; - this.type = Objects.requireNonNull(type); - this.typeName = Objects.requireNonNull(typeName); + this.type = requireNonNull(type); + this.typeName = requireNonNull(typeName); Preconditions.checkArgument(valueMatchesType(value, typeName, true)); Preconditions.checkArgument((value == null) == type.isNullable()); Preconditions.checkArgument(typeName != SqlTypeName.ANY); @@ -257,7 +267,10 @@ public class RexLiteral extends RexNode { * @param includeType whether the digest should include type or not * @return digest */ - public final String computeDigest(RexDigestIncludeType includeType) { + @RequiresNonNull({"typeName", "type"}) + public final String computeDigest( + @UnknownInitialization RexLiteral this, + RexDigestIncludeType includeType) { if (includeType == RexDigestIncludeType.OPTIONAL) { if (digest != null) { // digest is initialized with OPTIONAL, so cached value matches for @@ -282,14 +295,17 @@ public final String computeDigest(RexDigestIncludeType includeType) { * @see RexCall#computeDigest(boolean) * @return true if {@link RexDigestIncludeType#OPTIONAL} digest would include data type */ - RexDigestIncludeType digestIncludesType() { + @RequiresNonNull("type") + RexDigestIncludeType digestIncludesType( + @UnknownInitialization RexLiteral this + ) { return shouldIncludeType(value, type); } /** Returns whether a value is appropriate for its type. (We have rules about * these things!) */ public static boolean valueMatchesType( - Comparable value, + @Nullable Comparable value, SqlTypeName typeName, boolean strict) { if (value == null) { @@ -395,7 +411,7 @@ public static SqlTypeName strictTypeName(RelDataType type) { } private static String toJavaString( - Comparable value, + @Nullable Comparable value, SqlTypeName typeName, RelDataType type, RexDigestIncludeType includeType) { assert includeType != RexDigestIncludeType.OPTIONAL @@ -433,7 +449,8 @@ private static String toJavaString( * @param type type of the literal * @return NO_TYPE when type can be omitted, ALWAYS otherwise */ - private static RexDigestIncludeType shouldIncludeType(Comparable value, RelDataType type) { + private static RexDigestIncludeType shouldIncludeType(@Nullable Comparable value, + RelDataType type) { if (type.isNullable()) { // This means "null literal", so we require a type for it // There might be exceptions like AND(null, true) which are handled by RexCall#computeDigest @@ -454,10 +471,11 @@ private static RexDigestIncludeType shouldIncludeType(Comparable value, RelDataT // Ignore type information for 'Bar':CHAR(3) if (( - (nlsString.getCharset() != null && type.getCharset().equals(nlsString.getCharset())) + (nlsString.getCharset() != null + && Objects.equals(type.getCharset(), nlsString.getCharset())) || (nlsString.getCharset() == null - && SqlCollation.IMPLICIT.getCharset().equals(type.getCharset()))) - && nlsString.getCollation().equals(type.getCollation()) + && Objects.equals(SqlCollation.IMPLICIT.getCharset(), type.getCharset()))) + && Objects.equals(nlsString.getCollation(), type.getCollation()) && ((NlsString) value).getValue().length() == type.getPrecision()) { includeType = RexDigestIncludeType.NO_TYPE; } else { @@ -478,7 +496,7 @@ private static RexDigestIncludeType shouldIncludeType(Comparable value, RelDataT /** Returns whether a value is valid as a constant value, using the same * criteria as {@link #valueMatchesType}. */ - public static boolean validConstant(Object o, Litmus litmus) { + public static boolean validConstant(@Nullable Object o, Litmus litmus) { if (o == null || o instanceof BigDecimal || o instanceof NlsString @@ -598,12 +616,12 @@ public void printAsJava(PrintWriter pw) { * @param type Type to be used for the transformation of the value to a Java string * @param includeType Whether to include the data type in the Java representation */ - private static void appendAsJava(Comparable value, StringBuilder sb, + private static void appendAsJava(@Nullable Comparable value, StringBuilder sb, SqlTypeName typeName, RelDataType type, boolean java, RexDigestIncludeType includeType) { switch (typeName) { case CHAR: - NlsString nlsString = (NlsString) value; + NlsString nlsString = (NlsString) castNonNull(value); if (java) { Util.printJavaString( sb, @@ -689,13 +707,15 @@ private static void appendAsJava(Comparable value, StringBuilder sb, break; case MULTISET: case ROW: - final List list = (List) value; + assert value instanceof List : "value must implement List: " + value; + @SuppressWarnings("unchecked") final List list = + (List) castNonNull(value); Util.asStringBuilder(sb, sb2 -> Util.printList(sb, list.size(), (sb3, i) -> sb3.append(list.get(i).computeDigest(includeType)))); break; case GEOMETRY: - final String wkt = GeoFunctions.ST_AsWKT((Geometries.Geom) value); + final String wkt = GeoFunctions.ST_AsWKT((Geometries.Geom) castNonNull(value)); sb.append(wkt); break; default: @@ -717,6 +737,7 @@ private static RexLiteral toLiteral(RelDataType type, Comparable value) { final SqlTypeName typeName = strictTypeName(type); switch (typeName) { case ROW: + assert value instanceof List : "value must implement List: " + value; final List> fieldValues = (List) value; final List fields = type.getFieldList(); final List fieldLiterals = @@ -726,11 +747,12 @@ private static RexLiteral toLiteral(RelDataType type, Comparable value) { return new RexLiteral((Comparable) fieldLiterals, type, typeName); case MULTISET: + assert value instanceof List : "value must implement List: " + value; final List> elementValues = (List) value; final List elementLiterals = FlatLists.of( Functions.generate(elementValues.size(), i -> - toLiteral(type.getComponentType(), elementValues.get(i)))); + toLiteral(castNonNull(type.getComponentType()), elementValues.get(i)))); return new RexLiteral((Comparable) elementLiterals, type, typeName); default: @@ -752,17 +774,17 @@ private static RexLiteral toLiteral(RelDataType type, Comparable value) { * by the Jdbc call to return a column as a string * @return a typed RexLiteral, or null */ - public static RexLiteral fromJdbcString( + public static @PolyNull RexLiteral fromJdbcString( RelDataType type, SqlTypeName typeName, - String literal) { + @PolyNull String literal) { if (literal == null) { return null; } switch (typeName) { case CHAR: - Charset charset = type.getCharset(); + Charset charset = requireNonNull(type.getCharset(), () -> "charset for " + type); SqlCollation collation = type.getCollation(); NlsString str = new NlsString( @@ -771,7 +793,7 @@ public static RexLiteral fromJdbcString( collation); return new RexLiteral(str, type, typeName); case BOOLEAN: - boolean b = ConversionUtil.toBoolean(literal); + Boolean b = ConversionUtil.toBoolean(literal); return new RexLiteral(b, type, typeName); case DECIMAL: case DOUBLE: @@ -795,7 +817,7 @@ public static RexLiteral fromJdbcString( long millis = SqlParserUtil.intervalToMillis( literal, - type.getIntervalQualifier()); + castNonNull(type.getIntervalQualifier())); return new RexLiteral(BigDecimal.valueOf(millis), type, typeName); case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: @@ -803,7 +825,7 @@ public static RexLiteral fromJdbcString( long months = SqlParserUtil.intervalToMonths( literal, - type.getIntervalQualifier()); + castNonNull(type.getIntervalQualifier())); return new RexLiteral(BigDecimal.valueOf(months), type, typeName); case DATE: case TIME: @@ -892,7 +914,8 @@ public boolean isNull() { *

For backwards compatibility, returns DATE. TIME and TIMESTAMP as a * {@link Calendar} value in UTC time zone. */ - public Comparable getValue() { + @Pure + public @Nullable Comparable getValue() { assert valueMatchesType(value, typeName, true) : value; if (value == null) { return null; @@ -911,7 +934,7 @@ public Comparable getValue() { * Returns the value of this literal, in the form that the calculator * program builder wants it. */ - public Object getValue2() { + public @Nullable Object getValue2() { if (value == null) { return null; } @@ -935,7 +958,7 @@ public Object getValue2() { * Returns the value of this literal, in the form that the rex-to-lix * translator wants it. */ - public Object getValue3() { + public @Nullable Object getValue3() { if (value == null) { return null; } @@ -952,7 +975,7 @@ public Object getValue3() { * Returns the value of this literal, in the form that {@link RexInterpreter} * wants it. */ - public Comparable getValue4() { + public @Nullable Comparable getValue4() { if (value == null) { return null; } @@ -994,7 +1017,7 @@ public Comparable getValue4() { * @param Return type * @return Value of this literal in the desired type */ - public T getValueAs(Class clazz) { + public @Nullable T getValueAs(Class clazz) { if (value == null || clazz.isInstance(value)) { return clazz.cast(value); } @@ -1099,10 +1122,10 @@ public T getValueAs(Class clazz) { } else if (clazz == Long.class) { return clazz.cast(((BigDecimal) value).longValue()); } else if (clazz == String.class) { - return clazz.cast(intervalString(getValueAs(BigDecimal.class).abs())); + return clazz.cast(intervalString(castNonNull(getValueAs(BigDecimal.class)).abs())); } else if (clazz == Boolean.class) { // return whether negative - return clazz.cast(getValueAs(BigDecimal.class).signum() < 0); + return clazz.cast(castNonNull(getValueAs(BigDecimal.class)).signum() < 0); } break; default: @@ -1113,7 +1136,7 @@ public T getValueAs(Class clazz) { } public static boolean booleanValue(RexNode node) { - return (Boolean) ((RexLiteral) node).value; + return (Boolean) castNonNull(((RexLiteral) node).value); } @Override public boolean isAlwaysTrue() { @@ -1130,34 +1153,34 @@ public static boolean booleanValue(RexNode node) { return !booleanValue(this); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } return (obj instanceof RexLiteral) - && equals(((RexLiteral) obj).value, value) - && equals(((RexLiteral) obj).type, type); + && Objects.equals(((RexLiteral) obj).value, value) + && Objects.equals(((RexLiteral) obj).type, type); } @Override public int hashCode() { return Objects.hash(value, type); } - public static Comparable value(RexNode node) { + public static @Nullable Comparable value(RexNode node) { return findValue(node); } public static int intValue(RexNode node) { - final Comparable value = findValue(node); + final Comparable value = castNonNull(findValue(node)); return ((Number) value).intValue(); } - public static String stringValue(RexNode node) { + public static @Nullable String stringValue(RexNode node) { final Comparable value = findValue(node); return (value == null) ? null : ((NlsString) value).getValue(); } - private static Comparable findValue(RexNode node) { + private static @Nullable Comparable findValue(RexNode node) { if (node instanceof RexLiteral) { return ((RexLiteral) node).value; } @@ -1170,7 +1193,7 @@ private static Comparable findValue(RexNode node) { if (operator == SqlStdOperatorTable.UNARY_MINUS) { final BigDecimal value = (BigDecimal) findValue(call.getOperands().get(0)); - return value.negate(); + return requireNonNull(value, () -> "can't negate null in " + node).negate(); } } throw new AssertionError("not a literal: " + node); @@ -1181,10 +1204,6 @@ public static boolean isNullLiteral(RexNode node) { && (((RexLiteral) node).value == null); } - private static boolean equals(Object o1, Object o2) { - return Objects.equals(o1, o2); - } - @Override public R accept(RexVisitor visitor) { return visitor.visitLiteral(this); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexLocalRef.java b/core/src/main/java/org/apache/calcite/rex/RexLocalRef.java index 7cc31e9d4029..e3c0bc6388d6 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexLocalRef.java +++ b/core/src/main/java/org/apache/calcite/rex/RexLocalRef.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlKind; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -60,7 +62,7 @@ public RexLocalRef(int index, RelDataType type) { return SqlKind.LOCAL_REF; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RexLocalRef && Objects.equals(this.type, ((RexLocalRef) obj).type) diff --git a/core/src/main/java/org/apache/calcite/rex/RexMultisetUtil.java b/core/src/main/java/org/apache/calcite/rex/RexMultisetUtil.java index 953a00145df0..2534ed4d8a84 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexMultisetUtil.java +++ b/core/src/main/java/org/apache/calcite/rex/RexMultisetUtil.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Set; @@ -136,7 +138,7 @@ public static boolean isMultisetCast(RexCall call) { * Returns a reference to the first found multiset call or null if none was * found. */ - public static RexCall findFirstMultiset(final RexNode node, boolean deep) { + public static @Nullable RexCall findFirstMultiset(final RexNode node, boolean deep) { if (node instanceof RexFieldAccess) { return findFirstMultiset( ((RexFieldAccess) node).getReferenceExpr(), diff --git a/core/src/main/java/org/apache/calcite/rex/RexNode.java b/core/src/main/java/org/apache/calcite/rex/RexNode.java index 55bf69cd1e89..a3a841fb78c1 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexNode.java +++ b/core/src/main/java/org/apache/calcite/rex/RexNode.java @@ -19,8 +19,13 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlKind; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; +import static java.util.Objects.requireNonNull; + /** * Row expression. * @@ -40,7 +45,7 @@ public abstract class RexNode { //~ Instance fields -------------------------------------------------------- // Effectively final. Set in each sub-class constructor, and never re-set. - protected String digest; + protected @MonotonicNonNull String digest; //~ Methods ---------------------------------------------------------------- @@ -80,7 +85,7 @@ public SqlKind getKind() { } @Override public String toString() { - return digest; + return requireNonNull(digest, "digest"); } /** Returns the number of nodes in this expression. @@ -114,7 +119,7 @@ public int nodeCount() { * *

Every node must implement {@link #equals} based on its content */ - @Override public abstract boolean equals(Object obj); + @Override public abstract boolean equals(@Nullable Object obj); /** {@inheritDoc} * diff --git a/core/src/main/java/org/apache/calcite/rex/RexNormalize.java b/core/src/main/java/org/apache/calcite/rex/RexNormalize.java index fb7af07baf7b..c5b9cf9f4ff9 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexNormalize.java +++ b/core/src/main/java/org/apache/calcite/rex/RexNormalize.java @@ -175,7 +175,7 @@ private static int unorderedHash(List xs) { int b = 0; int c = 1; for (Object x : xs) { - int h = x.hashCode(); + int h = Objects.hashCode(x); a += h; b ^= h; if (h != 0) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexOver.java b/core/src/main/java/org/apache/calcite/rex/RexOver.java index dcfee0b00c5e..f2378c551601 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexOver.java +++ b/core/src/main/java/org/apache/calcite/rex/RexOver.java @@ -24,9 +24,10 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; /** * Call to an aggregate function over a window. @@ -96,7 +97,7 @@ public boolean ignoreNulls() { return ignoreNulls; } - @Override protected @Nonnull String computeDigest(boolean withType) { + @Override protected String computeDigest(boolean withType) { final StringBuilder sb = new StringBuilder(op.getName()); sb.append("("); if (distinct) { @@ -159,7 +160,7 @@ public static boolean containsOver(RexProgram program) { * Returns whether an expression list contains an OVER clause. */ public static boolean containsOver(List exprs, - RexNode condition) { + @Nullable RexNode condition) { try { RexUtil.apply(FINDER, exprs, condition); return false; @@ -197,7 +198,7 @@ private static class Finder extends RexVisitorImpl { } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java b/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java index 1f99fef7bbc6..9f418b1b7241 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java +++ b/core/src/main/java/org/apache/calcite/rex/RexPermuteInputsShuttle.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -100,7 +102,7 @@ private static ImmutableList fields(RelNode[] inputs) { return super.visitCall(call); } - private static int lookup(List fields, String name) { + private static int lookup(List fields, @Nullable String name) { for (int i = 0; i < fields.size(); i++) { final RelDataTypeField field = fields.get(i); if (field.getName().equals(name)) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgram.java b/core/src/main/java/org/apache/calcite/rex/RexProgram.java index 96a7267b0d52..bd0b5118c2f7 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexProgram.java +++ b/core/src/main/java/org/apache/calcite/rex/RexProgram.java @@ -40,6 +40,12 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; +import com.google.errorprone.annotations.CheckReturnValue; + +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import java.io.PrintWriter; import java.io.StringWriter; @@ -51,6 +57,10 @@ import java.util.List; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * A collection of expressions which read inputs, compute output expressions, * and optionally use a condition to filter rows. @@ -82,7 +92,7 @@ public class RexProgram { /** * The optional condition. If null, the calculator does not filter rows. */ - private final RexLocalRef condition; + private final @Nullable RexLocalRef condition; private final RelDataType inputRowType; @@ -91,7 +101,7 @@ public class RexProgram { /** * Reference counts for each expression, computed on demand. */ - private int[] refCounts; + private int @MonotonicNonNull[] refCounts; //~ Constructors ----------------------------------------------------------- @@ -112,7 +122,7 @@ public RexProgram( RelDataType inputRowType, List exprs, List projects, - RexLocalRef condition, + @Nullable RexLocalRef condition, RelDataType outputRowType) { this.inputRowType = inputRowType; this.exprs = ImmutableList.copyOf(exprs); @@ -169,7 +179,8 @@ public List> getNamedProjects() { * Returns the field reference of this program's filter condition, or null * if there is no condition. */ - public RexLocalRef getCondition() { + @Pure + public @Nullable RexLocalRef getCondition() { return condition; } @@ -187,7 +198,7 @@ public RexLocalRef getCondition() { public static RexProgram create( RelDataType inputRowType, List projectExprs, - RexNode conditionExpr, + @Nullable RexNode conditionExpr, RelDataType outputRowType, RexBuilder rexBuilder) { return create(inputRowType, projectExprs, conditionExpr, @@ -208,8 +219,8 @@ public static RexProgram create( public static RexProgram create( RelDataType inputRowType, List projectExprs, - RexNode conditionExpr, - List fieldNames, + @Nullable RexNode conditionExpr, + @Nullable List fieldNames, RexBuilder rexBuilder) { if (fieldNames == null) { fieldNames = Collections.nCopies(projectExprs.size(), null); @@ -234,8 +245,10 @@ public static RexProgram create( * In this case, the input is mainly from the output json string of {@link RelJsonWriter} */ public static RexProgram create(RelInput input) { - final List exprs = input.getExpressionList("exprs"); - final List projectRexNodes = input.getExpressionList("projects"); + final List exprs = requireNonNull(input.getExpressionList("exprs"), "exprs"); + final List projectRexNodes = requireNonNull( + input.getExpressionList("projects"), + "projects"); final List projects = new ArrayList<>(projectRexNodes.size()); for (RexNode rexNode: projectRexNodes) { projects.add((RexLocalRef) rexNode); @@ -430,7 +443,9 @@ public RelDataType getOutputRowType() { * or null if not known * @return Whether the program is valid */ - public boolean isValid(Litmus litmus, RelNode.Context context) { + public boolean isValid( + @UnknownInitialization RexProgram this, + Litmus litmus, RelNode.@Nullable Context context) { if (inputRowType == null) { return litmus.fail(null); } @@ -671,7 +686,7 @@ public int[] getReferenceCounts() { return refCounts; } refCounts = new int[exprs.size()]; - ReferenceCounter refCounter = new ReferenceCounter(); + ReferenceCounter refCounter = new ReferenceCounter(refCounts); RexUtil.apply(refCounter, exprs, null); if (condition != null) { refCounter.visitLocalRef(condition); @@ -689,7 +704,7 @@ public boolean isConstant(RexNode ref) { return ref.accept(new ConstantFinder()); } - public RexNode gatherExpr(RexNode expr) { + public @Nullable RexNode gatherExpr(RexNode expr) { return expr.accept(new Marshaller()); } @@ -737,7 +752,8 @@ public boolean isPermutation() { /** * Returns a permutation, if this program is a permutation, otherwise null. */ - public Permutation getPermutation() { + @CheckReturnValue + public @Nullable Permutation getPermutation() { Permutation permutation = new Permutation(projects.size()); if (projects.size() != inputRowType.getFieldList().size()) { return null; @@ -801,7 +817,7 @@ public boolean isNormalized(Litmus litmus, RexBuilder rexBuilder) { * or null to not simplify * @return Normalized program */ - public RexProgram normalize(RexBuilder rexBuilder, RexSimplify simplify) { + public RexProgram normalize(RexBuilder rexBuilder, @Nullable RexSimplify simplify) { // Normalize program by creating program builder from the program, then // converting to a program. getProgram does not need to normalize // because the builder was normalized on creation. @@ -863,7 +879,7 @@ static class Checker extends RexChecker { * @param litmus Whether to fail */ Checker(RelDataType inputRowType, - List internalExprTypeList, RelNode.Context context, + List internalExprTypeList, RelNode.@Nullable Context context, Litmus litmus) { super(inputRowType, context, litmus); this.internalExprTypeList = internalExprTypeList; @@ -930,7 +946,7 @@ private class ConstantFinder extends RexUtil.ConstantFinder { * Given an expression in a program, creates a clone of the expression with * sub-expressions (represented by {@link RexLocalRef}s) fully expanded. */ - private class Marshaller extends RexVisitorImpl { + private class Marshaller extends RexVisitorImpl<@Nullable RexNode> { Marshaller() { super(false); } @@ -939,7 +955,7 @@ private class Marshaller extends RexVisitorImpl { return inputRef; } - @Override public RexNode visitLocalRef(RexLocalRef localRef) { + @Override public @Nullable RexNode visitLocalRef(RexLocalRef localRef) { final RexNode expr = exprs.get(localRef.index); return expr.accept(this); } @@ -951,7 +967,7 @@ private class Marshaller extends RexVisitorImpl { @Override public RexNode visitCall(RexCall call) { final List newOperands = new ArrayList<>(); for (RexNode operand : call.getOperands()) { - newOperands.add(operand.accept(this)); + newOperands.add(castNonNull(operand.accept(this))); } return call.clone(call.getType(), newOperands); } @@ -976,7 +992,7 @@ private class Marshaller extends RexVisitorImpl { final RexNode referenceExpr = fieldAccess.getReferenceExpr().accept(this); return new RexFieldAccess( - referenceExpr, + requireNonNull(referenceExpr, "referenceExpr must not be null"), fieldAccess.getField()); } } @@ -984,9 +1000,12 @@ private class Marshaller extends RexVisitorImpl { /** * Visitor which marks which expressions are used. */ - private class ReferenceCounter extends RexVisitorImpl { - ReferenceCounter() { + private static class ReferenceCounter extends RexVisitorImpl { + private final int[] refCounts; + + ReferenceCounter(int[] refCounts) { super(true); + this.refCounts = refCounts; } @Override public Void visitLocalRef(RexLocalRef localRef) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java index 4f439481a78a..06aee7ce13c3 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java +++ b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java @@ -24,6 +24,8 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -47,10 +49,10 @@ public class RexProgramBuilder { new HashMap<>(); private final List localRefList = new ArrayList<>(); private final List projectRefList = new ArrayList<>(); - private final List projectNameList = new ArrayList<>(); + private final List<@Nullable String> projectNameList = new ArrayList<>(); @SuppressWarnings("unused") - private final RexSimplify simplify; - private RexLocalRef conditionRef = null; + private final @Nullable RexSimplify simplify; + private @Nullable RexLocalRef conditionRef = null; private boolean validating; //~ Constructors ----------------------------------------------------------- @@ -65,8 +67,9 @@ public RexProgramBuilder(RelDataType inputRowType, RexBuilder rexBuilder) { /** * Creates a program-builder. */ + @SuppressWarnings("method.invocation.invalid") private RexProgramBuilder(RelDataType inputRowType, RexBuilder rexBuilder, - RexSimplify simplify) { + @Nullable RexSimplify simplify) { this.inputRowType = Objects.requireNonNull(inputRowType); this.rexBuilder = Objects.requireNonNull(rexBuilder); this.simplify = simplify; // may be null @@ -93,15 +96,16 @@ private RexProgramBuilder(RelDataType inputRowType, RexBuilder rexBuilder, * @param normalize Whether to normalize * @param simplify Simplifier, or null to not simplify */ + @SuppressWarnings("method.invocation.invalid") private RexProgramBuilder( RexBuilder rexBuilder, final RelDataType inputRowType, final List exprList, final Iterable projectList, - RexNode condition, + @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize, - RexSimplify simplify) { + @Nullable RexSimplify simplify) { this(inputRowType, rexBuilder, simplify); // Create a shuttle for registering input expressions. @@ -203,7 +207,7 @@ private void validate(final RexNode expr, final int fieldOrdinal) { * be generated when the program is created * @return the ref created */ - public RexLocalRef addProject(RexNode expr, String name) { + public RexLocalRef addProject(RexNode expr, @Nullable String name) { final RexLocalRef ref = registerInput(expr); return addProject(ref.getIndex(), name); } @@ -216,7 +220,7 @@ public RexLocalRef addProject(RexNode expr, String name) { * will be generated when the program is created * @return the ref created */ - public RexLocalRef addProject(int ordinal, final String name) { + public RexLocalRef addProject(int ordinal, final @Nullable String name) { final RexLocalRef ref = localRefList.get(ordinal); projectRefList.add(ref); projectNameList.add(name); @@ -267,14 +271,15 @@ public RexLocalRef addProject(int at, int ordinal, final String name) { */ public void addCondition(RexNode expr) { assert expr != null; + RexLocalRef conditionRef = this.conditionRef; if (conditionRef == null) { - conditionRef = registerInput(expr); + this.conditionRef = conditionRef = registerInput(expr); } else { // AND the new condition with the existing condition. // If the new condition is identical to the existing condition, skip it. RexLocalRef ref = registerInput(expr); if (!ref.equals(conditionRef)) { - conditionRef = + this.conditionRef = registerInput( rexBuilder.makeCall( SqlStdOperatorTable.AND, @@ -528,10 +533,10 @@ public static RexProgramBuilder create( final RelDataType inputRowType, final List exprList, final List projectList, - final RexNode condition, + final @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize, - RexSimplify simplify) { + @Nullable RexSimplify simplify) { return new RexProgramBuilder(rexBuilder, inputRowType, exprList, projectList, condition, outputRowType, normalize, simplify); } @@ -542,7 +547,7 @@ public static RexProgramBuilder create( final RelDataType inputRowType, final List exprList, final List projectList, - final RexNode condition, + final @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize, boolean simplify_) { @@ -561,7 +566,7 @@ public static RexProgramBuilder create( final RelDataType inputRowType, final List exprList, final List projectList, - final RexNode condition, + final @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize) { return create(rexBuilder, inputRowType, exprList, projectList, condition, @@ -630,7 +635,7 @@ public static RexProgram normalize( private void add( List exprList, List projectRefList, - RexLocalRef conditionRef, + @Nullable RexLocalRef conditionRef, final RelDataType outputRowType, RexShuttle shuttle, boolean updateRefs) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java b/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java index c64751a90aad..1cdb19e41f21 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java +++ b/core/src/main/java/org/apache/calcite/rex/RexRangeRef.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.type.RelDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -77,7 +79,7 @@ public int getOffset() { return visitor.visitRangeRef(this, arg); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RexRangeRef && type.equals(((RexRangeRef) obj).type) diff --git a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java index 8936644d7139..ea05ce47c7c8 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java +++ b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java @@ -18,6 +18,9 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.ArrayList; import java.util.List; @@ -129,7 +132,7 @@ public RexWindow visitWindow(RexWindow window) { * was modified * @return Array of visited expressions */ - protected RexNode[] visitArray(RexNode[] exprs, boolean[] update) { + protected RexNode[] visitArray(RexNode[] exprs, boolean @Nullable [] update) { RexNode[] clonedOperands = new RexNode[exprs.length]; for (int i = 0; i < exprs.length; i++) { RexNode operand = exprs[i]; @@ -152,7 +155,7 @@ protected RexNode[] visitArray(RexNode[] exprs, boolean[] update) { * @return Array of visited expressions */ protected List visitList( - List exprs, boolean[] update) { + List exprs, boolean @Nullable [] update) { ImmutableList.Builder clonedOperands = ImmutableList.builder(); for (RexNode operand : exprs) { RexNode clonedOperand = operand.accept(this); @@ -174,7 +177,7 @@ protected List visitList( * @return Array of visited field collations */ protected List visitFieldCollations( - List collations, boolean[] update) { + List collations, boolean @Nullable [] update) { ImmutableList.Builder clonedOperands = ImmutableList.builder(); for (RexFieldCollation collation : collations) { @@ -231,7 +234,7 @@ protected List visitFieldCollations( * * @return whether any of the expressions changed */ - public final boolean mutate(List exprList) { + public final boolean mutate(List exprList) { int changeCount = 0; for (int i = 0; i < exprList.size(); i++) { T expr = exprList.get(i); @@ -248,9 +251,9 @@ public final boolean mutate(List exprList) { * Applies this shuttle to each expression in a list and returns the * resulting list. Does not modify the initial list. */ - public final List apply(List exprList) { + public final @PolyNull List apply(@PolyNull List exprList) { if (exprList == null) { - return null; + return exprList; } final List list2 = new ArrayList<>(exprList); if (mutate(list2)) { @@ -264,7 +267,7 @@ public final List apply(List exprList) { * Applies this shuttle to an expression, or returns null if the expression * is null. */ - public final RexNode apply(RexNode expr) { - return (expr == null) ? null : expr.accept(this); + public final @PolyNull RexNode apply(@PolyNull RexNode expr) { + return (expr == null) ? expr : expr.accept(this); } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java index 9e2ab58a49a1..99f38c8f5fb6 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java @@ -47,6 +47,8 @@ import com.google.common.collect.Sets; import com.google.common.collect.TreeRangeSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; @@ -57,14 +59,15 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.rex.RexUnknownAs.FALSE; import static org.apache.calcite.rex.RexUnknownAs.TRUE; import static org.apache.calcite.rex.RexUnknownAs.UNKNOWN; +import static java.util.Objects.requireNonNull; + /** * Context required to simplify a row-expression. */ @@ -95,12 +98,12 @@ public RexSimplify(RexBuilder rexBuilder, RelOptPredicateList predicates, private RexSimplify(RexBuilder rexBuilder, RelOptPredicateList predicates, RexUnknownAs defaultUnknownAs, boolean predicateElimination, boolean paranoid, RexExecutor executor) { - this.rexBuilder = Objects.requireNonNull(rexBuilder); - this.predicates = Objects.requireNonNull(predicates); - this.defaultUnknownAs = Objects.requireNonNull(defaultUnknownAs); + this.rexBuilder = requireNonNull(rexBuilder); + this.predicates = requireNonNull(predicates); + this.defaultUnknownAs = requireNonNull(defaultUnknownAs); this.predicateElimination = predicateElimination; this.paranoid = paranoid; - this.executor = Objects.requireNonNull(executor); + this.executor = requireNonNull(executor); this.strong = new Strong(); } @@ -332,7 +335,7 @@ private RexNode simplifyGenericNode(RexCall e) { private RexNode simplifyLike(RexCall e) { if (e.operands.get(1) instanceof RexLiteral) { final RexLiteral literal = (RexLiteral) e.operands.get(1); - if (literal.getValueAs(String.class).equals("%")) { + if ("%".equals(literal.getValueAs(String.class))) { return rexBuilder.makeLiteral(true); } } @@ -666,14 +669,14 @@ private RexNode simplifyUnaryPlus(RexCall call, RexUnknownAs unknownAs) { return simplify(call.getOperands().get(0), unknownAs); } - private @Nonnull RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) { + private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) { final SqlKind kind = call.getKind(); final RexNode a = call.getOperands().get(0); final RexNode simplified = simplifyIs1(kind, a, unknownAs); return simplified == null ? call : simplified; } - private RexNode simplifyIs1(SqlKind kind, RexNode a, RexUnknownAs unknownAs) { + private @Nullable RexNode simplifyIs1(SqlKind kind, RexNode a, RexUnknownAs unknownAs) { // UnknownAs.FALSE corresponds to x IS TRUE evaluation // UnknownAs.TRUE to x IS NOT FALSE // Note that both UnknownAs.TRUE and UnknownAs.FALSE only changes the meaning of Unknown @@ -701,7 +704,7 @@ private RexNode simplifyIs1(SqlKind kind, RexNode a, RexUnknownAs unknownAs) { return simplifyIs2(kind, a, unknownAs); } - private RexNode simplifyIsPredicate(SqlKind kind, RexNode a) { + private @Nullable RexNode simplifyIsPredicate(SqlKind kind, RexNode a) { if (!RexUtil.isReferenceOrAccess(a, true)) { return null; } @@ -718,7 +721,7 @@ private RexNode simplifyIsPredicate(SqlKind kind, RexNode a) { return null; } - private RexNode simplifyIs2(SqlKind kind, RexNode a, RexUnknownAs unknownAs) { + private @Nullable RexNode simplifyIs2(SqlKind kind, RexNode a, RexUnknownAs unknownAs) { final RexNode simplified; switch (kind) { case IS_NULL: @@ -783,7 +786,7 @@ private RexNode simplifyIs2(SqlKind kind, RexNode a, RexUnknownAs unknownAs) { return null; // cannot be simplified } - private RexNode simplifyIsNotNull(RexNode a) { + private @Nullable RexNode simplifyIsNotNull(RexNode a) { // Simplify the argument first, // call ourselves recursively to see whether we can make more progress. // For example, given @@ -832,7 +835,7 @@ private RexNode simplifyIsNotNull(RexNode a) { } } - private RexNode simplifyIsNull(RexNode a) { + private @Nullable RexNode simplifyIsNull(RexNode a) { // Simplify the argument first, // call ourselves recursively to see whether we can make more progress. // For example, given @@ -1223,7 +1226,7 @@ static boolean isSafeExpression(RexNode r) { return r.accept(SafeRexVisitor.INSTANCE); } - private static RexNode simplifyBooleanCase(RexBuilder rexBuilder, + private static @Nullable RexNode simplifyBooleanCase(RexBuilder rexBuilder, List inputBranches, @SuppressWarnings("unused") RexUnknownAs unknownAs, RelDataType branchType) { RexNode result; @@ -1535,6 +1538,9 @@ private > RexNode simplifyAnd2ForUnknownAsFalse( if (comparison != null && comparison.kind != SqlKind.NOT_EQUALS) { // not supported yet final C constant = comparison.literal.getValueAs(clazz); + if (constant == null) { + break; + } final RexNode result = processRange(rexBuilder, terms, rangeTerms, term, comparison.ref, constant, comparison.kind); if (result != null) { @@ -1645,6 +1651,9 @@ private > RexNode simplifyUsingPredicates(RexNode e, } final C v0 = comparison.literal.getValueAs(clazz); + if (v0 == null) { + return e; + } final RangeSet rangeSet = rangeSet(comparison.kind, v0); final RangeSet rangeSet2 = residue(comparison.ref, rangeSet, predicates.pulledUpPredicates, @@ -1707,6 +1716,7 @@ private > RangeSet residue(RexNode ref, && call.operands.get(1) instanceof RexLiteral) { final RexLiteral literal = (RexLiteral) call.operands.get(1); final C c1 = literal.getValueAs(clazz); + assert c1 != null : "value must not be null in " + literal; switch (predicate.getKind()) { case NOT_EQUALS: // We want to intersect result with the range set of everything but @@ -1832,9 +1842,12 @@ private RexNode simplifyOrs(List terms, RexUnknownAs unknownAs) { // - if it is not an IS_NOT_NULL (i.e. it is a NOT_EQUALS): check comparison values if (prevNotEquals.getKind() != SqlKind.IS_NOT_NULL) { final Comparable comparable1 = notEqualsComparison.literal.getValue(); - final Comparable comparable2 = Comparison.of(prevNotEquals).literal.getValue(); + final Comparable comparable2 = + castNonNull(Comparison.of(prevNotEquals)).literal.getValue(); //noinspection unchecked - if (comparable1.compareTo(comparable2) != 0) { + if (comparable1 != null + && comparable2 != null + && comparable1.compareTo(comparable2) != 0) { // X <> A OR X <> B => X IS NOT NULL OR NULL final RexNode isNotNull = rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, notEqualsComparison.ref); @@ -1890,7 +1903,7 @@ private void verify(RexNode before, RexNode simplified, RexUnknownAs unknownAs) for (Map map : foo0.assignments()) { for (RexNode predicate : predicates.pulledUpPredicates) { final Comparable v = RexInterpreter.evaluate(predicate, map); - if (!v.equals(true)) { + if (!Boolean.TRUE.equals(v)) { continue assignment_loop; } } @@ -1930,7 +1943,8 @@ private RexNode simplifySearch(RexCall call, RexUnknownAs unknownAs) { final RexNode a = call.getOperands().get(0); if (call.getOperands().get(1) instanceof RexLiteral) { RexLiteral literal = (RexLiteral) call.getOperands().get(1); - final Sarg sarg = literal.getValueAs(Sarg.class); + final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class)); + // Remove null from sarg if the left-hand side is never null if (sarg.containsNull) { final RexNode simplified = simplifyIs1(SqlKind.IS_NULL, a, unknownAs); if (simplified != null @@ -2032,7 +2046,7 @@ && sameTypeOrNarrowsNullability(e.getType(), intExpr.getType())) { final List reducedValues = new ArrayList<>(); final RexNode simplifiedExpr = rexBuilder.makeCast(e.getType(), operand); executor.reduce(rexBuilder, ImmutableList.of(simplifiedExpr), reducedValues); - return Objects.requireNonNull( + return requireNonNull( Iterables.getOnlyElement(reducedValues)); default: if (operand == e.getOperands().get(0)) { @@ -2145,7 +2159,7 @@ public RexNode removeNullabilityCast(RexNode e) { return RexUtil.removeNullabilityCast(rexBuilder.getTypeFactory(), e); } - private static > RexNode processRange( + private static > @Nullable RexNode processRange( RexBuilder rexBuilder, List terms, Map, List>> rangeTerms, RexNode term, RexNode ref, C v0, SqlKind comparison) { @@ -2386,7 +2400,7 @@ private static > RangeSet rangeSet(SqlKind comparison /** Marker interface for predicates (expressions that evaluate to BOOLEAN). */ private interface Predicate { /** Wraps an expression in a Predicate or returns null. */ - static Predicate of(RexNode t) { + static @Nullable Predicate of(RexNode t) { final Predicate p = Comparison.of(t); if (p != null) { return p; @@ -2411,20 +2425,20 @@ private static class Comparison implements Predicate { final RexLiteral literal; private Comparison(RexNode ref, SqlKind kind, RexLiteral literal) { - this.ref = Objects.requireNonNull(ref); - this.kind = Objects.requireNonNull(kind); - this.literal = Objects.requireNonNull(literal); + this.ref = requireNonNull(ref); + this.kind = requireNonNull(kind); + this.literal = requireNonNull(literal); } /** Creates a comparison, between a {@link RexInputRef} or {@link RexFieldAccess} or * deterministic {@link RexCall} and a literal. */ - static Comparison of(RexNode e) { + static @Nullable Comparison of(RexNode e) { return of(e, node -> RexUtil.isReferenceOrAccess(node, true) || RexUtil.isDeterministic(node)); } /** Creates a comparison, or returns null. */ - static Comparison of(RexNode e, java.util.function.Predicate nodePredicate) { + static @Nullable Comparison of(RexNode e, java.util.function.Predicate nodePredicate) { switch (e.getKind()) { case EQUALS: case NOT_EQUALS: @@ -2472,12 +2486,12 @@ private static class IsPredicate implements Predicate { final SqlKind kind; private IsPredicate(RexNode ref, SqlKind kind) { - this.ref = Objects.requireNonNull(ref); - this.kind = Objects.requireNonNull(kind); + this.ref = requireNonNull(ref); + this.kind = requireNonNull(kind); } /** Creates an IS predicate, or returns null. */ - static IsPredicate of(RexNode e) { + static @Nullable IsPredicate of(RexNode e) { switch (e.getKind()) { case IS_NULL: case IS_NOT_NULL: @@ -2541,7 +2555,7 @@ private static boolean isLowerBound(final RexNode e) { * @param predicates Filter condition predicates * @return simplified conjunction of predicates for the filter, null if always false */ - public RexNode simplifyFilterPredicates(Iterable predicates) { + public @Nullable RexNode simplifyFilterPredicates(Iterable predicates) { final RexNode simplifiedAnds = withPredicateElimination(Bug.CALCITE_2401_FIXED) .simplifyUnknownAsFalse( @@ -2565,6 +2579,7 @@ public RexNode simplifyFilterPredicates(Iterable predicates) *

Returns whether the value was found. */ private static boolean replaceLast(List list, E oldVal, E newVal) { + @SuppressWarnings("argument.type.incompatible") final int index = list.lastIndexOf(oldVal); if (index < 0) { return false; @@ -2646,14 +2661,39 @@ private static E addFluent(List list, E e) { // always returns true private boolean accept1(RexNode e, SqlKind kind, - @Nonnull RexLiteral literal, List newTerms) { + @Nullable RexLiteral literal, List newTerms) { final RexSargBuilder b = map.computeIfAbsent(e, e2 -> addFluent(newTerms, new RexSargBuilder(e2, rexBuilder, negate))); if (negate) { kind = kind.negateNullSafe(); } + + if (literal == null) { + switch (kind) { + case IS_NULL: + case IS_NOT_NULL: + if (negate ^ kind == SqlKind.IS_NULL) { + ++b.nullTermCount; + } else { + ++b.notNullTermCount; + } + return true; + default: + throw new AssertionError("unexpected " + kind); + } + } + + if (kind == SqlKind.SEARCH) { + final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class)); + b.addSarg(sarg, negate, literal.getType()); + return true; + } + final Comparable value = literal.getValueAs(Comparable.class); + final SqlKind actualKind = kind; + requireNonNull(value, () -> "value must not be null, kind: " + actualKind); + switch (kind) { case LESS_THAN: b.addRange(Range.lessThan(value), literal.getType()); @@ -2674,24 +2714,6 @@ private boolean accept1(RexNode e, SqlKind kind, b.addRange(Range.lessThan(value), literal.getType()); b.addRange(Range.greaterThan(value), literal.getType()); return true; - case IS_NULL: - if (negate) { - ++b.notNullTermCount; - } else { - ++b.nullTermCount; - } - return true; - case IS_NOT_NULL: - if (negate) { - ++b.nullTermCount; - } else { - ++b.notNullTermCount; - } - return true; - case SEARCH: - final Sarg sarg = literal.getValueAs(Sarg.class); - b.addSarg(sarg, negate, literal.getType()); - return true; default: throw new AssertionError("unexpected " + kind); } @@ -2727,8 +2749,8 @@ private static class RexSargBuilder extends RexNode { int nullTermCount; RexSargBuilder(RexNode ref, RexBuilder rexBuilder, boolean negate) { - this.ref = Objects.requireNonNull(ref); - this.rexBuilder = Objects.requireNonNull(rexBuilder); + this.ref = requireNonNull(ref); + this.rexBuilder = requireNonNull(rexBuilder); this.negate = negate; } @@ -2769,7 +2791,7 @@ > Sarg build(boolean negate) { return ref.getType(); } final List distinctTypes = Util.distinctList(this.types); - return Objects.requireNonNull( + return requireNonNull( rexBuilder.typeFactory.leastRestrictive(distinctTypes), () -> "Can't find leastRestrictive type among " + distinctTypes); } @@ -2782,7 +2804,7 @@ > Sarg build(boolean negate) { throw new UnsupportedOperationException(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { throw new UnsupportedOperationException(); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexSqlConvertlet.java b/core/src/main/java/org/apache/calcite/rex/RexSqlConvertlet.java index 663c4ba298dd..8db99b3a2cc1 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSqlConvertlet.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSqlConvertlet.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Converts a {@link RexNode} expression into a {@link SqlNode} expression. */ @@ -31,7 +33,7 @@ public interface RexSqlConvertlet { * @param call RexCall to translate * @return SqlNode, or null if translation was unavailable */ - SqlNode convertCall( + @Nullable SqlNode convertCall( RexToSqlNodeConverter converter, RexCall call); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexSqlConvertletTable.java b/core/src/main/java/org/apache/calcite/rex/RexSqlConvertletTable.java index 8406ec0d3873..05ed1c6c567a 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSqlConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSqlConvertletTable.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.rex; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Collection of {@link RexSqlConvertlet}s. */ @@ -25,5 +27,5 @@ public interface RexSqlConvertletTable { /** * Returns the convertlet applicable to a given expression. */ - RexSqlConvertlet get(RexCall call); + @Nullable RexSqlConvertlet get(RexCall call); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexSqlReflectiveConvertletTable.java b/core/src/main/java/org/apache/calcite/rex/RexSqlReflectiveConvertletTable.java index 1b2fdc00a803..fdaf3eb83dce 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSqlReflectiveConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSqlReflectiveConvertletTable.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.SqlOperator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashMap; import java.util.Map; @@ -36,7 +38,7 @@ public RexSqlReflectiveConvertletTable() { //~ Methods ---------------------------------------------------------------- - @Override public RexSqlConvertlet get(RexCall call) { + @Override public @Nullable RexSqlConvertlet get(RexCall call) { RexSqlConvertlet convertlet; final SqlOperator op = call.getOperator(); @@ -49,7 +51,7 @@ public RexSqlReflectiveConvertletTable() { // Is there a convertlet for this class of operator // (e.g. SqlBinaryOperator)? - Class clazz = op.getClass(); + @Nullable Class clazz = op.getClass(); while (clazz != null) { convertlet = (RexSqlConvertlet) map.get(clazz); if (convertlet != null) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexSqlStandardConvertletTable.java b/core/src/main/java/org/apache/calcite/rex/RexSqlStandardConvertletTable.java index 952e1a76a4fa..fad0578367dc 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSqlStandardConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSqlStandardConvertletTable.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -39,6 +41,7 @@ public class RexSqlStandardConvertletTable extends RexSqlReflectiveConvertletTable { //~ Constructors ----------------------------------------------------------- + @SuppressWarnings("method.invocation.invalid") public RexSqlStandardConvertletTable() { super(); @@ -140,7 +143,7 @@ public RexSqlStandardConvertletTable() { * @param call Call * @return Sql call */ - public SqlNode convertCall( + public @Nullable SqlNode convertCall( RexToSqlNodeConverter converter, RexCall call) { if (get(call) == null) { @@ -160,16 +163,17 @@ public SqlNode convertCall( SqlParserPos.ZERO); } - private SqlNode[] convertExpressionList( + private SqlNode @Nullable [] convertExpressionList( RexToSqlNodeConverter converter, List nodes) { final SqlNode[] exprs = new SqlNode[nodes.size()]; for (int i = 0; i < nodes.size(); i++) { RexNode node = nodes.get(i); - exprs[i] = converter.convertNode(node); - if (exprs[i] == null) { + SqlNode converted = converter.convertNode(node); + if (converted == null) { return null; } + exprs[i] = converted; } return exprs; } @@ -249,7 +253,7 @@ private class EquivConvertlet implements RexSqlConvertlet { this.op = op; } - @Override public SqlNode convertCall(RexToSqlNodeConverter converter, RexCall call) { + @Override public @Nullable SqlNode convertCall(RexToSqlNodeConverter converter, RexCall call) { SqlNode[] operands = convertExpressionList(converter, call.operands); if (operands == null) { return null; diff --git a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java index 378551bb5a28..3477064bbd03 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java @@ -29,9 +29,10 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; /** * Scalar expression that represents an IN, EXISTS or scalar sub-query. @@ -115,7 +116,7 @@ public static RexSubQuery scalar(RelNode rel) { return visitor.visitSubQuery(this, arg); } - @Override protected @Nonnull String computeDigest(boolean withType) { + @Override protected String computeDigest(boolean withType) { final StringBuilder sb = new StringBuilder(op.getName()); sb.append("("); for (RexNode operand : operands) { @@ -137,7 +138,7 @@ public RexSubQuery clone(RelNode rel) { return new RexSubQuery(type, getOperator(), operands, rel); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java b/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java index 6ebf65bb53d7..a27c73273cbe 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java +++ b/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java @@ -20,7 +20,10 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlKind; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import java.util.Objects; /** * Variable which references a column of a table occurrence in a relational plan. @@ -51,7 +54,7 @@ private RexTableInputRef(RelTableRef tableRef, int index, RelDataType type) { //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RexTableInputRef && tableRef.equals(((RexTableInputRef) obj).tableRef) @@ -59,7 +62,7 @@ private RexTableInputRef(RelTableRef tableRef, int index, RelDataType type) { } @Override public int hashCode() { - return digest.hashCode(); + return Objects.hashCode(digest); } public RelTableRef getTableRef() { @@ -110,7 +113,7 @@ private RelTableRef(RelOptTable table, int entityNumber) { //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof RelTableRef && table.getQualifiedName().equals(((RelTableRef) obj).getQualifiedName()) diff --git a/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverter.java b/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverter.java index f9273d365927..f5d6b3c078a1 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverter.java +++ b/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverter.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Converts expressions from {@link RexNode} to {@link SqlNode}. * @@ -37,7 +39,7 @@ public interface RexToSqlNodeConverter { * @param node RexNode to translate * @return SqlNode, or null if no translation was available */ - SqlNode convertNode(RexNode node); + @Nullable SqlNode convertNode(RexNode node); /** * Converts a {@link RexCall} to a {@link SqlNode} expression. @@ -45,7 +47,7 @@ public interface RexToSqlNodeConverter { * @param call RexCall to translate * @return SqlNode, or null if no translation was available */ - SqlNode convertCall(RexCall call); + @Nullable SqlNode convertCall(RexCall call); /** * Converts a {@link RexLiteral} to a {@link SqlLiteral}. @@ -53,7 +55,7 @@ public interface RexToSqlNodeConverter { * @param literal RexLiteral to translate * @return SqlNode, or null if no translation was available */ - SqlNode convertLiteral(RexLiteral literal); + @Nullable SqlNode convertLiteral(RexLiteral literal); /** * Converts a {@link RexInputRef} to a {@link SqlIdentifier}. @@ -61,5 +63,5 @@ public interface RexToSqlNodeConverter { * @param ref RexInputRef to translate * @return SqlNode, or null if no translation was available */ - SqlNode convertInputRef(RexInputRef ref); + @Nullable SqlNode convertInputRef(RexInputRef ref); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java b/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java index 14142b9d3782..9776d4f9aade 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java +++ b/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java @@ -25,6 +25,8 @@ import org.apache.calcite.util.TimeString; import org.apache.calcite.util.TimestampString; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Standard implementation of {@link RexToSqlNodeConverter}. */ @@ -41,8 +43,7 @@ public RexToSqlNodeConverterImpl(RexSqlConvertletTable convertletTable) { //~ Methods ---------------------------------------------------------------- - // implement RexToSqlNodeConverter - @Override public SqlNode convertNode(RexNode node) { + @Override public @Nullable SqlNode convertNode(RexNode node) { if (node instanceof RexLiteral) { return convertLiteral((RexLiteral) node); } else if (node instanceof RexInputRef) { @@ -54,7 +55,7 @@ public RexToSqlNodeConverterImpl(RexSqlConvertletTable convertletTable) { } // implement RexToSqlNodeConverter - @Override public SqlNode convertCall(RexCall call) { + @Override public @Nullable SqlNode convertCall(RexCall call) { final RexSqlConvertlet convertlet = convertletTable.get(call); if (convertlet != null) { return convertlet.convertCall(this, call); @@ -63,8 +64,7 @@ public RexToSqlNodeConverterImpl(RexSqlConvertletTable convertletTable) { return null; } - // implement RexToSqlNodeConverter - @Override public SqlNode convertLiteral(RexLiteral literal) { + @Override public @Nullable SqlNode convertLiteral(RexLiteral literal) { // Numeric if (SqlTypeFamily.EXACT_NUMERIC.getTypeNames().contains( literal.getTypeName())) { @@ -130,8 +130,7 @@ public RexToSqlNodeConverterImpl(RexSqlConvertletTable convertletTable) { return null; } - // implement RexToSqlNodeConverter - @Override public SqlNode convertInputRef(RexInputRef ref) { + @Override public @Nullable SqlNode convertInputRef(RexInputRef ref) { return null; } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexUnaryBiVisitor.java b/core/src/main/java/org/apache/calcite/rex/RexUnaryBiVisitor.java index 57007e4d4766..7f4ca0ea6623 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexUnaryBiVisitor.java +++ b/core/src/main/java/org/apache/calcite/rex/RexUnaryBiVisitor.java @@ -16,13 +16,15 @@ */ package org.apache.calcite.rex; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Default implementation of a {@link RexBiVisitor} whose payload and return * type are the same. * * @param Return type from each {@code visitXxx} method */ -public class RexUnaryBiVisitor extends RexBiVisitorImpl { +public class RexUnaryBiVisitor<@Nullable R> extends RexBiVisitorImpl { /** Creates a RexUnaryBiVisitor. */ protected RexUnaryBiVisitor(boolean deep) { super(deep); diff --git a/core/src/main/java/org/apache/calcite/rex/RexUnknownAs.java b/core/src/main/java/org/apache/calcite/rex/RexUnknownAs.java index 7ab948a258d7..4012dbfb00a2 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexUnknownAs.java +++ b/core/src/main/java/org/apache/calcite/rex/RexUnknownAs.java @@ -16,7 +16,6 @@ */ package org.apache.calcite.rex; -import javax.annotation.Nonnull; /** Policy for whether a simplified expression may instead return another * value. @@ -80,7 +79,7 @@ public enum RexUnknownAs { /** Returns {@link #FALSE} if {@code unknownAsFalse} is true, * {@link #UNKNOWN} otherwise. */ - public static @Nonnull RexUnknownAs falseIf(boolean unknownAsFalse) { + public static RexUnknownAs falseIf(boolean unknownAsFalse) { return unknownAsFalse ? FALSE : UNKNOWN; } diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java index bdda82bac9b7..3e4a991501ce 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java +++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java @@ -54,6 +54,7 @@ import com.google.common.collect.Range; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -66,9 +67,10 @@ import java.util.Objects; import java.util.Set; import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; /** * Utility methods concerning row-expressions. @@ -77,7 +79,7 @@ public class RexUtil { /** Executor for a bit of constant reduction. The user can pass in another executor. */ public static final RexExecutor EXECUTOR = - new RexExecutorImpl(Schemas.createDataContext(null, null)); + new RexExecutorImpl(Schemas.createDataContext(castNonNull(null), null)); private RexUtil() { } @@ -91,7 +93,7 @@ private RexUtil() { * selectivity of 1.0) * @return guessed selectivity */ - public static double getSelectivity(RexNode exp) { + public static double getSelectivity(@Nullable RexNode exp) { if ((exp == null) || exp.isAlwaysTrue()) { return 1d; } @@ -469,8 +471,11 @@ private static boolean canAssignFrom(RelDataType type1, RelDataType type2, RelDataTypeFactory typeFactory) { final SqlTypeName name1 = type1.getSqlTypeName(); final SqlTypeName name2 = type2.getSqlTypeName(); - if (name1.getFamily() == name2.getFamily()) { - switch (name1.getFamily()) { + final RelDataType type1Final = type1; + SqlTypeFamily family = requireNonNull(name1.getFamily(), + () -> "SqlTypeFamily is null for type " + type1Final + ", SqlTypeName " + name1); + if (family == name2.getFamily()) { + switch (family) { case NUMERIC: if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) { @@ -588,7 +593,7 @@ public static RexNode expandSearch(RexBuilder rexBuilder, final RexLiteral literal = (RexLiteral) deref(program, call.operands.get(1)); @SuppressWarnings("unchecked") - final Sarg sarg = literal.getValueAs(Sarg.class); + final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class)); final List orList = new ArrayList<>(); if (sarg.containsNull) { @@ -618,9 +623,10 @@ public static RexNode expandSearch(RexBuilder rexBuilder, return composeDisjunction(rexBuilder, orList); } - private static RexNode deref(RexProgram program, RexNode node) { + private static RexNode deref(@Nullable RexProgram program, RexNode node) { while (node instanceof RexLocalRef) { - node = program.getExprList().get(((RexLocalRef) node).index); + node = requireNonNull(program, "program") + .getExprList().get(((RexLocalRef) node).index); } return node; } @@ -740,7 +746,7 @@ public static List retainDeterministic(List list) { * @param operator Operator to look for * @param node a RexNode tree */ - public static RexCall findOperatorCall( + public static @Nullable RexCall findOperatorCall( final SqlOperator operator, RexNode node) { try { @@ -1019,7 +1025,7 @@ public static boolean containsTableInputRef(List nodes) { * @param node a RexNode tree * @return first such node found or null if it there is no such node */ - public static RexTableInputRef containsTableInputRef(RexNode node) { + public static @Nullable RexTableInputRef containsTableInputRef(RexNode node) { try { RexVisitor visitor = new RexVisitorImpl(true) { @@ -1079,8 +1085,8 @@ public static RelDataType createStructType( public static RelDataType createStructType( RelDataTypeFactory typeFactory, final List exprs, - List names, - SqlValidatorUtil.Suggester suggester) { + @Nullable List names, + SqlValidatorUtil.@Nullable Suggester suggester) { if (names != null && suggester != null) { names = SqlValidatorUtil.uniquify(names, suggester, typeFactory.getTypeSystem().isSchemaCaseSensitive()); @@ -1185,10 +1191,10 @@ public static boolean isIdentity(List exps, /** As {@link #composeConjunction(RexBuilder, Iterable, boolean)} but never * returns null. */ - public static @Nonnull RexNode composeConjunction(RexBuilder rexBuilder, - Iterable nodes) { + public static RexNode composeConjunction(RexBuilder rexBuilder, + Iterable nodes) { final RexNode e = composeConjunction(rexBuilder, nodes, false); - return Objects.requireNonNull(e); + return requireNonNull(e); } /** @@ -1199,8 +1205,8 @@ public static boolean isIdentity(List exps, * Removes expressions that always evaluate to TRUE. * Returns null only if {@code nullOnEmpty} and expression is TRUE. */ - public static RexNode composeConjunction(RexBuilder rexBuilder, - Iterable nodes, boolean nullOnEmpty) { + public static @Nullable RexNode composeConjunction(RexBuilder rexBuilder, + Iterable nodes, boolean nullOnEmpty) { ImmutableList list = flattenAnd(nodes); switch (list.size()) { case 0: @@ -1221,7 +1227,7 @@ public static RexNode composeConjunction(RexBuilder rexBuilder, * *

Treats null nodes as literal TRUE (i.e. ignores them). */ public static ImmutableList flattenAnd( - Iterable nodes) { + Iterable nodes) { if (nodes instanceof Collection && ((Collection) nodes).isEmpty()) { // Optimize common case return ImmutableList.of(); @@ -1259,17 +1265,17 @@ private static void addAnd(ImmutableList.Builder builder, * Removes expressions that always evaluate to FALSE. * Flattens expressions that are ORs. */ - @Nonnull public static RexNode composeDisjunction(RexBuilder rexBuilder, + public static RexNode composeDisjunction(RexBuilder rexBuilder, Iterable nodes) { final RexNode e = composeDisjunction(rexBuilder, nodes, false); - return Objects.requireNonNull(e); + return requireNonNull(e); } /** * Converts a collection of expressions into an OR, * optionally returning null if the list is empty. */ - public static RexNode composeDisjunction(RexBuilder rexBuilder, + public static @Nullable RexNode composeDisjunction(RexBuilder rexBuilder, Iterable nodes, boolean nullOnEmpty) { ImmutableList list = flattenOr(nodes); switch (list.size()) { @@ -1386,7 +1392,7 @@ public static RelCollation apply( * @param fieldCollation Field collation * @return collation with mapping applied */ - public static RelFieldCollation apply( + public static @Nullable RelFieldCollation apply( Mappings.TargetMapping mapping, RelFieldCollation fieldCollation) { final int target = @@ -1463,7 +1469,7 @@ public static T[] apply( public static void apply( RexVisitor visitor, RexNode[] exprs, - RexNode expr) { + @Nullable RexNode expr) { for (RexNode e : exprs) { e.accept(visitor); } @@ -1483,7 +1489,7 @@ public static void apply( public static void apply( RexVisitor visitor, List exprs, - RexNode expr) { + @Nullable RexNode expr) { for (RexNode e : exprs) { e.accept(visitor); } @@ -2009,7 +2015,7 @@ public static RexNode simplifyAnd2ForUnknownAsFalse(RexBuilder rexBuilder, .simplifyAnd2ForUnknownAsFalse(terms, notTerms); } - public static RexNode negate(RexBuilder rexBuilder, RexCall call) { + public static @Nullable RexNode negate(RexBuilder rexBuilder, RexCall call) { switch (call.getKind()) { case EQUALS: case NOT_EQUALS: @@ -2025,7 +2031,7 @@ public static RexNode negate(RexBuilder rexBuilder, RexCall call) { return null; } - public static RexNode invert(RexBuilder rexBuilder, RexCall call) { + public static @Nullable RexNode invert(RexBuilder rexBuilder, RexCall call) { switch (call.getKind()) { case EQUALS: case NOT_EQUALS: @@ -2074,7 +2080,7 @@ public static RexNode andNot(RexBuilder rexBuilder, RexNode e, * returns "x = 10 AND NOT (y = 30)" * */ - public static @Nonnull RexNode andNot(final RexBuilder rexBuilder, RexNode e, + public static RexNode andNot(final RexBuilder rexBuilder, RexNode e, Iterable notTerms) { // If "e" is of the form "x = literal", remove all "x = otherLiteral" // terms from notTerms. @@ -2199,14 +2205,16 @@ public static RexNode swapColumnReferences(final RexBuilder rexBuilder, * in the second map (in particular, the first element of the set in the map value). */ public static RexNode swapTableColumnReferences(final RexBuilder rexBuilder, - final RexNode node, final Map tableMapping, - final Map> ec) { + final RexNode node, final @Nullable Map tableMapping, + final @Nullable Map> ec) { RexShuttle visitor = new RexShuttle() { @Override public RexNode visitTableInputRef(RexTableInputRef inputRef) { if (tableMapping != null) { + RexTableInputRef inputRefFinal = inputRef; inputRef = RexTableInputRef.of( - tableMapping.get(inputRef.getTableRef()), + requireNonNull(tableMapping.get(inputRef.getTableRef()), + () -> "tableMapping.get(...) for " + inputRefFinal.getTableRef()), inputRef.getIndex(), inputRef.getType()); } @@ -2229,8 +2237,8 @@ public static RexNode swapTableColumnReferences(final RexBuilder rexBuilder, * {@link RexTableInputRef} using the contents in the second map. */ public static RexNode swapColumnTableReferences(final RexBuilder rexBuilder, - final RexNode node, final Map> ec, - final Map tableMapping) { + final RexNode node, final Map> ec, + final @Nullable Map tableMapping) { RexShuttle visitor = new RexShuttle() { @Override public RexNode visitTableInputRef(RexTableInputRef inputRef) { @@ -2241,8 +2249,10 @@ public static RexNode swapColumnTableReferences(final RexBuilder rexBuilder, } } if (tableMapping != null) { + RexTableInputRef inputRefFinal = inputRef; inputRef = RexTableInputRef.of( - tableMapping.get(inputRef.getTableRef()), + requireNonNull(tableMapping.get(inputRef.getTableRef()), + () -> "tableMapping.get(...) for " + inputRefFinal.getTableRef()), inputRef.getIndex(), inputRef.getType()); } @@ -2274,7 +2284,7 @@ public static Set gatherTableReferences(final List nodes) /** * Walks over expressions and builds a bank of common sub-expressions. */ - private static class ExpressionNormalizer extends RexVisitorImpl { + private static class ExpressionNormalizer extends RexVisitorImpl<@Nullable RexNode> { final Map map = new HashMap<>(); final boolean allowDups; @@ -2292,7 +2302,9 @@ protected RexNode register(RexNode expr) { } protected RexNode lookup(RexNode expr) { - return map.get(expr); + return requireNonNull( + map.get(expr), + () -> "missing normalization for expression " + expr); } @Override public RexNode visitInputRef(RexInputRef inputRef) { @@ -2787,7 +2799,7 @@ public static boolean containsSubQuery(Join join) { throw new Util.FoundOne(subQuery); } - public static RexSubQuery find(Iterable nodes) { + public static @Nullable RexSubQuery find(Iterable nodes) { for (RexNode node : nodes) { try { node.accept(INSTANCE); @@ -2798,7 +2810,7 @@ public static RexSubQuery find(Iterable nodes) { return null; } - public static RexSubQuery find(RexNode node) { + public static @Nullable RexSubQuery find(RexNode node) { try { node.accept(INSTANCE); return null; @@ -2928,10 +2940,10 @@ private static class RangeToRex> RangeToRex(RexNode ref, List list, RexBuilder rexBuilder, RelDataType type) { - this.ref = Objects.requireNonNull(ref); - this.list = Objects.requireNonNull(list); - this.rexBuilder = Objects.requireNonNull(rexBuilder); - this.type = Objects.requireNonNull(type); + this.ref = requireNonNull(ref); + this.list = requireNonNull(list); + this.rexBuilder = requireNonNull(rexBuilder); + this.type = requireNonNull(type); } private void addAnd(RexNode... nodes) { diff --git a/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java b/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java index 8a07b37668c2..a0a45f5229cf 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java +++ b/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.rex; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -24,7 +26,7 @@ * * @param Return type from each {@code visitXxx} method. */ -public class RexVisitorImpl implements RexVisitor { +public class RexVisitorImpl<@Nullable R> implements RexVisitor { //~ Instance fields -------------------------------------------------------- protected final boolean deep; diff --git a/core/src/main/java/org/apache/calcite/rex/RexWindow.java b/core/src/main/java/org/apache/calcite/rex/RexWindow.java index ca0b03586768..1b71ecbac318 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexWindow.java +++ b/core/src/main/java/org/apache/calcite/rex/RexWindow.java @@ -21,6 +21,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -67,6 +69,7 @@ public class RexWindow { * "ROWS BETWEEN 5 PRECEDING AND CURRENT ROW" is printed as * "ROWS 5 PRECEDING". */ + @SuppressWarnings("method.invocation.invalid") RexWindow( List partitionKeys, List orderKeys, @@ -96,7 +99,7 @@ public class RexWindow { return digest.hashCode(); } - @Override public boolean equals(Object that) { + @Override public boolean equals(@Nullable Object that) { if (that instanceof RexWindow) { RexWindow window = (RexWindow) that; return digest.equals(window.digest); diff --git a/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java b/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java index 7f94b11a20ef..7ad7247a3f05 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java +++ b/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java @@ -18,6 +18,10 @@ import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + /** * Abstracts "XX PRECEDING/FOLLOWING" and "CURRENT ROW" bounds for windowed * aggregates. @@ -36,6 +40,9 @@ public static RexWindowBound create(SqlNode node, RexNode rexNode) { * Returns if the bound is unbounded. * @return if the bound is unbounded */ + @Pure + @EnsuresNonNullIf(expression = "getOffset()", result = false) + @SuppressWarnings("contracts.conditional.postcondition.not.satisfied") public boolean isUnbounded() { return false; } @@ -60,6 +67,9 @@ public boolean isFollowing() { * Returns if the bound is CURRENT ROW. * @return if the bound is CURRENT ROW */ + @Pure + @EnsuresNonNullIf(expression = "getOffset()", result = false) + @SuppressWarnings("contracts.conditional.postcondition.not.satisfied") public boolean isCurrentRow() { return false; } @@ -69,7 +79,8 @@ public boolean isCurrentRow() { * * @return offset from XX PRECEDING/FOLLOWING */ - public RexNode getOffset() { + @Pure + public @Nullable RexNode getOffset() { return null; } diff --git a/core/src/main/java/org/apache/calcite/rex/RexWindowBounds.java b/core/src/main/java/org/apache/calcite/rex/RexWindowBounds.java index 3f5e6fa3e3ac..c7af903717f8 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexWindowBounds.java +++ b/core/src/main/java/org/apache/calcite/rex/RexWindowBounds.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -50,7 +52,7 @@ private RexWindowBounds() { * @param rexNode offset value when bound is not UNBOUNDED/CURRENT ROW * @return window bound */ - public static RexWindowBound create(SqlNode node, RexNode rexNode) { + public static RexWindowBound create(SqlNode node, @Nullable RexNode rexNode) { if (SqlWindow.isUnboundedPreceding(node)) { return UNBOUNDED_PRECEDING; } @@ -60,6 +62,7 @@ public static RexWindowBound create(SqlNode node, RexNode rexNode) { if (SqlWindow.isCurrentRow(node)) { return CURRENT_ROW; } + assert rexNode != null : "offset value cannot be null for bounded window"; return new RexBoundedWindowBound((RexCall) rexNode); } @@ -105,7 +108,7 @@ private static class RexUnboundedWindowBound extends RexWindowBound { return preceding ? 0 : 2; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof RexUnboundedWindowBound && preceding == ((RexUnboundedWindowBound) o).preceding; @@ -132,7 +135,7 @@ private static class RexCurrentRowWindowBound extends RexWindowBound { return 1; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof RexCurrentRowWindowBound; } @@ -187,7 +190,7 @@ private RexBoundedWindowBound(SqlKind sqlKind, RexNode offset) { return offset + " " + sqlKind; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof RexBoundedWindowBound && offset.equals(((RexBoundedWindowBound) o).offset) diff --git a/core/src/main/java/org/apache/calcite/runtime/AbstractImmutableList.java b/core/src/main/java/org/apache/calcite/runtime/AbstractImmutableList.java index 422b04140977..54e2092d1575 100644 --- a/core/src/main/java/org/apache/calcite/runtime/AbstractImmutableList.java +++ b/core/src/main/java/org/apache/calcite/runtime/AbstractImmutableList.java @@ -16,11 +16,14 @@ */ package org.apache.calcite.runtime; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import javax.annotation.Nonnull; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; /** * Base class for lists whose contents are constant after creation. @@ -30,11 +33,11 @@ abstract class AbstractImmutableList implements List { protected abstract List toList(); - @Override @Nonnull public Iterator iterator() { + @Override public Iterator iterator() { return toList().iterator(); } - @Override @Nonnull public ListIterator listIterator() { + @Override public ListIterator listIterator() { return toList().listIterator(); } @@ -46,19 +49,19 @@ abstract class AbstractImmutableList implements List { throw new UnsupportedOperationException(); } - @Override public boolean addAll(@Nonnull Collection c) { + @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } - @Override public boolean addAll(int index, @Nonnull Collection c) { + @Override public boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } - @Override public boolean removeAll(@Nonnull Collection c) { + @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } - @Override public boolean retainAll(@Nonnull Collection c) { + @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } @@ -78,19 +81,19 @@ abstract class AbstractImmutableList implements List { throw new UnsupportedOperationException(); } - @Override @Nonnull public ListIterator listIterator(int index) { + @Override public ListIterator listIterator(int index) { return toList().listIterator(index); } - @Override @Nonnull public List subList(int fromIndex, int toIndex) { + @Override public List subList(int fromIndex, int toIndex) { return toList().subList(fromIndex, toIndex); } - @Override public boolean contains(Object o) { - return indexOf(o) >= 0; + @Override public boolean contains(@Nullable Object o) { + return indexOf(castNonNull(o)) >= 0; } - @Override public boolean containsAll(@Nonnull Collection c) { + @Override public boolean containsAll(Collection c) { for (Object o : c) { if (!contains(o)) { return false; @@ -99,7 +102,7 @@ abstract class AbstractImmutableList implements List { return true; } - @Override public boolean remove(Object o) { + @Override public boolean remove(@Nullable Object o) { throw new UnsupportedOperationException(); } } diff --git a/core/src/main/java/org/apache/calcite/runtime/ArrayBindable.java b/core/src/main/java/org/apache/calcite/runtime/ArrayBindable.java index a88bc861f481..52815518a27e 100644 --- a/core/src/main/java/org/apache/calcite/runtime/ArrayBindable.java +++ b/core/src/main/java/org/apache/calcite/runtime/ArrayBindable.java @@ -16,13 +16,15 @@ */ package org.apache.calcite.runtime; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Extension to {@link Bindable} that returns rows that are arrays of objects. * *

It also implements {@link Typed}; the {@link #getElementType()} method * must return {@code Object[].class}. */ -public interface ArrayBindable extends Bindable, Typed { +public interface ArrayBindable extends Bindable<@Nullable Object[]>, Typed { // override @Override Class getElementType(); } diff --git a/core/src/main/java/org/apache/calcite/runtime/ArrayEnumeratorCursor.java b/core/src/main/java/org/apache/calcite/runtime/ArrayEnumeratorCursor.java index 11a8bc1a3006..219df429ffcf 100644 --- a/core/src/main/java/org/apache/calcite/runtime/ArrayEnumeratorCursor.java +++ b/core/src/main/java/org/apache/calcite/runtime/ArrayEnumeratorCursor.java @@ -18,18 +18,20 @@ import org.apache.calcite.linq4j.Enumerator; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Implementation of {@link org.apache.calcite.avatica.util.Cursor} on top of an * {@link org.apache.calcite.linq4j.Enumerator} that * returns an array of {@link Object} for each row. */ -public class ArrayEnumeratorCursor extends EnumeratorCursor { +public class ArrayEnumeratorCursor extends EnumeratorCursor<@Nullable Object[]> { /** * Creates an ArrayEnumeratorCursor. * * @param enumerator Enumerator */ - public ArrayEnumeratorCursor(Enumerator enumerator) { + public ArrayEnumeratorCursor(Enumerator<@Nullable Object[]> enumerator) { super(enumerator); } diff --git a/core/src/main/java/org/apache/calcite/runtime/Automaton.java b/core/src/main/java/org/apache/calcite/runtime/Automaton.java index 0079da8f68fb..baa55496fcd9 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Automaton.java +++ b/core/src/main/java/org/apache/calcite/runtime/Automaton.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** A nondeterministic finite-state automaton (NFA). @@ -97,7 +99,7 @@ static class State { this.id = id; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof State && ((State) o).id == id; diff --git a/core/src/main/java/org/apache/calcite/runtime/AutomatonBuilder.java b/core/src/main/java/org/apache/calcite/runtime/AutomatonBuilder.java index 05484e3025cb..21942c262ce3 100644 --- a/core/src/main/java/org/apache/calcite/runtime/AutomatonBuilder.java +++ b/core/src/main/java/org/apache/calcite/runtime/AutomatonBuilder.java @@ -37,7 +37,9 @@ public class AutomatonBuilder { private final Map symbolIds = new HashMap<>(); private final List stateList = new ArrayList<>(); private final List transitionList = new ArrayList<>(); + @SuppressWarnings("method.invocation.invalid") private final State startState = createState(); + @SuppressWarnings("method.invocation.invalid") private final State endState = createState(); /** Adds a pattern as a start-to-end transition. */ diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteContextException.java b/core/src/main/java/org/apache/calcite/runtime/CalciteContextException.java index 0b269d19328b..eb0ee560ba2d 100644 --- a/core/src/main/java/org/apache/calcite/runtime/CalciteContextException.java +++ b/core/src/main/java/org/apache/calcite/runtime/CalciteContextException.java @@ -20,6 +20,9 @@ // resource generation can use reflection. That means it must have no // dependencies on other Calcite code. +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Exception which contains information about the textual context of the causing * exception. @@ -44,7 +47,7 @@ public class CalciteContextException extends CalciteException { private int endPosColumn; - private String originalStatement; + private @Nullable String originalStatement; //~ Constructors ----------------------------------------------------------- @@ -121,6 +124,7 @@ public void setPosition(int posLine, int posColumn) { * @param endPosColumn 1-based end column number */ public void setPosition( + @UnknownInitialization CalciteContextException this, int posLine, int posColumn, int endPosLine, @@ -164,20 +168,25 @@ public int getEndPosColumn() { /** * Returns the input string that is associated with the context. */ - public String getOriginalStatement() { + public @Nullable String getOriginalStatement() { return originalStatement; } /** * Sets the input string to associate with the current context. */ - public void setOriginalStatement(String originalStatement) { + public void setOriginalStatement(@Nullable String originalStatement) { this.originalStatement = originalStatement; } - @Override public String getMessage() { + @Override public @Nullable String getMessage() { // The superclass' message is the textual context information // for this exception, so we add in the underlying cause to the message - return super.getMessage() + ": " + getCause().getMessage(); + Throwable cause = getCause(); + if (cause == null) { + // It would be sad to get NPE from getMessage + return super.getMessage(); + } + return super.getMessage() + ": " + cause.getMessage(); } } diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteException.java b/core/src/main/java/org/apache/calcite/runtime/CalciteException.java index 440bca1182c8..73a55c5de6ab 100644 --- a/core/src/main/java/org/apache/calcite/runtime/CalciteException.java +++ b/core/src/main/java/org/apache/calcite/runtime/CalciteException.java @@ -52,6 +52,7 @@ public class CalciteException extends RuntimeException { * @param message error message * @param cause underlying cause */ + @SuppressWarnings({"argument.type.incompatible", "method.invocation.invalid"}) public CalciteException( String message, Throwable cause) { diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java index 8f4cc893a0f1..88797b80c067 100644 --- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java +++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.validate.SqlValidatorException; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.apache.calcite.runtime.Resources.BaseMessage; import static org.apache.calcite.runtime.Resources.ExInst; import static org.apache.calcite.runtime.Resources.ExInstWithCause; @@ -515,7 +517,7 @@ ExInst intervalFractionalSecondPrecisionOutOfRange( int a0, String a1); @BaseMessage("Duplicate relation name ''{0}'' in FROM clause") - ExInst fromAliasDuplicate(String a0); + ExInst fromAliasDuplicate(@Nullable String a0); @BaseMessage("Duplicate column name ''{0}'' in output") ExInst duplicateColumnName(String a0); @@ -927,10 +929,10 @@ ExInst invalidTypesForComparison(String clazzName0, String op, ExInst invalidInputForXmlTransform(String xml); @BaseMessage("Invalid input for EXTRACT xpath: ''{0}'', namespace: ''{1}''") - ExInst invalidInputForExtractXml(String xpath, String namespace); + ExInst invalidInputForExtractXml(String xpath, @Nullable String namespace); @BaseMessage("Invalid input for EXISTSNODE xpath: ''{0}'', namespace: ''{1}''") - ExInst invalidInputForExistsNode(String xpath, String namespace); + ExInst invalidInputForExistsNode(String xpath, @Nullable String namespace); @BaseMessage("Invalid input for EXTRACTVALUE: xml: ''{0}'', xpath expression: ''{1}''") ExInst invalidInputForExtractValue(String xml, String xpath); diff --git a/core/src/main/java/org/apache/calcite/runtime/CompressionFunctions.java b/core/src/main/java/org/apache/calcite/runtime/CompressionFunctions.java index d16c434edb79..d20b50a0905f 100644 --- a/core/src/main/java/org/apache/calcite/runtime/CompressionFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/CompressionFunctions.java @@ -20,6 +20,8 @@ import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -40,7 +42,7 @@ private CompressionFunctions() { * Deflater * is used to implement compression. */ - public static ByteString compress(String data) { + public static @Nullable ByteString compress(@Nullable String data) { try { if (data == null) { return null; diff --git a/core/src/main/java/org/apache/calcite/runtime/ConsList.java b/core/src/main/java/org/apache/calcite/runtime/ConsList.java index 83cc303d41c6..610dce860c47 100644 --- a/core/src/main/java/org/apache/calcite/runtime/ConsList.java +++ b/core/src/main/java/org/apache/calcite/runtime/ConsList.java @@ -16,14 +16,19 @@ */ package org.apache.calcite.runtime; + import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import javax.annotation.Nonnull; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; /** * List that consists of a head element and an immutable non-empty list. @@ -78,7 +83,7 @@ private ConsList(E first, List rest) { return toList().hashCode(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof List && toList().equals(o); @@ -99,28 +104,28 @@ private ConsList(E first, List rest) { } } - @Override @Nonnull public ListIterator listIterator() { + @Override public ListIterator listIterator() { return toList().listIterator(); } - @Override @Nonnull public Iterator iterator() { + @Override public Iterator iterator() { return toList().iterator(); } - @Override @Nonnull public ListIterator listIterator(int index) { + @Override public ListIterator listIterator(int index) { return toList().listIterator(index); } - @Override @Nonnull public Object[] toArray() { + @Override public @PolyNull Object[] toArray(ConsList<@PolyNull E> this) { return toList().toArray(); } - @Override @Nonnull public T[] toArray(@Nonnull T[] a) { + @Override public @Nullable T[] toArray(T @Nullable [] a) { final int s = size(); - if (s > a.length) { - a = Arrays.copyOf(a, s); + if (s > castNonNull(a).length) { + a = (T[]) Arrays.copyOf(a, s, a.getClass()); } else if (s < a.length) { - a[s] = null; + a[s] = castNonNull(null); } int i = 0; for (ConsList c = this;; c = (ConsList) c.rest) { @@ -135,11 +140,11 @@ private ConsList(E first, List rest) { } } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { return toList().indexOf(o); } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { return toList().lastIndexOf(o); } } diff --git a/core/src/main/java/org/apache/calcite/runtime/DeterministicAutomaton.java b/core/src/main/java/org/apache/calcite/runtime/DeterministicAutomaton.java index 3602c0b43283..3969e21ab4d9 100644 --- a/core/src/main/java/org/apache/calcite/runtime/DeterministicAutomaton.java +++ b/core/src/main/java/org/apache/calcite/runtime/DeterministicAutomaton.java @@ -19,6 +19,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashSet; import java.util.Objects; import java.util.Optional; @@ -38,6 +40,7 @@ public class DeterministicAutomaton { private final ImmutableList transitions; /** Constructs the DFA from an epsilon-NFA. */ + @SuppressWarnings("method.invocation.invalid") DeterministicAutomaton(Automaton automaton) { this.automaton = Objects.requireNonNull(automaton); // Calculate eps closure of start state @@ -169,7 +172,7 @@ public boolean contains(Automaton.State state) { return states.contains(state); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof MultiState && Objects.equals(states, ((MultiState) o).states); diff --git a/core/src/main/java/org/apache/calcite/runtime/Enumerables.java b/core/src/main/java/org/apache/calcite/runtime/Enumerables.java index ce45f39eccf5..9682a9b5d09b 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Enumerables.java +++ b/core/src/main/java/org/apache/calcite/runtime/Enumerables.java @@ -22,6 +22,8 @@ import org.apache.calcite.linq4j.Enumerator; import org.apache.calcite.linq4j.function.Function1; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; @@ -52,21 +54,21 @@ public static Enumerable slice0(Enumerable enumerable) { /** Converts an {@link Enumerable} over object arrays into an * {@link Enumerable} over {@link Row} objects. */ - public static Enumerable toRow(final Enumerable enumerable) { - return enumerable.select((Function1) Row::asCopy); + public static Enumerable toRow(final Enumerable<@Nullable Object[]> enumerable) { + return enumerable.select((Function1<@Nullable Object[], Row>) Row::asCopy); } /** Converts a supplier of an {@link Enumerable} over object arrays into a * supplier of an {@link Enumerable} over {@link Row} objects. */ public static Supplier> toRow( - final Supplier> supplier) { + final Supplier> supplier) { return () -> toRow(supplier.get()); } @SuppressWarnings("Guava") @Deprecated // to be removed before 2.0 public static com.google.common.base.Supplier> toRow( - final com.google.common.base.Supplier> supplier) { + final com.google.common.base.Supplier> supplier) { return () -> toRow(supplier.get()); } @@ -89,7 +91,7 @@ public static Enumerable match( final Deque emitRows = new ArrayDeque<>(); /** Current result row. Null if no row is ready. */ - TResult resultRow; + @Nullable TResult resultRow; /** Match counter is 1-based in Oracle. */ final AtomicInteger matchCounter = new AtomicInteger(1); @@ -188,7 +190,7 @@ public static Enumerable match( * @param element type * @param result type */ public interface Emitter { - void emit(List rows, List rowStates, List rowSymbols, int match, + void emit(List rows, @Nullable List rowStates, List rowSymbols, int match, Consumer consumer); } } diff --git a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java index 2d61ae0edb04..0518178cab28 100644 --- a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java +++ b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java @@ -21,6 +21,9 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; @@ -31,6 +34,8 @@ import java.util.Objects; import java.util.RandomAccess; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Space-efficient, comparable, immutable lists. */ @@ -244,7 +249,7 @@ private static ComparableList of_(List t) { } /** Returns a list that consists of a given list plus an element. */ - public static List append(List list, E e) { + public static List append(List list, E e) { if (list instanceof AbstractFlatList) { //noinspection unchecked return ((AbstractFlatList) list).append(e); @@ -256,13 +261,14 @@ public static List append(List list, E e) { /** Returns a list that consists of a given list plus an element, guaranteed * to be an {@link ImmutableList}. */ - public static ImmutableList append(ImmutableList list, E e) { + public static ImmutableList append(ImmutableList list, E e) { return ImmutableList.builder().addAll(list).add(e).build(); } /** Returns a map that consists of a given map plus an (key, value), * guaranteed to be an {@link ImmutableMap}. */ - public static ImmutableMap append(Map map, K k, V v) { + public static ImmutableMap append( + Map map, K k, V v) { final ImmutableMap.Builder builder = ImmutableMap.builder(); builder.put(k, v); map.forEach((k2, v2) -> { @@ -332,7 +338,7 @@ protected static class Flat1List return Collections.singletonList(t0).iterator(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -351,7 +357,7 @@ protected static class Flat1List return h; } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { if (o == null) { if (t0 == null) { return 0; @@ -364,7 +370,7 @@ protected static class Flat1List return -1; } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { if (o == null) { if (t0 == null) { return 0; @@ -377,9 +383,9 @@ protected static class Flat1List return -1; } - @Override @SuppressWarnings({"unchecked" }) - public T2[] toArray(T2[] a) { - if (a.length < 1) { + @SuppressWarnings({"unchecked" }) + @Override public @Nullable T2[] toArray(T2 @Nullable [] a) { + if (castNonNull(a).length < 1) { // Make a new array of a's runtime type, but my contents: return (T2[]) Arrays.copyOf(toArray(), 1, a.getClass()); } @@ -387,8 +393,8 @@ public T2[] toArray(T2[] a) { return a; } - @Override public Object[] toArray() { - return new Object[] {t0}; + @Override public @PolyNull Object[] toArray(Flat1List<@PolyNull T> this) { + return new Object[]{castNonNull(t0)}; } @Override public int compareTo(List o) { @@ -448,7 +454,7 @@ protected static class Flat2List return Arrays.asList(t0, t1).iterator(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -471,7 +477,7 @@ protected static class Flat2List return h; } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { if (o == null) { if (t0 == null) { return 0; @@ -490,7 +496,7 @@ protected static class Flat2List return -1; } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { if (o == null) { if (t1 == null) { return 1; @@ -509,9 +515,9 @@ protected static class Flat2List return -1; } - @Override @SuppressWarnings({"unchecked" }) - public T2[] toArray(T2[] a) { - if (a.length < 2) { + @SuppressWarnings({"unchecked" }) + @Override public @Nullable T2[] toArray(T2 @Nullable [] a) { + if (castNonNull(a).length < 2) { // Make a new array of a's runtime type, but my contents: return (T2[]) Arrays.copyOf(toArray(), 2, a.getClass()); } @@ -520,8 +526,8 @@ public T2[] toArray(T2[] a) { return a; } - @Override public Object[] toArray() { - return new Object[] {t0, t1}; + @Override public @PolyNull Object[] toArray(Flat2List<@PolyNull T> this) { + return new Object[] {castNonNull(t0), castNonNull(t1)}; } @Override public int compareTo(List o) { @@ -585,7 +591,7 @@ protected static class Flat3List return Arrays.asList(t0, t1, t2).iterator(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -608,7 +614,7 @@ protected static class Flat3List return h; } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { if (o == null) { if (t0 == null) { return 0; @@ -633,7 +639,7 @@ protected static class Flat3List return -1; } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { if (o == null) { if (t2 == null) { return 2; @@ -658,9 +664,9 @@ protected static class Flat3List return -1; } - @Override @SuppressWarnings({"unchecked" }) - public T2[] toArray(T2[] a) { - if (a.length < 3) { + @SuppressWarnings({"unchecked" }) + @Override public @Nullable T2[] toArray(T2 @Nullable [] a) { + if (castNonNull(a).length < 3) { // Make a new array of a's runtime type, but my contents: return (T2[]) Arrays.copyOf(toArray(), 3, a.getClass()); } @@ -670,8 +676,8 @@ public T2[] toArray(T2[] a) { return a; } - @Override public Object[] toArray() { - return new Object[] {t0, t1, t2}; + @Override public @PolyNull Object[] toArray(Flat3List<@PolyNull T> this) { + return new Object[] {castNonNull(t0), castNonNull(t1), castNonNull(t2)}; } @Override public int compareTo(List o) { @@ -739,7 +745,7 @@ protected static class Flat4List return Arrays.asList(t0, t1, t2, t3).iterator(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -764,7 +770,7 @@ protected static class Flat4List return h; } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { if (o == null) { if (t0 == null) { return 0; @@ -795,7 +801,7 @@ protected static class Flat4List return -1; } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { if (o == null) { if (t3 == null) { return 3; @@ -826,9 +832,9 @@ protected static class Flat4List return -1; } - @Override @SuppressWarnings({"unchecked" }) - public T2[] toArray(T2[] a) { - if (a.length < 4) { + @SuppressWarnings({"unchecked" }) + @Override public @Nullable T2[] toArray(T2 @Nullable [] a) { + if (castNonNull(a).length < 4) { // Make a new array of a's runtime type, but my contents: return (T2[]) Arrays.copyOf(toArray(), 4, a.getClass()); } @@ -839,8 +845,8 @@ public T2[] toArray(T2[] a) { return a; } - @Override public Object[] toArray() { - return new Object[] {t0, t1, t2, t3}; + @Override public @PolyNull Object[] toArray(Flat4List<@PolyNull T> this) { + return new Object[] {castNonNull(t0), castNonNull(t1), castNonNull(t2), castNonNull(t3)}; } @Override public int compareTo(List o) { @@ -912,7 +918,7 @@ protected static class Flat5List return Arrays.asList(t0, t1, t2, t3, t4).iterator(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -939,7 +945,7 @@ protected static class Flat5List return h; } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { if (o == null) { if (t0 == null) { return 0; @@ -976,7 +982,7 @@ protected static class Flat5List return -1; } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { if (o == null) { if (t4 == null) { return 4; @@ -1013,9 +1019,9 @@ protected static class Flat5List return -1; } - @Override @SuppressWarnings({"unchecked" }) - public T2[] toArray(T2[] a) { - if (a.length < 5) { + @SuppressWarnings({"unchecked" }) + @Override public @Nullable T2[] toArray(T2 @Nullable [] a) { + if (castNonNull(a).length < 5) { // Make a new array of a's runtime type, but my contents: return (T2[]) Arrays.copyOf(toArray(), 5, a.getClass()); } @@ -1027,8 +1033,9 @@ public T2[] toArray(T2[] a) { return a; } - @Override public Object[] toArray() { - return new Object[] {t0, t1, t2, t3, t4}; + @Override public @PolyNull Object[] toArray(Flat5List<@PolyNull T> this) { + return new Object[] {castNonNull(t0), castNonNull(t1), castNonNull(t2), castNonNull(t3), + castNonNull(t4)}; } @Override public int compareTo(List o) { @@ -1105,7 +1112,7 @@ protected static class Flat6List return Arrays.asList(t0, t1, t2, t3, t4, t5).iterator(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -1134,7 +1141,7 @@ protected static class Flat6List return h; } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { if (o == null) { if (t0 == null) { return 0; @@ -1177,7 +1184,7 @@ protected static class Flat6List return -1; } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { if (o == null) { if (t5 == null) { return 5; @@ -1220,9 +1227,9 @@ protected static class Flat6List return -1; } - @Override @SuppressWarnings({"unchecked" }) - public T2[] toArray(T2[] a) { - if (a.length < 6) { + @SuppressWarnings({"unchecked" }) + @Override public @Nullable T2[] toArray(T2 @Nullable [] a) { + if (castNonNull(a).length < 6) { // Make a new array of a's runtime type, but my contents: return (T2[]) Arrays.copyOf(toArray(), 6, a.getClass()); } @@ -1235,8 +1242,9 @@ public T2[] toArray(T2[] a) { return a; } - @Override public Object[] toArray() { - return new Object[] {t0, t1, t2, t3, t4, t5}; + @Override public @PolyNull Object[] toArray(Flat6List<@PolyNull T> this) { + return new Object[] {castNonNull(t0), castNonNull(t1), castNonNull(t2), castNonNull(t3), + castNonNull(t4), castNonNull(t5)}; } @Override public int compareTo(List o) { @@ -1265,7 +1273,7 @@ private ComparableEmptyList() { return 1; // same as Collections.emptyList() } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof List && ((List) o).isEmpty(); } diff --git a/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java b/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java index 7b1fb251b0f8..a16a037ae8a8 100644 --- a/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java @@ -39,6 +39,8 @@ import com.esri.core.geometry.WktExportFlags; import com.esri.core.geometry.WktImportFlags; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import static org.apache.calcite.runtime.Geometries.NO_SRID; @@ -264,17 +266,17 @@ public static boolean ST_Is3D(Geom geom) { } /** Returns the x-value of the first coordinate of {@code geom}. */ - public static Double ST_X(Geom geom) { + public static @Nullable Double ST_X(Geom geom) { return geom.g() instanceof Point ? ((Point) geom.g()).getX() : null; } /** Returns the y-value of the first coordinate of {@code geom}. */ - public static Double ST_Y(Geom geom) { + public static @Nullable Double ST_Y(Geom geom) { return geom.g() instanceof Point ? ((Point) geom.g()).getY() : null; } /** Returns the z-value of the first coordinate of {@code geom}. */ - public static Double ST_Z(Geom geom) { + public static @Nullable Double ST_Z(Geom geom) { return geom.g().getDescription().hasZ() && geom.g() instanceof Point ? ((Point) geom.g()).getZ() : null; } @@ -487,7 +489,7 @@ public static Geom ST_SetSRID(Geom geom, int srid) { /** Returns the position of a point on the Hilbert curve, * or null if it is not a 2-dimensional point. */ @Hints({"SqlKind:HILBERT"}) - public static Long hilbert(Geom geom) { + public static @Nullable Long hilbert(Geom geom) { final Geometry g = geom.g(); if (g instanceof Point) { final double x = ((Point) g).getX(); diff --git a/core/src/main/java/org/apache/calcite/runtime/Geometries.java b/core/src/main/java/org/apache/calcite/runtime/Geometries.java index 0805a81d649b..1c2267858c8e 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Geometries.java +++ b/core/src/main/java/org/apache/calcite/runtime/Geometries.java @@ -33,6 +33,8 @@ import com.esri.core.geometry.SpatialReference; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.util.Objects; /** @@ -53,7 +55,7 @@ static UnsupportedOperationException todo() { return new UnsupportedOperationException(); } - protected static Geom bind(Geometry geometry, int srid) { + protected static @PolyNull Geom bind(@PolyNull Geometry geometry, int srid) { if (geometry == null) { return null; } diff --git a/core/src/main/java/org/apache/calcite/runtime/Hook.java b/core/src/main/java/org/apache/calcite/runtime/Hook.java index 9f36913a09e1..5909e4ce913c 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Hook.java +++ b/core/src/main/java/org/apache/calcite/runtime/Hook.java @@ -20,6 +20,7 @@ import org.apache.calcite.util.Holder; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; import java.util.List; @@ -27,6 +28,8 @@ import java.util.function.Consumer; import java.util.function.Function; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Collection of hooks that can be set by observers and are executed at various * parts of the query preparation process. @@ -107,7 +110,7 @@ public enum Hook { new CopyOnWriteArrayList<>(); @SuppressWarnings("ImmutableEnumChecker") - private final ThreadLocal>> threadHandlers = + private final ThreadLocal<@Nullable List>> threadHandlers = ThreadLocal.withInitial(ArrayList::new); /** Adds a handler for this Hook. @@ -151,7 +154,7 @@ private boolean remove(Consumer handler) { /** Adds a handler for this thread. */ public Closeable addThread(final Consumer handler) { //noinspection unchecked - threadHandlers.get().add((Consumer) handler); + castNonNull(threadHandlers.get()).add((Consumer) handler); return () -> removeThread(handler); } @@ -166,7 +169,7 @@ public Closeable addThread( /** Removes a thread handler from this Hook. */ private boolean removeThread(Consumer handler) { - return threadHandlers.get().remove(handler); + return castNonNull(threadHandlers.get()).remove(handler); } // CHECKSTYLE: IGNORE 1 @@ -194,7 +197,7 @@ public void run(Object arg) { for (Consumer handler : handlers) { handler.accept(arg); } - for (Consumer handler : threadHandlers.get()) { + for (Consumer handler : castNonNull(threadHandlers.get())) { handler.accept(arg); } } diff --git a/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java b/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java index 1f75ecbb3e8b..896ceb200984 100644 --- a/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java @@ -36,6 +36,9 @@ import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import com.jayway.jsonpath.spi.mapper.MappingProvider; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -52,8 +55,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * A collection of functions used in JSON processing. */ @@ -84,11 +90,11 @@ private static boolean isScalarObject(Object obj) { return true; } - public static String jsonize(Object input) { + public static String jsonize(@Nullable Object input) { return JSON_PATH_JSON_PROVIDER.toJson(input); } - public static Object dejsonize(String input) { + public static @Nullable Object dejsonize(String input) { return JSON_PATH_JSON_PROVIDER.parse(input); } @@ -121,8 +127,8 @@ public static JsonPathContext jsonApiCommonSyntax(JsonValueContext input, String mode = PathMode.STRICT; pathStr = pathSpec; } else { - mode = PathMode.valueOf(matcher.group(1).toUpperCase(Locale.ROOT)); - pathStr = matcher.group(2); + mode = PathMode.valueOf(castNonNull(matcher.group(1)).toUpperCase(Locale.ROOT)); + pathStr = castNonNull(matcher.group(2)); } DocumentContext ctx; switch (mode) { @@ -130,7 +136,7 @@ public static JsonPathContext jsonApiCommonSyntax(JsonValueContext input, String if (input.hasException()) { return JsonPathContext.withStrictException(pathSpec, input.exc); } - ctx = JsonPath.parse(input.obj, + ctx = JsonPath.parse(input.obj(), Configuration .builder() .jsonProvider(JSON_PATH_JSON_PROVIDER) @@ -141,7 +147,7 @@ public static JsonPathContext jsonApiCommonSyntax(JsonValueContext input, String if (input.hasException()) { return JsonPathContext.withJavaObj(PathMode.LAX, null); } - ctx = JsonPath.parse(input.obj, + ctx = JsonPath.parse(input.obj(), Configuration .builder() .options(Option.SUPPRESS_EXCEPTIONS) @@ -162,29 +168,29 @@ public static JsonPathContext jsonApiCommonSyntax(JsonValueContext input, String } } - public static Boolean jsonExists(String input, String pathSpec) { + public static @Nullable Boolean jsonExists(String input, String pathSpec) { return jsonExists(jsonApiCommonSyntax(input, pathSpec)); } - public static Boolean jsonExists(String input, String pathSpec, + public static @Nullable Boolean jsonExists(String input, String pathSpec, SqlJsonExistsErrorBehavior errorBehavior) { return jsonExists(jsonApiCommonSyntax(input, pathSpec), errorBehavior); } - public static Boolean jsonExists(JsonValueContext input, String pathSpec) { + public static @Nullable Boolean jsonExists(JsonValueContext input, String pathSpec) { return jsonExists(jsonApiCommonSyntax(input, pathSpec)); } - public static Boolean jsonExists(JsonValueContext input, String pathSpec, + public static @Nullable Boolean jsonExists(JsonValueContext input, String pathSpec, SqlJsonExistsErrorBehavior errorBehavior) { return jsonExists(jsonApiCommonSyntax(input, pathSpec), errorBehavior); } - public static Boolean jsonExists(JsonPathContext context) { + public static @Nullable Boolean jsonExists(JsonPathContext context) { return jsonExists(context, SqlJsonExistsErrorBehavior.FALSE); } - public static Boolean jsonExists(JsonPathContext context, + public static @Nullable Boolean jsonExists(JsonPathContext context, SqlJsonExistsErrorBehavior errorBehavior) { if (context.hasException()) { switch (errorBehavior) { @@ -205,7 +211,7 @@ public static Boolean jsonExists(JsonPathContext context, } } - public static Object jsonValue(String input, + public static @Nullable Object jsonValue(String input, String pathSpec, SqlJsonValueEmptyOrErrorBehavior emptyBehavior, Object defaultValueOnEmpty, @@ -219,7 +225,7 @@ public static Object jsonValue(String input, defaultValueOnError); } - public static Object jsonValue(JsonValueContext input, + public static @Nullable Object jsonValue(JsonValueContext input, String pathSpec, SqlJsonValueEmptyOrErrorBehavior emptyBehavior, Object defaultValueOnEmpty, @@ -233,7 +239,7 @@ public static Object jsonValue(JsonValueContext input, defaultValueOnError); } - public static Object jsonValue(JsonPathContext context, + public static @Nullable Object jsonValue(JsonPathContext context, SqlJsonValueEmptyOrErrorBehavior emptyBehavior, Object defaultValueOnEmpty, SqlJsonValueEmptyOrErrorBehavior errorBehavior, @@ -277,7 +283,7 @@ public static Object jsonValue(JsonPathContext context, } } - public static String jsonQuery(String input, + public static @Nullable String jsonQuery(String input, String pathSpec, SqlJsonQueryWrapperBehavior wrapperBehavior, SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, @@ -287,7 +293,7 @@ public static String jsonQuery(String input, wrapperBehavior, emptyBehavior, errorBehavior); } - public static String jsonQuery(JsonValueContext input, + public static @Nullable String jsonQuery(JsonValueContext input, String pathSpec, SqlJsonQueryWrapperBehavior wrapperBehavior, SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, @@ -297,7 +303,7 @@ public static String jsonQuery(JsonValueContext input, wrapperBehavior, emptyBehavior, errorBehavior); } - public static String jsonQuery(JsonPathContext context, + public static @Nullable String jsonQuery(JsonPathContext context, SqlJsonQueryWrapperBehavior wrapperBehavior, SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, SqlJsonQueryEmptyOrErrorBehavior errorBehavior) { @@ -370,9 +376,9 @@ && isScalarObject(value)) { } public static String jsonObject(SqlJsonConstructorNullClause nullClause, - Object... kvs) { + @Nullable Object... kvs) { assert kvs.length % 2 == 0; - Map map = new HashMap<>(); + Map map = new HashMap<>(); for (int i = 0; i < kvs.length; i += 2) { String k = (String) kvs[i]; Object v = kvs[i + 1]; @@ -390,7 +396,7 @@ public static String jsonObject(SqlJsonConstructorNullClause nullClause, return jsonize(map); } - public static void jsonObjectAggAdd(Map map, String k, Object v, + public static void jsonObjectAggAdd(Map map, String k, @Nullable Object v, SqlJsonConstructorNullClause nullClause) { if (k == null) { throw RESOURCE.nullKeyOfJsonObjectNotAllowed().ex(); @@ -405,8 +411,8 @@ public static void jsonObjectAggAdd(Map map, String k, Object v, } public static String jsonArray(SqlJsonConstructorNullClause nullClause, - Object... elements) { - List list = new ArrayList<>(); + @Nullable Object... elements) { + List<@Nullable Object> list = new ArrayList<>(); for (Object element : elements) { if (element == null) { if (nullClause == SqlJsonConstructorNullClause.NULL_ON_NULL) { @@ -419,7 +425,7 @@ public static String jsonArray(SqlJsonConstructorNullClause nullClause, return jsonize(list); } - public static void jsonArrayAggAdd(List list, Object element, + public static void jsonArrayAggAdd(List list, @Nullable Object element, SqlJsonConstructorNullClause nullClause) { if (element == null) { if (nullClause == SqlJsonConstructorNullClause.NULL_ON_NULL) { @@ -480,11 +486,11 @@ public static String jsonType(JsonValueContext input) { } } - public static Integer jsonDepth(String input) { + public static @Nullable Integer jsonDepth(String input) { return jsonDepth(jsonValueExpression(input)); } - public static Integer jsonDepth(JsonValueContext input) { + public static @Nullable Integer jsonDepth(JsonValueContext input) { final Integer result; final Object o = input.obj; try { @@ -529,23 +535,23 @@ private static Integer calculateDepth(Object o) { return depth; } - public static Integer jsonLength(String input) { + public static @Nullable Integer jsonLength(String input) { return jsonLength(jsonApiCommonSyntax(input)); } - public static Integer jsonLength(JsonValueContext input) { + public static @Nullable Integer jsonLength(JsonValueContext input) { return jsonLength(jsonApiCommonSyntax(input)); } - public static Integer jsonLength(String input, String pathSpec) { + public static @Nullable Integer jsonLength(String input, String pathSpec) { return jsonLength(jsonApiCommonSyntax(input, pathSpec)); } - public static Integer jsonLength(JsonValueContext input, String pathSpec) { + public static @Nullable Integer jsonLength(JsonValueContext input, String pathSpec) { return jsonLength(jsonApiCommonSyntax(input, pathSpec)); } - public static Integer jsonLength(JsonPathContext context) { + public static @Nullable Integer jsonLength(JsonPathContext context) { final Integer result; final Object value; try { @@ -620,7 +626,7 @@ public static String jsonRemove(String input, String... pathSpecs) { public static String jsonRemove(JsonValueContext input, String... pathSpecs) { try { - DocumentContext ctx = JsonPath.parse(input.obj, + DocumentContext ctx = JsonPath.parse(input.obj(), Configuration .builder() .options(Option.SUPPRESS_EXCEPTIONS) @@ -697,20 +703,21 @@ private static RuntimeException toUnchecked(Exception e) { */ public static class JsonPathContext { public final PathMode mode; - public final Object obj; - public final Exception exc; + public final @Nullable Object obj; + public final @Nullable Exception exc; - private JsonPathContext(Object obj, Exception exc) { + private JsonPathContext(@Nullable Object obj, @Nullable Exception exc) { this(PathMode.NONE, obj, exc); } - private JsonPathContext(PathMode mode, Object obj, Exception exc) { + private JsonPathContext(PathMode mode, @Nullable Object obj, @Nullable Exception exc) { assert obj == null || exc == null; this.mode = mode; this.obj = obj; this.exc = exc; } + @EnsuresNonNullIf(expression = "exc", result = true) public boolean hasException() { return exc != null; } @@ -730,7 +737,7 @@ public static JsonPathContext withStrictException(String pathSpec, Exception exc return withStrictException(exc); } - public static JsonPathContext withJavaObj(PathMode mode, Object obj) { + public static JsonPathContext withJavaObj(PathMode mode, @Nullable Object obj) { if (mode == PathMode.UNKNOWN) { throw RESOURCE.illegalJsonPathMode(mode.toString()).ex(); } @@ -754,16 +761,16 @@ public static JsonPathContext withJavaObj(PathMode mode, Object obj) { */ public static class JsonValueContext { @JsonValue - public final Object obj; - public final Exception exc; + public final @Nullable Object obj; + public final @Nullable Exception exc; - private JsonValueContext(Object obj, Exception exc) { + private JsonValueContext(@Nullable Object obj, @Nullable Exception exc) { assert obj == null || exc == null; this.obj = obj; this.exc = exc; } - public static JsonValueContext withJavaObj(Object obj) { + public static JsonValueContext withJavaObj(@Nullable Object obj) { return new JsonValueContext(obj, null); } @@ -771,11 +778,16 @@ public static JsonValueContext withException(Exception exc) { return new JsonValueContext(null, exc); } + Object obj() { + return requireNonNull(obj, "json object must not be null"); + } + + @EnsuresNonNullIf(expression = "exc", result = true) public boolean hasException() { return exc != null; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/src/main/java/org/apache/calcite/runtime/Like.java b/core/src/main/java/org/apache/calcite/runtime/Like.java index 806f6b75d136..8ebecb73f316 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Like.java +++ b/core/src/main/java/org/apache/calcite/runtime/Like.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.runtime; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Utilities for converting SQL {@code LIKE} and {@code SIMILAR} operators * to regular expressions. @@ -49,7 +51,7 @@ private Like() { */ static String sqlToRegexLike( String sqlPattern, - CharSequence escapeStr) { + @Nullable CharSequence escapeStr) { final char escapeChar; if (escapeStr != null) { if (escapeStr.length() != 1) { @@ -209,7 +211,7 @@ private static int sqlSimilarRewriteCharEnumeration( */ static String sqlToRegexSimilar( String sqlPattern, - CharSequence escapeStr) { + @Nullable CharSequence escapeStr) { final char escapeChar; if (escapeStr != null) { if (escapeStr.length() != 1) { diff --git a/core/src/main/java/org/apache/calcite/runtime/Matcher.java b/core/src/main/java/org/apache/calcite/runtime/Matcher.java index 2eda19c31c32..891fdbfa85b5 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Matcher.java +++ b/core/src/main/java/org/apache/calcite/runtime/Matcher.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -241,7 +243,7 @@ public PartialMatch append(String symbol, E row, return new PartialMatch<>(startRow, symbols, rows, toState); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof PartialMatch && startRow == ((PartialMatch) o).startRow @@ -331,7 +333,7 @@ static class Tuple { this.row = row; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof Tuple && ((Tuple) o).symbol.equals(symbol) diff --git a/core/src/main/java/org/apache/calcite/runtime/PredicateImpl.java b/core/src/main/java/org/apache/calcite/runtime/PredicateImpl.java index 614a22f0a7ec..d2ee6be35087 100644 --- a/core/src/main/java/org/apache/calcite/runtime/PredicateImpl.java +++ b/core/src/main/java/org/apache/calcite/runtime/PredicateImpl.java @@ -18,7 +18,7 @@ import com.google.common.base.Predicate; -import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Abstract implementation of {@link com.google.common.base.Predicate}. diff --git a/core/src/main/java/org/apache/calcite/runtime/RandomFunction.java b/core/src/main/java/org/apache/calcite/runtime/RandomFunction.java index f6a15cfb4951..71936c383bd2 100644 --- a/core/src/main/java/org/apache/calcite/runtime/RandomFunction.java +++ b/core/src/main/java/org/apache/calcite/runtime/RandomFunction.java @@ -19,6 +19,8 @@ import org.apache.calcite.linq4j.function.Deterministic; import org.apache.calcite.linq4j.function.Parameter; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + import java.util.Random; /** @@ -27,7 +29,7 @@ */ @SuppressWarnings("unused") public class RandomFunction { - private Random random; + private @MonotonicNonNull Random random; /** Creates a RandomFunction. * diff --git a/core/src/main/java/org/apache/calcite/runtime/Resources.java b/core/src/main/java/org/apache/calcite/runtime/Resources.java index 5ed5720d3dec..d3a2a800a383 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Resources.java +++ b/core/src/main/java/org/apache/calcite/runtime/Resources.java @@ -16,26 +16,57 @@ */ package org.apache.calcite.runtime; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; + import java.io.IOException; import java.io.InputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.reflect.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; import java.security.PrivilegedAction; -import java.text.*; -import java.util.*; +import java.text.DateFormat; +import java.text.Format; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Defining wrapper classes around resources that allow the compiler to check * whether the resources exist, and that uses of resources have the appropriate * number and types of arguments to match the message. */ public class Resources { - private static final ThreadLocal MAP_THREAD_TO_LOCALE = + private static final ThreadLocal<@Nullable Locale> MAP_THREAD_TO_LOCALE = new ThreadLocal<>(); private Resources() {} @@ -65,7 +96,7 @@ public static void setThreadLocale(Locale locale) { * thread has not called {@link #setThreadLocale}. * * @return Locale */ - public static Locale getThreadLocale() { + public static @Nullable Locale getThreadLocale() { return MAP_THREAD_TO_LOCALE.get(); } @@ -113,7 +144,7 @@ public static T create(Class clazz) { * @return Instance of the interface that can be used to instantiate * resources */ - public static T create(String base, Class clazz) { + public static T create(@Nullable String base, Class clazz) { return create(base, EmptyPropertyAccessor.INSTANCE, clazz); } @@ -129,7 +160,7 @@ public static T create(final Properties properties, Class clazz) { return create(null, new PropertiesAccessor(properties), clazz); } - private static T create(final String base, + private static T create(final @Nullable String base, final PropertyAccessor accessor, Class clazz) { //noinspection unchecked return (T) Proxy.newProxyInstance(clazz.getClassLoader(), @@ -137,8 +168,7 @@ private static T create(final String base, new InvocationHandler() { final Map cache = new ConcurrentHashMap<>(); - @Override - public Object invoke(Object proxy, Method method, Object[] args) + @Override public Object invoke(Object proxy, Method method, @Nullable Object @Nullable [] args) throws Throwable { if (args == null || args.length == 0) { Object o = cache.get(method.getName()); @@ -151,7 +181,7 @@ public Object invoke(Object proxy, Method method, Object[] args) return create(method, args); } - private Object create(Method method, Object[] args) + private Object create(Method method, @Nullable Object @Nullable [] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { if (method.equals(BuiltinMethod.OBJECT_TO_STRING.method)) { @@ -208,12 +238,13 @@ public static void validate(Object o, EnumSet validations) { && Inst.class.isAssignableFrom(method.getReturnType())) { ++count; final Class[] parameterTypes = method.getParameterTypes(); - Object[] args = new Object[parameterTypes.length]; + @Nullable Object[] args = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { args[i] = zero(parameterTypes[i]); } try { Inst inst = (Inst) method.invoke(o, args); + assert inst != null : "got null from " + method; inst.validate(validations); } catch (IllegalAccessException e) { throw new RuntimeException("in " + method, e); @@ -228,7 +259,7 @@ public static void validate(Object o, EnumSet validations) { } } - private static Object zero(Class clazz) { + private static @Nullable Object zero(Class clazz) { return clazz == String.class ? "" : clazz == byte.class ? (byte) 0 : clazz == char.class ? (char) 0 @@ -242,7 +273,7 @@ private static Object zero(Class clazz) { } /** Returns whether two objects are equal or are both null. */ - private static boolean equal(Object o0, Object o1) { + private static boolean equal(@Nullable Object o0, @Nullable Object o1) { return o0 == o1 || o0 != null && o0.equals(o1); } @@ -251,6 +282,7 @@ public static class Element { protected final Method method; protected final String key; + @SuppressWarnings("method.invocation.invalid") public Element(Method method) { this.method = method; this.key = deriveKey(); @@ -273,16 +305,16 @@ protected String deriveKey() { public static class Inst extends Element { private final Locale locale; protected final String base; - protected final Object[] args; + protected final @Nullable Object[] args; - public Inst(String base, Locale locale, Method method, Object... args) { + public Inst(String base, Locale locale, Method method, @Nullable Object... args) { super(method); this.base = base; this.locale = locale; this.args = args; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj != null && obj.getClass() == this.getClass() @@ -333,7 +365,9 @@ public void validate(EnumSet validations) { } break; case EVEN_QUOTES: - String message = method.getAnnotation(BaseMessage.class).value(); + String message = requireNonNull( + method.getAnnotation(BaseMessage.class), + () -> "@BaseMessage is missing for resource '" + method.getName() + "'").value(); if (countQuotesIn(message) % 2 == 1) { throw new AssertionError("resource '" + method.getName() + "' should have even number of quotes"); @@ -357,7 +391,7 @@ public void validate(EnumSet validations) { case ARGUMENT_MATCH: String raw = raw(); MessageFormat format = new MessageFormat(raw); - final Format[] formats = format.getFormatsByArgumentIndex(); + final @Nullable Format[] formats = format.getFormatsByArgumentIndex(); final List types = new ArrayList<>(); final Class[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < formats.length; i++) { @@ -417,7 +451,9 @@ public String raw() { } catch (MissingResourceException e) { // Resource is not in the bundle. (It is probably missing from the // .properties file.) Fall back to the base message. - return method.getAnnotation(BaseMessage.class).value(); + return requireNonNull( + method.getAnnotation(BaseMessage.class), + () -> "@BaseMessage is missing for resource '" + method.getName() + "'").value(); } } @@ -437,7 +473,7 @@ public Map getProperties() { * by exception.*/ public static class ExInstWithCause extends Inst { public ExInstWithCause(String base, Locale locale, Method method, - Object... args) { + @Nullable Object... args) { super(base, locale, method, args); } @@ -445,7 +481,7 @@ public ExInstWithCause(String base, Locale locale, Method method, return new ExInstWithCause(base, locale, method, args); } - public T ex(Throwable cause) { + public T ex(@Nullable Throwable cause) { try { //noinspection unchecked final Class exceptionClass = @@ -515,16 +551,17 @@ public static Class getExceptionClass(Type type) { "Unable to find superclass ExInstWithCause for " + type); } if (type instanceof Class) { - type = ((Class) type).getGenericSuperclass(); - if (type == null) { + Type superclass = ((Class) type).getGenericSuperclass(); + if (superclass == null) { throw new IllegalStateException( "Unable to find superclass ExInstWithCause for " + type0); } + type = superclass; } } } - protected void validateException(Callable exSupplier) { + protected void validateException(Callable exSupplier) { Throwable cause = null; try { //noinspection ThrowableResultOfMethodCallIgnored @@ -583,6 +620,17 @@ protected Prop(PropertyAccessor accessor, Method method) { this.hasDefault = resource != null; } + @RequiresNonNull("method") + protected final @Nullable Default getDefault( + @UnderInitialization Prop this + ) { + if (hasDefault) { + return castNonNull(method.getAnnotation(Default.class)); + } else { + return null; + } + } + public boolean isSet() { return accessor.isSet(this); } @@ -612,8 +660,8 @@ public static class IntProp extends Prop { public IntProp(PropertyAccessor accessor, Method method) { super(accessor, method); - if (hasDefault) { - final Default resource = method.getAnnotation(Default.class); + final Default resource = getDefault(); + if (resource != null) { defaultValue = Integer.parseInt(resource.value(), 10); } else { defaultValue = 0; @@ -643,8 +691,8 @@ public static class BooleanProp extends Prop { public BooleanProp(PropertyAccessor accessor, Method method) { super(accessor, method); - if (hasDefault) { - final Default resource = method.getAnnotation(Default.class); + final Default resource = getDefault(); + if (resource != null) { defaultValue = Boolean.parseBoolean(resource.value()); } else { defaultValue = false; @@ -674,8 +722,8 @@ public static class DoubleProp extends Prop { public DoubleProp(PropertyAccessor accessor, Method method) { super(accessor, method); - if (hasDefault) { - final Default resource = method.getAnnotation(Default.class); + final Default resource = getDefault(); + if (resource != null) { defaultValue = Double.parseDouble(resource.value()); } else { defaultValue = 0d; @@ -701,12 +749,12 @@ public double defaultValue() { /** String property instance. */ public static class StringProp extends Prop { - private final String defaultValue; + private final @Nullable String defaultValue; public StringProp(PropertyAccessor accessor, Method method) { super(accessor, method); - if (hasDefault) { - final Default resource = method.getAnnotation(Default.class); + final Default resource = getDefault(); + if (resource != null) { defaultValue = resource.value(); } else { defaultValue = null; @@ -714,17 +762,17 @@ public StringProp(PropertyAccessor accessor, Method method) { } /** Returns the value of this String property. */ - public String get() { + public @Nullable String get() { return accessor.stringValue(this); } /** Returns the value of this String property, returning the given default * value if the property is not set. */ - public String get(String defaultValue) { + public @PolyNull String get(@PolyNull String defaultValue) { return accessor.stringValue(this, defaultValue); } - public String defaultValue() { + public @Nullable String defaultValue() { checkDefault(); return defaultValue; } @@ -744,8 +792,8 @@ public interface PropertyAccessor { boolean isSet(Prop p); int intValue(IntProp p); int intValue(IntProp p, int defaultValue); - String stringValue(StringProp p); - String stringValue(StringProp p, String defaultValue); + @Nullable String stringValue(StringProp p); + @PolyNull String stringValue(StringProp p, @PolyNull String defaultValue); boolean booleanValue(BooleanProp p); boolean booleanValue(BooleanProp p, boolean defaultValue); double doubleValue(DoubleProp p); @@ -770,13 +818,11 @@ public int intValue(IntProp p, int defaultValue) { return defaultValue; } - @Override - public String stringValue(StringProp p) { + @Override public @Nullable String stringValue(StringProp p) { return p.defaultValue(); } - @Override - public String stringValue(StringProp p, String defaultValue) { + @Override public @PolyNull String stringValue(StringProp p, @PolyNull String defaultValue) { return defaultValue; } @@ -887,7 +933,7 @@ public enum Validation { * load the properties file based upon the name of the class. */ public abstract static class ShadowResourceBundle extends ResourceBundle { - private PropertyResourceBundle bundle; + private final PropertyResourceBundle bundle; /** * Creates a ShadowResourceBundle, and reads resources from @@ -936,11 +982,11 @@ protected ShadowResourceBundle() throws IOException { * Opens the properties file corresponding to a given class. The code is * copied from {@link ResourceBundle}. */ - private static InputStream openPropertiesFile(Class clazz) { + private static @Nullable InputStream openPropertiesFile(Class clazz) { final ClassLoader loader = clazz.getClassLoader(); final String resName = clazz.getName().replace('.', '/') + ".properties"; return java.security.AccessController.doPrivileged( - (PrivilegedAction) () -> { + (PrivilegedAction<@Nullable InputStream>) () -> { if (loader != null) { return loader.getResourceAsStream(resName); } else { @@ -1076,8 +1122,7 @@ public int intValue(IntProp p, int defaultValue) { return s == null ? defaultValue : Integer.parseInt(s, 10); } - @Override - public String stringValue(StringProp p) { + @Override public @Nullable String stringValue(StringProp p) { final String s = properties.getProperty(p.key); if (s != null) { return s; @@ -1086,8 +1131,7 @@ public String stringValue(StringProp p) { return p.defaultValue; } - @Override - public String stringValue(StringProp p, String defaultValue) { + @Override public @PolyNull String stringValue(StringProp p, @PolyNull String defaultValue) { final String s = properties.getProperty(p.key); return s == null ? defaultValue : s; } diff --git a/core/src/main/java/org/apache/calcite/runtime/ResultSetEnumerable.java b/core/src/main/java/org/apache/calcite/runtime/ResultSetEnumerable.java index 20870cad13c9..84defdd5e323 100644 --- a/core/src/main/java/org/apache/calcite/runtime/ResultSetEnumerable.java +++ b/core/src/main/java/org/apache/calcite/runtime/ResultSetEnumerable.java @@ -27,6 +27,7 @@ import org.apache.calcite.linq4j.tree.Primitive; import org.apache.calcite.util.Static; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,6 +55,8 @@ import java.util.List; import javax.sql.DataSource; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Executes a SQL statement and returns the result as an {@link Enumerable}. * @@ -63,16 +66,16 @@ public class ResultSetEnumerable extends AbstractEnumerable { private final DataSource dataSource; private final String sql; private final Function1> rowBuilderFactory; - private final PreparedStatementEnricher preparedStatementEnricher; + private final @Nullable PreparedStatementEnricher preparedStatementEnricher; private static final Logger LOGGER = LoggerFactory.getLogger( ResultSetEnumerable.class); - private Long queryStart; + private @Nullable Long queryStart; private long timeout; private boolean timeoutSetFailed; - private static final Function1> AUTO_ROW_BUILDER_FACTORY = + private static final Function1> AUTO_ROW_BUILDER_FACTORY = resultSet -> { final ResultSetMetaData metaData; final int columnCount; @@ -95,9 +98,9 @@ public class ResultSetEnumerable extends AbstractEnumerable { } }; - private static Object[] convertColumns(ResultSet resultSet, ResultSetMetaData metaData, + private static @Nullable Object[] convertColumns(ResultSet resultSet, ResultSetMetaData metaData, int columnCount) { - final List list = new ArrayList<>(columnCount); + final List<@Nullable Object> list = new ArrayList<>(columnCount); try { for (int i = 0; i < columnCount; i++) { if (metaData.getColumnType(i + 1) == Types.TIMESTAMP) { @@ -121,7 +124,7 @@ private ResultSetEnumerable( DataSource dataSource, String sql, Function1> rowBuilderFactory, - PreparedStatementEnricher preparedStatementEnricher) { + @Nullable PreparedStatementEnricher preparedStatementEnricher) { this.dataSource = dataSource; this.sql = sql; this.rowBuilderFactory = rowBuilderFactory; @@ -136,13 +139,13 @@ private ResultSetEnumerable( } /** Creates a ResultSetEnumerable. */ - public static ResultSetEnumerable of(DataSource dataSource, String sql) { + public static ResultSetEnumerable<@Nullable Object> of(DataSource dataSource, String sql) { return of(dataSource, sql, AUTO_ROW_BUILDER_FACTORY); } /** Creates a ResultSetEnumerable that retrieves columns as specific * Java types. */ - public static ResultSetEnumerable of(DataSource dataSource, String sql, + public static ResultSetEnumerable<@Nullable Object> of(DataSource dataSource, String sql, Primitive[] primitives) { return of(dataSource, sql, primitiveRowBuilderFactory(primitives)); } @@ -198,8 +201,9 @@ public static PreparedStatementEnricher createEnricher(Integer[] indexes, /** Assigns a value to a dynamic parameter in a prepared statement, calling * the appropriate {@code setXxx} method based on the type of the value. */ private static void setDynamicParam(PreparedStatement preparedStatement, - int i, Object value) throws SQLException { + int i, @Nullable Object value) throws SQLException { if (value == null) { + // TODO: use proper type instead of ANY preparedStatement.setObject(i, null, SqlType.ANY.id); } else if (value instanceof Timestamp) { preparedStatement.setTimestamp(i, (Timestamp) value); @@ -270,6 +274,7 @@ private Enumerator enumeratorBasedOnStatement() { return new ResultSetEnumerator<>(resultSet, rowBuilderFactory); } else { Integer updateCount = statement.getUpdateCount(); + //noinspection unchecked return Linq4j.singletonEnumerator((T) updateCount); } } catch (SQLException e) { @@ -287,7 +292,7 @@ private Enumerator enumeratorBasedOnPreparedStatement() { connection = dataSource.getConnection(); preparedStatement = connection.prepareStatement(sql); setTimeoutIfPossible(preparedStatement); - preparedStatementEnricher.enrich(preparedStatement); + castNonNull(preparedStatementEnricher).enrich(preparedStatement); if (preparedStatement.execute()) { final ResultSet resultSet = preparedStatement.getResultSet(); preparedStatement = null; @@ -295,6 +300,7 @@ private Enumerator enumeratorBasedOnPreparedStatement() { return new ResultSetEnumerator<>(resultSet, rowBuilderFactory); } else { Integer updateCount = preparedStatement.getUpdateCount(); + //noinspection unchecked return Linq4j.singletonEnumerator((T) updateCount); } } catch (SQLException e) { @@ -306,7 +312,8 @@ private Enumerator enumeratorBasedOnPreparedStatement() { } private void setTimeoutIfPossible(Statement statement) throws SQLException { - if (timeout == 0) { + Long queryStart = this.queryStart; + if (timeout == 0 || queryStart == null) { return; } long now = System.currentTimeMillis(); @@ -331,7 +338,7 @@ private void setTimeoutIfPossible(Statement statement) throws SQLException { } } - private void closeIfPossible(Connection connection, Statement statement) { + private void closeIfPossible(@Nullable Connection connection, @Nullable Statement statement) { if (statement != null) { try { statement.close(); @@ -354,7 +361,7 @@ private void closeIfPossible(Connection connection, Statement statement) { * @param element type */ private static class ResultSetEnumerator implements Enumerator { private final Function0 rowBuilder; - private ResultSet resultSet; + private @Nullable ResultSet resultSet; ResultSetEnumerator( ResultSet resultSet, @@ -363,13 +370,17 @@ private static class ResultSetEnumerator implements Enumerator { this.rowBuilder = rowBuilderFactory.apply(resultSet); } + private ResultSet resultSet() { + return castNonNull(resultSet); + } + @Override public T current() { return rowBuilder.apply(); } @Override public boolean moveNext() { try { - return resultSet.next(); + return resultSet().next(); } catch (SQLException e) { throw new RuntimeException(e); } @@ -377,7 +388,7 @@ private static class ResultSetEnumerator implements Enumerator { @Override public void reset() { try { - resultSet.beforeFirst(); + resultSet().beforeFirst(); } catch (SQLException e) { throw new RuntimeException(e); } @@ -404,7 +415,7 @@ private static class ResultSetEnumerator implements Enumerator { } } - private static Function1> + private static Function1> primitiveRowBuilderFactory(final Primitive[] primitives) { return resultSet -> { final ResultSetMetaData metaData; @@ -429,9 +440,9 @@ private static class ResultSetEnumerator implements Enumerator { }; } - private static Object[] convertPrimitiveColumns(Primitive[] primitives, + private static @Nullable Object[] convertPrimitiveColumns(Primitive[] primitives, ResultSet resultSet, int columnCount) { - final List list = new ArrayList<>(columnCount); + final List<@Nullable Object> list = new ArrayList<>(columnCount); try { for (int i = 0; i < columnCount; i++) { list.add(primitives[i].jdbcGet(resultSet, i + 1)); diff --git a/core/src/main/java/org/apache/calcite/runtime/SpaceFillingCurve2D.java b/core/src/main/java/org/apache/calcite/runtime/SpaceFillingCurve2D.java index bde003d82017..d66cbf9dc05d 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SpaceFillingCurve2D.java +++ b/core/src/main/java/org/apache/calcite/runtime/SpaceFillingCurve2D.java @@ -115,6 +115,7 @@ class OverlappingRange extends AbstractRange { /** Lexicographic ordering for {@link IndexRange}. */ class IndexRangeOrdering extends Ordering { + @SuppressWarnings("override.param.invalid") @Override public int compare(IndexRange x, IndexRange y) { final int c1 = Long.compare(x.lower(), y.lower()); if (c1 != 0) { diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java index 983e1ba4aceb..7affcd022c78 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java @@ -46,6 +46,9 @@ import com.google.common.base.Splitter; import com.google.common.base.Strings; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.BigInteger; @@ -68,17 +71,17 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BinaryOperator; import java.util.regex.Pattern; -import javax.annotation.Nonnull; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; /** * Helper methods to implement SQL functions in generated code. @@ -121,15 +124,15 @@ public class SqlFunctions { "Alpha", "XDigit", "Digit", "Alnum", "Punct", "Graph", "Print", "Blank", "Cntrl", "Space" }; @SuppressWarnings("unused") - private static final Function1> ARRAY_CARTESIAN_PRODUCT = + private static final Function1> ARRAY_CARTESIAN_PRODUCT = lists -> { - final List> enumerators = new ArrayList<>(); + final List> enumerators = new ArrayList<>(); for (Object list : lists) { enumerators.add(Linq4j.enumerator((List) list)); } - final Enumerator> product = Linq4j.product(enumerators); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + final Enumerator> product = Linq4j.product(enumerators); + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { return Linq4j.transform(product, List::toArray); } }; @@ -141,7 +144,7 @@ public class SqlFunctions { *

This is a straw man of an implementation whose main goal is to prove * that sequences can be parsed, validated and planned. A real application * will want persistent values for sequences, shared among threads. */ - private static final ThreadLocal> THREAD_SEQUENCES = + private static final ThreadLocal<@Nullable Map> THREAD_SEQUENCES = ThreadLocal.withInitial(HashMap::new); private static final Pattern PATTERN_0_STAR_E = Pattern.compile("0*E"); @@ -170,7 +173,7 @@ private static String toBase64_(byte[] bytes) { } /** SQL FROM_BASE64(string) function. */ - public static ByteString fromBase64(String base64) { + public static @Nullable ByteString fromBase64(String base64) { try { base64 = FROM_BASE64_REGEXP.matcher(base64).replaceAll(""); return new ByteString(Base64.getDecoder().decode(base64)); @@ -180,22 +183,22 @@ public static ByteString fromBase64(String base64) { } /** SQL MD5(string) function. */ - public static @Nonnull String md5(@Nonnull String string) { + public static String md5(String string) { return DigestUtils.md5Hex(string.getBytes(UTF_8)); } /** SQL MD5(string) function for binary string. */ - public static @Nonnull String md5(@Nonnull ByteString string) { + public static String md5(ByteString string) { return DigestUtils.md5Hex(string.getBytes()); } /** SQL SHA1(string) function. */ - public static @Nonnull String sha1(@Nonnull String string) { + public static String sha1(String string) { return DigestUtils.sha1Hex(string.getBytes(UTF_8)); } /** SQL SHA1(string) function for binary string. */ - public static @Nonnull String sha1(@Nonnull ByteString string) { + public static String sha1(ByteString string) { return DigestUtils.sha1Hex(string.getBytes()); } @@ -219,7 +222,7 @@ public static String regexpReplace(String s, String regex, String replacement, /** SQL {@code REGEXP_REPLACE} function with 6 arguments. */ public static String regexpReplace(String s, String regex, String replacement, - int pos, int occurrence, String matchType) { + int pos, int occurrence, @Nullable String matchType) { if (pos < 1 || pos > s.length()) { throw RESOURCE.invalidInputForRegexpReplace(Integer.toString(pos)).ex(); } @@ -230,7 +233,7 @@ public static String regexpReplace(String s, String regex, String replacement, return Unsafe.regexpReplace(s, pattern, replacement, pos, occurrence); } - private static int makeRegexpFlags(String stringFlags) { + private static int makeRegexpFlags(@Nullable String stringFlags) { int flags = 0; if (stringFlags != null) { for (int i = 0; i < stringFlags.length(); ++i) { @@ -302,17 +305,26 @@ public static ByteString substring(ByteString c, int s) { } /** SQL UPPER(string) function. */ - public static String upper(String s) { + public static @PolyNull String upper(@PolyNull String s) { + if (s == null) { + return s; + } return s.toUpperCase(Locale.ROOT); } /** SQL LOWER(string) function. */ - public static String lower(String s) { + public static @PolyNull String lower(@PolyNull String s) { + if (s == null) { + return s; + } return s.toLowerCase(Locale.ROOT); } /** SQL INITCAP(string) function. */ - public static String initcap(String s) { + public static @PolyNull String initcap(@PolyNull String s) { + if (s == null) { + return s; + } // Assumes Alpha as [A-Za-z0-9] // white space is treated as everything else. final int len = s.length(); @@ -396,7 +408,7 @@ public static int difference(String s0, String s1) { } /** SQL LEFT(string, integer) function. */ - public static @Nonnull String left(@Nonnull String s, int n) { + public static String left(String s, int n) { if (n <= 0) { return ""; } @@ -408,7 +420,7 @@ public static int difference(String s0, String s1) { } /** SQL LEFT(ByteString, integer) function. */ - public static @Nonnull ByteString left(@Nonnull ByteString s, int n) { + public static ByteString left(ByteString s, int n) { if (n <= 0) { return ByteString.EMPTY; } @@ -420,7 +432,7 @@ public static int difference(String s0, String s1) { } /** SQL RIGHT(string, integer) function. */ - public static @Nonnull String right(@Nonnull String s, int n) { + public static String right(String s, int n) { if (n <= 0) { return ""; } @@ -432,7 +444,7 @@ public static int difference(String s0, String s1) { } /** SQL RIGHT(ByteString, integer) function. */ - public static @Nonnull ByteString right(@Nonnull ByteString s, int n) { + public static ByteString right(ByteString s, int n) { if (n <= 0) { return ByteString.EMPTY; } @@ -631,7 +643,7 @@ public static boolean eq(BigDecimal b0, BigDecimal b1) { /** SQL = operator applied to Object[] values (neither may be * null). */ - public static boolean eq(Object[] b0, Object[] b1) { + public static boolean eq(@Nullable Object @Nullable [] b0, @Nullable Object @Nullable [] b1) { return Arrays.deepEquals(b0, b1); } @@ -868,45 +880,45 @@ public static int plus(int b0, int b1) { /** SQL + operator applied to int values; left side may be * null. */ - public static Integer plus(Integer b0, int b1) { - return b0 == null ? null : (b0 + b1); + public static @PolyNull Integer plus(@PolyNull Integer b0, int b1) { + return b0 == null ? castNonNull(null) : (b0 + b1); } /** SQL + operator applied to int values; right side may be * null. */ - public static Integer plus(int b0, Integer b1) { - return b1 == null ? null : (b0 + b1); + public static @PolyNull Integer plus(int b0, @PolyNull Integer b1) { + return b1 == null ? castNonNull(null) : (b0 + b1); } /** SQL + operator applied to nullable int values. */ - public static Integer plus(Integer b0, Integer b1) { - return (b0 == null || b1 == null) ? null : (b0 + b1); + public static @PolyNull Integer plus(@PolyNull Integer b0, @PolyNull Integer b1) { + return (b0 == null || b1 == null) ? castNonNull(null) : (b0 + b1); } /** SQL + operator applied to nullable long and int values. */ - public static Long plus(Long b0, Integer b1) { + public static @PolyNull Long plus(@PolyNull Long b0, @PolyNull Integer b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() + b1.longValue()); } /** SQL + operator applied to nullable int and long values. */ - public static Long plus(Integer b0, Long b1) { + public static @PolyNull Long plus(@PolyNull Integer b0, @PolyNull Long b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() + b1.longValue()); } /** SQL + operator applied to BigDecimal values. */ - public static BigDecimal plus(BigDecimal b0, BigDecimal b1) { - return (b0 == null || b1 == null) ? null : b0.add(b1); + public static @PolyNull BigDecimal plus(@PolyNull BigDecimal b0, @PolyNull BigDecimal b1) { + return (b0 == null || b1 == null) ? castNonNull(null) : b0.add(b1); } /** SQL + operator applied to Object values (at least one operand * has ANY type; either may be null). */ - public static Object plusAny(Object b0, Object b1) { + public static @PolyNull Object plusAny(@PolyNull Object b0, @PolyNull Object b1) { if (b0 == null || b1 == null) { - return null; + return castNonNull(null); } if (allAssignable(Number.class, b0, b1)) { @@ -925,45 +937,45 @@ public static int minus(int b0, int b1) { /** SQL - operator applied to int values; left side may be * null. */ - public static Integer minus(Integer b0, int b1) { - return b0 == null ? null : (b0 - b1); + public static @PolyNull Integer minus(@PolyNull Integer b0, int b1) { + return b0 == null ? castNonNull(null) : (b0 - b1); } /** SQL - operator applied to int values; right side may be * null. */ - public static Integer minus(int b0, Integer b1) { - return b1 == null ? null : (b0 - b1); + public static @PolyNull Integer minus(int b0, @PolyNull Integer b1) { + return b1 == null ? castNonNull(null) : (b0 - b1); } /** SQL - operator applied to nullable int values. */ - public static Integer minus(Integer b0, Integer b1) { - return (b0 == null || b1 == null) ? null : (b0 - b1); + public static @PolyNull Integer minus(@PolyNull Integer b0, @PolyNull Integer b1) { + return (b0 == null || b1 == null) ? castNonNull(null) : (b0 - b1); } /** SQL - operator applied to nullable long and int values. */ - public static Long minus(Long b0, Integer b1) { + public static @PolyNull Long minus(@PolyNull Long b0, @PolyNull Integer b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() - b1.longValue()); } /** SQL - operator applied to nullable int and long values. */ - public static Long minus(Integer b0, Long b1) { + public static @PolyNull Long minus(@PolyNull Integer b0, @PolyNull Long b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() - b1.longValue()); } /** SQL - operator applied to BigDecimal values. */ - public static BigDecimal minus(BigDecimal b0, BigDecimal b1) { - return (b0 == null || b1 == null) ? null : b0.subtract(b1); + public static @PolyNull BigDecimal minus(@PolyNull BigDecimal b0, @PolyNull BigDecimal b1) { + return (b0 == null || b1 == null) ? castNonNull(null) : b0.subtract(b1); } /** SQL - operator applied to Object values (at least one operand * has ANY type; either may be null). */ - public static Object minusAny(Object b0, Object b1) { + public static @PolyNull Object minusAny(@PolyNull Object b0, @PolyNull Object b1) { if (b0 == null || b1 == null) { - return null; + return castNonNull(null); } if (allAssignable(Number.class, b0, b1)) { @@ -982,47 +994,47 @@ public static int divide(int b0, int b1) { /** SQL / operator applied to int values; left side may be * null. */ - public static Integer divide(Integer b0, int b1) { - return b0 == null ? null : (b0 / b1); + public static @PolyNull Integer divide(@PolyNull Integer b0, int b1) { + return b0 == null ? castNonNull(null) : (b0 / b1); } /** SQL / operator applied to int values; right side may be * null. */ - public static Integer divide(int b0, Integer b1) { - return b1 == null ? null : (b0 / b1); + public static @PolyNull Integer divide(int b0, @PolyNull Integer b1) { + return b1 == null ? castNonNull(null) : (b0 / b1); } /** SQL / operator applied to nullable int values. */ - public static Integer divide(Integer b0, Integer b1) { - return (b0 == null || b1 == null) ? null : (b0 / b1); + public static @PolyNull Integer divide(@PolyNull Integer b0, @PolyNull Integer b1) { + return (b0 == null || b1 == null) ? castNonNull(null) : (b0 / b1); } /** SQL / operator applied to nullable long and int values. */ - public static Long divide(Long b0, Integer b1) { + public static @PolyNull Long divide(Long b0, @PolyNull Integer b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() / b1.longValue()); } /** SQL / operator applied to nullable int and long values. */ - public static Long divide(Integer b0, Long b1) { + public static @PolyNull Long divide(@PolyNull Integer b0, @PolyNull Long b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() / b1.longValue()); } /** SQL / operator applied to BigDecimal values. */ - public static BigDecimal divide(BigDecimal b0, BigDecimal b1) { + public static @PolyNull BigDecimal divide(@PolyNull BigDecimal b0, @PolyNull BigDecimal b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : b0.divide(b1, MathContext.DECIMAL64); } /** SQL / operator applied to Object values (at least one operand * has ANY type; either may be null). */ - public static Object divideAny(Object b0, Object b1) { + public static @PolyNull Object divideAny(@PolyNull Object b0, @PolyNull Object b1) { if (b0 == null || b1 == null) { - return null; + return castNonNull(null); } if (allAssignable(Number.class, b0, b1)) { @@ -1051,45 +1063,45 @@ public static int multiply(int b0, int b1) { /** SQL * operator applied to int values; left side may be * null. */ - public static Integer multiply(Integer b0, int b1) { - return b0 == null ? null : (b0 * b1); + public static @PolyNull Integer multiply(@PolyNull Integer b0, int b1) { + return b0 == null ? castNonNull(null) : (b0 * b1); } /** SQL * operator applied to int values; right side may be * null. */ - public static Integer multiply(int b0, Integer b1) { - return b1 == null ? null : (b0 * b1); + public static @PolyNull Integer multiply(int b0, @PolyNull Integer b1) { + return b1 == null ? castNonNull(null) : (b0 * b1); } /** SQL * operator applied to nullable int values. */ - public static Integer multiply(Integer b0, Integer b1) { - return (b0 == null || b1 == null) ? null : (b0 * b1); + public static @PolyNull Integer multiply(@PolyNull Integer b0, @PolyNull Integer b1) { + return (b0 == null || b1 == null) ? castNonNull(null) : (b0 * b1); } /** SQL * operator applied to nullable long and int values. */ - public static Long multiply(Long b0, Integer b1) { + public static @PolyNull Long multiply(@PolyNull Long b0, @PolyNull Integer b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() * b1.longValue()); } /** SQL * operator applied to nullable int and long values. */ - public static Long multiply(Integer b0, Long b1) { + public static @PolyNull Long multiply(@PolyNull Integer b0, @PolyNull Long b1) { return (b0 == null || b1 == null) - ? null + ? castNonNull(null) : (b0.longValue() * b1.longValue()); } /** SQL * operator applied to BigDecimal values. */ - public static BigDecimal multiply(BigDecimal b0, BigDecimal b1) { - return (b0 == null || b1 == null) ? null : b0.multiply(b1); + public static @PolyNull BigDecimal multiply(@PolyNull BigDecimal b0, @PolyNull BigDecimal b1) { + return (b0 == null || b1 == null) ? castNonNull(null) : b0.multiply(b1); } /** SQL * operator applied to Object values (at least one operand * has ANY type; either may be null). */ - public static Object multiplyAny(Object b0, Object b1) { + public static @PolyNull Object multiplyAny(@PolyNull Object b0, @PolyNull Object b1) { if (b0 == null || b1 == null) { - return null; + return castNonNull(null); } if (allAssignable(Number.class, b0, b1)) { @@ -1892,13 +1904,13 @@ public static int toInt(java.util.Date v, TimeZone timeZone) { return (int) (toLong(v, timeZone) / DateTimeUtils.MILLIS_PER_DAY); } - public static Integer toIntOptional(java.util.Date v) { - return v == null ? null : toInt(v); + public static @PolyNull Integer toIntOptional(java.util.@PolyNull Date v) { + return v == null ? castNonNull(null) : toInt(v); } - public static Integer toIntOptional(java.util.Date v, TimeZone timeZone) { + public static @PolyNull Integer toIntOptional(java.util.@PolyNull Date v, TimeZone timeZone) { return v == null - ? null + ? castNonNull(null) : toInt(v, timeZone); } @@ -1914,8 +1926,8 @@ public static int toInt(java.sql.Time v) { return (int) (toLong(v) % DateTimeUtils.MILLIS_PER_DAY); } - public static Integer toIntOptional(java.sql.Time v) { - return v == null ? null : toInt(v); + public static @PolyNull Integer toIntOptional(java.sql.@PolyNull Time v) { + return v == null ? castNonNull(null) : toInt(v); } public static int toInt(String s) { @@ -1934,8 +1946,8 @@ public static int toInt(Object o) { : (Integer) cannotConvert(o, int.class); } - public static Integer toIntOptional(Object o) { - return o == null ? null : toInt(o); + public static @PolyNull Integer toIntOptional(@PolyNull Object o) { + return o == null ? castNonNull(null) : toInt(o); } /** Converts the Java type used for UDF parameters of SQL TIMESTAMP type @@ -1954,15 +1966,15 @@ public static long toLong(java.util.Date v, TimeZone timeZone) { } // mainly intended for java.sql.Timestamp but works for other dates also - public static Long toLongOptional(java.util.Date v) { - return v == null ? null : toLong(v, LOCAL_TZ); + public static @PolyNull Long toLongOptional(java.util.@PolyNull Date v) { + return v == null ? castNonNull(null) : toLong(v, LOCAL_TZ); } - public static Long toLongOptional(Timestamp v, TimeZone timeZone) { + public static @PolyNull Long toLongOptional(@PolyNull Timestamp v, TimeZone timeZone) { if (v == null) { - return null; + return castNonNull(null); } - return toLong(v, LOCAL_TZ); + return toLong(v, timeZone); } public static long toLong(String s) { @@ -1984,8 +1996,8 @@ public static long toLong(Object o) { : (Long) cannotConvert(o, long.class); } - public static Long toLongOptional(Object o) { - return o == null ? null : toLong(o); + public static @PolyNull Long toLongOptional(@PolyNull Object o) { + return o == null ? castNonNull(null) : toLong(o); } public static float toFloat(String s) { @@ -2044,8 +2056,8 @@ public static java.sql.Date internalToDate(int v) { } /** As {@link #internalToDate(int)} but allows nulls. */ - public static java.sql.Date internalToDate(Integer v) { - return v == null ? null : internalToDate(v.intValue()); + public static java.sql.@PolyNull Date internalToDate(@PolyNull Integer v) { + return v == null ? castNonNull(null) : internalToDate(v.intValue()); } /** Converts the internal representation of a SQL TIME (int) to the Java @@ -2054,19 +2066,19 @@ public static java.sql.Time internalToTime(int v) { return new java.sql.Time(v - LOCAL_TZ.getOffset(v)); } - public static java.sql.Time internalToTime(Integer v) { - return v == null ? null : internalToTime(v.intValue()); + public static java.sql.@PolyNull Time internalToTime(@PolyNull Integer v) { + return v == null ? castNonNull(null) : internalToTime(v.intValue()); } - public static Integer toTimeWithLocalTimeZone(String v) { - return v == null ? null : new TimeWithTimeZoneString(v) + public static @PolyNull Integer toTimeWithLocalTimeZone(@PolyNull String v) { + return v == null ? castNonNull(null) : new TimeWithTimeZoneString(v) .withTimeZone(DateTimeUtils.UTC_ZONE) .getLocalTimeString() .getMillisOfDay(); } - public static Integer toTimeWithLocalTimeZone(String v, TimeZone timeZone) { - return v == null ? null : new TimeWithTimeZoneString(v + " " + timeZone.getID()) + public static @PolyNull Integer toTimeWithLocalTimeZone(@PolyNull String v, TimeZone timeZone) { + return v == null ? castNonNull(null) : new TimeWithTimeZoneString(v + " " + timeZone.getID()) .withTimeZone(DateTimeUtils.UTC_ZONE) .getLocalTimeString() .getMillisOfDay(); @@ -2108,8 +2120,8 @@ public static java.sql.Timestamp internalToTimestamp(long v) { return new java.sql.Timestamp(v - LOCAL_TZ.getOffset(v)); } - public static java.sql.Timestamp internalToTimestamp(Long v) { - return v == null ? null : internalToTimestamp(v.longValue()); + public static java.sql.@PolyNull Timestamp internalToTimestamp(@PolyNull Long v) { + return v == null ? castNonNull(null) : internalToTimestamp(v.longValue()); } public static int timestampWithLocalTimeZoneToDate(long v, TimeZone timeZone) { @@ -2191,15 +2203,16 @@ public static int unixDate(int v) { return v; } - public static Long toTimestampWithLocalTimeZone(String v) { - return v == null ? null : new TimestampWithTimeZoneString(v) + public static @PolyNull Long toTimestampWithLocalTimeZone(@PolyNull String v) { + return v == null ? castNonNull(null) : new TimestampWithTimeZoneString(v) .withTimeZone(DateTimeUtils.UTC_ZONE) .getLocalTimestampString() .getMillisSinceEpoch(); } - public static Long toTimestampWithLocalTimeZone(String v, TimeZone timeZone) { - return v == null ? null : new TimestampWithTimeZoneString(v + " " + timeZone.getID()) + public static @PolyNull Long toTimestampWithLocalTimeZone(@PolyNull String v, TimeZone timeZone) { + return v == null ? castNonNull(null) + : new TimestampWithTimeZoneString(v + " " + timeZone.getID()) .withTimeZone(DateTimeUtils.UTC_ZONE) .getLocalTimestampString() .getMillisSinceEpoch(); @@ -2208,9 +2221,9 @@ public static Long toTimestampWithLocalTimeZone(String v, TimeZone timeZone) { // Don't need shortValueOf etc. - Short.valueOf is sufficient. /** Helper for CAST(... AS VARCHAR(maxLength)). */ - public static String truncate(String s, int maxLength) { + public static @PolyNull String truncate(@PolyNull String s, int maxLength) { if (s == null) { - return null; + return s; } else if (s.length() > maxLength) { return s.substring(0, maxLength); } else { @@ -2219,9 +2232,9 @@ public static String truncate(String s, int maxLength) { } /** Helper for CAST(... AS CHAR(maxLength)). */ - public static String truncateOrPad(String s, int maxLength) { + public static @PolyNull String truncateOrPad(@PolyNull String s, int maxLength) { if (s == null) { - return null; + return s; } else { final int length = s.length(); if (length > maxLength) { @@ -2233,9 +2246,9 @@ public static String truncateOrPad(String s, int maxLength) { } /** Helper for CAST(... AS VARBINARY(maxLength)). */ - public static ByteString truncate(ByteString s, int maxLength) { + public static @PolyNull ByteString truncate(@PolyNull ByteString s, int maxLength) { if (s == null) { - return null; + return s; } else if (s.length() > maxLength) { return s.substring(0, maxLength); } else { @@ -2244,9 +2257,9 @@ public static ByteString truncate(ByteString s, int maxLength) { } /** Helper for CAST(... AS BINARY(maxLength)). */ - public static ByteString truncateOrPad(ByteString s, int maxLength) { + public static @PolyNull ByteString truncateOrPad(@PolyNull ByteString s, int maxLength) { if (s == null) { - return null; + return s; } else { final int length = s.length(); if (length > maxLength) { @@ -2467,13 +2480,13 @@ public static TimeZone timeZone(DataContext root) { /** SQL {@code USER} function. */ @Deterministic public static String user(DataContext root) { - return Objects.requireNonNull(DataContext.Variable.USER.get(root)); + return requireNonNull(DataContext.Variable.USER.get(root)); } /** SQL {@code SYSTEM_USER} function. */ @Deterministic public static String systemUser(DataContext root) { - return Objects.requireNonNull(DataContext.Variable.SYSTEM_USER.get(root)); + return requireNonNull(DataContext.Variable.SYSTEM_USER.get(root)); } @NonDeterministic @@ -2494,7 +2507,7 @@ public static String replace(String s, String search, String replacement) { /** Helper for "array element reference". Caller has already ensured that * array and index are not null. Index is 1-based, per SQL. */ - public static Object arrayItem(List list, int item) { + public static @Nullable Object arrayItem(List list, int item) { if (item < 1 || item > list.size()) { return null; } @@ -2503,14 +2516,14 @@ public static Object arrayItem(List list, int item) { /** Helper for "map element reference". Caller has already ensured that * array and index are not null. Index is 1-based, per SQL. */ - public static Object mapItem(Map map, Object item) { + public static @Nullable Object mapItem(Map map, Object item) { return map.get(item); } /** Implements the {@code [ ... ]} operator on an object whose type is not * known until runtime. */ - public static Object item(Object object, Object index) { + public static @Nullable Object item(Object object, Object index) { if (object instanceof Map) { return mapItem((Map) object, index); } @@ -2528,7 +2541,7 @@ public static Object item(Object object, Object index) { } /** As {@link #arrayItem} method, but allows array to be nullable. */ - public static Object arrayItemOptional(List list, int item) { + public static @Nullable Object arrayItemOptional(@Nullable List list, int item) { if (list == null) { return null; } @@ -2536,7 +2549,7 @@ public static Object arrayItemOptional(List list, int item) { } /** As {@link #mapItem} method, but allows map to be nullable. */ - public static Object mapItemOptional(Map map, Object item) { + public static @Nullable Object mapItemOptional(@Nullable Map map, Object item) { if (map == null) { return null; } @@ -2544,7 +2557,7 @@ public static Object mapItemOptional(Map map, Object item) { } /** As {@link #item} method, but allows object to be nullable. */ - public static Object itemOptional(Object object, Object index) { + public static @Nullable Object itemOptional(@Nullable Object object, Object index) { if (object == null) { return null; } @@ -2553,34 +2566,34 @@ public static Object itemOptional(Object object, Object index) { /** NULL → FALSE, FALSE → FALSE, TRUE → TRUE. */ - public static boolean isTrue(Boolean b) { + public static boolean isTrue(@Nullable Boolean b) { return b != null && b; } /** NULL → FALSE, FALSE → TRUE, TRUE → FALSE. */ - public static boolean isFalse(Boolean b) { + public static boolean isFalse(@Nullable Boolean b) { return b != null && !b; } /** NULL → TRUE, FALSE → TRUE, TRUE → FALSE. */ - public static boolean isNotTrue(Boolean b) { + public static boolean isNotTrue(@Nullable Boolean b) { return b == null || !b; } /** NULL → TRUE, FALSE → FALSE, TRUE → TRUE. */ - public static boolean isNotFalse(Boolean b) { + public static boolean isNotFalse(@Nullable Boolean b) { return b == null || b; } /** NULL → NULL, FALSE → TRUE, TRUE → FALSE. */ - public static Boolean not(Boolean b) { - return (b == null) ? null : !b; + public static @PolyNull Boolean not(@PolyNull Boolean b) { + return b == null ? castNonNull(null) : !b; } /** Converts a JDBC array to a list. */ - public static List arrayToList(final java.sql.Array a) { + public static @PolyNull List arrayToList(final java.sql.@PolyNull Array a) { if (a == null) { - return null; + return castNonNull(null); } try { return Primitive.asList(a.getArray()); @@ -2602,7 +2615,8 @@ public static long sequenceNextValue(String key) { } private static AtomicLong getAtomicLong(String key) { - final Map map = THREAD_SEQUENCES.get(); + final Map map = requireNonNull(THREAD_SEQUENCES.get(), + "THREAD_SEQUENCES.get()"); AtomicLong atomic = map.get(key); if (atomic == null) { atomic = new AtomicLong(); @@ -2621,7 +2635,7 @@ public static List slice(List list) { } /** Support the ELEMENT function. */ - public static Object element(List list) { + public static @Nullable Object element(List list) { switch (list.size()) { case 0: return null; @@ -2633,7 +2647,7 @@ public static Object element(List list) { } /** Support the MEMBER OF function. */ - public static boolean memberOf(Object object, Collection collection) { + public static boolean memberOf(@Nullable Object object, Collection collection) { return collection.contains(object); } @@ -2949,7 +2963,8 @@ public static int subtractMonths(long t0, long t1) { * {@link org.apache.calcite.adapter.enumerable.JavaRowFormat}. */ @Experimental - public static Object structAccess(Object structObject, int index, String fieldName) { + public static @Nullable Object structAccess(@Nullable Object structObject, int index, + @Nullable String fieldName) { if (structObject == null) { return null; } @@ -2962,6 +2977,7 @@ public static Object structAccess(Object structObject, int index, String fieldNa return ((Row) structObject).getObject(index); } else { Class beanClass = structObject.getClass(); + requireNonNull(fieldName, "fieldName"); try { Field structField; if (fieldName == null) { diff --git a/core/src/main/java/org/apache/calcite/runtime/TrustAllSslSocketFactory.java b/core/src/main/java/org/apache/calcite/runtime/TrustAllSslSocketFactory.java index ba09e5ac3699..9bab3dde09e6 100644 --- a/core/src/main/java/org/apache/calcite/runtime/TrustAllSslSocketFactory.java +++ b/core/src/main/java/org/apache/calcite/runtime/TrustAllSslSocketFactory.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.runtime; + +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.net.InetAddress; import java.net.Socket; @@ -26,6 +29,10 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Socket factory that trusts all SSL connections. */ @@ -46,7 +53,7 @@ protected TrustAllSslSocketFactory() { } catch (Exception e) { e.printStackTrace(); } - this.sslSocketFactory = factory; + this.sslSocketFactory = requireNonNull(factory, "sslSocketFactory"); } @Override public Socket createSocket() throws IOException { @@ -97,7 +104,7 @@ public static SSLSocketFactory getDefaultSSLSocketFactory() { * * @return SSLSocketFactory */ - public static SSLSocketFactory createSSLSocketFactory() { + public static @Nullable SSLSocketFactory createSSLSocketFactory() { SSLSocketFactory sslsocketfactory = null; TrustManager[] trustAllCerts = {new DummyTrustManager()}; try { @@ -114,7 +121,7 @@ public static SSLSocketFactory createSSLSocketFactory() { * certificates. */ private static class DummyTrustManager implements X509TrustManager { @Override public X509Certificate[] getAcceptedIssuers() { - return null; + return castNonNull(null); } @Override public void checkClientTrusted( diff --git a/core/src/main/java/org/apache/calcite/runtime/Utilities.java b/core/src/main/java/org/apache/calcite/runtime/Utilities.java index b09c91a5c6cd..458a07c8987c 100644 --- a/core/src/main/java/org/apache/calcite/runtime/Utilities.java +++ b/core/src/main/java/org/apache/calcite/runtime/Utilities.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.runtime; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.text.Collator; import java.util.Comparator; import java.util.Iterator; @@ -36,13 +38,13 @@ protected Utilities() { // CHECKSTYLE: IGNORE 1 /** @deprecated Use {@link java.util.Objects#equals}. */ @Deprecated // to be removed before 2.0 - public static boolean equal(Object o0, Object o1) { + public static boolean equal(@Nullable Object o0, @Nullable Object o1) { // Same as java.lang.Objects.equals (JDK 1.7 and later) // and com.google.common.base.Objects.equal return Objects.equals(o0, o1); } - public static int hash(Object v) { + public static int hash(@Nullable Object v) { return v == null ? 0 : v.hashCode(); } @@ -130,7 +132,7 @@ public static int hash(int h, double v) { return hash(h, Double.hashCode(v)); } - public static int hash(int h, Object v) { + public static int hash(int h, @Nullable Object v) { return h * 31 + (v == null ? 1 : v.hashCode()); } @@ -200,7 +202,7 @@ public static int compare(Comparable v0, Comparable v1) { return v0.compareTo(v1); } - public static int compareNullsFirst(Comparable v0, Comparable v1) { + public static int compareNullsFirst(@Nullable Comparable v0, @Nullable Comparable v1) { //noinspection unchecked return v0 == v1 ? 0 : v0 == null ? -1 @@ -208,7 +210,7 @@ public static int compareNullsFirst(Comparable v0, Comparable v1) { : v0.compareTo(v1); } - public static int compareNullsLast(Comparable v0, Comparable v1) { + public static int compareNullsLast(@Nullable Comparable v0, @Nullable Comparable v1) { //noinspection unchecked return v0 == v1 ? 0 : v0 == null ? 1 @@ -216,12 +218,14 @@ public static int compareNullsLast(Comparable v0, Comparable v1) { : v0.compareTo(v1); } - public static int compare(Comparable v0, Comparable v1, Comparator comparator) { + public static int compare(@Nullable Comparable v0, @Nullable Comparable v1, + Comparator comparator) { //noinspection unchecked return comparator.compare(v0, v1); } - public static int compareNullsFirst(Comparable v0, Comparable v1, Comparator comparator) { + public static int compareNullsFirst(@Nullable Comparable v0, @Nullable Comparable v1, + Comparator comparator) { //noinspection unchecked return v0 == v1 ? 0 : v0 == null ? -1 @@ -229,7 +233,8 @@ public static int compareNullsFirst(Comparable v0, Comparable v1, Comparator com : comparator.compare(v0, v1); } - public static int compareNullsLast(Comparable v0, Comparable v1, Comparator comparator) { + public static int compareNullsLast(@Nullable Comparable v0, @Nullable Comparable v1, + Comparator comparator) { //noinspection unchecked return v0 == v1 ? 0 : v0 == null ? 1 diff --git a/core/src/main/java/org/apache/calcite/runtime/XmlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/XmlFunctions.java index 67783744a0e6..13d270fe7be2 100644 --- a/core/src/main/java/org/apache/calcite/runtime/XmlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/XmlFunctions.java @@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; @@ -47,16 +48,19 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * A collection of functions used in Xml processing. */ public class XmlFunctions { - private static final ThreadLocal XPATH_FACTORY = + private static final ThreadLocal<@Nullable XPathFactory> XPATH_FACTORY = ThreadLocal.withInitial(XPathFactory::newInstance); - private static final ThreadLocal TRANSFORMER_FACTORY = + private static final ThreadLocal<@Nullable TransformerFactory> TRANSFORMER_FACTORY = ThreadLocal.withInitial(TransformerFactory::newInstance); private static final Pattern VALID_NAMESPACE_PATTERN = Pattern @@ -67,18 +71,21 @@ public class XmlFunctions { private XmlFunctions() { } - public static String extractValue(String input, String xpath) { + public static @Nullable String extractValue(@Nullable String input, @Nullable String xpath) { if (input == null || xpath == null) { return null; } try { - XPathExpression xpathExpression = XPATH_FACTORY.get().newXPath().compile(xpath); + XPathExpression xpathExpression = castNonNull(XPATH_FACTORY.get()).newXPath().compile(xpath); try { NodeList nodes = (NodeList) xpathExpression .evaluate(new InputSource(new StringReader(input)), XPathConstants.NODESET); - List result = new ArrayList<>(); + List<@Nullable String> result = new ArrayList<>(); for (int i = 0; i < nodes.getLength(); i++) { - result.add(nodes.item(i).getFirstChild().getTextContent()); + Node item = castNonNull(nodes.item(i)); + Node firstChild = requireNonNull(item.getFirstChild(), + () -> "firstChild of node " + item); + result.add(firstChild.getTextContent()); } return StringUtils.join(result, " "); } catch (XPathExpressionException e) { @@ -89,14 +96,15 @@ public static String extractValue(String input, String xpath) { } } - public static String xmlTransform(String xml, String xslt) { + public static @Nullable String xmlTransform(@Nullable String xml, @Nullable String xslt) { if (xml == null || xslt == null) { return null; } try { final Source xsltSource = new StreamSource(new StringReader(xslt)); final Source xmlSource = new StreamSource(new StringReader(xml)); - final Transformer transformer = TRANSFORMER_FACTORY.get().newTransformer(xsltSource); + final Transformer transformer = castNonNull(TRANSFORMER_FACTORY.get()) + .newTransformer(xsltSource); final StringWriter writer = new StringWriter(); final StreamResult result = new StreamResult(writer); transformer.transform(xmlSource, result); @@ -108,16 +116,17 @@ public static String xmlTransform(String xml, String xslt) { } } - public static String extractXml(String xml, String xpath) { + public static @Nullable String extractXml(@Nullable String xml, @Nullable String xpath) { return extractXml(xml, xpath, null); } - public static String extractXml(String xml, String xpath, String namespace) { + public static @Nullable String extractXml(@Nullable String xml, @Nullable String xpath, + @Nullable String namespace) { if (xml == null || xpath == null) { return null; } try { - XPath xPath = XPATH_FACTORY.get().newXPath(); + XPath xPath = castNonNull(XPATH_FACTORY.get()).newXPath(); if (namespace != null) { xPath.setNamespaceContext(extractNamespaceContext(namespace)); @@ -130,7 +139,7 @@ public static String extractXml(String xml, String xpath, String namespace) { NodeList nodes = (NodeList) xpathExpression .evaluate(new InputSource(new StringReader(xml)), XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { - result.add(convertNodeToString(nodes.item(i))); + result.add(convertNodeToString(castNonNull(nodes.item(i)))); } return StringUtils.join(result, ""); } catch (XPathExpressionException e) { @@ -143,16 +152,17 @@ public static String extractXml(String xml, String xpath, String namespace) { } } - public static Integer existsNode(String xml, String xpath) { + public static @Nullable Integer existsNode(@Nullable String xml, @Nullable String xpath) { return existsNode(xml, xpath, null); } - public static Integer existsNode(String xml, String xpath, String namespace) { + public static @Nullable Integer existsNode(@Nullable String xml, @Nullable String xpath, + @Nullable String namespace) { if (xml == null || xpath == null) { return null; } try { - XPath xPath = XPATH_FACTORY.get().newXPath(); + XPath xPath = castNonNull(XPATH_FACTORY.get()).newXPath(); if (namespace != null) { xPath.setNamespaceContext(extractNamespaceContext(namespace)); } @@ -185,14 +195,14 @@ private static SimpleNamespaceContext extractNamespaceContext(String namespace) Map namespaceMap = new HashMap<>(); Matcher matcher = EXTRACT_NAMESPACE_PATTERN.matcher(namespace); while (matcher.find()) { - namespaceMap.put(matcher.group(1), matcher.group(3)); + namespaceMap.put(castNonNull(matcher.group(1)), castNonNull(matcher.group(3))); } return new SimpleNamespaceContext(namespaceMap); } private static String convertNodeToString(Node node) throws TransformerException { StringWriter writer = new StringWriter(); - Transformer transformer = TRANSFORMER_FACTORY.get().newTransformer(); + Transformer transformer = castNonNull(TRANSFORMER_FACTORY.get()).newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(new DOMSource(node), new StreamResult(writer)); return writer.toString(); diff --git a/core/src/main/java/org/apache/calcite/schema/FilterableTable.java b/core/src/main/java/org/apache/calcite/schema/FilterableTable.java index 8340a3229f08..718fcdba84b0 100644 --- a/core/src/main/java/org/apache/calcite/schema/FilterableTable.java +++ b/core/src/main/java/org/apache/calcite/schema/FilterableTable.java @@ -20,6 +20,8 @@ import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -38,5 +40,5 @@ public interface FilterableTable extends Table { * If it cannot implement a filter, it should leave it in the list. * Any filters remaining will be implemented by the consuming Calcite * operator. */ - Enumerable scan(DataContext root, List filters); + Enumerable<@Nullable Object[]> scan(DataContext root, List filters); } diff --git a/core/src/main/java/org/apache/calcite/schema/ModifiableTable.java b/core/src/main/java/org/apache/calcite/schema/ModifiableTable.java index 487becbea0d9..1037baf78aea 100644 --- a/core/src/main/java/org/apache/calcite/schema/ModifiableTable.java +++ b/core/src/main/java/org/apache/calcite/schema/ModifiableTable.java @@ -23,6 +23,8 @@ import org.apache.calcite.rel.core.TableModify; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.List; @@ -37,7 +39,7 @@ public interface ModifiableTable extends QueryableTable { /** Returns the modifiable collection. * Modifying the collection will change the table's contents. */ - Collection getModifiableCollection(); + @Nullable Collection getModifiableCollection(); /** Creates a relational expression that modifies this table. */ TableModify toModificationRel( @@ -46,7 +48,7 @@ TableModify toModificationRel( Prepare.CatalogReader catalogReader, RelNode child, TableModify.Operation operation, - List updateColumnList, - List sourceExpressionList, + @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened); } diff --git a/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java b/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java index adf3b2b12371..366536312fe0 100644 --- a/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java +++ b/core/src/main/java/org/apache/calcite/schema/ProjectableFilterableTable.java @@ -20,6 +20,8 @@ import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.rex.RexNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -51,11 +53,11 @@ public interface ProjectableFilterableTable extends Table { * @param filters Mutable list of filters. The method should keep in the * list any filters that it cannot apply. * @param projects List of projects. Each is the 0-based ordinal of the column - * to project. + * to project. Null means "project all columns". * @return Enumerable over all rows that match the accepted filters, returning * for each row an array of column values, one value for each ordinal in * {@code projects}. */ - Enumerable scan(DataContext root, List filters, - int[] projects); + Enumerable<@Nullable Object[]> scan(DataContext root, List filters, + int @Nullable [] projects); } diff --git a/core/src/main/java/org/apache/calcite/schema/ScannableTable.java b/core/src/main/java/org/apache/calcite/schema/ScannableTable.java index 0c75478a19d1..31b3c6896c88 100644 --- a/core/src/main/java/org/apache/calcite/schema/ScannableTable.java +++ b/core/src/main/java/org/apache/calcite/schema/ScannableTable.java @@ -19,6 +19,8 @@ import org.apache.calcite.DataContext; import org.apache.calcite.linq4j.Enumerable; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Table that can be scanned without creating an intermediate relational * expression. @@ -26,5 +28,5 @@ public interface ScannableTable extends Table { /** Returns an enumerator over the rows in this Table. Each row is represented * as an array of its column values. */ - Enumerable scan(DataContext root); + Enumerable<@Nullable Object[]> scan(DataContext root); } diff --git a/core/src/main/java/org/apache/calcite/schema/Schema.java b/core/src/main/java/org/apache/calcite/schema/Schema.java index 7dee0c3b89d8..fa5994379d62 100644 --- a/core/src/main/java/org/apache/calcite/schema/Schema.java +++ b/core/src/main/java/org/apache/calcite/schema/Schema.java @@ -19,6 +19,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.rel.type.RelProtoDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.Set; @@ -60,7 +62,7 @@ public interface Schema { * @param name Table name * @return Table, or null */ - Table getTable(String name); + @Nullable Table getTable(String name); /** * Returns the names of the tables in this schema. @@ -75,7 +77,7 @@ public interface Schema { * @param name Table name * @return Table, or null */ - RelProtoDataType getType(String name); + @Nullable RelProtoDataType getType(String name); /** * Returns the names of the types in this schema. @@ -106,7 +108,7 @@ public interface Schema { * @param name Sub-schema name * @return Sub-schema with a given name, or null */ - Schema getSubSchema(String name); + @Nullable Schema getSubSchema(String name); /** * Returns the names of this schema's child schemas. @@ -123,7 +125,7 @@ public interface Schema { * @param name Name of this schema * @return Expression by which this schema can be referenced in generated code */ - Expression getExpression(SchemaPlus parentSchema, String name); + Expression getExpression(@Nullable SchemaPlus parentSchema, String name); /** Returns whether the user is allowed to create new tables, functions * and sub-schemas in this schema, in addition to those returned automatically diff --git a/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java b/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java index b57da5212060..70526032b0bb 100644 --- a/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java +++ b/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Extension to the {@link Schema} interface. * @@ -46,7 +48,7 @@ public interface SchemaPlus extends Schema { /** * Returns the parent schema, or null if this schema has no parent. */ - SchemaPlus getParentSchema(); + @Nullable SchemaPlus getParentSchema(); /** * Returns the name of this schema. @@ -57,7 +59,7 @@ public interface SchemaPlus extends Schema { String getName(); // override with stricter return - @Override SchemaPlus getSubSchema(String name); + @Override @Nullable SchemaPlus getSubSchema(String name); /** Adds a schema as a sub-schema of this schema, and returns the wrapped * object. */ @@ -78,7 +80,7 @@ public interface SchemaPlus extends Schema { @Override boolean isMutable(); /** Returns an underlying object. */ - T unwrap(Class clazz); + @Nullable T unwrap(Class clazz); void setPath(ImmutableList> path); diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java b/core/src/main/java/org/apache/calcite/schema/Schemas.java index 1f9ba5391f5c..f857ba3fc4f7 100644 --- a/core/src/main/java/org/apache/calcite/schema/Schemas.java +++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java @@ -46,6 +46,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.sql.Connection; import java.util.AbstractList; @@ -55,10 +57,11 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import static org.apache.calcite.jdbc.CalciteSchema.LatticeEntry; +import static java.util.Objects.requireNonNull; + /** * Utility functions for schemas. */ @@ -68,7 +71,7 @@ private Schemas() { throw new AssertionError("no instances!"); } - public static CalciteSchema.FunctionEntry resolve( + public static CalciteSchema.@Nullable FunctionEntry resolve( RelDataTypeFactory typeFactory, String name, Collection functionEntries, @@ -181,7 +184,7 @@ public static Expression tableExpression(SchemaPlus schema, Type elementType, } public static DataContext createDataContext( - Connection connection, SchemaPlus rootSchema) { + Connection connection, @Nullable SchemaPlus rootSchema) { return new DummyDataContext((CalciteConnection) connection, rootSchema); } @@ -198,8 +201,13 @@ public static Queryable queryable(DataContext root, Class clazz, SchemaPlus schema = root.getRootSchema(); for (Iterator iterator = names.iterator();;) { String name = iterator.next(); + requireNonNull(schema, "schema"); if (iterator.hasNext()) { - schema = schema.getSubSchema(name); + SchemaPlus next = schema.getSubSchema(name); + if (next == null) { + throw new IllegalArgumentException("schema " + name + " is not found in " + schema); + } + schema = next; } else { return queryable(root, schema, clazz, name); } @@ -209,13 +217,17 @@ public static Queryable queryable(DataContext root, Class clazz, /** Returns a {@link Queryable}, given a schema and table name. */ public static Queryable queryable(DataContext root, SchemaPlus schema, Class clazz, String tableName) { - QueryableTable table = (QueryableTable) schema.getTable(tableName); - return table.asQueryable(root.getQueryProvider(), schema, tableName); + QueryableTable table = (QueryableTable) requireNonNull( + schema.getTable(tableName), + () -> "table " + tableName + " is not found in " + schema); + QueryProvider queryProvider = requireNonNull(root.getQueryProvider(), + "root.getQueryProvider()"); + return table.asQueryable(queryProvider, schema, tableName); } /** Returns an {@link org.apache.calcite.linq4j.Enumerable} over the rows of * a given table, representing each row as an object array. */ - public static Enumerable enumerable(final ScannableTable table, + public static Enumerable<@Nullable Object[]> enumerable(final ScannableTable table, final DataContext root) { return table.scan(root); } @@ -223,7 +235,7 @@ public static Enumerable enumerable(final ScannableTable table, /** Returns an {@link org.apache.calcite.linq4j.Enumerable} over the rows of * a given table, not applying any filters, representing each row as an object * array. */ - public static Enumerable enumerable(final FilterableTable table, + public static Enumerable<@Nullable Object[]> enumerable(final FilterableTable table, final DataContext root) { return table.scan(root, new ArrayList<>()); } @@ -231,10 +243,11 @@ public static Enumerable enumerable(final FilterableTable table, /** Returns an {@link org.apache.calcite.linq4j.Enumerable} over the rows of * a given table, not applying any filters and projecting all columns, * representing each row as an object array. */ - public static Enumerable enumerable( + public static Enumerable<@Nullable Object[]> enumerable( final ProjectableFilterableTable table, final DataContext root) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); return table.scan(root, new ArrayList<>(), - identity(table.getRowType(root.getTypeFactory()).getFieldCount())); + identity(table.getRowType(typeFactory).getFieldCount())); } private static int[] identity(int count) { @@ -248,13 +261,18 @@ private static int[] identity(int count) { /** Returns an {@link org.apache.calcite.linq4j.Enumerable} over object * arrays, given a fully-qualified table name which leads to a * {@link ScannableTable}. */ - public static Table table(DataContext root, String... names) { + public static @Nullable Table table(DataContext root, String... names) { SchemaPlus schema = root.getRootSchema(); final List nameList = Arrays.asList(names); for (Iterator iterator = nameList.iterator();;) { String name = iterator.next(); + requireNonNull(schema, "schema"); if (iterator.hasNext()) { - schema = schema.getSubSchema(name); + SchemaPlus next = schema.getSubSchema(name); + if (next == null) { + throw new IllegalArgumentException("schema " + name + " is not found in " + schema); + } + schema = next; } else { return schema.getTable(name); } @@ -264,7 +282,7 @@ public static Table table(DataContext root, String... names) { /** Parses and validates a SQL query. For use within Calcite only. */ public static CalcitePrepare.ParseResult parse( final CalciteConnection connection, final CalciteSchema schema, - final List schemaPath, final String sql) { + final @Nullable List schemaPath, final String sql) { final CalcitePrepare prepare = CalcitePrepare.DEFAULT_FACTORY.apply(); final ImmutableMap propValues = ImmutableMap.of(); @@ -299,8 +317,8 @@ public static CalcitePrepare.ConvertResult convert( /** Analyzes a view. For use within Calcite only. */ public static CalcitePrepare.AnalyzeViewResult analyzeView( final CalciteConnection connection, final CalciteSchema schema, - final List schemaPath, final String viewSql, - List viewPath, boolean fail) { + final @Nullable List schemaPath, final String viewSql, + @Nullable List viewPath, boolean fail) { final CalcitePrepare prepare = CalcitePrepare.DEFAULT_FACTORY.apply(); final ImmutableMap propValues = ImmutableMap.of(); @@ -317,7 +335,7 @@ public static CalcitePrepare.AnalyzeViewResult analyzeView( /** Prepares a SQL query for execution. For use within Calcite only. */ public static CalcitePrepare.CalciteSignature prepare( final CalciteConnection connection, final CalciteSchema schema, - final List schemaPath, final String sql, + final @Nullable List schemaPath, final String sql, final ImmutableMap map) { final CalcitePrepare prepare = CalcitePrepare.DEFAULT_FACTORY.apply(); final CalcitePrepare.Context context = @@ -344,7 +362,7 @@ public static CalcitePrepare.CalciteSignature prepare( */ private static CalcitePrepare.Context makeContext( CalciteConnection connection, CalciteSchema schema, - List schemaPath, List objectPath, + @Nullable List schemaPath, @Nullable List objectPath, final ImmutableMap propValues) { if (connection == null) { final CalcitePrepare.Context context0 = CalcitePrepare.Dummy.peek(); @@ -376,8 +394,8 @@ private static CalcitePrepare.Context makeContext( final JavaTypeFactory typeFactory, final DataContext dataContext, final CalciteSchema schema, - final List schemaPath, final List objectPath_) { - final ImmutableList objectPath = + final @Nullable List schemaPath, final @Nullable List objectPath_) { + final @Nullable ImmutableList objectPath = objectPath_ == null ? null : ImmutableList.copyOf(objectPath_); return new CalcitePrepare.Context() { @Override public JavaTypeFactory getTypeFactory() { @@ -401,7 +419,7 @@ private static CalcitePrepare.Context makeContext( return schemaPath; } - @Override public List getObjectPath() { + @Override public @Nullable List getObjectPath() { return objectPath; } @@ -446,7 +464,7 @@ public static List getStarTables( final List list = getLatticeEntries(schema); return Util.transform(list, entry -> { final CalciteSchema.TableEntry starTable = - Objects.requireNonNull(entry).getStarTable(); + requireNonNull(entry).getStarTable(); assert starTable.getTable().getJdbcTableType() == Schema.TableType.STAR; return entry.getStarTable(); @@ -485,20 +503,21 @@ private static void gatherLattices(CalciteSchema schema, *

The result is null if the initial schema is null or any sub-schema does * not exist. */ - public static CalciteSchema subSchema(CalciteSchema schema, + public static @Nullable CalciteSchema subSchema(CalciteSchema schema, Iterable names) { + @Nullable CalciteSchema current = schema; for (String string : names) { - if (schema == null) { + if (current == null) { return null; } - schema = schema.getSubSchema(string, false); + current = current.getSubSchema(string, false); } - return schema; + return current; } /** Generates a table name that is unique within the given schema. */ public static String uniqueTableName(CalciteSchema schema, String base) { - String t = Objects.requireNonNull(base); + String t = requireNonNull(base); for (int x = 0; schema.getTable(t, true) != null; x++) { t = base + x; } @@ -526,7 +545,11 @@ public static Path path(CalciteSchema rootSchema, Iterable names) { if (!iterator.hasNext()) { return path(builder.build()); } - schema = schema.getSubSchema(name); + Schema next = schema.getSubSchema(name); + if (next == null) { + throw new IllegalArgumentException("schema " + name + " is not found in " + schema); + } + schema = next; } } @@ -546,20 +569,20 @@ public static Path path(SchemaPlus schema) { /** Dummy data context that has no variables. */ private static class DummyDataContext implements DataContext { private final CalciteConnection connection; - private final SchemaPlus rootSchema; + private final @Nullable SchemaPlus rootSchema; private final ImmutableMap map; - DummyDataContext(CalciteConnection connection, SchemaPlus rootSchema) { + DummyDataContext(CalciteConnection connection, @Nullable SchemaPlus rootSchema) { this.connection = connection; this.rootSchema = rootSchema; this.map = ImmutableMap.of(); } - @Override public SchemaPlus getRootSchema() { + @Override public @Nullable SchemaPlus getRootSchema() { return rootSchema; } - @Override public JavaTypeFactory getTypeFactory() { + @Override public @Nullable JavaTypeFactory getTypeFactory() { return connection.getTypeFactory(); } @@ -567,7 +590,7 @@ private static class DummyDataContext implements DataContext { return connection; } - @Override public Object get(String name) { + @Override public @Nullable Object get(String name) { return map.get(name); } } @@ -584,7 +607,7 @@ private static class PathImpl this.pairs = pairs; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof PathImpl && pairs.equals(((PathImpl) o).pairs); diff --git a/core/src/main/java/org/apache/calcite/schema/Statistic.java b/core/src/main/java/org/apache/calcite/schema/Statistic.java index 01793dab995f..2e4307f68fbc 100644 --- a/core/src/main/java/org/apache/calcite/schema/Statistic.java +++ b/core/src/main/java/org/apache/calcite/schema/Statistic.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.RelReferentialConstraint; import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -32,7 +34,7 @@ */ public interface Statistic { /** Returns the approximate number of rows in the table. */ - default Double getRowCount() { + default @Nullable Double getRowCount() { return null; } @@ -44,23 +46,23 @@ default boolean isKey(ImmutableBitSet columns) { } /** Returns a list of unique keys, or null if no key exist. */ - default List getKeys() { + default @Nullable List getKeys() { return null; } /** Returns the collection of referential constraints (foreign-keys) * for this table. */ - default List getReferentialConstraints() { + default @Nullable List getReferentialConstraints() { return null; } /** Returns the collections of columns on which this table is sorted. */ - default List getCollations() { + default @Nullable List getCollations() { return null; } /** Returns the distribution of the data in this table. */ - default RelDistribution getDistribution() { + default @Nullable RelDistribution getDistribution() { return null; } } diff --git a/core/src/main/java/org/apache/calcite/schema/Statistics.java b/core/src/main/java/org/apache/calcite/schema/Statistics.java index c9bc7273de7d..85de175989e5 100644 --- a/core/src/main/java/org/apache/calcite/schema/Statistics.java +++ b/core/src/main/java/org/apache/calcite/schema/Statistics.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -37,14 +39,14 @@ private Statistics() { }; /** Returns a statistic with a given set of referential constraints. */ - public static Statistic of(final List referentialConstraints) { + public static Statistic of(@Nullable List referentialConstraints) { return of(null, null, referentialConstraints, null); } /** Returns a statistic with a given row count and set of unique keys. */ public static Statistic of(final double rowCount, - final List keys) { + final @Nullable List keys) { return of(rowCount, keys, null, null); } @@ -52,17 +54,17 @@ public static Statistic of(final double rowCount, /** Returns a statistic with a given row count, set of unique keys, * and collations. */ public static Statistic of(final double rowCount, - final List keys, - final List collations) { + final @Nullable List keys, + final @Nullable List collations) { return of(rowCount, keys, null, collations); } /** Returns a statistic with a given row count, set of unique keys, * referential constraints, and collations. */ - public static Statistic of(final Double rowCount, - final List keys, - final List referentialConstraints, - final List collations) { + public static Statistic of(final @Nullable Double rowCount, + final @Nullable List keys, + final @Nullable List referentialConstraints, + final @Nullable List collations) { List keysCopy = keys == null ? ImmutableList.of() : ImmutableList.copyOf(keys); List referentialConstraintsCopy = referentialConstraints == null ? null : ImmutableList.copyOf(referentialConstraints); @@ -70,7 +72,7 @@ public static Statistic of(final Double rowCount, collations == null ? null : ImmutableList.copyOf(collations); return new Statistic() { - @Override public Double getRowCount() { + @Override public @Nullable Double getRowCount() { return rowCount; } @@ -83,15 +85,15 @@ public static Statistic of(final Double rowCount, return false; } - @Override public List getKeys() { + @Override public @Nullable List getKeys() { return keysCopy; } - @Override public List getReferentialConstraints() { + @Override public @Nullable List getReferentialConstraints() { return referentialConstraintsCopy; } - @Override public List getCollations() { + @Override public @Nullable List getCollations() { return collationsCopy; } }; diff --git a/core/src/main/java/org/apache/calcite/schema/Table.java b/core/src/main/java/org/apache/calcite/schema/Table.java index 79f55f69f189..d8d53d8e1697 100644 --- a/core/src/main/java/org/apache/calcite/schema/Table.java +++ b/core/src/main/java/org/apache/calcite/schema/Table.java @@ -22,6 +22,8 @@ import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Table. * @@ -79,5 +81,5 @@ public interface Table { * @return true iff the given aggregate call is valid */ boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config); + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config); } diff --git a/core/src/main/java/org/apache/calcite/schema/TableFactory.java b/core/src/main/java/org/apache/calcite/schema/TableFactory.java index 9875334d8dff..780bf2e6815c 100644 --- a/core/src/main/java/org/apache/calcite/schema/TableFactory.java +++ b/core/src/main/java/org/apache/calcite/schema/TableFactory.java @@ -18,6 +18,8 @@ import org.apache.calcite.rel.type.RelDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; /** @@ -78,10 +80,11 @@ public interface TableFactory { * @param name Name of this table * @param operand The "operand" JSON property * @param rowType Row type. Specified if the "columns" JSON property. + * @return created table */ T create( SchemaPlus schema, String name, Map operand, - RelDataType rowType); + @Nullable RelDataType rowType); } diff --git a/core/src/main/java/org/apache/calcite/schema/TemporalTable.java b/core/src/main/java/org/apache/calcite/schema/TemporalTable.java index 6b6a472aea26..e756eff3b0a4 100644 --- a/core/src/main/java/org/apache/calcite/schema/TemporalTable.java +++ b/core/src/main/java/org/apache/calcite/schema/TemporalTable.java @@ -16,7 +16,6 @@ */ package org.apache.calcite.schema; -import javax.annotation.Nonnull; /** * Table that is temporal. @@ -25,9 +24,9 @@ public interface TemporalTable extends Table { /** Returns the name of the system column that contains the start effective * time of each row. */ - @Nonnull String getSysStartFieldName(); + String getSysStartFieldName(); /** Returns the name of the system column that contains the end effective * time of each row. */ - @Nonnull String getSysEndFieldName(); + String getSysEndFieldName(); } diff --git a/core/src/main/java/org/apache/calcite/schema/Wrapper.java b/core/src/main/java/org/apache/calcite/schema/Wrapper.java index e68f61fc2b9b..91e43cac7d70 100644 --- a/core/src/main/java/org/apache/calcite/schema/Wrapper.java +++ b/core/src/main/java/org/apache/calcite/schema/Wrapper.java @@ -16,11 +16,25 @@ */ package org.apache.calcite.schema; +import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * Mix-in interface that allows you to find sub-objects. */ public interface Wrapper { /** Finds an instance of an interface implemented by this object, * or returns null if this object does not support that interface. */ - C unwrap(Class aClass); + @Nullable C unwrap(Class aClass); + + /** Finds an instance of an interface implemented by this object, + * or throws NullPointerException if this object does not support + * that interface. */ + @API(since = "1.27", status = API.Status.INTERNAL) + default C unwrapOrThrow(Class aClass) { + return requireNonNull(unwrap(aClass), + () -> "Can't unwrap " + aClass + " from " + this); + } } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java b/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java index 2888a658045f..04072f4cd98a 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java @@ -30,10 +30,14 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Abstract implementation of {@link Schema}. * @@ -63,7 +67,8 @@ public AbstractSchema() { return this; } - @Override public Expression getExpression(SchemaPlus parentSchema, String name) { + @Override public Expression getExpression(@Nullable SchemaPlus parentSchema, String name) { + requireNonNull(parentSchema, "parentSchema"); return Schemas.subSchemaExpression(parentSchema, name, getClass()); } @@ -82,10 +87,11 @@ protected Map getTableMap() { } @Override public final Set getTableNames() { - return getTableMap().keySet(); + //noinspection RedundantCast + return (Set) getTableMap().keySet(); } - @Override public final Table getTable(String name) { + @Override public final @Nullable Table getTable(String name) { return getTableMap().get(name); } @@ -103,12 +109,13 @@ protected Map getTypeMap() { return ImmutableMap.of(); } - @Override public RelProtoDataType getType(String name) { + @Override public @Nullable RelProtoDataType getType(String name) { return getTypeMap().get(name); } @Override public Set getTypeNames() { - return getTypeMap().keySet(); + //noinspection RedundantCast + return (Set) getTypeMap().keySet(); } /** @@ -151,10 +158,11 @@ protected Map getSubSchemaMap() { } @Override public final Set getSubSchemaNames() { - return getSubSchemaMap().keySet(); + //noinspection RedundantCast + return (Set) getSubSchemaMap().keySet(); } - @Override public final Schema getSubSchema(String name) { + @Override public final @Nullable Schema getSubSchema(String name) { return getSubSchemaMap().get(name); } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java b/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java index dad2035878f2..7f7d7b0ae92c 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java @@ -25,6 +25,8 @@ import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Abstract base class for implementing {@link Table}. * @@ -46,7 +48,7 @@ protected AbstractTable() { return Schema.TableType.TABLE; } - @Override public C unwrap(Class aClass) { + @Override public @Nullable C unwrap(Class aClass) { if (aClass.isInstance(this)) { return aClass.cast(this); } @@ -58,7 +60,7 @@ protected AbstractTable() { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java b/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java index 47157ccc7ba5..3486d7b5c9a1 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java @@ -27,6 +27,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; @@ -47,8 +49,8 @@ public class AggregateFunctionImpl implements AggregateFunction, public final boolean isStatic; public final Method initMethod; public final Method addMethod; - public final Method mergeMethod; - public final Method resultMethod; // may be null + public final @Nullable Method mergeMethod; + public final @Nullable Method resultMethod; // may be null public final ImmutableList> valueTypes; private final List parameters; public final Class accumulatorType; @@ -63,8 +65,8 @@ private AggregateFunctionImpl(Class declaringClass, Class resultType, Method initMethod, Method addMethod, - Method mergeMethod, - Method resultMethod) { + @Nullable Method mergeMethod, + @Nullable Method resultMethod) { this.declaringClass = declaringClass; this.valueTypes = ImmutableList.copyOf(valueTypes); this.parameters = params; @@ -80,7 +82,7 @@ private AggregateFunctionImpl(Class declaringClass, } /** Creates an aggregate function, or returns null. */ - public static AggregateFunctionImpl create(Class clazz) { + public static @Nullable AggregateFunctionImpl create(Class clazz) { final Method initMethod = ReflectiveFunctionBase.findMethod(clazz, "init"); final Method addMethod = ReflectiveFunctionBase.findMethod(clazz, "add"); final Method mergeMethod = null; // TODO: diff --git a/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java b/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java index 91a0e82083e8..e63f518878bf 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java @@ -24,6 +24,8 @@ import org.apache.calcite.schema.SchemaVersion; import org.apache.calcite.schema.Table; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.Set; @@ -55,11 +57,11 @@ public DelegatingSchema(Schema schema) { return schema.snapshot(version); } - @Override public Expression getExpression(SchemaPlus parentSchema, String name) { + @Override public Expression getExpression(@Nullable SchemaPlus parentSchema, String name) { return schema.getExpression(parentSchema, name); } - @Override public Table getTable(String name) { + @Override public @Nullable Table getTable(String name) { return schema.getTable(name); } @@ -67,7 +69,7 @@ public DelegatingSchema(Schema schema) { return schema.getTableNames(); } - @Override public RelProtoDataType getType(String name) { + @Override public @Nullable RelProtoDataType getType(String name) { return schema.getType(name); } @@ -83,7 +85,7 @@ public DelegatingSchema(Schema schema) { return schema.getFunctionNames(); } - @Override public Schema getSubSchema(String name) { + @Override public @Nullable Schema getSubSchema(String name) { return schema.getSubSchema(name); } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ListTransientTable.java b/core/src/main/java/org/apache/calcite/schema/impl/ListTransientTable.java index 12485e7f6e7a..0ee6ec4581ab 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ListTransientTable.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ListTransientTable.java @@ -41,12 +41,16 @@ import org.apache.calcite.schema.Schemas; import org.apache.calcite.schema.TransientTable; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Objects.requireNonNull; + /** * {@link TransientTable} backed by a Java list. It will be automatically added to the * current schema when {@link #scan(DataContext)} method gets called. @@ -73,8 +77,8 @@ public ListTransientTable(String name, RelDataType rowType) { Prepare.CatalogReader catalogReader, RelNode child, TableModify.Operation operation, - List updateColumnList, - List sourceExpressionList, + @Nullable List updateColumnList, + @Nullable List sourceExpressionList, boolean flattened) { return LogicalTableModify.create(table, catalogReader, child, operation, updateColumnList, sourceExpressionList, flattened); @@ -84,15 +88,16 @@ public ListTransientTable(String name, RelDataType rowType) { return rows; } - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { // add the table into the schema, so that it is accessible by any potential operator - root.getRootSchema().add(name, this); + requireNonNull(root.getRootSchema(), "root.getRootSchema()") + .add(name, this); final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { - return new Enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { + return new Enumerator<@Nullable Object[]>() { private final List list = new ArrayList(rows); private int i = -1; diff --git a/core/src/main/java/org/apache/calcite/schema/impl/LongSchemaVersion.java b/core/src/main/java/org/apache/calcite/schema/impl/LongSchemaVersion.java index fc52962fc261..dc60dacbe6e0 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/LongSchemaVersion.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/LongSchemaVersion.java @@ -18,6 +18,8 @@ import org.apache.calcite.schema.SchemaVersion; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Implementation of SchemaVersion that uses a long value as representation. */ public class LongSchemaVersion implements SchemaVersion { private final long value; @@ -36,7 +38,7 @@ public LongSchemaVersion(long value) { return this.value < ((LongSchemaVersion) other).value; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/MaterializedViewTable.java b/core/src/main/java/org/apache/calcite/schema/impl/MaterializedViewTable.java index c0b9ea7bdd7e..f2a815c3cd03 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/MaterializedViewTable.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/MaterializedViewTable.java @@ -30,6 +30,8 @@ import org.apache.calcite.schema.Table; import org.apache.calcite.schema.TranslatableTable; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.sql.DriverManager; import java.sql.SQLException; @@ -67,7 +69,7 @@ public MaterializedViewTable(Type elementType, RelProtoDataType relDataType, String viewSql, List viewSchemaPath, - List viewPath, + @Nullable List viewPath, MaterializationKey key) { super(elementType, relDataType, viewSql, viewSchemaPath, viewPath); this.key = key; @@ -75,8 +77,8 @@ public MaterializedViewTable(Type elementType, /** Table macro that returns a materialized view. */ public static MaterializedViewTableMacro create(final CalciteSchema schema, - final String viewSql, final List viewSchemaPath, List viewPath, - final String suggestedTableName, boolean existing) { + final String viewSql, final @Nullable List viewSchemaPath, List viewPath, + final @Nullable String suggestedTableName, boolean existing) { return new MaterializedViewTableMacro(schema, viewSql, viewSchemaPath, viewPath, suggestedTableName, existing); } @@ -101,7 +103,8 @@ public static class MaterializedViewTableMacro private final MaterializationKey key; private MaterializedViewTableMacro(CalciteSchema schema, String viewSql, - List viewSchemaPath, List viewPath, String suggestedTableName, + @Nullable List viewSchemaPath, List viewPath, + @Nullable String suggestedTableName, boolean existing) { super(schema, viewSql, viewSchemaPath != null ? viewSchemaPath : schema.path(null), viewPath, diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java b/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java index 18331fe6d4d3..7ed7026aac1f 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java @@ -40,6 +40,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; @@ -48,6 +50,8 @@ import static org.apache.calcite.sql.validate.SqlValidatorUtil.mapNameToIndex; +import static java.util.Objects.requireNonNull; + /** Extension to {@link ViewTable} that is modifiable. */ public class ModifiableViewTable extends ViewTable implements ModifiableView, Wrapper { @@ -59,7 +63,7 @@ public class ModifiableViewTable extends ViewTable /** Creates a ModifiableViewTable. */ public ModifiableViewTable(Type elementType, RelProtoDataType rowType, - String viewSql, List schemaPath, List viewPath, + String viewSql, List schemaPath, @Nullable List viewPath, Table table, Path tablePath, RexNode constraint, ImmutableIntList columnMapping) { super(elementType, rowType, viewSql, schemaPath, viewPath); @@ -87,7 +91,7 @@ public ModifiableViewTable(Type elementType, RelProtoDataType rowType, return tablePath; } - @Override public C unwrap(Class aClass) { + @Override public @Nullable C unwrap(Class aClass) { if (aClass.isInstance(initializerExpressionFactory)) { return aClass.cast(initializerExpressionFactory); } else if (aClass.isInstance(table)) { @@ -158,10 +162,11 @@ private static ImmutableIntList getNewColumnMapping(Table underlying, newMapping.addAll(oldColumnMapping); int newMappedIndex = baseColumns.size(); for (RelDataTypeField extendedColumn : extendedColumns) { - if (nameToIndex.containsKey(extendedColumn.getName())) { + String extendedColumnName = extendedColumn.getName(); + if (nameToIndex.containsKey(extendedColumnName)) { // The extended column duplicates a column in the underlying table. // Map to the index in the underlying table. - newMapping.add(nameToIndex.get(extendedColumn.getName())); + newMapping.add(nameToIndex.get(extendedColumnName)); } else { // The extended column is not in the underlying table. newMapping.add(newMappedIndex++); @@ -195,8 +200,9 @@ private ModifiableViewTableInitializerExpressionFactory() { @Override public ColumnStrategy generationStrategy(RelOptTable table, int iColumn) { - final ModifiableViewTable viewTable = - table.unwrap(ModifiableViewTable.class); + final ModifiableViewTable viewTable = requireNonNull( + table.unwrap(ModifiableViewTable.class), + () -> "unable to unwrap ModifiableViewTable from " + table); assert iColumn < viewTable.columnMapping.size(); // Use the view constraint to generate the default value if the column is @@ -222,7 +228,9 @@ private ModifiableViewTableInitializerExpressionFactory() { @Override public RexNode newColumnDefaultValue(RelOptTable table, int iColumn, InitializerContext context) { - final ModifiableViewTable viewTable = table.unwrap(ModifiableViewTable.class); + final ModifiableViewTable viewTable = requireNonNull( + table.unwrap(ModifiableViewTable.class), + () -> "unable to unwrap ModifiableViewTable from " + table); assert iColumn < viewTable.columnMapping.size(); final RexBuilder rexBuilder = context.getRexBuilder(); final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory(); diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java b/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java index 4764bc61dc99..3aa4879bd8da 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -81,7 +83,7 @@ static boolean classHasPublicZeroArgsConstructor(Class clazz) { * @param name name of the method to find * @return the first method with matching name or null when no method found */ - static Method findMethod(Class clazz, String name) { + static @Nullable Method findMethod(Class clazz, String name) { for (Method method : clazz.getMethods()) { if (method.getName().equals(name) && !method.isBridge()) { return method; diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java b/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java index e095b5924c6e..162a10239b97 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableMultimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -111,7 +113,7 @@ public static ImmutableMultimap functions(Class clazz) { * @param methodName Method name (typically "eval") * @return created {@link ScalarFunction} or null */ - public static ScalarFunction create(Class clazz, String methodName) { + public static @Nullable ScalarFunction create(Class clazz, String methodName) { final Method method = findMethod(clazz, methodName); if (method == null) { return null; diff --git a/core/src/main/java/org/apache/calcite/schema/impl/StarTable.java b/core/src/main/java/org/apache/calcite/schema/impl/StarTable.java index 1a0f03803fc4..fda608f002b0 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/StarTable.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/StarTable.java @@ -36,10 +36,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Virtual table that is composed of two or more tables joined together. * @@ -59,7 +63,7 @@ public class StarTable extends AbstractTable implements TranslatableTable { public final ImmutableList

tables; /** Number of fields in each table's row type. */ - public ImmutableIntList fieldCounts; + public @MonotonicNonNull ImmutableIntList fieldCounts; /** Creates a StarTable. */ private StarTable(Lattice lattice, ImmutableList
tables) { @@ -111,7 +115,7 @@ public StarTable add(Table table) { */ public int columnOffset(Table table) { int n = 0; - for (Pair pair : Pair.zip(tables, fieldCounts)) { + for (Pair pair : Pair.zip(tables, castNonNull(fieldCounts))) { if (pair.left == table) { return n; } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java b/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java index 0ecf91253824..ecad1b88761c 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java @@ -35,6 +35,8 @@ import org.apache.calcite.schema.TableFunction; import org.apache.calcite.util.BuiltInMethod; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -45,6 +47,8 @@ import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link org.apache.calcite.schema.TableFunction} based on a * method. @@ -61,13 +65,13 @@ private TableFunctionImpl(Method method, CallImplementor implementor) { /** Creates a {@link TableFunctionImpl} from a class, looking for an "eval" * method. Returns null if there is no such method. */ - public static TableFunction create(Class clazz) { + public static @Nullable TableFunction create(Class clazz) { return create(clazz, "eval"); } /** Creates a {@link TableFunctionImpl} from a class, looking for a method * with a given name. Returns null if there is no such method. */ - public static TableFunction create(Class clazz, String methodName) { + public static @Nullable TableFunction create(Class clazz, String methodName) { final Method method = findMethod(clazz, methodName); if (method == null) { return null; @@ -76,7 +80,7 @@ public static TableFunction create(Class clazz, String methodName) { } /** Creates a {@link TableFunctionImpl} from a method. */ - public static TableFunction create(final Method method) { + public static @Nullable TableFunction create(final Method method) { if (!Modifier.isStatic(method.getModifiers())) { Class clazz = method.getDeclaringClass(); if (!classHasPublicZeroArgsConstructor(clazz)) { @@ -148,9 +152,9 @@ private Table apply(List arguments) { method.getDeclaringClass().getConstructor(); o = constructor.newInstance(); } - //noinspection unchecked - final Object table = method.invoke(o, arguments.toArray()); - return (Table) table; + return (Table) requireNonNull( + method.invoke(o, arguments.toArray()), + () -> "got null from " + method + " with arguments " + arguments); } catch (IllegalArgumentException e) { throw RESOURCE.illegalArgumentForTableFunctionCall( method.toString(), diff --git a/core/src/main/java/org/apache/calcite/schema/impl/TableMacroImpl.java b/core/src/main/java/org/apache/calcite/schema/impl/TableMacroImpl.java index 754eee0ab123..8c317e0a795c 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/TableMacroImpl.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/TableMacroImpl.java @@ -19,6 +19,8 @@ import org.apache.calcite.schema.TableMacro; import org.apache.calcite.schema.TranslatableTable; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -28,6 +30,8 @@ import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link org.apache.calcite.schema.TableMacro} based on a * method. @@ -42,7 +46,7 @@ private TableMacroImpl(Method method) { /** Creates a {@code TableMacro} from a class, looking for an "eval" * method. Returns null if there is no such method. */ - public static TableMacro create(Class clazz) { + public static @Nullable TableMacro create(Class clazz) { final Method method = findMethod(clazz, "eval"); if (method == null) { return null; @@ -51,7 +55,7 @@ public static TableMacro create(Class clazz) { } /** Creates a {@code TableMacro} from a method. */ - public static TableMacro create(final Method method) { + public static @Nullable TableMacro create(final Method method) { Class clazz = method.getDeclaringClass(); if (!Modifier.isStatic(method.getModifiers())) { if (!classHasPublicZeroArgsConstructor(clazz)) { @@ -79,8 +83,9 @@ public static TableMacro create(final Method method) { method.getDeclaringClass().getConstructor(); o = constructor.newInstance(); } - //noinspection unchecked - return (TranslatableTable) method.invoke(o, arguments.toArray()); + return (TranslatableTable) requireNonNull( + method.invoke(o, arguments.toArray()), + () -> "got null from " + method + " with arguments " + arguments); } catch (IllegalArgumentException e) { throw new RuntimeException("Expected " + Arrays.toString(method.getParameterTypes()) + " actual " diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java b/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java index 5ae67162b639..55ecd9b12127 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java @@ -35,6 +35,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; @@ -49,10 +51,10 @@ public class ViewTable private final String viewSql; private final List schemaPath; private final RelProtoDataType protoRowType; - private final List viewPath; + private final @Nullable List viewPath; public ViewTable(Type elementType, RelProtoDataType rowType, String viewSql, - List schemaPath, List viewPath) { + List schemaPath, @Nullable List viewPath) { super(elementType); this.viewSql = viewSql; this.schemaPath = ImmutableList.copyOf(schemaPath); @@ -68,7 +70,7 @@ public static ViewTableMacro viewMacro(SchemaPlus schema, @Deprecated // to be removed before 2.0 public static ViewTableMacro viewMacro(SchemaPlus schema, String viewSql, - List schemaPath, Boolean modifiable) { + List schemaPath, @Nullable Boolean modifiable) { return viewMacro(schema, viewSql, schemaPath, null, modifiable); } @@ -80,7 +82,8 @@ public static ViewTableMacro viewMacro(SchemaPlus schema, String viewSql, * @param modifiable Whether view is modifiable, or null to deduce it */ public static ViewTableMacro viewMacro(SchemaPlus schema, String viewSql, - List schemaPath, List viewPath, Boolean modifiable) { + List schemaPath, @Nullable List viewPath, + @Nullable Boolean modifiable) { return new ViewTableMacro(CalciteSchema.from(schema), viewSql, schemaPath, viewPath, modifiable); } @@ -96,7 +99,7 @@ public List getSchemaPath() { } /** Returns the the path of the view. */ - public List getViewPath() { + public @Nullable List getViewPath() { return viewPath; } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ViewTableMacro.java b/core/src/main/java/org/apache/calcite/schema/impl/ViewTableMacro.java index 83424a3df8cf..fe989019e5ab 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ViewTableMacro.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ViewTableMacro.java @@ -28,6 +28,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Collections; import java.util.List; @@ -37,11 +39,11 @@ public class ViewTableMacro implements TableMacro { protected final String viewSql; protected final CalciteSchema schema; - private final Boolean modifiable; + private final @Nullable Boolean modifiable; /** Typically null. If specified, overrides the path of the schema as the * context for validating {@code viewSql}. */ - protected final List schemaPath; - protected final List viewPath; + protected final @Nullable List schemaPath; + protected final @Nullable List viewPath; /** * Creates a ViewTableMacro. @@ -54,7 +56,8 @@ public class ViewTableMacro implements TableMacro { * of {@code viewSql}) */ public ViewTableMacro(CalciteSchema schema, String viewSql, - List schemaPath, List viewPath, Boolean modifiable) { + @Nullable List schemaPath, @Nullable List viewPath, + @Nullable Boolean modifiable) { this.viewSql = viewSql; this.schema = schema; this.viewPath = viewPath == null ? null : ImmutableList.copyOf(viewPath); @@ -87,7 +90,7 @@ public ViewTableMacro(CalciteSchema schema, String viewSql, /** Allows a sub-class to return an extension of {@link ModifiableViewTable} * by overriding this method. */ protected ModifiableViewTable modifiableViewTable(CalcitePrepare.AnalyzeViewResult parsed, - String viewSql, List schemaPath, List viewPath, + String viewSql, List schemaPath, @Nullable List viewPath, CalciteSchema schema) { final JavaTypeFactory typeFactory = (JavaTypeFactory) parsed.typeFactory; final Type elementType = typeFactory.getJavaClass(parsed.rowType); @@ -100,7 +103,7 @@ protected ModifiableViewTable modifiableViewTable(CalcitePrepare.AnalyzeViewResu /** Allows a sub-class to return an extension of {@link ViewTable} by * overriding this method. */ protected ViewTable viewTable(CalcitePrepare.AnalyzeViewResult parsed, - String viewSql, List schemaPath, List viewPath) { + String viewSql, List schemaPath, @Nullable List viewPath) { final JavaTypeFactory typeFactory = (JavaTypeFactory) parsed.typeFactory; final Type elementType = typeFactory.getJavaClass(parsed.rowType); return new ViewTable(elementType, diff --git a/core/src/main/java/org/apache/calcite/server/CalciteServerStatement.java b/core/src/main/java/org/apache/calcite/server/CalciteServerStatement.java index 092d30ea5621..91e6f0865340 100644 --- a/core/src/main/java/org/apache/calcite/server/CalciteServerStatement.java +++ b/core/src/main/java/org/apache/calcite/server/CalciteServerStatement.java @@ -20,6 +20,8 @@ import org.apache.calcite.jdbc.CalciteConnection; import org.apache.calcite.jdbc.CalcitePrepare; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Iterator; /** @@ -34,9 +36,9 @@ public interface CalciteServerStatement { void setSignature(Meta.Signature signature); - Meta.Signature getSignature(); + Meta.@Nullable Signature getSignature(); - Iterator getResultSet(); + @Nullable Iterator getResultSet(); void setResultSet(Iterator resultSet); } diff --git a/core/src/main/java/org/apache/calcite/server/DdlExecutorImpl.java b/core/src/main/java/org/apache/calcite/server/DdlExecutorImpl.java index b5a8e2f9ac78..914b98f27b2a 100644 --- a/core/src/main/java/org/apache/calcite/server/DdlExecutorImpl.java +++ b/core/src/main/java/org/apache/calcite/server/DdlExecutorImpl.java @@ -31,6 +31,7 @@ protected DdlExecutorImpl() { /** Dispatches calls to the appropriate method based on the type of the * first argument. */ + @SuppressWarnings({"method.invocation.invalid", "argument.type.incompatible"}) private final ReflectUtil.MethodDispatcher dispatcher = ReflectUtil.createMethodDispatcher(void.class, this, "execute", SqlNode.class, CalcitePrepare.Context.class); diff --git a/core/src/main/java/org/apache/calcite/sql/ExplicitOperatorBinding.java b/core/src/main/java/org/apache/calcite/sql/ExplicitOperatorBinding.java index cc8a0f8e414d..c66730f354a9 100644 --- a/core/src/main/java/org/apache/calcite/sql/ExplicitOperatorBinding.java +++ b/core/src/main/java/org/apache/calcite/sql/ExplicitOperatorBinding.java @@ -23,6 +23,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.validate.SqlValidatorException; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -33,7 +35,7 @@ public class ExplicitOperatorBinding extends SqlOperatorBinding { //~ Instance fields -------------------------------------------------------- private final List types; - private final SqlOperatorBinding delegate; + private final @Nullable SqlOperatorBinding delegate; //~ Constructors ----------------------------------------------------------- @@ -55,7 +57,7 @@ public ExplicitOperatorBinding( } private ExplicitOperatorBinding( - SqlOperatorBinding delegate, + @Nullable SqlOperatorBinding delegate, RelDataTypeFactory typeFactory, SqlOperator operator, List types) { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java index cc7873a5ac67..c677cd0d987e 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java @@ -22,6 +22,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.TimestampString; +import static java.util.Objects.requireNonNull; + /** * A SQL literal representing a DATE, TIME or TIMESTAMP value. * @@ -55,7 +57,7 @@ protected SqlAbstractDateTimeLiteral(Object d, boolean tz, /** Converts this literal to a {@link TimestampString}. */ protected TimestampString getTimestamp() { - return (TimestampString) value; + return (TimestampString) requireNonNull(value); } public int getPrec() { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java index 099c08a182b6..dc7798c0931c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java @@ -27,9 +27,10 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.Optionality; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; /** * Abstract base class for the definition of an aggregate function: an operator @@ -50,8 +51,8 @@ protected SqlAggFunction( String name, SqlKind kind, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory funcType) { // We leave sqlIdentifier as null to indicate that this is a builtin. this(name, null, kind, returnTypeInference, operandTypeInference, @@ -63,11 +64,11 @@ protected SqlAggFunction( @Deprecated // to be removed before 2.0 protected SqlAggFunction( String name, - SqlIdentifier sqlIdentifier, + @Nullable SqlIdentifier sqlIdentifier, SqlKind kind, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory funcType) { this(name, sqlIdentifier, kind, returnTypeInference, operandTypeInference, operandTypeChecker, funcType, false, false, @@ -77,11 +78,11 @@ protected SqlAggFunction( @Deprecated // to be removed before 2.0 protected SqlAggFunction( String name, - SqlIdentifier sqlIdentifier, + @Nullable SqlIdentifier sqlIdentifier, SqlKind kind, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory funcType, boolean requiresOrder, boolean requiresOver) { @@ -96,11 +97,11 @@ protected SqlAggFunction( * a built-in function it will be null. */ protected SqlAggFunction( String name, - SqlIdentifier sqlIdentifier, + @Nullable SqlIdentifier sqlIdentifier, SqlKind kind, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory funcType, boolean requiresOrder, boolean requiresOver, @@ -114,7 +115,7 @@ protected SqlAggFunction( //~ Methods ---------------------------------------------------------------- - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { return clazz.isInstance(this) ? clazz.cast(this) : null; } @@ -162,7 +163,7 @@ protected SqlAggFunction( * and {@code AGG(x)} is valid. * */ - public @Nonnull Optionality requiresGroupOrder() { + public Optionality requiresGroupOrder() { return requiresGroupOrder; } @@ -182,7 +183,7 @@ protected SqlAggFunction( * {@link Optionality#IGNORED} to indicate this. For such functions, * Calcite will probably remove {@code DISTINCT} while optimizing the query. */ - public @Nonnull Optionality getDistinctOptionality() { + public Optionality getDistinctOptionality() { return Optionality.OPTIONAL; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAlter.java b/core/src/main/java/org/apache/calcite/sql/SqlAlter.java index 2cea5c3a97d1..b562dc2c215b 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlAlter.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlAlter.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Base class for an ALTER statements parse tree nodes. The portion of the * statement covered by this class is "ALTER <SCOPE>. Subclasses handle @@ -26,18 +28,19 @@ public abstract class SqlAlter extends SqlCall { /** Scope of the operation. Values "SYSTEM" and "SESSION" are typical. */ - String scope; + @Nullable String scope; protected SqlAlter(SqlParserPos pos) { this(pos, null); } - protected SqlAlter(SqlParserPos pos, String scope) { + protected SqlAlter(SqlParserPos pos, @Nullable String scope) { super(pos); this.scope = scope; } @Override public final void unparse(SqlWriter writer, int leftPrec, int rightPrec) { + String scope = this.scope; if (scope != null) { writer.keyword("ALTER"); writer.keyword(scope); @@ -47,11 +50,11 @@ protected SqlAlter(SqlParserPos pos, String scope) { protected abstract void unparseAlterOperation(SqlWriter writer, int leftPrec, int rightPrec); - public String getScope() { + public @Nullable String getScope() { return scope; } - public void setScope(String scope) { + public void setScope(@Nullable String scope) { this.scope = scope; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java b/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java index b1d99d9760a9..bd9a3f8f0cc4 100755 --- a/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlBasicCall.java @@ -19,31 +19,35 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.UnmodifiableArrayList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Implementation of {@link SqlCall} that keeps its operands in an array. */ public class SqlBasicCall extends SqlCall { private SqlOperator operator; - public final SqlNode[] operands; - private final SqlLiteral functionQuantifier; + public final @Nullable SqlNode[] operands; + private final @Nullable SqlLiteral functionQuantifier; private final boolean expanded; public SqlBasicCall( SqlOperator operator, - SqlNode[] operands, + @Nullable SqlNode[] operands, SqlParserPos pos) { this(operator, operands, pos, false, null); } public SqlBasicCall( SqlOperator operator, - SqlNode[] operands, + @Nullable SqlNode[] operands, SqlParserPos pos, boolean expanded, - SqlLiteral functionQualifier) { + @Nullable SqlLiteral functionQualifier) { super(pos); this.operator = Objects.requireNonNull(operator); this.operands = operands; @@ -59,7 +63,7 @@ public SqlBasicCall( return expanded; } - @Override public void setOperand(int i, SqlNode operand) { + @Override public void setOperand(int i, @Nullable SqlNode operand) { operands[i] = operand; } @@ -71,24 +75,25 @@ public void setOperator(SqlOperator operator) { return operator; } - public SqlNode[] getOperands() { + public @Nullable SqlNode[] getOperands() { return operands; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return UnmodifiableArrayList.of(operands); // not immutable, but quick } @SuppressWarnings("unchecked") @Override public S operand(int i) { - return (S) operands[i]; + return (S) castNonNull(operands[i]); } @Override public int operandCount() { return operands.length; } - @Override public SqlLiteral getFunctionQuantifier() { + @Override public @Nullable SqlLiteral getFunctionQuantifier() { return functionQuantifier; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java b/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java index 4e3d7db948ce..8e96d2fb8eed 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlBasicTypeNameSpec.java @@ -24,9 +24,10 @@ import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.util.Litmus; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.charset.Charset; import java.util.Objects; -import javax.annotation.Nullable; /** * A sql type name specification of basic sql type. @@ -76,7 +77,7 @@ public class SqlBasicTypeNameSpec extends SqlTypeNameSpec { private int precision; private int scale; - private String charSetName; + private @Nullable String charSetName; /** * Create a basic sql type name specification. @@ -127,7 +128,7 @@ public int getPrecision() { return precision; } - public String getCharSetName() { + public @Nullable String getCharSetName() { return charSetName; } @@ -187,9 +188,6 @@ public String getCharSetName() { @Override public RelDataType deriveType(SqlValidator validator) { final RelDataTypeFactory typeFactory = validator.getTypeFactory(); - if (sqlTypeName == null) { - return null; - } RelDataType type; // NOTE jvs 15-Jan-2009: earlier validation is supposed to // have caught these, which is why it's OK for them diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBinaryOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlBinaryOperator.java index 4ff5a26d91b6..5e19369eac3c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlBinaryOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlBinaryOperator.java @@ -28,11 +28,15 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.nio.charset.Charset; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * SqlBinaryOperator is a binary operator. */ @@ -55,9 +59,9 @@ public SqlBinaryOperator( SqlKind kind, int prec, boolean leftAssoc, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { super( name, kind, @@ -74,7 +78,7 @@ public SqlBinaryOperator( return SqlSyntax.BINARY; } - @Override public String getSignatureTemplate(final int operandsCount) { + @Override public @Nullable String getSignatureTemplate(final int operandsCount) { Util.discard(operandsCount); // op0 opname op1 @@ -135,7 +139,7 @@ private RelDataType convertType(SqlValidator validator, SqlCall call, RelDataTyp .createTypeWithCharsetAndCollation( type, type.getCharset(), - resultCol); + requireNonNull(resultCol)); } } return type; @@ -159,9 +163,6 @@ private RelDataType convertType(SqlValidator validator, SqlCall call, RelDataTyp final SqlMonotonicity mono0 = call.getOperandMonotonicity(0); final SqlMonotonicity mono1 = call.getOperandMonotonicity(1); - if (mono0 == null || mono1 == null) { - return null; - } if (mono1 == SqlMonotonicity.CONSTANT) { if (call.isOperandLiteral(1, false)) { BigDecimal value = call.getOperandLiteralValue(1, BigDecimal.class); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBinaryStringLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlBinaryStringLiteral.java index eb7a7e3458b3..9dd3d669068c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlBinaryStringLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlBinaryStringLiteral.java @@ -22,6 +22,7 @@ import org.apache.calcite.util.Util; import java.util.List; +import java.util.Objects; /** * A binary (or hexadecimal) string literal. @@ -47,19 +48,22 @@ protected SqlBinaryStringLiteral( */ @Deprecated // to be removed before 2.0 public BitString getBitString() { - return (BitString) value; + return getValueNonNull(); + } + + private BitString getValueNonNull() { + return (BitString) Objects.requireNonNull(value, "value"); } @Override public SqlBinaryStringLiteral clone(SqlParserPos pos) { - return new SqlBinaryStringLiteral((BitString) value, pos); + return new SqlBinaryStringLiteral(getValueNonNull(), pos); } @Override public void unparse( SqlWriter writer, int leftPrec, int rightPrec) { - assert value instanceof BitString; - writer.literal("X'" + ((BitString) value).toHexString() + "'"); + writer.literal("X'" + getValueNonNull().toHexString() + "'"); } @Override protected SqlAbstractStringLiteral concat1(List literals) { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCall.java b/core/src/main/java/org/apache/calcite/sql/SqlCall.java index 37cafbf0bea3..3cd00ae5963f 100755 --- a/core/src/main/java/org/apache/calcite/sql/SqlCall.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlCall.java @@ -26,10 +26,15 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.Litmus; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.ArrayList; import java.util.Collection; import java.util.List; -import javax.annotation.Nonnull; +import java.util.Objects; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; /** * A SqlCall is a call to an {@link SqlOperator operator}. @@ -61,7 +66,7 @@ public boolean isExpanded() { * @param i Operand index * @param operand Operand value */ - public void setOperand(int i, SqlNode operand) { + public void setOperand(int i, @Nullable SqlNode operand) { throw new UnsupportedOperationException(); } @@ -69,13 +74,31 @@ public void setOperand(int i, SqlNode operand) { return getOperator().getKind(); } - public abstract @Nonnull SqlOperator getOperator(); + @Pure + public abstract SqlOperator getOperator(); - public abstract @Nonnull List getOperandList(); + /** + * Returns the list of operands. The set and order of operands is call-specific. + *

Note: the proper type would be {@code List<@Nullable SqlNode>}, however, + * it would trigger too many changes to the current codebase.

+ * @return the list of call operands, never null, the operands can be null + */ + public abstract List getOperandList(); + /** + * Returns i-th operand (0-based). + *

Note: the result might be null, so the proper signature would be + * {@code }, however, it would trigger to many changes to the current + * codebase.

+ * @param i operand index (0-based) + * @param type of the result + * @return i-th operand (0-based), the result might be null + */ @SuppressWarnings("unchecked") - public S operand(int i) { - return (S) getOperandList().get(i); + public S operand(int i) { + // Note: in general, null elements exist in the list, however, the code + // assumes operand(..) is non-nullable, so we add a cast here + return (S) castNonNull(getOperandList().get(i)); } public int operandCount() { @@ -138,7 +161,7 @@ public int operandCount() { return visitor.visit(this); } - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { if (node == this) { return true; } @@ -162,10 +185,12 @@ public int operandCount() { */ protected String getCallSignature( SqlValidator validator, - SqlValidatorScope scope) { + @Nullable SqlValidatorScope scope) { List signatureList = new ArrayList<>(); for (final SqlNode operand : getOperandList()) { - final RelDataType argType = validator.deriveType(scope, operand); + final RelDataType argType = validator.deriveType( + Objects.requireNonNull(scope, "scope"), + operand); if (null == argType) { continue; } @@ -174,7 +199,8 @@ protected String getCallSignature( return SqlUtil.getOperatorSignature(getOperator(), signatureList); } - @Override public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) { + @Override public SqlMonotonicity getMonotonicity(@Nullable SqlValidatorScope scope) { + Objects.requireNonNull(scope, "scope"); // Delegate to operator. final SqlCallBinding binding = new SqlCallBinding(scope.getValidator(), scope, this); @@ -202,7 +228,8 @@ && operandCount() == 1) { return false; } - public SqlLiteral getFunctionQuantifier() { + @Pure + public @Nullable SqlLiteral getFunctionQuantifier() { return null; } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java index 8448ef4e338e..f3d80aee61ed 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java @@ -43,6 +43,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -61,7 +63,7 @@ public class SqlCallBinding extends SqlOperatorBinding { //~ Instance fields -------------------------------------------------------- private final SqlValidator validator; - private final SqlValidatorScope scope; + private final @Nullable SqlValidatorScope scope; private final SqlCall call; //~ Constructors ----------------------------------------------------------- @@ -75,7 +77,7 @@ public class SqlCallBinding extends SqlOperatorBinding { */ public SqlCallBinding( SqlValidator validator, - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlCall call) { super( validator.getTypeFactory(), @@ -119,7 +121,7 @@ public SqlValidator getValidator() { /** * Returns the scope of the call. */ - public SqlValidatorScope getScope() { + public @Nullable SqlValidatorScope getScope() { return scope; } @@ -168,8 +170,9 @@ private boolean hasAssignment() { /** Returns the operands to a call permuted into the same order as the * formal parameters of the function. */ private List permutedOperands(final SqlCall call) { - final SqlOperandMetadata operandMetadata = - (SqlOperandMetadata) call.getOperator().getOperandTypeChecker(); + final SqlOperandMetadata operandMetadata = requireNonNull( + (SqlOperandMetadata) call.getOperator().getOperandTypeChecker(), + () -> "operandTypeChecker is null for " + call + ", operator " + call.getOperator()); final List paramNames = operandMetadata.paramNames(); final List permuted = new ArrayList<>(); final SqlNameMatcher nameMatcher = @@ -232,7 +235,7 @@ public SqlCall permutedCall() { } @SuppressWarnings("deprecation") - @Override public String getStringLiteralOperand(int ordinal) { + @Override public @Nullable String getStringLiteralOperand(int ordinal) { SqlNode node = call.operand(ordinal); final Object o = SqlLiteral.value(node); return o instanceof NlsString ? ((NlsString) o).getValue() : null; @@ -254,12 +257,13 @@ public SqlCall permutedCall() { throw new AssertionError(); } - @Override public T getOperandLiteralValue(int ordinal, Class clazz) { + @Override public @Nullable T getOperandLiteralValue(int ordinal, + Class clazz) { final SqlNode node = operand(ordinal); return valueAs(node, clazz); } - @Override public Object getOperandLiteralValue(int ordinal, RelDataType type) { + @Override public @Nullable Object getOperandLiteralValue(int ordinal, RelDataType type) { if (!(type instanceof RelDataTypeFactoryImpl.JavaType)) { return null; } @@ -275,11 +279,11 @@ public SqlCall permutedCall() { return EnumUtils.evaluate(o2, clazz); } - private T valueAs(SqlNode node, Class clazz) { + private @Nullable T valueAs(SqlNode node, Class clazz) { final SqlLiteral literal; switch (node.getKind()) { case ARRAY_VALUE_CONSTRUCTOR: - final List list = new ArrayList<>(); + final List<@Nullable Object> list = new ArrayList<>(); for (SqlNode o : ((SqlCall) node).getOperandList()) { list.add(valueAs(o, Object.class)); } @@ -351,7 +355,7 @@ private T valueAs(SqlNode node, Class clazz) { return type; } - @Override public RelDataType getCursorOperand(int ordinal) { + @Override public @Nullable RelDataType getCursorOperand(int ordinal) { final SqlNode operand = call.operand(ordinal); if (!SqlUtil.isCallTo(operand, SqlStdOperatorTable.CURSOR)) { return null; @@ -361,7 +365,7 @@ private T valueAs(SqlNode node, Class clazz) { return SqlTypeUtil.deriveType(this, query); } - @Override public String getColumnListParamInfo( + @Override public @Nullable String getColumnListParamInfo( int ordinal, String paramName, List columnList) { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCharStringLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlCharStringLiteral.java index bab807f53daa..46fbc332d7f1 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlCharStringLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlCharStringLiteral.java @@ -22,7 +22,10 @@ import org.apache.calcite.util.NlsString; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import java.util.Objects; /** * A character string literal. @@ -47,26 +50,28 @@ protected SqlCharStringLiteral(NlsString val, SqlParserPos pos) { */ @Deprecated // to be removed before 2.0 public NlsString getNlsString() { - return (NlsString) value; + return getValueNonNull(); } + private NlsString getValueNonNull() { + return (NlsString) Objects.requireNonNull(value, "value"); + } /** * Returns the collation. */ - public SqlCollation getCollation() { - return ((NlsString) value).getCollation(); + public @Nullable SqlCollation getCollation() { + return getValueNonNull().getCollation(); } @Override public SqlCharStringLiteral clone(SqlParserPos pos) { - return new SqlCharStringLiteral((NlsString) value, pos); + return new SqlCharStringLiteral(getValueNonNull(), pos); } @Override public void unparse( SqlWriter writer, int leftPrec, int rightPrec) { - assert value instanceof NlsString; - final NlsString nlsString = (NlsString) this.value; + final NlsString nlsString = getValueNonNull(); if (false) { Util.discard(Bug.FRG78_FIXED); String stringValue = nlsString.getValue(); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCollation.java b/core/src/main/java/org/apache/calcite/sql/SqlCollation.java index 36cb6312e826..b6576d4edcfe 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlCollation.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlCollation.java @@ -22,6 +22,10 @@ import org.apache.calcite.util.SerializableCharset; import org.apache.calcite.util.Util; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.io.Serializable; import java.nio.charset.Charset; import java.text.Collator; @@ -122,7 +126,7 @@ public SqlCollation( //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof SqlCollation && collationName.equals(((SqlCollation) o).collationName); @@ -132,8 +136,10 @@ public SqlCollation( return collationName.hashCode(); } - protected String generateCollationName(Charset charset) { - return charset.name().toUpperCase(Locale.ROOT) + "$" + locale.toString() + "$" + strength; + protected String generateCollationName( + @UnderInitialization SqlCollation this, + Charset charset) { + return charset.name().toUpperCase(Locale.ROOT) + "$" + String.valueOf(locale) + "$" + strength; } /** @@ -147,7 +153,7 @@ protected String generateCollationName(Charset charset) { * * @see Glossary#SQL99 SQL:1999 Part 2 Section 4.2.3 Table 2 */ - public static SqlCollation getCoercibilityDyadicOperator( + public static @Nullable SqlCollation getCoercibilityDyadicOperator( SqlCollation col1, SqlCollation col2) { return getCoercibilityDyadic(col1, col2); @@ -205,7 +211,7 @@ public static String getCoercibilityDyadicComparison( * Returns the result for {@link #getCoercibilityDyadicComparison} and * {@link #getCoercibilityDyadicOperator}. */ - protected static SqlCollation getCoercibilityDyadic( + protected static @Nullable SqlCollation getCoercibilityDyadic( SqlCollation col1, SqlCollation col2) { assert null != col1; @@ -305,7 +311,8 @@ public final Locale getLocale() { * collation, or {@code null} if no specific {@link Collator} is needed, in * which case {@link String#compareTo} will be used. */ - public Collator getCollator() { + @Pure + public @Nullable Collator getCollator() { return null; } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java b/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java index 5d3c263f75b1..8c41e3001e8a 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDataTypeSpec.java @@ -25,6 +25,8 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.Litmus; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; import java.util.TimeZone; @@ -63,14 +65,14 @@ public class SqlDataTypeSpec extends SqlNode { //~ Instance fields -------------------------------------------------------- private final SqlTypeNameSpec typeNameSpec; - private final TimeZone timeZone; + private final @Nullable TimeZone timeZone; /** Whether data type allows nulls. * *

Nullable is nullable! Null means "not specified". E.g. * {@code CAST(x AS INTEGER)} preserves the same nullability as {@code x}. */ - private final Boolean nullable; + private final @Nullable Boolean nullable; //~ Constructors ----------------------------------------------------------- @@ -95,7 +97,7 @@ public SqlDataTypeSpec( */ public SqlDataTypeSpec( final SqlTypeNameSpec typeNameSpec, - TimeZone timeZone, + @Nullable TimeZone timeZone, SqlParserPos pos) { this(typeNameSpec, timeZone, null, pos); } @@ -111,8 +113,8 @@ public SqlDataTypeSpec( */ public SqlDataTypeSpec( SqlTypeNameSpec typeNameSpec, - TimeZone timeZone, - Boolean nullable, + @Nullable TimeZone timeZone, + @Nullable Boolean nullable, SqlParserPos pos) { super(pos); this.typeNameSpec = typeNameSpec; @@ -126,11 +128,11 @@ public SqlDataTypeSpec( return new SqlDataTypeSpec(typeNameSpec, timeZone, pos); } - @Override public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) { + @Override public SqlMonotonicity getMonotonicity(@Nullable SqlValidatorScope scope) { return SqlMonotonicity.CONSTANT; } - public SqlIdentifier getCollectionsTypeName() { + public @Nullable SqlIdentifier getCollectionsTypeName() { if (typeNameSpec instanceof SqlCollectionTypeNameSpec) { return typeNameSpec.getTypeName(); } @@ -145,11 +147,11 @@ public SqlTypeNameSpec getTypeNameSpec() { return typeNameSpec; } - public TimeZone getTimeZone() { + public @Nullable TimeZone getTimeZone() { return timeZone; } - public Boolean getNullable() { + public @Nullable Boolean getNullable() { return nullable; } @@ -195,7 +197,7 @@ public SqlDataTypeSpec getComponentTypeSpec() { return visitor.visit(this); } - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { if (!(node instanceof SqlDataTypeSpec)) { return litmus.fail("{} != {}", this, node); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java index bc302a3c025f..d90a895c1b3f 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java @@ -22,6 +22,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.DateString; +import java.util.Objects; + /** * A SQL literal representing a DATE value, such as DATE * '2004-10-22'. @@ -39,11 +41,11 @@ public class SqlDateLiteral extends SqlAbstractDateTimeLiteral { /** Converts this literal to a {@link DateString}. */ protected DateString getDate() { - return (DateString) value; + return (DateString) Objects.requireNonNull(value, "value"); } @Override public SqlDateLiteral clone(SqlParserPos pos) { - return new SqlDateLiteral((DateString) value, pos); + return new SqlDateLiteral(getDate(), pos); } @Override public String toString() { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDelete.java b/core/src/main/java/org/apache/calcite/sql/SqlDelete.java index a76c40c2e625..a5645289ebd5 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDelete.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDelete.java @@ -22,8 +22,11 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; + /** * A SqlDelete is a node of a parse tree which represents a DELETE * statement. @@ -33,18 +36,18 @@ public class SqlDelete extends SqlCall { new SqlSpecialOperator("DELETE", SqlKind.DELETE); SqlNode targetTable; - SqlNode condition; - SqlSelect sourceSelect; - SqlIdentifier alias; + @Nullable SqlNode condition; + @Nullable SqlSelect sourceSelect; + @Nullable SqlIdentifier alias; //~ Constructors ----------------------------------------------------------- public SqlDelete( SqlParserPos pos, SqlNode targetTable, - SqlNode condition, - SqlSelect sourceSelect, - SqlIdentifier alias) { + @Nullable SqlNode condition, + @Nullable SqlSelect sourceSelect, + @Nullable SqlIdentifier alias) { super(pos); this.targetTable = targetTable; this.condition = condition; @@ -62,11 +65,13 @@ public SqlDelete( return OPERATOR; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(targetTable, condition, alias); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: targetTable = operand; @@ -95,7 +100,7 @@ public SqlNode getTargetTable() { /** * Returns the alias for the target table of the deletion. */ - public SqlIdentifier getAlias() { + public @Nullable SqlIdentifier getAlias() { return alias; } @@ -105,7 +110,7 @@ public SqlIdentifier getAlias() { * @return the condition expression for the data to be deleted, or null for * all rows in the table */ - public SqlNode getCondition() { + public @Nullable SqlNode getCondition() { return condition; } @@ -116,7 +121,7 @@ public SqlNode getCondition() { * * @return the source SELECT for the data to be inserted */ - public SqlSelect getSourceSelect() { + public @Nullable SqlSelect getSourceSelect() { return sourceSelect; } @@ -126,10 +131,12 @@ public SqlSelect getSourceSelect() { final int opLeft = getOperator().getLeftPrec(); final int opRight = getOperator().getRightPrec(); targetTable.unparse(writer, opLeft, opRight); + SqlIdentifier alias = this.alias; if (alias != null) { writer.keyword("AS"); alias.unparse(writer, opLeft, opRight); } + SqlNode condition = this.condition; if (condition != null) { writer.sep("WHERE"); condition.unparse(writer, opLeft, opRight); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDescribeSchema.java b/core/src/main/java/org/apache/calcite/sql/SqlDescribeSchema.java index 440396d42779..0ef0a54ac0e1 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDescribeSchema.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDescribeSchema.java @@ -19,8 +19,11 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; + /** * A SqlDescribeSchema is a node of a parse tree that represents a * {@code DESCRIBE SCHEMA} statement. @@ -29,8 +32,9 @@ public class SqlDescribeSchema extends SqlCall { public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("DESCRIBE_SCHEMA", SqlKind.DESCRIBE_SCHEMA) { - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { return new SqlDescribeSchema(pos, (SqlIdentifier) operands[0]); } }; @@ -49,7 +53,8 @@ public SqlDescribeSchema(SqlParserPos pos, SqlIdentifier schema) { schema.unparse(writer, leftPrec, rightPrec); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: schema = (SqlIdentifier) operand; diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDescribeTable.java b/core/src/main/java/org/apache/calcite/sql/SqlDescribeTable.java index d275a4a6c09b..2cff6f3a677b 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDescribeTable.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDescribeTable.java @@ -19,7 +19,11 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import java.util.Objects; + /** * A SqlDescribeTable is a node of a parse tree that represents a @@ -29,22 +33,23 @@ public class SqlDescribeTable extends SqlCall { public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("DESCRIBE_TABLE", SqlKind.DESCRIBE_TABLE) { - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { return new SqlDescribeTable(pos, (SqlIdentifier) operands[0], - (SqlIdentifier) operands[1]); + (@Nullable SqlIdentifier) operands[1]); } }; SqlIdentifier table; - SqlIdentifier column; + @Nullable SqlIdentifier column; /** Creates a SqlDescribeTable. */ public SqlDescribeTable(SqlParserPos pos, SqlIdentifier table, - SqlIdentifier column) { + @Nullable SqlIdentifier column) { super(pos); - this.table = table; + this.table = Objects.requireNonNull(table); this.column = column; } @@ -57,7 +62,8 @@ public SqlDescribeTable(SqlParserPos pos, } } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: table = (SqlIdentifier) operand; @@ -74,6 +80,7 @@ public SqlDescribeTable(SqlParserPos pos, return OPERATOR; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(table, column); } @@ -82,7 +89,7 @@ public SqlIdentifier getTable() { return table; } - public SqlIdentifier getColumn() { + public @Nullable SqlIdentifier getColumn() { return column; } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java index f3ed08608256..503643e4d752 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java @@ -31,7 +31,7 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParser; import org.apache.calcite.sql.parser.SqlParserPos; -import org.apache.calcite.sql.type.BasicSqlType; +import org.apache.calcite.sql.type.AbstractSqlType; import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.sql.validate.SqlConformance; import org.apache.calcite.sql.validate.SqlConformanceEnum; @@ -40,6 +40,8 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,8 +54,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Supplier; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.calcite.util.DateTimeStringUtils.getDateFormatter; @@ -136,9 +136,9 @@ public class SqlDialect { //~ Instance fields -------------------------------------------------------- - protected final String identifierQuoteString; - protected final String identifierEndQuoteString; - protected final String identifierEscapedQuote; + protected final @Nullable String identifierQuoteString; + protected final @Nullable String identifierEndQuoteString; + protected final @Nullable String identifierEscapedQuote; protected final String literalQuoteString; protected final String literalEndQuoteString; protected final String literalEscapedQuote; @@ -361,6 +361,8 @@ public StringBuilder quoteIdentifier( StringBuilder buf, String val) { if (identifierQuoteString == null // quoting is not supported + || identifierEndQuoteString == null + || identifierEscapedQuote == null || !identifierNeedsQuote(val)) { buf.append(val); } else { @@ -414,7 +416,7 @@ public final String quoteStringLiteral(String val) { * @param charsetName Character set name, e.g. "utf16", or null * @param val String value */ - public void quoteStringLiteral(StringBuilder buf, String charsetName, + public void quoteStringLiteral(StringBuilder buf, @Nullable String charsetName, String val) { if (containsNonAscii(val) && charsetName == null) { quoteStringLiteralUnicode(buf, val); @@ -533,7 +535,7 @@ public void unparseSqlIntervalLiteral(SqlWriter writer, if (interval.getSign() == -1) { writer.print("-"); } - writer.literal("'" + literal.getValue().toString() + "'"); + writer.literal("'" + interval.getIntervalLiteral() + "'"); unparseSqlIntervalQualifier(writer, interval.getIntervalQualifier(), RelDataTypeSystem.DEFAULT); } @@ -591,7 +593,7 @@ public void quoteStringLiteralUnicode(StringBuilder buf, String val) { * Converts a string literal back into a string. For example, 'can''t * run' becomes can't run. */ - public String unquoteStringLiteral(String val) { + public @Nullable String unquoteStringLiteral(@Nullable String val) { if (val != null && val.startsWith(literalQuoteString) && val.endsWith(literalEndQuoteString)) { @@ -696,6 +698,7 @@ public DatabaseProduct getDatabaseProduct() { * Returns whether the dialect supports character set names as part of a * data type, for instance {@code VARCHAR(30) CHARACTER SET `ISO-8859-1`}. */ + @Pure public boolean supportsCharSet() { return true; } @@ -778,9 +781,10 @@ public boolean supportsDataType(RelDataType type) { *

If this method returns null, the cast will be omitted. In the default * implementation, this is the case for the NULL type, and therefore * {@code CAST(NULL AS )} is rendered as {@code NULL}. */ - public SqlNode getCastSpec(RelDataType type) { - if (type instanceof BasicSqlType) { - int maxPrecision = -1; + public @Nullable SqlNode getCastSpec(RelDataType type) { + int maxPrecision = -1; + if (type instanceof AbstractSqlType) { + System.out.println("type.getSqlTypeName() = " + type.getSqlTypeName().getName()); switch (type.getSqlTypeName()) { case NULL: return null; @@ -814,11 +818,11 @@ public SqlNode rewriteSingleValueExpr(SqlNode aggCall) { * @param node The SqlNode representing the expression * @param nullsFirst Whether nulls should come first * @param desc Whether the sort direction is - * {@link org.apache.calcite.rel.RelFieldCollation.Direction#DESCENDING} or - * {@link org.apache.calcite.rel.RelFieldCollation.Direction#STRICTLY_DESCENDING} + * {@link RelFieldCollation.Direction#DESCENDING} or + * {@link RelFieldCollation.Direction#STRICTLY_DESCENDING} * @return A SqlNode for null direction emulation or null if not required */ - public SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, + public @Nullable SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, boolean desc) { return null; } @@ -827,7 +831,7 @@ public JoinType emulateJoinTypeForCrossJoin() { return JoinType.COMMA; } - protected SqlNode emulateNullDirectionWithIsNull(SqlNode node, + protected @Nullable SqlNode emulateNullDirectionWithIsNull(SqlNode node, boolean nullsFirst, boolean desc) { // No need for emulation if the nulls will anyways come out the way we want // them based on "nullsFirst" and "desc". @@ -876,8 +880,8 @@ public boolean supportsOffsetFetch() { * @see #unparseFetchUsingAnsi(SqlWriter, SqlNode, SqlNode) * @see #unparseFetchUsingLimit(SqlWriter, SqlNode, SqlNode) */ - public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseFetchUsingAnsi(writer, offset, fetch); } @@ -893,13 +897,13 @@ public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, * @param offset Number of rows to skip before emitting, or null * @param fetch Number of rows to fetch, or null */ - public void unparseTopN(SqlWriter writer, SqlNode offset, SqlNode fetch) { + public void unparseTopN(SqlWriter writer, @Nullable SqlNode offset, @Nullable SqlNode fetch) { } /** Unparses offset/fetch using ANSI standard "OFFSET offset ROWS FETCH NEXT * fetch ROWS ONLY" syntax. */ - protected final void unparseFetchUsingAnsi(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + protected final void unparseFetchUsingAnsi(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { Preconditions.checkArgument(fetch != null || offset != null); if (offset != null) { writer.newlineAndIndent(); @@ -924,14 +928,14 @@ protected final void unparseFetchUsingAnsi(SqlWriter writer, SqlNode offset, } /** Unparses offset/fetch using "LIMIT fetch OFFSET offset" syntax. */ - protected final void unparseFetchUsingLimit(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + protected final void unparseFetchUsingLimit(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { Preconditions.checkArgument(fetch != null || offset != null); unparseLimit(writer, fetch); unparseOffset(writer, offset); } - protected final void unparseLimit(SqlWriter writer, SqlNode fetch) { + protected final void unparseLimit(SqlWriter writer, @Nullable SqlNode fetch) { if (fetch != null) { writer.newlineAndIndent(); final SqlWriter.Frame fetchFrame = @@ -942,7 +946,7 @@ protected final void unparseLimit(SqlWriter writer, SqlNode fetch) { } } - protected final void unparseOffset(SqlWriter writer, SqlNode offset) { + protected final void unparseOffset(SqlWriter writer, @Nullable SqlNode offset) { if (offset != null) { writer.newlineAndIndent(); final SqlWriter.Frame offsetFrame = @@ -1011,7 +1015,7 @@ public NullCollation getNullCollation() { /** Returns whether NULL values are sorted first or last, in this dialect, * in an ORDER BY item of a given direction. */ - public @Nonnull RelFieldCollation.NullDirection defaultNullDirection( + public RelFieldCollation.NullDirection defaultNullDirection( RelFieldCollation.Direction direction) { switch (direction) { case ASCENDING: @@ -1101,7 +1105,7 @@ public boolean supportsImplicitTypeCoercion(RexCall call) { * * @return The configuration builder */ - public @Nonnull SqlParser.Config configureParser(SqlParser.Config config) { + public SqlParser.Config configureParser(SqlParser.Config config) { final Quoting quoting = getQuoting(); if (quoting != null) { config = config.withQuoting(quoting); @@ -1113,7 +1117,7 @@ public boolean supportsImplicitTypeCoercion(RexCall call) { } @Deprecated // to be removed before 2.0 - public @Nonnull SqlParser.ConfigBuilder configureParser( + public SqlParser.ConfigBuilder configureParser( SqlParser.ConfigBuilder configBuilder) { return SqlParser.configBuilder( configureParser(configBuilder.build())); @@ -1123,7 +1127,7 @@ public boolean supportsImplicitTypeCoercion(RexCall call) { * *

The base implementation returns its best guess, based upon * {@link #databaseProduct}; sub-classes may override. */ - @Nonnull public SqlConformance getConformance() { + public SqlConformance getConformance() { switch (databaseProduct) { case UNKNOWN: case CALCITE: @@ -1144,7 +1148,7 @@ public boolean supportsImplicitTypeCoercion(RexCall call) { /** Returns the quoting scheme, or null if the combination of * {@link #identifierQuoteString} and {@link #identifierEndQuoteString} * does not correspond to any known quoting scheme. */ - protected Quoting getQuoting() { + protected @Nullable Quoting getQuoting() { if ("\"".equals(identifierQuoteString) && "\"".equals(identifierEndQuoteString)) { return Quoting.DOUBLE_QUOTE; @@ -1293,10 +1297,13 @@ public enum DatabaseProduct { @SuppressWarnings("ImmutableEnumChecker") private final Supplier dialect; - DatabaseProduct(String databaseProductName, String quoteString, + @SuppressWarnings("argument.type.incompatible") + DatabaseProduct(String databaseProductName, @Nullable String quoteString, NullCollation nullCollation) { Objects.requireNonNull(databaseProductName); Objects.requireNonNull(nullCollation); + // Note: below lambda accesses uninitialized DatabaseProduct.this, so it might be + // worth refactoring dialect = Suppliers.memoize(() -> { final SqlDialect dialect = SqlDialectFactoryImpl.simple(DatabaseProduct.this); @@ -1332,35 +1339,35 @@ public SqlDialect getDialect() { *

It is immutable; to "set" a property, call one of the "with" methods, * which returns a new context with the desired property value. */ public interface Context { - @Nonnull DatabaseProduct databaseProduct(); - Context withDatabaseProduct(@Nonnull DatabaseProduct databaseProduct); - String databaseProductName(); + DatabaseProduct databaseProduct(); + Context withDatabaseProduct(DatabaseProduct databaseProduct); + @Nullable String databaseProductName(); Context withDatabaseProductName(String databaseProductName); - String databaseVersion(); + @Nullable String databaseVersion(); Context withDatabaseVersion(String databaseVersion); int databaseMajorVersion(); Context withDatabaseMajorVersion(int databaseMajorVersion); int databaseMinorVersion(); Context withDatabaseMinorVersion(int databaseMinorVersion); - @Nonnull String literalQuoteString(); - @Nonnull Context withLiteralQuoteString(String literalQuoteString); - @Nonnull String literalEscapedQuoteString(); - @Nonnull Context withLiteralEscapedQuoteString( + String literalQuoteString(); + Context withLiteralQuoteString(String literalQuoteString); + String literalEscapedQuoteString(); + Context withLiteralEscapedQuoteString( String literalEscapedQuoteString); - String identifierQuoteString(); - @Nonnull Context withIdentifierQuoteString(String identifierQuoteString); - @Nonnull Casing unquotedCasing(); - @Nonnull Context withUnquotedCasing(Casing unquotedCasing); - @Nonnull Casing quotedCasing(); - @Nonnull Context withQuotedCasing(Casing unquotedCasing); + @Nullable String identifierQuoteString(); + Context withIdentifierQuoteString(@Nullable String identifierQuoteString); + Casing unquotedCasing(); + Context withUnquotedCasing(Casing unquotedCasing); + Casing quotedCasing(); + Context withQuotedCasing(Casing unquotedCasing); boolean caseSensitive(); - @Nonnull Context withCaseSensitive(boolean caseSensitive); - @Nonnull SqlConformance conformance(); - @Nonnull Context withConformance(SqlConformance conformance); - @Nonnull NullCollation nullCollation(); - @Nonnull Context withNullCollation(@Nonnull NullCollation nullCollation); - @Nonnull RelDataTypeSystem dataTypeSystem(); - Context withDataTypeSystem(@Nonnull RelDataTypeSystem dataTypeSystem); + Context withCaseSensitive(boolean caseSensitive); + SqlConformance conformance(); + Context withConformance(SqlConformance conformance); + NullCollation nullCollation(); + Context withNullCollation(NullCollation nullCollation); + RelDataTypeSystem dataTypeSystem(); + Context withDataTypeSystem(RelDataTypeSystem dataTypeSystem); JethroDataSqlDialect.JethroInfo jethroInfo(); Context withJethroInfo(JethroDataSqlDialect.JethroInfo jethroInfo); } @@ -1368,13 +1375,13 @@ public interface Context { /** Implementation of Context. */ private static class ContextImpl implements Context { private final DatabaseProduct databaseProduct; - private final String databaseProductName; - private final String databaseVersion; + private final @Nullable String databaseProductName; + private final @Nullable String databaseVersion; private final int databaseMajorVersion; private final int databaseMinorVersion; private final String literalQuoteString; private final String literalEscapedQuoteString; - private final String identifierQuoteString; + private final @Nullable String identifierQuoteString; private final Casing unquotedCasing; private final Casing quotedCasing; private final boolean caseSensitive; @@ -1384,10 +1391,10 @@ private static class ContextImpl implements Context { private final JethroDataSqlDialect.JethroInfo jethroInfo; private ContextImpl(DatabaseProduct databaseProduct, - String databaseProductName, String databaseVersion, + @Nullable String databaseProductName, @Nullable String databaseVersion, int databaseMajorVersion, int databaseMinorVersion, String literalQuoteString, String literalEscapedQuoteString, - String identifierQuoteString, Casing quotedCasing, + @Nullable String identifierQuoteString, Casing quotedCasing, Casing unquotedCasing, boolean caseSensitive, SqlConformance conformance, NullCollation nullCollation, RelDataTypeSystem dataTypeSystem, @@ -1409,12 +1416,12 @@ private ContextImpl(DatabaseProduct databaseProduct, this.jethroInfo = Objects.requireNonNull(jethroInfo); } - @Override @Nonnull public DatabaseProduct databaseProduct() { + @Override public DatabaseProduct databaseProduct() { return databaseProduct; } @Override public Context withDatabaseProduct( - @Nonnull DatabaseProduct databaseProduct) { + DatabaseProduct databaseProduct) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1422,7 +1429,7 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override public String databaseProductName() { + @Override public @Nullable String databaseProductName() { return databaseProductName; } @@ -1434,7 +1441,7 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override public String databaseVersion() { + @Override public @Nullable String databaseVersion() { return databaseVersion; } @@ -1495,12 +1502,12 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override public String identifierQuoteString() { + @Override public @Nullable String identifierQuoteString() { return identifierQuoteString; } - @Override @Nonnull public Context withIdentifierQuoteString( - String identifierQuoteString) { + @Override public Context withIdentifierQuoteString( + @Nullable String identifierQuoteString) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1508,11 +1515,11 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override @Nonnull public Casing unquotedCasing() { + @Override public Casing unquotedCasing() { return unquotedCasing; } - @Override @Nonnull public Context withUnquotedCasing(Casing unquotedCasing) { + @Override public Context withUnquotedCasing(Casing unquotedCasing) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1520,11 +1527,11 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override @Nonnull public Casing quotedCasing() { + @Override public Casing quotedCasing() { return quotedCasing; } - @Override @Nonnull public Context withQuotedCasing(Casing quotedCasing) { + @Override public Context withQuotedCasing(Casing quotedCasing) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1536,7 +1543,7 @@ private ContextImpl(DatabaseProduct databaseProduct, return caseSensitive; } - @Override @Nonnull public Context withCaseSensitive(boolean caseSensitive) { + @Override public Context withCaseSensitive(boolean caseSensitive) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1544,11 +1551,11 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override @Nonnull public SqlConformance conformance() { + @Override public SqlConformance conformance() { return conformance; } - @Override @Nonnull public Context withConformance(SqlConformance conformance) { + @Override public Context withConformance(SqlConformance conformance) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1556,12 +1563,12 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override @Nonnull public NullCollation nullCollation() { + @Override public NullCollation nullCollation() { return nullCollation; } - @Override @Nonnull public Context withNullCollation( - @Nonnull NullCollation nullCollation) { + @Override public Context withNullCollation( + NullCollation nullCollation) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1569,11 +1576,11 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override @Nonnull public RelDataTypeSystem dataTypeSystem() { + @Override public RelDataTypeSystem dataTypeSystem() { return dataTypeSystem; } - @Override public Context withDataTypeSystem(@Nonnull RelDataTypeSystem dataTypeSystem) { + @Override public Context withDataTypeSystem(RelDataTypeSystem dataTypeSystem) { return new ContextImpl(databaseProduct, databaseProductName, databaseVersion, databaseMajorVersion, databaseMinorVersion, literalQuoteString, literalEscapedQuoteString, @@ -1581,7 +1588,7 @@ private ContextImpl(DatabaseProduct databaseProduct, conformance, nullCollation, dataTypeSystem, jethroInfo); } - @Override @Nonnull public JethroDataSqlDialect.JethroInfo jethroInfo() { + @Override public JethroDataSqlDialect.JethroInfo jethroInfo() { return jethroInfo; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java index da3e979c6e2a..952e665bd38c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java @@ -51,6 +51,8 @@ import org.apache.calcite.sql.dialect.TeradataSqlDialect; import org.apache.calcite.sql.dialect.VerticaSqlDialect; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.Locale; @@ -237,7 +239,7 @@ private String getIdentifierQuoteString(DatabaseMetaData databaseMetaData) { } /** Returns a basic dialect for a given product, or null if none is known. */ - static SqlDialect simple(SqlDialect.DatabaseProduct databaseProduct) { + static @Nullable SqlDialect simple(SqlDialect.DatabaseProduct databaseProduct) { switch (databaseProduct) { case ACCESS: return AccessSqlDialect.DEFAULT; diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java b/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java index 0cdb794727a5..21980f3f13c1 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java @@ -23,6 +23,8 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.Litmus; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDynamicParam represents a dynamic parameter marker in an * SQL statement. The textual order in which dynamic parameters appear within an @@ -68,7 +70,7 @@ public int getIndex() { validator.validateDynamicParam(this); } - @Override public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) { + @Override public SqlMonotonicity getMonotonicity(@Nullable SqlValidatorScope scope) { return SqlMonotonicity.CONSTANT; } @@ -76,7 +78,7 @@ public int getIndex() { return visitor.visit(this); } - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { if (!(node instanceof SqlDynamicParam)) { return litmus.fail("{} != {}", this, node); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlExplain.java b/core/src/main/java/org/apache/calcite/sql/SqlExplain.java index e959d8eeaedc..e0ad70857a42 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlExplain.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlExplain.java @@ -19,8 +19,12 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; + /** * A SqlExplain is a node of a parse tree which represents an * EXPLAIN PLAN statement. @@ -28,8 +32,9 @@ public class SqlExplain extends SqlCall { public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("EXPLAIN", SqlKind.EXPLAIN) { - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { return new SqlExplain(pos, operands[0], (SqlLiteral) operands[1], (SqlLiteral) operands[2], (SqlLiteral) operands[3], 0); } @@ -82,7 +87,8 @@ public SqlExplain(SqlParserPos pos, return ImmutableNullableList.of(explicandum, detailLevel, depth, format); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: explicandum = operand; @@ -104,6 +110,7 @@ public SqlExplain(SqlParserPos pos, /** * Returns the underlying SQL statement to be explained. */ + @Pure public SqlNode getExplicandum() { return explicandum; } @@ -111,20 +118,23 @@ public SqlNode getExplicandum() { /** * Return the detail level to be generated. */ + @Pure public SqlExplainLevel getDetailLevel() { - return detailLevel.symbolValue(SqlExplainLevel.class); + return detailLevel.getValueAs(SqlExplainLevel.class); } /** * Returns the level of abstraction at which this plan should be displayed. */ + @Pure public Depth getDepth() { - return depth.symbolValue(Depth.class); + return depth.getValueAs(Depth.class); } /** * Returns the number of dynamic parameters in the statement. */ + @Pure public int getDynamicParamCount() { return dynamicParameterCount; } @@ -132,6 +142,7 @@ public int getDynamicParamCount() { /** * Returns whether physical plan implementation should be returned. */ + @Pure public boolean withImplementation() { return getDepth() == Depth.PHYSICAL; } @@ -139,6 +150,7 @@ public boolean withImplementation() { /** * Returns whether type should be returned. */ + @Pure public boolean withType() { return getDepth() == Depth.TYPE; } @@ -146,8 +158,9 @@ public boolean withType() { /** * Returns the desired output format. */ + @Pure public SqlExplainFormat getFormat() { - return format.symbolValue(SqlExplainFormat.class); + return format.getValueAs(SqlExplainFormat.class); } /** diff --git a/core/src/main/java/org/apache/calcite/sql/SqlFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlFunction.java index 4000b0eacbc5..a48447c33960 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlFunction.java @@ -30,10 +30,13 @@ import org.apache.calcite.sql.validate.implicit.TypeCoercion; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; /** @@ -46,7 +49,7 @@ public class SqlFunction extends SqlOperator { private final SqlFunctionCategory category; - private final SqlIdentifier sqlIdentifier; + private final @Nullable SqlIdentifier sqlIdentifier; //~ Constructors ----------------------------------------------------------- @@ -63,9 +66,9 @@ public class SqlFunction extends SqlOperator { public SqlFunction( String name, SqlKind kind, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory category) { // We leave sqlIdentifier as null to indicate // that this is a built-in. @@ -90,10 +93,10 @@ public SqlFunction( */ public SqlFunction( SqlIdentifier sqlIdentifier, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, - List paramTypes, + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, + @Nullable List paramTypes, SqlFunctionCategory funcType) { this(Util.last(sqlIdentifier.names), sqlIdentifier, SqlKind.OTHER_FUNCTION, returnTypeInference, operandTypeInference, operandTypeChecker, @@ -103,12 +106,12 @@ public SqlFunction( @Deprecated // to be removed before 2.0 protected SqlFunction( String name, - SqlIdentifier sqlIdentifier, + @Nullable SqlIdentifier sqlIdentifier, SqlKind kind, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, - List paramTypes, + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, + @Nullable List paramTypes, SqlFunctionCategory category) { this(name, sqlIdentifier, kind, returnTypeInference, operandTypeInference, operandTypeChecker, category); @@ -119,11 +122,11 @@ protected SqlFunction( */ protected SqlFunction( String name, - SqlIdentifier sqlIdentifier, + @Nullable SqlIdentifier sqlIdentifier, SqlKind kind, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory category) { super(name, kind, 100, 100, returnTypeInference, operandTypeInference, operandTypeChecker); @@ -142,7 +145,7 @@ protected SqlFunction( * Returns the fully-qualified name of function, or null for a built-in * function. */ - public SqlIdentifier getSqlIdentifier() { + public @Nullable SqlIdentifier getSqlIdentifier() { return sqlIdentifier; } @@ -156,7 +159,7 @@ public SqlIdentifier getSqlIdentifier() { /** Use {@link SqlOperandMetadata#paramTypes(RelDataTypeFactory)} on the * result of {@link #getOperandTypeChecker()}. */ @Deprecated // to be removed before 2.0 - public List getParamTypes() { + public @Nullable List getParamTypes() { return null; } @@ -164,7 +167,7 @@ public List getParamTypes() { * {@link #getOperandTypeChecker()}. */ @Deprecated // to be removed before 2.0 public List getParamNames() { - return Functions.generate(getParamTypes().size(), i -> "arg" + i); + return Functions.generate(castNonNull(getParamTypes()).size(), i -> "arg" + i); } @Override public void unparse( @@ -178,7 +181,7 @@ public List getParamNames() { /** * Return function category. */ - @Nonnull public SqlFunctionCategory getFunctionType() { + public SqlFunctionCategory getFunctionType() { return this.category; } @@ -187,6 +190,7 @@ public List getParamNames() { * ALL quantifier. The default is false; some aggregate * functions return true. */ + @Pure public boolean isQuantifierAllowed() { return false; } @@ -213,8 +217,9 @@ public boolean isQuantifierAllowed() { * not allowed. */ protected void validateQuantifier(SqlValidator validator, SqlCall call) { - if ((null != call.getFunctionQuantifier()) && !isQuantifierAllowed()) { - throw validator.newValidationError(call.getFunctionQuantifier(), + SqlLiteral functionQuantifier = call.getFunctionQuantifier(); + if ((null != functionQuantifier) && !isQuantifierAllowed()) { + throw validator.newValidationError(functionQuantifier, RESOURCE.functionQuantifierNotAllowed(call.getOperator().getName())); } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlFunctionalOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlFunctionalOperator.java index 0d57ef151ad8..7f7f36ebc477 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlFunctionalOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlFunctionalOperator.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.type.SqlOperandTypeInference; import org.apache.calcite.sql.type.SqlReturnTypeInference; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * SqlFunctionalOperator is a base class for special operators which use * functional syntax. @@ -32,9 +34,9 @@ public SqlFunctionalOperator( SqlKind kind, int pred, boolean isLeftAssoc, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { super( name, kind, diff --git a/core/src/main/java/org/apache/calcite/sql/SqlGroupedWindowFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlGroupedWindowFunction.java index fd9dd88e95d7..e7b9a3a6d689 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlGroupedWindowFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlGroupedWindowFunction.java @@ -25,6 +25,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -47,7 +49,7 @@ */ public class SqlGroupedWindowFunction extends SqlFunction { /** The grouped function, if this an auxiliary function; null otherwise. */ - public final SqlGroupedWindowFunction groupFunction; + public final @Nullable SqlGroupedWindowFunction groupFunction; /** Creates a SqlGroupedWindowFunction. * @@ -61,10 +63,10 @@ public class SqlGroupedWindowFunction extends SqlFunction { * @param category Categorization for function */ public SqlGroupedWindowFunction(String name, SqlKind kind, - SqlGroupedWindowFunction groupFunction, + @Nullable SqlGroupedWindowFunction groupFunction, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory category) { + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory category) { super(name, kind, returnTypeInference, operandTypeInference, operandTypeChecker, category); this.groupFunction = groupFunction; @@ -74,16 +76,16 @@ public SqlGroupedWindowFunction(String name, SqlKind kind, @Deprecated // to be removed before 2.0 public SqlGroupedWindowFunction(String name, SqlKind kind, - SqlGroupedWindowFunction groupFunction, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlGroupedWindowFunction groupFunction, + @Nullable SqlOperandTypeChecker operandTypeChecker) { this(name, kind, groupFunction, ReturnTypes.ARG0, null, operandTypeChecker, SqlFunctionCategory.SYSTEM); } @Deprecated // to be removed before 2.0 public SqlGroupedWindowFunction(SqlKind kind, - SqlGroupedWindowFunction groupFunction, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlGroupedWindowFunction groupFunction, + @Nullable SqlOperandTypeChecker operandTypeChecker) { this(kind.name(), kind, groupFunction, ReturnTypes.ARG0, null, operandTypeChecker, SqlFunctionCategory.SYSTEM); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java index 3db2bad0419e..eb1e36725a05 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java @@ -28,8 +28,12 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * A SqlIdentifier is an identifier, possibly compound. @@ -59,12 +63,12 @@ public class SqlIdentifier extends SqlNode { /** * This identifier's collation (if any). */ - final SqlCollation collation; + final @Nullable SqlCollation collation; /** * A list of the positions of the components of compound identifiers. */ - protected ImmutableList componentPositions; + protected @Nullable ImmutableList componentPositions; //~ Constructors ----------------------------------------------------------- @@ -75,9 +79,9 @@ public class SqlIdentifier extends SqlNode { */ public SqlIdentifier( List names, - SqlCollation collation, + @Nullable SqlCollation collation, SqlParserPos pos, - List componentPositions) { + @Nullable List componentPositions) { super(pos); this.names = ImmutableList.copyOf(names); this.collation = collation; @@ -98,7 +102,7 @@ public SqlIdentifier(List names, SqlParserPos pos) { */ public SqlIdentifier( String name, - SqlCollation collation, + @Nullable SqlCollation collation, SqlParserPos pos) { this(ImmutableList.of(name), collation, pos, null); } @@ -156,7 +160,7 @@ public static List toStar(List names) { * @param names Names of components * @param poses Positions of components */ - public void setNames(List names, List poses) { + public void setNames(List names, @Nullable List poses) { this.names = ImmutableList.copyOf(names); this.componentPositions = poses == null ? null : ImmutableList.copyOf(poses); @@ -246,11 +250,12 @@ public SqlIdentifier plus(String name, SqlParserPos pos) { ImmutableList.builder().addAll(this.names).add(name).build(); final ImmutableList componentPositions; final SqlParserPos pos2; - if (this.componentPositions != null) { + ImmutableList thisComponentPositions = this.componentPositions; + if (thisComponentPositions != null) { final ImmutableList.Builder builder = ImmutableList.builder(); componentPositions = - builder.addAll(this.componentPositions).add(pos).build(); + builder.addAll(thisComponentPositions).add(pos).build(); pos2 = SqlParserPos.sum(builder.add(this.pos).build()); } else { componentPositions = null; @@ -300,7 +305,7 @@ public SqlIdentifier skipLast(int n) { validator.validateIdentifier(this, scope); } - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { if (!(node instanceof SqlIdentifier)) { return litmus.fail("{} != {}", this, node); } @@ -320,7 +325,8 @@ public SqlIdentifier skipLast(int n) { return visitor.visit(this); } - public SqlCollation getCollation() { + @Pure + public @Nullable SqlCollation getCollation() { return collation; } @@ -356,12 +362,13 @@ public boolean isComponentQuoted(int i) { && componentPositions.get(i).isQuoted(); } - @Override public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) { + @Override public SqlMonotonicity getMonotonicity(@Nullable SqlValidatorScope scope) { // for "star" column, whether it's static or dynamic return not_monotonic directly. if (Util.last(names).equals("") || DynamicRecordType.isDynamicStarColName(Util.last(names))) { return SqlMonotonicity.NOT_MONOTONIC; } + Objects.requireNonNull(scope, "scope"); // First check for builtin functions which don't have parentheses, // like "LOCALTIME". final SqlValidator validator = scope.getValidator(); @@ -370,6 +377,7 @@ public boolean isComponentQuoted(int i) { return call.getMonotonicity(scope); } final SqlQualified qualified = scope.fullyQualify(this); + assert qualified.namespace != null : "namespace must not be null in " + qualified; final SqlIdentifier fqId = qualified.identifier; return qualified.namespace.resolve().getMonotonicity(Util.last(fqId.names)); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlInfixOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlInfixOperator.java index 10489adb8a47..49aa1f0f737b 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlInfixOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlInfixOperator.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.type.SqlOperandTypeInference; import org.apache.calcite.sql.type.SqlReturnTypeInference; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A generalization of a binary operator to involve several (two or more) * arguments, and keywords between each pair of arguments. @@ -39,9 +41,9 @@ protected SqlInfixOperator( String[] names, SqlKind kind, int precedence, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { super( names[0], kind, diff --git a/core/src/main/java/org/apache/calcite/sql/SqlInsert.java b/core/src/main/java/org/apache/calcite/sql/SqlInsert.java index 30f8cf24fca0..3b8c355cee5a 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlInsert.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlInsert.java @@ -21,8 +21,12 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; + /** * A SqlInsert is a node of a parse tree which represents an INSERT * statement. @@ -30,8 +34,10 @@ public class SqlInsert extends SqlCall { public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("INSERT", SqlKind.INSERT) { - @Override public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, + @Nullable SqlNode... operands) { return new SqlInsert( pos, (SqlNodeList) operands[0], @@ -44,7 +50,7 @@ public class SqlInsert extends SqlCall { SqlNodeList keywords; SqlNode targetTable; SqlNode source; - SqlNodeList columnList; + @Nullable SqlNodeList columnList; //~ Constructors ----------------------------------------------------------- @@ -52,7 +58,7 @@ public SqlInsert(SqlParserPos pos, SqlNodeList keywords, SqlNode targetTable, SqlNode source, - SqlNodeList columnList) { + @Nullable SqlNodeList columnList) { super(pos); this.keywords = keywords; this.targetTable = targetTable; @@ -71,6 +77,7 @@ public SqlInsert(SqlParserPos pos, return OPERATOR; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(keywords, targetTable, source, columnList); } @@ -84,7 +91,8 @@ public final boolean isUpsert() { return getModifierNode(SqlInsertKeyword.UPSERT) != null; } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: keywords = (SqlNodeList) operand; @@ -126,11 +134,12 @@ public void setSource(SqlSelect source) { * Returns the list of target column names, or null for all columns in the * target table. */ - public SqlNodeList getTargetColumnList() { + @Pure + public @Nullable SqlNodeList getTargetColumnList() { return columnList; } - public final SqlNode getModifierNode(SqlInsertKeyword modifier) { + public final @Nullable SqlNode getModifierNode(SqlInsertKeyword modifier) { for (SqlNode keyword : keywords) { SqlInsertKeyword keyword2 = ((SqlLiteral) keyword).symbolValue(SqlInsertKeyword.class); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlInternalOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlInternalOperator.java index e72f937c6dc1..70c590650afc 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlInternalOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlInternalOperator.java @@ -25,6 +25,8 @@ import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.sql.validate.SqlValidatorScope; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Generic operator for nodes with internal syntax. * @@ -60,7 +62,7 @@ public SqlInternalOperator( int prec, boolean isLeftAssoc, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, SqlOperandTypeChecker operandTypeChecker) { super( name, diff --git a/core/src/main/java/org/apache/calcite/sql/SqlIntervalLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlIntervalLiteral.java index bbaae81fc171..7cf370a8feb2 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlIntervalLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlIntervalLiteral.java @@ -20,8 +20,12 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Litmus; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * A SQL literal representing a time interval. * @@ -53,7 +57,7 @@ protected SqlIntervalLiteral( } private SqlIntervalLiteral( - IntervalValue intervalValue, + @Nullable IntervalValue intervalValue, SqlTypeName sqlTypeName, SqlParserPos pos) { super( @@ -77,7 +81,7 @@ private SqlIntervalLiteral( @SuppressWarnings("deprecation") @Override public int signum() { - return ((IntervalValue) value).signum(); + return ((IntervalValue) castNonNull(value)).signum(); } //~ Inner Classes ---------------------------------------------------------- @@ -109,7 +113,7 @@ public static class IntervalValue { this.intervalStr = intervalStr; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof IntervalValue)) { return false; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java b/core/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java index d8afe4398fcf..64ba997b75a8 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java @@ -29,14 +29,17 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nonnull; import static org.apache.calcite.util.Static.RESOURCE; +import static org.checkerframework.checker.nullness.NullnessUtil.castNonNull; + /** * Represents an INTERVAL qualifier. * @@ -101,7 +104,7 @@ public class SqlIntervalQualifier extends SqlNode { public SqlIntervalQualifier( TimeUnit startUnit, int startPrecision, - TimeUnit endUnit, + @Nullable TimeUnit endUnit, int fractionalSecondPrecision, SqlParserPos pos) { super(pos); @@ -109,14 +112,14 @@ public SqlIntervalQualifier( endUnit = null; } this.timeUnitRange = - TimeUnitRange.of(Objects.requireNonNull(startUnit), endUnit); + TimeUnitRange.of(Objects.requireNonNull(startUnit, "startUnit"), endUnit); this.startPrecision = startPrecision; this.fractionalSecondPrecision = fractionalSecondPrecision; } public SqlIntervalQualifier( TimeUnit startUnit, - TimeUnit endUnit, + @Nullable TimeUnit endUnit, SqlParserPos pos) { this( startUnit, @@ -128,7 +131,7 @@ public SqlIntervalQualifier( //~ Methods ---------------------------------------------------------------- - @Nonnull @Override public SqlKind getKind() { + @Override public SqlKind getKind() { return SqlKind.INTERVAL_QUALIFIER; } @@ -188,7 +191,10 @@ public SqlTypeName typeName() { return visitor.visit(this); } - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { + if (node == null) { + return litmus.fail("other==null"); + } final String thisString = this.toString(); final String thatString = node.toString(); if (!thisString.equals(thatString)) { @@ -739,7 +745,7 @@ private int[] evaluateIntervalLiteralAsDayToSecond( } if (hasFractionalSecond) { - secondFrac = normalizeSecondFraction(m.group(5)); + secondFrac = normalizeSecondFraction(castNonNull(m.group(5))); } else { secondFrac = ZERO; } @@ -890,7 +896,7 @@ private int[] evaluateIntervalLiteralAsHourToSecond( } if (hasFractionalSecond) { - secondFrac = normalizeSecondFraction(m.group(4)); + secondFrac = normalizeSecondFraction(castNonNull(m.group(4))); } else { secondFrac = ZERO; } @@ -996,7 +1002,7 @@ private int[] evaluateIntervalLiteralAsMinuteToSecond( } if (hasFractionalSecond) { - secondFrac = normalizeSecondFraction(m.group(3)); + secondFrac = normalizeSecondFraction(castNonNull(m.group(3))); } else { secondFrac = ZERO; } @@ -1064,7 +1070,7 @@ private int[] evaluateIntervalLiteralAsSecond( } if (hasFractionalSecond) { - secondFrac = normalizeSecondFraction(m.group(2)); + secondFrac = normalizeSecondFraction(castNonNull(m.group(2))); } else { secondFrac = ZERO; } @@ -1162,7 +1168,7 @@ public int[] evaluateIntervalLiteral(String value, SqlParserPos pos, } private BigDecimal parseField(Matcher m, int i) { - return new BigDecimal(m.group(i)); + return new BigDecimal(castNonNull(m.group(i))); } private CalciteContextException invalidValueException(SqlParserPos pos, diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java b/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java index bc7b3d7f9edd..eaf47de6ee7f 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlJdbcDataTypeName.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Defines the name of the types which can occur as a type argument * in a JDBC {fn CONVERT(value, type)} function. @@ -63,8 +65,8 @@ public enum SqlJdbcDataTypeName implements Symbolizable { SQL_INTERVAL_MINUTE_TO_SECOND(TimeUnitRange.MINUTE_TO_SECOND), SQL_INTERVAL_SECOND(TimeUnitRange.SECOND); - private final TimeUnitRange range; - private final SqlTypeName typeName; + private final @Nullable TimeUnitRange range; + private final @Nullable SqlTypeName typeName; SqlJdbcDataTypeName(SqlTypeName typeName) { this(typeName, null); @@ -74,7 +76,7 @@ public enum SqlJdbcDataTypeName implements Symbolizable { this(null, range); } - SqlJdbcDataTypeName(SqlTypeName typeName, TimeUnitRange range) { + SqlJdbcDataTypeName(@Nullable SqlTypeName typeName, @Nullable TimeUnitRange range) { assert (typeName == null) != (range == null); this.typeName = typeName; this.range = range; diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJdbcFunctionCall.java b/core/src/main/java/org/apache/calcite/sql/SqlJdbcFunctionCall.java index 058ed5914808..b70f2ef41e4c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlJdbcFunctionCall.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlJdbcFunctionCall.java @@ -28,6 +28,8 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; import java.util.Objects; @@ -398,10 +400,10 @@ public class SqlJdbcFunctionCall extends SqlFunction { //~ Instance fields -------------------------------------------------------- private final String jdbcName; - private final MakeCall lookupMakeCallObj; - private SqlCall lookupCall; + private final @Nullable MakeCall lookupMakeCallObj; + private @Nullable SqlCall lookupCall; - private SqlNode[] thisOperands; + private SqlNode @Nullable [] thisOperands; //~ Constructors ----------------------------------------------------------- @@ -436,9 +438,9 @@ private static String constructFuncList(String... functionNames) { } @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { thisOperands = operands; return super.createCall(functionQualifier, pos, operands); } @@ -570,7 +572,7 @@ private interface MakeCall { SqlOperator getOperator(); - String isValidArgCount(SqlCallBinding binding); + @Nullable String isValidArgCount(SqlCallBinding binding); } /** Converter that calls a built-in function with the same arguments. */ @@ -589,7 +591,7 @@ public SimpleMakeCall(SqlOperator operator) { return operator.createCall(pos, operands); } - @Override public String isValidArgCount(SqlCallBinding binding) { + @Override public @Nullable String isValidArgCount(SqlCallBinding binding) { return null; // any number of arguments is valid } } @@ -618,7 +620,7 @@ private static class PermutingMakeCall extends SimpleMakeCall { return super.createCall(pos, reorder(operands)); } - @Override public String isValidArgCount(SqlCallBinding binding) { + @Override public @Nullable String isValidArgCount(SqlCallBinding binding) { if (order.length == binding.getOperandCount()) { return null; // operand count is valid } else { @@ -669,6 +671,7 @@ private static class JdbcToInternalLookupTable { private final Map map; + @SuppressWarnings("method.invocation.invalid") private JdbcToInternalLookupTable() { // A table of all functions can be found at // http://java.sun.com/products/jdbc/driverdevs.html @@ -762,7 +765,7 @@ private JdbcToInternalLookupTable() { assert typeOperand.getKind() == SqlKind.LITERAL; SqlJdbcDataTypeName jdbcType = ((SqlLiteral) typeOperand) - .symbolValue(SqlJdbcDataTypeName.class); + .getValueAs(SqlJdbcDataTypeName.class); return super.createCall(pos, operands[0], jdbcType.createDataType(typeOperand.pos)); } @@ -790,7 +793,7 @@ private MakeCall simple(SqlOperator operator) { * Tries to lookup a given function name JDBC to an internal * representation. Returns null if no function defined. */ - public MakeCall lookup(String name) { + public @Nullable MakeCall lookup(String name) { return map.get(name); } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJoin.java b/core/src/main/java/org/apache/calcite/sql/SqlJoin.java index 41342bc853b2..5f6a0b82f1b5 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlJoin.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlJoin.java @@ -23,8 +23,11 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; -import java.util.Objects; + +import static java.util.Objects.requireNonNull; /** * Parse tree node representing a {@code JOIN} clause. @@ -52,24 +55,24 @@ public class SqlJoin extends SqlCall { * {@link JoinConditionType}. */ SqlLiteral conditionType; - SqlNode condition; + @Nullable SqlNode condition; //~ Constructors ----------------------------------------------------------- public SqlJoin(SqlParserPos pos, SqlNode left, SqlLiteral natural, SqlLiteral joinType, SqlNode right, SqlLiteral conditionType, - SqlNode condition) { + @Nullable SqlNode condition) { super(pos); this.left = left; - this.natural = Objects.requireNonNull(natural); - this.joinType = Objects.requireNonNull(joinType); + this.natural = requireNonNull(natural); + this.joinType = requireNonNull(joinType); this.right = right; - this.conditionType = Objects.requireNonNull(conditionType); + this.conditionType = requireNonNull(conditionType); this.condition = condition; Preconditions.checkArgument(natural.getTypeName() == SqlTypeName.BOOLEAN); - Objects.requireNonNull(conditionType.symbolValue(JoinConditionType.class)); - Objects.requireNonNull(joinType.symbolValue(JoinType.class)); + conditionType.getValueAs(JoinConditionType.class); + joinType.getValueAs(JoinType.class); } //~ Methods ---------------------------------------------------------------- @@ -82,12 +85,14 @@ public SqlJoin(SqlParserPos pos, SqlNode left, SqlLiteral natural, return SqlKind.JOIN; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(left, natural, joinType, right, conditionType, condition); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: left = operand; @@ -112,13 +117,13 @@ public SqlJoin(SqlParserPos pos, SqlNode left, SqlLiteral natural, } } - public final SqlNode getCondition() { + public final @Nullable SqlNode getCondition() { return condition; } /** Returns a {@link JoinConditionType}, never null. */ public final JoinConditionType getConditionType() { - return conditionType.symbolValue(JoinConditionType.class); + return conditionType.getValueAs(JoinConditionType.class); } public SqlLiteral getConditionTypeNode() { @@ -127,7 +132,7 @@ public SqlLiteral getConditionTypeNode() { /** Returns a {@link JoinType}, never null. */ public final JoinType getJoinType() { - return joinType.symbolValue(JoinType.class); + return joinType.getValueAs(JoinType.class); } public SqlLiteral getJoinTypeNode() { @@ -179,10 +184,11 @@ private SqlJoinOperator() { return SqlSyntax.SPECIAL; } + @SuppressWarnings("argument.type.incompatible") @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { assert functionQualifier == null; return new SqlJoin(pos, operands[0], (SqlLiteral) operands[1], (SqlLiteral) operands[2], operands[3], (SqlLiteral) operands[4], @@ -227,22 +233,24 @@ private SqlJoinOperator() { throw Util.unexpected(join.getJoinType()); } join.right.unparse(writer, getRightPrec(), rightPrec); - if (join.condition != null) { + SqlNode joinCondition = join.condition; + if (joinCondition != null) { switch (join.getConditionType()) { case USING: // No need for an extra pair of parens -- the condition is a // list. The result is something like "USING (deptno, gender)". writer.keyword("USING"); - assert join.condition instanceof SqlNodeList; + assert joinCondition instanceof SqlNodeList + : "joinCondition should be SqlNodeList, got " + joinCondition; final SqlWriter.Frame frame = writer.startList(FRAME_TYPE, "(", ")"); - join.condition.unparse(writer, 0, 0); + joinCondition.unparse(writer, 0, 0); writer.endList(frame); break; case ON: writer.keyword("ON"); - join.condition.unparse(writer, leftPrec, rightPrec); + joinCondition.unparse(writer, leftPrec, rightPrec); break; default: diff --git a/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java index dfa6fcb6a9cb..de89bdd2f201 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java @@ -39,15 +39,19 @@ import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.Calendar; import java.util.Objects; -import javax.annotation.Nonnull; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * A SqlLiteral is a constant. It is, appropriately, immutable. * @@ -157,7 +161,7 @@ public class SqlLiteral extends SqlNode { * The value of this literal. The type of the value must be appropriate for * the typeName, as defined by the {@link #valueMatchesType} method. */ - protected final Object value; + protected final @Nullable Object value; //~ Constructors ----------------------------------------------------------- @@ -165,7 +169,7 @@ public class SqlLiteral extends SqlNode { * Creates a SqlLiteral. */ protected SqlLiteral( - Object value, + @Nullable Object value, SqlTypeName typeName, SqlParserPos pos) { super(pos); @@ -185,7 +189,7 @@ public SqlTypeName getTypeName() { /** Returns whether value is appropriate for its type. (We have rules about * these things!) */ public static boolean valueMatchesType( - Object value, + @Nullable Object value, SqlTypeName typeName) { switch (typeName) { case BOOLEAN: @@ -236,7 +240,7 @@ public static boolean valueMatchesType( return new SqlLiteral(value, typeName, pos); } - @Override public @Nonnull SqlKind getKind() { + @Override public SqlKind getKind() { return SqlKind.LITERAL; } @@ -250,7 +254,7 @@ public static boolean valueMatchesType( * @see #booleanValue() * @see #symbolValue(Class) */ - public Object getValue() { + public @Nullable Object getValue() { return value; } @@ -275,13 +279,16 @@ public Object getValue() { * * @throws AssertionError if the value type is not supported */ - @Nonnull public T getValueAs(Class clazz) { + public T getValueAs(Class clazz) { + Object value = this.value; if (clazz.isInstance(value)) { return clazz.cast(value); } - switch (typeName) { - case NULL: + if (typeName == SqlTypeName.NULL) { return clazz.cast(NullSentinel.INSTANCE); + } + requireNonNull(value, "value"); + switch (typeName) { case CHAR: if (clazz == String.class) { return clazz.cast(((NlsString) value).getValue()); @@ -294,7 +301,7 @@ public Object getValue() { break; case DECIMAL: if (clazz == Long.class) { - return clazz.cast(((BigDecimal) value).unscaledValue().longValue()); + return clazz.cast(((BigDecimal) value).longValueExact()); } // fall through case BIGINT: @@ -305,13 +312,13 @@ public Object getValue() { case REAL: case FLOAT: if (clazz == Long.class) { - return clazz.cast(((BigDecimal) value).longValue()); + return clazz.cast(((BigDecimal) value).longValueExact()); } else if (clazz == Integer.class) { - return clazz.cast(((BigDecimal) value).intValue()); + return clazz.cast(((BigDecimal) value).intValueExact()); } else if (clazz == Short.class) { - return clazz.cast(((BigDecimal) value).shortValue()); + return clazz.cast(((BigDecimal) value).shortValueExact()); } else if (clazz == Byte.class) { - return clazz.cast(((BigDecimal) value).byteValue()); + return clazz.cast(((BigDecimal) value).byteValueExact()); } else if (clazz == Double.class) { return clazz.cast(((BigDecimal) value).doubleValue()); } else if (clazz == Float.class) { @@ -380,19 +387,19 @@ public Object getValue() { /** Returns the value as a symbol. */ @Deprecated // to be removed before 2.0 - public > E symbolValue_() { + public > @Nullable E symbolValue_() { //noinspection unchecked - return (E) value; + return (@Nullable E) value; } /** Returns the value as a symbol. */ - public > E symbolValue(Class class_) { + public > @Nullable E symbolValue(Class class_) { return class_.cast(value); } /** Returns the value as a boolean. */ public boolean booleanValue() { - return (Boolean) value; + return (Boolean) requireNonNull(value); } /** @@ -402,7 +409,7 @@ public boolean booleanValue() { * @see #createSymbol(Enum, SqlParserPos) */ public static SqlSampleSpec sampleValue(SqlNode node) { - return (SqlSampleSpec) ((SqlLiteral) node).value; + return (SqlSampleSpec) requireNonNull(((SqlLiteral) node).value); } /** @@ -429,25 +436,26 @@ public static SqlSampleSpec sampleValue(SqlNode node) { *

  • Otherwise throws {@link IllegalArgumentException}. * */ - public static Comparable value(SqlNode node) + public static @Nullable Comparable value(SqlNode node) throws IllegalArgumentException { if (node instanceof SqlLiteral) { final SqlLiteral literal = (SqlLiteral) node; if (literal.getTypeName() == SqlTypeName.SYMBOL) { - return (Enum) literal.value; + return (Enum) literal.value; } - switch (literal.getTypeName().getFamily()) { + // Literals always have non-null family + switch (requireNonNull(literal.getTypeName().getFamily())) { case CHARACTER: return (NlsString) literal.value; case NUMERIC: return (BigDecimal) literal.value; case INTERVAL_YEAR_MONTH: final SqlIntervalLiteral.IntervalValue valMonth = - (SqlIntervalLiteral.IntervalValue) literal.value; + (SqlIntervalLiteral.IntervalValue) requireNonNull(literal.value); return valMonth.getSign() * SqlParserUtil.intervalToMonths(valMonth); case INTERVAL_DAY_TIME: final SqlIntervalLiteral.IntervalValue valTime = - (SqlIntervalLiteral.IntervalValue) literal.value; + (SqlIntervalLiteral.IntervalValue) requireNonNull(literal.value); return valTime.getSign() * SqlParserUtil.intervalToMillis(valTime); default: break; @@ -469,7 +477,7 @@ public static Comparable value(SqlNode node) return value(((SqlCall) node).operand(0)); case MINUS_PREFIX: assert node instanceof SqlCall; - Comparable o = value(((SqlCall) node).operand(0)); + Comparable o = value(((SqlCall) node).operand(0)); if (o instanceof BigDecimal) { BigDecimal bigDecimal = (BigDecimal) o; return bigDecimal.negate(); @@ -491,12 +499,12 @@ public static String stringValue(SqlNode node) { if (node instanceof SqlLiteral) { SqlLiteral literal = (SqlLiteral) node; assert SqlTypeUtil.inCharFamily(literal.getTypeName()); - return literal.value.toString(); + return requireNonNull(literal.value).toString(); } else if (SqlUtil.isLiteralChain(node)) { final SqlLiteral literal = SqlLiteralChainOperator.concatenateOperands((SqlCall) node); assert SqlTypeUtil.inCharFamily(literal.getTypeName()); - return literal.value.toString(); + return requireNonNull(literal.value).toString(); } else if (node instanceof SqlCall && ((SqlCall) node).getOperator() == SqlStdOperatorTable.CAST) { return stringValue(((SqlCall) node).operand(0)); @@ -540,7 +548,7 @@ public static SqlLiteral unchain(SqlNode node) { * * @return string representation of the value */ - public String toValue() { + public @Nullable String toValue() { if (value == null) { return null; } @@ -562,7 +570,7 @@ public String toValue() { return visitor.visit(this); } - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { if (!(node instanceof SqlLiteral)) { return litmus.fail("{} != {}", this, node); } @@ -573,7 +581,7 @@ public String toValue() { return litmus.succeed(); } - @Override public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) { + @Override public SqlMonotonicity getMonotonicity(@Nullable SqlValidatorScope scope) { return SqlMonotonicity.CONSTANT; } @@ -609,7 +617,7 @@ public static SqlLiteral createUnknown(SqlParserPos pos) { * * @see #symbolValue(Class) */ - public static SqlLiteral createSymbol(Enum o, SqlParserPos pos) { + public static SqlLiteral createSymbol(@Nullable Enum o, SqlParserPos pos) { return new SqlLiteral(o, SqlTypeName.SYMBOL, pos); } @@ -622,7 +630,7 @@ public static SqlLiteral createSample( return new SqlLiteral(sampleSpec, SqlTypeName.SYMBOL, pos); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof SqlLiteral)) { return false; } @@ -646,7 +654,7 @@ public int intValue(boolean exact) { switch (typeName) { case DECIMAL: case DOUBLE: - BigDecimal bd = (BigDecimal) value; + BigDecimal bd = (BigDecimal) requireNonNull(value); if (exact) { try { return bd.intValueExact(); @@ -674,7 +682,7 @@ public long longValue(boolean exact) { switch (typeName) { case DECIMAL: case DOUBLE: - BigDecimal bd = (BigDecimal) value; + BigDecimal bd = (BigDecimal) requireNonNull(value); if (exact) { try { return bd.longValueExact(); @@ -697,14 +705,14 @@ public long longValue(boolean exact) { */ @Deprecated // to be removed before 2.0 public int signum() { - return bigDecimalValue().compareTo( + return castNonNull(bigDecimalValue()).compareTo( BigDecimal.ZERO); } /** * Returns a numeric literal's value as a {@link BigDecimal}. */ - public BigDecimal bigDecimalValue() { + public @Nullable BigDecimal bigDecimalValue() { switch (typeName) { case DECIMAL: case DOUBLE: @@ -716,7 +724,7 @@ public BigDecimal bigDecimalValue() { @Deprecated // to be removed before 2.0 public String getStringValue() { - return ((NlsString) value).getValue(); + return ((NlsString) requireNonNull(value)).getValue(); } @Override public void unparse( @@ -739,15 +747,10 @@ public String getStringValue() { throw Util.unexpected(typeName); case SYMBOL: - if (value instanceof Enum) { - Enum enumVal = (Enum) value; - writer.keyword(enumVal.toString()); - } else { - writer.keyword(String.valueOf(value)); - } + writer.keyword(String.valueOf(value)); break; default: - writer.literal(value.toString()); + writer.literal(String.valueOf(value)); } } @@ -760,11 +763,11 @@ public RelDataType createSqlType(RelDataTypeFactory typeFactory) { ret = typeFactory.createTypeWithNullability(ret, null == value); return ret; case BINARY: - bitString = (BitString) value; + bitString = (BitString) requireNonNull(value); int bitCount = bitString.getBitCount(); return typeFactory.createSqlType(SqlTypeName.BINARY, bitCount / 8); case CHAR: - NlsString string = (NlsString) value; + NlsString string = (NlsString) requireNonNull(value); Charset charset = string.getCharset(); if (null == charset) { charset = typeFactory.getDefaultCharset(); @@ -798,7 +801,7 @@ public RelDataType createSqlType(RelDataTypeFactory typeFactory) { case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: SqlIntervalLiteral.IntervalValue intervalValue = - (SqlIntervalLiteral.IntervalValue) value; + (SqlIntervalLiteral.IntervalValue) requireNonNull(value); return typeFactory.createSqlIntervalType( intervalValue.getIntervalQualifier()); @@ -879,7 +882,7 @@ public static SqlNumericLiteral createNegative( SqlNumericLiteral num, SqlParserPos pos) { return new SqlNumericLiteral( - ((BigDecimal) num.getValue()).negate(), + ((BigDecimal) requireNonNull(num.getValue())).negate(), num.getPrec(), num.getScale(), num.isExact(), @@ -986,7 +989,7 @@ public static SqlCharStringLiteral createCharString( */ public static SqlCharStringLiteral createCharString( String s, - String charSet, + @Nullable String charSet, SqlParserPos pos) { NlsString slit = new NlsString(s, charSet, null); return new SqlCharStringLiteral(slit, pos); @@ -1006,7 +1009,7 @@ public SqlLiteral unescapeUnicode(char unicodeEscapeChar) { return this; } assert SqlTypeUtil.inCharFamily(getTypeName()); - NlsString ns = (NlsString) value; + NlsString ns = (NlsString) requireNonNull(value); String s = ns.getValue(); StringBuilder sb = new StringBuilder(); int n = s.length(); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlMatchFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlMatchFunction.java index 361255108aca..7c8f3a5673b2 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlMatchFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlMatchFunction.java @@ -20,11 +20,13 @@ import org.apache.calcite.sql.type.SqlOperandTypeInference; import org.apache.calcite.sql.type.SqlReturnTypeInference; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Base class for all functions used in MATCH_RECOGNIZE. */ public class SqlMatchFunction extends SqlFunction { public SqlMatchFunction(String name, SqlKind kind, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory category) { super(name, kind, returnTypeInference, operandTypeInference, operandTypeChecker, category); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlMatchRecognize.java b/core/src/main/java/org/apache/calcite/sql/SqlMatchRecognize.java index c3b132229f74..e6c83bd469a9 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlMatchRecognize.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlMatchRecognize.java @@ -25,9 +25,11 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; + /** * SqlNode for MATCH_RECOGNIZE clause. @@ -62,19 +64,19 @@ public class SqlMatchRecognize extends SqlCall { private SqlLiteral strictEnd; private SqlNodeList patternDefList; private SqlNodeList measureList; - private SqlNode after; + private @Nullable SqlNode after; private SqlNodeList subsetList; - private SqlLiteral rowsPerMatch; + private @Nullable SqlLiteral rowsPerMatch; private SqlNodeList partitionList; private SqlNodeList orderList; - private SqlLiteral interval; + private @Nullable SqlLiteral interval; /** Creates a SqlMatchRecognize. */ public SqlMatchRecognize(SqlParserPos pos, SqlNode tableRef, SqlNode pattern, SqlLiteral strictStart, SqlLiteral strictEnd, SqlNodeList patternDefList, - SqlNodeList measureList, SqlNode after, SqlNodeList subsetList, - SqlLiteral rowsPerMatch, SqlNodeList partitionList, - SqlNodeList orderList, SqlLiteral interval) { + SqlNodeList measureList, @Nullable SqlNode after, SqlNodeList subsetList, + @Nullable SqlLiteral rowsPerMatch, SqlNodeList partitionList, + SqlNodeList orderList, @Nullable SqlLiteral interval) { super(pos); this.tableRef = Objects.requireNonNull(tableRef); this.pattern = Objects.requireNonNull(pattern); @@ -103,6 +105,7 @@ public SqlMatchRecognize(SqlParserPos pos, SqlNode tableRef, SqlNode pattern, return SqlKind.MATCH_RECOGNIZE; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(tableRef, pattern, strictStart, strictEnd, patternDefList, measureList, after, subsetList, partitionList, orderList); @@ -117,7 +120,8 @@ public SqlMatchRecognize(SqlParserPos pos, SqlNode tableRef, SqlNode pattern, validator.validateMatchRecognize(this); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case OPERAND_TABLE_REF: tableRef = Objects.requireNonNull(operand); @@ -163,7 +167,7 @@ public SqlMatchRecognize(SqlParserPos pos, SqlNode tableRef, SqlNode pattern, } } - @Nonnull public SqlNode getTableRef() { + public SqlNode getTableRef() { return tableRef; } @@ -179,15 +183,15 @@ public SqlLiteral getStrictEnd() { return strictEnd; } - @Nonnull public SqlNodeList getPatternDefList() { + public SqlNodeList getPatternDefList() { return patternDefList; } - @Nonnull public SqlNodeList getMeasureList() { + public SqlNodeList getMeasureList() { return measureList; } - public SqlNode getAfter() { + public @Nullable SqlNode getAfter() { return after; } @@ -195,7 +199,7 @@ public SqlNodeList getSubsetList() { return subsetList; } - public SqlLiteral getRowsPerMatch() { + public @Nullable SqlLiteral getRowsPerMatch() { return rowsPerMatch; } @@ -207,7 +211,7 @@ public SqlNodeList getOrderList() { return orderList; } - public SqlLiteral getInterval() { + public @Nullable SqlLiteral getInterval() { return interval; } @@ -266,10 +270,11 @@ private SqlMatchRecognizeOperator() { return SqlSyntax.SPECIAL; } + @SuppressWarnings("argument.type.incompatible") @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { assert functionQualifier == null; assert operands.length == 12; @@ -340,15 +345,17 @@ private SqlMatchRecognizeOperator() { writer.endList(measureFrame); } - if (pattern.rowsPerMatch != null) { + SqlLiteral rowsPerMatch = pattern.rowsPerMatch; + if (rowsPerMatch != null) { writer.newlineAndIndent(); - pattern.rowsPerMatch.unparse(writer, 0, 0); + rowsPerMatch.unparse(writer, 0, 0); } - if (pattern.after != null) { + SqlNode after = pattern.after; + if (after != null) { writer.newlineAndIndent(); writer.sep("AFTER MATCH"); - pattern.after.unparse(writer, 0, 0); + after.unparse(writer, 0, 0); } writer.newlineAndIndent(); @@ -363,9 +370,10 @@ private SqlMatchRecognizeOperator() { writer.sep("$"); } writer.endList(patternFrame); - if (pattern.interval != null) { + SqlLiteral interval = pattern.interval; + if (interval != null) { writer.sep("WITHIN"); - pattern.interval.unparse(writer, 0, 0); + interval.unparse(writer, 0, 0); } if (pattern.subsetList != null && pattern.subsetList.size() > 0) { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlMerge.java b/core/src/main/java/org/apache/calcite/sql/SqlMerge.java index 167aa8b0a2e3..dba476804d09 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlMerge.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlMerge.java @@ -23,8 +23,12 @@ import org.apache.calcite.util.ImmutableNullableList; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; + /** * A SqlMerge is a node of a parse tree which represents a MERGE * statement. @@ -36,10 +40,10 @@ public class SqlMerge extends SqlCall { SqlNode targetTable; SqlNode condition; SqlNode source; - SqlUpdate updateCall; - SqlInsert insertCall; - SqlSelect sourceSelect; - SqlIdentifier alias; + @Nullable SqlUpdate updateCall; + @Nullable SqlInsert insertCall; + @Nullable SqlSelect sourceSelect; + @Nullable SqlIdentifier alias; //~ Constructors ----------------------------------------------------------- @@ -47,10 +51,10 @@ public SqlMerge(SqlParserPos pos, SqlNode targetTable, SqlNode condition, SqlNode source, - SqlUpdate updateCall, - SqlInsert insertCall, - SqlSelect sourceSelect, - SqlIdentifier alias) { + @Nullable SqlUpdate updateCall, + @Nullable SqlInsert insertCall, + @Nullable SqlSelect sourceSelect, + @Nullable SqlIdentifier alias) { super(pos); this.targetTable = targetTable; this.condition = condition; @@ -71,12 +75,14 @@ public SqlMerge(SqlParserPos pos, return SqlKind.MERGE; } - @Override public List getOperandList() { + @SuppressWarnings("nullness") + @Override public List<@Nullable SqlNode> getOperandList() { return ImmutableNullableList.of(targetTable, condition, source, updateCall, insertCall, sourceSelect, alias); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: assert operand instanceof SqlIdentifier; @@ -89,13 +95,13 @@ public SqlMerge(SqlParserPos pos, source = operand; break; case 3: - updateCall = (SqlUpdate) operand; + updateCall = (@Nullable SqlUpdate) operand; break; case 4: - insertCall = (SqlInsert) operand; + insertCall = (@Nullable SqlInsert) operand; break; case 5: - sourceSelect = (SqlSelect) operand; + sourceSelect = (@Nullable SqlSelect) operand; break; case 6: alias = (SqlIdentifier) operand; @@ -111,7 +117,8 @@ public SqlNode getTargetTable() { } /** Returns the alias for the target table of this MERGE. */ - public SqlIdentifier getAlias() { + @Pure + public @Nullable SqlIdentifier getAlias() { return alias; } @@ -125,12 +132,12 @@ public void setSourceTableRef(SqlNode tableRef) { } /** Returns the UPDATE statement for this MERGE. */ - public SqlUpdate getUpdateCall() { + public @Nullable SqlUpdate getUpdateCall() { return updateCall; } /** Returns the INSERT statement for this MERGE. */ - public SqlInsert getInsertCall() { + public @Nullable SqlInsert getInsertCall() { return insertCall; } @@ -147,7 +154,7 @@ public SqlNode getCondition() { * * @return the source SELECT for the data to be updated */ - public SqlSelect getSourceSelect() { + public @Nullable SqlSelect getSourceSelect() { return sourceSelect; } @@ -161,6 +168,7 @@ public void setSourceSelect(SqlSelect sourceSelect) { final int opLeft = getOperator().getLeftPrec(); final int opRight = getOperator().getRightPrec(); targetTable.unparse(writer, opLeft, opRight); + SqlIdentifier alias = this.alias; if (alias != null) { writer.keyword("AS"); alias.unparse(writer, opLeft, opRight); @@ -174,6 +182,7 @@ public void setSourceSelect(SqlSelect sourceSelect) { writer.keyword("ON"); condition.unparse(writer, opLeft, opRight); + SqlUpdate updateCall = this.updateCall; if (updateCall != null) { writer.newlineAndIndent(); writer.keyword("WHEN MATCHED THEN UPDATE"); @@ -195,11 +204,13 @@ public void setSourceSelect(SqlSelect sourceSelect) { writer.endList(setFrame); } + SqlInsert insertCall = this.insertCall; if (insertCall != null) { writer.newlineAndIndent(); writer.keyword("WHEN NOT MATCHED THEN INSERT"); - if (insertCall.getTargetColumnList() != null) { - insertCall.getTargetColumnList().unparse(writer, opLeft, opRight); + SqlNodeList targetColumnList = insertCall.getTargetColumnList(); + if (targetColumnList != null) { + targetColumnList.unparse(writer, opLeft, opRight); } insertCall.getSource().unparse(writer, opLeft, opRight); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlNode.java b/core/src/main/java/org/apache/calcite/sql/SqlNode.java index 73341be62a39..948392a0ac27 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlNode.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlNode.java @@ -28,6 +28,8 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -35,7 +37,6 @@ import java.util.Set; import java.util.function.UnaryOperator; import java.util.stream.Collector; -import javax.annotation.Nonnull; /** * A SqlNode is a SQL parse tree. @@ -94,7 +95,7 @@ public static E clone(E e) { * @return a {@link SqlKind} value, never null * @see #isA */ - public @Nonnull SqlKind getKind() { + public SqlKind getKind() { return SqlKind.OTHER; } @@ -173,7 +174,7 @@ public SqlString toSqlString(UnaryOperator transform) { * @param forceParens Whether to wrap all expressions in parentheses; * useful for parse test, but false by default */ - public SqlString toSqlString(SqlDialect dialect, boolean forceParens) { + public SqlString toSqlString(@Nullable SqlDialect dialect, boolean forceParens) { return toSqlString(c -> c.withDialect(Util.first(dialect, AnsiSqlDialect.DEFAULT)) .withAlwaysUseParentheses(forceParens) @@ -182,7 +183,7 @@ public SqlString toSqlString(SqlDialect dialect, boolean forceParens) { .withIndentation(0)); } - public SqlString toSqlString(SqlDialect dialect) { + public SqlString toSqlString(@Nullable SqlDialect dialect) { return toSqlString(dialect, false); } @@ -297,7 +298,7 @@ public void validateExpr( * (2 + 3), because the '+' operator is left-associative
  • * */ - public abstract boolean equalsDeep(SqlNode node, Litmus litmus); + public abstract boolean equalsDeep(@Nullable SqlNode node, Litmus litmus); @Deprecated // to be removed before 2.0 public final boolean equalsDeep(SqlNode node, boolean fail) { @@ -336,7 +337,7 @@ public static boolean equalDeep( * * @param scope Scope */ - public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) { + public SqlMonotonicity getMonotonicity(@Nullable SqlValidatorScope scope) { return SqlMonotonicity.NOT_MONOTONIC; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlNodeList.java b/core/src/main/java/org/apache/calcite/sql/SqlNodeList.java index 0d17ada30365..8c72d1d7974b 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlNodeList.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlNodeList.java @@ -24,11 +24,15 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * A SqlNodeList is a list of {@link SqlNode}s. It is also a * {@link SqlNode}, so may appear in a parse tree. @@ -43,7 +47,7 @@ public class SqlNodeList extends SqlNode implements Iterable { */ public static final SqlNodeList EMPTY = new SqlNodeList(SqlParserPos.ZERO) { - @Override public void add(SqlNode node) { + @Override public void add(@Nullable SqlNode node) { throw new UnsupportedOperationException(); } }; @@ -62,7 +66,9 @@ public class SqlNodeList extends SqlNode implements Iterable { //~ Instance fields -------------------------------------------------------- - private final List list; + // Sometimes null values are present in the list, however, it is assumed that callers would + // perform all the required null-checks. + private final List<@Nullable SqlNode> list; //~ Constructors ----------------------------------------------------------- @@ -79,7 +85,7 @@ public SqlNodeList(SqlParserPos pos) { * list. The list is copied, but the nodes in it are not. */ public SqlNodeList( - Collection collection, + Collection collection, SqlParserPos pos) { super(pos); list = new ArrayList<>(collection); @@ -87,29 +93,31 @@ public SqlNodeList( //~ Methods ---------------------------------------------------------------- - // implement Iterable - @Override public Iterator iterator() { + @SuppressWarnings("return.type.incompatible") + @Override public Iterator iterator() { return list.iterator(); } - public List getList() { + @SuppressWarnings("return.type.incompatible") + public List getList() { return list; } - public void add(SqlNode node) { + public void add(@Nullable SqlNode node) { list.add(node); } + @SuppressWarnings("argument.type.incompatible") @Override public SqlNodeList clone(SqlParserPos pos) { return new SqlNodeList(list, pos); } - public SqlNode get(int n) { - return list.get(n); + public /*Nullable*/ SqlNode get(int n) { + return castNonNull(list.get(n)); } - public SqlNode set(int n, SqlNode node) { - return list.set(n, node); + public SqlNode set(int n, @Nullable SqlNode node) { + return castNonNull(list.set(n, node)); } public int size() { @@ -139,6 +147,9 @@ void andOrList(SqlWriter writer, SqlBinaryOperator sepOp) { @Override public void validate(SqlValidator validator, SqlValidatorScope scope) { for (SqlNode child : list) { + if (child == null) { + continue; + } child.validate(validator, scope); } } @@ -147,7 +158,7 @@ void andOrList(SqlWriter writer, SqlBinaryOperator sepOp) { return visitor.visit(this); } - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { if (!(node instanceof SqlNodeList)) { return litmus.fail("{} != {}", this, node); } @@ -158,6 +169,13 @@ void andOrList(SqlWriter writer, SqlBinaryOperator sepOp) { for (int i = 0; i < list.size(); i++) { SqlNode thisChild = list.get(i); final SqlNode thatChild = that.list.get(i); + if (thisChild == null) { + if (thatChild == null) { + continue; + } else { + return litmus.fail(null); + } + } if (!thisChild.equalsDeep(thatChild, litmus)) { return litmus.fail(null); } @@ -165,7 +183,8 @@ void andOrList(SqlWriter writer, SqlBinaryOperator sepOp) { return litmus.succeed(); } - public SqlNode[] toArray() { + @SuppressWarnings("return.type.incompatible") + public /*Nullable*/ SqlNode[] toArray() { return list.toArray(new SqlNode[0]); } @@ -216,6 +235,9 @@ public static SqlNodeList of(SqlNode node1, SqlNode node2, SqlNode... nodes) { // SqlNodeList(SqlLiteral(10), SqlLiteral(20)) } for (SqlNode node : list) { + if (node == null) { + continue; + } node.validateExpr(validator, scope); } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlNumericLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlNumericLiteral.java index d1c0c7e14e08..8cf6bd1ff07f 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlNumericLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlNumericLiteral.java @@ -22,24 +22,29 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.math.BigDecimal; +import static java.util.Objects.requireNonNull; + /** * A numeric SQL literal. */ public class SqlNumericLiteral extends SqlLiteral { //~ Instance fields -------------------------------------------------------- - private Integer prec; - private Integer scale; + private @Nullable Integer prec; + private @Nullable Integer scale; private boolean isExact; //~ Constructors ----------------------------------------------------------- protected SqlNumericLiteral( BigDecimal value, - Integer prec, - Integer scale, + @Nullable Integer prec, + @Nullable Integer scale, boolean isExact, SqlParserPos pos) { super( @@ -53,11 +58,16 @@ protected SqlNumericLiteral( //~ Methods ---------------------------------------------------------------- - public Integer getPrec() { + private BigDecimal getValueNonNull() { + return (BigDecimal) requireNonNull(value, "value"); + } + + public @Nullable Integer getPrec() { return prec; } - public Integer getScale() { + @Pure + public @Nullable Integer getScale() { return scale; } @@ -66,7 +76,7 @@ public boolean isExact() { } @Override public SqlNumericLiteral clone(SqlParserPos pos) { - return new SqlNumericLiteral((BigDecimal) value, getPrec(), getScale(), + return new SqlNumericLiteral(getValueNonNull(), getPrec(), getScale(), isExact, pos); } @@ -78,18 +88,18 @@ public boolean isExact() { } @Override public String toValue() { - BigDecimal bd = (BigDecimal) value; + BigDecimal bd = getValueNonNull(); if (isExact) { - return value.toString(); + return getValueNonNull().toString(); } return Util.toScientificNotation(bd); } @Override public RelDataType createSqlType(RelDataTypeFactory typeFactory) { if (isExact) { - int scaleValue = scale.intValue(); + int scaleValue = requireNonNull(scale, "scale"); if (0 == scaleValue) { - BigDecimal bd = (BigDecimal) value; + BigDecimal bd = getValueNonNull(); SqlTypeName result; long l = bd.longValue(); if ((l >= Integer.MIN_VALUE) && (l <= Integer.MAX_VALUE)) { @@ -103,7 +113,7 @@ public boolean isExact() { // else we have a decimal return typeFactory.createSqlType( SqlTypeName.DECIMAL, - prec.intValue(), + requireNonNull(prec, "prec"), scaleValue); } @@ -113,6 +123,6 @@ public boolean isExact() { } public boolean isInteger() { - return 0 == scale.intValue(); + return scale != null && 0 == scale.intValue(); } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java index 5c93326c6dee..3b86154b8181 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java @@ -39,13 +39,19 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.Supplier; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * A SqlOperator is a type of node in a SQL parse tree (it is NOT a * node in a SQL parse tree). It includes functions, operators such as '=', and @@ -107,13 +113,13 @@ public abstract class SqlOperator { private final int rightPrec; /** Used to infer the return type of a call to this operator. */ - private final SqlReturnTypeInference returnTypeInference; + private final @Nullable SqlReturnTypeInference returnTypeInference; /** Used to infer types of unknown operands. */ - private final SqlOperandTypeInference operandTypeInference; + private final @Nullable SqlOperandTypeInference operandTypeInference; /** Used to validate operand types. */ - private final SqlOperandTypeChecker operandTypeChecker; + private final @Nullable SqlOperandTypeChecker operandTypeChecker; //~ Constructors ----------------------------------------------------------- @@ -125,9 +131,9 @@ protected SqlOperator( SqlKind kind, int leftPrecedence, int rightPrecedence, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { assert kind != null; this.name = name; this.kind = kind; @@ -146,9 +152,9 @@ protected SqlOperator( SqlKind kind, int prec, boolean leftAssoc, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { this( name, kind, @@ -177,7 +183,7 @@ protected static int rightPrec(int prec, boolean leftAssoc) { return prec; } - public SqlOperandTypeChecker getOperandTypeChecker() { + public @Nullable SqlOperandTypeChecker getOperandTypeChecker() { return operandTypeChecker; } @@ -210,6 +216,7 @@ public SqlIdentifier getNameAsId() { return new SqlIdentifier(getName(), SqlParserPos.ZERO); } + @Pure public SqlKind getKind() { return kind; } @@ -242,9 +249,9 @@ public int getRightPrec() { * @param operands Array of operands */ public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { pos = pos.plusAll(Arrays.asList(operands)); return new SqlBasicCall(this, operands, pos, false, functionQualifier); } @@ -260,7 +267,7 @@ public SqlCall createCall( * @param operands List of operands */ public final SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, Iterable operands) { return createCall(functionQualifier, pos, @@ -279,7 +286,7 @@ public final SqlCall createCall( */ public final SqlCall createCall( SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { return createCall(null, pos, operands); } @@ -308,7 +315,7 @@ public final SqlCall createCall( */ public final SqlCall createCall( SqlParserPos pos, - List operandList) { + List operandList) { return createCall( null, pos, @@ -361,7 +368,7 @@ protected void unparseListClause(SqlWriter writer, SqlNode clause) { protected void unparseListClause( SqlWriter writer, SqlNode clause, - SqlKind sepKind) { + @Nullable SqlKind sepKind) { final SqlNodeList nodeList = clause instanceof SqlNodeList ? (SqlNodeList) clause @@ -384,8 +391,7 @@ protected void unparseListClause( writer.list(SqlWriter.FrameTypeEnum.SIMPLE, sepOp, nodeList); } - // override Object - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof SqlOperator)) { return false; } @@ -546,7 +552,7 @@ public RelDataType deriveType( argTypes, null, null, getSyntax(), getKind(), validator.getCatalogReader().nameMatcher(), false); - ((SqlBasicCall) call).setOperator(sqlOperator); + ((SqlBasicCall) call).setOperator(castNonNull(sqlOperator)); RelDataType type = call.getOperator().validateOperands(validator, scope, call); // Validate and determine coercibility and resulting collation @@ -556,7 +562,7 @@ argTypes, null, null, getSyntax(), getKind(), return type; } - protected List constructArgNameList(SqlCall call) { + protected @Nullable List constructArgNameList(SqlCall call) { // If any arguments are named, construct a map. final ImmutableList.Builder nameBuilder = ImmutableList.builder(); for (SqlNode operand : call.getOperandList()) { @@ -577,7 +583,7 @@ protected List constructArgNameList(SqlCall call) { protected List constructOperandList( SqlValidator validator, SqlCall call, - List argNames) { + @Nullable List argNames) { if (argNames == null) { return call.getOperandList(); } @@ -702,7 +708,7 @@ public boolean checkOperandTypes( protected void checkOperandCount( SqlValidator validator, - SqlOperandTypeChecker argType, + @Nullable SqlOperandTypeChecker argType, SqlCall call) { SqlOperandCountRange od = call.getOperator().getOperandCountRange(); if (od.isValidCount(call.operandCount())) { @@ -739,7 +745,7 @@ public boolean validRexOperands(int count, Litmus litmus) { * @return signature template, or null to indicate that a default template * will suffice */ - public String getSignatureTemplate(final int operandsCount) { + public @Nullable String getSignatureTemplate(final int operandsCount) { return null; } @@ -757,14 +763,14 @@ public final String getAllowedSignatures() { * example) can be replaced by a specified name. */ public String getAllowedSignatures(String opNameToUse) { - assert operandTypeChecker != null - : "If you see this, assign operandTypeChecker a value " - + "or override this function"; + requireNonNull(operandTypeChecker, + "If you see this, assign operandTypeChecker a value " + + "or override this function"); return operandTypeChecker.getAllowedSignatures(this, opNameToUse) .trim(); } - public SqlOperandTypeInference getOperandTypeInference() { + public @Nullable SqlOperandTypeInference getOperandTypeInference() { return operandTypeInference; } @@ -788,6 +794,7 @@ public SqlOperandTypeInference getOperandTypeInference() { * @return whether this operator is an analytic function (aggregate function * or window function) */ + @Pure public boolean isAggregator() { return false; } @@ -863,7 +870,7 @@ public boolean isGroupAuxiliary() { * @param visitor Visitor * @param call Call to visit */ - public R acceptCall(SqlVisitor visitor, SqlCall call) { + public @Nullable R acceptCall(SqlVisitor visitor, SqlCall call) { for (SqlNode operand : call.getOperandList()) { if (operand == null) { continue; @@ -901,7 +908,7 @@ public void acceptCall( /** Returns the return type inference strategy for this operator, or null if * return type inference is implemented by a subclass override. */ - public SqlReturnTypeInference getReturnTypeInference() { + public @Nullable SqlReturnTypeInference getReturnTypeInference() { return returnTypeInference; } @@ -912,7 +919,8 @@ public SqlReturnTypeInference getReturnTypeInference() { * * @see Strong */ - public Supplier getStrongPolicyInference() { + @Pure + public @Nullable Supplier getStrongPolicyInference() { return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java index fce400a630ff..1693e89cd5fb 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java @@ -23,6 +23,8 @@ import org.apache.calcite.sql.validate.SqlMonotonicity; import org.apache.calcite.sql.validate.SqlValidatorException; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.List; @@ -92,7 +94,7 @@ public RelDataTypeFactory getTypeFactory() { * @return string value */ @Deprecated // to be removed before 2.0 - public String getStringLiteralOperand(int ordinal) { + public @Nullable String getStringLiteralOperand(int ordinal) { throw new UnsupportedOperationException(); } @@ -131,7 +133,7 @@ public int getIntLiteralOperand(int ordinal) { * * @return value of operand */ - public T getOperandLiteralValue(int ordinal, Class clazz) { + public @Nullable T getOperandLiteralValue(int ordinal, Class clazz) { throw new UnsupportedOperationException(); } @@ -143,12 +145,12 @@ public T getOperandLiteralValue(int ordinal, Class clazz) { * * @return value of operand */ - public Object getOperandLiteralValue(int ordinal, RelDataType type) { + public @Nullable Object getOperandLiteralValue(int ordinal, RelDataType type) { throw new UnsupportedOperationException(); } @Deprecated // to be removed before 2.0 - public Comparable getOperandLiteralValue(int ordinal) { + public @Nullable Comparable getOperandLiteralValue(int ordinal) { return getOperandLiteralValue(ordinal, Comparable.class); } @@ -224,7 +226,7 @@ public List collectOperandTypes() { * @param ordinal Ordinal of the operand * @return Rowtype of the query underlying the cursor */ - public RelDataType getCursorOperand(int ordinal) { + public @Nullable RelDataType getCursorOperand(int ordinal) { throw new UnsupportedOperationException(); } @@ -238,7 +240,7 @@ public RelDataType getCursorOperand(int ordinal) { * @return the name of the parent cursor referenced by the column list * parameter if it is a column list parameter; otherwise, null is returned */ - public String getColumnListParamInfo( + public @Nullable String getColumnListParamInfo( int ordinal, String paramName, List columnList) { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/SqlOperatorTable.java index 3ff6073fb3f1..9027bbfadc09 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOperatorTable.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOperatorTable.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.validate.SqlNameMatcher; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -40,7 +42,7 @@ public interface SqlOperatorTable { * @param nameMatcher Name matcher */ void lookupOperatorOverloads(SqlIdentifier opName, - SqlFunctionCategory category, + @Nullable SqlFunctionCategory category, SqlSyntax syntax, List operatorList, SqlNameMatcher nameMatcher); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOrderBy.java b/core/src/main/java/org/apache/calcite/sql/SqlOrderBy.java index 50a967051be6..5dff9b2d9093 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOrderBy.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOrderBy.java @@ -19,8 +19,11 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; + /** * Parse tree node that represents an {@code ORDER BY} on a query other than a * {@code SELECT} (e.g. {@code VALUES} or {@code UNION}). @@ -31,8 +34,9 @@ */ public class SqlOrderBy extends SqlCall { public static final SqlSpecialOperator OPERATOR = new Operator() { - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { return new SqlOrderBy(pos, operands[0], (SqlNodeList) operands[1], operands[2], operands[3]); } @@ -40,13 +44,13 @@ public class SqlOrderBy extends SqlCall { public final SqlNode query; public final SqlNodeList orderList; - public final SqlNode offset; - public final SqlNode fetch; + public final @Nullable SqlNode offset; + public final @Nullable SqlNode fetch; //~ Constructors ----------------------------------------------------------- public SqlOrderBy(SqlParserPos pos, SqlNode query, SqlNodeList orderList, - SqlNode offset, SqlNode fetch) { + @Nullable SqlNode offset, @Nullable SqlNode fetch) { super(pos); this.query = query; this.orderList = orderList; @@ -64,6 +68,7 @@ public SqlOrderBy(SqlParserPos pos, SqlNode query, SqlNodeList orderList, return OPERATOR; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(query, orderList, offset, fetch); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlPivot.java b/core/src/main/java/org/apache/calcite/sql/SqlPivot.java index dce7dec5c855..b707c6274183 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlPivot.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlPivot.java @@ -26,13 +26,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Collectors; -import javax.annotation.Nonnull; /** * Parse tree node that represents a PIVOT applied to a table reference @@ -66,7 +67,7 @@ public SqlPivot(SqlParserPos pos, SqlNode query, SqlNodeList aggList, //~ Methods ---------------------------------------------------------------- - @Override @Nonnull public SqlOperator getOperator() { + @Override public SqlOperator getOperator() { return OPERATOR; } @@ -74,7 +75,8 @@ public SqlPivot(SqlParserPos pos, SqlNode query, SqlNodeList aggList, return ImmutableNullableList.of(query, aggList, axisList, inList); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("nullness") + @Override public void setOperand(int i, @Nullable SqlNode operand) { // Only 'query' is mutable. (It is required for validation.) switch (i) { case 0: @@ -127,7 +129,7 @@ private static SqlNode strip(SqlNode e) { /** Returns the aggregate list as (alias, call) pairs. * If there is no 'AS', alias is null. */ - public void forEachAgg(BiConsumer consumer) { + public void forEachAgg(BiConsumer<@Nullable String, SqlNode> consumer) { for (SqlNode agg : aggList) { final SqlNode call = SqlUtil.stripAs(agg); final String alias = SqlValidatorUtil.getAlias(agg, -1); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlPostfixOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlPostfixOperator.java index c55085ce0f8a..b4e172a032b5 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlPostfixOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlPostfixOperator.java @@ -25,6 +25,10 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * A postfix unary operator. */ @@ -35,9 +39,9 @@ public SqlPostfixOperator( String name, SqlKind kind, int prec, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { super( name, kind, @@ -54,7 +58,7 @@ public SqlPostfixOperator( return SqlSyntax.POSTFIX; } - @Override public String getSignatureTemplate(final int operandsCount) { + @Override public @Nullable String getSignatureTemplate(final int operandsCount) { Util.discard(operandsCount); return "{1} {0}"; } @@ -79,7 +83,7 @@ public SqlPostfixOperator( validator.getTypeFactory() .createTypeWithCharsetAndCollation( type, - type.getCharset(), + castNonNull(type.getCharset()), collation); } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlPrefixOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlPrefixOperator.java index 5c0bfe7d98ba..a7e2a144b1ca 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlPrefixOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlPrefixOperator.java @@ -26,6 +26,11 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation; + /** * A unary operator. */ @@ -36,9 +41,9 @@ public SqlPrefixOperator( String name, SqlKind kind, int prec, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { super( name, kind, @@ -55,7 +60,7 @@ public SqlPrefixOperator( return SqlSyntax.PREFIX; } - @Override public String getSignatureTemplate(final int operandsCount) { + @Override public @Nullable String getSignatureTemplate(final int operandsCount) { Util.discard(operandsCount); return "{0}{1}"; } @@ -73,14 +78,12 @@ public SqlPrefixOperator( throw new AssertionError("operand's type should have been derived"); } if (SqlTypeUtil.inCharFamily(operandType)) { - SqlCollation collation = operandType.getCollation(); - assert null != collation - : "An implicit or explicit collation should have been set"; + SqlCollation collation = getCollation(operandType); type = validator.getTypeFactory() .createTypeWithCharsetAndCollation( type, - type.getCharset(), + getCharset(type), collation); } } @@ -89,8 +92,7 @@ public SqlPrefixOperator( @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) { if (getName().equals("-")) { - SqlMonotonicity monotonicity = call.getOperandMonotonicity(0); - return monotonicity == null ? null : monotonicity.reverse(); + return call.getOperandMonotonicity(0).reverse(); } return super.getMonotonicity(call); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlRowTypeNameSpec.java b/core/src/main/java/org/apache/calcite/sql/SqlRowTypeNameSpec.java index b0b6ce1ca2b0..c6f22e4db799 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlRowTypeNameSpec.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlRowTypeNameSpec.java @@ -92,7 +92,8 @@ public int getArity() { writer.sep(",", false); p.left.unparse(writer, 0, 0); p.right.unparse(writer, leftPrec, rightPrec); - if (p.right.getNullable() != null && p.right.getNullable()) { + Boolean isNullable = p.right.getNullable(); + if (isNullable != null && isNullable) { // Row fields default is not nullable. writer.print("NULL"); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSelect.java b/core/src/main/java/org/apache/calcite/sql/SqlSelect.java index 5b3a92502cf5..aad7a9283982 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSelect.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSelect.java @@ -21,9 +21,11 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; /** * A SqlSelect is a node of a parse tree which represents a select @@ -39,31 +41,31 @@ public class SqlSelect extends SqlCall { public static final int HAVING_OPERAND = 5; SqlNodeList keywordList; - SqlNodeList selectList; - SqlNode from; - SqlNode where; - SqlNodeList groupBy; - SqlNode having; + @Nullable SqlNodeList selectList; + @Nullable SqlNode from; + @Nullable SqlNode where; + @Nullable SqlNodeList groupBy; + @Nullable SqlNode having; SqlNodeList windowDecls; - SqlNodeList orderBy; - SqlNode offset; - SqlNode fetch; - SqlNodeList hints; + @Nullable SqlNodeList orderBy; + @Nullable SqlNode offset; + @Nullable SqlNode fetch; + @Nullable SqlNodeList hints; //~ Constructors ----------------------------------------------------------- public SqlSelect(SqlParserPos pos, - SqlNodeList keywordList, - SqlNodeList selectList, - SqlNode from, - SqlNode where, - SqlNodeList groupBy, - SqlNode having, - SqlNodeList windowDecls, - SqlNodeList orderBy, - SqlNode offset, - SqlNode fetch, - SqlNodeList hints) { + @Nullable SqlNodeList keywordList, + @Nullable SqlNodeList selectList, + @Nullable SqlNode from, + @Nullable SqlNode where, + @Nullable SqlNodeList groupBy, + @Nullable SqlNode having, + @Nullable SqlNodeList windowDecls, + @Nullable SqlNodeList orderBy, + @Nullable SqlNode offset, + @Nullable SqlNode fetch, + @Nullable SqlNodeList hints) { super(pos); this.keywordList = Objects.requireNonNull(keywordList != null ? keywordList : new SqlNodeList(pos)); @@ -90,12 +92,13 @@ public SqlSelect(SqlParserPos pos, return SqlKind.SELECT; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(keywordList, selectList, from, where, groupBy, having, windowDecls, orderBy, offset, fetch, hints); } - @Override public void setOperand(int i, SqlNode operand) { + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: keywordList = Objects.requireNonNull((SqlNodeList) operand); @@ -136,7 +139,7 @@ public final boolean isDistinct() { return getModifierNode(SqlSelectKeyword.DISTINCT) != null; } - public final SqlNode getModifierNode(SqlSelectKeyword modifier) { + public final @Nullable SqlNode getModifierNode(SqlSelectKeyword modifier) { for (SqlNode keyword : keywordList) { SqlSelectKeyword keyword2 = ((SqlLiteral) keyword).symbolValue(SqlSelectKeyword.class); @@ -147,79 +150,88 @@ public final SqlNode getModifierNode(SqlSelectKeyword modifier) { return null; } - public final SqlNode getFrom() { + @Pure + public final @Nullable SqlNode getFrom() { return from; } - public void setFrom(SqlNode from) { + public void setFrom(@Nullable SqlNode from) { this.from = from; } - public final SqlNodeList getGroup() { + @Pure + public final @Nullable SqlNodeList getGroup() { return groupBy; } - public void setGroupBy(SqlNodeList groupBy) { + public void setGroupBy(@Nullable SqlNodeList groupBy) { this.groupBy = groupBy; } - public final SqlNode getHaving() { + @Pure + public final @Nullable SqlNode getHaving() { return having; } - public void setHaving(SqlNode having) { + public void setHaving(@Nullable SqlNode having) { this.having = having; } - public final SqlNodeList getSelectList() { + @Pure + public final @Nullable SqlNodeList getSelectList() { return selectList; } - public void setSelectList(SqlNodeList selectList) { + public void setSelectList(@Nullable SqlNodeList selectList) { this.selectList = selectList; } - public final SqlNode getWhere() { + @Pure + public final @Nullable SqlNode getWhere() { return where; } - public void setWhere(SqlNode whereClause) { + public void setWhere(@Nullable SqlNode whereClause) { this.where = whereClause; } - @Nonnull public final SqlNodeList getWindowList() { + public final SqlNodeList getWindowList() { return windowDecls; } - public final SqlNodeList getOrderList() { + @Pure + public final @Nullable SqlNodeList getOrderList() { return orderBy; } - public void setOrderBy(SqlNodeList orderBy) { + public void setOrderBy(@Nullable SqlNodeList orderBy) { this.orderBy = orderBy; } - public final SqlNode getOffset() { + @Pure + public final @Nullable SqlNode getOffset() { return offset; } - public void setOffset(SqlNode offset) { + public void setOffset(@Nullable SqlNode offset) { this.offset = offset; } - public final SqlNode getFetch() { + @Pure + public final @Nullable SqlNode getFetch() { return fetch; } - public void setFetch(SqlNode fetch) { + public void setFetch(@Nullable SqlNode fetch) { this.fetch = fetch; } - public void setHints(SqlNodeList hints) { + public void setHints(@Nullable SqlNodeList hints) { this.hints = hints; } - public SqlNodeList getHints() { + @Pure + public @Nullable SqlNodeList getHints() { return this.hints; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java index 56d757840662..ce61e074675c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java @@ -22,6 +22,8 @@ import org.apache.calcite.sql.util.SqlBasicVisitor; import org.apache.calcite.sql.util.SqlVisitor; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -58,9 +60,9 @@ private SqlSelectOperator() { } @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { assert functionQualifier == null; return new SqlSelect(pos, (SqlNodeList) operands[0], diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSetOption.java b/core/src/main/java/org/apache/calcite/sql/SqlSetOption.java index b99f14a4ec4c..169e96713138 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSetOption.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSetOption.java @@ -21,9 +21,12 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; + /** * SQL parse tree node to represent {@code SET} and {@code RESET} statements, * optionally preceded by {@code ALTER SYSTEM} or {@code ALTER SESSION}. @@ -60,8 +63,9 @@ public class SqlSetOption extends SqlAlter { public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("SET_OPTION", SqlKind.SET_OPTION) { - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { final SqlNode scopeNode = operands[0]; return new SqlSetOption(pos, scopeNode == null ? null : scopeNode.toString(), @@ -77,7 +81,7 @@ public class SqlSetOption extends SqlAlter { * a {@link org.apache.calcite.sql.SqlIdentifier} with one * part. Reserved words (currently just 'ON') are converted to * identifiers by the parser. */ - SqlNode value; + @Nullable SqlNode value; /** * Creates a node. @@ -88,8 +92,8 @@ public class SqlSetOption extends SqlAlter { * @param value Value of option, as an identifier or literal, may be null. * If null, assume RESET command, else assume SET command. */ - public SqlSetOption(SqlParserPos pos, String scope, SqlIdentifier name, - SqlNode value) { + public SqlSetOption(SqlParserPos pos, @Nullable String scope, SqlIdentifier name, + @Nullable SqlNode value) { super(pos, scope); this.scope = scope; this.name = name; @@ -105,8 +109,9 @@ public SqlSetOption(SqlParserPos pos, String scope, SqlIdentifier name, return OPERATOR; } + @SuppressWarnings("nullness") @Override public List getOperandList() { - final List operandList = new ArrayList<>(); + final List<@Nullable SqlNode> operandList = new ArrayList<>(); if (scope == null) { operandList.add(null); } else { @@ -117,7 +122,7 @@ public SqlSetOption(SqlParserPos pos, String scope, SqlIdentifier name, return ImmutableNullableList.copyOf(operandList); } - @Override public void setOperand(int i, SqlNode operand) { + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: if (operand != null) { @@ -155,7 +160,9 @@ public SqlSetOption(SqlParserPos pos, String scope, SqlIdentifier name, @Override public void validate(SqlValidator validator, SqlValidatorScope scope) { - validator.validate(value); + if (value != null) { + validator.validate(value); + } } public SqlIdentifier getName() { @@ -166,7 +173,7 @@ public void setName(SqlIdentifier name) { this.name = name; } - public SqlNode getValue() { + public @Nullable SqlNode getValue() { return value; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSnapshot.java b/core/src/main/java/org/apache/calcite/sql/SqlSnapshot.java index e81d8e49912d..84e3e8ba42e6 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSnapshot.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSnapshot.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.util.SqlVisitor; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -61,7 +63,7 @@ public SqlNode getPeriod() { return period; } - @Override public void setOperand(int i, SqlNode operand) { + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case OPERAND_TABLE_REF: tableRef = Objects.requireNonNull(operand); @@ -93,10 +95,11 @@ private SqlSnapshotOperator() { return SqlSyntax.SPECIAL; } + @SuppressWarnings("argument.type.incompatible") @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { assert functionQualifier == null; assert operands.length == 2; return new SqlSnapshot(pos, operands[0], operands[1]); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSpecialOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlSpecialOperator.java index 219fec1a41f2..a37ec2ba9979 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSpecialOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSpecialOperator.java @@ -23,6 +23,8 @@ import org.apache.calcite.util.PrecedenceClimbingParser; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.function.Predicate; /** @@ -49,9 +51,9 @@ public SqlSpecialOperator( SqlKind kind, int prec, boolean leftAssoc, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker) { + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker) { super( name, kind, diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java index 5ec772f084f6..9144fdc5d98f 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -49,7 +51,7 @@ AggregateCall split(AggregateCall aggregateCall, /** Called to generate an aggregate for the other side of the join * than the side aggregate call's arguments come from. Returns null if * no aggregate is required. */ - AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e); + @Nullable AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e); /** Generates an aggregate call to merge sub-totals. * @@ -107,7 +109,7 @@ RexNode singleton(RexBuilder rexBuilder, RelDataType inputRowType, * @param bottom bottom aggregate call * @return Merged aggregate call, null if fails to merge aggregate calls */ - AggregateCall merge(AggregateCall top, AggregateCall bottom); + @Nullable AggregateCall merge(AggregateCall top, AggregateCall bottom); /** Collection in which one can register an element. Registering may return * a reference to an existing element. @@ -131,7 +133,8 @@ class CountSplitter implements SqlSplittableAggFunction { return aggregateCall.transform(mapping); } - @Override public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) { + @Override public @Nullable AggregateCall other(RelDataTypeFactory typeFactory, + AggregateCall e) { return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false, false, ImmutableIntList.of(), -1, RelCollations.EMPTY, typeFactory.createSqlType(SqlTypeName.BIGINT), null); @@ -196,7 +199,7 @@ class CountSplitter implements SqlSplittableAggFunction { } } - @Override public AggregateCall merge(AggregateCall top, AggregateCall bottom) { + @Override public @Nullable AggregateCall merge(AggregateCall top, AggregateCall bottom) { if (bottom.getAggregation().getKind() == SqlKind.COUNT && (top.getAggregation().getKind() == SqlKind.SUM || top.getAggregation().getKind() == SqlKind.SUM0)) { @@ -228,7 +231,8 @@ class SelfSplitter implements SqlSplittableAggFunction { return aggregateCall.transform(mapping); } - @Override public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) { + @Override public @Nullable AggregateCall other(RelDataTypeFactory typeFactory, + AggregateCall e) { return null; // no aggregate function required on other side } @@ -242,7 +246,7 @@ class SelfSplitter implements SqlSplittableAggFunction { RelCollations.EMPTY); } - @Override public AggregateCall merge(AggregateCall top, AggregateCall bottom) { + @Override public @Nullable AggregateCall merge(AggregateCall top, AggregateCall bottom) { if (top.getAggregation().getKind() == bottom.getAggregation().getKind()) { return AggregateCall.create(bottom.getAggregation(), bottom.isDistinct(), bottom.isApproximate(), false, @@ -272,7 +276,8 @@ abstract class AbstractSumSplitter implements SqlSplittableAggFunction { return aggregateCall.transform(mapping); } - @Override public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) { + @Override public @Nullable AggregateCall other(RelDataTypeFactory typeFactory, + AggregateCall e) { return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false, false, ImmutableIntList.of(), -1, @@ -311,7 +316,7 @@ abstract class AbstractSumSplitter implements SqlSplittableAggFunction { aggregateCall.type, aggregateCall.name); } - @Override public AggregateCall merge(AggregateCall top, AggregateCall bottom) { + @Override public @Nullable AggregateCall merge(AggregateCall top, AggregateCall bottom) { SqlKind topKind = top.getAggregation().getKind(); if (topKind == bottom.getAggregation().getKind() && (topKind == SqlKind.SUM diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSyntax.java b/core/src/main/java/org/apache/calcite/sql/SqlSyntax.java index 356355e19fc8..b0480bcac785 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSyntax.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSyntax.java @@ -19,7 +19,7 @@ import org.apache.calcite.sql.validate.SqlConformance; import org.apache.calcite.util.Util; -import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Enumeration of possible syntactic types of {@link SqlOperator operators}. @@ -168,8 +168,8 @@ public enum SqlSyntax { this(null); } - SqlSyntax(SqlSyntax family) { - this.family = Objects.requireNonNull(family == null ? this : family); + SqlSyntax(@Nullable SqlSyntax family) { + this.family = Util.first(family, this); } /** diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java index 20944ffb964c..b1ff7af7fa03 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; +import java.util.Objects; + /** * A SQL literal representing a TIME value, for example TIME * '14:33:44.567'. @@ -41,11 +43,11 @@ public class SqlTimeLiteral extends SqlAbstractDateTimeLiteral { /** Converts this literal to a {@link TimeString}. */ protected TimeString getTime() { - return (TimeString) value; + return (TimeString) Objects.requireNonNull(value, "value"); } @Override public SqlTimeLiteral clone(SqlParserPos pos) { - return new SqlTimeLiteral((TimeString) value, precision, hasTimeZone, pos); + return new SqlTimeLiteral(getTime(), precision, hasTimeZone, pos); } @Override public String toString() { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java index 8ee775a1e6c1..62869df83992 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; +import java.util.Objects; + /** * A SQL literal representing a TIMESTAMP value, for example TIMESTAMP * '1969-07-21 03:15 GMT'. @@ -40,7 +42,9 @@ public class SqlTimestampLiteral extends SqlAbstractDateTimeLiteral { //~ Methods ---------------------------------------------------------------- @Override public SqlTimestampLiteral clone(SqlParserPos pos) { - return new SqlTimestampLiteral((TimestampString) value, precision, + return new SqlTimestampLiteral( + (TimestampString) Objects.requireNonNull(value, "value"), + precision, hasTimeZone, pos); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java index eb1da8c5ea94..85e36a4d78e7 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java @@ -26,6 +26,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Util; +import static java.util.Objects.requireNonNull; + /** * The UNNEST operator. */ @@ -80,14 +82,16 @@ public SqlUnnestOperator(boolean withOrdinality) { assert type instanceof ArraySqlType || type instanceof MultisetSqlType || type instanceof MapSqlType; if (type instanceof MapSqlType) { - builder.add(MAP_KEY_COLUMN_NAME, type.getKeyType()); - builder.add(MAP_VALUE_COLUMN_NAME, type.getValueType()); + MapSqlType mapType = (MapSqlType) type; + builder.add(MAP_KEY_COLUMN_NAME, mapType.getKeyType()); + builder.add(MAP_VALUE_COLUMN_NAME, mapType.getValueType()); } else { - if (!allowAliasUnnestItems(opBinding) && type.getComponentType().isStruct()) { - builder.addAll(type.getComponentType().getFieldList()); + RelDataType componentType = requireNonNull(type.getComponentType(), "componentType"); + if (!allowAliasUnnestItems(opBinding) && componentType.isStruct()) { + builder.addAll(componentType.getFieldList()); } else { builder.add(SqlUtil.deriveAliasFromOrdinal(operand), - type.getComponentType()); + componentType); } } } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUnresolvedFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlUnresolvedFunction.java index 9f3a1e633760..9ea781f4027f 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUnresolvedFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUnresolvedFunction.java @@ -23,6 +23,8 @@ import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -46,10 +48,10 @@ public class SqlUnresolvedFunction extends SqlFunction { */ public SqlUnresolvedFunction( SqlIdentifier sqlIdentifier, - SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, - List paramTypes, + @Nullable SqlReturnTypeInference returnTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeChecker operandTypeChecker, + @Nullable List paramTypes, SqlFunctionCategory funcType) { super(sqlIdentifier, returnTypeInference, operandTypeInference, operandTypeChecker, paramTypes, funcType); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java index 1aef579bc503..d3c35d72973f 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java @@ -23,8 +23,12 @@ import org.apache.calcite.util.ImmutableNullableList; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; + /** * A SqlUpdate is a node of a parse tree which represents an UPDATE * statement. @@ -36,9 +40,9 @@ public class SqlUpdate extends SqlCall { SqlNode targetTable; SqlNodeList targetColumnList; SqlNodeList sourceExpressionList; - SqlNode condition; - SqlSelect sourceSelect; - SqlIdentifier alias; + @Nullable SqlNode condition; + @Nullable SqlSelect sourceSelect; + @Nullable SqlIdentifier alias; //~ Constructors ----------------------------------------------------------- @@ -46,9 +50,9 @@ public SqlUpdate(SqlParserPos pos, SqlNode targetTable, SqlNodeList targetColumnList, SqlNodeList sourceExpressionList, - SqlNode condition, - SqlSelect sourceSelect, - SqlIdentifier alias) { + @Nullable SqlNode condition, + @Nullable SqlSelect sourceSelect, + @Nullable SqlIdentifier alias) { super(pos); this.targetTable = targetTable; this.targetColumnList = targetColumnList; @@ -69,12 +73,14 @@ public SqlUpdate(SqlParserPos pos, return OPERATOR; } - @Override public List getOperandList() { + @SuppressWarnings("nullness") + @Override public List<@Nullable SqlNode> getOperandList() { return ImmutableNullableList.of(targetTable, targetColumnList, sourceExpressionList, condition, alias); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: assert operand instanceof SqlIdentifier; @@ -106,7 +112,8 @@ public SqlNode getTargetTable() { } /** Returns the alias for the target table of this UPDATE. */ - public SqlIdentifier getAlias() { + @Pure + public @Nullable SqlIdentifier getAlias() { return alias; } @@ -130,7 +137,7 @@ public SqlNodeList getSourceExpressionList() { * @return the condition expression for the data to be updated, or null for * all rows in the table */ - public SqlNode getCondition() { + public @Nullable SqlNode getCondition() { return condition; } @@ -141,7 +148,7 @@ public SqlNode getCondition() { * * @return the source SELECT for the data to be updated */ - public SqlSelect getSourceSelect() { + public @Nullable SqlSelect getSourceSelect() { return sourceSelect; } @@ -155,6 +162,7 @@ public void setSourceSelect(SqlSelect sourceSelect) { final int opLeft = getOperator().getLeftPrec(); final int opRight = getOperator().getRightPrec(); targetTable.unparse(writer, opLeft, opRight); + SqlIdentifier alias = this.alias; if (alias != null) { writer.keyword("AS"); alias.unparse(writer, opLeft, opRight); @@ -171,6 +179,7 @@ public void setSourceSelect(SqlSelect sourceSelect) { sourceExp.unparse(writer, opLeft, opRight); } writer.endList(setFrame); + SqlNode condition = this.condition; if (condition != null) { writer.sep("WHERE"); condition.unparse(writer, opLeft, opRight); diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java index 9f9175d2d1cb..ed2f77c1a64c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java @@ -32,6 +32,7 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlOperandMetadata; +import org.apache.calcite.sql.type.SqlOperandTypeChecker; import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; @@ -51,6 +52,9 @@ import com.google.common.collect.Iterators; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; @@ -66,8 +70,6 @@ import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.calcite.util.Static.RESOURCE; @@ -81,9 +83,9 @@ public abstract class SqlUtil { * *

    If {@code node1} is null, returns {@code node2}. * Flattens if either node is an AND. */ - public static @Nonnull SqlNode andExpressions( + public static SqlNode andExpressions( @Nullable SqlNode node1, - @Nonnull SqlNode node2) { + SqlNode node2) { if (node1 == null) { return node2; } @@ -115,7 +117,9 @@ static ArrayList flatten(SqlNode node) { public static SqlNode getFromNode( SqlSelect query, int ordinal) { - ArrayList list = flatten(query.getFrom()); + SqlNode from = query.getFrom(); + assert from != null : "from must not be null for " + query; + ArrayList list = flatten(from); return list.get(ordinal); } @@ -165,7 +169,7 @@ public static SqlNodeList toNodeList(SqlNode[] operands) { * */ public static boolean isNullLiteral( - SqlNode node, + @Nullable SqlNode node, boolean allowCast) { if (node instanceof SqlLiteral) { SqlLiteral literal = (SqlLiteral) node; @@ -178,7 +182,7 @@ public static boolean isNullLiteral( return false; } } - if (allowCast) { + if (allowCast && node != null) { if (node.getKind() == SqlKind.CAST) { SqlCall call = (SqlCall) node; if (isNullLiteral(call.operand(0), false)) { @@ -460,10 +464,10 @@ public static SqlLiteral concatenateLiterals(List lits) { * * @see Glossary#SQL99 SQL:1999 Part 2 Section 10.4 */ - public static SqlOperator lookupRoutine(SqlOperatorTable opTab, + public static @Nullable SqlOperator lookupRoutine(SqlOperatorTable opTab, RelDataTypeFactory typeFactory, SqlIdentifier funcName, List argTypes, - List argNames, SqlFunctionCategory category, + @Nullable List argNames, @Nullable SqlFunctionCategory category, SqlSyntax syntax, SqlKind sqlKind, SqlNameMatcher nameMatcher, boolean coerce) { Iterator list = @@ -510,9 +514,9 @@ private static Iterator filterOperatorRoutinesByKind( */ public static Iterator lookupSubjectRoutines( SqlOperatorTable opTab, RelDataTypeFactory typeFactory, - SqlIdentifier funcName, List argTypes, List argNames, + SqlIdentifier funcName, List argTypes, @Nullable List argNames, SqlSyntax sqlSyntax, SqlKind sqlKind, - SqlFunctionCategory category, SqlNameMatcher nameMatcher, + @Nullable SqlFunctionCategory category, SqlNameMatcher nameMatcher, boolean coerce) { // start with all routines matching by name Iterator routines = @@ -587,7 +591,7 @@ private static Iterator lookupSubjectRoutinesByName( SqlOperatorTable opTab, SqlIdentifier funcName, final SqlSyntax syntax, - SqlFunctionCategory category, + @Nullable SqlFunctionCategory category, SqlNameMatcher nameMatcher) { final List sqlOperators = new ArrayList<>(); opTab.lookupOperatorOverloads(funcName, category, syntax, sqlOperators, @@ -619,7 +623,7 @@ private static Iterator filterRoutinesByParameterCount( private static Iterator filterRoutinesByParameterTypeAndName( RelDataTypeFactory typeFactory, SqlSyntax syntax, final Iterator routines, final List argTypes, - final List argNames, final boolean coerce) { + final @Nullable List argNames, final boolean coerce) { if (syntax != SqlSyntax.FUNCTION) { return routines; } @@ -628,17 +632,19 @@ private static Iterator filterRoutinesByParameterTypeAndName( return (Iterator) Iterators.filter( Iterators.filter(routines, SqlFunction.class), function -> { - if (Objects.requireNonNull(function).getOperandTypeChecker() == null - || !function.getOperandTypeChecker().isFixedParameters()) { + SqlOperandTypeChecker operandTypeChecker = + Objects.requireNonNull(function, "function").getOperandTypeChecker(); + if (operandTypeChecker == null + || !operandTypeChecker.isFixedParameters()) { // no parameter information for builtins; keep for now, // the type coerce will not work here. return true; } - final SqlOperandMetadata operandMetadata = - (SqlOperandMetadata) function.getOperandTypeChecker(); - final List paramTypes = + final SqlOperandMetadata operandMetadata = (SqlOperandMetadata) operandTypeChecker; + @SuppressWarnings("assignment.type.incompatible") + final List<@Nullable RelDataType> paramTypes = operandMetadata.paramTypes(typeFactory); - final List permutedArgTypes; + final List<@Nullable RelDataType> permutedArgTypes; if (argNames != null) { final List paramNames = operandMetadata.paramNames(); permutedArgTypes = permuteArgTypes(paramNames, argNames, argTypes); @@ -651,11 +657,12 @@ private static Iterator filterRoutinesByParameterTypeAndName( paramTypes.add(null); } } - for (Pair p + for (Pair<@Nullable RelDataType, @Nullable RelDataType> p : Pair.zip(paramTypes, permutedArgTypes)) { final RelDataType argType = p.right; final RelDataType paramType = p.left; if (argType != null + && paramType != null && !SqlTypeUtil.canCastFrom(paramType, argType, coerce)) { return false; } @@ -667,7 +674,7 @@ private static Iterator filterRoutinesByParameterTypeAndName( /** * Permutes argument types to correspond to the order of parameter names. */ - private static List permuteArgTypes(List paramNames, + private static @Nullable List<@Nullable RelDataType> permuteArgTypes(List paramNames, List argNames, List argTypes) { // Arguments passed by name. Make sure that the function has // parameters of all of these names. @@ -679,12 +686,9 @@ private static List permuteArgTypes(List paramNames, } map.put(i, argName.i); } - return Functions.generate(paramNames.size(), index -> { - if (map.containsKey(index)) { - return argTypes.get(map.get(index)); - } else { - return null; - } + return Functions.<@Nullable RelDataType>generate(paramNames.size(), index -> { + Integer argIndex = map.get(index); + return argIndex != null ? argTypes.get(argIndex) : null; }); } @@ -699,7 +703,7 @@ private static Iterator filterRoutinesByTypePrecedence( RelDataTypeFactory typeFactory, Iterator routines, List argTypes, - List argNames) { + @Nullable List argNames) { if (sqlSyntax != SqlSyntax.FUNCTION) { return routines; } @@ -715,11 +719,11 @@ private static Iterator filterRoutinesByTypePrecedence( if (bestMatch != null) { sqlFunctions = sqlFunctions.stream() .filter(function -> { - if (!function.getOperandTypeChecker().isFixedParameters()) { + SqlOperandTypeChecker operandTypeChecker = function.getOperandTypeChecker(); + if (operandTypeChecker == null || !operandTypeChecker.isFixedParameters()) { return false; } - final SqlOperandMetadata operandMetadata = - (SqlOperandMetadata) function.getOperandTypeChecker(); + final SqlOperandMetadata operandMetadata = (SqlOperandMetadata) operandTypeChecker; final List paramNames = operandMetadata.paramNames(); final List paramTypes = operandMetadata.paramTypes(typeFactory); @@ -736,16 +740,16 @@ private static Iterator filterRoutinesByTypePrecedence( return (Iterator) sqlFunctions.iterator(); } - private static RelDataType bestMatch(RelDataTypeFactory typeFactory, + private static @Nullable RelDataType bestMatch(RelDataTypeFactory typeFactory, List sqlFunctions, int i, - List argNames, RelDataTypePrecedenceList precList) { + @Nullable List argNames, RelDataTypePrecedenceList precList) { RelDataType bestMatch = null; for (SqlFunction function : sqlFunctions) { - if (!function.getOperandTypeChecker().isFixedParameters()) { + SqlOperandTypeChecker operandTypeChecker = function.getOperandTypeChecker(); + if (operandTypeChecker == null || !operandTypeChecker.isFixedParameters()) { continue; } - final SqlOperandMetadata operandMetadata = - (SqlOperandMetadata) function.getOperandTypeChecker(); + final SqlOperandMetadata operandMetadata = (SqlOperandMetadata) operandTypeChecker; final List paramTypes = operandMetadata.paramTypes(typeFactory); final List paramNames = operandMetadata.paramNames(); @@ -782,6 +786,7 @@ public static SqlNode getSelectListItem(SqlNode query, int i) { } final SqlNodeList fields = select.getSelectList(); + assert fields != null : "fields must not be null in " + select; // Range check the index to avoid index out of range. This // could be expanded to actually check to see if the select // list is a "*" @@ -847,7 +852,7 @@ public static String getAliasedSignature( if (i > 0) { ret.append(", "); } - final String t = typeList.get(i).toString().toUpperCase(Locale.ROOT); + final String t = String.valueOf(typeList.get(i)).toUpperCase(Locale.ROOT); ret.append("<").append(t).append(">"); } ret.append(")'"); @@ -856,7 +861,7 @@ public static String getAliasedSignature( values[0] = opName; ret.append("'"); for (int i = 0; i < typeList.size(); i++) { - final String t = typeList.get(i).toString().toUpperCase(Locale.ROOT); + final String t = String.valueOf(typeList.get(i)).toUpperCase(Locale.ROOT); values[i + 1] = "<" + t + ">"; } ret.append(new MessageFormat(template, Locale.ROOT).format(values)); @@ -959,7 +964,7 @@ public static RelDataType createNlsStringType( * @param name SQL-level name * @return Java-level name, or null if SQL-level name is unknown */ - public static String translateCharacterSetName(String name) { + public static @Nullable String translateCharacterSetName(String name) { switch (name) { case "BIG5": return "Big5"; @@ -1022,7 +1027,7 @@ public static void validateCharset(ByteString value, Charset charset) { /** If a node is "AS", returns the underlying expression; otherwise returns * the node. */ - public static SqlNode stripAs(SqlNode node) { + public static @PolyNull SqlNode stripAs(@PolyNull SqlNode node) { if (node != null && node.getKind() == SqlKind.AS) { return ((SqlCall) node).operand(0); } @@ -1056,7 +1061,9 @@ public static ImmutableList getAncestry(SqlNode root, throw new AssertionError("not found: " + predicate + " in " + root); } catch (Util.FoundOne e) { //noinspection unchecked - return (ImmutableList) e.getNode(); + return (ImmutableList) Objects.requireNonNull( + e.getNode(), + "Genealogist result"); } } @@ -1070,7 +1077,8 @@ public static ImmutableList getAncestry(SqlNode root, * @param sqlHints The sql hints nodes * @return the {@code RelHint} list */ - public static List getRelHint(HintStrategyTable hintStrategies, SqlNodeList sqlHints) { + public static List getRelHint(HintStrategyTable hintStrategies, + @Nullable SqlNodeList sqlHints) { if (sqlHints == null || sqlHints.size() == 0) { return ImmutableList.of(); } @@ -1245,7 +1253,7 @@ private Void postCheck(SqlNode node) { return null; } - private void visitChild(SqlNode node) { + private void visitChild(@Nullable SqlNode node) { if (node == null) { return; } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWindow.java b/core/src/main/java/org/apache/calcite/sql/SqlWindow.java index 93767e84bb35..d8b9b6e0f672 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlWindow.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlWindow.java @@ -35,8 +35,13 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; /** @@ -73,10 +78,10 @@ public class SqlWindow extends SqlCall { //~ Instance fields -------------------------------------------------------- /** The name of the window being declared. */ - SqlIdentifier declName; + @Nullable SqlIdentifier declName; /** The name of the window being referenced, or null. */ - SqlIdentifier refName; + @Nullable SqlIdentifier refName; /** The list of partitioning columns. */ SqlNodeList partitionList; @@ -88,25 +93,25 @@ public class SqlWindow extends SqlCall { SqlLiteral isRows; /** The lower bound of the window. */ - SqlNode lowerBound; + @Nullable SqlNode lowerBound; /** The upper bound of the window. */ - SqlNode upperBound; + @Nullable SqlNode upperBound; /** Whether to allow partial results. It may be null. */ - SqlLiteral allowPartial; + @Nullable SqlLiteral allowPartial; - private SqlCall windowCall = null; + private @Nullable SqlCall windowCall = null; //~ Constructors ----------------------------------------------------------- /** * Creates a window. */ - public SqlWindow(SqlParserPos pos, SqlIdentifier declName, - SqlIdentifier refName, SqlNodeList partitionList, SqlNodeList orderList, - SqlLiteral isRows, SqlNode lowerBound, SqlNode upperBound, - SqlLiteral allowPartial) { + public SqlWindow(SqlParserPos pos, @Nullable SqlIdentifier declName, + @Nullable SqlIdentifier refName, SqlNodeList partitionList, SqlNodeList orderList, + SqlLiteral isRows, @Nullable SqlNode lowerBound, @Nullable SqlNode upperBound, + @Nullable SqlLiteral allowPartial) { super(pos); this.declName = declName; this.refName = refName; @@ -122,9 +127,9 @@ public SqlWindow(SqlParserPos pos, SqlIdentifier declName, assert orderList != null; } - public static SqlWindow create(SqlIdentifier declName, SqlIdentifier refName, + public static SqlWindow create(@Nullable SqlIdentifier declName, @Nullable SqlIdentifier refName, SqlNodeList partitionList, SqlNodeList orderList, SqlLiteral isRows, - SqlNode lowerBound, SqlNode upperBound, SqlLiteral allowPartial, + @Nullable SqlNode lowerBound, @Nullable SqlNode upperBound, @Nullable SqlLiteral allowPartial, SqlParserPos pos) { // If there's only one bound and it's 'FOLLOWING', make it the upper // bound. @@ -148,12 +153,14 @@ public static SqlWindow create(SqlIdentifier declName, SqlIdentifier refName, return SqlKind.WINDOW; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(declName, refName, partitionList, orderList, isRows, lowerBound, upperBound, allowPartial); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: this.declName = (SqlIdentifier) operand; @@ -194,7 +201,7 @@ public static SqlWindow create(SqlIdentifier declName, SqlIdentifier refName, getOperator().unparse(writer, this, 0, 0); } - public SqlIdentifier getDeclName() { + public @Nullable SqlIdentifier getDeclName() { return declName; } @@ -203,19 +210,19 @@ public void setDeclName(SqlIdentifier declName) { this.declName = declName; } - public SqlNode getLowerBound() { + public @Nullable SqlNode getLowerBound() { return lowerBound; } - public void setLowerBound(SqlNode lowerBound) { + public void setLowerBound(@Nullable SqlNode lowerBound) { this.lowerBound = lowerBound; } - public SqlNode getUpperBound() { + public @Nullable SqlNode getUpperBound() { return upperBound; } - public void setUpperBound(SqlNode upperBound) { + public void setUpperBound(@Nullable SqlNode upperBound) { this.upperBound = upperBound; } @@ -260,6 +267,7 @@ public void setRows(SqlLiteral isRows) { this.isRows = isRows; } + @Pure public boolean isRows() { return isRows.booleanValue(); } @@ -280,17 +288,17 @@ public void setPartitionList(SqlNodeList partitionList) { this.partitionList = partitionList; } - public SqlIdentifier getRefName() { + public @Nullable SqlIdentifier getRefName() { return refName; } - public void setWindowCall(SqlCall windowCall) { + public void setWindowCall(@Nullable SqlCall windowCall) { this.windowCall = windowCall; assert windowCall == null || windowCall.getOperator() instanceof SqlAggFunction; } - public SqlCall getWindowCall() { + public @Nullable SqlCall getWindowCall() { return windowCall; } @@ -329,7 +337,7 @@ static void checkSpecialLiterals(SqlWindow window, SqlValidator validator) { if (Bound.CURRENT_ROW == lowerLitType) { if (null != upperOp) { if (upperOp == PRECEDING_OPERATOR) { - throw validator.newValidationError(upperBound, + throw validator.newValidationError(castNonNull(upperBound), RESOURCE.currentRowPrecedingError()); } } @@ -337,12 +345,12 @@ static void checkSpecialLiterals(SqlWindow window, SqlValidator validator) { if (lowerOp == FOLLOWING_OPERATOR) { if (null != upperOp) { if (upperOp == PRECEDING_OPERATOR) { - throw validator.newValidationError(upperBound, + throw validator.newValidationError(castNonNull(upperBound), RESOURCE.followingBeforePrecedingError()); } } else if (null != upperLitType) { if (Bound.CURRENT_ROW == upperLitType) { - throw validator.newValidationError(upperBound, + throw validator.newValidationError(castNonNull(upperBound), RESOURCE.currentRowFollowingError()); } } @@ -477,7 +485,7 @@ public SqlWindow overlay(SqlWindow that, SqlValidator validator) { allowPartialNew); } - private static boolean setOperand(SqlNode clonedOperand, SqlNode thatOperand, + private static boolean setOperand(@Nullable SqlNode clonedOperand, @Nullable SqlNode thatOperand, SqlValidator validator) { if ((thatOperand != null) && !SqlNodeList.isEmptyList(thatOperand)) { if ((clonedOperand == null) @@ -500,7 +508,7 @@ private static boolean setOperand(SqlNode clonedOperand, SqlNode thatOperand, * * @return boolean true if all nodes in the subtree are equal */ - @Override public boolean equalsDeep(SqlNode node, Litmus litmus) { + @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) { // This is the difference over super.equalsDeep. It skips // operands[0] the declared name fo this window. We only want // to check the window components. @@ -516,6 +524,7 @@ private static boolean setOperand(SqlNode clonedOperand, SqlNode thatOperand, * (for example, a window of size 1 hour which has only 45 minutes of data * in it) will appear to windowed aggregate functions to be empty. */ + @EnsuresNonNullIf(expression = "allowPartial", result = false) public boolean isAllowPartial() { // Default (and standard behavior) is to allow partial windows. return allowPartial == null @@ -640,15 +649,15 @@ public boolean isAllowPartial() { } if (!isRows() && !isAllowPartial()) { - throw validator.newValidationError(allowPartial, + throw validator.newValidationError(castNonNull(allowPartial), RESOURCE.cannotUseDisallowPartialWithRange()); } } private void validateFrameBoundary( - SqlNode bound, + @Nullable SqlNode bound, boolean isRows, - SqlTypeFamily orderTypeFam, + @Nullable SqlTypeFamily orderTypeFam, SqlValidator validator, SqlValidatorScope scope) { if (null == bound) { @@ -677,7 +686,7 @@ private void validateFrameBoundary( final SqlNumericLiteral boundLiteral = (SqlNumericLiteral) boundVal; if (!boundLiteral.isExact() - || (boundLiteral.getScale() != 0) + || (boundLiteral.getScale() != null && boundLiteral.getScale() != 0) || (0 > boundLiteral.longValue(true))) { // true == throw if not exact (we just tested that - right?) throw validator.newValidationError(boundVal, @@ -808,10 +817,11 @@ private SqlWindowOperator() { return SqlSyntax.SPECIAL; } + @SuppressWarnings("argument.type.incompatible") @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { assert functionQualifier == null; assert operands.length == 8; return create( @@ -873,24 +883,26 @@ private SqlWindowOperator() { window.orderList.unparse(writer, 0, 0); writer.endList(orderFrame); } - if (window.lowerBound == null) { + SqlNode lowerBound = window.lowerBound; + SqlNode upperBound = window.upperBound; + if (lowerBound == null) { // No ROWS or RANGE clause - } else if (window.upperBound == null) { + } else if (upperBound == null) { if (window.isRows()) { writer.sep("ROWS"); } else { writer.sep("RANGE"); } - window.lowerBound.unparse(writer, 0, 0); + lowerBound.unparse(writer, 0, 0); } else { if (window.isRows()) { writer.sep("ROWS BETWEEN"); } else { writer.sep("RANGE BETWEEN"); } - window.lowerBound.unparse(writer, 0, 0); + lowerBound.unparse(writer, 0, 0); writer.keyword("AND"); - window.upperBound.unparse(writer, 0, 0); + upperBound.unparse(writer, 0, 0); } // ALLOW PARTIAL/DISALLOW PARTIAL diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWindowTableFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlWindowTableFunction.java index 82a1d75117f0..b06d566c2524 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlWindowTableFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlWindowTableFunction.java @@ -31,6 +31,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.List; @@ -80,8 +82,8 @@ public SqlWindowTableFunction(String name, SqlOperandMetadata operandMetadata) { operandMetadata, SqlFunctionCategory.SYSTEM); } - @Override public SqlOperandMetadata getOperandTypeChecker() { - return (SqlOperandMetadata) super.getOperandTypeChecker(); + @Override public @Nullable SqlOperandMetadata getOperandTypeChecker() { + return (@Nullable SqlOperandMetadata) super.getOperandTypeChecker(); } @Override public SqlReturnTypeInference getRowTypeInference() { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWith.java b/core/src/main/java/org/apache/calcite/sql/SqlWith.java index e9fb1c801ee3..d109bc612c12 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlWith.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlWith.java @@ -22,8 +22,11 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; + /** * The WITH clause of a query. It wraps a SELECT, UNION, or INTERSECT. */ @@ -53,7 +56,8 @@ public SqlWith(SqlParserPos pos, SqlNodeList withList, SqlNode body) { return ImmutableList.of(withList, body); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: withList = (SqlNodeList) operand; @@ -107,8 +111,9 @@ private SqlWithOperator() { } - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { return new SqlWith(pos, (SqlNodeList) operands[0], operands[1]); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWithItem.java b/core/src/main/java/org/apache/calcite/sql/SqlWithItem.java index b0c67be237fd..38d2f8b03495 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlWithItem.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlWithItem.java @@ -19,19 +19,22 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; + /** * An item in a WITH clause of a query. * It has a name, an optional column list, and a query. */ public class SqlWithItem extends SqlCall { public SqlIdentifier name; - public SqlNodeList columnList; // may be null + public @Nullable SqlNodeList columnList; // may be null public SqlNode query; public SqlWithItem(SqlParserPos pos, SqlIdentifier name, - SqlNodeList columnList, SqlNode query) { + @Nullable SqlNodeList columnList, SqlNode query) { super(pos); this.name = name; this.columnList = columnList; @@ -44,17 +47,19 @@ public SqlWithItem(SqlParserPos pos, SqlIdentifier name, return SqlKind.WITH_ITEM; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, columnList, query); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: name = (SqlIdentifier) operand; break; case 1: - columnList = (SqlNodeList) operand; + columnList = (@Nullable SqlNodeList) operand; break; case 2: query = operand; @@ -96,8 +101,9 @@ private static class SqlWithItemOperator extends SqlSpecialOperator { withItem.query.unparse(writer, 10, 10); } - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @SuppressWarnings("argument.type.incompatible") + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { assert functionQualifier == null; assert operands.length == 3; return new SqlWithItem(pos, (SqlIdentifier) operands[0], diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWriter.java b/core/src/main/java/org/apache/calcite/sql/SqlWriter.java index 1c5cb015e998..7269b5632cdf 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlWriter.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlWriter.java @@ -19,6 +19,8 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.util.SqlString; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.function.Consumer; /** @@ -373,14 +375,14 @@ public static FrameType create(final String name) { /** * Prints the OFFSET/FETCH clause. */ - void fetchOffset(SqlNode fetch, SqlNode offset); + void fetchOffset(@Nullable SqlNode fetch, @Nullable SqlNode offset); /** * Prints the TOP(n) clause. * * @see #fetchOffset */ - void topN(SqlNode fetch, SqlNode offset); + void topN(@Nullable SqlNode fetch, @Nullable SqlNode offset); /** * Prints a new line, and indents. @@ -463,7 +465,7 @@ public static FrameType create(final String name) { * * @param frame The frame which was created by {@link #startList}. */ - void endList(Frame frame); + void endList(@Nullable Frame frame); /** * Writes a list. diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWriterConfig.java b/core/src/main/java/org/apache/calcite/sql/SqlWriterConfig.java index 7e9c1c65f7d4..f4774cf11e9d 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlWriterConfig.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlWriterConfig.java @@ -19,14 +19,16 @@ import org.apache.calcite.sql.pretty.SqlPrettyWriter; import org.apache.calcite.util.ImmutableBeans; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Configuration for {@link SqlWriter} and {@link SqlPrettyWriter}. */ public interface SqlWriterConfig { /** Returns the dialect. */ @ImmutableBeans.Property - SqlDialect dialect(); + @Nullable SqlDialect dialect(); /** Sets {@link #dialect()}. */ - SqlWriterConfig withDialect(SqlDialect dialect); + SqlWriterConfig withDialect(@Nullable SqlDialect dialect); /** Returns whether to print keywords (SELECT, AS, etc.) in lower-case. * Default is false: keywords are printed in upper-case. */ @@ -100,18 +102,18 @@ SqlWriterConfig withSelectListItemsOnSeparateLines( * {@link #updateSetListNewline()}, * {@link #windowDeclListNewline()} are used. */ @ImmutableBeans.Property - LineFolding lineFolding(); + @Nullable LineFolding lineFolding(); /** Sets {@link #lineFolding()}. */ - SqlWriterConfig withLineFolding(LineFolding lineFolding); + SqlWriterConfig withLineFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the SELECT clause. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding selectFolding(); + @Nullable LineFolding selectFolding(); /** Sets {@link #selectFolding()}. */ - SqlWriterConfig withSelectFolding(LineFolding lineFolding); + SqlWriterConfig withSelectFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the FROM clause (and JOIN). * If not set, the value of {@link #lineFolding()} is used. */ @@ -125,74 +127,74 @@ SqlWriterConfig withSelectListItemsOnSeparateLines( /** Returns the line-folding policy for the WHERE clause. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding whereFolding(); + @Nullable LineFolding whereFolding(); /** Sets {@link #whereFolding()}. */ - SqlWriterConfig withWhereFolding(LineFolding lineFolding); + SqlWriterConfig withWhereFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the GROUP BY clause. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding groupByFolding(); + @Nullable LineFolding groupByFolding(); /** Sets {@link #groupByFolding()}. */ - SqlWriterConfig withGroupByFolding(LineFolding lineFolding); + SqlWriterConfig withGroupByFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the HAVING clause. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding havingFolding(); + @Nullable LineFolding havingFolding(); /** Sets {@link #havingFolding()}. */ - SqlWriterConfig withHavingFolding(LineFolding lineFolding); + SqlWriterConfig withHavingFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the WINDOW clause. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding windowFolding(); + @Nullable LineFolding windowFolding(); /** Sets {@link #windowFolding()}. */ - SqlWriterConfig withWindowFolding(LineFolding lineFolding); + SqlWriterConfig withWindowFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the MATCH_RECOGNIZE clause. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding matchFolding(); + @Nullable LineFolding matchFolding(); /** Sets {@link #matchFolding()}. */ - SqlWriterConfig withMatchFolding(LineFolding lineFolding); + SqlWriterConfig withMatchFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the ORDER BY clause. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding orderByFolding(); + @Nullable LineFolding orderByFolding(); /** Sets {@link #orderByFolding()}. */ - SqlWriterConfig withOrderByFolding(LineFolding lineFolding); + SqlWriterConfig withOrderByFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the OVER clause or a window * declaration. If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding overFolding(); + @Nullable LineFolding overFolding(); /** Sets {@link #overFolding()}. */ - SqlWriterConfig withOverFolding(LineFolding lineFolding); + SqlWriterConfig withOverFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the VALUES expression. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding valuesFolding(); + @Nullable LineFolding valuesFolding(); /** Sets {@link #valuesFolding()}. */ - SqlWriterConfig withValuesFolding(LineFolding lineFolding); + SqlWriterConfig withValuesFolding(@Nullable LineFolding lineFolding); /** Returns the line-folding policy for the SET clause of an UPDATE statement. * If not set, the value of {@link #lineFolding()} is used. */ @ImmutableBeans.Property - LineFolding updateSetFolding(); + @Nullable LineFolding updateSetFolding(); /** Sets {@link #updateSetFolding()}. */ - SqlWriterConfig withUpdateSetFolding(LineFolding lineFolding); + SqlWriterConfig withUpdateSetFolding(@Nullable LineFolding lineFolding); /** * Returns whether to use a fix for SELECT list indentations. diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java index 6e0803a5f67d..323240af0b81 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisor.java @@ -38,6 +38,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.util.ArrayList; @@ -50,6 +52,8 @@ import java.util.Set; import java.util.TreeSet; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * An assistant which offers hints and corrections to a partially-formed SQL * statement. It is used in the SQL editor user-interface. @@ -69,12 +73,12 @@ public class SqlAdvisor { private final SqlParser.Config parserConfig; // Cache for getPreferredCasing - private String prevWord; - private Casing prevPreferredCasing; + private @Nullable String prevWord; + private @Nullable Casing prevPreferredCasing; // Reserved words cache - private Set reservedWordsSet; - private List reservedWordsList; + private @Nullable Set reservedWordsSet; + private @Nullable List reservedWordsList; //~ Constructors ----------------------------------------------------------- @@ -225,7 +229,7 @@ public List getCompletionHints0(String sql, int cursor) { */ private Casing getPreferredCasing(String word) { if (word == prevWord) { - return prevPreferredCasing; + return castNonNull(prevPreferredCasing); } boolean hasLower = false; boolean hasUpper = false; @@ -388,7 +392,7 @@ private static boolean isSelectListItem(SqlNode root, * failure * @return Parse tree if succeeded, null if parse failed */ - private SqlNode tryParse(String sql, List hintList) { + private @Nullable SqlNode tryParse(String sql, List hintList) { try { return parseQuery(sql); } catch (SqlParseException e) { @@ -423,7 +427,7 @@ private SqlNode tryParse(String sql, List hintList) { * the specified SQL identifier, returns null if none is found or the SQL * statement is invalid. */ - public SqlMoniker getQualifiedName(String sql, int cursor) { + public @Nullable SqlMoniker getQualifiedName(String sql, int cursor) { SqlNode sqlNode; try { sqlNode = parseQuery(sql); @@ -472,13 +476,16 @@ public boolean isValid(String sql) { * @param sql A user-input sql statement to be validated * @return a List of ValidateErrorInfo (null if sql is valid) */ - public List validate(String sql) { + public @Nullable List validate(String sql) { SqlNode sqlNode; List errorList = new ArrayList<>(); sqlNode = collectParserError(sql, errorList); if (!errorList.isEmpty()) { return errorList; + } else if (sqlNode == null) { + throw new IllegalStateException("collectParserError returned null (sql is not valid)" + + ", however, the resulting errorList is empty. sql=" + sql); } try { validator.validate(sqlNode); @@ -524,18 +531,21 @@ public String simplifySql(String sql, int cursor) { * * @return an of SQL reserved and keywords */ + @EnsuresNonNull({"reservedWordsSet", "reservedWordsList"}) public List getReservedAndKeyWords() { ensureReservedAndKeyWords(); return reservedWordsList; } + @EnsuresNonNull({"reservedWordsSet", "reservedWordsList"}) private Set getReservedAndKeyWordsSet() { ensureReservedAndKeyWords(); return reservedWordsSet; } + @EnsuresNonNull({"reservedWordsSet", "reservedWordsList"}) private void ensureReservedAndKeyWords() { - if (reservedWordsSet != null) { + if (reservedWordsSet != null && reservedWordsList != null) { return; } Collection c = SqlAbstractParserImpl.getSql92ReservedWords(); @@ -587,7 +597,7 @@ protected SqlNode parseQuery(String sql) throws SqlParseException { * @return {@link SqlNode } that is root of the parse tree, null if the sql * is not valid */ - protected SqlNode collectParserError( + protected @Nullable SqlNode collectParserError( String sql, List errorList) { try { @@ -612,7 +622,7 @@ public static class ValidateErrorInfo { private int startColumnNum; private int endLineNum; private int endColumnNum; - private String errorMsg; + private @Nullable String errorMsg; /** * Creates a new ValidateErrorInfo with the position coordinates and an @@ -629,7 +639,7 @@ public ValidateErrorInfo( int startColumnNum, int endLineNum, int endColumnNum, - String errorMsg) { + @Nullable String errorMsg) { this.startLineNum = startLineNum; this.startColumnNum = startColumnNum; this.endLineNum = endLineNum; @@ -648,7 +658,8 @@ public ValidateErrorInfo( this.startColumnNum = e.getPosColumn(); this.endLineNum = e.getEndPosLine(); this.endColumnNum = e.getEndPosColumn(); - this.errorMsg = e.getCause().getMessage(); + Throwable cause = e.getCause(); + this.errorMsg = (cause == null ? e : cause).getMessage(); } /** @@ -660,7 +671,7 @@ public ValidateErrorInfo( */ public ValidateErrorInfo( SqlParserPos pos, - String errorMsg) { + @Nullable String errorMsg) { this.startLineNum = pos.getLineNum(); this.startColumnNum = pos.getColumnNum(); this.endLineNum = pos.getEndLineNum(); @@ -689,7 +700,7 @@ public int getEndColumnNum() { } /** Returns the error message. */ - public String getMessage() { + public @Nullable String getMessage() { return errorMsg; } } diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java index cbd2a1893a28..f9467c029d4a 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java @@ -101,7 +101,7 @@ public class SqlAdvisorGetHintsFunction */ public static Enumerable getCompletionHints( final SqlAdvisor advisor, final String sql, final int pos) { - final String[] replaced = {null}; + final String[] replaced = new String[1]; final List hints = advisor.getCompletionHints(sql, pos, replaced); final List res = new ArrayList<>(hints.size() + 1); diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction2.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction2.java index 68b2924865d2..9b1fca935f31 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction2.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction2.java @@ -103,7 +103,7 @@ public class SqlAdvisorGetHintsFunction2 */ public static Enumerable getCompletionHints( final SqlAdvisor advisor, final String sql, final int pos) { - final String[] replaced = {null}; + final String[] replaced = new String[1]; final List hints = advisor.getCompletionHints(sql, pos, replaced); final List res = new ArrayList<>(hints.size() + 1); diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint.java index c141b17da733..9746fc62265e 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.validate.SqlMoniker; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -26,13 +28,13 @@ */ public class SqlAdvisorHint { /** Fully qualified object name as string. */ - public final String id; + public final @Nullable String id; /** Fully qualified object name as array of names. */ - public final String[] names; + public final String @Nullable [] names; /** One of {@link org.apache.calcite.sql.validate.SqlMonikerType}. */ public final String type; - public SqlAdvisorHint(String id, String[] names, String type) { + public SqlAdvisorHint(String id, String @Nullable [] names, String type) { this.id = id; this.names = names; this.type = type; diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint2.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint2.java index bc073a44fd62..bf9a405d20cb 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint2.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorHint2.java @@ -18,15 +18,18 @@ import org.apache.calcite.sql.validate.SqlMoniker; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * This class is used to return values for * {@link SqlAdvisor#getCompletionHints (String, int, String[])}. */ public class SqlAdvisorHint2 extends SqlAdvisorHint { /** Replacement string. */ - public final String replacement; + public final @Nullable String replacement; - public SqlAdvisorHint2(String id, String[] names, String type, String replacement) { + public SqlAdvisorHint2(String id, String @Nullable [] names, String type, + @Nullable String replacement) { super(id, names, type); this.replacement = replacement; } diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java index ba2bcc5c65b9..5593436f5707 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlSimpleParser.java @@ -19,6 +19,8 @@ import org.apache.calcite.avatica.util.Quoting; import org.apache.calcite.sql.parser.SqlParser; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; @@ -26,6 +28,7 @@ import java.util.ListIterator; import java.util.Locale; import java.util.Map; +import java.util.Objects; /** * A simple parser that takes an incomplete and turn it into a syntactically @@ -307,7 +310,7 @@ private Token parseQuotedIdentifier() { return new Token(TokenType.DQID, match); } - public Token nextToken() { + public @Nullable Token nextToken() { while (pos < sql.length()) { char c = sql.charAt(pos); final String match; @@ -443,13 +446,13 @@ private int indexOfLineEnd(String sql, int i) { /** Token. */ public static class Token { private final TokenType type; - private final String s; + private final @Nullable String s; Token(TokenType tokenType) { this(tokenType, null); } - Token(TokenType type, String s) { + Token(TokenType type, @Nullable String s) { this.type = type; this.s = s; } @@ -511,7 +514,7 @@ public static void simplifyList(List list, String hintToken) { } } - public Query simplify(String hintToken) { + public Query simplify(@Nullable String hintToken) { TokenType clause = TokenType.SELECT; TokenType foundInClause = null; Query foundInSubQuery = null; @@ -773,31 +776,37 @@ private void purgeFromExcept(String hintToken) { } private void purgeWhere() { - List sublist = findClause(TokenType.WHERE); + List sublist = findClauseOrNull(TokenType.WHERE); if (sublist != null) { sublist.clear(); } } private void purgeGroupByHaving() { - List sublist = findClause(TokenType.GROUP); + List sublist = findClauseOrNull(TokenType.GROUP); if (sublist != null) { sublist.clear(); } - sublist = findClause(TokenType.HAVING); + sublist = findClauseOrNull(TokenType.HAVING); if (sublist != null) { sublist.clear(); } } private void purgeOrderBy() { - List sublist = findClause(TokenType.ORDER); + List sublist = findClauseOrNull(TokenType.ORDER); if (sublist != null) { sublist.clear(); } } private List findClause(TokenType keyword) { + return Objects.requireNonNull( + findClauseOrNull(keyword), + () -> "clause does not exist: " + keyword); + } + + private @Nullable List findClauseOrNull(TokenType keyword) { int start = -1; int k = -1; EnumSet clauses = diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlAttributeDefinition.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlAttributeDefinition.java index 1d7c88bda9ef..a25528784608 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlAttributeDefinition.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlAttributeDefinition.java @@ -29,6 +29,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -41,12 +43,12 @@ public class SqlAttributeDefinition extends SqlCall { public final SqlIdentifier name; public final SqlDataTypeSpec dataType; - final SqlNode expression; - final SqlCollation collation; + final @Nullable SqlNode expression; + final @Nullable SqlCollation collation; /** Creates a SqlAttributeDefinition; use {@link SqlDdlNodes#attribute}. */ SqlAttributeDefinition(SqlParserPos pos, SqlIdentifier name, - SqlDataTypeSpec dataType, SqlNode expression, SqlCollation collation) { + SqlDataTypeSpec dataType, @Nullable SqlNode expression, @Nullable SqlCollation collation) { super(pos); this.name = name; this.dataType = dataType; @@ -69,23 +71,13 @@ public class SqlAttributeDefinition extends SqlCall { writer.keyword("COLLATE"); collation.unparse(writer); } - if (dataType.getNullable() != null && !dataType.getNullable()) { + if (Boolean.FALSE.equals(dataType.getNullable())) { writer.keyword("NOT NULL"); } + SqlNode expression = this.expression; if (expression != null) { writer.keyword("DEFAULT"); - exp(writer); - } - } - - // TODO: refactor this to a util class to share with SqlColumnDeclaration - private void exp(SqlWriter writer) { - if (writer.isAlwaysUseParentheses()) { - expression.unparse(writer, 0, 0); - } else { - writer.sep("("); - expression.unparse(writer, 0, 0); - writer.sep(")"); + SqlColumnDeclaration.exp(writer, expression); } } } diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java index a685c62c99a5..a74ed3e90ef7 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java @@ -26,6 +26,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -37,11 +39,11 @@ public class SqlCheckConstraint extends SqlCall { private static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("CHECK", SqlKind.CHECK); - private final SqlIdentifier name; + private final @Nullable SqlIdentifier name; private final SqlNode expression; /** Creates a SqlCheckConstraint; use {@link SqlDdlNodes#check}. */ - SqlCheckConstraint(SqlParserPos pos, SqlIdentifier name, + SqlCheckConstraint(SqlParserPos pos, @Nullable SqlIdentifier name, SqlNode expression) { super(pos); this.name = name; // may be null @@ -52,6 +54,7 @@ public class SqlCheckConstraint extends SqlCall { return OPERATOR; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, expression); } diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java index 83cb03733592..e37075f63123 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java @@ -29,6 +29,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -42,12 +44,12 @@ public class SqlColumnDeclaration extends SqlCall { public final SqlIdentifier name; public final SqlDataTypeSpec dataType; - public final SqlNode expression; + public final @Nullable SqlNode expression; public final ColumnStrategy strategy; /** Creates a SqlColumnDeclaration; use {@link SqlDdlNodes#column}. */ SqlColumnDeclaration(SqlParserPos pos, SqlIdentifier name, - SqlDataTypeSpec dataType, SqlNode expression, + SqlDataTypeSpec dataType, @Nullable SqlNode expression, ColumnStrategy strategy) { super(pos); this.name = name; @@ -67,20 +69,21 @@ public class SqlColumnDeclaration extends SqlCall { @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { name.unparse(writer, 0, 0); dataType.unparse(writer, 0, 0); - if (dataType.getNullable() != null && !dataType.getNullable()) { + if (Boolean.FALSE.equals(dataType.getNullable())) { writer.keyword("NOT NULL"); } + SqlNode expression = this.expression; if (expression != null) { switch (strategy) { case VIRTUAL: case STORED: writer.keyword("AS"); - exp(writer); + exp(writer, expression); writer.keyword(strategy.name()); break; case DEFAULT: writer.keyword("DEFAULT"); - exp(writer); + exp(writer, expression); break; default: throw new AssertionError("unexpected: " + strategy); @@ -88,7 +91,7 @@ public class SqlColumnDeclaration extends SqlCall { } } - private void exp(SqlWriter writer) { + static void exp(SqlWriter writer, SqlNode expression) { if (writer.isAlwaysUseParentheses()) { expression.unparse(writer, 0, 0); } else { diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java index a2c255868137..a09809eea82c 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java @@ -29,19 +29,24 @@ import org.apache.calcite.util.Pair; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.AbstractList; import java.util.List; import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Parse tree for {@code CREATE FOREIGN SCHEMA} statement. */ public class SqlCreateForeignSchema extends SqlCreate { public final SqlIdentifier name; - public final SqlNode type; - public final SqlNode library; - private final SqlNodeList optionList; + public final @Nullable SqlNode type; + public final @Nullable SqlNode library; + private final @Nullable SqlNodeList optionList; private static final SqlOperator OPERATOR = new SqlSpecialOperator("CREATE FOREIGN SCHEMA", @@ -49,8 +54,8 @@ public class SqlCreateForeignSchema extends SqlCreate { /** Creates a SqlCreateForeignSchema. */ SqlCreateForeignSchema(SqlParserPos pos, boolean replace, boolean ifNotExists, - SqlIdentifier name, SqlNode type, SqlNode library, - SqlNodeList optionList) { + SqlIdentifier name, @Nullable SqlNode type, @Nullable SqlNode library, + @Nullable SqlNodeList optionList) { super(OPERATOR, pos, replace, ifNotExists); this.name = Objects.requireNonNull(name); this.type = type; @@ -60,6 +65,7 @@ public class SqlCreateForeignSchema extends SqlCreate { this.optionList = optionList; // may be null } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, type, library, optionList); } @@ -104,11 +110,14 @@ public List> options() { } private static List> options( - final SqlNodeList optionList) { + final @Nullable SqlNodeList optionList) { + if (optionList == null) { + return ImmutableList.of(); + } return new AbstractList>() { @Override public Pair get(int index) { - return Pair.of((SqlIdentifier) optionList.get(index * 2), - optionList.get(index * 2 + 1)); + return Pair.of((SqlIdentifier) castNonNull(optionList.get(index * 2)), + castNonNull(optionList.get(index * 2 + 1))); } @Override public int size() { diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java index dbd93c1592e1..14b9bf06b089 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java @@ -27,6 +27,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -35,7 +37,7 @@ */ public class SqlCreateMaterializedView extends SqlCreate { public final SqlIdentifier name; - public final SqlNodeList columnList; + public final @Nullable SqlNodeList columnList; public final SqlNode query; private static final SqlOperator OPERATOR = @@ -44,7 +46,7 @@ public class SqlCreateMaterializedView extends SqlCreate { /** Creates a SqlCreateView. */ SqlCreateMaterializedView(SqlParserPos pos, boolean replace, - boolean ifNotExists, SqlIdentifier name, SqlNodeList columnList, + boolean ifNotExists, SqlIdentifier name, @Nullable SqlNodeList columnList, SqlNode query) { super(OPERATOR, pos, replace, ifNotExists); this.name = Objects.requireNonNull(name); @@ -52,6 +54,7 @@ public class SqlCreateMaterializedView extends SqlCreate { this.query = Objects.requireNonNull(query); } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, columnList, query); } diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java index 8f97b954ec46..b509ac1e40cb 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java @@ -27,6 +27,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -35,21 +37,22 @@ */ public class SqlCreateTable extends SqlCreate { public final SqlIdentifier name; - public final SqlNodeList columnList; - public final SqlNode query; + public final @Nullable SqlNodeList columnList; + public final @Nullable SqlNode query; private static final SqlOperator OPERATOR = new SqlSpecialOperator("CREATE TABLE", SqlKind.CREATE_TABLE); /** Creates a SqlCreateTable. */ protected SqlCreateTable(SqlParserPos pos, boolean replace, boolean ifNotExists, - SqlIdentifier name, SqlNodeList columnList, SqlNode query) { + SqlIdentifier name, @Nullable SqlNodeList columnList, @Nullable SqlNode query) { super(OPERATOR, pos, replace, ifNotExists); this.name = Objects.requireNonNull(name); this.columnList = columnList; // may be null this.query = query; // for "CREATE TABLE ... AS query"; may be null } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, columnList, query); } diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateType.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateType.java index 40e06899481c..f12b57518862 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateType.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateType.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -36,21 +38,22 @@ */ public class SqlCreateType extends SqlCreate { public final SqlIdentifier name; - public final SqlNodeList attributeDefs; - public final SqlDataTypeSpec dataType; + public final @Nullable SqlNodeList attributeDefs; + public final @Nullable SqlDataTypeSpec dataType; private static final SqlOperator OPERATOR = new SqlSpecialOperator("CREATE TYPE", SqlKind.CREATE_TYPE); /** Creates a SqlCreateType. */ SqlCreateType(SqlParserPos pos, boolean replace, SqlIdentifier name, - SqlNodeList attributeDefs, SqlDataTypeSpec dataType) { + @Nullable SqlNodeList attributeDefs, @Nullable SqlDataTypeSpec dataType) { super(OPERATOR, pos, replace, false); this.name = Objects.requireNonNull(name); this.attributeDefs = attributeDefs; // may be null this.dataType = dataType; // may be null } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, attributeDefs); } diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java index 7751f2b523b7..f232d2ab2c1c 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java @@ -27,6 +27,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -35,7 +37,7 @@ */ public class SqlCreateView extends SqlCreate { public final SqlIdentifier name; - public final SqlNodeList columnList; + public final @Nullable SqlNodeList columnList; public final SqlNode query; private static final SqlOperator OPERATOR = @@ -43,13 +45,14 @@ public class SqlCreateView extends SqlCreate { /** Creates a SqlCreateView. */ SqlCreateView(SqlParserPos pos, boolean replace, SqlIdentifier name, - SqlNodeList columnList, SqlNode query) { + @Nullable SqlNodeList columnList, SqlNode query) { super(OPERATOR, pos, replace, false); this.name = Objects.requireNonNull(name); this.columnList = columnList; // may be null this.query = Objects.requireNonNull(query); } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, columnList, query); } diff --git a/core/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java b/core/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java index 5a9f06c35865..bc526e1d1516 100644 --- a/core/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java +++ b/core/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java @@ -27,6 +27,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.ImmutableNullableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -41,11 +43,11 @@ public class SqlKeyConstraint extends SqlCall { protected static final SqlSpecialOperator PRIMARY = new SqlSpecialOperator("PRIMARY KEY", SqlKind.PRIMARY_KEY); - private final SqlIdentifier name; + private final @Nullable SqlIdentifier name; private final SqlNodeList columnList; /** Creates a SqlKeyConstraint. */ - SqlKeyConstraint(SqlParserPos pos, SqlIdentifier name, + SqlKeyConstraint(SqlParserPos pos, @Nullable SqlIdentifier name, SqlNodeList columnList) { super(pos); this.name = name; @@ -72,6 +74,7 @@ public static SqlKeyConstraint primary(SqlParserPos pos, SqlIdentifier name, return UNIQUE; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return ImmutableNullableList.of(name, columnList); } diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java index 33eb12328fde..abad4e3971ad 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/BigQuerySqlDialect.java @@ -46,11 +46,12 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.regex.Pattern; -import javax.annotation.Nonnull; /** * A SqlDialect implementation for Google BigQuery's "Standard SQL" @@ -102,7 +103,7 @@ public BigQuerySqlDialect(SqlDialect.Context context) { || RESERVED_KEYWORDS.contains(val.toUpperCase(Locale.ROOT)); } - @Override public SqlNode emulateNullDirection(SqlNode node, + @Override public @Nullable SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, boolean desc) { return emulateNullDirectionWithIsNull(node, nullsFirst, desc); } @@ -121,14 +122,14 @@ public BigQuerySqlDialect(SqlDialect.Context context) { return false; } - @Override public @Nonnull SqlParser.Config configureParser( + @Override public SqlParser.Config configureParser( SqlParser.Config configBuilder) { return super.configureParser(configBuilder) .withCharLiteralStyles(Lex.BIG_QUERY.charLiteralStyles); } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseFetchUsingLimit(writer, offset, fetch); } @@ -189,13 +190,12 @@ public BigQuerySqlDialect(SqlDialect.Context context) { if (interval.getSign() == -1) { writer.print("-"); } - Long intervalValueInLong; try { - intervalValueInLong = Long.parseLong(literal.getValue().toString()); + Long.parseLong(interval.getIntervalLiteral()); } catch (NumberFormatException e) { throw new RuntimeException("Only INT64 is supported as the interval value for BigQuery."); } - writer.literal(intervalValueInLong.toString()); + writer.literal(interval.getIntervalLiteral()); unparseSqlIntervalQualifier(writer, interval.getIntervalQualifier(), RelDataTypeSystem.DEFAULT); } @@ -269,7 +269,7 @@ private TimeUnit validate(TimeUnit timeUnit) { * * BigQuery Standard SQL Data Types. */ - @Override public SqlNode getCastSpec(final RelDataType type) { + @Override public @Nullable SqlNode getCastSpec(final RelDataType type) { if (type instanceof BasicSqlType) { final SqlTypeName typeName = type.getSqlTypeName(); switch (typeName) { diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/ClickHouseSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/ClickHouseSqlDialect.java index f4e375f15509..700660ae4764 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/ClickHouseSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/ClickHouseSqlDialect.java @@ -38,6 +38,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the ClickHouse database. */ @@ -70,7 +72,7 @@ public ClickHouseSqlDialect(Context context) { return CalendarPolicy.SHIFT; } - @Override public SqlNode getCastSpec(RelDataType type) { + @Override public @Nullable SqlNode getCastSpec(RelDataType type) { if (type instanceof BasicSqlType) { SqlTypeName typeName = type.getSqlTypeName(); switch (typeName) { @@ -128,8 +130,8 @@ private SqlDataTypeSpec createSqlDataTypeSpecByName(String typeAlias, SqlTypeNam writer.literal(toFunc + "('" + literal.toFormattedString() + "')"); } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { Preconditions.checkArgument(fetch != null); writer.newlineAndIndent(); @@ -191,7 +193,7 @@ private SqlDataTypeSpec createSqlDataTypeSpecByName(String typeAlias, SqlTypeNam */ private void unparseFloor(SqlWriter writer, SqlCall call) { final SqlLiteral timeUnitNode = call.operand(1); - TimeUnitRange unit = (TimeUnitRange) timeUnitNode.getValue(); + TimeUnitRange unit = timeUnitNode.getValueAs(TimeUnitRange.class); String funName; switch (unit) { diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/Db2SqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/Db2SqlDialect.java index 9dbbda032a2a..fb335e8aa5c8 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/Db2SqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/Db2SqlDialect.java @@ -88,7 +88,7 @@ public Db2SqlDialect(Context context) { if (interval.getSign() == -1) { writer.print("-"); } - writer.literal(literal.getValue().toString()); + writer.literal(interval.getIntervalLiteral()); unparseSqlIntervalQualifier(writer, interval.getIntervalQualifier(), RelDataTypeSystem.DEFAULT); } diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java index 2f1e31319b72..3aba1e51ead2 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/HiveSqlDialect.java @@ -32,6 +32,8 @@ import org.apache.calcite.sql.type.BasicSqlType; import org.apache.calcite.util.RelToSqlConverterUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the Apache Hive database. */ @@ -62,12 +64,12 @@ public HiveSqlDialect(Context context) { return false; } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseFetchUsingLimit(writer, offset, fetch); } - @Override public SqlNode emulateNullDirection(SqlNode node, + @Override public @Nullable SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, boolean desc) { if (emulateNullDirection) { return emulateNullDirectionWithIsNull(node, nullsFirst, desc); @@ -133,7 +135,7 @@ public HiveSqlDialect(Context context) { return false; } - @Override public SqlNode getCastSpec(final RelDataType type) { + @Override public @Nullable SqlNode getCastSpec(final RelDataType type) { if (type instanceof BasicSqlType) { switch (type.getSqlTypeName()) { case INTEGER: diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java index bda481c2f5fc..b509ea5d38f1 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java @@ -29,6 +29,8 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParserPos; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the Hsqldb database. */ @@ -78,8 +80,8 @@ public HsqldbSqlDialect(Context context) { } } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseFetchUsingLimit(writer, offset, fetch); } diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java index 6d9a150b2e97..f7b7e247f17b 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java @@ -28,6 +28,8 @@ import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -55,7 +57,7 @@ public JethroDataSqlDialect(Context context) { return false; } - @Override public SqlNode emulateNullDirection(SqlNode node, + @Override public @Nullable SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, boolean desc) { return node; } @@ -134,7 +136,7 @@ static class JethroSupportedFunction { this.operandTypes = b.build(); } - private SqlTypeName parse(String strType) { + private static SqlTypeName parse(String strType) { switch (strType.toLowerCase(Locale.ROOT)) { case "bigint": case "long": @@ -203,8 +205,12 @@ private JethroInfo makeInfo(Connection jethroConnection) { final Multimap supportedFunctions = LinkedHashMultimap.create(); while (functionsTupleSet.next()) { - String functionName = functionsTupleSet.getString(1); - String operandsType = functionsTupleSet.getString(3); + String functionName = Objects.requireNonNull( + functionsTupleSet.getString(1), + "functionName"); + String operandsType = Objects.requireNonNull( + functionsTupleSet.getString(3), + () -> "operands for " + functionName); supportedFunctions.put(functionName, new JethroSupportedFunction(functionName, operandsType)); } diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java index 7db41b89b605..8e25c1fd0d06 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java @@ -36,6 +36,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.ReturnTypes; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the Microsoft SQL Server * database. @@ -79,7 +81,7 @@ public MssqlSqlDialect(Context context) { * {@code ORDER BY CASE WHEN x IS NULL THEN 0 ELSE 1 END, x} * */ - @Override public SqlNode emulateNullDirection(SqlNode node, + @Override public @Nullable SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, boolean desc) { // Default ordering preserved if (nullCollation.isDefaultOrder(nullsFirst, desc)) { @@ -110,15 +112,15 @@ public MssqlSqlDialect(Context context) { } } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { if (!top) { super.unparseOffsetFetch(writer, offset, fetch); } } - @Override public void unparseTopN(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseTopN(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { if (top) { // Per Microsoft: // "For backward compatibility, the parentheses are optional in SELECT @@ -183,7 +185,7 @@ public MssqlSqlDialect(Context context) { */ private void unparseFloor(SqlWriter writer, SqlCall call) { SqlLiteral node = call.operand(1); - TimeUnitRange unit = (TimeUnitRange) node.getValue(); + TimeUnitRange unit = node.getValueAs(TimeUnitRange.class); switch (unit) { case YEAR: @@ -277,7 +279,7 @@ private void unparseSqlIntervalLiteralMssql( if (interval.getSign() * sign == -1) { writer.print("-"); } - writer.literal(literal.getValue().toString()); + writer.literal(interval.getIntervalLiteral()); } private void unparseFloorWithUnit(SqlWriter writer, SqlCall call, int charLen, diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java index fe67b5223b3b..9b1ec16a88a4 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java @@ -46,7 +46,7 @@ import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlTypeName; -import static org.apache.calcite.sql.type.SqlTypeName.TIMESTAMP; +import org.checkerframework.checker.nullness.qual.Nullable; /** * A SqlDialect implementation for the MySQL database. @@ -106,12 +106,12 @@ public MysqlSqlDialect(Context context) { return false; } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseFetchUsingLimit(writer, offset, fetch); } - @Override public SqlNode emulateNullDirection(SqlNode node, + @Override public @Nullable SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, boolean desc) { return emulateNullDirectionWithIsNull(node, nullsFirst, desc); } @@ -147,7 +147,7 @@ public MysqlSqlDialect(Context context) { return CalendarPolicy.SHIFT; } - @Override public SqlNode getCastSpec(RelDataType type) { + @Override public @Nullable SqlNode getCastSpec(RelDataType type) { switch (type.getSqlTypeName()) { case VARCHAR: // MySQL doesn't have a VARCHAR type, only CHAR. @@ -236,7 +236,7 @@ public MysqlSqlDialect(Context context) { */ private void unparseFloor(SqlWriter writer, SqlCall call) { SqlLiteral node = call.operand(1); - TimeUnitRange unit = (TimeUnitRange) node.getValue(); + TimeUnitRange unit = node.getValueAs(TimeUnitRange.class); if (unit == TimeUnitRange.WEEK) { writer.print("STR_TO_DATE"); diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/OracleSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/OracleSqlDialect.java index deeb8a50fe77..7de88dab2109 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/OracleSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/OracleSqlDialect.java @@ -40,6 +40,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -85,7 +87,7 @@ public OracleSqlDialect(Context context) { } } - @Override public SqlNode getCastSpec(RelDataType type) { + @Override public @Nullable SqlNode getCastSpec(RelDataType type) { String castSpec; switch (type.getSqlTypeName()) { case SMALLINT: diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java index fa18abd66ed6..cb873737720a 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/PostgresqlSqlDialect.java @@ -32,6 +32,8 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the PostgreSQL database. */ @@ -72,7 +74,7 @@ public PostgresqlSqlDialect(Context context) { return false; } - @Override public SqlNode getCastSpec(RelDataType type) { + @Override public @Nullable SqlNode getCastSpec(RelDataType type) { String castSpec; switch (type.getSqlTypeName()) { case TINYINT: diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java index 59fc6faaeb09..8bee34847edd 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java @@ -31,6 +31,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the Presto database. */ @@ -58,8 +60,8 @@ public PrestoSqlDialect(Context context) { return true; } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseUsingLimit(writer, offset, fetch); } @@ -71,7 +73,7 @@ private void unparseUsingLimit(SqlWriter writer, SqlNode offset, unparseLimit(writer, fetch); } - @Override public SqlNode emulateNullDirection(SqlNode node, + @Override public @Nullable SqlNode emulateNullDirection(SqlNode node, boolean nullsFirst, boolean desc) { return emulateNullDirectionWithIsNull(node, nullsFirst, desc); } @@ -108,7 +110,7 @@ private void unparseUsingLimit(SqlWriter writer, SqlNode offset, return CalendarPolicy.SHIFT; } - @Override public SqlNode getCastSpec(RelDataType type) { + @Override public @Nullable SqlNode getCastSpec(RelDataType type) { return super.getCastSpec(type); } diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java index 6a9364b28879..586d3ae75bd9 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlWriter; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the Redshift database. */ @@ -39,8 +41,8 @@ public RedshiftSqlDialect(Context context) { super(context); } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseFetchUsingLimit(writer, offset, fetch); } } diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java index 272e907c7d69..c53bf3a6a14a 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java @@ -32,6 +32,8 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.ReturnTypes; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.apache.calcite.util.RelToSqlConverterUtil.unparseHiveTrim; /** @@ -80,8 +82,8 @@ public SparkSqlDialect(SqlDialect.Context context) { return true; } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { unparseFetchUsingLimit(writer, offset, fetch); } diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/SybaseSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/SybaseSqlDialect.java index 8cbf3ea85b3c..8b42498685f6 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/SybaseSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/SybaseSqlDialect.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlWriter; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A SqlDialect implementation for the Sybase database. */ @@ -34,14 +36,14 @@ public SybaseSqlDialect(Context context) { super(context); } - @Override public void unparseOffsetFetch(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { // No-op; see unparseTopN. // Sybase uses "SELECT TOP (n)" rather than "FETCH NEXT n ROWS". } - @Override public void unparseTopN(SqlWriter writer, SqlNode offset, - SqlNode fetch) { + @Override public void unparseTopN(SqlWriter writer, @Nullable SqlNode offset, + @Nullable SqlNode fetch) { // Parentheses are not required, but we use them to be consistent with // Microsoft SQL Server, which recommends them but does not require them. // diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java index 27f72da3884c..8c5c4712de60 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java @@ -34,6 +34,8 @@ import org.apache.calcite.util.Optionality; import org.apache.calcite.util.Static; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Base class for grouping functions {@code GROUP_ID}, {@code GROUPING_ID}, * {@code GROUPING}. @@ -52,7 +54,7 @@ public class SqlAbstractGroupFunction extends SqlAggFunction { public SqlAbstractGroupFunction(String name, SqlKind kind, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory category) { super(name, null, kind, returnTypeInference, operandTypeInference, diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java index 78b144323002..e291c4121194 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.SqlOperatorBinding; import org.apache.calcite.sql.type.SqlTypeUtil; +import static java.util.Objects.requireNonNull; + /** * Definition of the SQL:2003 standard ARRAY constructor, ARRAY * [<expr>, ...]. @@ -35,9 +37,7 @@ public SqlArrayValueConstructor() { getComponentType( opBinding.getTypeFactory(), opBinding.collectOperandTypes()); - if (null == type) { - return null; - } + requireNonNull(type, "inferred array element type"); return SqlTypeUtil.createArrayType( opBinding.getTypeFactory(), type, false); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java index f553f13902c4..2b7dd0e12a12 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java @@ -32,7 +32,6 @@ import org.apache.calcite.util.Optionality; import java.util.Objects; -import javax.annotation.Nonnull; /** * Concrete implementation of {@link SqlAggFunction}. @@ -95,12 +94,12 @@ public static SqlBasicAggFunction create(String name, SqlKind kind, return super.deriveType(validator, scope, call); } - @Override @Nonnull public Optionality getDistinctOptionality() { + @Override public Optionality getDistinctOptionality() { return distinctOptionality; } /** Sets {@link #getDistinctOptionality()}. */ - SqlBasicAggFunction withDistinct(@Nonnull Optionality distinctOptionality) { + SqlBasicAggFunction withDistinct(Optionality distinctOptionality) { return new SqlBasicAggFunction(getName(), getSqlIdentifier(), kind, getReturnTypeInference(), getOperandTypeInference(), getOperandTypeChecker(), getFunctionType(), requiresOrder(), diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java index 54b60cf08ebd..685a12d41414 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java @@ -39,6 +39,8 @@ import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Defines the BETWEEN operator. * @@ -125,8 +127,9 @@ public boolean isNegated() { new ExplicitOperatorBinding( opBinding, opBinding.collectOperandTypes()); - return ReturnTypes.BOOLEAN_NULLABLE.inferReturnType( + RelDataType type = ReturnTypes.BOOLEAN_NULLABLE.inferReturnType( newOpBinding); + return requireNonNull(type, "inferred BETWEEN element type"); } @Override public String getSignatureTemplate(final int operandsCount) { diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlBitOpAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlBitOpAggFunction.java index cc3db36abf5f..ad9629d84ad3 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlBitOpAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlBitOpAggFunction.java @@ -26,6 +26,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Definition of the BIT_AND and BIT_OR aggregate functions, * returning the bitwise AND/OR of all non-null input values, or null if none. @@ -54,7 +56,7 @@ public SqlBitOpAggFunction(SqlKind kind) { || kind == SqlKind.BIT_XOR); } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz == SqlSplittableAggFunction.class) { return clazz.cast(SqlSplittableAggFunction.SelfSplitter.INSTANCE); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCase.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCase.java index 2c7f5b2748c9..f062d4737dbe 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCase.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCase.java @@ -25,18 +25,21 @@ import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.util.UnmodifiableArrayList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; + /** * A SqlCase is a node of a parse tree which represents a case * statement. It warrants its own node type just because we have a lot of * methods to put somewhere. */ public class SqlCase extends SqlCall { - SqlNode value; + @Nullable SqlNode value; SqlNodeList whenList; SqlNodeList thenList; - SqlNode elseExpr; + @Nullable SqlNode elseExpr; //~ Constructors ----------------------------------------------------------- @@ -49,8 +52,8 @@ public class SqlCase extends SqlCall { * @param thenList List of all THEN expressions * @param elseExpr The implicit or explicit ELSE expression */ - public SqlCase(SqlParserPos pos, SqlNode value, SqlNodeList whenList, - SqlNodeList thenList, SqlNode elseExpr) { + public SqlCase(SqlParserPos pos, @Nullable SqlNode value, SqlNodeList whenList, + SqlNodeList thenList, @Nullable SqlNode elseExpr) { super(pos); this.value = value; this.whenList = whenList; @@ -68,8 +71,8 @@ public SqlCase(SqlParserPos pos, SqlNode value, SqlNodeList whenList, * ELSE elseClause
    * END */ - public static SqlCase createSwitched(SqlParserPos pos, SqlNode value, - SqlNodeList whenList, SqlNodeList thenList, SqlNode elseClause) { + public static SqlCase createSwitched(SqlParserPos pos, @Nullable SqlNode value, + SqlNodeList whenList, SqlNodeList thenList, @Nullable SqlNode elseClause) { if (null != value) { List list = whenList.getList(); for (int i = 0; i < list.size(); i++) { @@ -101,11 +104,13 @@ public static SqlCase createSwitched(SqlParserPos pos, SqlNode value, return SqlStdOperatorTable.CASE; } + @SuppressWarnings("nullness") @Override public List getOperandList() { return UnmodifiableArrayList.of(value, whenList, thenList, elseExpr); } - @Override public void setOperand(int i, SqlNode operand) { + @SuppressWarnings("assignment.type.incompatible") + @Override public void setOperand(int i, @Nullable SqlNode operand) { switch (i) { case 0: value = operand; @@ -124,7 +129,7 @@ public static SqlCase createSwitched(SqlParserPos pos, SqlNode value, } } - public SqlNode getValueOperand() { + public @Nullable SqlNode getValueOperand() { return value; } @@ -136,7 +141,7 @@ public SqlNodeList getThenOperands() { return thenList; } - public SqlNode getElseOperand() { + public @Nullable SqlNode getElseOperand() { return elseExpr; } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCaseOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCaseOperator.java index bdbfb06aec29..dd6a1c0d3d32 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCaseOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCaseOperator.java @@ -47,11 +47,15 @@ import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * An operator describing a CASE, NULLIF or * COALESCE expression. All of these forms are normalized at parse time @@ -249,7 +253,8 @@ private RelDataType inferTypeFromValidator( } } - SqlNode elseOp = caseCall.getElseOperand(); + SqlNode elseOp = requireNonNull(caseCall.getElseOperand(), + () -> "elseOperand for " + caseCall); argTypes.add( SqlTypeUtil.deriveType(callBinding, elseOp)); if (SqlUtil.isNullLiteral(elseOp, false)) { @@ -279,6 +284,7 @@ private RelDataType inferTypeFromValidator( } final SqlValidatorImpl validator = (SqlValidatorImpl) callBinding.getValidator(); + requireNonNull(ret, () -> "return type for " + callBinding); for (SqlNode node : nullList) { validator.setValidatedNodeType(node, ret); } @@ -311,7 +317,9 @@ private RelDataType inferTypeFromOperands(SqlOperatorBinding opBinding) { } thenTypes.add(Iterables.getLast(argTypes)); - return typeFactory.leastRestrictive(thenTypes); + return requireNonNull( + typeFactory.leastRestrictive(thenTypes), + () -> "Can't find leastRestrictive type for " + thenTypes); } @Override public SqlOperandCountRange getOperandCountRange() { @@ -322,10 +330,11 @@ private RelDataType inferTypeFromOperands(SqlOperatorBinding opBinding) { return SqlSyntax.SPECIAL; } + @SuppressWarnings("argument.type.incompatible") @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { assert functionQualifier == null; assert operands.length == 4; return new SqlCase(pos, operands[0], (SqlNodeList) operands[1], @@ -348,8 +357,11 @@ private RelDataType inferTypeFromOperands(SqlOperatorBinding opBinding) { pair.right.unparse(writer, 0, 0); } - writer.sep("ELSE"); - kase.elseExpr.unparse(writer, 0, 0); + SqlNode elseExpr = kase.elseExpr; + if (elseExpr != null) { + writer.sep("ELSE"); + elseExpr.unparse(writer, 0, 0); + } writer.endList(frame); } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java index 5d6e49eee70d..ce45bf7e24fe 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java @@ -25,7 +25,6 @@ import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlKind; -import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlOperandCountRange; import org.apache.calcite.sql.SqlOperatorBinding; @@ -109,8 +108,7 @@ public SqlCastFunction() { // dynamic parameters and null constants need their types assigned // to them using the type they are casted to. - if (((operand0 instanceof SqlLiteral) - && (((SqlLiteral) operand0).getValue() == null)) + if (SqlUtil.isNullLiteral(operand0, false) || (operand0 instanceof SqlDynamicParam)) { final SqlValidatorImpl validator = (SqlValidatorImpl) callBinding.getValidator(); diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlConvertFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlConvertFunction.java index 4720a5c88002..9f9a6da01637 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlConvertFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlConvertFunction.java @@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlWriter; + /** * Common base for the CONVERT and TRANSLATE * functions. @@ -60,7 +61,6 @@ protected SqlConvertFunction(String name) { default: break; } - assert false; - return null; + throw new IllegalStateException("operandsCount should be 2, got " + operandsCount); } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java index 4a7c8d326583..763b291c689f 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java @@ -35,6 +35,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -89,7 +91,7 @@ public SqlCountAggFunction(String name, return super.deriveType(validator, scope, call); } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz == SqlSplittableAggFunction.class) { return clazz.cast(SqlSplittableAggFunction.CountSplitter.INSTANCE); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java index a580f5d0e4c1..b1824cb37540 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.validate.SqlMonotonicity; import org.apache.calcite.util.Util; +import static java.util.Objects.requireNonNull; + /** * The SQL EXTRACT operator. Extracts a specified field value from * a DATETIME or an INTERVAL. E.g.
    @@ -65,10 +67,10 @@ public SqlExtractFunction() { } @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) { - switch (call.getOperandLiteralValue(0, TimeUnitRange.class)) { + TimeUnitRange value = call.getOperandLiteralValue(0, TimeUnitRange.class); + switch (requireNonNull(value, "value for " + call)) { case YEAR: - SqlMonotonicity monotonicity = call.getOperandMonotonicity(1); - return monotonicity == null ? null : monotonicity.unstrict(); + return call.getOperandMonotonicity(1).unstrict(); default: return SqlMonotonicity.NOT_MONOTONIC; } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java index 729bd144eac7..ed7921d3e63a 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlFloorFunction.java @@ -55,8 +55,7 @@ public SqlFloorFunction(SqlKind kind) { @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) { // Monotonic iff its first argument is, but not strict. - SqlMonotonicity monotonicity = call.getOperandMonotonicity(0); - return monotonicity == null ? null : monotonicity.unstrict(); + return call.getOperandMonotonicity(0).unstrict(); } @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlGeoFunctions.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlGeoFunctions.java index dcd4baac58b0..08206aabeb50 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlGeoFunctions.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlGeoFunctions.java @@ -37,6 +37,8 @@ import com.esri.core.geometry.Geometry; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; /** @@ -101,7 +103,7 @@ public static class GridTable implements ScannableTable { .build(); } - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { if (geom != null && deltaX != null && deltaY != null) { final Geometry geometry = geom.g(); final Envelope envelope = new Envelope(); @@ -128,7 +130,7 @@ public static class GridTable implements ScannableTable { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java index cc83a4d829e0..d06e907a0491 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java @@ -35,6 +35,8 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.Optionality; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -70,8 +72,8 @@ public SqlJsonArrayAggAggFunction(SqlKind kind, return validateOperands(validator, scope, call); } - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { assert operands.length == 1 || operands.length == 2; final SqlNode valueExpr = operands[0]; if (operands.length == 2) { @@ -85,7 +87,8 @@ public SqlJsonArrayAggAggFunction(SqlKind kind, return createCall_(functionQualifier, pos, valueExpr); } - private SqlCall createCall_(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode valueExpr) { + private SqlCall createCall_(@Nullable SqlLiteral functionQualifier, SqlParserPos pos, + @Nullable SqlNode valueExpr) { return super.createCall(functionQualifier, pos, valueExpr); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java index 50d696c17be3..73031d6affac 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java @@ -33,6 +33,8 @@ import org.apache.calcite.sql.type.SqlOperandTypeChecker; import org.apache.calcite.sql.validate.SqlValidator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Locale; /** @@ -49,12 +51,12 @@ public SqlJsonArrayFunction() { } @Override protected void checkOperandCount(SqlValidator validator, - SqlOperandTypeChecker argType, SqlCall call) { + @Nullable SqlOperandTypeChecker argType, SqlCall call) { assert call.operandCount() >= 1; } - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { if (operands[0] == null) { operands[0] = SqlLiteral.createSymbol(SqlJsonConstructorNullClause.ABSENT_ON_NULL, @@ -63,7 +65,7 @@ public SqlJsonArrayFunction() { return super.createCall(functionQualifier, pos, operands); } - @Override public String getSignatureTemplate(int operandsCount) { + @Override public @Nullable String getSignatureTemplate(int operandsCount) { assert operandsCount >= 1; final StringBuilder sb = new StringBuilder(); sb.append("{0}("); diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java index d221a31e0fb0..91a017bca6a6 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.type.SqlTypeTransforms; import org.apache.calcite.sql.validate.SqlValidator; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * The JSON_DEPTH function. */ @@ -46,7 +48,7 @@ public SqlJsonDepthFunction() { } @Override protected void checkOperandCount(SqlValidator validator, - SqlOperandTypeChecker argType, SqlCall call) { + @Nullable SqlOperandTypeChecker argType, SqlCall call) { assert call.operandCount() == 1; } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java index 160f166e255c..5696fd386a15 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java @@ -36,6 +36,8 @@ import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.sql.validate.SqlValidator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Locale; import static org.apache.calcite.util.Static.RESOURCE; @@ -64,7 +66,7 @@ public SqlJsonObjectFunction() { } @Override protected void checkOperandCount(SqlValidator validator, - SqlOperandTypeChecker argType, SqlCall call) { + @Nullable SqlOperandTypeChecker argType, SqlCall call) { assert call.operandCount() % 2 == 1; } @@ -91,8 +93,8 @@ public SqlJsonObjectFunction() { return true; } - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { if (operands[0] == null) { operands[0] = SqlLiteral.createSymbol( SqlJsonConstructorNullClause.NULL_ON_NULL, pos); @@ -100,7 +102,7 @@ public SqlJsonObjectFunction() { return super.createCall(functionQualifier, pos, operands); } - @Override public String getSignatureTemplate(int operandsCount) { + @Override public @Nullable String getSignatureTemplate(int operandsCount) { assert operandsCount % 2 == 1; StringBuilder sb = new StringBuilder(); sb.append("{0}("); diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonPrettyFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonPrettyFunction.java index 8a621db93981..ab20f71f5e0a 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonPrettyFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonPrettyFunction.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.type.SqlTypeTransforms; import org.apache.calcite.sql.validate.SqlValidator; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * The JSON_TYPE function. */ @@ -44,7 +46,7 @@ public SqlJsonPrettyFunction() { } @Override protected void checkOperandCount(SqlValidator validator, - SqlOperandTypeChecker argType, SqlCall call) { + @Nullable SqlOperandTypeChecker argType, SqlCall call) { assert call.operandCount() == 1; } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java index 83a210e04691..5c5ba03b4e15 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java @@ -31,6 +31,8 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeTransforms; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * The JSON_QUERY function. */ @@ -44,7 +46,7 @@ public SqlJsonQueryFunction() { SqlFunctionCategory.SYSTEM); } - @Override public String getSignatureTemplate(int operandsCount) { + @Override public @Nullable String getSignatureTemplate(int operandsCount) { return "{0}({1} {2} {3} WRAPPER {4} ON EMPTY {5} ON ERROR)"; } @@ -77,8 +79,8 @@ public SqlJsonQueryFunction() { writer.endFunCall(frame); } - @Override public SqlCall createCall(SqlLiteral functionQualifier, - SqlParserPos pos, SqlNode... operands) { + @Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier, + SqlParserPos pos, @Nullable SqlNode... operands) { if (operands[2] == null) { operands[2] = SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITHOUT_ARRAY, pos); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonTypeFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonTypeFunction.java index 39d50cd3845b..12929993659d 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonTypeFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonTypeFunction.java @@ -29,6 +29,8 @@ import org.apache.calcite.sql.type.SqlTypeTransforms; import org.apache.calcite.sql.validate.SqlValidator; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * The JSON_TYPE function. */ @@ -45,7 +47,7 @@ public SqlJsonTypeFunction() { } @Override protected void checkOperandCount(SqlValidator validator, - SqlOperandTypeChecker argType, SqlCall call) { + @Nullable SqlOperandTypeChecker argType, SqlCall call) { assert call.operandCount() == 1; } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueFunction.java index 8e5b1e1fdfde..e5ce908a1b90 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueFunction.java @@ -37,6 +37,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -70,7 +72,7 @@ private static RelDataType getDefaultType(SqlOperatorBinding opBinding) { * Returns new operand list with type specification removed. */ public static List removeTypeSpecOperands(SqlCall call) { - SqlNode[] operands = call.getOperandList().toArray(SqlNode.EMPTY_ARRAY); + @Nullable SqlNode[] operands = call.getOperandList().toArray(new SqlNode[0]); if (hasExplicitTypeSpec(operands)) { operands[2] = null; operands[3] = null; diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java index 37d90587ce2d..6e4c26a6a261 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Locale; import java.util.Map; @@ -79,7 +81,7 @@ public enum SqlLibrary { /** Looks up a value. * Returns null if not found. * You can use upper- or lower-case name. */ - public static SqlLibrary of(String name) { + public static @Nullable SqlLibrary of(String name) { return MAP.get(name); } @@ -87,7 +89,9 @@ public static SqlLibrary of(String name) { public static List parse(String libraryNameList) { final ImmutableList.Builder list = ImmutableList.builder(); for (String libraryName : libraryNameList.split(",")) { - list.add(SqlLibrary.of(libraryName)); + SqlLibrary library = Objects.requireNonNull( + SqlLibrary.of(libraryName), () -> "library does not exist: " + libraryName); + list.add(library); } return list.build(); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java index 8ff1b46a1706..b18a91407e8e 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java @@ -39,6 +39,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -86,7 +88,7 @@ private SqlLibraryOperators() { } final RelDataTypeFactory typeFactory = opBinding.getTypeFactory(); RelDataType type = typeFactory.leastRestrictive(list); - if (opBinding.getOperandCount() % 2 == 1) { + if (type != null && opBinding.getOperandCount() % 2 == 1) { type = typeFactory.createTypeWithNullability(type, true); } return type; @@ -123,7 +125,7 @@ private SqlLibraryOperators() { /** Infers the return type of {@code IF(b, x, y)}, * namely the least restrictive of the types of x and y. * Similar to {@link ReturnTypes#LEAST_RESTRICTIVE}. */ - private static RelDataType inferIfReturnType(SqlOperatorBinding opBinding) { + private static @Nullable RelDataType inferIfReturnType(SqlOperatorBinding opBinding) { return opBinding.getTypeFactory() .leastRestrictive(opBinding.collectOperandTypes().subList(1, 3)); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java index 6cd82396278f..11cf63cbdd75 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java @@ -43,8 +43,10 @@ import java.util.List; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; + /** * Internal operator, by which the parser represents a continued string literal. * @@ -84,10 +86,8 @@ private boolean argTypesValid(SqlCallBinding callBinding) { RelDataType type = SqlTypeUtil.deriveType(callBinding, operand.e); if (operand.i == 0) { firstType = type; - } else { - if (!SqlTypeUtil.sameNamedType(firstType, type)) { - return false; - } + } else if (!SqlTypeUtil.sameNamedType(castNonNull(firstType), type)) { + return false; } } return true; @@ -179,7 +179,7 @@ private boolean argTypesValid(SqlCallBinding callBinding) { } else { // print without prefix if (rand.getTypeName() == SqlTypeName.BINARY) { - BitString bs = (BitString) rand.getValue(); + BitString bs = rand.getValueAs(BitString.class); writer.literal("'" + bs.toHexString() + "'"); } else { writer.literal("'" + rand.toValue() + "'"); diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java index 61300d1b910a..e5f2c9318fd4 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java @@ -25,10 +25,14 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Definition of the MAP constructor, * MAP [<key>, <value>, ...]. @@ -41,16 +45,13 @@ public SqlMapValueConstructor() { } @Override public RelDataType inferReturnType(SqlOperatorBinding opBinding) { - Pair type = + Pair<@Nullable RelDataType, @Nullable RelDataType> type = getComponentTypes( opBinding.getTypeFactory(), opBinding.collectOperandTypes()); - if (null == type) { - return null; - } return SqlTypeUtil.createMapType( opBinding.getTypeFactory(), - type.left, - type.right, + requireNonNull(type.left, "inferred key type"), + requireNonNull(type.right, "inferred value type"), false); } @@ -64,7 +65,7 @@ public SqlMapValueConstructor() { if (argTypes.size() % 2 > 0) { throw callBinding.newValidationError(RESOURCE.mapRequiresEvenArgCount()); } - final Pair componentType = + final Pair<@Nullable RelDataType, @Nullable RelDataType> componentType = getComponentTypes( callBinding.getTypeFactory(), argTypes); if (null == componentType.left || null == componentType.right) { @@ -76,7 +77,7 @@ public SqlMapValueConstructor() { return true; } - private Pair getComponentTypes( + private Pair<@Nullable RelDataType, @Nullable RelDataType> getComponentTypes( RelDataTypeFactory typeFactory, List argTypes) { return Pair.of( diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java index cca0787540e6..1d0a7b29932a 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java @@ -30,6 +30,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -143,7 +145,7 @@ public int getMinMaxKind() { } } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz == SqlSplittableAggFunction.class) { return clazz.cast(SqlSplittableAggFunction.SelfSplitter.INSTANCE); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicBinaryOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicBinaryOperator.java index 72a67073b889..9ec3e8a9929c 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicBinaryOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicBinaryOperator.java @@ -58,10 +58,6 @@ public SqlMonotonicBinaryOperator( final SqlMonotonicity mono0 = call.getOperandMonotonicity(0); final SqlMonotonicity mono1 = call.getOperandMonotonicity(1); - // unknown unknown --> unknown - if (mono0 == null || mono1 == null) { - return null; - } // constant constant --> constant if ((mono1 == SqlMonotonicity.CONSTANT) && (mono0 == SqlMonotonicity.CONSTANT)) { diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java index 17c78fedc313..bee115520313 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMonotonicUnaryFunction.java @@ -25,6 +25,8 @@ import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.sql.validate.SqlMonotonicity; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Base class for unary operators such as FLOOR/CEIL which are monotonic for * monotonic inputs. @@ -36,7 +38,7 @@ protected SqlMonotonicUnaryFunction( String name, SqlKind kind, SqlReturnTypeInference returnTypeInference, - SqlOperandTypeInference operandTypeInference, + @Nullable SqlOperandTypeInference operandTypeInference, SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory funcType) { super( diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java index 2eaec975a9bd..0d77cb8af29d 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java @@ -32,10 +32,14 @@ import org.apache.calcite.sql.validate.SqlValidatorNamespace; import org.apache.calcite.sql.validate.SqlValidatorScope; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Definition of the SQL:2003 standard MULTISET query constructor, * MULTISET (<query>). @@ -67,16 +71,14 @@ protected SqlMultisetQueryConstructor(String name, SqlKind kind) { getComponentType( opBinding.getTypeFactory(), opBinding.collectOperandTypes()); - if (null == type) { - return null; - } + requireNonNull(type, "inferred multiset query element type"); return SqlTypeUtil.createMultisetType( opBinding.getTypeFactory(), type, false); } - private RelDataType getComponentType( + private @Nullable RelDataType getComponentType( RelDataTypeFactory typeFactory, List argTypes) { return typeFactory.leastRestrictive(argTypes); @@ -106,6 +108,7 @@ private RelDataType getComponentType( SqlSelect subSelect = call.operand(0); subSelect.validateExpr(validator, scope); SqlValidatorNamespace ns = validator.getNamespace(subSelect); + assert ns != null : "namespace is missing for " + subSelect; assert null != ns.getRowType(); return SqlTypeUtil.createMultisetType( validator.getTypeFactory(), diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java index d91f54e13f5e..ccf681f3ee1c 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java @@ -30,10 +30,14 @@ import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Definition of the SQL:2003 standard MULTISET constructor, MULTISET * [<expr>, ...]. @@ -67,16 +71,14 @@ protected SqlMultisetValueConstructor(String name, SqlKind kind) { getComponentType( opBinding.getTypeFactory(), opBinding.collectOperandTypes()); - if (null == type) { - return null; - } + requireNonNull(type, "inferred multiset value"); return SqlTypeUtil.createMultisetType( opBinding.getTypeFactory(), type, false); } - protected RelDataType getComponentType( + protected @Nullable RelDataType getComponentType( RelDataTypeFactory typeFactory, List argTypes) { return typeFactory.leastRestrictive(argTypes); diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlayFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlayFunction.java index f3f95c1fb374..71192cfb6619 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlayFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlayFunction.java @@ -75,9 +75,7 @@ public SqlOverlayFunction() { case 4: return "{0}({1} PLACING {2} FROM {3} FOR {4})"; default: - break; + throw new IllegalArgumentException("operandsCount shuld be 3 or 4, got " + operandsCount); } - assert false; - return null; } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlPosixRegexOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlPosixRegexOperator.java index 2e45a1c153a9..96d1281bf93e 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlPosixRegexOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlPosixRegexOperator.java @@ -33,6 +33,8 @@ import org.apache.calcite.sql.type.SqlOperandCountRanges; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; /** @@ -79,9 +81,9 @@ public class SqlPosixRegexOperator extends SqlBinaryOperator { } @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { pos = pos.plusAll(Arrays.asList(operands)); operands = Arrays.copyOf(operands, operands.length + 1); operands[operands.length - 1] = SqlLiteral.createBoolean(caseSensitive, SqlParserPos.ZERO); diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java index b8f90acad834..c87462fc86cd 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java @@ -71,8 +71,15 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link org.apache.calcite.sql.SqlOperatorTable} containing * the standard operators and functions. @@ -84,7 +91,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { /** * The standard operator table. */ - private static SqlStdOperatorTable instance; + private static @MonotonicNonNull SqlStdOperatorTable instance; //------------------------------------------------------------- // SET OPERATORS @@ -930,7 +937,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { /** * SUM aggregate function. */ - public static final SqlAggFunction SUM = new SqlSumAggFunction(null); + public static final SqlAggFunction SUM = new SqlSumAggFunction(castNonNull(null)); /** * COUNT aggregate function. @@ -1013,7 +1020,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { * SINGLE_VALUE aggregate function. */ public static final SqlAggFunction SINGLE_VALUE = - new SqlSingleValueAggFunction(null); + new SqlSingleValueAggFunction(castNonNull(null)); /** * AVG aggregate function. @@ -1113,7 +1120,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { * aggregate versions of MIN/MAX */ public static final SqlAggFunction HISTOGRAM_AGG = - new SqlHistogramAggFunction(null); + new SqlHistogramAggFunction(castNonNull(null)); /** * HISTOGRAM_MIN window aggregate function. @@ -2478,7 +2485,7 @@ public static synchronized SqlStdOperatorTable instance() { /** Returns the group function for which a given kind is an auxiliary * function, or null if it is not an auxiliary function. */ - public static SqlGroupedWindowFunction auxiliaryToGroup(SqlKind kind) { + public static @Nullable SqlGroupedWindowFunction auxiliaryToGroup(SqlKind kind) { switch (kind) { case TUMBLE_START: case TUMBLE_END: @@ -2499,11 +2506,12 @@ public static SqlGroupedWindowFunction auxiliaryToGroup(SqlKind kind) { * *

    For example, converts {@code TUMBLE_START(rowtime, INTERVAL '1' HOUR))} * to {@code TUMBLE(rowtime, INTERVAL '1' HOUR))}. */ - public static SqlCall convertAuxiliaryToGroupCall(SqlCall call) { + public static @Nullable SqlCall convertAuxiliaryToGroupCall(SqlCall call) { final SqlOperator op = call.getOperator(); if (op instanceof SqlGroupedWindowFunction && op.isGroupAuxiliary()) { - return copy(call, ((SqlGroupedWindowFunction) op).groupFunction); + SqlGroupedWindowFunction groupFunction = ((SqlGroupedWindowFunction) op).groupFunction; + return copy(call, requireNonNull(groupFunction, "groupFunction")); } return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java index e02f4ac4aa31..7cec5f2eaed1 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java @@ -28,6 +28,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -77,7 +79,7 @@ public RelDataType getType() { return type; } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz == SqlSplittableAggFunction.class) { return clazz.cast(SqlSplittableAggFunction.SumSplitter.INSTANCE); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java index 277e22a50484..0b3ce66be351 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java @@ -29,6 +29,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -68,7 +70,7 @@ public SqlSumEmptyIsZeroAggFunction() { typeFactory.createSqlType(SqlTypeName.ANY), true); } - @Override public T unwrap(Class clazz) { + @Override public @Nullable T unwrap(Class clazz) { if (clazz == SqlSplittableAggFunction.class) { return clazz.cast(SqlSplittableAggFunction.Sum0Splitter.INSTANCE); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java index 0594dfe92dfd..420ad81f7bda 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java @@ -37,6 +37,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.List; @@ -119,9 +121,9 @@ public SqlTrimFunction(String name, SqlKind kind, } @Override public SqlCall createCall( - SqlLiteral functionQualifier, + @Nullable SqlLiteral functionQualifier, SqlParserPos pos, - SqlNode... operands) { + @Nullable SqlNode... operands) { assert functionQualifier == null; switch (operands.length) { case 1: diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java index 66f11524ee70..8fc5692c77a3 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java @@ -34,6 +34,10 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.io.Reader; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; @@ -350,7 +354,7 @@ protected enum ExprContext { protected int nDynamicParams; - protected String originalSql; + protected @Nullable String originalSql; protected final List warnings = new ArrayList<>(); @@ -421,7 +425,7 @@ protected SqlCall createCall( * @param ex dirty excn * @return clean excn */ - public abstract SqlParseException normalizeException(Throwable ex); + public abstract SqlParseException normalizeException(@PolyNull Throwable ex); protected abstract SqlParserPos getPos() throws Exception; @@ -499,7 +503,7 @@ public void setOriginalSql(String originalSql) { /** * Returns the SQL text. */ - public String getOriginalSql() { + public @Nullable String getOriginalSql() { return originalSql; } @@ -669,6 +673,7 @@ public MetadataImpl(SqlAbstractParserImpl sqlParser) { * Initializes lists of keywords. */ private void initList( + @UnderInitialization MetadataImpl this, SqlAbstractParserImpl parserImpl, Set keywords, String name) { @@ -714,13 +719,14 @@ private void initList( * @param name Name of method. For example "ReservedFunctionName". * @return Result of calling method */ - private Object virtualCall( + private @Nullable Object virtualCall( + @UnderInitialization MetadataImpl this, SqlAbstractParserImpl parserImpl, String name) throws Throwable { Class clazz = parserImpl.getClass(); try { - final Method method = clazz.getMethod(name, (Class[]) null); - return method.invoke(parserImpl, (Object[]) null); + final Method method = clazz.getMethod(name); + return method.invoke(parserImpl); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); throw parserImpl.normalizeException(cause); @@ -730,7 +736,8 @@ private Object virtualCall( /** * Builds a comma-separated list of JDBC reserved words. */ - private String constructSql92ReservedWordList() { + private String constructSql92ReservedWordList( + @UnderInitialization MetadataImpl this) { StringBuilder sb = new StringBuilder(); TreeSet jdbcReservedSet = new TreeSet<>(); jdbcReservedSet.addAll(tokenSet); diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java index d2eacec767f9..ecb793c7829a 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java @@ -21,6 +21,9 @@ import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -28,6 +31,7 @@ import java.util.List; import java.util.Objects; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; /** @@ -89,7 +93,7 @@ public SqlParserPos( return Objects.hash(lineNumber, columnNumber, endLineNumber, endColumnNumber); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof SqlParserPos && this.lineNumber == ((SqlParserPos) o).lineNumber @@ -167,7 +171,7 @@ public SqlParserPos plusAll(SqlNode[] nodes) { /** * Combines this parser position with a list of positions. */ - public SqlParserPos plusAll(Collection nodeList) { + public SqlParserPos plusAll(Collection<@Nullable SqlNode> nodeList) { int line = getLineNum(); int column = getColumnNum(); int endLine = getEndLineNum(); @@ -194,8 +198,10 @@ private static List toPos(final SqlNode[] nodes) { }; } - private static Iterable toPos(Iterable nodes) { - return Util.transform(nodes, node -> node == null ? null : node.getParserPosition()); + private static Iterable<@PolyNull SqlParserPos> toPos( + Iterable nodes) { + return Util.transform(nodes, + node -> node == null ? castNonNull(null) : node.getParserPosition()); } /** @@ -256,7 +262,7 @@ private static SqlParserPos sum_(final List positions) { * @return Sum of parser positions */ private static SqlParserPos sum( - Iterable poses, + Iterable poses, int line, int column, int endLine, diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java index 6d6393338ff3..a462ddb46801 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java @@ -49,6 +49,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; import org.slf4j.Logger; import java.math.BigDecimal; @@ -83,7 +85,7 @@ private SqlParserUtil() { /** Returns the character-set prefix of a SQL string literal; returns null if * there is none. */ - public static String getCharacterSet(String s) { + public static @Nullable String getCharacterSet(String s) { if (s.charAt(0) == '\'') { return null; } @@ -340,8 +342,8 @@ public static byte[] parseBinaryString(String s) { /** * Unquotes a quoted string, using different quotes for beginning and end. */ - public static String strip(String s, String startQuote, String endQuote, - String escape, Casing casing) { + public static String strip(String s, @PolyNull String startQuote, @PolyNull String endQuote, + @PolyNull String escape, Casing casing) { if (startQuote != null) { assert endQuote != null; assert startQuote.length() == 1; @@ -481,7 +483,7 @@ public static String addCarets( return sqlWithCarets; } - public static String getTokenVal(String token) { + public static @Nullable String getTokenVal(String token) { // We don't care about the token which are not string if (!token.startsWith("\"")) { return null; @@ -925,7 +927,7 @@ private OldTokenSequenceImpl(List list) { /** Pre-initialized {@link DateFormat} objects, to be used within the current * thread, because {@code DateFormat} is not thread-safe. */ private static class Format { - private static final ThreadLocal PER_THREAD = + private static final ThreadLocal<@Nullable Format> PER_THREAD = ThreadLocal.withInitial(Format::new); final DateFormat timestamp = new SimpleDateFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING, diff --git a/core/src/main/java/org/apache/calcite/sql/pretty/SqlPrettyWriter.java b/core/src/main/java/org/apache/calcite/sql/pretty/SqlPrettyWriter.java index dff6934d038c..14cdd10e0497 100644 --- a/core/src/main/java/org/apache/calcite/sql/pretty/SqlPrettyWriter.java +++ b/core/src/main/java/org/apache/calcite/sql/pretty/SqlPrettyWriter.java @@ -33,6 +33,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.LoggerFactory; import java.io.PrintWriter; @@ -48,7 +49,8 @@ import java.util.Properties; import java.util.Set; import java.util.function.Consumer; -import javax.annotation.Nonnull; + +import static java.util.Objects.requireNonNull; /** * Pretty printer for SQL statements. @@ -266,32 +268,33 @@ public class SqlPrettyWriter implements SqlWriter { private final SqlDialect dialect; private final StringBuilder buf; private final Deque listStack = new ArrayDeque<>(); - private ImmutableList.Builder dynamicParameters; - protected FrameImpl frame; + private ImmutableList.@Nullable Builder dynamicParameters; + protected @Nullable FrameImpl frame; private boolean needWhitespace; - protected String nextWhitespace; + protected @Nullable String nextWhitespace; private SqlWriterConfig config; - private Bean bean; + private @Nullable Bean bean; private int currentIndent; private int lineStart; //~ Constructors ----------------------------------------------------------- + @SuppressWarnings("method.invocation.invalid") private SqlPrettyWriter(SqlWriterConfig config, StringBuilder buf, @SuppressWarnings("unused") boolean ignore) { - this.buf = Objects.requireNonNull(buf); - this.dialect = Objects.requireNonNull(config.dialect()); - this.config = Objects.requireNonNull(config); + this.buf = requireNonNull(buf); + this.dialect = requireNonNull(config.dialect()); + this.config = requireNonNull(config); lineStart = 0; reset(); } /** Creates a writer with the given configuration * and a given buffer to write to. */ - public SqlPrettyWriter(@Nonnull SqlWriterConfig config, - @Nonnull StringBuilder buf) { - this(config, Objects.requireNonNull(buf), false); + public SqlPrettyWriter(SqlWriterConfig config, + StringBuilder buf) { + this(config, requireNonNull(buf), false); } /** Creates a writer with the given configuration and dialect, @@ -300,14 +303,14 @@ public SqlPrettyWriter( SqlDialect dialect, SqlWriterConfig config, StringBuilder buf) { - this(config.withDialect(Objects.requireNonNull(dialect)), buf); + this(config.withDialect(requireNonNull(dialect)), buf); } /** Creates a writer with the given configuration * and a private print writer. */ @Deprecated public SqlPrettyWriter(SqlDialect dialect, SqlWriterConfig config) { - this(config.withDialect(Objects.requireNonNull(dialect))); + this(config.withDialect(requireNonNull(dialect))); } @Deprecated @@ -316,7 +319,7 @@ public SqlPrettyWriter( boolean alwaysUseParentheses, PrintWriter pw) { // NOTE that 'pw' is ignored; there is no place for it in the new API - this(config().withDialect(Objects.requireNonNull(dialect)) + this(config().withDialect(requireNonNull(dialect)) .withAlwaysUseParentheses(alwaysUseParentheses)); } @@ -324,7 +327,7 @@ public SqlPrettyWriter( public SqlPrettyWriter( SqlDialect dialect, boolean alwaysUseParentheses) { - this(config().withDialect(Objects.requireNonNull(dialect)) + this(config().withDialect(requireNonNull(dialect)) .withAlwaysUseParentheses(alwaysUseParentheses)); } @@ -332,12 +335,12 @@ public SqlPrettyWriter( * and a private print writer. */ @Deprecated public SqlPrettyWriter(SqlDialect dialect) { - this(config().withDialect(Objects.requireNonNull(dialect))); + this(config().withDialect(requireNonNull(dialect))); } /** Creates a writer with the given configuration, * and a private builder. */ - public SqlPrettyWriter(@Nonnull SqlWriterConfig config) { + public SqlPrettyWriter(SqlWriterConfig config) { this(config, new StringBuilder(), true); } @@ -468,7 +471,7 @@ public void describe(PrintWriter pw, boolean omitDefaults) { final String[] propertyNames = properties.getPropertyNames(); int count = 0; for (String key : propertyNames) { - final Object value = bean.get(key); + final Object value = properties.get(key); final Object defaultValue = DEFAULT_BEAN.get(key); if (Objects.equals(value, defaultValue)) { continue; @@ -565,7 +568,7 @@ public void setQuoteAllIdentifiers(boolean quoteAllIdentifiers) { */ protected FrameImpl createListFrame( FrameType frameType, - String keyword, + @Nullable String keyword, String open, String close) { final FrameTypeEnum frameTypeEnum = @@ -660,12 +663,11 @@ protected FrameImpl createListFrame( } final int chopColumn; - final SqlWriterConfig.LineFolding lineFolding; - if (config.lineFolding() == null) { + SqlWriterConfig.LineFolding lineFolding = config.lineFolding(); + if (lineFolding == null) { lineFolding = SqlWriterConfig.LineFolding.WIDE; chopColumn = -1; } else { - lineFolding = config.lineFolding(); if (config.foldLength() > 0 && (lineFolding == SqlWriterConfig.LineFolding.CHOP || lineFolding == SqlWriterConfig.LineFolding.FOLD @@ -824,8 +826,8 @@ private SqlWriterConfig.LineFolding fold(FrameTypeEnum frameType) { } } - private SqlWriterConfig.LineFolding f3(SqlWriterConfig.LineFolding folding0, - SqlWriterConfig.LineFolding folding1, boolean opt) { + private SqlWriterConfig.LineFolding f3(SqlWriterConfig.@Nullable LineFolding folding0, + SqlWriterConfig.@Nullable LineFolding folding1, boolean opt) { return folding0 != null ? folding0 : folding1 != null ? folding1 : opt ? SqlWriterConfig.LineFolding.TALL @@ -843,10 +845,11 @@ private SqlWriterConfig.LineFolding f3(SqlWriterConfig.LineFolding folding0, */ protected Frame startList( FrameType frameType, - String keyword, + @Nullable String keyword, String open, String close) { assert frameType != null; + FrameImpl frame = this.frame; if (frame != null) { if (frame.itemCount++ == 0 && frame.newlineAfterOpen) { newlineAndIndent(); @@ -865,27 +868,28 @@ protected Frame startList( listStack.push(frame); } frame = createListFrame(frameType, keyword, open, close); + this.frame = frame; frame.before(); return frame; } - @Override public void endList(Frame frame) { + @Override public void endList(@Nullable Frame frame) { FrameImpl endedFrame = (FrameImpl) frame; Preconditions.checkArgument(frame == this.frame, "Frame does not match current frame"); - if (this.frame == null) { + if (endedFrame == null) { throw new RuntimeException("No list started"); } - if (this.frame.open.equals("(")) { - if (!this.frame.close.equals(")")) { + if (endedFrame.open.equals("(")) { + if (!endedFrame.close.equals(")")) { throw new RuntimeException("Expected ')'"); } } - if (this.frame.newlineBeforeClose) { + if (endedFrame.newlineBeforeClose) { newlineAndIndent(); } - keyword(this.frame.close); - if (this.frame.newlineAfterClose) { + keyword(endedFrame.close); + if (endedFrame.newlineAfterClose) { newlineAndIndent(); } @@ -959,7 +963,7 @@ private static boolean needWhitespaceAfter(String s) { protected void whiteSpace() { if (needWhitespace) { - if (nextWhitespace.equals(NL)) { + if (NL.equals(nextWhitespace)) { newlineAndIndent(); } else { buf.append(nextWhitespace); @@ -1018,14 +1022,14 @@ protected boolean tooLong(String s) { setNeedWhitespace(true); } - @Override public void fetchOffset(SqlNode fetch, SqlNode offset) { + @Override public void fetchOffset(@Nullable SqlNode fetch, @Nullable SqlNode offset) { if (fetch == null && offset == null) { return; } dialect.unparseOffsetFetch(this, offset, fetch); } - @Override public void topN(SqlNode fetch, SqlNode offset) { + @Override public void topN(@Nullable SqlNode fetch, @Nullable SqlNode offset) { if (fetch == null && offset == null) { return; } @@ -1096,7 +1100,7 @@ public void setLineLength(int lineLength) { this.config = config.withLineLength(lineLength); } - public void setFormatOptions(SqlFormatOptions options) { + public void setFormatOptions(@Nullable SqlFormatOptions options) { if (options == null) { return; } @@ -1122,7 +1126,7 @@ public void setFormatOptions(SqlFormatOptions options) { */ protected class FrameImpl implements Frame { final FrameType frameType; - final String keyword; + final @Nullable String keyword; final String open; final String close; @@ -1163,7 +1167,7 @@ protected class FrameImpl implements Frame { /** How lines are to be folded. */ private final SqlWriterConfig.LineFolding lineFolding; - FrameImpl(FrameType frameType, String keyword, String open, String close, + FrameImpl(FrameType frameType, @Nullable String keyword, String open, String close, int left, int extraIndent, int chopLimit, SqlWriterConfig.LineFolding lineFolding, boolean newlineAfterOpen, boolean newlineBeforeSep, int sepIndent, boolean newlineAfterSep, @@ -1406,13 +1410,16 @@ private static class Bean { } } - private String stripPrefix(String name, int offset) { + private static String stripPrefix(String name, int offset) { return name.substring(offset, offset + 1).toLowerCase(Locale.ROOT) + name.substring(offset + 1); } public void set(String name, String value) { - final Method method = setterMethods.get(name); + final Method method = requireNonNull( + setterMethods.get(name), + () -> "setter method " + name + " not found" + ); try { method.invoke(o, value); } catch (IllegalAccessException | InvocationTargetException e) { @@ -1420,8 +1427,11 @@ public void set(String name, String value) { } } - public Object get(String name) { - final Method method = getterMethods.get(name); + public @Nullable Object get(String name) { + final Method method = requireNonNull( + getterMethods.get(name), + () -> "getter method " + name + " not found" + ); try { return method.invoke(o); } catch (IllegalAccessException | InvocationTargetException e) { diff --git a/core/src/main/java/org/apache/calcite/sql/type/AbstractSqlType.java b/core/src/main/java/org/apache/calcite/sql/type/AbstractSqlType.java index 25013b3eda15..939abc3d3e54 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/AbstractSqlType.java +++ b/core/src/main/java/org/apache/calcite/sql/type/AbstractSqlType.java @@ -22,6 +22,8 @@ import org.apache.calcite.rel.type.RelDataTypeImpl; import org.apache.calcite.rel.type.RelDataTypePrecedenceList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.Serializable; import java.util.List; import java.util.Objects; @@ -49,7 +51,7 @@ public abstract class AbstractSqlType protected AbstractSqlType( SqlTypeName typeName, boolean isNullable, - List fields) { + @Nullable List fields) { super(fields); this.typeName = Objects.requireNonNull(typeName); this.isNullable = isNullable || (typeName == SqlTypeName.NULL); diff --git a/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java index b00ad7ffa76f..7be007e97553 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java @@ -26,6 +26,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -37,7 +39,7 @@ public class AssignableOperandTypeChecker implements SqlOperandTypeChecker { //~ Instance fields -------------------------------------------------------- private final List paramTypes; - private final ImmutableList paramNames; + private final @Nullable ImmutableList paramNames; //~ Constructors ----------------------------------------------------------- @@ -49,7 +51,7 @@ public class AssignableOperandTypeChecker implements SqlOperandTypeChecker { * @param paramNames parameter names, or null */ public AssignableOperandTypeChecker(List paramTypes, - List paramNames) { + @Nullable List paramNames) { this.paramTypes = ImmutableList.copyOf(paramTypes); this.paramNames = paramNames == null ? null : ImmutableList.copyOf(paramNames); diff --git a/core/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java b/core/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java index 90b41cf7ea75..38d1da1fa573 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java +++ b/core/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.charset.Charset; import java.util.Objects; @@ -39,8 +41,8 @@ public class BasicSqlType extends AbstractSqlType { private final int precision; private final int scale; private final RelDataTypeSystem typeSystem; - private final SqlCollation collation; - private final SerializableCharset wrappedCharset; + private final @Nullable SqlCollation collation; + private final @Nullable SerializableCharset wrappedCharset; //~ Constructors ----------------------------------------------------------- @@ -102,8 +104,8 @@ private BasicSqlType( boolean nullable, int precision, int scale, - SqlCollation collation, - SerializableCharset wrappedCharset) { + @Nullable SqlCollation collation, + @Nullable SerializableCharset wrappedCharset) { super(typeName, nullable, null); this.typeSystem = Objects.requireNonNull(typeSystem); this.precision = precision; @@ -162,11 +164,11 @@ BasicSqlType createWithCharsetAndCollation(Charset charset, return scale; } - @Override public Charset getCharset() { + @Override public @Nullable Charset getCharset() { return wrappedCharset == null ? null : wrappedCharset.getCharset(); } - @Override public SqlCollation getCollation() { + @Override public @Nullable SqlCollation getCollation() { return collation; } @@ -275,7 +277,7 @@ BasicSqlType createWithCharsetAndCollation(Charset charset, * the value at the limit * @return Limit value */ - public Object getLimit( + public @Nullable Object getLimit( boolean sign, SqlTypeName.Limit limit, boolean beyond) { diff --git a/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java index 96dd97af50f1..c1cca46583e2 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java @@ -26,13 +26,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import javax.annotation.Nullable; /** * This class allows multiple existing {@link SqlOperandTypeChecker} rules to be diff --git a/core/src/main/java/org/apache/calcite/sql/type/CursorReturnTypeInference.java b/core/src/main/java/org/apache/calcite/sql/type/CursorReturnTypeInference.java index 69f92148089e..c4cc272a8a58 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/CursorReturnTypeInference.java +++ b/core/src/main/java/org/apache/calcite/sql/type/CursorReturnTypeInference.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlOperatorBinding; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Returns the rowtype of a cursor of the operand at a particular 0-based * ordinal position. @@ -38,7 +40,7 @@ public CursorReturnTypeInference(int ordinal) { //~ Methods ---------------------------------------------------------------- - @Override public RelDataType inferReturnType( + @Override public @Nullable RelDataType inferReturnType( SqlOperatorBinding opBinding) { return opBinding.getCursorOperand(ordinal); } diff --git a/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java b/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java index ca549ff268ce..4b211d3e003d 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java +++ b/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.sql.Date; import java.sql.ResultSet; @@ -98,7 +100,7 @@ public static JavaToSqlTypeConversionRules instance() { * @param javaClass the Java class to lookup * @return a corresponding SqlTypeName if found, otherwise null is returned */ - public SqlTypeName lookup(Class javaClass) { + public @Nullable SqlTypeName lookup(Class javaClass) { return rules.get(javaClass); } diff --git a/core/src/main/java/org/apache/calcite/sql/type/MatchReturnTypeInference.java b/core/src/main/java/org/apache/calcite/sql/type/MatchReturnTypeInference.java index a8465506fbea..ad653069c598 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/MatchReturnTypeInference.java +++ b/core/src/main/java/org/apache/calcite/sql/type/MatchReturnTypeInference.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -59,7 +61,7 @@ public MatchReturnTypeInference(int start, Iterable typeNames) { //~ Methods ---------------------------------------------------------------- - @Override public RelDataType inferReturnType( + @Override public @Nullable RelDataType inferReturnType( SqlOperatorBinding opBinding) { for (int i = start; i < opBinding.getOperandCount(); i++) { RelDataType argType = opBinding.getOperandType(i); diff --git a/core/src/main/java/org/apache/calcite/sql/type/NonNullableAccessors.java b/core/src/main/java/org/apache/calcite/sql/type/NonNullableAccessors.java new file mode 100644 index 000000000000..eea2c685c71b --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/type/NonNullableAccessors.java @@ -0,0 +1,50 @@ +/* + * 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.calcite.sql.type; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.sql.SqlCollation; + +import org.apiguardian.api.API; + +import java.nio.charset.Charset; + +import static java.util.Objects.requireNonNull; + +/** + * This class provides non-nullable accessors for common getters. + */ +@API(since = "1.27", status = API.Status.EXPERIMENTAL) +public class NonNullableAccessors { + private NonNullableAccessors() { + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static Charset getCharset(RelDataType type) { + return requireNonNull(type.getCharset(), + () -> "charset is null for " + type); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlCollation getCollation(RelDataType type) { + return requireNonNull(type.getCollation(), + () -> !SqlTypeUtil.inCharFamily(type) + ? "collation is null for " + type + : "RelDataType object should have been assigned " + + "a (default) collation when calling deriveType, type=" + type); + } +} diff --git a/core/src/main/java/org/apache/calcite/sql/type/ObjectSqlType.java b/core/src/main/java/org/apache/calcite/sql/type/ObjectSqlType.java index e5901ca5579a..b354688449d0 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/ObjectSqlType.java +++ b/core/src/main/java/org/apache/calcite/sql/type/ObjectSqlType.java @@ -21,6 +21,8 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.sql.SqlIdentifier; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -29,11 +31,11 @@ public class ObjectSqlType extends AbstractSqlType { //~ Instance fields -------------------------------------------------------- - private final SqlIdentifier sqlIdentifier; + private final @Nullable SqlIdentifier sqlIdentifier; private final RelDataTypeComparability comparability; - private RelDataTypeFamily family; + private @Nullable RelDataTypeFamily family; //~ Constructors ----------------------------------------------------------- @@ -49,7 +51,7 @@ public class ObjectSqlType extends AbstractSqlType { */ public ObjectSqlType( SqlTypeName typeName, - SqlIdentifier sqlIdentifier, + @Nullable SqlIdentifier sqlIdentifier, boolean nullable, List fields, RelDataTypeComparability comparability) { @@ -69,7 +71,7 @@ public void setFamily(RelDataTypeFamily family) { return comparability; } - @Override public SqlIdentifier getSqlIdentifier() { + @Override public @Nullable SqlIdentifier getSqlIdentifier() { return sqlIdentifier; } @@ -84,7 +86,7 @@ public void setFamily(RelDataTypeFamily family) { @Override protected void generateTypeString(StringBuilder sb, boolean withDetail) { // TODO jvs 10-Feb-2005: proper quoting; dump attributes withDetail? sb.append("ObjectSqlType("); - sb.append(sqlIdentifier.toString()); + sb.append(sqlIdentifier); sb.append(")"); } } diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java index e0e1353147b0..c78704aa5ef8 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java @@ -341,7 +341,7 @@ public static SqlOperandTypeChecker variadic( } final SqlLiteral arg = (SqlLiteral) node; - final BigDecimal value = (BigDecimal) arg.getValue(); + final BigDecimal value = arg.getValueAs(BigDecimal.class); if (value.compareTo(BigDecimal.ZERO) < 0 || hasFractionalPart(value)) { if (throwOnFailure) { diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java index 1204674384da..00e8fb2643c0 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java @@ -38,8 +38,12 @@ import java.util.List; import java.util.function.UnaryOperator; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * A collection of return-type inference strategies. */ @@ -693,10 +697,10 @@ public static SqlCall stripOrderBy(SqlCall call) { argType1.getFullTypeString())); } - pickedCollation = + pickedCollation = requireNonNull( SqlCollation.getCoercibilityDyadicOperator( - argType0.getCollation(), argType1.getCollation()); - assert null != pickedCollation; + getCollation(argType0), getCollation(argType1)), + () -> "getCoercibilityDyadicOperator is null for " + argType0 + " and " + argType1); } // Determine whether result is variable-length @@ -723,16 +727,17 @@ public static SqlCall stripOrderBy(SqlCall call) { ret = typeFactory.createSqlType(typeName, typePrecision); if (null != pickedCollation) { RelDataType pickedType; - if (argType0.getCollation().equals(pickedCollation)) { + if (getCollation(argType0).equals(pickedCollation)) { pickedType = argType0; - } else if (argType1.getCollation().equals(pickedCollation)) { + } else if (getCollation(argType1).equals(pickedCollation)) { pickedType = argType1; } else { - throw new AssertionError("should never come here"); + throw new AssertionError("should never come here, " + + "argType0=" + argType0 + ", argType1=" + argType1); } ret = typeFactory.createTypeWithCharsetAndCollation(ret, - pickedType.getCharset(), pickedType.getCollation()); + getCharset(pickedType), getCollation(pickedType)); } if (ret.getSqlTypeName() == SqlTypeName.NULL) { ret = typeFactory.createTypeWithNullability( diff --git a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java index 4087102d9fe6..221028e06690 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java @@ -27,6 +27,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.List; @@ -75,7 +77,7 @@ protected List getOperandList(int operandCount) { protected boolean checkOperandTypesImpl( SqlOperatorBinding operatorBinding, boolean throwOnFailure, - SqlCallBinding callBinding) { + @Nullable SqlCallBinding callBinding) { int nOperandsActual = nOperands; if (nOperandsActual == -1) { nOperandsActual = operatorBinding.getOperandCount(); diff --git a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeExceptLastOperandChecker.java b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeExceptLastOperandChecker.java index 7c34c1e4a36c..6c4403ce2721 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeExceptLastOperandChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeExceptLastOperandChecker.java @@ -24,11 +24,15 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.List; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Parameter type-checking strategy where all operand types except last one must be the same. */ @@ -50,7 +54,7 @@ public SameOperandTypeExceptLastOperandChecker( @Override protected boolean checkOperandTypesImpl( SqlOperatorBinding operatorBinding, boolean throwOnFailure, - SqlCallBinding callBinding) { + @Nullable SqlCallBinding callBinding) { int nOperandsActual = nOperands; if (nOperandsActual == -1) { nOperandsActual = operatorBinding.getOperandCount(); @@ -61,7 +65,7 @@ public SameOperandTypeExceptLastOperandChecker( getOperandList(operatorBinding.getOperandCount()); for (int i : operandList) { if (operatorBinding.isOperandNull(i, false)) { - if (callBinding.isTypeCoercionEnabled()) { + if (requireNonNull(callBinding, "callBinding").isTypeCoercionEnabled()) { types[i] = operatorBinding.getTypeFactory() .createSqlType(SqlTypeName.NULL); } else if (throwOnFailure) { diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlOperandMetadata.java b/core/src/main/java/org/apache/calcite/sql/type/SqlOperandMetadata.java index 41bbe45ac5f0..bd7998c7e78f 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlOperandMetadata.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlOperandMetadata.java @@ -20,7 +20,6 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import java.util.List; -import javax.annotation.Nonnull; /** * Extension to {@link SqlOperandTypeChecker} that also provides @@ -31,7 +30,6 @@ * * @see OperandTypes */ -@Nonnull public interface SqlOperandMetadata extends SqlOperandTypeChecker { //~ Methods ---------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInference.java b/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInference.java index 16a80ea37091..d832f96c6184 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInference.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInference.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlOperatorBinding; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Strategy interface to infer the type of an operator call from the type of the * operands. @@ -42,7 +44,7 @@ public interface SqlReturnTypeInference { * @param opBinding description of operator binding * @return inferred type; may be null */ - RelDataType inferReturnType( + @Nullable RelDataType inferReturnType( SqlOperatorBinding opBinding); /** Returns a return-type inference that applies this rule then a diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInferenceChain.java b/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInferenceChain.java index 213eba9f9ffa..825bb0133f19 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInferenceChain.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlReturnTypeInferenceChain.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Strategy to infer the type of an operator call from the type of the operands * by using a series of {@link SqlReturnTypeInference} rules in a given order. @@ -48,7 +50,7 @@ public class SqlReturnTypeInferenceChain implements SqlReturnTypeInference { //~ Methods ---------------------------------------------------------------- - @Override public RelDataType inferReturnType(SqlOperatorBinding opBinding) { + @Override public @Nullable RelDataType inferReturnType(SqlOperatorBinding opBinding) { for (SqlReturnTypeInference rule : rules) { RelDataType ret = rule.inferReturnType(opBinding); if (ret != null) { diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java index fc785d906311..4d01356311b3 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeCoercionRule.java @@ -19,8 +19,11 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -72,7 +75,7 @@ public class SqlTypeCoercionRule implements SqlTypeMappingRule { private static final SqlTypeCoercionRule INSTANCE; - public static final ThreadLocal THREAD_PROVIDERS = + public static final ThreadLocal<@Nullable SqlTypeCoercionRule> THREAD_PROVIDERS = ThreadLocal.withInitial(() -> SqlTypeCoercionRule.INSTANCE); //~ Instance fields -------------------------------------------------------- @@ -270,7 +273,7 @@ private SqlTypeCoercionRule(Map> map) { /** Returns an instance. */ public static SqlTypeCoercionRule instance() { - return THREAD_PROVIDERS.get(); + return Objects.requireNonNull(THREAD_PROVIDERS.get(), "threadProviders"); } /** Returns an instance with specified type mappings. */ diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeExplicitPrecedenceList.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeExplicitPrecedenceList.java index 45309971a3a3..92593288a9dc 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeExplicitPrecedenceList.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeExplicitPrecedenceList.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; import java.util.List; import java.util.Map; @@ -156,7 +158,7 @@ private static int getListPosition(SqlTypeName type, List list) { return i; } - static RelDataTypePrecedenceList getListForType(RelDataType type) { + static @Nullable RelDataTypePrecedenceList getListForType(RelDataType type) { SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return null; diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java index fecf939c9c07..a6362f332171 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java @@ -25,9 +25,13 @@ import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.charset.Charset; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * SqlTypeFactoryImpl provides a default implementation of * {@link RelDataTypeFactory} which supports SQL types. @@ -127,8 +131,8 @@ public SqlTypeFactoryImpl(RelDataTypeSystem typeSystem) { Charset charset, SqlCollation collation) { assert SqlTypeUtil.inCharFamily(type) : type; - assert charset != null; - assert collation != null; + requireNonNull(charset, "charset"); + requireNonNull(collation, "collation"); RelDataType newType; if (type instanceof BasicSqlType) { BasicSqlType sqlType = (BasicSqlType) type; @@ -147,7 +151,7 @@ public SqlTypeFactoryImpl(RelDataTypeSystem typeSystem) { return canonize(newType); } - @Override public RelDataType leastRestrictive(List types) { + @Override public @Nullable RelDataType leastRestrictive(List types) { assert types != null; assert types.size() >= 1; @@ -163,7 +167,7 @@ public SqlTypeFactoryImpl(RelDataTypeSystem typeSystem) { return super.leastRestrictive(types); } - private RelDataType leastRestrictiveByCast(List types) { + private @Nullable RelDataType leastRestrictiveByCast(List types) { RelDataType resultType = types.get(0); boolean anyNullable = resultType.isNullable(); for (int i = 1; i < types.size(); i++) { @@ -228,7 +232,7 @@ private void assertBasic(SqlTypeName typeName) { : "use createSqlIntervalType() instead"; } - private RelDataType leastRestrictiveSqlType(List types) { + private @Nullable RelDataType leastRestrictiveSqlType(List types) { RelDataType resultType = null; int nullCount = 0; int nullableCount = 0; @@ -365,7 +369,7 @@ private RelDataType leastRestrictiveSqlType(List types) { createTypeWithCharsetAndCollation( resultType, charset, - collation0 != null ? collation0 : collation); + collation0 != null ? collation0 : requireNonNull(collation, "collation")); } } else if (SqlTypeUtil.isExactNumeric(type)) { if (SqlTypeUtil.isExactNumeric(resultType)) { @@ -511,7 +515,8 @@ private RelDataType copyMultisetType(RelDataType type, boolean nullable) { private RelDataType copyIntervalType(RelDataType type, boolean nullable) { return new IntervalSqlType(typeSystem, - type.getIntervalQualifier(), + requireNonNull(type.getIntervalQualifier(), + () -> "type.getIntervalQualifier() for " + type), nullable); } diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java index 1666e83ca3ff..6f07924ca3b2 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java @@ -26,6 +26,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.Types; import java.util.Collection; import java.util.Map; @@ -122,7 +124,7 @@ public enum SqlTypeFamily implements RelDataTypeFamily { * @param jdbcType the JDBC type of interest * @return containing family */ - public static SqlTypeFamily getFamilyForJdbcType(int jdbcType) { + public static @Nullable SqlTypeFamily getFamilyForJdbcType(int jdbcType) { return JDBC_TYPE_TO_FAMILY.get(jdbcType); } @@ -183,7 +185,7 @@ public Collection getTypeNames() { } /** Return the default {@link RelDataType} that belongs to this family. */ - public RelDataType getDefaultConcreteType(RelDataTypeFactory factory) { + public @Nullable RelDataType getDefaultConcreteType(RelDataTypeFactory factory) { switch (this) { case CHARACTER: return factory.createSqlType(SqlTypeName.VARCHAR); diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeMappingRules.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeMappingRules.java index d13350debe14..93c786bbde98 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeMappingRules.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeMappingRules.java @@ -30,6 +30,8 @@ import java.util.Set; import java.util.concurrent.ExecutionException; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * This class defines some utilities to build type mapping matrix * which would then use to construct the {@link SqlTypeMappingRule} rules. @@ -91,7 +93,7 @@ void addAll(Map> typeMapping) { * returns as a {@link ImmutableSet.Builder}. */ ImmutableSet.Builder copyValues(SqlTypeName typeName) { return ImmutableSet.builder() - .addAll(map.get(typeName)); + .addAll(castNonNull(map.get(typeName))); } } } diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java index bd1a745edb2a..8263fb5cf9ec 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java @@ -29,6 +29,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.sql.Types; import java.util.Arrays; @@ -263,10 +265,10 @@ public enum SqlTypeName { */ private final boolean special; private final int jdbcOrdinal; - private final SqlTypeFamily family; + private final @Nullable SqlTypeFamily family; SqlTypeName(int signatures, boolean special, int jdbcType, - SqlTypeFamily family) { + @Nullable SqlTypeFamily family) { this.signatures = signatures; this.special = special; this.jdbcOrdinal = jdbcType; @@ -278,7 +280,7 @@ public enum SqlTypeName { * * @return Type name, or null if not found */ - public static SqlTypeName get(String name) { + public static @Nullable SqlTypeName get(String name) { if (false) { // The following code works OK, but the spurious exceptions are // annoying. @@ -384,9 +386,9 @@ public int getDefaultScale() { /** * Gets the SqlTypeFamily containing this SqlTypeName. * - * @return containing family, or null for none + * @return containing family, or null for none (SYMBOL, DISTINCT, STRUCTURED, ROW, OTHER) */ - public SqlTypeFamily getFamily() { + public @Nullable SqlTypeFamily getFamily() { return family; } @@ -396,7 +398,7 @@ public SqlTypeFamily getFamily() { * @param jdbcType the JDBC type of interest * @return corresponding SqlTypeName, or null if the type is not known */ - public static SqlTypeName getNameForJdbcType(int jdbcType) { + public static @Nullable SqlTypeName getNameForJdbcType(int jdbcType) { return JDBC_TYPE_TO_NAME.get(jdbcType); } @@ -470,7 +472,7 @@ public static SqlTypeName getNameForJdbcType(int jdbcType) { * @param scale Scale, or -1 if not applicable * @return Limit value */ - public Object getLimit( + public @Nullable Object getLimit( boolean sign, Limit limit, boolean beyond, @@ -527,7 +529,7 @@ public Object getLimit( case OVERFLOW: final BigDecimal other = (BigDecimal) BIGINT.getLimit(sign, limit, beyond, -1, -1); - if (decimal.compareTo(other) == (sign ? 1 : -1)) { + if (other != null && decimal.compareTo(other) == (sign ? 1 : -1)) { decimal = other; } break; @@ -877,7 +879,7 @@ public enum Limit { ZERO, UNDERFLOW, OVERFLOW } - private BigDecimal getNumericLimit( + private @Nullable BigDecimal getNumericLimit( int radix, int exponent, boolean sign, diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransformCascade.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransformCascade.java index 2916cacb4a2b..76ea9ab246c4 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransformCascade.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransformCascade.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -51,7 +53,7 @@ public SqlTypeTransformCascade( //~ Methods ---------------------------------------------------------------- - @Override public RelDataType inferReturnType( + @Override public @Nullable RelDataType inferReturnType( SqlOperatorBinding opBinding) { RelDataType ret = rule.inferReturnType(opBinding); if (ret == null) { diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java index 33640b5d7555..c70dbbcb27d5 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java @@ -23,7 +23,11 @@ import org.apache.calcite.util.Util; import java.util.List; -import java.util.Objects; + +import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation; + +import static java.util.Objects.requireNonNull; /** * SqlTypeTransforms defines a number of reusable instances of @@ -46,7 +50,7 @@ public abstract class SqlTypeTransforms { (opBinding, typeToTransform) -> SqlTypeUtil.makeNullableIfOperandsAre(opBinding.getTypeFactory(), opBinding.collectOperandTypes(), - Objects.requireNonNull(typeToTransform)); + requireNonNull(typeToTransform)); /** * Parameter type-inference transform strategy where a derived type is @@ -66,7 +70,7 @@ public abstract class SqlTypeTransforms { public static final SqlTypeTransform TO_NOT_NULLABLE = (opBinding, typeToTransform) -> opBinding.getTypeFactory().createTypeWithNullability( - Objects.requireNonNull(typeToTransform), false); + requireNonNull(typeToTransform), false); /** * Parameter type-inference transform strategy where a derived type is @@ -75,7 +79,7 @@ public abstract class SqlTypeTransforms { public static final SqlTypeTransform FORCE_NULLABLE = (opBinding, typeToTransform) -> opBinding.getTypeFactory().createTypeWithNullability( - Objects.requireNonNull(typeToTransform), true); + requireNonNull(typeToTransform), true); /** * Type-inference strategy whereby the result is NOT NULL if any of @@ -122,8 +126,8 @@ public abstract class SqlTypeTransforms { opBinding.getTypeFactory() .createTypeWithCharsetAndCollation( ret, - typeToTransform.getCharset(), - typeToTransform.getCollation()); + getCharset(typeToTransform), + getCollation(typeToTransform)); } return opBinding.getTypeFactory().createTypeWithNullability( ret, @@ -154,7 +158,9 @@ private SqlTypeName toVar(RelDataType type) { * @see MultisetSqlType#getComponentType */ public static final SqlTypeTransform TO_MULTISET_ELEMENT_TYPE = - (opBinding, typeToTransform) -> typeToTransform.getComponentType(); + (opBinding, typeToTransform) -> requireNonNull( + typeToTransform.getComponentType(), + () -> "componentType for " + typeToTransform + " in opBinding " + opBinding); /** * Parameter type-inference transform strategy that wraps a given type diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java index f1cd62b42931..170e548ee527 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java @@ -47,6 +47,8 @@ import com.google.common.collect.Sets; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; import java.math.BigDecimal; import java.nio.charset.Charset; @@ -58,6 +60,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation; import static org.apache.calcite.util.Static.RESOURCE; import static java.util.Objects.requireNonNull; @@ -94,18 +98,12 @@ public static boolean isCharTypeComparable(List argTypes) { return false; } - if (t0.getCharset() == null) { - throw new AssertionError("RelDataType object should have been assigned " - + "a (default) charset when calling deriveType"); - } else if (!t0.getCharset().equals(t1.getCharset())) { + if (!getCharset(t0).equals(getCharset(t1))) { return false; } - if (t0.getCollation() == null) { - throw new AssertionError("RelDataType object should have been assigned " - + "a (default) collation when calling deriveType"); - } else if (!t0.getCollation().getCharset().equals( - t1.getCollation().getCharset())) { + if (!getCollation(t0).getCharset().equals( + getCollation(t1).getCharset())) { return false; } } @@ -205,7 +203,7 @@ public static List deriveType(SqlCallBinding binding, public static RelDataType promoteToRowType( RelDataTypeFactory typeFactory, RelDataType type, - String fieldName) { + @Nullable String fieldName) { if (!type.isStruct()) { if (fieldName == null) { fieldName = "ROW_VALUE"; @@ -343,11 +341,14 @@ public static boolean isTimestamp(RelDataType type) { } /** Returns whether a type is some kind of INTERVAL. */ + @EnsuresNonNullIf(expression = "#1.getIntervalQualifier()", result = true) public static boolean isInterval(RelDataType type) { return SqlTypeFamily.DATETIME_INTERVAL.contains(type); } /** Returns whether a type is in SqlTypeFamily.Character. */ + @EnsuresNonNullIf(expression = "#1.getCharset()", result = true) + @EnsuresNonNullIf(expression = "#1.getCollation()", result = true) public static boolean inCharFamily(RelDataType type) { return type.getFamily() == SqlTypeFamily.CHARACTER; } @@ -583,7 +584,7 @@ public static int getMaxByteSize(RelDataType type) { case VARCHAR: return (int) Math.ceil( ((double) type.getPrecision()) - * type.getCharset().newEncoder().maxBytesPerChar()); + * getCharset(type).newEncoder().maxBytesPerChar()); case BINARY: case VARBINARY: @@ -671,7 +672,7 @@ public static boolean isJavaPrimitive(RelDataType type) { /** Returns the class name of the wrapper for the primitive data type. */ @Deprecated // to be removed before 2.0 - public static String getPrimitiveWrapperJavaClassName(RelDataType type) { + public static @Nullable String getPrimitiveWrapperJavaClassName(@Nullable RelDataType type) { if (type == null) { return null; } @@ -691,7 +692,7 @@ public static String getPrimitiveWrapperJavaClassName(RelDataType type) { /** Returns the class name of a numeric data type. */ @Deprecated // to be removed before 2.0 - public static String getNumericJavaClassName(RelDataType type) { + public static @Nullable String getNumericJavaClassName(@Nullable RelDataType type) { if (type == null) { return null; } @@ -915,7 +916,7 @@ public static boolean canCastFrom( public static RelDataType flattenRecordType( RelDataTypeFactory typeFactory, RelDataType recordType, - int[] flatteningMap) { + int @Nullable [] flatteningMap) { if (!recordType.isStruct()) { return recordType; } @@ -962,7 +963,7 @@ private static boolean flattenFields( RelDataTypeFactory typeFactory, RelDataType type, List list, - int[] flatteningMap) { + int @Nullable [] flatteningMap) { boolean nested = false; for (RelDataTypeField field : type.getFieldList()) { if (flatteningMap != null) { @@ -1018,7 +1019,7 @@ private static boolean flattenFields( * @return corresponding parse representation */ public static SqlDataTypeSpec convertTypeToSpec(RelDataType type, - String charSetName, int maxPrecision) { + @Nullable String charSetName, int maxPrecision) { SqlTypeName typeName = type.getSqlTypeName(); // TODO jvs 28-Dec-2004: support row types, user-defined types, @@ -1264,7 +1265,7 @@ public static boolean equalAsStructSansNullability( RelDataTypeFactory factory, RelDataType type1, RelDataType type2, - SqlNameMatcher nameMatcher) { + @Nullable SqlNameMatcher nameMatcher) { Preconditions.checkArgument(type1.isStruct(), "Input type must be struct type"); Preconditions.checkArgument(type2.isStruct(), "Input type must be struct type"); @@ -1427,7 +1428,7 @@ && canConvertStringInCompare(family1)) { /** Returns the least restrictive type T, such that a value of type T can be * compared with values of type {@code type1} and {@code type2} using * {@code =}. */ - public static RelDataType leastRestrictiveForComparison( + public static @Nullable RelDataType leastRestrictiveForComparison( RelDataTypeFactory typeFactory, RelDataType type1, RelDataType type2) { final RelDataType type = typeFactory.leastRestrictive(ImmutableList.of(type1, type2)); @@ -1731,7 +1732,7 @@ public static RelDataType extractLastNFields(RelDataTypeFactory typeFactory, * @param toType Type of the literal * @return whether the value is valid for the type */ - public static boolean isValidDecimalValue(BigDecimal value, RelDataType toType) { + public static boolean isValidDecimalValue(@Nullable BigDecimal value, RelDataType toType) { if (value == null) { return true; } diff --git a/core/src/main/java/org/apache/calcite/sql/type/TableFunctionReturnTypeInference.java b/core/src/main/java/org/apache/calcite/sql/type/TableFunctionReturnTypeInference.java index ffe396e2fe42..cff8ad250f00 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/TableFunctionReturnTypeInference.java +++ b/core/src/main/java/org/apache/calcite/sql/type/TableFunctionReturnTypeInference.java @@ -23,6 +23,9 @@ import org.apache.calcite.sql.SqlCallBinding; import org.apache.calcite.sql.SqlOperatorBinding; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -40,7 +43,7 @@ public class TableFunctionReturnTypeInference private final List paramNames; - private Set columnMappings; // not re-entrant! + private @MonotonicNonNull Set columnMappings; // not re-entrant! private final boolean isPassthrough; @@ -57,7 +60,7 @@ public TableFunctionReturnTypeInference( //~ Methods ---------------------------------------------------------------- - public Set getColumnMappings() { + public @Nullable Set getColumnMappings() { return columnMappings; } diff --git a/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java index 4f8b4509bb19..2cc599d81748 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java +++ b/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java @@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -61,7 +63,7 @@ public void add(SqlOperatorTable table) { } @Override public void lookupOperatorOverloads(SqlIdentifier opName, - SqlFunctionCategory category, SqlSyntax syntax, + @Nullable SqlFunctionCategory category, SqlSyntax syntax, List operatorList, SqlNameMatcher nameMatcher) { for (SqlOperatorTable table : tableList) { table.lookupOperatorOverloads(opName, category, syntax, operatorList, diff --git a/core/src/main/java/org/apache/calcite/sql/util/IdPair.java b/core/src/main/java/org/apache/calcite/sql/util/IdPair.java index 9cd211dfb495..21e660742b76 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/IdPair.java +++ b/core/src/main/java/org/apache/calcite/sql/util/IdPair.java @@ -17,6 +17,8 @@ package org.apache.calcite.sql.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Map; import java.util.Objects; @@ -48,7 +50,7 @@ protected IdPair(L left, R right) { return left + "=" + right; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof IdPair && left == ((IdPair) obj).left diff --git a/core/src/main/java/org/apache/calcite/sql/util/ListSqlOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/util/ListSqlOperatorTable.java index 3fa96f875c7e..ad008633a99d 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/ListSqlOperatorTable.java +++ b/core/src/main/java/org/apache/calcite/sql/util/ListSqlOperatorTable.java @@ -24,6 +24,8 @@ import org.apache.calcite.sql.SqlSyntax; import org.apache.calcite.sql.validate.SqlNameMatcher; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -53,7 +55,7 @@ public void add(SqlOperator op) { } @Override public void lookupOperatorOverloads(SqlIdentifier opName, - SqlFunctionCategory category, + @Nullable SqlFunctionCategory category, SqlSyntax syntax, List operatorList, SqlNameMatcher nameMatcher) { diff --git a/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java index ccece2b1529b..aaf33c65825b 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java +++ b/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java @@ -31,6 +31,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Field; import java.util.Collection; import java.util.List; @@ -75,7 +77,9 @@ public final void init() { } else if ( SqlOperator.class.isAssignableFrom(field.getType())) { SqlOperator op = (SqlOperator) field.get(this); - register(op); + if (op != null) { + register(op); + } } } catch (IllegalArgumentException | IllegalAccessException e) { throw Util.throwAsRuntime(Util.causeOrSelf(e)); @@ -85,7 +89,7 @@ public final void init() { // implement SqlOperatorTable @Override public void lookupOperatorOverloads(SqlIdentifier opName, - SqlFunctionCategory category, SqlSyntax syntax, + @Nullable SqlFunctionCategory category, SqlSyntax syntax, List operatorList, SqlNameMatcher nameMatcher) { // NOTE jvs 3-Mar-2005: ignore category until someone cares diff --git a/core/src/main/java/org/apache/calcite/sql/util/SqlBasicVisitor.java b/core/src/main/java/org/apache/calcite/sql/util/SqlBasicVisitor.java index a0f04f8b5d5f..1a4fa559a415 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/SqlBasicVisitor.java +++ b/core/src/main/java/org/apache/calcite/sql/util/SqlBasicVisitor.java @@ -25,6 +25,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlNodeList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Basic implementation of {@link SqlVisitor} which does nothing at each node. * @@ -34,7 +36,7 @@ * * @param Return type */ -public class SqlBasicVisitor implements SqlVisitor { +public class SqlBasicVisitor<@Nullable R> implements SqlVisitor { //~ Methods ---------------------------------------------------------------- @Override public R visit(SqlLiteral literal) { @@ -89,7 +91,7 @@ R visitChild( SqlVisitor visitor, SqlNode expr, int i, - SqlNode operand); + @Nullable SqlNode operand); } //~ Inner Classes ---------------------------------------------------------- @@ -100,12 +102,12 @@ R visitChild( * * @param result type */ - public static class ArgHandlerImpl implements ArgHandler { - private static final ArgHandler INSTANCE = new ArgHandlerImpl(); + public static class ArgHandlerImpl<@Nullable R> implements ArgHandler { + private static final ArgHandler INSTANCE = new ArgHandlerImpl<>(); @SuppressWarnings("unchecked") public static ArgHandler instance() { - return INSTANCE; + return (ArgHandler) INSTANCE; } @Override public R result() { @@ -116,7 +118,7 @@ public static ArgHandler instance() { SqlVisitor visitor, SqlNode expr, int i, - SqlNode operand) { + @Nullable SqlNode operand) { if (operand == null) { return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/util/SqlShuttle.java b/core/src/main/java/org/apache/calcite/sql/util/SqlShuttle.java index a38d1b635e7e..7be504fc4998 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/SqlShuttle.java +++ b/core/src/main/java/org/apache/calcite/sql/util/SqlShuttle.java @@ -25,6 +25,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlNodeList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -36,42 +38,42 @@ * {@link SqlVisitor} interface and have {@link SqlNode} as the return type. The * derived class can override whichever methods it chooses. */ -public class SqlShuttle extends SqlBasicVisitor { +public class SqlShuttle extends SqlBasicVisitor<@Nullable SqlNode> { //~ Methods ---------------------------------------------------------------- - @Override public SqlNode visit(SqlLiteral literal) { + @Override public @Nullable SqlNode visit(SqlLiteral literal) { return literal; } - @Override public SqlNode visit(SqlIdentifier id) { + @Override public @Nullable SqlNode visit(SqlIdentifier id) { return id; } - @Override public SqlNode visit(SqlDataTypeSpec type) { + @Override public @Nullable SqlNode visit(SqlDataTypeSpec type) { return type; } - @Override public SqlNode visit(SqlDynamicParam param) { + @Override public @Nullable SqlNode visit(SqlDynamicParam param) { return param; } - @Override public SqlNode visit(SqlIntervalQualifier intervalQualifier) { + @Override public @Nullable SqlNode visit(SqlIntervalQualifier intervalQualifier) { return intervalQualifier; } - @Override public SqlNode visit(final SqlCall call) { + @Override public @Nullable SqlNode visit(final SqlCall call) { // Handler creates a new copy of 'call' only if one or more operands // change. - ArgHandler argHandler = new CallCopyingArgHandler(call, false); + CallCopyingArgHandler argHandler = new CallCopyingArgHandler(call, false); call.getOperator().acceptCall(this, call, false, argHandler); return argHandler.result(); } - @Override public SqlNode visit(SqlNodeList nodeList) { + @Override public @Nullable SqlNode visit(SqlNodeList nodeList) { boolean update = false; List exprs = nodeList.getList(); int exprCount = exprs.size(); - List newList = new ArrayList<>(exprCount); + List<@Nullable SqlNode> newList = new ArrayList<>(exprCount); for (SqlNode operand : exprs) { SqlNode clonedOperand; if (operand == null) { @@ -98,16 +100,16 @@ public class SqlShuttle extends SqlBasicVisitor { * {@link org.apache.calcite.sql.util.SqlBasicVisitor.ArgHandler} * that deep-copies {@link SqlCall}s and their operands. */ - protected class CallCopyingArgHandler implements ArgHandler { + protected class CallCopyingArgHandler implements ArgHandler<@Nullable SqlNode> { boolean update; - SqlNode[] clonedOperands; + @Nullable SqlNode[] clonedOperands; private final SqlCall call; private final boolean alwaysCopy; public CallCopyingArgHandler(SqlCall call, boolean alwaysCopy) { this.call = call; this.update = false; - final List operands = call.getOperandList(); + final List<@Nullable SqlNode> operands = (List<@Nullable SqlNode>) call.getOperandList(); this.clonedOperands = operands.toArray(new SqlNode[0]); this.alwaysCopy = alwaysCopy; } @@ -123,11 +125,11 @@ public CallCopyingArgHandler(SqlCall call, boolean alwaysCopy) { } } - @Override public SqlNode visitChild( - SqlVisitor visitor, + @Override public @Nullable SqlNode visitChild( + SqlVisitor<@Nullable SqlNode> visitor, SqlNode expr, int i, - SqlNode operand) { + @Nullable SqlNode operand) { if (operand == null) { return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/util/SqlString.java b/core/src/main/java/org/apache/calcite/sql/util/SqlString.java index 3e968575f0f6..c1678f5cc565 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/SqlString.java +++ b/core/src/main/java/org/apache/calcite/sql/util/SqlString.java @@ -20,6 +20,9 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + /** * String that represents a kocher SQL statement, expression, or fragment. * @@ -32,7 +35,7 @@ public class SqlString { private final String sql; private SqlDialect dialect; - private ImmutableList dynamicParameters; + private @Nullable ImmutableList dynamicParameters; /** * Creates a SqlString. @@ -48,7 +51,8 @@ public SqlString(SqlDialect dialect, String sql) { * @param sql text * @param dynamicParameters indices */ - public SqlString(SqlDialect dialect, String sql, ImmutableList dynamicParameters) { + public SqlString(SqlDialect dialect, String sql, + @Nullable ImmutableList dynamicParameters) { this.dialect = dialect; this.sql = sql; this.dynamicParameters = dynamicParameters; @@ -60,7 +64,7 @@ public SqlString(SqlDialect dialect, String sql, ImmutableList dynamicP return sql.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof SqlString && sql.equals(((SqlString) obj).sql); @@ -92,7 +96,8 @@ public String getSql() { * * @return indices of dynamic parameters */ - public ImmutableList getDynamicParameters() { + @Pure + public @Nullable ImmutableList getDynamicParameters() { return dynamicParameters; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java index 5b8d5c989c5b..bac1db61c3cc 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java @@ -25,7 +25,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import java.util.Objects; + +import static org.checkerframework.checker.nullness.NullnessUtil.castNonNull; /** * Abstract implementation of {@link SqlValidatorNamespace}. @@ -46,12 +51,12 @@ abstract class AbstractNamespace implements SqlValidatorNamespace { * Type of the output row, which comprises the name and type of each output * column. Set on validate. */ - protected RelDataType rowType; + protected @Nullable RelDataType rowType; /** As {@link #rowType}, but not necessarily a struct. */ - protected RelDataType type; + protected @Nullable RelDataType type; - protected final SqlNode enclosingNode; + protected final @Nullable SqlNode enclosingNode; //~ Constructors ----------------------------------------------------------- @@ -63,7 +68,7 @@ abstract class AbstractNamespace implements SqlValidatorNamespace { */ AbstractNamespace( SqlValidatorImpl validator, - SqlNode enclosingNode) { + @Nullable SqlNode enclosingNode) { this.validator = validator; this.enclosingNode = enclosingNode; } @@ -113,7 +118,7 @@ abstract class AbstractNamespace implements SqlValidatorNamespace { @Override public RelDataType getRowType() { if (rowType == null) { validator.validateNamespace(this, validator.unknownType); - Preconditions.checkArgument(rowType != null, "validate must set rowType"); + Objects.requireNonNull(rowType, "validate must set rowType"); } return rowType; } @@ -124,7 +129,7 @@ abstract class AbstractNamespace implements SqlValidatorNamespace { @Override public RelDataType getType() { Util.discard(getRowType()); - return type; + return Objects.requireNonNull(type, "type"); } @Override public void setType(RelDataType type) { @@ -132,15 +137,15 @@ abstract class AbstractNamespace implements SqlValidatorNamespace { this.rowType = convertToStruct(type); } - @Override public SqlNode getEnclosingNode() { + @Override public @Nullable SqlNode getEnclosingNode() { return enclosingNode; } - @Override public SqlValidatorTable getTable() { + @Override public @Nullable SqlValidatorTable getTable() { return null; } - @Override public SqlValidatorNamespace lookupChild(String name) { + @Override public @Nullable SqlValidatorNamespace lookupChild(String name) { return validator.lookupFieldNamespace( getRowType(), name); @@ -175,7 +180,7 @@ public String translate(String name) { return true; } - @Override public T unwrap(Class clazz) { + @Override public T unwrap(Class clazz) { return clazz.cast(this); } @@ -210,12 +215,17 @@ protected RelDataType convertToStruct(RelDataType type) { } /** Converts a type to a struct if it is not already. */ - protected RelDataType toStruct(RelDataType type, SqlNode unnest) { + protected RelDataType toStruct(RelDataType type, @Nullable SqlNode unnest) { if (type.isStruct()) { return type; } return validator.getTypeFactory().builder() - .add(validator.deriveAlias(unnest, 0), type) + .add( + castNonNull( + validator.deriveAlias( + Objects.requireNonNull(unnest, "unnest"), + 0)), + type) .build(); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java index b9d280e5f6f2..71bfee7954ff 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java @@ -31,8 +31,11 @@ import java.util.Deque; import java.util.List; +import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getSelectList; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Visitor which throws an exception if any component of the expression is not a * group expression. @@ -104,7 +107,8 @@ boolean isGroupExpr(SqlNode expr) { // it fully-qualified. // TODO: It would be better if we always compared fully-qualified // to fully-qualified. - final SqlQualified fqId = scopes.peek().fullyQualify(id); + final SqlQualified fqId = requireNonNull(scopes.peek(), "scopes.peek()") + .fullyQualify(id); if (isGroupExpr(fqId.identifier)) { return null; } @@ -122,7 +126,7 @@ boolean isGroupExpr(SqlNode expr) { if (distinct) { if (scope instanceof AggregatingSelectScope) { SqlNodeList selectList = - ((SqlSelect) scope.getNode()).getSelectList(); + getSelectList((SqlSelect) scope.getNode()); // Check if this aggregation function is just an element in the select for (SqlNode sqlNode : selectList) { @@ -170,7 +174,9 @@ boolean isGroupExpr(SqlNode expr) { } else if (over instanceof SqlIdentifier) { // Check the corresponding SqlWindow in WINDOW clause final SqlWindow window = - scope.lookupWindow(((SqlIdentifier) over).getSimple()); + requireNonNull(scope, () -> "scope for " + call) + .lookupWindow(((SqlIdentifier) over).getSimple()); + requireNonNull(window, () -> "window for " + call); window.getPartitionList().accept(this); window.getOrderList().accept(this); } @@ -206,7 +212,8 @@ boolean isGroupExpr(SqlNode expr) { } // Switch to new scope. - SqlValidatorScope newScope = scope.getOperandScope(call); + SqlValidatorScope newScope = requireNonNull(scope, () -> "scope for " + call) + .getOperandScope(call); scopes.push(newScope); // Visit the operands (only expressions). diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggFinder.java b/core/src/main/java/org/apache/calcite/sql/validate/AggFinder.java index 3a4ca99e0149..132c945ab86c 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AggFinder.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AggFinder.java @@ -21,10 +21,11 @@ import org.apache.calcite.sql.SqlOperatorTable; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import javax.annotation.Nonnull; /** Visitor that looks for an aggregate function inside a tree of * {@link SqlNode} objects and throws {@link Util.FoundOne} when it finds @@ -42,7 +43,7 @@ class AggFinder extends AggVisitor { * @param nameMatcher Whether to match the agg function case-sensitively */ AggFinder(SqlOperatorTable opTab, boolean over, boolean aggregate, - boolean group, AggFinder delegate, SqlNameMatcher nameMatcher) { + boolean group, @Nullable AggFinder delegate, SqlNameMatcher nameMatcher) { super(opTab, over, aggregate, group, delegate, nameMatcher); } @@ -54,7 +55,7 @@ class AggFinder extends AggVisitor { * @param node Parse tree to search * @return First aggregate function in parse tree, or null if not found */ - public SqlCall findAgg(SqlNode node) { + public @Nullable SqlCall findAgg(SqlNode node) { try { node.accept(this); return null; @@ -64,7 +65,7 @@ public SqlCall findAgg(SqlNode node) { } } - public SqlCall findAgg(List nodes) { + public @Nullable SqlCall findAgg(List nodes) { try { for (SqlNode node : nodes) { node.accept(this); @@ -96,7 +97,7 @@ static class AggIterable extends AggVisitor implements Iterable { private final List calls = new ArrayList<>(); AggIterable(SqlOperatorTable opTab, boolean over, boolean aggregate, - boolean group, AggFinder delegate, SqlNameMatcher nameMatcher) { + boolean group, @Nullable AggFinder delegate, SqlNameMatcher nameMatcher) { super(opTab, over, aggregate, group, delegate, nameMatcher); } @@ -105,7 +106,7 @@ static class AggIterable extends AggVisitor implements Iterable { return null; } - @Override @Nonnull public Iterator iterator() { + @Override public Iterator iterator() { return calls.iterator(); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggVisitor.java b/core/src/main/java/org/apache/calcite/sql/validate/AggVisitor.java index ab8c5d915978..9995ac0b5362 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AggVisitor.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AggVisitor.java @@ -26,10 +26,11 @@ import org.apache.calcite.sql.fun.SqlAbstractGroupFunction; import org.apache.calcite.sql.util.SqlBasicVisitor; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; -import javax.annotation.Nullable; /** Visitor that can find aggregate and windowed aggregate functions. * @@ -38,7 +39,7 @@ abstract class AggVisitor extends SqlBasicVisitor { protected final SqlOperatorTable opTab; /** Whether to find windowed aggregates. */ protected final boolean over; - protected final AggFinder delegate; + protected final @Nullable AggFinder delegate; /** Whether to find regular (non-windowed) aggregates. */ protected final boolean aggregate; /** Whether to find group functions (e.g. {@code TUMBLE}) diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java b/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java index 2e4e3597ad43..53cc201f287a 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AggregatingSelectScope.java @@ -32,9 +32,12 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMultiset; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Supplier; import static org.apache.calcite.sql.SqlUtil.stripAs; @@ -54,8 +57,9 @@ public class AggregatingSelectScope private final boolean distinct; /** Use while resolving. */ - private SqlValidatorUtil.GroupAnalyzer groupAnalyzer; + private SqlValidatorUtil.@Nullable GroupAnalyzer groupAnalyzer; + @SuppressWarnings("methodref.receiver.bound.invalid") public final Supplier resolved = Suppliers.memoize(this::resolve)::get; @@ -84,7 +88,8 @@ public class AggregatingSelectScope private Resolved resolve() { assert groupAnalyzer == null : "resolve already in progress"; - groupAnalyzer = new SqlValidatorUtil.GroupAnalyzer(); + SqlValidatorUtil.GroupAnalyzer groupAnalyzer = new SqlValidatorUtil.GroupAnalyzer(); + this.groupAnalyzer = groupAnalyzer; try { final ImmutableList.Builder> builder = ImmutableList.builder(); @@ -109,7 +114,7 @@ private Resolved resolve() { return new Resolved(groupAnalyzer.extraExprs, groupAnalyzer.groupExprs, flatGroupSets, groupAnalyzer.groupExprProjection); } finally { - groupAnalyzer = null; + this.groupAnalyzer = null; } } @@ -133,11 +138,15 @@ private Pair, ImmutableList> getGroupExprs() { // OrderExpressionExpander. ImmutableList.Builder groupExprs = ImmutableList.builder(); final SelectScope selectScope = (SelectScope) parent; - for (SqlNode selectItem : selectScope.getExpandedSelectList()) { + List expandedSelectList = Objects.requireNonNull( + selectScope.getExpandedSelectList(), + () -> "expandedSelectList for " + selectScope); + for (SqlNode selectItem : expandedSelectList) { groupExprs.add(stripAs(selectItem)); } return Pair.of(ImmutableList.of(), groupExprs.build()); } else if (select.getGroup() != null) { + SqlValidatorUtil.GroupAnalyzer groupAnalyzer = this.groupAnalyzer; if (groupAnalyzer != null) { // we are in the middle of resolving return Pair.of(ImmutableList.of(), diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java index fc2973688fff..f2f81f75f8db 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java @@ -28,6 +28,8 @@ import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import static org.apache.calcite.util.Static.RESOURCE; @@ -68,14 +70,14 @@ protected AliasNamespace( @Override public boolean supportsModality(SqlModality modality) { final List operands = call.getOperandList(); final SqlValidatorNamespace childNs = - validator.getNamespace(operands.get(0)); + validator.getNamespaceOrThrow(operands.get(0)); return childNs.supportsModality(modality); } @Override protected RelDataType validateImpl(RelDataType targetRowType) { final List operands = call.getOperandList(); final SqlValidatorNamespace childNs = - validator.getNamespace(operands.get(0)); + validator.getNamespaceOrThrow(operands.get(0)); final RelDataType rowType = childNs.getRowTypeSansSystemColumns(); final RelDataType aliasedType; if (operands.size() == 2) { @@ -145,7 +147,7 @@ private String getString(RelDataType rowType) { return buf.toString(); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return call; } @@ -153,7 +155,7 @@ private String getString(RelDataType rowType) { final RelDataType underlyingRowType = validator.getValidatedNodeType(call.operand(0)); int i = 0; - for (RelDataTypeField field : rowType.getFieldList()) { + for (RelDataTypeField field : getRowType().getFieldList()) { if (field.getName().equals(name)) { return underlyingRowType.getFieldList().get(i).getName(); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java index ce6b6edbb76b..f2f153129402 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Namespace for COLLECT and TABLE constructs. * @@ -68,7 +70,7 @@ public class CollectNamespace extends AbstractNamespace { return child.getOperator().deriveType(validator, scope, child); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return child; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/CollectScope.java b/core/src/main/java/org/apache/calcite/sql/validate/CollectScope.java index 8e56d6738bf6..b3f6f7025b54 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/CollectScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/CollectScope.java @@ -19,6 +19,8 @@ import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * The name-resolution context for expression inside a multiset call. The * objects visible are multiset expressions, and those inherited from the parent @@ -30,14 +32,14 @@ class CollectScope extends ListScope { //~ Instance fields -------------------------------------------------------- @SuppressWarnings("unused") - private final SqlValidatorScope usingScope; + private final @Nullable SqlValidatorScope usingScope; private final SqlCall child; //~ Constructors ----------------------------------------------------------- CollectScope( SqlValidatorScope parent, - SqlValidatorScope usingScope, + @Nullable SqlValidatorScope usingScope, SqlCall child) { super(parent); this.usingScope = usingScope; diff --git a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java index b7b5f6081477..97d6abdf1a32 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -48,7 +50,7 @@ protected DelegatingNamespace(SqlValidatorNamespace namespace) { return namespace.getValidator(); } - @Override public SqlValidatorTable getTable() { + @Override public @Nullable SqlValidatorTable getTable() { return namespace.getTable(); } @@ -72,15 +74,15 @@ protected DelegatingNamespace(SqlValidatorNamespace namespace) { namespace.validate(targetRowType); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return namespace.getNode(); } - @Override public SqlNode getEnclosingNode() { + @Override public @Nullable SqlNode getEnclosingNode() { return namespace.getEnclosingNode(); } - @Override public SqlValidatorNamespace lookupChild( + @Override public @Nullable SqlValidatorNamespace lookupChild( String name) { return namespace.lookupChild(name); } @@ -101,7 +103,7 @@ protected DelegatingNamespace(SqlValidatorNamespace namespace) { @Override public void makeNullable() { } - @Override public T unwrap(Class clazz) { + @Override public T unwrap(Class clazz) { if (clazz.isInstance(this)) { return clazz.cast(this); } else { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java index 9c44e2d6e6b9..5f2d3089062d 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java @@ -35,6 +35,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -44,6 +46,8 @@ import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * A scope which delegates all requests to its parent scope. Use this as a base * class for defining nested scopes. @@ -124,7 +128,9 @@ void resolveInNamespace(SqlValidatorNamespace ns, boolean nullable, final String name = names.get(0); final RelDataTypeField field0 = nameMatcher.field(rowType, name); if (field0 != null) { - final SqlValidatorNamespace ns2 = ns.lookupChild(field0.getName()); + final SqlValidatorNamespace ns2 = requireNonNull( + ns.lookupChild(field0.getName()), + () -> "field " + field0.getName() + " is not found in " + ns); final Step path2 = path.plus(rowType, field0.getIndex(), field0.getName(), StructKind.FULLY_QUALIFIED); resolveInNamespace(ns2, nullable, names.subList(1, names.size()), @@ -137,7 +143,9 @@ void resolveInNamespace(SqlValidatorNamespace ns, boolean nullable, case PEEK_FIELDS_NO_EXPAND: final Step path2 = path.plus(rowType, field.getIndex(), field.getName(), field.getType().getStructKind()); - final SqlValidatorNamespace ns2 = ns.lookupChild(field.getName()); + final SqlValidatorNamespace ns2 = requireNonNull( + ns.lookupChild(field.getName()), + () -> "field " + field.getName() + " is not found in " + ns); resolveInNamespace(ns2, nullable, names, nameMatcher, path2, resolved); break; @@ -188,7 +196,7 @@ protected void addColumnNames( return parent.findQualifyingTableNames(columnName, ctx, nameMatcher); } - @Override public RelDataType resolveColumn(String name, SqlNode ctx) { + @Override public @Nullable RelDataType resolveColumn(String name, SqlNode ctx) { return parent.resolveColumn(name, ctx); } @@ -196,8 +204,8 @@ protected void addColumnNames( return parent.nullifyType(node, type); } - @Override @SuppressWarnings("deprecation") - public SqlValidatorNamespace getTableNamespace(List names) { + @SuppressWarnings("deprecation") + @Override public @Nullable SqlValidatorNamespace getTableNamespace(List names) { return parent.getTableNamespace(names); } @@ -251,6 +259,9 @@ public SqlValidatorNamespace getTableNamespace(List names) { final RelDataTypeField field = liberalMatcher.field(entry.namespace.getRowType(), columnName); + if (field == null) { + continue; + } list.add(field.getName()); } Collections.sort(list); @@ -394,7 +405,7 @@ public SqlValidatorNamespace getTableNamespace(List names) { identifier = identifier.setName(i - 1, alias); } } - if (fromPath.stepCount() > 1) { + if (requireNonNull(fromPath, "fromPath").stepCount() > 1) { assert fromRowType != null; for (Step p : fromPath.steps()) { fromRowType = fromRowType.getFieldList().get(p.i).getType(); @@ -483,7 +494,10 @@ private int worstKind(Path path) { identifier, RESOURCE.columnNotFound(name)); } final RelDataTypeField field0 = - step.rowType.getFieldList().get(step.i); + requireNonNull( + step.rowType, + () -> "rowType of step " + step.name + ).getFieldList().get(step.i); final String fieldName = field0.getName(); switch (step.kind) { case PEEK_FIELDS: @@ -533,7 +547,7 @@ private int worstKind(Path path) { // be valid in the parent scope. } - @Override public SqlWindow lookupWindow(String name) { + @Override public @Nullable SqlWindow lookupWindow(String name) { return parent.lookupWindow(name); } @@ -541,7 +555,7 @@ private int worstKind(Path path) { return parent.getMonotonicity(expr); } - @Override public SqlNodeList getOrderList() { + @Override public @Nullable SqlNodeList getOrderList() { return parent.getOrderList(); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java index 3502065016ee..6f9b7531cbd2 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingSqlValidatorCatalogReader.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlIdentifier; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -40,11 +42,11 @@ protected DelegatingSqlValidatorCatalogReader( this.catalogReader = catalogReader; } - @Override public SqlValidatorTable getTable(List names) { + @Override public @Nullable SqlValidatorTable getTable(List names) { return catalogReader.getTable(names); } - @Override public RelDataType getNamedType(SqlIdentifier typeName) { + @Override public @Nullable RelDataType getNamedType(SqlIdentifier typeName) { return catalogReader.getNamedType(typeName); } @@ -56,7 +58,7 @@ protected DelegatingSqlValidatorCatalogReader( return catalogReader.getSchemaPaths(); } - @Override public C unwrap(Class aClass) { + @Override public @Nullable C unwrap(Class aClass) { return catalogReader.unwrap(aClass); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java b/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java index dde9205e678c..1a7a06ed7f12 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java @@ -38,6 +38,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -81,8 +83,8 @@ class EmptyScope implements SqlValidatorScope { boolean deep, Resolved resolved) { } - @Override @SuppressWarnings("deprecation") - public SqlValidatorNamespace getTableNamespace(List names) { + @SuppressWarnings("deprecation") + @Override public @Nullable SqlValidatorNamespace getTableNamespace(List names) { SqlValidatorTable table = validator.catalogReader.getTable(names); return table != null ? new TableNamespace(validator, table) @@ -185,7 +187,7 @@ public void findAllTableNames(List result) { @Override public void findAliases(Collection result) { } - @Override public RelDataType resolveColumn(String name, SqlNode ctx) { + @Override public @Nullable RelDataType resolveColumn(String name, SqlNode ctx) { return null; } @@ -215,7 +217,7 @@ public void findAllTableNames(List result) { throw new UnsupportedOperationException(); } - @Override public SqlWindow lookupWindow(String name) { + @Override public @Nullable SqlWindow lookupWindow(String name) { // No windows defined in this scope. return null; } @@ -228,7 +230,7 @@ public void findAllTableNames(List result) { : SqlMonotonicity.NOT_MONOTONIC; } - @Override public SqlNodeList getOrderList() { + @Override public @Nullable SqlNodeList getOrderList() { // scope is not ordered return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java index a60f629ac75d..59607e746375 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java @@ -19,6 +19,10 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link SqlValidatorNamespace} for a field of a record. * @@ -50,15 +54,15 @@ class FieldNamespace extends AbstractNamespace { } @Override protected RelDataType validateImpl(RelDataType targetRowType) { - return rowType; + return requireNonNull(rowType, "rowType"); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return null; } - @Override public SqlValidatorNamespace lookupChild(String name) { - if (rowType.isStruct()) { + @Override public @Nullable SqlValidatorNamespace lookupChild(String name) { + if (requireNonNull(rowType, "rowType").isStruct()) { return validator.lookupFieldNamespace( rowType, name); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java index f37cc5adf557..ba64067c300c 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java @@ -28,11 +28,13 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; -import javax.annotation.Nullable; import static org.apache.calcite.util.Static.RESOURCE; @@ -45,18 +47,18 @@ public class IdentifierNamespace extends AbstractNamespace { private final SqlIdentifier id; private final SqlValidatorScope parentScope; - public final SqlNodeList extendList; + public final @Nullable SqlNodeList extendList; /** * The underlying namespace. Often a {@link TableNamespace}. * Set on validate. */ - private SqlValidatorNamespace resolvedNamespace; + private @MonotonicNonNull SqlValidatorNamespace resolvedNamespace; /** * List of monotonic expressions. Set on validate. */ - private List> monotonicExprs; + private @Nullable List> monotonicExprs; //~ Constructors ----------------------------------------------------------- @@ -70,7 +72,7 @@ public class IdentifierNamespace extends AbstractNamespace { * @param parentScope Parent scope which this namespace turns to in order to */ IdentifierNamespace(SqlValidatorImpl validator, SqlIdentifier id, - @Nullable SqlNodeList extendList, SqlNode enclosingNode, + @Nullable SqlNodeList extendList, @Nullable SqlNode enclosingNode, SqlValidatorScope parentScope) { super(validator, enclosingNode); this.id = id; @@ -79,14 +81,14 @@ public class IdentifierNamespace extends AbstractNamespace { } IdentifierNamespace(SqlValidatorImpl validator, SqlNode node, - SqlNode enclosingNode, SqlValidatorScope parentScope) { + @Nullable SqlNode enclosingNode, SqlValidatorScope parentScope) { this(validator, split(node).left, split(node).right, enclosingNode, parentScope); } //~ Methods ---------------------------------------------------------------- - protected static Pair split(SqlNode node) { + protected static Pair split(SqlNode node) { switch (node.getKind()) { case EXTEND: final SqlCall call = (SqlCall) node; @@ -97,8 +99,10 @@ protected static Pair split(SqlNode node) { return Pair.of(identifier, call.operand(1)); case TABLE_REF: final SqlCall tableRef = (SqlCall) node; + //noinspection ConstantConditions return Pair.of(tableRef.operand(0), null); default: + //noinspection ConstantConditions return Pair.of((SqlIdentifier) node, null); } } @@ -181,9 +185,9 @@ private SqlValidatorNamespace resolveImpl(SqlIdentifier id) { } @Override public RelDataType validateImpl(RelDataType targetRowType) { - resolvedNamespace = Objects.requireNonNull(resolveImpl(id)); + resolvedNamespace = resolveImpl(id); if (resolvedNamespace instanceof TableNamespace) { - SqlValidatorTable table = resolvedNamespace.getTable(); + SqlValidatorTable table = ((TableNamespace) resolvedNamespace).getTable(); if (validator.config().identifierExpansion()) { // TODO: expand qualifiers for column references also List qualifiedNames = table.getQualifiedName(); @@ -244,7 +248,7 @@ public SqlIdentifier getId() { return id; } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return id; } @@ -253,16 +257,20 @@ public SqlIdentifier getId() { return resolvedNamespace.resolve(); } - @Override public SqlValidatorTable getTable() { + @Override public @Nullable SqlValidatorTable getTable() { return resolvedNamespace == null ? null : resolve().getTable(); } @Override public List> getMonotonicExprs() { - return monotonicExprs; + List> monotonicExprs = this.monotonicExprs; + return monotonicExprs == null ? ImmutableList.of() : monotonicExprs; } @Override public SqlMonotonicity getMonotonicity(String columnName) { final SqlValidatorTable table = getTable(); + if (table == null) { + return SqlMonotonicity.NOT_MONOTONIC; + } return table.getMonotonicity(columnName); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java index 60802d1217d7..43564fca16d8 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.SqlJoin; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Namespace representing the row type produced by joining two relations. */ @@ -40,9 +42,9 @@ class JoinNamespace extends AbstractNamespace { @Override protected RelDataType validateImpl(RelDataType targetRowType) { RelDataType leftType = - validator.getNamespace(join.getLeft()).getRowType(); + validator.getNamespaceOrThrow(join.getLeft()).getRowType(); RelDataType rightType = - validator.getNamespace(join.getRight()).getRowType(); + validator.getNamespaceOrThrow(join.getRight()).getRowType(); final RelDataTypeFactory typeFactory = validator.getTypeFactory(); switch (join.getJoinType()) { case LEFT: @@ -61,7 +63,7 @@ class JoinNamespace extends AbstractNamespace { return typeFactory.createJoinType(leftType, rightType); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return join; } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java b/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java index 6cdd48afb0a7..d73b16800ff3 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java @@ -20,6 +20,10 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlWindow; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * The name-resolution context for expression inside a JOIN clause. The objects * visible are the joined table expressions, and those inherited from the parent @@ -32,7 +36,7 @@ public class JoinScope extends ListScope { //~ Instance fields -------------------------------------------------------- - private final SqlValidatorScope usingScope; + private final @Nullable SqlValidatorScope usingScope; private final SqlJoin join; //~ Constructors ----------------------------------------------------------- @@ -46,7 +50,7 @@ public class JoinScope extends ListScope { */ JoinScope( SqlValidatorScope parent, - SqlValidatorScope usingScope, + @Nullable SqlValidatorScope usingScope, SqlJoin join) { super(parent); this.usingScope = usingScope; @@ -77,7 +81,7 @@ public class JoinScope extends ListScope { } } - @Override public SqlWindow lookupWindow(String name) { + @Override public @Nullable SqlWindow lookupWindow(String name) { // Lookup window in enclosing select. if (usingScope != null) { return usingScope.lookupWindow(name); @@ -89,15 +93,15 @@ public class JoinScope extends ListScope { /** * Returns the scope which is used for resolving USING clause. */ - public SqlValidatorScope getUsingScope() { + public @Nullable SqlValidatorScope getUsingScope() { return usingScope; } - @Override public boolean isWithin(SqlValidatorScope scope2) { + @Override public boolean isWithin(@Nullable SqlValidatorScope scope2) { if (this == scope2) { return true; } // go from the JOIN to the enclosing SELECT - return usingScope.isWithin(scope2); + return requireNonNull(usingScope, "usingScope").isWithin(scope2); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java b/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java index 89972c911331..1c4061d49329 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/ListScope.java @@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -74,11 +76,11 @@ public List getChildren() { * * @return list of child namespaces */ - List getChildNames() { + List<@Nullable String> getChildNames() { return Util.transform(children, scopeChild -> scopeChild.name); } - private ScopeChild findChild(List names, + private @Nullable ScopeChild findChild(List names, SqlNameMatcher nameMatcher) { for (ScopeChild child : children) { String lastName = Util.last(names); @@ -100,18 +102,24 @@ private ScopeChild findChild(List names, if (table != null) { final ResolvedImpl resolved = new ResolvedImpl(); resolveTable(names, nameMatcher, Path.EMPTY, resolved); - if (resolved.count() == 1 - && resolved.only().remainingNames.isEmpty() - && resolved.only().namespace instanceof TableNamespace - && resolved.only().namespace.getTable().getQualifiedName().equals( - table.getQualifiedName())) { - return child; + if (resolved.count() == 1) { + Resolve only = resolved.only(); + List qualifiedName = table.getQualifiedName(); + if (only.remainingNames.isEmpty() + && only.namespace instanceof TableNamespace + && Objects.equals(qualifiedName, getQualifiedName(only.namespace.getTable()))) { + return child; + } } } } return null; } + private @Nullable List getQualifiedName(@Nullable SqlValidatorTable table) { + return table == null ? null : table.getQualifiedName(); + } + @Override public void findAllColumnNames(List result) { for (ScopeChild child : children) { addColumnNames(child.namespace, result); @@ -200,7 +208,7 @@ private ScopeChild findChild(List names, super.resolve(names, nameMatcher, deep, resolved); } - @Override public RelDataType resolveColumn(String columnName, SqlNode ctx) { + @Override public @Nullable RelDataType resolveColumn(String columnName, SqlNode ctx) { final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher(); int found = 0; RelDataType type = null; diff --git a/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeNamespace.java index d1ac797436d2..91b4e70e3925 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeNamespace.java @@ -20,6 +20,10 @@ import org.apache.calcite.sql.SqlMatchRecognize; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * Namespace for a {@code MATCH_RECOGNIZE} clause. */ @@ -36,10 +40,10 @@ protected MatchRecognizeNamespace(SqlValidatorImpl validator, @Override public RelDataType validateImpl(RelDataType targetRowType) { validator.validateMatchRecognize(matchRecognize); - return rowType; + return requireNonNull(rowType, "rowType"); } - @Override public SqlMatchRecognize getNode() { + @Override public @Nullable SqlNode getNode() { return matchRecognize; } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeScope.java b/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeScope.java index 069a44ffde20..89df7fbb9517 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/MatchRecognizeScope.java @@ -84,7 +84,7 @@ public void addPatternVar(String str) { @Override public void resolve(List names, SqlNameMatcher nameMatcher, boolean deep, Resolved resolved) { if (patternVars.contains(names.get(0))) { - final Step path = new EmptyPath().plus(null, 0, null, StructKind.FULLY_QUALIFIED); + final Step path = new EmptyPath().plus(null, 0, "", StructKind.FULLY_QUALIFIED); final ScopeChild child = children.get(0); resolved.found(child.namespace, child.nullable, this, path, names); if (resolved.count() > 0) { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java b/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java index c1288019a53d..61d73f37edd6 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java @@ -23,8 +23,11 @@ import org.apache.calcite.sql.SqlNodeList; import org.apache.calcite.sql.SqlSelect; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getSelectList; import static org.apache.calcite.util.Static.RESOURCE; /** @@ -63,7 +66,7 @@ public class OrderByScope extends DelegatingScope { } @Override public void findAllColumnNames(List result) { - final SqlValidatorNamespace ns = validator.getNamespace(select); + final SqlValidatorNamespace ns = validator.getNamespaceOrThrow(select); addColumnNames(ns, result); } @@ -73,7 +76,7 @@ public class OrderByScope extends DelegatingScope { && validator.config().sqlConformance().isSortByAlias()) { final String name = identifier.names.get(0); final SqlValidatorNamespace selectNs = - validator.getNamespace(select); + validator.getNamespaceOrThrow(select); final RelDataType rowType = selectNs.getRowType(); final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher(); @@ -97,7 +100,7 @@ public class OrderByScope extends DelegatingScope { * {@code t.c as name}) alias. */ private int aliasCount(SqlNameMatcher nameMatcher, String name) { int n = 0; - for (SqlNode s : select.getSelectList()) { + for (SqlNode s : getSelectList(select)) { final String alias = SqlValidatorUtil.getAlias(s, -1); if (alias != null && nameMatcher.matches(alias, name)) { n++; @@ -106,8 +109,8 @@ private int aliasCount(SqlNameMatcher nameMatcher, String name) { return n; } - @Override public RelDataType resolveColumn(String name, SqlNode ctx) { - final SqlValidatorNamespace selectNs = validator.getNamespace(select); + @Override public @Nullable RelDataType resolveColumn(String name, SqlNode ctx) { + final SqlValidatorNamespace selectNs = validator.getNamespaceOrThrow(select); final RelDataType rowType = selectNs.getRowType(); final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher(); final RelDataTypeField field = nameMatcher.field(rowType, name); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java index 15e65bfb30a4..9a2c54e31d05 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java @@ -19,6 +19,8 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Namespace representing the type of a dynamic parameter. * @@ -39,7 +41,7 @@ class ParameterNamespace extends AbstractNamespace { //~ Methods ---------------------------------------------------------------- - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java b/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java index e5ff5cae0628..249996be63e5 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/ParameterScope.java @@ -21,6 +21,8 @@ import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; /** @@ -57,7 +59,7 @@ public ParameterScope( return this; } - @Override public RelDataType resolveColumn(String name, SqlNode ctx) { + @Override public @Nullable RelDataType resolveColumn(String name, SqlNode ctx) { return nameToTypeMap.get(name); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/PivotNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/PivotNamespace.java index 3b0dc256d73d..208dc80f959b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/PivotNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/PivotNamespace.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlPivot; +import static java.util.Objects.requireNonNull; + /** * Namespace for a {@code PIVOT} clause. */ @@ -35,7 +37,7 @@ protected PivotNamespace(SqlValidatorImpl validator, SqlPivot pivot, @Override public RelDataType validateImpl(RelDataType targetRowType) { validator.validatePivot(pivot); - return rowType; + return requireNonNull(rowType, "rowType"); } @Override public SqlPivot getNode() { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java b/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java index 4d208124c8ae..eb0436cbef4b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.SqlPivot; +import static java.util.Objects.requireNonNull; + /** * Scope for expressions in a {@code PIVOT} clause. */ @@ -36,7 +38,9 @@ public PivotScope(SqlValidatorScope parent, SqlPivot pivot) { * {@link org.apache.calcite.sql.validate.ListScope#getChildren()}, but this * scope only has one namespace, and it is anonymous. */ public SqlValidatorNamespace getChild() { - return validator.getNamespace(pivot.query); + return requireNonNull( + validator.getNamespace(pivot.query), + () -> "namespace for pivot.query " + pivot.query); } @Override public SqlPivot getNode() { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java index 25ed70586605..9d97a252efa5 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java @@ -25,6 +25,10 @@ import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * Namespace whose contents are defined by the result of a call to a * user-defined procedure. @@ -67,10 +71,12 @@ public class ProcedureNamespace extends AbstractNamespace { } final SqlReturnTypeInference rowTypeInference = tableFunction.getRowTypeInference(); - return rowTypeInference.inferReturnType(callBinding); + return requireNonNull( + rowTypeInference.inferReturnType(callBinding), + () -> "got null from inferReturnType for call " + callBinding.getCall()); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return call; } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java index 1f9c98fae999..ec69f442b0ac 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java @@ -23,9 +23,13 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** Namespace based on a schema. * *

    The visible names are tables and sub-schemas. @@ -46,13 +50,15 @@ class SchemaNamespace extends AbstractNamespace { for (SqlMoniker moniker : validator.catalogReader.getAllSchemaObjectNames(names)) { final List names1 = moniker.getFullyQualifiedNames(); - final SqlValidatorTable table = validator.catalogReader.getTable(names1); + final SqlValidatorTable table = requireNonNull( + validator.catalogReader.getTable(names1), + () -> "table " + names1 + " is not found in scope " + names); builder.add(Util.last(names1), table.getRowType()); } return builder.build(); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return null; } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ScopeChild.java b/core/src/main/java/org/apache/calcite/sql/validate/ScopeChild.java index 9a6c8d7be9be..9186dafa0be8 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/ScopeChild.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/ScopeChild.java @@ -16,7 +16,6 @@ */ package org.apache.calcite.sql.validate; - /** One of the inputs of a {@link SqlValidatorScope}. * *

    Most commonly, it is an item in a FROM clause, and consists of a namespace diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java index 6dd4f69d725f..f34b19235bd6 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java @@ -21,6 +21,10 @@ import org.apache.calcite.sql.SqlSelect; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * Namespace offered by a sub-query. * @@ -52,13 +56,13 @@ public SelectNamespace( //~ Methods ---------------------------------------------------------------- // implement SqlValidatorNamespace, overriding return type - @Override public SqlSelect getNode() { + @Override public @Nullable SqlNode getNode() { return select; } @Override public RelDataType validateImpl(RelDataType targetRowType) { validator.validateSelect(select, targetRowType); - return rowType; + return requireNonNull(rowType, "rowType"); } @Override public boolean supportsModality(SqlModality modality) { @@ -68,9 +72,12 @@ public SelectNamespace( @Override public SqlMonotonicity getMonotonicity(String columnName) { final RelDataType rowType = this.getRowTypeSansSystemColumns(); final int field = SqlTypeUtil.findField(rowType, columnName); - final SqlNode selectItem = - validator.getRawSelectScope(select) - .getExpandedSelectList().get(field); + SelectScope selectScope = requireNonNull( + validator.getRawSelectScope(select), + () -> "rawSelectScope for " + select); + final SqlNode selectItem = requireNonNull( + selectScope.getExpandedSelectList(), + () -> "expandedSelectList for selectScope of " + select).get(field); return validator.getSelectScope(select).getMonotonicity(selectItem); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java b/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java index d118db12d410..e042fa436760 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SelectScope.java @@ -27,8 +27,12 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * The name-resolution scope of a SELECT clause. The objects visible are those @@ -91,16 +95,16 @@ public class SelectScope extends ListScope { private final SqlSelect select; protected final List windowNames = new ArrayList<>(); - private List expandedSelectList = null; + private @Nullable List expandedSelectList = null; /** * List of column names which sort this scope. Empty if this scope is not * sorted. Null if has not been computed yet. */ - private SqlNodeList orderList; + private @MonotonicNonNull SqlNodeList orderList; /** Scope to use to resolve windows. */ - private final SqlValidatorScope windowParent; + private final @Nullable SqlValidatorScope windowParent; //~ Constructors ----------------------------------------------------------- @@ -113,7 +117,7 @@ public class SelectScope extends ListScope { */ SelectScope( SqlValidatorScope parent, - SqlValidatorScope winParent, + @Nullable SqlValidatorScope winParent, SqlSelect select) { super(parent); this.select = select; @@ -122,7 +126,7 @@ public class SelectScope extends ListScope { //~ Methods ---------------------------------------------------------------- - public SqlValidatorTable getTable() { + public @Nullable SqlValidatorTable getTable() { return null; } @@ -130,11 +134,13 @@ public SqlValidatorTable getTable() { return select; } - @Override public SqlWindow lookupWindow(String name) { + @Override public @Nullable SqlWindow lookupWindow(String name) { final SqlNodeList windowList = select.getWindowList(); for (int i = 0; i < windowList.size(); i++) { SqlWindow window = (SqlWindow) windowList.get(i); - final SqlIdentifier declId = window.getDeclName(); + final SqlIdentifier declId = Objects.requireNonNull( + window.getDeclName(), + () -> "declName of window " + window); assert declId.isSimple(); if (declId.names.get(0).equals(name)) { return window; @@ -214,11 +220,11 @@ public boolean existingWindowName(String winName) { return false; } - public List getExpandedSelectList() { + public @Nullable List getExpandedSelectList() { return expandedSelectList; } - public void setExpandedSelectList(List selectList) { + public void setExpandedSelectList(@Nullable List selectList) { expandedSelectList = selectList; } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java index eaac6ab5650d..f92b12d36036 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java @@ -20,9 +20,14 @@ import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.util.Util; + +import org.checkerframework.checker.nullness.qual.Nullable; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Namespace based upon a set operation (UNION, INTERSECT, EXCEPT). */ @@ -50,7 +55,7 @@ protected SetopNamespace( //~ Methods ---------------------------------------------------------------- - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return call; } @@ -61,15 +66,19 @@ protected SetopNamespace( return SqlMonotonicity.NOT_MONOTONIC; } for (SqlNode operand : call.getOperandList()) { - final SqlValidatorNamespace namespace = validator.getNamespace(operand); + final SqlValidatorNamespace namespace = + requireNonNull( + validator.getNamespace(operand), + () -> "namespace for " + operand); monotonicity = combine(monotonicity, namespace.getMonotonicity( namespace.getRowType().getFieldNames().get(index))); } - return monotonicity; + return Util.first(monotonicity, SqlMonotonicity.NOT_MONOTONIC); } - private SqlMonotonicity combine(SqlMonotonicity m0, SqlMonotonicity m1) { + private SqlMonotonicity combine(@Nullable SqlMonotonicity m0, + SqlMonotonicity m1) { if (m0 == null) { return m1; } @@ -93,7 +102,9 @@ private SqlMonotonicity combine(SqlMonotonicity m0, SqlMonotonicity m1) { case UNION: case INTERSECT: case EXCEPT: - final SqlValidatorScope scope = validator.scopes.get(call); + final SqlValidatorScope scope = requireNonNull( + validator.scopes.get(call), + () -> "scope for " + call); for (SqlNode operand : call.getOperandList()) { if (!operand.isA(SqlKind.QUERY)) { throw validator.newValidationError(operand, diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java index 27fe51fb56a0..769e403aa5d1 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlMonikerImpl.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -53,7 +55,7 @@ public SqlMonikerImpl(String name, SqlMonikerType type) { //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof SqlMonikerImpl && type == ((SqlMonikerImpl) obj).type diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java index 14a905bedae8..b2c040cf0c66 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java @@ -21,6 +21,8 @@ import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Map; import java.util.Set; @@ -43,7 +45,7 @@ public interface SqlNameMatcher { boolean matches(String string, String name); /** Looks up an item in a map. */ - , V> V get(Map map, List prefixNames, + , V> @Nullable V get(Map map, List prefixNames, List names); /** Returns the most recent match. @@ -59,7 +61,7 @@ , V> V get(Map map, List prefixNames, * @param fieldName Field name * @return Field, or null if not found */ - RelDataTypeField field(RelDataType rowType, String fieldName); + @Nullable RelDataTypeField field(RelDataType rowType, String fieldName); /** Returns how many times a string occurs in a collection. * diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java index 966485707f3f..478c9ab4ce19 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatchers.java @@ -23,12 +23,16 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import static java.util.Objects.requireNonNull; + /** * Helpers for {@link SqlNameMatcher}. */ @@ -82,7 +86,7 @@ protected boolean listMatches(List list0, List list1) { return true; } - @Override public , V> V get(Map map, + @Override public , V> @Nullable V get(Map map, List prefixNames, List names) { final List key = concat(prefixNames, names); if (caseSensitive) { @@ -118,7 +122,7 @@ protected List bestMatch() { return SqlIdentifier.getString(bestMatch()); } - @Override public RelDataTypeField field(RelDataType rowType, String fieldName) { + @Override public @Nullable RelDataTypeField field(RelDataType rowType, String fieldName) { return rowType.getField(fieldName, caseSensitive, false); } @@ -141,7 +145,7 @@ protected List bestMatch() { /** Matcher that remembers the requests that were made of it. */ private static class LiberalNameMatcher extends BaseMatcher { - List matchedNames; + @Nullable List matchedNames; LiberalNameMatcher() { super(false); @@ -165,7 +169,7 @@ private static class LiberalNameMatcher extends BaseMatcher { } @Override public List bestMatch() { - return matchedNames; + return requireNonNull(matchedNames, "matchedNames"); } } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java new file mode 100644 index 000000000000..d1ce07f6d774 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java @@ -0,0 +1,97 @@ +/* + * 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.calcite.sql.validate; + +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlDelete; +import org.apache.calcite.sql.SqlJoin; +import org.apache.calcite.sql.SqlMerge; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.sql.SqlUpdate; + +import org.apiguardian.api.API; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +/** + * This class provides non-nullable accessors for common getters. + */ +@API(since = "1.27", status = API.Status.EXPERIMENTAL) +public class SqlNonNullableAccessors { + private SqlNonNullableAccessors() { + } + + private static String safeToString(Object obj) { + try { + return Objects.toString(obj); + } catch (Throwable e) { + return "Error in toString: " + e; + } + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlSelect getSourceSelect(SqlUpdate statement) { + return requireNonNull(statement.getSourceSelect(), + () -> "sourceSelect of " + safeToString(statement)); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlSelect getSourceSelect(SqlDelete statement) { + return requireNonNull(statement.getSourceSelect(), + () -> "sourceSelect of " + safeToString(statement)); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlSelect getSourceSelect(SqlMerge statement) { + return requireNonNull(statement.getSourceSelect(), + () -> "sourceSelect of " + safeToString(statement)); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlNode getCondition(SqlJoin join) { + return requireNonNull(join.getCondition(), + () -> "getCondition of " + safeToString(join)); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + static SqlNode getNode(ScopeChild child) { + return requireNonNull(child.namespace.getNode(), + () -> "child.namespace.getNode() of " + child.name); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlNodeList getSelectList(SqlSelect innerSelect) { + return requireNonNull(innerSelect.getSelectList(), + () -> "selectList of " + safeToString(innerSelect)); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlValidatorTable getTable(SqlValidatorNamespace ns) { + return requireNonNull(ns.getTable(), + () -> "ns.getTable() for " + safeToString(ns)); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public static SqlValidatorScope getScope(SqlCallBinding callBinding) { + return requireNonNull(callBinding.getScope(), + () -> "scope is null for " + safeToString(callBinding)); + } +} diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java index 954bef310ad6..9742f1510b59 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlQualified.java @@ -19,6 +19,8 @@ import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -33,11 +35,11 @@ */ public class SqlQualified { public final int prefixLength; - public final SqlValidatorNamespace namespace; + public final @Nullable SqlValidatorNamespace namespace; public final SqlIdentifier identifier; - private SqlQualified(SqlValidatorScope scope, int prefixLength, - SqlValidatorNamespace namespace, SqlIdentifier identifier) { + private SqlQualified(@Nullable SqlValidatorScope scope, int prefixLength, + @Nullable SqlValidatorNamespace namespace, SqlIdentifier identifier) { Util.discard(scope); this.prefixLength = prefixLength; this.namespace = namespace; @@ -48,8 +50,8 @@ private SqlQualified(SqlValidatorScope scope, int prefixLength, return "{id: " + identifier.toString() + ", prefix: " + prefixLength + "}"; } - public static SqlQualified create(SqlValidatorScope scope, int prefixLength, - SqlValidatorNamespace namespace, SqlIdentifier identifier) { + public static SqlQualified create(@Nullable SqlValidatorScope scope, int prefixLength, + @Nullable SqlValidatorNamespace namespace, SqlIdentifier identifier) { return new SqlQualified(scope, prefixLength, namespace, identifier); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlScopedShuttle.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlScopedShuttle.java index 1006374e1e88..ca4d55ff7a9a 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlScopedShuttle.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlScopedShuttle.java @@ -21,9 +21,13 @@ import org.apache.calcite.sql.util.SqlShuttle; import org.apache.calcite.sql.util.SqlVisitor; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayDeque; import java.util.Deque; +import static java.util.Objects.requireNonNull; + /** * Refinement to {@link SqlShuttle} which maintains a stack of scopes. * @@ -43,8 +47,8 @@ protected SqlScopedShuttle(SqlValidatorScope initialScope) { //~ Methods ---------------------------------------------------------------- - @Override public final SqlNode visit(SqlCall call) { - SqlValidatorScope oldScope = scopes.peek(); + @Override public final @Nullable SqlNode visit(SqlCall call) { + SqlValidatorScope oldScope = getScope(); SqlValidatorScope newScope = oldScope.getOperandScope(call); scopes.push(newScope); SqlNode result = visitScoped(call); @@ -56,7 +60,7 @@ protected SqlScopedShuttle(SqlValidatorScope initialScope) { * Visits an operator call. If the call has entered a new scope, the base * class will have already modified the scope. */ - protected SqlNode visitScoped(SqlCall call) { + protected @Nullable SqlNode visitScoped(SqlCall call) { return super.visit(call); } @@ -64,6 +68,6 @@ protected SqlNode visitScoped(SqlCall call) { * Returns the current scope. */ protected SqlValidatorScope getScope() { - return scopes.peek(); + return requireNonNull(scopes.peek(), "scopes.peek()"); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java index cf16d3d155e7..3384ec636512 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java @@ -29,6 +29,8 @@ import org.apache.calcite.util.Optionality; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * User-defined aggregate function. * @@ -42,7 +44,7 @@ public class SqlUserDefinedAggFunction extends SqlAggFunction { public SqlUserDefinedAggFunction(SqlIdentifier opName, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, AggregateFunction function, + @Nullable SqlOperandTypeChecker operandTypeChecker, AggregateFunction function, boolean requiresOrder, boolean requiresOver, Optionality requiresGroupOrder, RelDataTypeFactory typeFactory) { this(opName, SqlKind.OTHER_FUNCTION, returnTypeInference, @@ -57,7 +59,7 @@ public SqlUserDefinedAggFunction(SqlIdentifier opName, public SqlUserDefinedAggFunction(SqlIdentifier opName, SqlKind kind, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandMetadata operandMetadata, AggregateFunction function, + @Nullable SqlOperandMetadata operandMetadata, AggregateFunction function, boolean requiresOrder, boolean requiresOver, Optionality requiresGroupOrder) { super(Util.last(opName.names), opName, kind, @@ -67,7 +69,7 @@ public SqlUserDefinedAggFunction(SqlIdentifier opName, SqlKind kind, this.function = function; } - @Override public SqlOperandMetadata getOperandTypeChecker() { - return (SqlOperandMetadata) super.getOperandTypeChecker(); + @Override public @Nullable SqlOperandMetadata getOperandTypeChecker() { + return (@Nullable SqlOperandMetadata) super.getOperandTypeChecker(); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java index a5d9647d1931..b8717a60215c 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java @@ -29,6 +29,8 @@ import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -44,7 +46,7 @@ public class SqlUserDefinedFunction extends SqlFunction { public SqlUserDefinedFunction(SqlIdentifier opName, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlOperandTypeChecker operandTypeChecker, List paramTypes, Function function) { this(opName, SqlKind.OTHER_FUNCTION, returnTypeInference, @@ -58,7 +60,7 @@ public SqlUserDefinedFunction(SqlIdentifier opName, public SqlUserDefinedFunction(SqlIdentifier opName, SqlKind kind, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandMetadata operandMetadata, + @Nullable SqlOperandMetadata operandMetadata, Function function) { this(opName, kind, returnTypeInference, operandTypeInference, operandMetadata, function, SqlFunctionCategory.USER_DEFINED_FUNCTION); @@ -68,7 +70,7 @@ public SqlUserDefinedFunction(SqlIdentifier opName, SqlKind kind, protected SqlUserDefinedFunction(SqlIdentifier opName, SqlKind kind, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandMetadata operandMetadata, + @Nullable SqlOperandMetadata operandMetadata, Function function, SqlFunctionCategory category) { super(Util.last(opName.names), opName, kind, returnTypeInference, @@ -76,8 +78,8 @@ protected SqlUserDefinedFunction(SqlIdentifier opName, SqlKind kind, this.function = function; } - @Override public SqlOperandMetadata getOperandTypeChecker() { - return (SqlOperandMetadata) super.getOperandTypeChecker(); + @Override public @Nullable SqlOperandMetadata getOperandTypeChecker() { + return (@Nullable SqlOperandMetadata) super.getOperandTypeChecker(); } /** diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java index 1afd7fc8a552..9bdef0df2e6e 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java @@ -29,6 +29,8 @@ import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; @@ -44,7 +46,7 @@ public class SqlUserDefinedTableFunction extends SqlUserDefinedFunction public SqlUserDefinedTableFunction(SqlIdentifier opName, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, + @Nullable SqlOperandTypeChecker operandTypeChecker, List paramTypes, // no longer used TableFunction function) { this(opName, SqlKind.OTHER_FUNCTION, returnTypeInference, @@ -58,7 +60,7 @@ public SqlUserDefinedTableFunction(SqlIdentifier opName, public SqlUserDefinedTableFunction(SqlIdentifier opName, SqlKind kind, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandMetadata operandMetadata, + @Nullable SqlOperandMetadata operandMetadata, TableFunction function) { super(opName, kind, returnTypeInference, operandTypeInference, operandMetadata, function, diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java index 82deb8f55521..670810ac85f7 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java @@ -35,6 +35,8 @@ import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -52,7 +54,7 @@ public class SqlUserDefinedTableMacro extends SqlFunction public SqlUserDefinedTableMacro(SqlIdentifier opName, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, List paramTypes, + @Nullable SqlOperandTypeChecker operandTypeChecker, List paramTypes, TableMacro tableMacro) { this(opName, SqlKind.OTHER_FUNCTION, returnTypeInference, operandTypeInference, @@ -65,7 +67,7 @@ public SqlUserDefinedTableMacro(SqlIdentifier opName, public SqlUserDefinedTableMacro(SqlIdentifier opName, SqlKind kind, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandMetadata operandMetadata, + @Nullable SqlOperandMetadata operandMetadata, TableMacro tableMacro) { super(Util.last(opName.names), opName, kind, returnTypeInference, operandTypeInference, operandMetadata, @@ -73,8 +75,8 @@ public SqlUserDefinedTableMacro(SqlIdentifier opName, SqlKind kind, this.tableMacro = tableMacro; } - @Override public SqlOperandMetadata getOperandTypeChecker() { - return (SqlOperandMetadata) super.getOperandTypeChecker(); + @Override public @Nullable SqlOperandMetadata getOperandTypeChecker() { + return (@Nullable SqlOperandMetadata) super.getOperandTypeChecker(); } @SuppressWarnings("deprecation") diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java index cd563cbac26b..a650c17397cb 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java @@ -49,11 +49,12 @@ import org.apache.calcite.util.ImmutableBeans; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import java.util.List; import java.util.Map; import java.util.function.UnaryOperator; -import javax.annotation.Nullable; /** * Validates the parse tree of a SQL statement, and provides semantic @@ -118,6 +119,7 @@ public interface SqlValidator { * * @return catalog reader */ + @Pure SqlValidatorCatalogReader getCatalogReader(); /** @@ -125,6 +127,7 @@ public interface SqlValidator { * * @return operator table */ + @Pure SqlOperatorTable getOperatorTable(); /** @@ -168,7 +171,7 @@ SqlNode validateParameterizedExpression( * type 'unknown'. * @throws RuntimeException if the query is not valid */ - void validateQuery(SqlNode node, SqlValidatorScope scope, + void validateQuery(SqlNode node, @Nullable SqlValidatorScope scope, RelDataType targetRowType); /** @@ -188,7 +191,7 @@ void validateQuery(SqlNode node, SqlValidatorScope scope, * @param node the node of interest * @return validated type, or null if unknown or not applicable */ - RelDataType getValidatedNodeTypeIfKnown(SqlNode node); + @Nullable RelDataType getValidatedNodeTypeIfKnown(SqlNode node); /** * Resolves an identifier to a fully-qualified name. @@ -297,8 +300,8 @@ void validateCall( * or null * @param scope Syntactic scope */ - void validateAggregateParams(SqlCall aggCall, SqlNode filter, - SqlNodeList orderList, SqlValidatorScope scope); + void validateAggregateParams(SqlCall aggCall, @Nullable SqlNode filter, + @Nullable SqlNodeList orderList, SqlValidatorScope scope); /** * Validates a COLUMN_LIST parameter. @@ -414,7 +417,7 @@ default SqlWindow resolveWindow( * @param node Parse tree node * @return namespace of node */ - SqlValidatorNamespace getNamespace(SqlNode node); + @Nullable SqlValidatorNamespace getNamespace(SqlNode node); /** * Derives an alias for an expression. If no alias can be derived, returns @@ -426,7 +429,7 @@ default SqlWindow resolveWindow( * @return derived alias, or null if no alias can be derived and ordinal is * less than zero */ - String deriveAlias( + @Nullable String deriveAlias( SqlNode node, int ordinal); @@ -459,6 +462,7 @@ SqlNodeList expandStar( * * @return type factory */ + @Pure RelDataTypeFactory getTypeFactory(); /** @@ -532,7 +536,7 @@ void setValidatedNodeType( * @param select SELECT statement * @return naming scope for SELECT statement, sans any aggregating scope */ - SelectScope getRawSelectScope(SqlSelect select); + @Nullable SelectScope getRawSelectScope(SqlSelect select); /** * Returns a scope containing the objects visible from the FROM clause of a @@ -541,7 +545,7 @@ void setValidatedNodeType( * @param select SELECT statement * @return naming scope for FROM clause */ - SqlValidatorScope getFromScope(SqlSelect select); + @Nullable SqlValidatorScope getFromScope(SqlSelect select); /** * Returns a scope containing the objects visible from the ON and USING @@ -552,7 +556,7 @@ void setValidatedNodeType( * @return naming scope for JOIN clause * @see #getFromScope */ - SqlValidatorScope getJoinScope(SqlNode node); + @Nullable SqlValidatorScope getJoinScope(SqlNode node); /** * Returns a scope containing the objects visible from the GROUP BY clause @@ -616,7 +620,7 @@ void setValidatedNodeType( * @param columnListParamName name of the column list parameter * @return name of the parent cursor */ - String getParentCursor(String columnListParamName); + @Nullable String getParentCursor(String columnListParamName); /** * Derives the type of a constructor. @@ -632,7 +636,7 @@ RelDataType deriveConstructorType( SqlValidatorScope scope, SqlCall call, SqlFunction unresolvedConstructor, - SqlFunction resolvedConstructor, + @Nullable SqlFunction resolvedConstructor, List argTypes); /** @@ -647,7 +651,7 @@ RelDataType deriveConstructorType( */ CalciteException handleUnresolvedFunction(SqlCall call, SqlFunction unresolvedFunction, List argTypes, - List argNames); + @Nullable List argNames); /** * Expands an expression in the ORDER BY clause into an expression with the @@ -702,7 +706,7 @@ CalciteException handleUnresolvedFunction(SqlCall call, * @return Description of how each field in the row type maps to a schema * object */ - List> getFieldOrigins(SqlNode sqlQuery); + List<@Nullable List> getFieldOrigins(SqlNode sqlQuery); /** * Returns a record type that contains the name and type of each parameter. @@ -740,7 +744,7 @@ boolean validateModality(SqlSelect select, SqlModality modality, void validateSequenceValue(SqlValidatorScope scope, SqlIdentifier id); - SqlValidatorScope getWithScope(SqlNode withItem); + @Nullable SqlValidatorScope getWithScope(SqlNode withItem); /** Get the type coercion instance. */ TypeCoercion getTypeCoercion(); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java index 1dea5800550c..9cfb3f89c6f4 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorCatalogReader.java @@ -23,8 +23,11 @@ import org.apache.calcite.schema.Wrapper; import org.apache.calcite.sql.SqlIdentifier; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; + /** * Supplies catalog information for {@link SqlValidator}. * @@ -50,7 +53,7 @@ public interface SqlValidatorCatalogReader extends Wrapper { * * @return Table with the given name, or null */ - SqlValidatorTable getTable(List names); + @Nullable SqlValidatorTable getTable(List names); /** * Finds a user-defined type with the given name, possibly qualified. @@ -63,7 +66,7 @@ public interface SqlValidatorCatalogReader extends Wrapper { * @param typeName Name of type * @return named type, or null if not found */ - RelDataType getNamedType(SqlIdentifier typeName); + @Nullable RelDataType getNamedType(SqlIdentifier typeName); /** * Given fully qualified schema name, returns schema object names as @@ -88,7 +91,7 @@ public interface SqlValidatorCatalogReader extends Wrapper { /** @deprecated Use * {@link #nameMatcher()}.{@link SqlNameMatcher#field(RelDataType, String)} */ @Deprecated // to be removed before 2.0 - RelDataTypeField field(RelDataType rowType, String alias); + @Nullable RelDataTypeField field(RelDataType rowType, String alias); /** Returns an implementation of * {@link org.apache.calcite.sql.validate.SqlNameMatcher} diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorException.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorException.java index b2bd9c6146a6..12fbdbb3190d 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorException.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorException.java @@ -50,6 +50,7 @@ public class SqlValidatorException extends Exception * @param message error message * @param cause underlying cause */ + @SuppressWarnings({"argument.type.incompatible", "method.invocation.invalid"}) public SqlValidatorException( String message, Throwable cause) { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java index 1c823d1fcba4..753abb4bcb33 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java @@ -113,6 +113,11 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.KeyFor; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.dataflow.qual.Pure; import org.slf4j.Logger; import java.math.BigDecimal; @@ -137,12 +142,17 @@ import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.sql.SqlUtil.stripAs; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation; +import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getCondition; +import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getTable; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Default implementation of {@link SqlValidator}. */ @@ -194,7 +204,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { /** * The name-resolution scope of a LATERAL TABLE clause. */ - private TableScope tableScope = null; + private @Nullable TableScope tableScope = null; /** * Maps a {@link SqlNode node} to the @@ -243,7 +253,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { private final Map originalExprs = new HashMap<>(); - private SqlNode top; + private @Nullable SqlNode top; // TODO jvs 11-Dec-2008: make this local to performUnconditionalRewrites // if it's OK to expand the signature of that method. @@ -272,10 +282,10 @@ protected SqlValidatorImpl( SqlValidatorCatalogReader catalogReader, RelDataTypeFactory typeFactory, Config config) { - this.opTab = Objects.requireNonNull(opTab); - this.catalogReader = Objects.requireNonNull(catalogReader); - this.typeFactory = Objects.requireNonNull(typeFactory); - this.config = Objects.requireNonNull(config); + this.opTab = requireNonNull(opTab); + this.catalogReader = requireNonNull(catalogReader); + this.typeFactory = requireNonNull(typeFactory); + this.config = requireNonNull(config); unknownType = typeFactory.createUnknownType(); booleanType = typeFactory.createSqlType(SqlTypeName.BOOLEAN); @@ -289,7 +299,9 @@ protected SqlValidatorImpl( groupFinder = new AggFinder(opTab, false, false, true, null, nameMatcher); aggOrOverOrGroupFinder = new AggFinder(opTab, true, true, true, null, nameMatcher); - this.typeCoercion = config.typeCoercionFactory().create(typeFactory, this); + @SuppressWarnings("argument.type.incompatible") + TypeCoercion typeCoercion = config.typeCoercionFactory().create(typeFactory, this); + this.typeCoercion = typeCoercion; if (config.typeCoercionRules() != null) { SqlTypeCoercionRule.THREAD_PROVIDERS.set(config.typeCoercionRules()); } @@ -301,14 +313,17 @@ public SqlConformance getConformance() { return config.sqlConformance(); } + @Pure @Override public SqlValidatorCatalogReader getCatalogReader() { return catalogReader; } + @Pure @Override public SqlOperatorTable getOperatorTable() { return opTab; } + @Pure @Override public RelDataTypeFactory getTypeFactory() { return typeFactory; } @@ -335,7 +350,7 @@ public SqlConformance getConformance() { types, includeSystemVars); } - getRawSelectScope(select).setExpandedSelectList(list); + getRawSelectScopeNonNull(select).setExpandedSelectList(list); return new SqlNodeList(list, SqlParserPos.ZERO); } @@ -345,7 +360,7 @@ public SqlConformance getConformance() { // add the cursor to a map that maps the cursor to its select based on // the position of the cursor relative to other cursors in that call - FunctionParamInfo funcParamInfo = functionCallStack.peek(); + FunctionParamInfo funcParamInfo = requireNonNull(functionCallStack.peek(), "functionCall"); Map cursorMap = funcParamInfo.cursorPosToSelectMap; int numCursors = cursorMap.size(); cursorMap.put(numCursors, select); @@ -372,8 +387,8 @@ public SqlConformance getConformance() { } // implement SqlValidator - @Override public String getParentCursor(String columnListParamName) { - FunctionParamInfo funcParamInfo = functionCallStack.peek(); + @Override public @Nullable String getParentCursor(String columnListParamName) { + FunctionParamInfo funcParamInfo = requireNonNull(functionCallStack.peek(), "functionCall"); Map parentCursorMap = funcParamInfo.columnListParamToParentCursorMap; return parentCursorMap.get(columnListParamName); @@ -410,7 +425,7 @@ private boolean expandSelectItem( // calls. SqlNode expanded = expandSelectExpr(selectItem, scope, select); final String alias = - deriveAlias( + deriveAliasNonNull( selectItem, aliases.size()); @@ -418,10 +433,10 @@ private boolean expandSelectItem( final SqlValidatorScope selectScope = getSelectScope(select); if (expanded != selectItem) { String newAlias = - deriveAlias( + deriveAliasNonNull( expanded, aliases.size()); - if (!newAlias.equals(alias)) { + if (!Objects.equals(newAlias, alias)) { expanded = SqlStdOperatorTable.AS.createCall( selectItem.getParserPosition(), @@ -444,16 +459,16 @@ private boolean expandSelectItem( } private static SqlNode expandExprFromJoin(SqlJoin join, SqlIdentifier identifier, - SelectScope scope) { + @Nullable SelectScope scope) { if (join.getConditionType() != JoinConditionType.USING) { return identifier; } - for (SqlNode node : (SqlNodeList) join.getCondition()) { + for (SqlNode node : (SqlNodeList) getCondition(join)) { final String name = ((SqlIdentifier) node).getSimple(); if (identifier.getSimple().equals(name)) { final List qualifiedNode = new ArrayList<>(); - for (ScopeChild child : scope.children) { + for (ScopeChild child : requireNonNull(scope, "scope").children) { if (child.namespace.getRowType() .getFieldNames().indexOf(name) >= 0) { final SqlIdentifier exp = @@ -487,12 +502,12 @@ private static SqlNode expandExprFromJoin(SqlJoin join, SqlIdentifier identifier /** Returns the set of field names in the join condition specified by USING * or implicitly by NATURAL, de-duplicated and in order. */ - public List usingNames(SqlJoin join) { + public @Nullable List usingNames(SqlJoin join) { switch (join.getConditionType()) { case USING: final ImmutableList.Builder list = ImmutableList.builder(); final Set names = catalogReader.nameMatcher().createSet(); - for (SqlNode node : (SqlNodeList) join.getCondition()) { + for (SqlNode node : (SqlNodeList) getCondition(join)) { final String name = ((SqlIdentifier) node).getSimple(); if (names.add(name)) { list.add(name); @@ -514,7 +529,7 @@ public List usingNames(SqlJoin join) { } private static SqlNode expandCommonColumn(SqlSelect sqlSelect, - SqlNode selectItem, SelectScope scope, SqlValidatorImpl validator) { + SqlNode selectItem, @Nullable SelectScope scope, SqlValidatorImpl validator) { if (!(selectItem instanceof SqlIdentifier)) { return selectItem; } @@ -536,18 +551,19 @@ private static SqlNode expandCommonColumn(SqlSelect sqlSelect, } private static void validateQualifiedCommonColumn(SqlJoin join, - SqlIdentifier identifier, SelectScope scope, SqlValidatorImpl validator) { + SqlIdentifier identifier, @Nullable SelectScope scope, SqlValidatorImpl validator) { List names = validator.usingNames(join); if (names == null) { // Not USING or NATURAL. return; } + requireNonNull(scope, "scope"); // First we should make sure that the first component is the table name. // Then check whether the qualified identifier contains common column. for (ScopeChild child : scope.children) { - if (child.name.equals(identifier.getComponent(0).toString())) { - if (names.indexOf(identifier.getComponent(1).toString()) >= 0) { + if (Objects.equals(child.name, identifier.getComponent(0).toString())) { + if (names.contains(identifier.getComponent(1).toString())) { throw validator.newValidationError(identifier, RESOURCE.disallowsQualifyingCommonColumn(identifier.toString())); } @@ -596,9 +612,8 @@ private boolean expandStar(List selectItems, Set aliases, scope, includeSystemVars); } else { - final SqlNode from = child.namespace.getNode(); - final SqlValidatorNamespace fromNs = getNamespace(from, scope); - assert fromNs != null; + final SqlNode from = SqlNonNullableAccessors.getNode(child); + final SqlValidatorNamespace fromNs = getNamespaceOrThrow(from, scope); final RelDataType rowType = fromNs.getRowType(); for (RelDataTypeField field : rowType.getFieldList()) { String columnName = field.getName(); @@ -636,7 +651,9 @@ private boolean expandStar(List selectItems, Set aliases, // If NATURAL JOIN or USING is present, move key fields to the front of // the list, per standard SQL. Disabled if there are dynamic fields. if (!hasDynamicStruct || Bug.CALCITE_2400_FIXED) { - new Permute(scope.getNode().getFrom(), 0).permute(selectItems, fields); + SqlNode from = requireNonNull(scope.getNode().getFrom(), + () -> "getFrom for " + scope.getNode()); + new Permute(from, 0).permute(selectItems, fields); } return true; @@ -752,7 +769,7 @@ private boolean addOrExpandField(List selectItems, Set aliases, return ImmutableList.copyOf(hintList); } - @Override public SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos) { + @Override public @Nullable SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos) { final String posString = pos.toString(); IdInfo info = idPositions.get(posString); if (info != null) { @@ -799,15 +816,15 @@ private void lookupSelectHints( } private void lookupFromHints( - SqlNode node, - SqlValidatorScope scope, + @Nullable SqlNode node, + @Nullable SqlValidatorScope scope, SqlParserPos pos, Collection hintList) { if (node == null) { // This can happen in cases like "select * _suggest_", so from clause is absent return; } - final SqlValidatorNamespace ns = getNamespace(node); + final SqlValidatorNamespace ns = getNamespaceOrThrow(node); if (ns.isWrapperFor(IdentifierNamespace.class)) { IdentifierNamespace idNs = ns.unwrap(IdentifierNamespace.class); final SqlIdentifier id = idNs.getId(); @@ -840,7 +857,7 @@ private void lookupFromHints( private void lookupJoinHints( SqlJoin join, - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlParserPos pos, Collection hintList) { SqlNode left = join.getLeft(); @@ -855,10 +872,12 @@ private void lookupJoinHints( return; } final JoinConditionType conditionType = join.getConditionType(); - final SqlValidatorScope joinScope = scopes.get(join); switch (conditionType) { case ON: - condition.findValidOptions(this, joinScope, pos, hintList); + requireNonNull(condition, () -> "join.getCondition() for " + join) + .findValidOptions(this, + getScopeOrThrow(join), + pos, hintList); return; default: @@ -1026,9 +1045,9 @@ private SqlNode validateScopedExpression( return outermostNode; } - @Override public void validateQuery(SqlNode node, SqlValidatorScope scope, + @Override public void validateQuery(SqlNode node, @Nullable SqlValidatorScope scope, RelDataType targetRowType) { - final SqlValidatorNamespace ns = getNamespace(node, scope); + final SqlValidatorNamespace ns = getNamespaceOrThrow(node, scope); if (node.getKind() == SqlKind.TABLESAMPLE) { List operands = ((SqlCall) node).getOperandList(); SqlSampleSpec sampleSpec = SqlLiteral.sampleValue(operands.get(1)); @@ -1045,7 +1064,7 @@ private SqlNode validateScopedExpression( switch (node.getKind()) { case EXTEND: // Until we have a dedicated namespace for EXTEND - deriveType(scope, node); + deriveType(requireNonNull(scope, "scope"), node); break; default: break; @@ -1071,8 +1090,9 @@ private SqlNode validateScopedExpression( protected void validateNamespace(final SqlValidatorNamespace namespace, RelDataType targetRowType) { namespace.validate(targetRowType); - if (namespace.getNode() != null) { - setValidatedNodeType(namespace.getNode(), namespace.getType()); + SqlNode node = namespace.getNode(); + if (node != null) { + setValidatedNodeType(node, namespace.getType()); } } @@ -1081,58 +1101,73 @@ public SqlValidatorScope getEmptyScope() { return new EmptyScope(this); } + private SqlValidatorScope getScope(SqlSelect select, Clause clause) { + return requireNonNull( + clauseScopes.get(IdPair.of(select, clause)), + () -> "no " + clause + " scope for " + select); + } + public SqlValidatorScope getCursorScope(SqlSelect select) { - return clauseScopes.get(IdPair.of(select, Clause.CURSOR)); + return getScope(select, Clause.CURSOR); } @Override public SqlValidatorScope getWhereScope(SqlSelect select) { - return clauseScopes.get(IdPair.of(select, Clause.WHERE)); + return getScope(select, Clause.WHERE); } @Override public SqlValidatorScope getSelectScope(SqlSelect select) { - return clauseScopes.get(IdPair.of(select, Clause.SELECT)); + return getScope(select, Clause.SELECT); } - @Override public SelectScope getRawSelectScope(SqlSelect select) { - SqlValidatorScope scope = getSelectScope(select); + @Override public @Nullable SelectScope getRawSelectScope(SqlSelect select) { + SqlValidatorScope scope = clauseScopes.get(IdPair.of(select, Clause.SELECT)); if (scope instanceof AggregatingSelectScope) { scope = ((AggregatingSelectScope) scope).getParent(); } return (SelectScope) scope; } + private SelectScope getRawSelectScopeNonNull(SqlSelect select) { + return requireNonNull(getRawSelectScope(select), + () -> "getRawSelectScope for " + select); + } + @Override public SqlValidatorScope getHavingScope(SqlSelect select) { // Yes, it's the same as getSelectScope - return clauseScopes.get(IdPair.of(select, Clause.SELECT)); + return getScope(select, Clause.SELECT); } @Override public SqlValidatorScope getGroupScope(SqlSelect select) { // Yes, it's the same as getWhereScope - return clauseScopes.get(IdPair.of(select, Clause.GROUP_BY)); + return getScope(select, Clause.WHERE); } - @Override public SqlValidatorScope getFromScope(SqlSelect select) { + @Override public @Nullable SqlValidatorScope getFromScope(SqlSelect select) { return scopes.get(select); } @Override public SqlValidatorScope getOrderScope(SqlSelect select) { - return clauseScopes.get(IdPair.of(select, Clause.ORDER)); + return getScope(select, Clause.ORDER); } @Override public SqlValidatorScope getMatchRecognizeScope(SqlMatchRecognize node) { - return scopes.get(node); + return getScopeOrThrow(node); } - @Override public SqlValidatorScope getJoinScope(SqlNode node) { + @Override public @Nullable SqlValidatorScope getJoinScope(SqlNode node) { return scopes.get(stripAs(node)); } @Override public SqlValidatorScope getOverScope(SqlNode node) { - return scopes.get(node); + return getScopeOrThrow(node); } - private SqlValidatorNamespace getNamespace(SqlNode node, - SqlValidatorScope scope) { + private SqlValidatorScope getScopeOrThrow(SqlNode node) { + return requireNonNull(scopes.get(node), () -> "scope for " + node); + } + + private @Nullable SqlValidatorNamespace getNamespace(SqlNode node, + @Nullable SqlValidatorScope scope) { if (node instanceof SqlIdentifier && scope instanceof DelegatingScope) { final SqlIdentifier id = (SqlIdentifier) node; final DelegatingScope idScope = (DelegatingScope) ((DelegatingScope) scope).getParent(); @@ -1167,12 +1202,14 @@ private SqlValidatorNamespace getNamespace(SqlNode node, return getNamespace(node); } - private SqlValidatorNamespace getNamespace(SqlIdentifier id, DelegatingScope scope) { + private @Nullable SqlValidatorNamespace getNamespace(SqlIdentifier id, + @Nullable DelegatingScope scope) { if (id.isSimple()) { final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); final SqlValidatorScope.ResolvedImpl resolved = new SqlValidatorScope.ResolvedImpl(); - scope.resolve(id.names, nameMatcher, false, resolved); + requireNonNull(scope, () -> "scope needed to lookup " + id) + .resolve(id.names, nameMatcher, false, resolved); if (resolved.count() == 1) { return resolved.only().namespace; } @@ -1180,7 +1217,7 @@ private SqlValidatorNamespace getNamespace(SqlIdentifier id, DelegatingScope sco return getNamespace(id); } - @Override public SqlValidatorNamespace getNamespace(SqlNode node) { + @Override public @Nullable SqlValidatorNamespace getNamespace(SqlNode node) { switch (node.getKind()) { case AS: @@ -1202,7 +1239,50 @@ private SqlValidatorNamespace getNamespace(SqlIdentifier id, DelegatingScope sco } } - private void handleOffsetFetch(SqlNode offset, SqlNode fetch) { + /** + * Namespace for the given node. + * @param node node to compute the namespace for + * @return namespace for the given node, never null + * @see #getNamespace(SqlNode) + */ + @API(since = "1.27", status = API.Status.INTERNAL) + SqlValidatorNamespace getNamespaceOrThrow(SqlNode node) { + return requireNonNull( + getNamespace(node), + () -> "namespace for " + node); + } + + /** + * Namespace for the given node. + * @param node node to compute the namespace for + * @param scope namespace scope + * @return namespace for the given node, never null + * @see #getNamespace(SqlNode) + */ + @API(since = "1.27", status = API.Status.INTERNAL) + SqlValidatorNamespace getNamespaceOrThrow(SqlNode node, + @Nullable SqlValidatorScope scope) { + return requireNonNull( + getNamespace(node, scope), + () -> "namespace for " + node + ", scope " + scope); + } + + /** + * Namespace for the given node. + * @param id identifier to resolve + * @param scope namespace scope + * @return namespace for the given node, never null + * @see #getNamespace(SqlIdentifier, DelegatingScope) + */ + @API(since = "1.26", status = API.Status.INTERNAL) + SqlValidatorNamespace getNamespaceOrThrow(SqlIdentifier id, + @Nullable DelegatingScope scope) { + return requireNonNull( + getNamespace(id, scope), + () -> "namespace for " + id + ", scope " + scope); + } + + private void handleOffsetFetch(@Nullable SqlNode offset, @Nullable SqlNode fetch) { if (offset instanceof SqlDynamicParam) { setValidatedNodeType(offset, typeFactory.createSqlType(SqlTypeName.INTEGER)); @@ -1222,8 +1302,8 @@ private void handleOffsetFetch(SqlNode offset, SqlNode fetch) { * @param underFrom whether node appears directly under a FROM clause * @return rewritten expression */ - protected SqlNode performUnconditionalRewrites( - SqlNode node, + protected @PolyNull SqlNode performUnconditionalRewrites( + @PolyNull SqlNode node, boolean underFrom) { if (node == null) { return null; @@ -1343,13 +1423,14 @@ protected SqlNode performUnconditionalRewrites( final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO); selectList.add(SqlIdentifier.star(SqlParserPos.ZERO)); final SqlNodeList orderList; - if (getInnerSelect(node) != null && isAggregate(getInnerSelect(node))) { + SqlSelect innerSelect = getInnerSelect(node); + if (innerSelect != null && isAggregate(innerSelect)) { orderList = SqlNode.clone(orderBy.orderList); // We assume that ORDER BY item does not have ASC etc. // We assume that ORDER BY item is present in SELECT list. for (int i = 0; i < orderList.size(); i++) { SqlNode sqlNode = orderList.get(i); - SqlNodeList selectList2 = getInnerSelect(node).getSelectList(); + SqlNodeList selectList2 = SqlNonNullableAccessors.getSelectList(innerSelect); for (Ord sel : Ord.zip(selectList2)) { if (stripAs(sel.e).equalsDeep(sqlNode, Litmus.IGNORE)) { orderList.set(i, @@ -1413,7 +1494,7 @@ protected SqlNode performUnconditionalRewrites( return node; } - private SqlSelect getInnerSelect(SqlNode node) { + private @Nullable SqlSelect getInnerSelect(SqlNode node) { for (;;) { if (node instanceof SqlSelect) { return (SqlSelect) node; @@ -1435,7 +1516,8 @@ private void rewriteMerge(SqlMerge call) { // from the update statement's source since it's the same as // what we want for the select list of the merge source -- '*' // followed by the update set expressions - selectList = SqlNode.clone(updateStmt.getSourceSelect().getSelectList()); + SqlSelect sourceSelect = SqlNonNullableAccessors.getSourceSelect(updateStmt); + selectList = SqlNode.clone(SqlNonNullableAccessors.getSelectList(sourceSelect)); } else { // otherwise, just use select * selectList = new SqlNodeList(SqlParserPos.ZERO); @@ -1495,14 +1577,15 @@ private SqlNode rewriteUpdateToMerge( SqlUpdate updateCall, SqlNode selfJoinSrcExpr) { // Make sure target has an alias. - if (updateCall.getAlias() == null) { - updateCall.setAlias( - new SqlIdentifier(UPDATE_TGT_ALIAS, SqlParserPos.ZERO)); + SqlIdentifier updateAlias = updateCall.getAlias(); + if (updateAlias == null) { + updateAlias = new SqlIdentifier(UPDATE_TGT_ALIAS, SqlParserPos.ZERO); + updateCall.setAlias(updateAlias); } SqlNode selfJoinTgtExpr = getSelfJoinExprForUpdate( updateCall.getTargetTable(), - updateCall.getAlias().getSimple()); + updateAlias.getSimple()); assert selfJoinTgtExpr != null; // Create join condition between source and target exprs, @@ -1535,7 +1618,8 @@ private SqlNode rewriteUpdateToMerge( // target because downstream, the optimizer rules // don't want to see any projection on top of the target. IdentifierNamespace ns = - new IdentifierNamespace(this, target, null, null); + new IdentifierNamespace(this, target, null, + castNonNull(null)); RelDataType rowType = ns.getRowType(); SqlNode source = updateCall.getTargetTable().clone(SqlParserPos.ZERO); final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO); @@ -1574,7 +1658,7 @@ private SqlNode rewriteUpdateToMerge( * number. * @return expression for unique identifier, or null to prevent conversion */ - protected SqlNode getSelfJoinExprForUpdate( + protected @Nullable SqlNode getSelfJoinExprForUpdate( SqlNode table, String alias) { return null; @@ -1599,11 +1683,12 @@ protected SqlSelect createSourceSelectForUpdate(SqlUpdate call) { ++ordinal; } SqlNode sourceTable = call.getTargetTable(); - if (call.getAlias() != null) { + SqlIdentifier alias = call.getAlias(); + if (alias != null) { sourceTable = SqlValidatorUtil.addAlias( sourceTable, - call.getAlias().getSimple()); + alias.getSimple()); } return new SqlSelect(SqlParserPos.ZERO, null, selectList, sourceTable, call.getCondition(), null, null, null, null, null, null, null); @@ -1620,11 +1705,12 @@ protected SqlSelect createSourceSelectForDelete(SqlDelete call) { final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO); selectList.add(SqlIdentifier.star(SqlParserPos.ZERO)); SqlNode sourceTable = call.getTargetTable(); - if (call.getAlias() != null) { + SqlIdentifier alias = call.getAlias(); + if (alias != null) { sourceTable = SqlValidatorUtil.addAlias( sourceTable, - call.getAlias().getSimple()); + alias.getSimple()); } return new SqlSelect(SqlParserPos.ZERO, null, selectList, sourceTable, call.getCondition(), null, null, null, null, null, null, null); @@ -1634,7 +1720,7 @@ protected SqlSelect createSourceSelectForDelete(SqlDelete call) { * Returns null if there is no common type. E.g. if the rows have a * different number of columns. */ - RelDataType getTableConstructorRowType( + @Nullable RelDataType getTableConstructorRowType( SqlCall values, SqlValidatorScope scope) { final List rows = values.getOperandList(); @@ -1649,7 +1735,7 @@ RelDataType getTableConstructorRowType( final List aliasList = new ArrayList<>(); final List typeList = new ArrayList<>(); for (Ord column : Ord.zip(rowConstructor.getOperandList())) { - final String alias = deriveAlias(column.e, column.i); + final String alias = deriveAliasNonNull(column.e, column.i); aliasList.add(alias); final RelDataType type = deriveType(scope, column.e); typeList.add(type); @@ -1673,7 +1759,7 @@ RelDataType getTableConstructorRowType( } } - @Override public RelDataType getValidatedNodeTypeIfKnown(SqlNode node) { + @Override public @Nullable RelDataType getValidatedNodeTypeIfKnown(SqlNode node) { final RelDataType type = nodeToTypeMap.get(node); if (type != null) { return type; @@ -1702,8 +1788,8 @@ RelDataType getTableConstructorRowType( * @param type Its type; must not be null */ @Override public final void setValidatedNodeType(SqlNode node, RelDataType type) { - Objects.requireNonNull(type); - Objects.requireNonNull(node); + requireNonNull(type); + requireNonNull(node); if (type.equals(unknownType)) { // don't set anything until we know what it is, and don't overwrite // a known type with the unknown type @@ -1716,7 +1802,7 @@ RelDataType getTableConstructorRowType( nodeToTypeMap.remove(node); } - @Override @Nullable public SqlCall makeNullaryCall(SqlIdentifier id) { + @Override public @Nullable SqlCall makeNullaryCall(SqlIdentifier id) { if (id.names.size() == 1 && !id.isComponentQuoted(0)) { final List list = new ArrayList<>(); opTab.lookupOperatorOverloads(id, null, SqlSyntax.FUNCTION, list, @@ -1738,8 +1824,8 @@ RelDataType getTableConstructorRowType( @Override public RelDataType deriveType( SqlValidatorScope scope, SqlNode expr) { - Objects.requireNonNull(scope); - Objects.requireNonNull(expr); + requireNonNull(scope); + requireNonNull(expr); // if we already know the type, no need to re-derive RelDataType type = nodeToTypeMap.get(expr); @@ -1766,14 +1852,14 @@ RelDataType deriveTypeImpl( SqlNode operand) { DeriveTypeVisitor v = new DeriveTypeVisitor(scope); final RelDataType type = operand.accept(v); - return Objects.requireNonNull(scope.nullifyType(operand, type)); + return requireNonNull(scope.nullifyType(operand, type)); } @Override public RelDataType deriveConstructorType( SqlValidatorScope scope, SqlCall call, SqlFunction unresolvedConstructor, - SqlFunction resolvedConstructor, + @Nullable SqlFunction resolvedConstructor, List argTypes) { SqlIdentifier sqlIdentifier = unresolvedConstructor.getSqlIdentifier(); assert sqlIdentifier != null; @@ -1811,7 +1897,7 @@ RelDataType deriveTypeImpl( // fake a fully-qualified call to the default constructor ((SqlBasicCall) call).setOperator( new SqlFunction( - type.getSqlIdentifier(), + requireNonNull(type.getSqlIdentifier(), () -> "sqlIdentifier of " + type), ReturnTypes.explicit(type), null, null, @@ -1824,7 +1910,7 @@ RelDataType deriveTypeImpl( @Override public CalciteException handleUnresolvedFunction(SqlCall call, SqlFunction unresolvedFunction, List argTypes, - List argNames) { + @Nullable List argNames) { // For builtins, we can give a better error message final List overloads = new ArrayList<>(); opTab.lookupOperatorOverloads(unresolvedFunction.getNameAsId(), null, @@ -1852,12 +1938,12 @@ RelDataType deriveTypeImpl( } protected void inferUnknownTypes( - @Nonnull RelDataType inferredType, - @Nonnull SqlValidatorScope scope, - @Nonnull SqlNode node) { - Objects.requireNonNull(inferredType); - Objects.requireNonNull(scope); - Objects.requireNonNull(node); + RelDataType inferredType, + SqlValidatorScope scope, + SqlNode node) { + requireNonNull(inferredType); + requireNonNull(scope); + requireNonNull(node); final SqlValidatorScope newScope = scopes.get(node); if (newScope != null) { scope = newScope; @@ -1885,8 +1971,8 @@ protected void inferUnknownTypes( newInferredType = typeFactory.createTypeWithCharsetAndCollation( newInferredType, - inferredType.getCharset(), - inferredType.getCollation()); + getCharset(inferredType), + getCollation(inferredType)); } setValidatedNodeType(node, newInferredType); } else if (node instanceof SqlNodeList) { @@ -1923,13 +2009,16 @@ protected void inferUnknownTypes( inferUnknownTypes(returnType, scope, sqlNode); } - if (!SqlUtil.isNullLiteral(caseCall.getElseOperand(), false)) { + SqlNode elseOperand = requireNonNull( + caseCall.getElseOperand(), + () -> "elseOperand for " + caseCall); + if (!SqlUtil.isNullLiteral(elseOperand, false)) { inferUnknownTypes( returnType, scope, - caseCall.getElseOperand()); + elseOperand); } else { - setValidatedNodeType(caseCall.getElseOperand(), returnType); + setValidatedNodeType(elseOperand, returnType); } } else if (node.getKind() == SqlKind.AS) { // For AS operator, only infer the operand not the alias @@ -1981,12 +2070,18 @@ protected void addToSelectList( list.add(exp); } - @Override public String deriveAlias( + @Override public @Nullable String deriveAlias( SqlNode node, int ordinal) { return SqlValidatorUtil.getAlias(node, ordinal); } + private String deriveAliasNonNull(SqlNode node, int ordinal) { + return requireNonNull( + deriveAlias(node, ordinal), + () -> "non-null alias expected for node = " + node + ", ordinal = " + ordinal); + } + protected boolean shouldAllowIntermediateOrderBy() { return true; } @@ -1996,7 +2091,7 @@ private void registerMatchRecognize( SqlValidatorScope usingScope, SqlMatchRecognize call, SqlNode enclosingNode, - String alias, + @Nullable String alias, boolean forceNullable) { final MatchRecognizeNamespace matchRecognizeNamespace = @@ -2027,7 +2122,7 @@ private void registerPivot( SqlValidatorScope usingScope, SqlPivot call, SqlNode enclosingNode, - String alias, + @Nullable String alias, boolean forceNullable) { final PivotNamespace namespace = createPivotNameSpace(call, enclosingNode); @@ -2063,12 +2158,14 @@ protected PivotNamespace createPivotNameSpace(SqlPivot call, * @param forceNullable Whether to force the type of namespace to be nullable */ protected void registerNamespace( - SqlValidatorScope usingScope, - String alias, + @Nullable SqlValidatorScope usingScope, + @Nullable String alias, SqlValidatorNamespace ns, boolean forceNullable) { - namespaces.put(ns.getNode(), ns); + namespaces.put(requireNonNull(ns.getNode(), () -> "ns.getNode() for " + ns), ns); if (usingScope != null) { + assert alias != null : "Registering namespace " + ns + ", into scope " + usingScope + + ", so alias must not be null"; usingScope.addChild(ns, alias, forceNullable); } } @@ -2110,8 +2207,8 @@ private SqlNode registerFrom( boolean register, final SqlNode node, SqlNode enclosingNode, - String alias, - SqlNodeList extendList, + @Nullable String alias, + @Nullable SqlNodeList extendList, boolean forceNullable, final boolean lateral) { final SqlKind kind = node.getKind(); @@ -2127,7 +2224,7 @@ private SqlNode registerFrom( case OVER: alias = deriveAlias(node, -1); if (alias == null) { - alias = deriveAlias(node, nextGeneratedId++); + alias = deriveAliasNonNull(node, nextGeneratedId++); } if (config.identifierExpansion()) { newNode = SqlValidatorUtil.addAlias(node, alias); @@ -2147,7 +2244,7 @@ private SqlNode registerFrom( // give this anonymous construct a name since later // query processing stages rely on it - alias = deriveAlias(node, nextGeneratedId++); + alias = deriveAliasNonNull(node, nextGeneratedId++); if (config.identifierExpansion()) { // Since we're expanding identifiers, we should make the // aliases explicit too, otherwise the expanded query @@ -2184,7 +2281,7 @@ private SqlNode registerFrom( case AS: call = (SqlCall) node; if (alias == null) { - alias = call.operand(1).toString(); + alias = String.valueOf(call.operand(1)); } expr = call.operand(0); final boolean needAlias = call.operandCount() > 2 @@ -2316,7 +2413,7 @@ private SqlNode registerFrom( if (tableScope == null) { tableScope = new TableScope(parentScope, node); } - tableScope.addChild(newNs, alias, forceNullable); + tableScope.addChild(newNs, requireNonNull(alias, "alias"), forceNullable); if (extendList != null && extendList.size() != 0) { return enclosingNode; } @@ -2381,7 +2478,7 @@ private SqlNode registerFrom( case WITH: case OTHER_FUNCTION: if (alias == null) { - alias = deriveAlias(node, nextGeneratedId++); + alias = deriveAliasNonNull(node, nextGeneratedId++); } registerQuery( parentScope, @@ -2522,10 +2619,10 @@ protected SetopNamespace createSetopNamespace( */ private void registerQuery( SqlValidatorScope parentScope, - SqlValidatorScope usingScope, + @Nullable SqlValidatorScope usingScope, SqlNode node, SqlNode enclosingNode, - String alias, + @Nullable String alias, boolean forceNullable) { Preconditions.checkArgument(usingScope == null || alias != null); registerQuery( @@ -2552,14 +2649,14 @@ private void registerQuery( */ private void registerQuery( SqlValidatorScope parentScope, - SqlValidatorScope usingScope, + @Nullable SqlValidatorScope usingScope, SqlNode node, SqlNode enclosingNode, - String alias, + @Nullable String alias, boolean forceNullable, boolean checkUpdate) { - Objects.requireNonNull(node); - Objects.requireNonNull(enclosingNode); + requireNonNull(node); + requireNonNull(enclosingNode); Preconditions.checkArgument(usingScope == null || alias != null); SqlCall call; @@ -2625,7 +2722,7 @@ private void registerQuery( aggScope, select, SqlSelect.HAVING_OPERAND); - registerSubQueries(aggScope, select.getSelectList()); + registerSubQueries(aggScope, SqlNonNullableAccessors.getSelectList(select)); final SqlNodeList orderList = select.getOrderList(); if (orderList != null) { // If the query is 'SELECT DISTINCT', restrict the columns @@ -2742,7 +2839,7 @@ private void registerQuery( registerQuery( parentScope, usingScope, - deleteCall.getSourceSelect(), + SqlNonNullableAccessors.getSourceSelect(deleteCall), enclosingNode, null, false); @@ -2764,7 +2861,7 @@ private void registerQuery( registerQuery( parentScope, usingScope, - updateCall.getSourceSelect(), + SqlNonNullableAccessors.getSourceSelect(updateCall), enclosingNode, null, false); @@ -2783,7 +2880,7 @@ private void registerQuery( registerQuery( parentScope, usingScope, - mergeCall.getSourceSelect(), + SqlNonNullableAccessors.getSourceSelect(mergeCall), enclosingNode, null, false); @@ -2792,21 +2889,23 @@ private void registerQuery( // or the target table, so set its parent scope to the merge's // source select; when validating the update, skip the feature // validation check - if (mergeCall.getUpdateCall() != null) { + SqlUpdate mergeUpdateCall = mergeCall.getUpdateCall(); + if (mergeUpdateCall != null) { registerQuery( - clauseScopes.get(IdPair.of(mergeCall.getSourceSelect(), Clause.WHERE)), + getScope(SqlNonNullableAccessors.getSourceSelect(mergeCall), Clause.WHERE), null, - mergeCall.getUpdateCall(), + mergeUpdateCall, enclosingNode, null, false, false); } - if (mergeCall.getInsertCall() != null) { + SqlInsert mergeInsertCall = mergeCall.getInsertCall(); + if (mergeInsertCall != null) { registerQuery( parentScope, null, - mergeCall.getInsertCall(), + mergeInsertCall, enclosingNode, null, false); @@ -2848,7 +2947,7 @@ private void registerQuery( CollectScope cs = new CollectScope(parentScope, usingScope, call); final CollectNamespace tableConstructorNs = new CollectNamespace(call, cs, enclosingNode); - final String alias2 = deriveAlias(node, nextGeneratedId++); + final String alias2 = deriveAliasNonNull(node, nextGeneratedId++); registerNamespace( usingScope, alias2, @@ -2867,10 +2966,10 @@ private void registerQuery( private void registerSetop( SqlValidatorScope parentScope, - SqlValidatorScope usingScope, + @Nullable SqlValidatorScope usingScope, SqlNode node, SqlNode enclosingNode, - String alias, + @Nullable String alias, boolean forceNullable) { SqlCall call = (SqlCall) node; final SetopNamespace setopNamespace = @@ -2892,10 +2991,10 @@ private void registerSetop( private void registerWith( SqlValidatorScope parentScope, - SqlValidatorScope usingScope, + @Nullable SqlValidatorScope usingScope, SqlWith with, SqlNode enclosingNode, - String alias, + @Nullable String alias, boolean forceNullable, boolean checkUpdate) { final WithNamespace withNamespace = @@ -2925,7 +3024,7 @@ private void registerWith( return true; } // Also when nested window aggregates are present - for (SqlCall call : overFinder.findAll(select.getSelectList())) { + for (SqlCall call : overFinder.findAll(SqlNonNullableAccessors.getSelectList(select))) { assert call.getKind() == SqlKind.OVER; if (isNestedAggregateWindow(call.operand(0))) { return true; @@ -2954,7 +3053,7 @@ protected boolean isOverAggregateWindow(SqlNode node) { * *

    The node is useful context for error messages, * but you cannot assume that the node is the only aggregate function. */ - protected SqlNode getAggregate(SqlSelect select) { + protected @Nullable SqlNode getAggregate(SqlSelect select) { SqlNode node = select.getGroup(); if (node != null) { return node; @@ -2968,7 +3067,7 @@ protected SqlNode getAggregate(SqlSelect select) { /** If there is at least one call to an aggregate function, returns the * first. */ - private SqlNode getAgg(SqlSelect select) { + private @Nullable SqlNode getAgg(SqlSelect select) { final SelectScope selectScope = getRawSelectScope(select); if (selectScope != null) { final List selectList = selectScope.getExpandedSelectList(); @@ -2976,7 +3075,7 @@ private SqlNode getAgg(SqlSelect select) { return aggFinder.findAgg(selectList); } } - return aggFinder.findAgg(select.getSelectList()); + return aggFinder.findAgg(SqlNonNullableAccessors.getSelectList(select)); } @Deprecated @@ -2996,7 +3095,7 @@ private void validateNodeFeature(SqlNode node) { private void registerSubQueries( SqlValidatorScope parentScope, - SqlNode node) { + @Nullable SqlNode node) { if (node == null) { return; } @@ -3079,7 +3178,7 @@ private void registerOperandSubQueries( // // jhyde 2006/12/21: I think the limits should be baked into the // type system, not dependent on the calculator implementation. - BigDecimal bd = (BigDecimal) literal.getValue(); + BigDecimal bd = literal.getValueAs(BigDecimal.class); BigInteger unscaled = bd.unscaledValue(); long longValue = unscaled.longValue(); if (!BigInteger.valueOf(longValue).equals(unscaled)) { @@ -3094,7 +3193,7 @@ private void registerOperandSubQueries( break; case BINARY: - final BitString bitString = (BitString) literal.getValue(); + final BitString bitString = literal.getValueAs(BitString.class); if ((bitString.getBitCount() % 8) != 0) { throw newValidationError(literal, RESOURCE.binaryLiteralOdd()); } @@ -3146,7 +3245,7 @@ private void registerOperandSubQueries( } private void validateLiteralAsDouble(SqlLiteral literal) { - BigDecimal bd = (BigDecimal) literal.getValue(); + BigDecimal bd = literal.getValueAs(BigDecimal.class); double d = bd.doubleValue(); if (Double.isInfinite(d) || Double.isNaN(d)) { // overflow @@ -3204,7 +3303,7 @@ protected void validateFrom( SqlNode node, RelDataType targetRowType, SqlValidatorScope scope) { - Objects.requireNonNull(targetRowType); + requireNonNull(targetRowType); switch (node.getKind()) { case AS: case TABLE_REF: @@ -3232,7 +3331,7 @@ protected void validateFrom( // Validate the namespace representation of the node, just in case the // validation did not occur implicitly. - getNamespace(node, scope).validate(targetRowType); + getNamespaceOrThrow(node, scope).validate(targetRowType); } protected void validateOver(SqlCall call, SqlValidatorScope scope) { @@ -3253,7 +3352,7 @@ private void checkRollUpInUsing(SqlIdentifier identifier, if (namespace != null) { SqlValidatorTable sqlValidatorTable = namespace.getTable(); if (sqlValidatorTable != null) { - Table table = sqlValidatorTable.unwrap(Table.class); + Table table = sqlValidatorTable.unwrapOrThrow(Table.class); String column = Util.last(identifier.names); if (table.isRolledUp(column)) { @@ -3271,7 +3370,7 @@ protected void validateJoin(SqlJoin join, SqlValidatorScope scope) { boolean natural = join.isNatural(); final JoinType joinType = join.getJoinType(); final JoinConditionType conditionType = join.getConditionType(); - final SqlValidatorScope joinScope = scopes.get(join); + final SqlValidatorScope joinScope = getScopeOrThrow(join); // getJoinScope? validateFrom(left, unknownType, joinScope); validateFrom(right, unknownType, joinScope); @@ -3281,15 +3380,15 @@ protected void validateJoin(SqlJoin join, SqlValidatorScope scope) { Preconditions.checkArgument(condition == null); break; case ON: - Preconditions.checkArgument(condition != null); + requireNonNull(condition, "join.getCondition()"); SqlNode expandedCondition = expand(condition, joinScope); join.setOperand(5, expandedCondition); - condition = join.getCondition(); + condition = getCondition(join); validateWhereOrOn(joinScope, condition, "ON"); checkRollUp(null, join, condition, joinScope, "ON"); break; case USING: - SqlNodeList list = (SqlNodeList) condition; + SqlNodeList list = (SqlNodeList) requireNonNull(condition, "join.getCondition()"); // Parser ensures that using clause is not empty. Preconditions.checkArgument(list.size() > 0, "Empty USING clause"); @@ -3319,8 +3418,8 @@ protected void validateJoin(SqlJoin join, SqlValidatorScope scope) { // Join on fields that occur exactly once on each side. Ignore // fields that occur more than once on either side. - final RelDataType leftRowType = getNamespace(left).getRowType(); - final RelDataType rightRowType = getNamespace(right).getRowType(); + final RelDataType leftRowType = getNamespaceOrThrow(left).getRowType(); + final RelDataType rightRowType = getNamespaceOrThrow(right).getRowType(); final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); List naturalColumnNames = SqlValidatorUtil.deriveNaturalJoinColumnList(nameMatcher, @@ -3328,10 +3427,12 @@ protected void validateJoin(SqlJoin join, SqlValidatorScope scope) { // Check compatibility of the chosen columns. for (String name : naturalColumnNames) { - final RelDataType leftColType = - nameMatcher.field(leftRowType, name).getType(); - final RelDataType rightColType = - nameMatcher.field(rightRowType, name).getType(); + final RelDataType leftColType = requireNonNull( + nameMatcher.field(leftRowType, name), + () -> "unable to find left field " + name + " in " + leftRowType).getType(); + final RelDataType rightColType = requireNonNull( + nameMatcher.field(rightRowType, name), + () -> "unable to find right field " + name + " in " + rightRowType).getType(); if (!SqlTypeUtil.isComparable(leftColType, rightColType)) { throw newValidationError(join, RESOURCE.naturalOrUsingColumnNotCompatible(name, @@ -3403,7 +3504,7 @@ private void validateNoAggs(AggFinder aggFinder, SqlNode node, private RelDataType validateUsingCol(SqlIdentifier id, SqlNode leftOrRight) { if (id.names.size() == 1) { String name = id.names.get(0); - final SqlValidatorNamespace namespace = getNamespace(leftOrRight); + final SqlValidatorNamespace namespace = getNamespaceOrThrow(leftOrRight); final RelDataType rowType = namespace.getRowType(); final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); final RelDataTypeField field = nameMatcher.field(rowType, name); @@ -3431,20 +3532,21 @@ protected void validateSelect( assert targetRowType != null; // Namespace is either a select namespace or a wrapper around one. final SelectNamespace ns = - getNamespace(select).unwrap(SelectNamespace.class); + getNamespaceOrThrow(select).unwrap(SelectNamespace.class); // Its rowtype is null, meaning it hasn't been validated yet. // This is important, because we need to take the targetRowType into // account. assert ns.rowType == null; - if (select.isDistinct()) { + SqlNode distinctNode = select.getModifierNode(SqlSelectKeyword.DISTINCT); + if (distinctNode != null) { validateFeature(RESOURCE.sQLFeature_E051_01(), - select.getModifierNode(SqlSelectKeyword.DISTINCT) + distinctNode .getParserPosition()); } - final SqlNodeList selectItems = select.getSelectList(); + final SqlNodeList selectItems = SqlNonNullableAccessors.getSelectList(select); RelDataType fromType = unknownType; if (selectItems.size() == 1) { final SqlNode selectItem = selectItems.get(0); @@ -3462,18 +3564,23 @@ protected void validateSelect( } // Make sure that items in FROM clause have distinct aliases. - final SelectScope fromScope = (SelectScope) getFromScope(select); - List names = fromScope.getChildNames(); + final SelectScope fromScope = (SelectScope) requireNonNull(getFromScope(select), + () -> "fromScope for " + select); + List<@Nullable String> names = fromScope.getChildNames(); if (!catalogReader.nameMatcher().isCaseSensitive()) { + //noinspection RedundantTypeArguments names = names.stream() - .map(s -> s.toUpperCase(Locale.ROOT)) + .<@Nullable String>map(s -> s == null ? null : s.toUpperCase(Locale.ROOT)) .collect(Collectors.toList()); } final int duplicateAliasOrdinal = Util.firstDuplicate(names); if (duplicateAliasOrdinal >= 0) { final ScopeChild child = fromScope.children.get(duplicateAliasOrdinal); - throw newValidationError(child.namespace.getEnclosingNode(), + throw newValidationError( + requireNonNull( + child.namespace.getEnclosingNode(), + () -> "enclosingNode of namespace of " + child.name), RESOURCE.fromAliasDuplicate(child.name)); } @@ -3515,7 +3622,7 @@ protected void validateSelect( private void checkRollUpInSelectList(SqlSelect select) { SqlValidatorScope scope = getSelectScope(select); - for (SqlNode item : select.getSelectList()) { + for (SqlNode item : SqlNonNullableAccessors.getSelectList(select)) { checkRollUp(null, select, item, scope); } } @@ -3538,7 +3645,7 @@ private void checkRollUpInOrderBy(SqlSelect select) { } } - private void checkRollUpInWindow(SqlWindow window, SqlValidatorScope scope) { + private void checkRollUpInWindow(@Nullable SqlWindow window, SqlValidatorScope scope) { if (window != null) { for (SqlNode node : window.getPartitionList()) { checkRollUp(null, window, node, scope, "PARTITION BY"); @@ -3556,44 +3663,46 @@ private void checkRollUpInWindowDecl(SqlSelect select) { } } - private SqlNode stripDot(SqlNode node) { + private @Nullable SqlNode stripDot(@Nullable SqlNode node) { if (node != null && node.getKind() == SqlKind.DOT) { return stripDot(((SqlCall) node).operand(0)); } return node; } - private void checkRollUp(SqlNode grandParent, SqlNode parent, - SqlNode current, SqlValidatorScope scope, String optionalClause) { + private void checkRollUp(@Nullable SqlNode grandParent, @Nullable SqlNode parent, + @Nullable SqlNode current, SqlValidatorScope scope, @Nullable String optionalClause) { current = stripAs(current); if (current instanceof SqlCall && !(current instanceof SqlSelect)) { // Validate OVER separately checkRollUpInWindow(getWindowInOver(current), scope); current = stripOver(current); - List children = ((SqlCall) stripAs(stripDot(current))).getOperandList(); + SqlNode stripDot = requireNonNull(stripDot(current), "stripDot(current)"); + List children = + ((SqlCall) stripAs(stripDot)).getOperandList(); for (SqlNode child : children) { checkRollUp(parent, current, child, scope, optionalClause); } } else if (current instanceof SqlIdentifier) { SqlIdentifier id = (SqlIdentifier) current; if (!id.isStar() && isRolledUpColumn(id, scope)) { - if (!isAggregation(parent.getKind()) + if (!isAggregation(requireNonNull(parent, "parent").getKind()) || !isRolledUpColumnAllowedInAgg(id, scope, (SqlCall) parent, grandParent)) { String context = optionalClause != null ? optionalClause : parent.getKind().toString(); throw newValidationError(id, - RESOURCE.rolledUpNotAllowed(deriveAlias(id, 0), context)); + RESOURCE.rolledUpNotAllowed(deriveAliasNonNull(id, 0), context)); } } } } - private void checkRollUp(SqlNode grandParent, SqlNode parent, - SqlNode current, SqlValidatorScope scope) { + private void checkRollUp(@Nullable SqlNode grandParent, SqlNode parent, + @Nullable SqlNode current, SqlValidatorScope scope) { checkRollUp(grandParent, parent, current, scope, null); } - private SqlWindow getWindowInOver(SqlNode over) { + private @Nullable SqlWindow getWindowInOver(SqlNode over) { if (over.getKind() == SqlKind.OVER) { SqlNode window = ((SqlCall) over).getOperandList().get(1); if (window instanceof SqlWindow) { @@ -3614,7 +3723,7 @@ private static SqlNode stripOver(SqlNode node) { } } - private Pair findTableColumnPair(SqlIdentifier identifier, + private @Nullable Pair findTableColumnPair(SqlIdentifier identifier, SqlValidatorScope scope) { final SqlCall call = makeNullaryCall(identifier); if (call != null) { @@ -3632,7 +3741,7 @@ private Pair findTableColumnPair(SqlIdentifier identifier, // Returns true iff the given column is valid inside the given aggCall. private boolean isRolledUpColumnAllowedInAgg(SqlIdentifier identifier, SqlValidatorScope scope, - SqlCall aggCall, SqlNode parent) { + SqlCall aggCall, @Nullable SqlNode parent) { Pair pair = findTableColumnPair(identifier, scope); if (pair == null) { @@ -3641,16 +3750,25 @@ private boolean isRolledUpColumnAllowedInAgg(SqlIdentifier identifier, SqlValida String columnName = pair.right; - SqlValidatorTable sqlValidatorTable = - scope.fullyQualify(identifier).namespace.getTable(); - if (sqlValidatorTable != null) { - Table table = sqlValidatorTable.unwrap(Table.class); + Table table = resolveTable(identifier, scope); + if (table != null) { return table.rolledUpColumnValidInsideAgg(columnName, aggCall, parent, catalogReader.getConfig()); } return true; } + private @Nullable Table resolveTable(SqlIdentifier identifier, SqlValidatorScope scope) { + SqlQualified fullyQualified = scope.fullyQualify(identifier); + assert fullyQualified.namespace != null : "namespace must not be null in " + fullyQualified; + SqlValidatorTable sqlValidatorTable = + fullyQualified.namespace.getTable(); + if (sqlValidatorTable != null) { + return sqlValidatorTable.unwrapOrThrow(Table.class); + } + return null; + } + // Returns true iff the given column is actually rolled up. private boolean isRolledUpColumn(SqlIdentifier identifier, SqlValidatorScope scope) { @@ -3662,16 +3780,14 @@ private boolean isRolledUpColumn(SqlIdentifier identifier, SqlValidatorScope sco String columnName = pair.right; - SqlValidatorTable sqlValidatorTable = - scope.fullyQualify(identifier).namespace.getTable(); - if (sqlValidatorTable != null) { - Table table = sqlValidatorTable.unwrap(Table.class); + Table table = resolveTable(identifier, scope); + if (table != null) { return table.isRolledUp(columnName); } return false; } - private boolean shouldCheckForRollUp(SqlNode from) { + private boolean shouldCheckForRollUp(@Nullable SqlNode from) { if (from != null) { SqlKind kind = stripAs(from).getKind(); return kind != SqlKind.VALUES && kind != SqlKind.SELECT; @@ -3724,7 +3840,7 @@ private SqlModality deduceModality(SqlNode query) { @Override public boolean validateModality(SqlSelect select, SqlModality modality, boolean fail) { - final SelectScope scope = getRawSelectScope(select); + final SelectScope scope = getRawSelectScopeNonNull(select); switch (modality) { case STREAM: @@ -3732,7 +3848,8 @@ private SqlModality deduceModality(SqlNode query) { for (ScopeChild child : scope.children) { if (!child.namespace.supportsModality(modality)) { if (fail) { - throw newValidationError(child.namespace.getNode(), + SqlNode node = SqlNonNullableAccessors.getNode(child); + throw newValidationError(node, Static.RESOURCE.cannotConvertToStream(child.name)); } else { return false; @@ -3762,7 +3879,8 @@ private SqlModality deduceModality(SqlNode query) { for (ScopeChild child : scope.children) { if (!child.namespace.supportsModality(modality)) { if (fail) { - throw newValidationError(child.namespace.getNode(), + SqlNode node = SqlNonNullableAccessors.getNode(child); + throw newValidationError(node, Static.RESOURCE.cannotConvertToRelation(child.name)); } else { return false; @@ -3848,13 +3966,14 @@ protected void validateWindowClause(SqlSelect select) { return; } - final SelectScope windowScope = (SelectScope) getFromScope(select); - assert windowScope != null; + final SelectScope windowScope = (SelectScope) requireNonNull(getFromScope(select), + () -> "fromScope for " + select); // 1. ensure window names are simple // 2. ensure they are unique within this scope for (SqlWindow window : windows) { - SqlIdentifier declName = window.getDeclName(); + SqlIdentifier declName = requireNonNull(window.getDeclName(), + () -> "window.getDeclName() for " + window); if (!declName.isSimple()) { throw newValidationError(declName, RESOURCE.windowNameMustBeSimple()); } @@ -3895,20 +4014,21 @@ protected void validateWindowClause(SqlSelect select) { } @Override public void validateWith(SqlWith with, SqlValidatorScope scope) { - final SqlValidatorNamespace namespace = getNamespace(with); + final SqlValidatorNamespace namespace = getNamespaceOrThrow(with); validateNamespace(namespace, unknownType); } @Override public void validateWithItem(SqlWithItem withItem) { - if (withItem.columnList != null) { + SqlNodeList columnList = withItem.columnList; + if (columnList != null) { final RelDataType rowType = getValidatedNodeType(withItem.query); final int fieldCount = rowType.getFieldCount(); - if (withItem.columnList.size() != fieldCount) { - throw newValidationError(withItem.columnList, + if (columnList.size() != fieldCount) { + throw newValidationError(columnList, RESOURCE.columnCountMismatch()); } SqlValidatorUtil.checkIdentifierListForDuplicates( - withItem.columnList.getList(), validationErrorFunction); + columnList.getList(), validationErrorFunction); } else { // Luckily, field names have not been make unique yet. final List fieldNames = @@ -3933,7 +4053,7 @@ protected void validateWindowClause(SqlSelect select) { // We've found a table. But is it a sequence? final SqlValidatorNamespace ns = resolved.only().namespace; if (ns instanceof TableNamespace) { - final Table table = ns.getTable().unwrap(Table.class); + final Table table = getTable(ns).unwrapOrThrow(Table.class); switch (table.getJdbcTableType()) { case SEQUENCE: case TEMPORARY_SEQUENCE: @@ -3945,7 +4065,7 @@ protected void validateWindowClause(SqlSelect select) { throw newValidationError(id, RESOURCE.notASequence(id.toString())); } - @Override public SqlValidatorScope getWithScope(SqlNode withItem) { + @Override public @Nullable SqlValidatorScope getWithScope(SqlNode withItem) { assert withItem.getKind() == SqlKind.WITH_ITEM; return scopes.get(withItem); } @@ -3983,7 +4103,7 @@ protected void validateOrderList(SqlSelect select) { } } final SqlValidatorScope orderScope = getOrderScope(select); - Objects.requireNonNull(orderScope); + requireNonNull(orderScope); List expandList = new ArrayList<>(); for (SqlNode orderItem : orderList) { @@ -4127,7 +4247,7 @@ protected void validateGroupClause(SqlSelect select) { } private void validateGroupItem(SqlValidatorScope groupScope, - AggregatingSelectScope aggregatingScope, + @Nullable AggregatingSelectScope aggregatingScope, SqlNode groupItem) { switch (groupItem.getKind()) { case GROUPING_SETS: @@ -4145,7 +4265,7 @@ private void validateGroupItem(SqlValidatorScope groupScope, } private void validateGroupingSets(SqlValidatorScope groupScope, - AggregatingSelectScope aggregatingScope, SqlCall groupItem) { + @Nullable AggregatingSelectScope aggregatingScope, SqlCall groupItem) { for (SqlNode node : groupItem.getOperandList()) { validateGroupItem(groupScope, aggregatingScope, node); } @@ -4261,7 +4381,7 @@ protected RelDataType validateSelectList( if (config.identifierExpansion()) { select.setSelectList(newSelectList); } - getRawSelectScope(select).setExpandedSelectList(expandedSelectItems); + getRawSelectScopeNonNull(select).setExpandedSelectList(expandedSelectItems); // TODO: when SELECT appears as a value sub-query, should be using // something other than unknownType for targetRowType @@ -4319,7 +4439,7 @@ private void handleScalarSubQuery( Set aliasList, List> fieldList) { // A scalar sub-query only has one output column. - if (1 != selectItem.getSelectList().size()) { + if (1 != SqlNonNullableAccessors.getSelectList(selectItem).size()) { throw newValidationError(selectItem, RESOURCE.onlyScalarSubQueryAllowed()); } @@ -4329,7 +4449,7 @@ private void handleScalarSubQuery( // Get or generate alias and add to list. final String alias = - deriveAlias( + deriveAliasNonNull( selectItem, aliasList.size()); aliasList.add(alias); @@ -4360,7 +4480,7 @@ private void handleScalarSubQuery( */ protected RelDataType createTargetRowType( SqlValidatorTable table, - SqlNodeList targetColumnList, + @Nullable SqlNodeList targetColumnList, boolean append) { RelDataType baseRowType = table.getRowType(); if (targetColumnList == null) { @@ -4397,13 +4517,13 @@ protected RelDataType createTargetRowType( } @Override public void validateInsert(SqlInsert insert) { - final SqlValidatorNamespace targetNamespace = getNamespace(insert); + final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(insert); validateNamespace(targetNamespace, unknownType); final RelOptTable relOptTable = SqlValidatorUtil.getRelOptTable( targetNamespace, catalogReader.unwrap(Prepare.CatalogReader.class), null, null); final SqlValidatorTable table = relOptTable == null - ? targetNamespace.getTable() - : relOptTable.unwrap(SqlValidatorTable.class); + ? getTable(targetNamespace) + : relOptTable.unwrapOrThrow(SqlValidatorTable.class); // INSERT has an optional column name list. If present then // reduce the rowtype to the columns specified. If not present @@ -4428,7 +4548,7 @@ protected RelDataType createTargetRowType( // from validateSelect above). It would be better if that information // were used here so that we never saw any untyped nulls during // checkTypeAssignment. - final RelDataType sourceRowType = getNamespace(source).getRowType(); + final RelDataType sourceRowType = getNamespaceOrThrow(source).getRowType(); final RelDataType logicalTargetRowType = getLogicalTargetRowType(targetRowType, insert); setValidatedNodeType(insert, logicalTargetRowType); @@ -4436,7 +4556,7 @@ protected RelDataType createTargetRowType( getLogicalSourceRowType(sourceRowType, insert); final List strategies = - table.unwrap(RelOptTable.class).getColumnStrategies(); + table.unwrapOrThrow(RelOptTable.class).getColumnStrategies(); final RelDataType realTargetRowType = typeFactory.createStructType( logicalTargetRowType.getFieldList() @@ -4480,7 +4600,7 @@ private void checkConstraint( final ModifiableViewTable modifiableViewTable = validatorTable.unwrap(ModifiableViewTable.class); if (modifiableViewTable != null && source instanceof SqlCall) { - final Table table = modifiableViewTable.unwrap(Table.class); + final Table table = modifiableViewTable.unwrapOrThrow(Table.class); final RelDataType tableRowType = table.getRowType(typeFactory); final List tableFields = tableRowType.getFieldList(); @@ -4493,16 +4613,19 @@ private void checkConstraint( // Determine columns (indexed to the underlying table) that need // to be validated against the view constraint. + @SuppressWarnings("RedundantCast") final ImmutableBitSet targetColumns = - ImmutableBitSet.of(tableIndexToTargetField.keySet()); + ImmutableBitSet.of((Iterable) tableIndexToTargetField.keySet()); + @SuppressWarnings("RedundantCast") final ImmutableBitSet constrainedColumns = - ImmutableBitSet.of(projectMap.keySet()); - final ImmutableBitSet constrainedTargetColumns = - targetColumns.intersect(constrainedColumns); + ImmutableBitSet.of((Iterable) projectMap.keySet()); + @SuppressWarnings("assignment.type.incompatible") + List<@KeyFor({"tableIndexToTargetField", "projectMap"}) Integer> constrainedTargetColumns = + targetColumns.intersect(constrainedColumns).asList(); // Validate insert values against the view constraint. final List values = ((SqlCall) source).getOperandList(); - for (final int colIndex : constrainedTargetColumns.asList()) { + for (final int colIndex: constrainedTargetColumns) { final String colName = tableFields.get(colIndex).getName(); final RelDataTypeField targetField = tableIndexToTargetField.get(colIndex); for (SqlNode row : values) { @@ -4534,7 +4657,7 @@ private void checkConstraint( final ModifiableViewTable modifiableViewTable = validatorTable.unwrap(ModifiableViewTable.class); if (modifiableViewTable != null) { - final Table table = modifiableViewTable.unwrap(Table.class); + final Table table = modifiableViewTable.unwrapOrThrow(Table.class); final RelDataType tableRowType = table.getRowType(typeFactory); final Map projectMap = @@ -4657,14 +4780,14 @@ protected RelDataType getLogicalTargetRowType( && this.config.sqlConformance().isInsertSubsetColumnsAllowed()) { // Target an implicit subset of columns. final SqlNode source = insert.getSource(); - final RelDataType sourceRowType = getNamespace(source).getRowType(); + final RelDataType sourceRowType = getNamespaceOrThrow(source).getRowType(); final RelDataType logicalSourceRowType = getLogicalSourceRowType(sourceRowType, insert); final RelDataType implicitTargetRowType = typeFactory.createStructType( targetRowType.getFieldList() .subList(0, logicalSourceRowType.getFieldCount())); - final SqlValidatorNamespace targetNamespace = getNamespace(insert); + final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(insert); validateNamespace(targetNamespace, implicitTargetRowType); return implicitTargetRowType; } else { @@ -4695,7 +4818,7 @@ protected RelDataType getLogicalSourceRowType( * @param query The query */ protected void checkTypeAssignment( - SqlValidatorScope sourceScope, + @Nullable SqlValidatorScope sourceScope, SqlValidatorTable table, RelDataType sourceRowType, RelDataType targetRowType, @@ -4789,14 +4912,15 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { return update.getSourceExpressionList().get(ordinal); } else { return getNthExpr( - update.getSourceSelect(), + SqlNonNullableAccessors.getSourceSelect(update), ordinal, sourceCount); } } else if (query instanceof SqlSelect) { SqlSelect select = (SqlSelect) query; - if (select.getSelectList().size() == sourceCount) { - return select.getSelectList().get(ordinal); + SqlNodeList selectList = SqlNonNullableAccessors.getSelectList(select); + if (selectList.size() == sourceCount) { + return selectList.get(ordinal); } else { return query; // give up } @@ -4806,10 +4930,10 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { } @Override public void validateDelete(SqlDelete call) { - final SqlSelect sqlSelect = call.getSourceSelect(); + final SqlSelect sqlSelect = SqlNonNullableAccessors.getSourceSelect(call); validateSelect(sqlSelect, unknownType); - final SqlValidatorNamespace targetNamespace = getNamespace(call); + final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(call); validateNamespace(targetNamespace, unknownType); final SqlValidatorTable table = targetNamespace.getTable(); @@ -4817,13 +4941,14 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { } @Override public void validateUpdate(SqlUpdate call) { - final SqlValidatorNamespace targetNamespace = getNamespace(call); + final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(call); validateNamespace(targetNamespace, unknownType); final RelOptTable relOptTable = SqlValidatorUtil.getRelOptTable( - targetNamespace, catalogReader.unwrap(Prepare.CatalogReader.class), null, null); + targetNamespace, castNonNull(catalogReader.unwrap(Prepare.CatalogReader.class)), + null, null); final SqlValidatorTable table = relOptTable == null - ? targetNamespace.getTable() - : relOptTable.unwrap(SqlValidatorTable.class); + ? getTable(targetNamespace) + : relOptTable.unwrapOrThrow(SqlValidatorTable.class); final RelDataType targetRowType = createTargetRowType( @@ -4831,7 +4956,7 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { call.getTargetColumnList(), true); - final SqlSelect select = call.getSourceSelect(); + final SqlSelect select = SqlNonNullableAccessors.getSourceSelect(call); validateSelect(select, targetRowType); final RelDataType sourceRowType = getValidatedNodeType(select); @@ -4847,7 +4972,7 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { } @Override public void validateMerge(SqlMerge call) { - SqlSelect sqlSelect = call.getSourceSelect(); + SqlSelect sqlSelect = SqlNonNullableAccessors.getSourceSelect(call); // REVIEW zfong 5/25/06 - Does an actual type have to be passed into // validateSelect()? @@ -4861,7 +4986,7 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { // since validateSelect() would bail. // Let's use the update/insert targetRowType when available. IdentifierNamespace targetNamespace = - (IdentifierNamespace) getNamespace(call.getTargetTable()); + (IdentifierNamespace) getNamespaceOrThrow(call.getTargetTable()); validateNamespace(targetNamespace, unknownType); SqlValidatorTable table = targetNamespace.getTable(); @@ -4869,26 +4994,32 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { RelDataType targetRowType = unknownType; - if (call.getUpdateCall() != null) { + SqlUpdate updateCall = call.getUpdateCall(); + if (updateCall != null) { + requireNonNull(table, () -> "ns.getTable() for " + targetNamespace); targetRowType = createTargetRowType( table, - call.getUpdateCall().getTargetColumnList(), + updateCall.getTargetColumnList(), true); } - if (call.getInsertCall() != null) { + SqlInsert insertCall = call.getInsertCall(); + if (insertCall != null) { + requireNonNull(table, () -> "ns.getTable() for " + targetNamespace); targetRowType = createTargetRowType( table, - call.getInsertCall().getTargetColumnList(), + insertCall.getTargetColumnList(), false); } validateSelect(sqlSelect, targetRowType); - if (call.getUpdateCall() != null) { - validateUpdate(call.getUpdateCall()); + SqlUpdate updateCallAfterValidate = call.getUpdateCall(); + if (updateCallAfterValidate != null) { + validateUpdate(updateCallAfterValidate); } - if (call.getInsertCall() != null) { - validateInsert(call.getInsertCall()); + SqlInsert insertCallAfterValidate = call.getInsertCall(); + if (insertCallAfterValidate != null) { + validateInsert(insertCallAfterValidate); } } @@ -4900,7 +5031,7 @@ private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { */ private void validateAccess( SqlNode node, - SqlValidatorTable table, + @Nullable SqlValidatorTable table, SqlAccessEnum requiredAccess) { if (table != null) { SqlAccessType access = table.getAllowedAccess(); @@ -4921,18 +5052,19 @@ private void validateAccess( */ private void validateSnapshot( SqlNode node, - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlValidatorNamespace ns) { if (node.getKind() == SqlKind.SNAPSHOT) { SqlSnapshot snapshot = (SqlSnapshot) node; SqlNode period = snapshot.getPeriod(); - RelDataType dataType = deriveType(scope, period); + RelDataType dataType = deriveType(requireNonNull(scope, "scope"), period); if (dataType.getSqlTypeName() != SqlTypeName.TIMESTAMP) { throw newValidationError(period, Static.RESOURCE.illegalExpressionForTemporal(dataType.getSqlTypeName().getName())); } - if (!ns.getTable().isTemporal()) { - List qualifiedName = ns.getTable().getQualifiedName(); + SqlValidatorTable table = getTable(ns); + if (!table.isTemporal()) { + List qualifiedName = table.getQualifiedName(); String tableName = qualifiedName.get(qualifiedName.size() - 1); throw newValidationError(snapshot.getTableRef(), Static.RESOURCE.notTemporalTable(tableName)); @@ -5140,7 +5272,7 @@ public void setOriginal(SqlNode expr, SqlNode original) { originalExprs.putIfAbsent(expr, original); } - SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { + @Nullable SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); final RelDataTypeField field = nameMatcher.field(rowType, name); if (field == null) { @@ -5188,7 +5320,7 @@ SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { (MatchRecognizeScope) getMatchRecognizeScope(matchRecognize); final MatchRecognizeNamespace ns = - getNamespace(call).unwrap(MatchRecognizeNamespace.class); + getNamespaceOrThrow(call).unwrap(MatchRecognizeNamespace.class); assert ns.rowType == null; // rows per match @@ -5218,9 +5350,10 @@ SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { node.validate(this, scope); SqlIdentifier identifier; if (node instanceof SqlBasicCall) { - identifier = (SqlIdentifier) ((SqlBasicCall) node).getOperands()[0]; + identifier = (SqlIdentifier) ((SqlBasicCall) node).operand(0); } else { - identifier = (SqlIdentifier) node; + identifier = (SqlIdentifier) requireNonNull(node, + () -> "order by field is null. All fields: " + orderBy); } if (allRows) { @@ -5235,7 +5368,7 @@ SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { if (allRows) { final SqlValidatorNamespace sqlNs = - getNamespace(matchRecognize.getTableRef()); + getNamespaceOrThrow(matchRecognize.getTableRef()); final RelDataType inputDataType = sqlNs.getRowType(); for (RelDataTypeField fs : inputDataType.getFieldList()) { if (!typeBuilder.nameExists(fs.getName())) { @@ -5253,8 +5386,10 @@ SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { if (interval != null) { interval.validate(this, scope); if (((SqlIntervalLiteral) interval).signum() < 0) { + String intervalValue = interval.toValue(); throw newValidationError(interval, - RESOURCE.intervalMustBeNonNegative(interval.toValue())); + RESOURCE.intervalMustBeNonNegative( + intervalValue != null ? intervalValue : interval.toString())); } if (orderBy == null || orderBy.size() == 0) { throw newValidationError(interval, @@ -5264,9 +5399,9 @@ SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { SqlNode firstOrderByColumn = orderBy.getList().get(0); SqlIdentifier identifier; if (firstOrderByColumn instanceof SqlBasicCall) { - identifier = (SqlIdentifier) ((SqlBasicCall) firstOrderByColumn).getOperands()[0]; + identifier = ((SqlBasicCall) firstOrderByColumn).operand(0); } else { - identifier = (SqlIdentifier) firstOrderByColumn; + identifier = (SqlIdentifier) requireNonNull(firstOrderByColumn, "firstOrderByColumn"); } RelDataType firstOrderByColumnType = deriveType(scope, identifier); if (firstOrderByColumnType.getSqlTypeName() != SqlTypeName.TIMESTAMP) { @@ -5323,7 +5458,7 @@ SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) { final RelDataType rowType = typeBuilder.build(); if (matchRecognize.getMeasureList().size() == 0) { - ns.setType(getNamespace(matchRecognize.getTableRef()).getRowType()); + ns.setType(getNamespaceOrThrow(matchRecognize.getTableRef()).getRowType()); } else { ns.setType(rowType); } @@ -5338,7 +5473,7 @@ private List> validateMeasure(SqlMatchRecognize m for (SqlNode measure : measures) { assert measure instanceof SqlCall; - final String alias = deriveAlias(measure, aliases.size()); + final String alias = deriveAliasNonNull(measure, aliases.size()); aliases.add(alias); SqlNode expand = expand(measure, scope); @@ -5430,10 +5565,11 @@ private void validateDefinitions(SqlMatchRecognize mr, } public void validatePivot(SqlPivot pivot) { - final PivotScope scope = (PivotScope) getJoinScope(pivot); + final PivotScope scope = (PivotScope) requireNonNull(getJoinScope(pivot), + () -> "joinScope for " + pivot); final PivotNamespace ns = - getNamespace(pivot).unwrap(PivotNamespace.class); + getNamespaceOrThrow(pivot).unwrap(PivotNamespace.class); assert ns.rowType == null; // Given @@ -5447,7 +5583,7 @@ public void validatePivot(SqlPivot pivot) { // an aggregate or as an axis. // Aggregates, e.g. "PIVOT (sum(x) AS sum_x, count(*) AS c)" - final List> aggNames = new ArrayList<>(); + final List> aggNames = new ArrayList<>(); pivot.forEachAgg((alias, call) -> { call.validate(this, scope); final RelDataType type = deriveType(scope, call); @@ -5524,8 +5660,8 @@ private SqlNode navigationInDefine(SqlNode node, String alpha) { return node; } - @Override public void validateAggregateParams(SqlCall aggCall, SqlNode filter, - SqlNodeList orderList, SqlValidatorScope scope) { + @Override public void validateAggregateParams(SqlCall aggCall, @Nullable SqlNode filter, + @Nullable SqlNodeList orderList, SqlValidatorScope scope) { // For "agg(expr)", expr cannot itself contain aggregate function // invocations. For example, "SUM(2 * MAX(x))" is illegal; when // we see it, we'll report the error for the SUM (not the MAX). @@ -5642,7 +5778,7 @@ protected void validateFeature( public SqlNode expandSelectExpr(SqlNode expr, SelectScope scope, SqlSelect select) { final Expander expander = new SelectExpander(this, scope, select); - final SqlNode newExpr = expr.accept(expander); + final SqlNode newExpr = expander.go(expr); if (expr != newExpr) { setOriginal(newExpr, expr); } @@ -5651,7 +5787,7 @@ public SqlNode expandSelectExpr(SqlNode expr, @Override public SqlNode expand(SqlNode expr, SqlValidatorScope scope) { final Expander expander = new Expander(this, scope); - SqlNode newExpr = expr.accept(expander); + SqlNode newExpr = expander.go(expr); if (expr != newExpr) { setOriginal(newExpr, expr); } @@ -5662,7 +5798,7 @@ public SqlNode expandGroupByOrHavingExpr(SqlNode expr, SqlValidatorScope scope, SqlSelect select, boolean havingExpression) { final Expander expander = new ExtendedExpander(this, scope, select, expr, havingExpression); - SqlNode newExpr = expr.accept(expander); + SqlNode newExpr = expander.go(expr); if (expr != newExpr) { setOriginal(newExpr, expr); } @@ -5673,7 +5809,7 @@ public SqlNode expandGroupByOrHavingExpr(SqlNode expr, return false; } - @Override public List> getFieldOrigins(SqlNode sqlQuery) { + @Override public List<@Nullable List> getFieldOrigins(SqlNode sqlQuery) { if (sqlQuery instanceof SqlExplain) { return Collections.emptyList(); } @@ -5682,23 +5818,25 @@ public SqlNode expandGroupByOrHavingExpr(SqlNode expr, if (!sqlQuery.isA(SqlKind.QUERY)) { return Collections.nCopies(fieldCount, null); } - final List> list = new ArrayList<>(); + final List<@Nullable List> list = new ArrayList<>(); for (int i = 0; i < fieldCount; i++) { list.add(getFieldOrigin(sqlQuery, i)); } return ImmutableNullableList.copyOf(list); } - private List getFieldOrigin(SqlNode sqlQuery, int i) { + private @Nullable List getFieldOrigin(SqlNode sqlQuery, int i) { if (sqlQuery instanceof SqlSelect) { SqlSelect sqlSelect = (SqlSelect) sqlQuery; - final SelectScope scope = getRawSelectScope(sqlSelect); - final List selectList = scope.getExpandedSelectList(); + final SelectScope scope = getRawSelectScopeNonNull(sqlSelect); + final List selectList = requireNonNull(scope.getExpandedSelectList(), + () -> "expandedSelectList for " + scope); final SqlNode selectItem = stripAs(selectList.get(i)); if (selectItem instanceof SqlIdentifier) { final SqlQualified qualified = scope.fullyQualify((SqlIdentifier) selectItem); - SqlValidatorNamespace namespace = qualified.namespace; + SqlValidatorNamespace namespace = requireNonNull(qualified.namespace, + () -> "namespace for " + qualified); final SqlValidatorTable table = namespace.getTable(); if (table == null) { return null; @@ -5805,10 +5943,10 @@ private static class InsertNamespace extends DmlNamespace { InsertNamespace(SqlValidatorImpl validator, SqlInsert node, SqlNode enclosingNode, SqlValidatorScope parentScope) { super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = Objects.requireNonNull(node); + this.node = requireNonNull(node); } - @Override public SqlInsert getNode() { + @Override public @Nullable SqlNode getNode() { return node; } } @@ -5822,10 +5960,10 @@ private static class UpdateNamespace extends DmlNamespace { UpdateNamespace(SqlValidatorImpl validator, SqlUpdate node, SqlNode enclosingNode, SqlValidatorScope parentScope) { super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = Objects.requireNonNull(node); + this.node = requireNonNull(node); } - @Override public SqlUpdate getNode() { + @Override public @Nullable SqlNode getNode() { return node; } } @@ -5839,10 +5977,10 @@ private static class DeleteNamespace extends DmlNamespace { DeleteNamespace(SqlValidatorImpl validator, SqlDelete node, SqlNode enclosingNode, SqlValidatorScope parentScope) { super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = Objects.requireNonNull(node); + this.node = requireNonNull(node); } - @Override public SqlDelete getNode() { + @Override public @Nullable SqlNode getNode() { return node; } } @@ -5856,10 +5994,10 @@ private static class MergeNamespace extends DmlNamespace { MergeNamespace(SqlValidatorImpl validator, SqlMerge node, SqlNode enclosingNode, SqlValidatorScope parentScope) { super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = Objects.requireNonNull(node); + this.node = requireNonNull(node); } - @Override public SqlMerge getNode() { + @Override public @Nullable SqlNode getNode() { return node; } } @@ -6050,7 +6188,12 @@ private static class Expander extends SqlScopedShuttle { this.validator = validator; } - @Override public SqlNode visit(SqlIdentifier id) { + public SqlNode go(SqlNode root) { + return requireNonNull(root.accept(this), + () -> this + " returned null for " + root); + } + + @Override public @Nullable SqlNode visit(SqlIdentifier id) { // First check for builtin functions which don't have // parentheses, like "LOCALTIME". final SqlCall call = validator.makeNullaryCall(id); @@ -6075,7 +6218,7 @@ private static class Expander extends SqlScopedShuttle { } // Only visits arguments which are expressions. We don't want to // qualify non-expressions such as 'x' in 'empno * 5 AS x'. - ArgHandler argHandler = + CallCopyingArgHandler argHandler = new CallCopyingArgHandler(call, false); call.getOperator().acceptCall(this, call, true, argHandler); final SqlNode result = argHandler.result(); @@ -6115,14 +6258,15 @@ class OrderExpressionExpander extends SqlScopedShuttle { super(getOrderScope(select)); this.select = select; this.root = root; - this.aliasList = getNamespace(select).getRowType().getFieldNames(); + this.aliasList = getNamespaceOrThrow(select).getRowType().getFieldNames(); } public SqlNode go() { - return root.accept(this); + return requireNonNull(root.accept(this), + () -> "OrderExpressionExpander returned null for " + root); } - @Override public SqlNode visit(SqlLiteral literal) { + @Override public @Nullable SqlNode visit(SqlLiteral literal) { // Ordinal markers, e.g. 'select a, b from t order by 2'. // Only recognize them if they are the whole expression, // and if the dialect permits. @@ -6160,7 +6304,7 @@ private SqlNode nthSelectItem(int ordinal, final SqlParserPos pos) { SqlNodeList expandedSelectList = expandStar( - select.getSelectList(), + SqlNonNullableAccessors.getSelectList(select), select, false); SqlNode expr = expandedSelectList.get(ordinal); @@ -6179,7 +6323,7 @@ private SqlNode nthSelectItem(int ordinal, final SqlParserPos pos) { if (id.isSimple() && config.sqlConformance().isSortByAlias()) { String alias = id.getSimple(); - final SqlValidatorNamespace selectNs = getNamespace(select); + final SqlValidatorNamespace selectNs = getNamespaceOrThrow(select); final RelDataType rowType = selectNs.getRowTypeSansSystemColumns(); final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); @@ -6195,7 +6339,7 @@ private SqlNode nthSelectItem(int ordinal, final SqlParserPos pos) { return getScope().fullyQualify(id).identifier; } - @Override protected SqlNode visitScoped(SqlCall call) { + @Override protected @Nullable SqlNode visitScoped(SqlCall call) { // Don't attempt to expand sub-queries. We haven't implemented // these yet. if (call instanceof SqlSelect) { @@ -6219,7 +6363,7 @@ static class SelectExpander extends Expander { this.select = select; } - @Override public SqlNode visit(SqlIdentifier id) { + @Override public @Nullable SqlNode visit(SqlIdentifier id) { final SqlNode node = expandCommonColumn(select, id, (SelectScope) getScope(), validator); if (node != id) { return node; @@ -6246,7 +6390,7 @@ static class ExtendedExpander extends Expander { this.havingExpr = havingExpr; } - @Override public SqlNode visit(SqlIdentifier id) { + @Override public @Nullable SqlNode visit(SqlIdentifier id) { if (id.isSimple() && (havingExpr ? validator.config().sqlConformance().isHavingAlias() @@ -6256,7 +6400,7 @@ static class ExtendedExpander extends Expander { final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher(); int n = 0; - for (SqlNode s : select.getSelectList()) { + for (SqlNode s : SqlNonNullableAccessors.getSelectList(select)) { final String alias = SqlValidatorUtil.getAlias(s, -1); if (alias != null && nameMatcher.matches(alias, name)) { expr = s; @@ -6291,7 +6435,7 @@ static class ExtendedExpander extends Expander { return super.visit(id); } - @Override public SqlNode visit(SqlLiteral literal) { + @Override public @Nullable SqlNode visit(SqlLiteral literal) { if (havingExpr || !validator.config().sqlConformance().isGroupByOrdinal()) { return super.visit(literal); } @@ -6319,14 +6463,14 @@ static class ExtendedExpander extends Expander { case DOUBLE: final int intValue = literal.intValue(false); if (intValue >= 0) { - if (intValue < 1 || intValue > select.getSelectList().size()) { + if (intValue < 1 || intValue > SqlNonNullableAccessors.getSelectList(select).size()) { throw validator.newValidationError(literal, RESOURCE.orderByOrdinalOutOfRange()); } // SQL ordinals are 1-based, but Sort's are 0-based int ordinal = intValue - 1; - return SqlUtil.stripAs(select.getSelectList().get(ordinal)); + return SqlUtil.stripAs(SqlNonNullableAccessors.getSelectList(select).get(ordinal)); } break; default: @@ -6379,7 +6523,8 @@ public FunctionParamInfo() { */ private static class NavigationModifier extends SqlShuttle { public SqlNode go(SqlNode node) { - return node.accept(this); + return requireNonNull(node.accept(this), + () -> "NavigationModifier returned for " + node); } } @@ -6396,22 +6541,22 @@ public SqlNode go(SqlNode node) { * */ private static class NavigationExpander extends NavigationModifier { - final SqlOperator op; - final SqlNode offset; + final @Nullable SqlOperator op; + final @Nullable SqlNode offset; NavigationExpander() { this(null, null); } - NavigationExpander(SqlOperator operator, SqlNode offset) { + NavigationExpander(@Nullable SqlOperator operator, @Nullable SqlNode offset) { this.offset = offset; this.op = operator; } - @Override public SqlNode visit(SqlCall call) { + @Override public @Nullable SqlNode visit(SqlCall call) { SqlKind kind = call.getKind(); List operands = call.getOperandList(); - List newOperands = new ArrayList<>(); + List<@Nullable SqlNode> newOperands = new ArrayList<>(); if (call.getFunctionQuantifier() != null && call.getFunctionQuantifier().getValue() == SqlSelectKeyword.DISTINCT) { @@ -6497,7 +6642,7 @@ private static class NavigationReplacer extends NavigationModifier { this.alpha = alpha; } - @Override public SqlNode visit(SqlCall call) { + @Override public @Nullable SqlNode visit(SqlCall call) { SqlKind kind = call.getKind(); if (isLogicalNavigation(kind) || isAggregation(kind) @@ -6534,7 +6679,7 @@ private static class NavigationReplacer extends NavigationModifier { /** Validates that within one navigation function, the pattern var is the * same. */ - private class PatternValidator extends SqlBasicVisitor> { + private class PatternValidator extends SqlBasicVisitor<@Nullable Set> { private final boolean isMeasure; int firstLastCount; int prevNextCount; @@ -6598,9 +6743,11 @@ private class PatternValidator extends SqlBasicVisitor> { for (SqlNode node : operands) { if (node != null) { vars.addAll( - node.accept( - new PatternValidator(isMeasure, firstLastCount, prevNextCount, - aggregateCount))); + requireNonNull( + node.accept( + new PatternValidator(isMeasure, firstLastCount, prevNextCount, + aggregateCount)), + () -> "node.accept(PatternValidator) for node " + node)); } } @@ -6723,7 +6870,9 @@ private class Permute { } private RelDataTypeField field(String name) { - return catalogReader.nameMatcher().field(rowType, name); + RelDataTypeField field = catalogReader.nameMatcher().field(rowType, name); + assert field != null : "field " + name + " was not found in " + rowType; + return field; } /** Moves fields according to the permutation. */ @@ -6750,9 +6899,10 @@ public void permute(List selectItems, final RelDataType type1 = field1.getValue(); // output is nullable only if both inputs are final boolean nullable = type.isNullable() && type1.isNullable(); - final RelDataType type2 = - SqlTypeUtil.leastRestrictiveForComparison(typeFactory, type, - type1); + RelDataType currentType = type; + final RelDataType type2 = requireNonNull( + SqlTypeUtil.leastRestrictiveForComparison(typeFactory, type, type1), + () -> "leastRestrictiveForComparison for types " + currentType + " and " + type1); selectItem = SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, SqlStdOperatorTable.COALESCE.createCall(SqlParserPos.ZERO, diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java index f7e581d335c1..735345a54b85 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java @@ -20,6 +20,9 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.util.List; /** @@ -61,7 +64,7 @@ public interface SqlValidatorNamespace { /** * Returns the underlying table, or null if there is none. */ - SqlValidatorTable getTable(); + @Nullable SqlValidatorTable getTable(); /** * Returns the row type of this namespace, which comprises a list of names @@ -116,14 +119,15 @@ public interface SqlValidatorNamespace { * * @return parse tree node; null for {@link TableNamespace} */ - SqlNode getNode(); + @Nullable SqlNode getNode(); /** * Returns the parse tree node that at is at the root of this namespace and * includes all decorations. If there are no decorations, returns the same * as {@link #getNode()}. */ - SqlNode getEnclosingNode(); + @Pure + @Nullable SqlNode getEnclosingNode(); /** * Looks up a child namespace of a given name. @@ -135,7 +139,7 @@ public interface SqlValidatorNamespace { * @param name Name of namespace * @return Namespace */ - SqlValidatorNamespace lookupChild(String name); + @Nullable SqlValidatorNamespace lookupChild(String name); /** * Returns whether this namespace has a field of a given name. @@ -169,7 +173,7 @@ public interface SqlValidatorNamespace { * @return This namespace cast to desired type * @throws ClassCastException if no such interface is available */ - T unwrap(Class clazz); + T unwrap(Class clazz); /** * Returns whether this namespace implements a given interface, or wraps a diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java index fcdd9acf3cb2..a48b7b5d1cbd 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java @@ -30,6 +30,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -133,7 +135,7 @@ Map findQualifyingTableNames(String columnName, /** * Finds a window with a given name. Returns null if not found. */ - SqlWindow lookupWindow(String name); + @Nullable SqlWindow lookupWindow(String name); /** * Returns whether an expression is monotonic in this scope. For example, if @@ -146,7 +148,7 @@ Map findQualifyingTableNames(String columnName, * Returns the expressions by which the rows in this scope are sorted. If * the rows are unsorted, returns null. */ - SqlNodeList getOrderList(); + @Nullable SqlNodeList getOrderList(); /** * Resolves a single identifier to a column, and returns the datatype of @@ -159,7 +161,7 @@ Map findQualifyingTableNames(String columnName, * @param ctx Context for exception * @return Type of column, if found and unambiguous; null if not found */ - RelDataType resolveColumn(String name, SqlNode ctx); + @Nullable RelDataType resolveColumn(String name, SqlNode ctx); /** * Returns the scope within which operands to a call are to be validated. @@ -182,7 +184,7 @@ Map findQualifyingTableNames(String columnName, /** @deprecated Use * {@link #resolveTable(List, SqlNameMatcher, Path, Resolved)}. */ @Deprecated // to be removed before 2.0 - SqlValidatorNamespace getTableNamespace(List names); + @Nullable SqlValidatorNamespace getTableNamespace(List names); /** * Looks up a table in this scope from its name. If found, calls @@ -206,14 +208,14 @@ void resolveTable(List names, SqlNameMatcher nameMatcher, Path path, /** Returns whether this scope is enclosed within {@code scope2} in such * a way that it can see the contents of {@code scope2}. */ - default boolean isWithin(SqlValidatorScope scope2) { + default boolean isWithin(@Nullable SqlValidatorScope scope2) { return this == scope2; } /** Callback from {@link SqlValidatorScope#resolve}. */ interface Resolved { void found(SqlValidatorNamespace namespace, boolean nullable, - SqlValidatorScope scope, Path path, List remainingNames); + @Nullable SqlValidatorScope scope, Path path, @Nullable List remainingNames); int count(); } @@ -224,7 +226,7 @@ abstract class Path { public static final EmptyPath EMPTY = new EmptyPath(); /** Creates a path that consists of this path plus one additional step. */ - public Step plus(RelDataType rowType, int i, String name, StructKind kind) { + public Step plus(@Nullable RelDataType rowType, int i, String name, StructKind kind) { return new Step(this, rowType, i, name, kind); } @@ -260,12 +262,12 @@ class EmptyPath extends Path { /** A step in resolving an identifier. */ class Step extends Path { final Path parent; - final RelDataType rowType; + final @Nullable RelDataType rowType; public final int i; public final String name; final StructKind kind; - Step(Path parent, RelDataType rowType, int i, String name, + Step(Path parent, @Nullable RelDataType rowType, int i, String name, StructKind kind) { this.parent = Objects.requireNonNull(parent); this.rowType = rowType; // may be null @@ -290,7 +292,7 @@ class ResolvedImpl implements Resolved { final List resolves = new ArrayList<>(); @Override public void found(SqlValidatorNamespace namespace, boolean nullable, - SqlValidatorScope scope, Path path, List remainingNames) { + @Nullable SqlValidatorScope scope, Path path, @Nullable List remainingNames) { if (scope instanceof TableScope) { scope = scope.getValidator().getSelectScope((SqlSelect) scope.getNode()); } @@ -321,13 +323,13 @@ public void clear() { class Resolve { public final SqlValidatorNamespace namespace; private final boolean nullable; - public final SqlValidatorScope scope; // may be null + public final @Nullable SqlValidatorScope scope; // may be null public final Path path; /** Names not matched; empty if it was a full match. */ final List remainingNames; Resolve(SqlValidatorNamespace namespace, boolean nullable, - SqlValidatorScope scope, Path path, List remainingNames) { + @Nullable SqlValidatorScope scope, Path path, @Nullable List remainingNames) { this.namespace = Objects.requireNonNull(namespace); this.nullable = nullable; this.scope = scope; diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java index f2409d5b4d95..97ff58a38dd9 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java @@ -63,6 +63,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; @@ -72,13 +74,16 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.TreeSet; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset; +import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Utility methods related to validation. */ @@ -99,15 +104,16 @@ private SqlValidatorUtil() {} * @param usedDataset Output parameter which is set to true if a sample * dataset is found; may be null */ - public static RelOptTable getRelOptTable( + public static @Nullable RelOptTable getRelOptTable( SqlValidatorNamespace namespace, - Prepare.CatalogReader catalogReader, - String datasetName, - boolean[] usedDataset) { + Prepare.@Nullable CatalogReader catalogReader, + @Nullable String datasetName, + boolean @Nullable [] usedDataset) { if (namespace.isWrapperFor(TableNamespace.class)) { final TableNamespace tableNamespace = namespace.unwrap(TableNamespace.class); - return getRelOptTable(tableNamespace, catalogReader, datasetName, usedDataset, + return getRelOptTable(tableNamespace, + requireNonNull(catalogReader, "catalogReader"), datasetName, usedDataset, tableNamespace.extendedFields); } else if (namespace.isWrapperFor(SqlValidatorImpl.DmlNamespace.class)) { final SqlValidatorImpl.DmlNamespace dmlNamespace = namespace.unwrap( @@ -120,17 +126,18 @@ public static RelOptTable getRelOptTable( ? ImmutableList.of() : getExtendedColumns(namespace.getValidator(), validatorTable, dmlNamespace.extendList); return getRelOptTable( - tableNamespace, catalogReader, datasetName, usedDataset, extendedFields); + tableNamespace, requireNonNull(catalogReader, "catalogReader"), + datasetName, usedDataset, extendedFields); } } return null; } - private static RelOptTable getRelOptTable( + private static @Nullable RelOptTable getRelOptTable( TableNamespace tableNamespace, Prepare.CatalogReader catalogReader, - String datasetName, - boolean[] usedDataset, + @Nullable String datasetName, + boolean @Nullable [] usedDataset, List extendedFields) { final List names = tableNamespace.getTable().getQualifiedName(); RelOptTable table; @@ -143,7 +150,7 @@ private static RelOptTable getRelOptTable( // Schema does not support substitution. Ignore the data set, if any. table = catalogReader.getTableForMember(names); } - if (!extendedFields.isEmpty()) { + if (table != null && !extendedFields.isEmpty()) { table = table.extend(extendedFields); } return table; @@ -153,7 +160,7 @@ private static RelOptTable getRelOptTable( * Gets a list of extended columns with field indices to the underlying table. */ public static List getExtendedColumns( - SqlValidator validator, SqlValidatorTable table, SqlNodeList extendedColumns) { + @Nullable SqlValidator validator, SqlValidatorTable table, SqlNodeList extendedColumns) { final ImmutableList.Builder extendedFields = ImmutableList.builder(); final ExtensibleTable extTable = table.unwrap(ExtensibleTable.class); @@ -167,7 +174,7 @@ public static List getExtendedColumns( extendedFields.add( new RelDataTypeFieldImpl(identifier.toString(), extendedFieldOffset++, - type.deriveType(validator))); + type.deriveType(requireNonNull(validator, "validator")))); } return extendedFields.build(); } @@ -227,8 +234,10 @@ public static ImmutableBitSet getOrdinalBitSet( Map indexToField) { ImmutableBitSet source = ImmutableBitSet.of( Util.transform(sourceRowType.getFieldList(), RelDataTypeField::getIndex)); + // checkerframework: found : Set<@KeyFor("indexToField") Integer> + //noinspection RedundantCast ImmutableBitSet target = - ImmutableBitSet.of(indexToField.keySet()); + ImmutableBitSet.of((Iterable) indexToField.keySet()); return source.intersect(target); } @@ -242,7 +251,7 @@ public static Map mapNameToIndex(List fields) } @Deprecated // to be removed before 2.0 - public static RelDataTypeField lookupField(boolean caseSensitive, + public static @Nullable RelDataTypeField lookupField(boolean caseSensitive, final RelDataType rowType, String columnName) { return rowType.getField(columnName, caseSensitive, false); } @@ -251,10 +260,8 @@ public static void checkCharsetAndCollateConsistentIfCharType( RelDataType type) { // (every charset must have a default collation) if (SqlTypeUtil.inCharFamily(type)) { - Charset strCharset = type.getCharset(); - Charset colCharset = type.getCollation().getCharset(); - assert null != strCharset; - assert null != colCharset; + Charset strCharset = getCharset(type); + Charset colCharset = getCollation(type).getCharset(); if (!strCharset.equals(colCharset)) { if (false) { // todo: enable this checking when we have a charset to @@ -307,7 +314,7 @@ public static SqlNode addAlias( * @return An alias, if one can be derived; or a synthetic alias * "expr$ordinal" if ordinal < 0; otherwise null */ - public static String getAlias(SqlNode node, int ordinal) { + public static @Nullable String getAlias(SqlNode node, int ordinal) { switch (node.getKind()) { case AS: // E.g. "1 + 2 as foo" --> "foo" @@ -363,7 +370,7 @@ public static SqlValidatorWithHints newValidator( * @param suggester Base for name when input name is null * @return Unique name */ - public static String uniquify(String name, Set usedNames, + public static String uniquify(@Nullable String name, Set usedNames, Suggester suggester) { if (name != null) { if (usedNames.add(name)) { @@ -441,7 +448,7 @@ public static List uniquify(List nameList, * @return List of unique strings */ public static List uniquify( - List nameList, + List nameList, Suggester suggester, boolean caseSensitive) { final Set used = caseSensitive @@ -457,7 +464,7 @@ public static List uniquify( newNameList.add(uniqueName); } return changeCount == 0 - ? nameList + ? (List) nameList : newNameList; } @@ -477,22 +484,24 @@ public static List uniquify( */ public static RelDataType deriveJoinRowType( RelDataType leftType, - RelDataType rightType, + @Nullable RelDataType rightType, JoinRelType joinType, RelDataTypeFactory typeFactory, - List fieldNameList, + @Nullable List fieldNameList, List systemFieldList) { assert systemFieldList != null; switch (joinType) { case LEFT: - rightType = typeFactory.createTypeWithNullability(rightType, true); + rightType = typeFactory.createTypeWithNullability( + requireNonNull(rightType, "rightType"), true); break; case RIGHT: leftType = typeFactory.createTypeWithNullability(leftType, true); break; case FULL: leftType = typeFactory.createTypeWithNullability(leftType, true); - rightType = typeFactory.createTypeWithNullability(rightType, true); + rightType = typeFactory.createTypeWithNullability( + requireNonNull(rightType, "rightType"), true); break; case SEMI: case ANTI: @@ -527,14 +536,14 @@ public static RelDataType deriveJoinRowType( public static RelDataType createJoinType( RelDataTypeFactory typeFactory, RelDataType leftType, - RelDataType rightType, - List fieldNameList, + @Nullable RelDataType rightType, + @Nullable List fieldNameList, List systemFieldList) { assert (fieldNameList == null) || (fieldNameList.size() == (systemFieldList.size() + leftType.getFieldCount() - + rightType.getFieldCount())); + + (rightType == null ? 0 : rightType.getFieldCount()))); List nameList = new ArrayList<>(); final List typeList = new ArrayList<>(); @@ -589,10 +598,10 @@ private static void addFields(List fieldList, * @param id the target column identifier * @param table the target table or null if it is not a RelOptTable instance */ - public static RelDataTypeField getTargetField( + public static @Nullable RelDataTypeField getTargetField( RelDataType rowType, RelDataTypeFactory typeFactory, SqlIdentifier id, SqlValidatorCatalogReader catalogReader, - RelOptTable table) { + @Nullable RelOptTable table) { final Table t = table == null ? null : table.unwrap(Table.class); if (!(t instanceof CustomColumnResolvingTable)) { final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); @@ -651,7 +660,7 @@ public static void getSchemaObjectMonikers( } } - public static SelectScope getEnclosingSelectScope(SqlValidatorScope scope) { + public static @Nullable SelectScope getEnclosingSelectScope(@Nullable SqlValidatorScope scope) { while (scope instanceof DelegatingScope) { if (scope instanceof SelectScope) { return (SelectScope) scope; @@ -661,7 +670,7 @@ public static SelectScope getEnclosingSelectScope(SqlValidatorScope scope) { return null; } - public static AggregatingSelectScope getEnclosingAggregateSelectScope( + public static @Nullable AggregatingSelectScope getEnclosingAggregateSelectScope( SqlValidatorScope scope) { while (scope instanceof DelegatingScope) { if (scope instanceof AggregatingSelectScope) { @@ -707,6 +716,8 @@ public static RelDataType createTypeFromProjection(RelDataType type, new ArrayList<>(columnNameList.size()); for (String name : columnNameList) { RelDataTypeField field = type.getField(name, caseSensitive, false); + assert field != null : "field " + name + (caseSensitive ? " (caseSensitive)" : "") + + " is not found in " + type; fields.add(type.getFieldList().get(field.getIndex())); } return typeFactory.createStructType(fields); @@ -884,7 +895,10 @@ private static ImmutableBitSet analyzeGroupExpr(SqlValidatorScope scope, } } - RelDataTypeField field = nameMatcher.field(rowType, originalFieldName); + RelDataTypeField field = requireNonNull( + nameMatcher.field(rowType, originalFieldName), + () -> "field " + originalFieldName + " is not found in " + rowType + + " with " + nameMatcher); int origPos = namespaceOffset + field.getIndex(); groupAnalyzer.groupExprProjection.put(origPos, ref); @@ -970,7 +984,7 @@ public static ImmutableList cube( * * @return TypeEntry with a table with the given name, or null */ - public static CalciteSchema.TypeEntry getTypeEntry( + public static CalciteSchema.@Nullable TypeEntry getTypeEntry( CalciteSchema rootSchema, SqlIdentifier typeName) { final String name; final List path; @@ -988,8 +1002,11 @@ public static CalciteSchema.TypeEntry getTypeEntry( continue; } schema = schema.getSubSchema(p, true); + if (schema == null) { + return null; + } } - return schema == null ? null : schema.getType(name, false); + return schema.getType(name, false); } /** @@ -1005,7 +1022,7 @@ public static CalciteSchema.TypeEntry getTypeEntry( * * @return TableEntry with a table with the given name, or null */ - public static CalciteSchema.TableEntry getTableEntry( + public static CalciteSchema.@Nullable TableEntry getTableEntry( SqlValidatorCatalogReader catalogReader, List names) { // First look in the default schema, if any. // If not found, look in the root schema. @@ -1041,7 +1058,7 @@ public static CalciteSchema.TableEntry getTableEntry( * * @return CalciteSchema that corresponds specified schemaPath */ - public static CalciteSchema getSchema(CalciteSchema rootSchema, + public static @Nullable CalciteSchema getSchema(CalciteSchema rootSchema, Iterable schemaPath, SqlNameMatcher nameMatcher) { CalciteSchema schema = rootSchema; for (String schemaName : schemaPath) { @@ -1058,7 +1075,7 @@ public static CalciteSchema getSchema(CalciteSchema rootSchema, return schema; } - private static CalciteSchema.TableEntry getTableEntryFrom( + private static CalciteSchema.@Nullable TableEntry getTableEntryFrom( CalciteSchema schema, String name, boolean caseSensitive) { CalciteSchema.TableEntry entry = schema.getTable(name, caseSensitive); @@ -1117,9 +1134,9 @@ static boolean containsMonotonic(SelectScope scope, SqlNodeList nodes) { * @param funcType function category * @return A sql function if and only if there is one operator matches, else null */ - public static SqlOperator lookupSqlFunctionByID(SqlOperatorTable opTab, + public static @Nullable SqlOperator lookupSqlFunctionByID(SqlOperatorTable opTab, SqlIdentifier funName, - SqlFunctionCategory funcType) { + @Nullable SqlFunctionCategory funcType) { if (funName.isSimple()) { final List list = new ArrayList<>(); opTab.lookupOperatorOverloads(funName, funcType, SqlSyntax.FUNCTION, list, @@ -1164,9 +1181,12 @@ public static Pair validateExprWithRowType( typeFactory, SqlValidator.Config.DEFAULT); final SqlSelect select = (SqlSelect) validator.validate(select0); - assert select.getSelectList().size() == 1 + SqlNodeList selectList = requireNonNull( + select.getSelectList(), + () -> "selectList in " + select); + assert selectList.size() == 1 : "Expression " + expr + " should be atom expression"; - final SqlNode node = select.getSelectList().get(0); + final SqlNode node = selectList.get(0); final RelDataType nodeType = validator .getValidatedNodeType(select) .getFieldList() @@ -1227,9 +1247,9 @@ public static class DeepCopier extends SqlScopedShuttle { } /** Copies a list of nodes. */ - public static SqlNodeList copy(SqlValidatorScope scope, SqlNodeList list) { + public static @Nullable SqlNodeList copy(SqlValidatorScope scope, SqlNodeList list) { //noinspection deprecation - return (SqlNodeList) list.accept(new DeepCopier(scope)); + return (@Nullable SqlNodeList) list.accept(new DeepCopier(scope)); } @Override public SqlNode visit(SqlNodeList list) { @@ -1243,7 +1263,7 @@ public static SqlNodeList copy(SqlValidatorScope scope, SqlNodeList list) { // Override to copy all arguments regardless of whether visitor changes // them. @Override protected SqlNode visitScoped(SqlCall call) { - ArgHandler argHandler = + CallCopyingArgHandler argHandler = new CallCopyingArgHandler(call, true); call.getOperator().acceptCall(this, call, false, argHandler); return argHandler.result(); @@ -1281,7 +1301,7 @@ public static SqlNodeList copy(SqlValidatorScope scope, SqlNodeList list) { /** Suggests candidates for unique names, given the number of attempts so far * and the number of expressions in the project list. */ public interface Suggester { - String apply(String original, int attempt, int size); + String apply(@Nullable String original, int attempt, int size); } public static final Suggester EXPR_SUGGESTER = @@ -1310,7 +1330,7 @@ private static class ExplicitRowTypeTable extends AbstractTable { private final RelDataType rowType; ExplicitRowTypeTable(RelDataType rowType) { - this.rowType = Objects.requireNonNull(rowType); + this.rowType = requireNonNull(rowType); } @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) { @@ -1325,7 +1345,7 @@ private static class ExplicitTableSchema extends AbstractSchema { private final Map tableMap; ExplicitTableSchema(Map tableMap) { - this.tableMap = Objects.requireNonNull(tableMap); + this.tableMap = requireNonNull(tableMap); } @Override protected Map getTableMap() { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java index f5d4fca328f9..d24a218ccf51 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorWithHints.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.parser.SqlParserPos; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -60,7 +62,7 @@ public interface SqlValidatorWithHints extends SqlValidator { * name for * @return a string of the fully qualified name of the {@link SqlIdentifier} * if the Parser position represents a valid {@link SqlIdentifier}. Else - * return an empty string + * return null */ - SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos); + @Nullable SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java index 0ca3ae77e068..58715a6327b9 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlNode; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.apache.calcite.util.Static.RESOURCE; /** @@ -65,7 +67,7 @@ public class TableConstructorNamespace extends AbstractNamespace { return tableConstructorRowType; } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return values; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java index 3ff1ed667c57..7caee2c38756 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java @@ -31,6 +31,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Map; import java.util.Objects; @@ -65,7 +67,7 @@ private TableNamespace(SqlValidatorImpl validator, SqlValidatorTable table, return builder.build(); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { // This is the only kind of namespace not based on a node in the parse tree. return null; } @@ -104,7 +106,9 @@ public TableNamespace extend(SqlNodeList extendList) { final RelOptTable relOptTable = ((RelOptTable) table).extend(extendedFields); final SqlValidatorTable validatorTable = - relOptTable.unwrap(SqlValidatorTable.class); + Objects.requireNonNull( + relOptTable.unwrap(SqlValidatorTable.class), + () -> "cant unwrap SqlValidatorTable from " + relOptTable); return new TableNamespace(validator, validatorTable, ImmutableList.of()); } return new TableNamespace(validator, table, extendedFields); @@ -115,7 +119,9 @@ public TableNamespace extend(SqlNodeList extendList) { * columns of the underlying table. */ private RelDataType getBaseRowType() { - final Table schemaTable = table.unwrap(Table.class); + final Table schemaTable = Objects.requireNonNull( + table.unwrap(Table.class), + () -> "can't unwrap Table from " + table); if (schemaTable instanceof ModifiableViewTable) { final Table underlying = ((ModifiableViewTable) schemaTable).unwrap(Table.class); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java b/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java index c8eb339834e8..113da7adf2f5 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java @@ -19,6 +19,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlSelect; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -50,7 +52,7 @@ class TableScope extends ListScope { return node; } - @Override public boolean isWithin(SqlValidatorScope scope2) { + @Override public boolean isWithin(@Nullable SqlValidatorScope scope2) { if (this == scope2) { return true; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java index bb80bf6998c9..531ba53a9028 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java @@ -22,6 +22,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlUnnestOperator; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Namespace for UNNEST. */ @@ -47,13 +49,16 @@ class UnnestNamespace extends AbstractNamespace { //~ Methods ---------------------------------------------------------------- - @Override public SqlValidatorTable getTable() { + @Override public @Nullable SqlValidatorTable getTable() { final SqlNode toUnnest = unnest.operand(0); if (toUnnest instanceof SqlIdentifier) { // When operand of SqlIdentifier type does not have struct, fake a table // for UnnestNamespace final SqlIdentifier id = (SqlIdentifier) toUnnest; final SqlQualified qualified = this.scope.fullyQualify(id); + if (qualified.namespace == null) { + return null; + } return qualified.namespace.getTable(); } return null; @@ -68,7 +73,7 @@ class UnnestNamespace extends AbstractNamespace { return toStruct(type, unnest); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return unnest; } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java index cd3fc4c88088..4519b4bcb047 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java @@ -21,9 +21,12 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; import org.apache.calcite.sql.SqlWithItem; import org.apache.calcite.util.Pair; +import org.checkerframework.checker.nullness.qual.Nullable; + /** Very similar to {@link AliasNamespace}. */ class WithItemNamespace extends AbstractNamespace { private final SqlWithItem withItem; @@ -36,22 +39,23 @@ class WithItemNamespace extends AbstractNamespace { @Override protected RelDataType validateImpl(RelDataType targetRowType) { final SqlValidatorNamespace childNs = - validator.getNamespace(withItem.query); + validator.getNamespaceOrThrow(withItem.query); final RelDataType rowType = childNs.getRowTypeSansSystemColumns(); - if (withItem.columnList == null) { + SqlNodeList columnList = withItem.columnList; + if (columnList == null) { return rowType; } final RelDataTypeFactory.Builder builder = validator.getTypeFactory().builder(); for (Pair pair - : Pair.zip(withItem.columnList, rowType.getFieldList())) { + : Pair.zip(columnList, rowType.getFieldList())) { builder.add(((SqlIdentifier) pair.left).getSimple(), pair.right.getType()); } return builder.build(); } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return withItem; } @@ -62,7 +66,7 @@ class WithItemNamespace extends AbstractNamespace { final RelDataType underlyingRowType = validator.getValidatedNodeType(withItem.query); int i = 0; - for (RelDataTypeField field : rowType.getFieldList()) { + for (RelDataTypeField field : getRowType().getFieldList()) { if (field.getName().equals(name)) { return underlyingRowType.getFieldList().get(i).getName(); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java index 364116a4e5ce..6cf6cca4b0ba 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java @@ -22,6 +22,8 @@ import org.apache.calcite.sql.SqlWithItem; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Namespace for WITH clause. */ @@ -60,7 +62,7 @@ public class WithNamespace extends AbstractNamespace { return rowType; } - @Override public SqlNode getNode() { + @Override public @Nullable SqlNode getNode() { return with; } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java b/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java index 35cc98a3a911..b42df0fcb41a 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java @@ -20,6 +20,8 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlWithItem; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** Scope providing the objects that are available after evaluating an item @@ -47,7 +49,7 @@ class WithScope extends ListScope { return withItem; } - @Override public SqlValidatorNamespace getTableNamespace(List names) { + @Override public @Nullable SqlValidatorNamespace getTableNamespace(List names) { if (names.size() == 1 && names.get(0).equals(withItem.name.getSimple())) { return validator.getNamespace(withItem); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java index 858bb3c50917..ac8e59721b4d 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java @@ -42,11 +42,16 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; + +import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation; + +import static java.util.Objects.requireNonNull; /** * Base class for all the type coercion rules. If you want to have a custom type coercion rules, @@ -71,8 +76,8 @@ public abstract class AbstractTypeCoercion implements TypeCoercion { //~ Constructors ----------------------------------------------------------- AbstractTypeCoercion(RelDataTypeFactory typeFactory, SqlValidator validator) { - this.factory = Objects.requireNonNull(typeFactory); - this.validator = Objects.requireNonNull(validator); + this.factory = requireNonNull(typeFactory); + this.validator = requireNonNull(validator); } //~ Methods ---------------------------------------------------------------- @@ -82,7 +87,7 @@ public abstract class AbstractTypeCoercion implements TypeCoercion { * we do this base on the fact that validate happens before type coercion. */ protected boolean coerceOperandType( - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlCall call, int index, RelDataType targetType) { @@ -97,6 +102,7 @@ protected boolean coerceOperandType( // Do not support implicit type coercion for dynamic param. return false; } + requireNonNull(scope, "scope"); // Check it early. if (!needToCast(scope, operand, targetType)) { return false; @@ -117,7 +123,7 @@ protected boolean coerceOperandType( * @param commonType common type to coerce to */ protected boolean coerceOperandsType( - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlCall call, RelDataType commonType) { boolean coerced = false; @@ -136,7 +142,7 @@ protected boolean coerceOperandsType( * @param targetType Target type to cast to */ protected boolean coerceColumnType( - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlNodeList nodeList, int index, RelDataType targetType) { @@ -175,6 +181,7 @@ protected boolean coerceColumnType( } } + requireNonNull(scope, "scope is needed for needToCast(scope, operand, targetType)"); if (node instanceof SqlCall) { SqlCall node2 = (SqlCall) node; if (node2.getOperator().kind == SqlKind.AS) { @@ -212,8 +219,8 @@ RelDataType syncAttributes( if (SqlTypeUtil.inCharOrBinaryFamilies(fromType) && SqlTypeUtil.inCharOrBinaryFamilies(toType)) { Charset charset = fromType.getCharset(); - SqlCollation collation = fromType.getCollation(); if (charset != null && SqlTypeUtil.inCharFamily(syncedType)) { + SqlCollation collation = getCollation(fromType); syncedType = factory.createTypeWithCharsetAndCollation(syncedType, charset, collation); @@ -324,7 +331,8 @@ protected void updateInferredColumnType( * * @return tightest common type, i.e. INTEGER + DECIMAL(10, 2) returns DECIMAL(10, 2) */ - @Override public RelDataType getTightestCommonType(RelDataType type1, RelDataType type2) { + @Override public @Nullable RelDataType getTightestCommonType( + @Nullable RelDataType type1, @Nullable RelDataType type2) { if (type1 == null || type2 == null) { return null; } @@ -376,7 +384,7 @@ protected void updateInferredColumnType( : Pair.zip(type1.getFieldList(), type2.getFieldList())) { RelDataType leftType = pair.left.getType(); RelDataType rightType = pair.right.getType(); - RelDataType dataType = getTightestCommonType(leftType, rightType); + RelDataType dataType = getTightestCommonTypeOrThrow(leftType, rightType); boolean isNullable = leftType.isNullable() || rightType.isNullable(); fields.add(factory.createTypeWithNullability(dataType, isNullable)); } @@ -393,8 +401,10 @@ protected void updateInferredColumnType( if (SqlTypeUtil.isMap(type1) && SqlTypeUtil.isMap(type2)) { if (SqlTypeUtil.equalSansNullability(factory, type1, type2)) { - RelDataType keyType = getTightestCommonType(type1.getKeyType(), type2.getKeyType()); - RelDataType valType = getTightestCommonType(type1.getValueType(), type2.getValueType()); + RelDataType keyType = + getTightestCommonTypeOrThrow(type1.getKeyType(), type2.getKeyType()); + RelDataType valType = + getTightestCommonTypeOrThrow(type1.getValueType(), type2.getValueType()); resultType = factory.createMapType(keyType, valType); } } @@ -402,11 +412,21 @@ protected void updateInferredColumnType( return resultType; } + private RelDataType getTightestCommonTypeOrThrow( + @Nullable RelDataType type1, @Nullable RelDataType type2) { + return requireNonNull(getTightestCommonType(type1, type2), + () -> "expected non-null getTightestCommonType for " + type1 + " and " + type2); + } + /** * Promote all the way to VARCHAR. */ - private RelDataType promoteToVarChar(RelDataType type1, RelDataType type2) { + private @Nullable RelDataType promoteToVarChar( + @Nullable RelDataType type1, @Nullable RelDataType type2) { RelDataType resultType = null; + if (type1 == null || type2 == null) { + return null; + } // No promotion for char and varchar. if (SqlTypeUtil.isCharacter(type1) && SqlTypeUtil.isCharacter(type2)) { return null; @@ -430,7 +450,12 @@ private RelDataType promoteToVarChar(RelDataType type1, RelDataType type2) { * other is not. For date + timestamp operands, use timestamp as common type, * i.e. Timestamp(2017-01-01 00:00 ...) > Date(2018) evaluates to be false. */ - @Override public RelDataType commonTypeForBinaryComparison(RelDataType type1, RelDataType type2) { + @Override public @Nullable RelDataType commonTypeForBinaryComparison( + @Nullable RelDataType type1, @Nullable RelDataType type2) { + if (type1 == null || type2 == null) { + return null; + } + SqlTypeName typeName1 = type1.getSqlTypeName(); SqlTypeName typeName2 = type2.getSqlTypeName(); @@ -508,10 +533,13 @@ private RelDataType promoteToVarChar(RelDataType type1, RelDataType type2) { * is that we allow some precision loss when widening decimal to fractional, * or promote fractional to string type. */ - @Override public RelDataType getWiderTypeForTwo( - RelDataType type1, - RelDataType type2, + @Override public @Nullable RelDataType getWiderTypeForTwo( + @Nullable RelDataType type1, + @Nullable RelDataType type2, boolean stringPromotion) { + if (type1 == null || type2 == null) { + return null; + } RelDataType resultType = getTightestCommonType(type1, type2); if (null == resultType) { resultType = getWiderTypeForDecimal(type1, type2); @@ -542,7 +570,11 @@ private RelDataType promoteToVarChar(RelDataType type1, RelDataType type2) { * you can override it based on the specific system requirement in * {@link org.apache.calcite.rel.type.RelDataTypeSystem}. */ - @Override public RelDataType getWiderTypeForDecimal(RelDataType type1, RelDataType type2) { + @Override public @Nullable RelDataType getWiderTypeForDecimal( + @Nullable RelDataType type1, @Nullable RelDataType type2) { + if (type1 == null || type2 == null) { + return null; + } if (!SqlTypeUtil.isDecimal(type1) && !SqlTypeUtil.isDecimal(type2)) { return null; } @@ -564,7 +596,7 @@ private RelDataType promoteToVarChar(RelDataType type1, RelDataType type2) { * {@link #getWiderTypeForTwo} satisfies the associative law. For instance, * (DATE, INTEGER, VARCHAR) should have VARCHAR as the wider common type. */ - @Override public RelDataType getWiderTypeFor(List typeList, + @Override public @Nullable RelDataType getWiderTypeFor(List typeList, boolean stringPromotion) { assert typeList.size() > 1; RelDataType resultType = typeList.get(0); @@ -633,7 +665,7 @@ boolean canImplicitTypeCast(List types, List familie * @param expected Expected {@link SqlTypeFamily} of registered SqlFunction * @return common type of implicit cast, null if we do not find any */ - public RelDataType implicitCast(RelDataType in, SqlTypeFamily expected) { + public @Nullable RelDataType implicitCast(RelDataType in, SqlTypeFamily expected) { List numericFamilies = ImmutableList.of( SqlTypeFamily.NUMERIC, SqlTypeFamily.DECIMAL, diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java index 3b0d06457250..04c3536b9672 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java @@ -24,6 +24,8 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.validate.SqlValidatorScope; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -54,14 +56,16 @@ public interface TypeCoercion { * * @return common type */ - RelDataType getTightestCommonType(RelDataType type1, RelDataType type2); + @Nullable RelDataType getTightestCommonType( + @Nullable RelDataType type1, @Nullable RelDataType type2); /** * Case2: type widening. The main difference with * {@link #getTightestCommonType} is that we allow * some precision loss when widening decimal to fractional, or promote to string type. */ - RelDataType getWiderTypeForTwo(RelDataType type1, RelDataType type2, boolean stringPromotion); + @Nullable RelDataType getWiderTypeForTwo(@Nullable RelDataType type1, @Nullable RelDataType type2, + boolean stringPromotion); /** * Similar to {@link #getWiderTypeForTwo}, but can handle @@ -71,7 +75,7 @@ public interface TypeCoercion { * {@link #getWiderTypeForTwo} satisfies the associative law. For instance, * (DATE, INTEGER, VARCHAR) should have VARCHAR as the wider common type. */ - RelDataType getWiderTypeFor(List typeList, boolean stringPromotion); + @Nullable RelDataType getWiderTypeFor(List typeList, boolean stringPromotion); /** * Finds a wider type when one or both types are DECIMAL type. @@ -85,13 +89,15 @@ public interface TypeCoercion { * you can override it based on the specific system requirement in * {@link org.apache.calcite.rel.type.RelDataTypeSystem}. */ - RelDataType getWiderTypeForDecimal(RelDataType type1, RelDataType type2); + @Nullable RelDataType getWiderTypeForDecimal( + @Nullable RelDataType type1, @Nullable RelDataType type2); /** * Determines common type for a comparison operator whose operands are STRING * type and the other (non STRING) type. */ - RelDataType commonTypeForBinaryComparison(RelDataType type1, RelDataType type2); + @Nullable RelDataType commonTypeForBinaryComparison( + @Nullable RelDataType type1, @Nullable RelDataType type2); /** * Widen a SqlNode ith column type to target type, mainly used for set @@ -103,7 +109,7 @@ public interface TypeCoercion { * @param targetType Target type to cast to */ boolean rowTypeCoercion( - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlNode query, int columnIndex, RelDataType targetType); @@ -183,6 +189,6 @@ boolean userDefinedFunctionCoercion(SqlValidatorScope scope, SqlCall call, * @param targetRowType Target row type * @param query The query, either an INSERT or UPDATE */ - boolean querySourceCoercion(SqlValidatorScope scope, + boolean querySourceCoercion(@Nullable SqlValidatorScope scope, RelDataType sourceRowType, RelDataType targetRowType, SqlNode query); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java index a602337fe99f..ed6f5777a5d0 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java @@ -31,6 +31,7 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlSelect; import org.apache.calcite.sql.SqlUpdate; +import org.apache.calcite.sql.SqlUtil; import org.apache.calcite.sql.SqlWith; import org.apache.calcite.sql.fun.SqlCase; import org.apache.calcite.sql.parser.SqlParserPos; @@ -41,6 +42,8 @@ import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.AbstractList; import java.util.ArrayList; @@ -48,6 +51,11 @@ import java.util.List; import java.util.stream.Collectors; +import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getScope; +import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getSelectList; + +import static java.util.Objects.requireNonNull; + /** * Default implementation of Calcite implicit type cast. */ @@ -80,7 +88,7 @@ public TypeCoercionImpl(RelDataTypeFactory typeFactory, SqlValidator validator) * @param targetType Target type to cast to */ @Override public boolean rowTypeCoercion( - SqlValidatorScope scope, + @Nullable SqlValidatorScope scope, SqlNode query, int columnIndex, RelDataType targetType) { @@ -89,7 +97,7 @@ public TypeCoercionImpl(RelDataTypeFactory typeFactory, SqlValidator validator) case SELECT: SqlSelect selectNode = (SqlSelect) query; SqlValidatorScope scope1 = validator.getSelectScope(selectNode); - if (!coerceColumnType(scope1, selectNode.getSelectList(), columnIndex, targetType)) { + if (!coerceColumnType(scope1, getSelectList(selectNode), columnIndex, targetType)) { return false; } updateInferredColumnType(scope1, query, columnIndex, targetType); @@ -100,7 +108,8 @@ public TypeCoercionImpl(RelDataTypeFactory typeFactory, SqlValidator validator) return false; } } - updateInferredColumnType(scope, query, columnIndex, targetType); + updateInferredColumnType( + requireNonNull(scope, "scope"), query, columnIndex, targetType); return true; case WITH: SqlNode body = ((SqlWith) query).body; @@ -115,7 +124,8 @@ public TypeCoercionImpl(RelDataTypeFactory typeFactory, SqlValidator validator) && rowTypeCoercion(scope, operand1, columnIndex, targetType); // Update the nested SET operator node type. if (coerced) { - updateInferredColumnType(scope, query, columnIndex, targetType); + updateInferredColumnType( + requireNonNull(scope, "scope"), query, columnIndex, targetType); } return coerced; default: @@ -259,7 +269,7 @@ protected boolean binaryArithmeticWithStrings( * For operand data types (type1, type2, type3), deduce the common type type4 * from type1 and type2, then common type type5 from type4 and type3. */ - protected RelDataType commonTypeForComparison(List dataTypes) { + protected @Nullable RelDataType commonTypeForComparison(List dataTypes) { assert dataTypes.size() > 2; final RelDataType type1 = dataTypes.get(0); final RelDataType type2 = dataTypes.get(1); @@ -332,10 +342,11 @@ protected boolean booleanEquality(SqlCallBinding binding, SqlNode lNode = binding.operand(0); SqlNode rNode = binding.operand(1); if (SqlTypeUtil.isNumeric(left) + && !SqlUtil.isNullLiteral(lNode, false) && SqlTypeUtil.isBoolean(right)) { // Case1: numeric literal and boolean if (lNode.getKind() == SqlKind.LITERAL) { - BigDecimal val = ((SqlLiteral) lNode).bigDecimalValue(); + BigDecimal val = ((SqlLiteral) lNode).getValueAs(BigDecimal.class); if (val.compareTo(BigDecimal.ONE) == 0) { SqlNode lNode1 = SqlLiteral.createBoolean(true, SqlParserPos.ZERO); binding.getCall().setOperand(0, lNode1); @@ -351,10 +362,11 @@ protected boolean booleanEquality(SqlCallBinding binding, } if (SqlTypeUtil.isNumeric(right) + && !SqlUtil.isNullLiteral(rNode, false) && SqlTypeUtil.isBoolean(left)) { // Case1: literal numeric + boolean if (rNode.getKind() == SqlKind.LITERAL) { - BigDecimal val = ((SqlLiteral) rNode).bigDecimalValue(); + BigDecimal val = ((SqlLiteral) rNode).getValueAs(BigDecimal.class); if (val.compareTo(BigDecimal.ONE) == 0) { SqlNode rNode1 = SqlLiteral.createBoolean(true, SqlParserPos.ZERO); binding.getCall().setOperand(1, rNode1); @@ -383,14 +395,15 @@ protected boolean booleanEquality(SqlCallBinding binding, SqlCase caseCall = (SqlCase) callBinding.getCall(); SqlNodeList thenList = caseCall.getThenOperands(); List argTypes = new ArrayList(); + SqlValidatorScope scope = getScope(callBinding); for (SqlNode node : thenList) { argTypes.add( validator.deriveType( - callBinding.getScope(), node)); + scope, node)); } - SqlNode elseOp = caseCall.getElseOperand(); - RelDataType elseOpType = validator.deriveType( - callBinding.getScope(), caseCall.getElseOperand()); + SqlNode elseOp = requireNonNull(caseCall.getElseOperand(), + () -> "getElseOperand() is null for " + caseCall); + RelDataType elseOpType = validator.deriveType(scope, elseOp); argTypes.add(elseOpType); // Entering this method means we have already got a wider type, recompute it here // just to make the interface more clear. @@ -398,10 +411,10 @@ protected boolean booleanEquality(SqlCallBinding binding, if (null != widerType) { boolean coerced = false; for (int i = 0; i < thenList.size(); i++) { - coerced = coerceColumnType(callBinding.getScope(), thenList, i, widerType) || coerced; + coerced = coerceColumnType(scope, thenList, i, widerType) || coerced; } - if (needToCast(callBinding.getScope(), elseOp, widerType)) { - coerced = coerceOperandType(callBinding.getScope(), caseCall, 3, widerType) + if (needToCast(scope, elseOp, widerType)) { + coerced = coerceOperandType(scope, caseCall, 3, widerType) || coerced; } return coerced; @@ -502,7 +515,9 @@ protected boolean booleanEquality(SqlCallBinding binding, if (node1.getKind() == SqlKind.ROW) { assert node1 instanceof SqlCall; if (coerceOperandType(scope, (SqlCall) node1, i, desired)) { - updateInferredColumnType(scope, node1, i, widenTypes.get(i)); + updateInferredColumnType( + requireNonNull(scope, "scope"), + node1, i, widenTypes.get(i)); coerced = true; } } else { @@ -519,7 +534,9 @@ protected boolean booleanEquality(SqlCallBinding binding, listCoerced = coerceOperandType(scope, (SqlCall) node, i, desired) || listCoerced; } if (listCoerced) { - updateInferredColumnType(scope, node2, i, desired); + updateInferredColumnType( + requireNonNull(scope, "scope"), + node2, i, desired); } } else { for (int j = 0; j < ((SqlNodeList) node2).size(); j++) { @@ -566,8 +583,9 @@ && coerceOperandType(binding.getScope(), binding.getCall(), i, implicitType) */ @Override public boolean userDefinedFunctionCoercion(SqlValidatorScope scope, SqlCall call, SqlFunction function) { - final SqlOperandMetadata operandMetadata = - (SqlOperandMetadata) function.getOperandTypeChecker(); + final SqlOperandMetadata operandMetadata = requireNonNull( + (SqlOperandMetadata) function.getOperandTypeChecker(), + () -> "getOperandTypeChecker is not defined for " + function); final List paramTypes = operandMetadata.paramTypes(scope.getValidator().getTypeFactory()); boolean coerced = false; @@ -591,7 +609,7 @@ && coerceOperandType(binding.getScope(), binding.getCall(), i, implicitType) return coerced; } - @Override public boolean querySourceCoercion(SqlValidatorScope scope, + @Override public boolean querySourceCoercion(@Nullable SqlValidatorScope scope, RelDataType sourceRowType, RelDataType targetRowType, SqlNode query) { final List sourceFields = sourceRowType.getFieldList(); final List targetFields = targetRowType.getFieldList(); @@ -623,7 +641,7 @@ && coerceOperandType(binding.getScope(), binding.getCall(), i, implicitType) * @param targetType Target type */ private boolean coerceSourceRowType( - SqlValidatorScope sourceScope, + @Nullable SqlValidatorScope sourceScope, SqlNode query, int columnIndex, RelDataType targetType) { diff --git a/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java b/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java index 92defcf1180f..f6ba9b7dd030 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/DeduplicateCorrelateVariables.java @@ -27,11 +27,15 @@ import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnderInitialization; + /** * Rewrites relations to ensure the same correlation is referenced by the same * correlation variable. */ public class DeduplicateCorrelateVariables extends RelHomogeneousShuttle { + @NotOnlyInitialized private final RexShuttle dedupRex; /** Creates a DeduplicateCorrelateVariables. */ @@ -64,11 +68,12 @@ private static class DeduplicateCorrelateVariablesShuttle extends RexShuttle { private final RexBuilder builder; private final CorrelationId canonicalId; private final ImmutableSet alternateIds; + @NotOnlyInitialized private final DeduplicateCorrelateVariables shuttle; private DeduplicateCorrelateVariablesShuttle(RexBuilder builder, CorrelationId canonicalId, ImmutableSet alternateIds, - DeduplicateCorrelateVariables shuttle) { + @UnderInitialization DeduplicateCorrelateVariables shuttle) { this.builder = builder; this.canonicalId = canonicalId; this.alternateIds = alternateIds; diff --git a/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java b/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java index f7c3190f6aee..e4e632fa53bb 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java @@ -23,6 +23,8 @@ import org.apache.calcite.schema.ColumnStrategy; import org.apache.calcite.sql.SqlFunction; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.function.BiFunction; @@ -89,7 +91,7 @@ RexNode newColumnDefaultValue( * * @see #newColumnDefaultValue(RelOptTable, int, InitializerContext) */ - BiFunction postExpressionConversionHook(); + @Nullable BiFunction postExpressionConversionHook(); /** * Creates an expression which evaluates to the initializer expression for a diff --git a/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java b/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java index 8992f04890d7..13ac72c37ba1 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java @@ -23,6 +23,8 @@ import org.apache.calcite.schema.ColumnStrategy; import org.apache.calcite.sql.SqlFunction; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.function.BiFunction; @@ -61,7 +63,8 @@ public NullInitializerExpressionFactory() { return context.getRexBuilder().makeNullLiteral(fieldType); } - @Override public BiFunction postExpressionConversionHook() { + @Override public @Nullable BiFunction< + InitializerContext, RelNode, RelNode> postExpressionConversionHook() { return null; } diff --git a/core/src/main/java/org/apache/calcite/sql2rel/ReflectiveConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/ReflectiveConvertletTable.java index 47d977882d26..e8f0040ce955 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/ReflectiveConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/ReflectiveConvertletTable.java @@ -24,12 +24,17 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link SqlRexConvertletTable} which uses reflection to call * any method of the form public RexNode convertXxx(ConvertletContext, @@ -80,8 +85,10 @@ private void registerNodeTypeMethod(final Method method) { } map.put(parameterType, (SqlRexConvertlet) (cx, call) -> { try { - return (RexNode) method.invoke(ReflectiveConvertletTable.this, + RexNode result = (RexNode) method.invoke(ReflectiveConvertletTable.this, cx, call); + return requireNonNull(result, () -> "null result from " + method + + " for call " + call); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("while converting " + call, e); } @@ -121,15 +128,17 @@ private void registerOpTypeMethod(final Method method) { } map.put(opClass, (SqlRexConvertlet) (cx, call) -> { try { - return (RexNode) method.invoke(ReflectiveConvertletTable.this, + RexNode result = (RexNode) method.invoke(ReflectiveConvertletTable.this, cx, call.getOperator(), call); + return requireNonNull(result, () -> "null result from " + method + + " for call " + call); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("while converting " + call, e); } }); } - @Override public SqlRexConvertlet get(SqlCall call) { + @Override public @Nullable SqlRexConvertlet get(SqlCall call) { SqlRexConvertlet convertlet; final SqlOperator op = call.getOperator(); @@ -171,7 +180,9 @@ private void registerOpTypeMethod(final Method method) { * {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#MINUS} * @param convertlet Convertlet */ - protected void registerOp(SqlOperator op, SqlRexConvertlet convertlet) { + protected void registerOp( + @UnderInitialization ReflectiveConvertletTable this, + SqlOperator op, SqlRexConvertlet convertlet) { map.put(op, convertlet); } @@ -181,7 +192,9 @@ protected void registerOp(SqlOperator op, SqlRexConvertlet convertlet) { * @param alias Operator which is alias * @param target Operator to translate calls to */ - protected void addAlias(final SqlOperator alias, final SqlOperator target) { + protected void addAlias( + @UnderInitialization ReflectiveConvertletTable this, + final SqlOperator alias, final SqlOperator target) { map.put( alias, (SqlRexConvertlet) (cx, call) -> { Preconditions.checkArgument(call.getOperator() == alias, diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java index 5a6b364d8928..bf6830c88af6 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java @@ -103,6 +103,7 @@ import com.google.common.collect.Sets; import com.google.common.collect.SortedSetMultimap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.math.BigDecimal; @@ -118,7 +119,8 @@ import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; -import javax.annotation.Nonnull; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; /** * RelDecorrelator replaces all correlated expressions (corExp) in a relational @@ -152,12 +154,12 @@ public class RelDecorrelator implements ReflectiveVisitor { // map built during translation protected CorelMap cm; - protected final ReflectUtil.MethodDispatcher dispatcher = + protected final ReflectUtil.MethodDispatcher<@Nullable Frame> dispatcher = ReflectUtil.createMethodDispatcher(Frame.class, getVisitor(), "decorrelateRel", RelNode.class); // The rel which is being visited - protected RelNode currentRel; + protected @Nullable RelNode currentRel; protected final Context context; @@ -228,7 +230,7 @@ public static RelNode decorrelateQuery(RelNode rootRel, return newRootRel; } - private void setCurrent(RelNode root, Correlate corRel) { + private void setCurrent(RelNode root, @Nullable Correlate corRel) { currentRel = corRel; if (corRel != null) { cm = new CorelMapBuilder().build(Util.first(root, corRel)); @@ -308,7 +310,7 @@ protected RelNode decorrelate(RelNode root) { return root; } - private Function2 createCopyHook() { + private Function2 createCopyHook() { return (oldNode, newNode) -> { if (cm.mapRefRelToCorRef.containsKey(oldNode)) { cm.mapRefRelToCorRef.putAll(newNode, @@ -395,7 +397,7 @@ protected RexNode removeCorrelationExpr( } /** Fallback if none of the other {@code decorrelateRel} methods match. */ - public Frame decorrelateRel(RelNode rel) { + public @Nullable Frame decorrelateRel(RelNode rel) { RelNode newRel = rel.copy(rel.getTraitSet(), rel.getInputs()); if (rel.getInputs().size() > 0) { @@ -423,7 +425,7 @@ public Frame decorrelateRel(RelNode rel) { ImmutableSortedMap.of()); } - public Frame decorrelateRel(Sort rel) { + public @Nullable Frame decorrelateRel(Sort rel) { // // Rewrite logic: // @@ -468,16 +470,16 @@ public Frame decorrelateRel(Sort rel) { return register(rel, newSort, frame.oldToNewOutputs, frame.corDefOutputs); } - public Frame decorrelateRel(Values rel) { + public @Nullable Frame decorrelateRel(Values rel) { // There are no inputs, so rel does not need to be changed. return null; } - public Frame decorrelateRel(LogicalAggregate rel) { + public @Nullable Frame decorrelateRel(LogicalAggregate rel) { return decorrelateRel((Aggregate) rel); } - public Frame decorrelateRel(Aggregate rel) { + public @Nullable Frame decorrelateRel(Aggregate rel) { // // Rewrite logic: // @@ -683,7 +685,7 @@ private static void shiftMapping(Map mapping, int startIndex, } } - public Frame getInvoke(RelNode r, RelNode parent) { + public @Nullable Frame getInvoke(RelNode r, @Nullable RelNode parent) { final Frame frame = dispatcher.invoke(r); currentRel = parent; if (frame != null && parent != null && r instanceof Sort) { @@ -703,7 +705,7 @@ public Frame getInvoke(RelNode r, RelNode parent) { } /** Returns a literal output field, or null if it is not literal. */ - private static RexLiteral projectedLiteral(RelNode rel, int i) { + private static @Nullable RexLiteral projectedLiteral(RelNode rel, int i) { if (rel instanceof Project) { final Project project = (Project) rel; final RexNode node = project.getProjects().get(i); @@ -714,11 +716,11 @@ private static RexLiteral projectedLiteral(RelNode rel, int i) { return null; } - public Frame decorrelateRel(LogicalProject rel) { + public @Nullable Frame decorrelateRel(LogicalProject rel) { return decorrelateRel((Project) rel); } - public Frame decorrelateRel(Project rel) { + public @Nullable Frame decorrelateRel(Project rel) { // // Rewrite logic: // @@ -785,7 +787,7 @@ public Frame decorrelateRel(Project rel) { * generated * @return RelNode the root of the resultant RelNode tree */ - private RelNode createValueGenerator( + private @Nullable RelNode createValueGenerator( Iterable correlations, int valueGenFieldOffset, NavigableMap corDefOutputs) { @@ -889,7 +891,7 @@ private RelNode createValueGenerator( return r; } - private Frame getFrame(RelNode r, boolean safe) { + private @Nullable Frame getFrame(RelNode r, boolean safe) { final Frame frame = map.get(r); if (frame == null && safe) { return new Frame(r, r, ImmutableSortedMap.of(), @@ -1068,25 +1070,25 @@ private boolean isWidening(RelDataType type, RelDataType type1) { && type.getPrecision() >= type1.getPrecision(); } - public Frame decorrelateRel(LogicalSnapshot rel) { + public @Nullable Frame decorrelateRel(LogicalSnapshot rel) { if (RexUtil.containsCorrelation(rel.getPeriod())) { return null; } return decorrelateRel((RelNode) rel); } - public Frame decorrelateRel(LogicalTableFunctionScan rel) { + public @Nullable Frame decorrelateRel(LogicalTableFunctionScan rel) { if (RexUtil.containsCorrelation(rel.getCall())) { return null; } return decorrelateRel((RelNode) rel); } - public Frame decorrelateRel(LogicalFilter rel) { + public @Nullable Frame decorrelateRel(LogicalFilter rel) { return decorrelateRel((Filter) rel); } - public Frame decorrelateRel(Filter rel) { + public @Nullable Frame decorrelateRel(Filter rel) { // // Rewrite logic: // @@ -1125,7 +1127,7 @@ public Frame decorrelateRel(Filter rel) { // Replace the filter expression to reference output of the join // Map filter to the new filter over join relBuilder.push(frame.r) - .filter(decorrelateExpr(currentRel, map, cm2, rel.getCondition())); + .filter(decorrelateExpr(castNonNull(currentRel), map, cm2, rel.getCondition())); // Filter does not change the input ordering. // Filter rel does not permute the input. @@ -1135,11 +1137,11 @@ public Frame decorrelateRel(Filter rel) { frame.corDefOutputs); } - public Frame decorrelateRel(LogicalCorrelate rel) { + public @Nullable Frame decorrelateRel(LogicalCorrelate rel) { return decorrelateRel((Correlate) rel); } - public Frame decorrelateRel(Correlate rel) { + public @Nullable Frame decorrelateRel(Correlate rel) { // // Rewrite logic: // @@ -1237,11 +1239,11 @@ public Frame decorrelateRel(Correlate rel) { return register(rel, newJoin, mapOldToNewOutputs, corDefOutputs); } - public Frame decorrelateRel(LogicalJoin rel) { + public @Nullable Frame decorrelateRel(LogicalJoin rel) { return decorrelateRel((Join) rel); } - public Frame decorrelateRel(Join rel) { + public @Nullable Frame decorrelateRel(Join rel) { // For SEMI/ANTI join decorrelate it's input directly, // because the correlate variables can only be propagated from // the left side, which is not supported yet. @@ -1270,7 +1272,7 @@ public Frame decorrelateRel(Join rel) { .push(leftFrame.r) .push(rightFrame.r) .join(rel.getJoinType(), - decorrelateExpr(currentRel, map, cm, rel.getCondition()), + decorrelateExpr(castNonNull(currentRel), map, cm, rel.getCondition()), ImmutableSet.of()) .hints(rel.getHints()) .build(); @@ -1471,9 +1473,9 @@ private RelNode aggregateCorrelatorOutput( */ private boolean checkCorVars( Correlate correlate, - Project project, - Filter filter, - List correlatedJoinKeys) { + @Nullable Project project, + @Nullable Filter filter, + @Nullable List correlatedJoinKeys) { if (filter != null) { assert correlatedJoinKeys != null; @@ -1521,9 +1523,7 @@ private boolean checkCorVars( * @param correlate Correlate */ private void removeCorVarFromTree(Correlate correlate) { - if (cm.mapCorToCorRel.get(correlate.getCorrelationId()) == correlate) { - cm.mapCorToCorRel.remove(correlate.getCorrelationId()); - } + cm.mapCorToCorRel.remove(correlate.getCorrelationId(), correlate); } /** @@ -1645,13 +1645,13 @@ private class RemoveCorrelationRexShuttle extends RexShuttle { final RexBuilder rexBuilder; final RelDataTypeFactory typeFactory; final boolean projectPulledAboveLeftCorrelator; - final RexInputRef nullIndicator; + final @Nullable RexInputRef nullIndicator; final ImmutableSet isCount; RemoveCorrelationRexShuttle( RexBuilder rexBuilder, boolean projectPulledAboveLeftCorrelator, - RexInputRef nullIndicator, + @Nullable RexInputRef nullIndicator, Set isCount) { this.projectPulledAboveLeftCorrelator = projectPulledAboveLeftCorrelator; @@ -1663,7 +1663,7 @@ private class RemoveCorrelationRexShuttle extends RexShuttle { private RexNode createCaseExpression( RexInputRef nullInputRef, - RexLiteral lit, + @Nullable RexLiteral lit, RexNode rexNode) { RexNode[] caseOperands = new RexNode[3]; @@ -1748,7 +1748,7 @@ private RexNode createCaseExpression( RexInputRef newInputRef = new RexInputRef(leftInputFieldCount + pos, newType); - if ((isCount != null) && isCount.contains(pos)) { + if (isCount.contains(pos)) { return createCaseExpression( newInputRef, rexBuilder.makeExactLiteral(BigDecimal.ZERO), @@ -2692,7 +2692,7 @@ static class CorRef implements Comparable { return Objects.hash(uniqueKey, corr, field); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof CorRef && uniqueKey == ((CorRef) o).uniqueKey @@ -2700,7 +2700,7 @@ static class CorRef implements Comparable { && field == ((CorRef) o).field; } - @Override public int compareTo(@Nonnull CorRef o) { + @Override public int compareTo(CorRef o) { int c = corr.compareTo(o.corr); if (c != 0) { return c; @@ -2735,14 +2735,14 @@ static class CorDef implements Comparable { return Objects.hash(corr, field); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof CorDef && corr == ((CorDef) o).corr && field == ((CorDef) o).field; } - @Override public int compareTo(@Nonnull CorDef o) { + @Override public int compareTo(CorDef o) { int c = corr.compareTo(o.corr); if (c != 0) { return c; @@ -2794,7 +2794,7 @@ private CorelMap(Multimap mapRefRelToCorRef, } @SuppressWarnings("UndefinedEquals") - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof CorelMap // TODO: Multimap does not have well-defined equals behavior diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java index 9f91cedb5b70..18e33f013a30 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java @@ -74,6 +74,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; @@ -120,10 +122,11 @@ public class RelFieldTrimmer implements ReflectiveVisitor { * * @param validator Validator */ - public RelFieldTrimmer(SqlValidator validator, RelBuilder relBuilder) { + public RelFieldTrimmer(@Nullable SqlValidator validator, RelBuilder relBuilder) { Util.discard(validator); // may be useful one day this.relBuilder = relBuilder; - this.trimFieldsDispatcher = + @SuppressWarnings("argument.type.incompatible") + ReflectUtil.MethodDispatcher dispatcher = ReflectUtil.createMethodDispatcher( TrimResult.class, this, @@ -131,10 +134,11 @@ public RelFieldTrimmer(SqlValidator validator, RelBuilder relBuilder) { RelNode.class, ImmutableBitSet.class, Set.class); + this.trimFieldsDispatcher = dispatcher; } @Deprecated // to be removed before 2.0 - public RelFieldTrimmer(SqlValidator validator, + public RelFieldTrimmer(@Nullable SqlValidator validator, RelOptCluster cluster, RelFactories.ProjectFactory projectFactory, RelFactories.FilterFactory filterFactory, @@ -530,7 +534,8 @@ protected TrimResult dummyProject(int fieldCount, RelNode input) { * @param originalRelNode Source RelNode for hint propagation (or null if no propagation needed) * @return Dummy project */ - protected TrimResult dummyProject(int fieldCount, RelNode input, RelNode originalRelNode) { + protected TrimResult dummyProject(int fieldCount, RelNode input, + @Nullable RelNode originalRelNode) { final RelOptCluster cluster = input.getCluster(); final Mapping mapping = Mappings.create(MappingType.INVERSE_SURJECTION, fieldCount, 1); diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java index 1de67608b8d7..6d6cac92a90d 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java @@ -81,6 +81,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.SortedSetMultimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -89,9 +91,12 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.SortedMap; import java.util.SortedSet; +import static java.util.Objects.requireNonNull; + // TODO jvs 10-Feb-2005: factor out generic rewrite helper, with the // ability to map between old and new rels and field ordinals. Also, // for now need to prohibit queries which return UDT instances. @@ -138,10 +143,10 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor { private final boolean restructure; private final Map oldToNewRelMap = new HashMap<>(); - private RelNode currentRel; + private @Nullable RelNode currentRel; private int iRestructureInput; @SuppressWarnings("unused") - private RelDataType flattenedRootType; + private @Nullable RelDataType flattenedRootType; boolean restructured; private final RelOptTable.ToRelContext toRelContext; @@ -169,6 +174,10 @@ public RelStructuredTypeFlattener( //~ Methods ---------------------------------------------------------------- + private RelNode getCurrentRelOrThrow() { + return requireNonNull(currentRel, "currentRel"); + } + public void updateRelInMap( SortedSetMultimap mapRefRelToCorVar) { for (RelNode rel : Lists.newArrayList(mapRefRelToCorVar.keySet())) { @@ -267,7 +276,9 @@ protected void setNewForOldRel(RelNode oldRel, RelNode newRel) { } protected RelNode getNewForOldRel(RelNode oldRel) { - return oldToNewRelMap.get(oldRel); + return requireNonNull( + oldToNewRelMap.get(oldRel), + () -> "newRel not found for " + oldRel); } /** @@ -294,10 +305,9 @@ protected int getNewForOldInput(int oldOrdinal) { * @return flat type with new ordinal relative to new inputs */ private Ord getNewFieldForOldInput(int oldOrdinal, int innerOrdinal) { - assert currentRel != null; // sum of predecessors post flatten sizes points to new ordinal // of flat field or first field of flattened struct - final int postFlatteningOrdinal = currentRel.getInputs().stream() + final int postFlatteningOrdinal = getCurrentRelOrThrow().getInputs().stream() .flatMap(node -> node.getRowType().getFieldList().stream()) .limit(oldOrdinal) .map(RelDataTypeField::getType) @@ -312,7 +322,7 @@ private Ord getNewFieldForOldInput(int oldOrdinal, int innerOrdinal } private RelDataTypeField getNewInputFieldByNewOrdinal(int newOrdinal) { - return currentRel.getInputs().stream() + return getCurrentRelOrThrow().getInputs().stream() .map(this::getNewForOldRel) .flatMap(node -> node.getRowType().getFieldList().stream()) .skip(newOrdinal) @@ -322,7 +332,7 @@ private RelDataTypeField getNewInputFieldByNewOrdinal(int newOrdinal) { /** Returns whether the old field at index {@code fieldIdx} was not flattened. */ private boolean noFlatteningForInput(int fieldIdx) { - final List inputs = currentRel.getInputs(); + final List inputs = getCurrentRelOrThrow().getInputs(); int fieldCnt = 0; for (RelNode input : inputs) { fieldCnt += input.getRowType().getFieldCount(); @@ -593,7 +603,7 @@ public void rewriteGeneric(RelNode rel) { private void flattenProjections(RewriteRexShuttle shuttle, List exps, - List fieldNames, + @Nullable List fieldNames, String prefix, List> flattenedExps) { for (int i = 0; i < exps.size(); ++i) { @@ -603,7 +613,7 @@ private void flattenProjections(RewriteRexShuttle shuttle, } } - private String extractName(List fieldNames, String prefix, int i) { + private String extractName(@Nullable List fieldNames, String prefix, int i) { String fieldName = (fieldNames == null || fieldNames.get(i) == null) ? ("$" + i) : fieldNames.get(i); @@ -663,7 +673,8 @@ private void flattenProjection(RewriteRexShuttle shuttle, // why we're trying to get range from to. For primitive just one field will be in range. int from = 0; for (RelDataTypeField field : firstOp.getType().getFieldList()) { - if (literalString.equalsIgnoreCase(field.getName())) { + if (Objects.equals(literalString, field.getName()) + || String.CASE_INSENSITIVE_ORDER.compare(literalString, field.getName()) == 0) { int oldOrdinal = ((RexInputRef) firstOp).getIndex(); int to = from + postFlattenSize(field.getType()); for (int newInnerOrdinal = from; newInnerOrdinal < to; newInnerOrdinal++) { @@ -820,7 +831,7 @@ private class RewriteRelVisitor extends RelVisitor { RelStructuredTypeFlattener.class, RelNode.class); - @Override public void visit(RelNode p, int ordinal, RelNode parent) { + @Override public void visit(RelNode p, int ordinal, @Nullable RelNode parent) { // rewrite children first super.visit(p, ordinal, parent); @@ -850,7 +861,7 @@ private class RewriteRexShuttle extends RexShuttle { @Override public RexNode visitInputRef(RexInputRef input) { final int oldIndex = input.getIndex(); final Ord field = getNewFieldForOldInput(oldIndex); - RelDataTypeField inputFieldByOldIndex = currentRel.getInputs().stream() + RelDataTypeField inputFieldByOldIndex = getCurrentRelOrThrow().getInputs().stream() .flatMap(relInput -> relInput.getRowType().getFieldList().stream()) .skip(oldIndex) .findFirst() @@ -990,7 +1001,7 @@ private RelDataType removeDistinct(RelDataType type) { return subQuery.clone(rel); } - private RexNode flattenComparison( + private @Nullable RexNode flattenComparison( RexBuilder rexBuilder, SqlOperator op, List exprs) { diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlRexConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlRexConvertletTable.java index db96cddd59ad..92562019c327 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlRexConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlRexConvertletTable.java @@ -18,6 +18,8 @@ import org.apache.calcite.sql.SqlCall; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Collection of {@link SqlRexConvertlet}s. */ @@ -27,5 +29,5 @@ public interface SqlRexConvertletTable { /** * Returns the convertlet applicable to a given expression. */ - SqlRexConvertlet get(SqlCall call); + @Nullable SqlRexConvertlet get(SqlCall call); } diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java index 39ca7dd030b1..7799f9cb66b9 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -182,6 +182,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.lang.reflect.Type; @@ -201,13 +202,16 @@ import java.util.Objects; import java.util.Set; import java.util.TreeSet; +import java.util.function.BiFunction; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import javax.annotation.Nonnull; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.sql.SqlUtil.stripAs; +import static java.util.Objects.requireNonNull; + /** * Converts a SQL parse tree (consisting of * {@link org.apache.calcite.sql.SqlNode} objects) into a relational algebra @@ -240,13 +244,13 @@ public class SqlToRelConverter { //~ Instance fields -------------------------------------------------------- - protected final SqlValidator validator; + protected final @Nullable SqlValidator validator; protected final RexBuilder rexBuilder; protected final Prepare.CatalogReader catalogReader; protected final RelOptCluster cluster; private SubQueryConverter subQueryConverter; protected final Map leaves = new HashMap<>(); - private final List dynamicParamSqlNodes = new ArrayList<>(); + private final List<@Nullable SqlDynamicParam> dynamicParamSqlNodes = new ArrayList<>(); private final SqlOperatorTable opTab; protected final RelDataTypeFactory typeFactory; private final SqlNodeToRexConverter exprConverter; @@ -314,7 +318,7 @@ public SqlToRelConverter( /* Creates a converter. */ public SqlToRelConverter( RelOptTable.ViewExpander viewExpander, - SqlValidator validator, + @Nullable SqlValidator validator, Prepare.CatalogReader catalogReader, RelOptCluster cluster, SqlRexConvertletTable convertletTable, @@ -331,17 +335,33 @@ public SqlToRelConverter( this.typeFactory = rexBuilder.getTypeFactory(); this.exprConverter = new SqlNodeToRexConverterImpl(convertletTable); this.explainParamCount = 0; - this.config = Objects.requireNonNull(config); + this.config = requireNonNull(config); this.relBuilder = config.getRelBuilderFactory().create(cluster, null) .transform(config.getRelBuilderConfigTransform()); this.hintStrategies = config.getHintStrategyTable(); cluster.setHintStrategies(this.hintStrategies); - this.cluster = Objects.requireNonNull(cluster); + this.cluster = requireNonNull(cluster); } //~ Methods ---------------------------------------------------------------- + private SqlValidator validator() { + return requireNonNull(validator, "validator"); + } + + private T getNamespace(SqlNode node) { + //noinspection unchecked + return (T) requireNonNull( + getNamespaceOrNull(node), + () -> "Namespace is not found for " + node); + } + + @SuppressWarnings("unchecked") + private @Nullable T getNamespaceOrNull(SqlNode node) { + return (@Nullable T) validator().getNamespace(node); + } + /** Returns the RelOptCluster in use. */ public RelOptCluster getCluster() { return cluster; @@ -375,7 +395,7 @@ public RelDataType getDynamicParamType(int index) { if (sqlNode == null) { throw Util.needToImplement("dynamic param type inference"); } - return validator.getValidatedNodeType(sqlNode); + return validator().getValidatedNodeType(sqlNode); } /** @@ -442,9 +462,9 @@ private void checkConvertedType(SqlNode query, RelNode result) { // validator type information associated with its result, // hence the namespace check above.) final List validatedFields = - validator.getValidatedNodeType(query).getFieldList(); + validator().getValidatedNodeType(query).getFieldList(); final RelDataType validatedRowType = - validator.getTypeFactory().createStructType( + validator().getTypeFactory().createStructType( Pair.right(validatedFields), SqlValidatorUtil.uniquify(Pair.left(validatedFields), catalogReader.nameMatcher().isCaseSensitive())); @@ -452,7 +472,7 @@ private void checkConvertedType(SqlNode query, RelNode result) { final List convertedFields = result.getRowType().getFieldList().subList(0, validatedFields.size()); final RelDataType convertedRowType = - validator.getTypeFactory().createStructType(convertedFields); + validator().getTypeFactory().createStructType(convertedFields); if (!RelOptUtil.equal("validated row type", validatedRowType, "converted row type", convertedRowType, Litmus.IGNORE)) { @@ -563,7 +583,7 @@ public RelRoot convertQuery( final boolean needsValidation, final boolean top) { if (needsValidation) { - query = validator.validate(query); + query = validator().validate(query); } RelNode result = convertQueryRecursive(query, top, null).rel; @@ -587,7 +607,7 @@ public RelRoot convertQuery( SqlExplainLevel.EXPPLAN_ATTRIBUTES)); } - final RelDataType validatedRowType = validator.getValidatedNodeType(query); + final RelDataType validatedRowType = validator().getValidatedNodeType(query); List hints = new ArrayList<>(); if (query.getKind() == SqlKind.SELECT) { final SqlSelect select = (SqlSelect) query; @@ -610,8 +630,9 @@ private static boolean isStream(SqlNode query) { public static boolean isOrdered(SqlNode query) { switch (query.getKind()) { case SELECT: - return ((SqlSelect) query).getOrderList() != null - && ((SqlSelect) query).getOrderList().size() > 0; + SqlNodeList orderList = ((SqlSelect) query).getOrderList(); + return orderList != null + && orderList.size() > 0; case WITH: return isOrdered(((SqlWith) query).body); case ORDER_BY: @@ -638,17 +659,17 @@ private RelCollation requiredCollation(RelNode r) { * Converts a SELECT statement's parse tree into a relational expression. */ public RelNode convertSelect(SqlSelect select, boolean top) { - final SqlValidatorScope selectScope = validator.getWhereScope(select); + final SqlValidatorScope selectScope = validator().getWhereScope(select); final Blackboard bb = createBlackboard(selectScope, null, top); convertSelectImpl(bb, select); - return bb.root; + return castNonNull(bb.root); } /** * Factory method for creating translation workspace. */ - protected Blackboard createBlackboard(SqlValidatorScope scope, - Map nameToNodeMap, boolean top) { + protected Blackboard createBlackboard(@Nullable SqlValidatorScope scope, + @Nullable Map nameToNodeMap, boolean top) { return new Blackboard(scope, nameToNodeMap, top); } @@ -677,7 +698,7 @@ protected void convertSelectImpl( final RelCollation collation = cluster.traitSet().canonize(RelCollations.of(collationList)); - if (validator.isAggregate(select)) { + if (validator().isAggregate(select)) { convertAgg( bb, select, @@ -700,7 +721,7 @@ protected void convertSelectImpl( if (select.hasHints()) { final List hints = SqlUtil.getRelHint(hintStrategies, select.getHints()); // Attach the hints to the first Hintable node we found from the root node. - bb.setRoot(bb.root + bb.setRoot(bb.root() .accept( new RelShuttleImpl() { boolean attached = false; @@ -714,7 +735,7 @@ protected void convertSelectImpl( } }), true); } else { - bb.setRoot(bb.root, true); + bb.setRoot(bb.root(), true); } } @@ -771,7 +792,7 @@ private void distinctify( Pair.left(newProjects), Pair.right(newProjects)); bb.root = rel; distinctify(bb, false); - rel = bb.root; + rel = bb.root(); // Create the expressions to reverse the mapping. // Project($0, $1, $0, $2). @@ -782,7 +803,7 @@ private void distinctify( undoProjects.add( Pair.of( new RexInputRef( - squished.get(origin), + castNonNull(squished.get(origin)), field.getType()), field.getName())); } @@ -797,6 +818,7 @@ private void distinctify( return; } + assert rel != null : "rel must not be null, root = " + bb.root; // Usual case: all of the expressions in the SELECT clause are // different. final ImmutableBitSet groupSet = @@ -829,15 +851,15 @@ protected void convertOrder( Blackboard bb, RelCollation collation, List orderExprList, - SqlNode offset, - SqlNode fetch) { + @Nullable SqlNode offset, + @Nullable SqlNode fetch) { if (removeSortInSubQuery(bb.top) || select.getOrderList() == null || select.getOrderList().getList().isEmpty()) { assert removeSortInSubQuery(bb.top) || collation.getFieldCollations().isEmpty(); if ((offset == null || (offset instanceof SqlLiteral - && ((SqlLiteral) offset).bigDecimalValue().equals(BigDecimal.ZERO))) + && Objects.equals(((SqlLiteral) offset).bigDecimalValue(), BigDecimal.ZERO))) && fetch == null) { return; } @@ -845,7 +867,7 @@ protected void convertOrder( // Create a sorter using the previously constructed collations. bb.setRoot( - LogicalSort.create(bb.root, collation, + LogicalSort.create(bb.root(), collation, offset == null ? null : convertExpression(offset), fetch == null ? null : convertExpression(fetch)), false); @@ -857,14 +879,14 @@ protected void convertOrder( // If it is the top node, use the real collation, but don't trim fields. if (orderExprList.size() > 0 && !bb.top) { final List exprs = new ArrayList<>(); - final RelDataType rowType = bb.root.getRowType(); + final RelDataType rowType = bb.root().getRowType(); final int fieldCount = rowType.getFieldCount() - orderExprList.size(); for (int i = 0; i < fieldCount; i++) { - exprs.add(rexBuilder.makeInputRef(bb.root, i)); + exprs.add(rexBuilder.makeInputRef(bb.root(), i)); } bb.setRoot( - LogicalProject.create(bb.root, + LogicalProject.create(bb.root(), ImmutableList.of(), exprs, rowType.getFieldNames().subList(0, fieldCount)), @@ -945,7 +967,9 @@ private static SqlNode pushDownNotForIn(SqlValidatorScope scope, thenOperand); thenOperands.add(pushDownNotForIn(scope, reg(scope, not))); } - SqlNode elseOperand = caseNode.getElseOperand(); + SqlNode elseOperand = requireNonNull( + caseNode.getElseOperand(), + "getElseOperand for " + caseNode); if (!SqlUtil.isNull(elseOperand)) { // "not(unknown)" is "unknown", so no need to simplify final SqlCall not = @@ -1025,11 +1049,11 @@ private static SqlNode reg(SqlValidatorScope scope, SqlNode e) { */ private void convertWhere( final Blackboard bb, - final SqlNode where) { + final @Nullable SqlNode where) { if (where == null) { return; } - SqlNode newWhere = pushDownNotForIn(bb.scope, where); + SqlNode newWhere = pushDownNotForIn(bb.scope(), where); replaceSubQueries(bb, newWhere, RelOptUtil.Logic.UNKNOWN_AS_FALSE); final RexNode convertedWhere = bb.convertExpression(newWhere); final RexNode convertedWhere2 = @@ -1043,7 +1067,7 @@ private void convertWhere( final RelFactories.FilterFactory filterFactory = RelFactories.DEFAULT_FILTER_FACTORY; final RelNode filter = - filterFactory.createFilter(bb.root, convertedWhere2, ImmutableSet.of()); + filterFactory.createFilter(bb.root(), convertedWhere2, ImmutableSet.of()); final RelNode r; final CorrelationUse p = getCorrelationUse(bb, filter); if (p != null) { @@ -1168,7 +1192,7 @@ private void substituteSubQuery(Blackboard bb, SubQuery subQuery) { } final RelDataType targetRowType = SqlTypeUtil.promoteToRowType(typeFactory, - validator.getValidatedNodeType(leftKeyNode), null); + validator().getValidatedNodeType(leftKeyNode), null); final boolean notIn = call.getOperator().kind == SqlKind.NOT_IN; converted = convertExists(query, RelOptUtil.SubQueryType.IN, subQuery.logic, @@ -1194,7 +1218,7 @@ private void substituteSubQuery(Blackboard bb, SubQuery subQuery) { AggregateCall.create(SqlStdOperatorTable.COUNT, false, false, false, args, -1, RelCollations.EMPTY, longType, null))); LogicalJoin join = - LogicalJoin.create(bb.root, aggregate, ImmutableList.of(), + LogicalJoin.create(bb.root(), aggregate, ImmutableList.of(), rexBuilder.makeLiteral(true), ImmutableSet.of(), JoinRelType.INNER); bb.setRoot(join, false); } @@ -1238,10 +1262,11 @@ private void substituteSubQuery(Blackboard bb, SubQuery subQuery) { } final SqlValidatorScope seekScope = (query instanceof SqlSelect) - ? validator.getSelectScope((SqlSelect) query) + ? validator().getSelectScope((SqlSelect) query) : null; final Blackboard seekBb = createBlackboard(seekScope, null, false); final RelNode seekRel = convertQueryOrInList(seekBb, query, null); + requireNonNull(seekRel, () -> "seelkRel is null for query " + query); // An EXIST sub-query whose inner child has at least 1 tuple // (e.g. an Aggregate with no grouping columns or non-empty Values // node) should be simplified to a Boolean constant expression. @@ -1301,7 +1326,7 @@ private void substituteSubQuery(Blackboard bb, SubQuery subQuery) { } } - private RexNode translateIn(RelOptUtil.Logic logic, RelNode root, + private RexNode translateIn(RelOptUtil.Logic logic, @Nullable RelNode root, final RexNode rex) { switch (logic) { case TRUE: @@ -1348,7 +1373,7 @@ private RexNode translateIn(RelOptUtil.Logic logic, RelNode root, // cross join (select count(*) as c, count(deptno) as ck from v) as ct // left join (select distinct deptno, true as i from v) as dt // on e.deptno = dt.deptno - final Join join = (Join) root; + final Join join = (Join) requireNonNull(root, "root"); final Project left = (Project) join.getLeft(); final RelNode leftLeft = ((Join) left.getInput()).getLeft(); final int leftLeftCount = leftLeft.getRowType().getFieldCount(); @@ -1453,7 +1478,7 @@ public RelNode convertToSingleValueSubq( // Check whether query is guaranteed to produce a single value. if (query instanceof SqlSelect) { SqlSelect select = (SqlSelect) query; - SqlNodeList selectList = select.getSelectList(); + SqlNodeList selectList = requireNonNull(select.getSelectList(), "selectList"); SqlNodeList groupList = select.getGroup(); if ((selectList.size() == 1) @@ -1468,10 +1493,10 @@ public RelNode convertToSingleValueSubq( // If there is a limit with 0 or 1, // it is ensured to produce a single value - if (select.getFetch() != null - && select.getFetch() instanceof SqlNumericLiteral) { - SqlNumericLiteral limitNum = (SqlNumericLiteral) select.getFetch(); - if (((BigDecimal) limitNum.getValue()).intValue() < 2) { + SqlNode fetch = select.getFetch(); + if (fetch instanceof SqlNumericLiteral) { + long value = ((SqlNumericLiteral) fetch).getValueAs(Long.class); + if (value < 2) { return plan; } } @@ -1502,7 +1527,7 @@ public RelNode convertToSingleValueSubq( * @param op The operator (IN, NOT IN, > SOME, ...) * @return converted expression */ - private RexNode convertInToOr( + private @Nullable RexNode convertInToOr( final Blackboard bb, final List leftKeys, SqlNodeList valuesList, @@ -1544,7 +1569,7 @@ private RexNode convertInToOr( return RexUtil.composeConjunction(rexBuilder, comparisons, true); case NOT_IN: return rexBuilder.makeCall(SqlStdOperatorTable.NOT, - RexUtil.composeDisjunction(rexBuilder, comparisons, true)); + RexUtil.composeDisjunction(rexBuilder, comparisons)); case IN: case SOME: return RexUtil.composeDisjunction(rexBuilder, comparisons, true); @@ -1601,22 +1626,23 @@ private RelOptUtil.Exists convertExists( RelOptUtil.SubQueryType subQueryType, RelOptUtil.Logic logic, boolean notIn, - RelDataType targetDataType) { + @Nullable RelDataType targetDataType) { final SqlValidatorScope seekScope = (seek instanceof SqlSelect) - ? validator.getSelectScope((SqlSelect) seek) + ? validator().getSelectScope((SqlSelect) seek) : null; final Blackboard seekBb = createBlackboard(seekScope, null, false); RelNode seekRel = convertQueryOrInList(seekBb, seek, targetDataType); + requireNonNull(seekRel, () -> "seelkRel is null for query " + seek); return RelOptUtil.createExistsPlan(seekRel, subQueryType, logic, notIn, relBuilder); } - private RelNode convertQueryOrInList( + private @Nullable RelNode convertQueryOrInList( Blackboard bb, SqlNode seek, - RelDataType targetRowType) { + @Nullable RelDataType targetRowType) { // NOTE: Once we start accepting single-row queries as row constructors, // there will be an ambiguity here for a case like X IN ((SELECT Y FROM // Z)). The SQL standard resolves the ambiguity by saying that a lone @@ -1635,12 +1661,12 @@ private RelNode convertQueryOrInList( } } - private RelNode convertRowValues( + private @Nullable RelNode convertRowValues( Blackboard bb, SqlNode rowList, Collection rows, boolean allowLiteralsOnly, - RelDataType targetRowType) { + @Nullable RelDataType targetRowType) { // NOTE jvs 30-Apr-2006: We combine all rows consisting entirely of // literals into a single LogicalValues; this gives the optimizer a smaller // input tree. For everything else (computed expressions, row @@ -1656,7 +1682,7 @@ private RelNode convertRowValues( rowType = SqlTypeUtil.promoteToRowType( typeFactory, - validator.getValidatedNodeType(rowList), + validator().getValidatedNodeType(rowList), null); } @@ -1666,7 +1692,7 @@ private RelNode convertRowValues( if (isRowConstructor(node)) { call = (SqlBasicCall) node; ImmutableList.Builder tuple = ImmutableList.builder(); - for (Ord operand : Ord.zip(call.operands)) { + for (Ord<@Nullable SqlNode> operand : Ord.zip(call.operands)) { RexLiteral rexLiteral = convertLiteralInValuesList( operand.e, @@ -1726,8 +1752,8 @@ private RelNode convertRowValues( return resultRel; } - private RexLiteral convertLiteralInValuesList( - SqlNode sqlNode, + private @Nullable RexLiteral convertLiteralInValuesList( + @Nullable SqlNode sqlNode, Blackboard bb, RelDataType rowType, int iField) { @@ -1878,7 +1904,7 @@ private void findSubQueries( case ALL: switch (logic) { case TRUE_FALSE_UNKNOWN: - RelDataType type = validator.getValidatedNodeTypeIfKnown(node); + RelDataType type = validator().getValidatedNodeTypeIfKnown(node); if (type == null) { // The node might not be validated if we still don't know type of the node. // Therefore return directly. @@ -1909,7 +1935,7 @@ public RexNode convertExpression( SqlNode node) { Map nameToTypeMap = Collections.emptyMap(); final ParameterScope scope = - new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap); + new ParameterScope((SqlValidatorImpl) validator(), nameToTypeMap); final Blackboard bb = createBlackboard(scope, null, false); return bb.convertExpression(node); } @@ -1933,7 +1959,7 @@ public RexNode convertExpression( nameToTypeMap.put(entry.getKey(), entry.getValue().getType()); } final ParameterScope scope = - new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap); + new ParameterScope((SqlValidatorImpl) validator(), nameToTypeMap); final Blackboard bb = createBlackboard(scope, nameToNodeMap, false); return bb.convertExpression(node); } @@ -1949,7 +1975,7 @@ public RexNode convertExpression( * @param bb Blackboard * @return null to proceed with the usual expression translation process */ - protected RexNode convertExtendedExpression( + protected @Nullable RexNode convertExtendedExpression( SqlNode node, Blackboard bb) { return null; @@ -1972,7 +1998,7 @@ private RexNode convertOver(Blackboard bb, SqlNode node) { SqlNode windowOrRef = call.operand(1); final SqlWindow window = - validator.resolveWindow(windowOrRef, bb.scope); + validator().resolveWindow(windowOrRef, bb.scope()); SqlNode sqlLowerBound = window.getLowerBound(); SqlNode sqlUpperBound = window.getUpperBound(); @@ -2006,13 +2032,15 @@ private RexNode convertOver(Blackboard bb, SqlNode node) { for (SqlNode partition : partitionList) { partitionKeys.add(bb.convertExpression(partition)); } - final RexNode lowerBound = bb.convertExpression(sqlLowerBound); - final RexNode upperBound = bb.convertExpression(sqlUpperBound); + final RexNode lowerBound = bb.convertExpression( + requireNonNull(sqlLowerBound, "sqlLowerBound")); + final RexNode upperBound = bb.convertExpression( + requireNonNull(sqlUpperBound, "sqlUpperBound")); if (orderList.size() == 0 && !rows) { // A logical range requires an ORDER BY clause. Use the implicit // ordering of this relation. There must be one, otherwise it would // have failed validation. - orderList = bb.scope.getOrderList(); + orderList = bb.scope().getOrderList(); if (orderList == null) { throw new AssertionError( "Relation should have sort key for implicit ORDER BY"); @@ -2035,7 +2063,7 @@ private RexNode convertOver(Blackboard bb, SqlNode node) { RexNode rexAgg = exprConverter.convertCall(bb, aggCall); rexAgg = rexBuilder.ensureType( - validator.getValidatedNodeType(call), rexAgg, false); + validator().getValidatedNodeType(call), rexAgg, false); // Walk over the tree and apply 'over' to all agg functions. This is // necessary because the returned expression is not necessarily a call @@ -2062,7 +2090,7 @@ private RexNode convertOver(Blackboard bb, SqlNode node) { protected void convertFrom( Blackboard bb, - SqlNode from) { + @Nullable SqlNode from) { convertFrom(bb, from, Collections.emptyList()); } @@ -2086,7 +2114,7 @@ protected void convertFrom( */ protected void convertFrom( Blackboard bb, - SqlNode from, + @Nullable SqlNode from, List fieldNames) { if (from == null) { bb.setRoot(LogicalValues.createOneRow(cluster), false); @@ -2094,7 +2122,7 @@ protected void convertFrom( } final SqlCall call; - final SqlNode[] operands; + final @Nullable SqlNode[] operands; switch (from.getKind()) { case AS: call = (SqlCall) from; @@ -2127,7 +2155,8 @@ protected void convertFrom( case TABLESAMPLE: operands = ((SqlBasicCall) from).getOperands(); - SqlSampleSpec sampleSpec = SqlLiteral.sampleValue(operands[1]); + SqlSampleSpec sampleSpec = SqlLiteral.sampleValue( + requireNonNull(operands[1], () -> "operand[1] of " + from)); if (sampleSpec instanceof SqlSampleSpec.SqlSubstitutionSampleSpec) { String sampleName = ((SqlSampleSpec.SqlSubstitutionSampleSpec) sampleSpec) @@ -2145,7 +2174,7 @@ protected void convertFrom( tableSampleSpec.getSamplePercentage(), tableSampleSpec.isRepeatable(), tableSampleSpec.getRepeatableSeed()); - bb.setRoot(new Sample(cluster, bb.root, params), false); + bb.setRoot(new Sample(cluster, bb.root(), params), false); } else { throw new AssertionError("unknown TABLESAMPLE type: " + sampleSpec); } @@ -2189,7 +2218,7 @@ protected void convertFrom( case VALUES: convertValuesImpl(bb, (SqlCall) from, null); if (fieldNames.size() > 0) { - bb.setRoot(relBuilder.push(bb.root).rename(fieldNames).build(), true); + bb.setRoot(relBuilder.push(bb.root()).rename(fieldNames).build(), true); } return; @@ -2220,12 +2249,12 @@ private void convertUnnest(Blackboard bb, SqlCall call, List fieldNames) final List exprs = new ArrayList<>(); for (Ord node : Ord.zip(nodes)) { exprs.add( - relBuilder.alias(bb.convertExpression(node.e), validator.deriveAlias(node.e, node.i))); + relBuilder.alias(bb.convertExpression(node.e), validator().deriveAlias(node.e, node.i))); } RelNode child = (null != bb.root) ? bb.root : LogicalValues.createOneRow(cluster); RelNode uncollect; - if (validator.config().sqlConformance().allowAliasUnnestItems()) { + if (validator().config().sqlConformance().allowAliasUnnestItems()) { uncollect = relBuilder .push(child) .project(exprs) @@ -2245,15 +2274,15 @@ private void convertUnnest(Blackboard bb, SqlCall call, List fieldNames) protected void convertMatchRecognize(Blackboard bb, SqlMatchRecognize matchRecognize) { - final SqlValidatorNamespace ns = validator.getNamespace(matchRecognize); - final SqlValidatorScope scope = validator.getMatchRecognizeScope(matchRecognize); + final SqlValidatorNamespace ns = getNamespace(matchRecognize); + final SqlValidatorScope scope = validator().getMatchRecognizeScope(matchRecognize); final Blackboard matchBb = createBlackboard(scope, null, false); final RelDataType rowType = ns.getRowType(); // convert inner query, could be a table name or a derived table SqlNode expr = matchRecognize.getTableRef(); convertFrom(matchBb, expr); - final RelNode input = matchBb.root; + final RelNode input = matchBb.root(); // PARTITION BY final SqlNodeList partitionList = matchRecognize.getPartitionList(); @@ -2281,7 +2310,7 @@ protected void convertMatchRecognize(Blackboard bb, break; } final RelFieldCollation.NullDirection nullDirection = - validator.config().defaultNullCollation().last(desc(direction)) + validator().config().defaultNullCollation().last(desc(direction)) ? RelFieldCollation.NullDirection.LAST : RelFieldCollation.NullDirection.FIRST; RexNode e = matchBb.convertExpression(order); @@ -2294,16 +2323,17 @@ protected void convertMatchRecognize(Blackboard bb, // convert pattern final Set patternVarsSet = new HashSet<>(); SqlNode pattern = matchRecognize.getPattern(); - final SqlBasicVisitor patternVarVisitor = - new SqlBasicVisitor() { + final SqlBasicVisitor<@Nullable RexNode> patternVarVisitor = + new SqlBasicVisitor<@Nullable RexNode>() { @Override public RexNode visit(SqlCall call) { List operands = call.getOperandList(); List newOperands = new ArrayList<>(); for (SqlNode node : operands) { - newOperands.add(node.accept(this)); + RexNode arg = requireNonNull(node.accept(this), node::toString); + newOperands.add(arg); } return rexBuilder.makeCall( - validator.getUnknownType(), call.getOperator(), newOperands); + validator().getUnknownType(), call.getOperator(), newOperands); } @Override public RexNode visit(SqlIdentifier id) { @@ -2321,6 +2351,7 @@ protected void convertMatchRecognize(Blackboard bb, } }; final RexNode patternNode = pattern.accept(patternVarVisitor); + assert patternNode != null : "pattern is not found in " + pattern; SqlLiteral interval = matchRecognize.getInterval(); RexNode intervalNode = null; @@ -2360,7 +2391,7 @@ protected void convertMatchRecognize(Blackboard bb, : id.getSimple() + " not defined in pattern"; RexNode rex = rexBuilder.makeLiteral(id.getSimple()); after = - rexBuilder.makeCall(validator.getUnknownType(), operator, + rexBuilder.makeCall(validator().getUnknownType(), operator, ImmutableList.of(rex)); } else { after = matchBb.convertExpression(afterMatch); @@ -2407,13 +2438,13 @@ protected void convertMatchRecognize(Blackboard bb, } protected void convertPivot(Blackboard bb, SqlPivot pivot) { - final SqlValidatorScope scope = validator.getJoinScope(pivot); + final SqlValidatorScope scope = validator().getJoinScope(pivot); final Blackboard pivotBb = createBlackboard(scope, null, false); // Convert input convertFrom(pivotBb, pivot.query); - final RelNode input = pivotBb.root; + final RelNode input = pivotBb.root(); final RelDataType inputRowType = input.getRowType(); relBuilder.push(input); @@ -2435,7 +2466,7 @@ protected void convertPivot(Blackboard bb, SqlPivot pivot) { // 3. Gather columns used as arguments to aggregate functions. pivotBb.agg = aggConverter; - final List aggAliasList = new ArrayList<>(); + final List<@Nullable String> aggAliasList = new ArrayList<>(); assert aggConverter.aggCalls.size() == 0; pivot.forEachAgg((alias, call) -> { call.accept(aggConverter); @@ -2491,9 +2522,8 @@ protected void convertPivot(Blackboard bb, SqlPivot pivot) { } private void convertIdentifier(Blackboard bb, SqlIdentifier id, - SqlNodeList extendedColumns, SqlNodeList tableHints) { - final SqlValidatorNamespace fromNamespace = - validator.getNamespace(id).resolve(); + @Nullable SqlNodeList extendedColumns, @Nullable SqlNodeList tableHints) { + final SqlValidatorNamespace fromNamespace = getNamespace(id).resolve(); if (fromNamespace.getNode() != null) { convertFrom(bb, fromNamespace.getNode()); return; @@ -2504,10 +2534,11 @@ private void convertIdentifier(Blackboard bb, SqlIdentifier id, RelOptTable table = SqlValidatorUtil.getRelOptTable(fromNamespace, catalogReader, datasetName, usedDataset); + assert table != null : "getRelOptTable returned null for " + fromNamespace; if (extendedColumns != null && extendedColumns.size() > 0) { - assert table != null; final SqlValidatorTable validatorTable = table.unwrap(SqlValidatorTable.class); + assert validatorTable != null : "unwra(SqlValidatorTable) returned null for " + table; final List extendedFields = SqlValidatorUtil.getExtendedColumns(validator, validatorTable, extendedColumns); @@ -2546,7 +2577,7 @@ protected void convertCollectionTable( // Expand table macro if possible. It's more efficient than // LogicalTableFunctionScan. final SqlCallBinding callBinding = - new SqlCallBinding(bb.scope.getValidator(), bb.scope, call); + new SqlCallBinding(bb.scope().getValidator(), bb.scope, call); if (operator instanceof SqlUserDefinedTableMacro) { final SqlUserDefinedTableMacro udf = (SqlUserDefinedTableMacro) operator; @@ -2577,7 +2608,7 @@ protected void convertCollectionTable( inputs, rexCall, elementType, - validator.getValidatedNodeType(call), + validator().getValidatedNodeType(call), columnMappings); bb.setRoot(callRel, true); afterTableFunction(bb, call, callRel); @@ -2597,12 +2628,12 @@ private void convertTemporalTable(Blackboard bb, SqlCall call) { SqlNode expr = snapshot.getTableRef(); convertFrom(bb, expr); - final RelNode snapshotRel = relBuilder.push(bb.root).snapshot(period).build(); + final RelNode snapshotRel = relBuilder.push(bb.root()).snapshot(period).build(); bb.setRoot(snapshotRel, false); } - private Set getColumnMappings(SqlOperator op) { + private @Nullable Set getColumnMappings(SqlOperator op) { SqlReturnTypeInference rti = op.getReturnTypeInference(); if (rti == null) { return null; @@ -2694,7 +2725,7 @@ protected RelNode createJoin( return node; } - private CorrelationUse getCorrelationUse(Blackboard bb, final RelNode r0) { + private @Nullable CorrelationUse getCorrelationUse(Blackboard bb, final RelNode r0) { final Set correlatedVariables = RelOptUtil.getVariablesUsed(r0); if (correlatedVariables.isEmpty()) { @@ -2710,8 +2741,9 @@ private CorrelationUse getCorrelationUse(Blackboard bb, final RelNode r0) { SqlValidatorNamespace prevNs = null; for (CorrelationId correlName : correlatedVariables) { - DeferredLookup lookup = - mapCorrelToDeferred.get(correlName); + DeferredLookup lookup = requireNonNull( + mapCorrelToDeferred.get(correlName), + () -> "correlation variable is not found: " + correlName); RexFieldAccess fieldAccess = lookup.getFieldAccess(correlName); String originalRelName = lookup.getOriginalRelName(); String originalFieldName = fieldAccess.getField().getName(); @@ -2720,7 +2752,7 @@ private CorrelationUse getCorrelationUse(Blackboard bb, final RelNode r0) { bb.getValidator().getCatalogReader().nameMatcher(); final SqlValidatorScope.ResolvedImpl resolved = new SqlValidatorScope.ResolvedImpl(); - lookup.bb.scope.resolve(ImmutableList.of(originalRelName), + lookup.bb.scope().resolve(ImmutableList.of(originalRelName), nameMatcher, false, resolved); assert resolved.count() == 1; final SqlValidatorScope.Resolve resolve = resolved.only(); @@ -2728,7 +2760,7 @@ private CorrelationUse getCorrelationUse(Blackboard bb, final RelNode r0) { final RelDataType rowType = resolve.rowType(); final int childNamespaceIndex = resolve.path.steps().get(0).i; final SqlValidatorScope ancestorScope = resolve.scope; - boolean correlInCurrentScope = bb.scope.isWithin(ancestorScope); + boolean correlInCurrentScope = bb.scope().isWithin(ancestorScope); if (!correlInCurrentScope) { continue; @@ -2771,16 +2803,16 @@ private CorrelationUse getCorrelationUse(Blackboard bb, final RelNode r0) { assert pos != -1; - if (bb.mapRootRelToFieldProjection.containsKey(bb.root)) { - // bb.root is an aggregate and only projects group by - // keys. - Map exprProjection = - bb.mapRootRelToFieldProjection.get(bb.root); - + // bb.root is an aggregate and only projects group by + // keys. + Map exprProjection = + bb.mapRootRelToFieldProjection.get(bb.root); + if (exprProjection != null) { // sub-query can reference group by keys projected from // the root of the outer relation. - if (exprProjection.containsKey(pos)) { - pos = exprProjection.get(pos); + Integer projection = exprProjection.get(pos); + if (projection != null) { + pos = projection; } else { // correl not grouped throw new AssertionError("Identifier '" + originalRelName + "." @@ -2823,14 +2855,16 @@ private CorrelationUse getCorrelationUse(Blackboard bb, final RelNode r0) { private boolean isSubQueryNonCorrelated(RelNode subq, Blackboard bb) { Set correlatedVariables = RelOptUtil.getVariablesUsed(subq); for (CorrelationId correlName : correlatedVariables) { - DeferredLookup lookup = mapCorrelToDeferred.get(correlName); + DeferredLookup lookup = requireNonNull( + mapCorrelToDeferred.get(correlName), + () -> "correlation variable is not found: " + correlName); String originalRelName = lookup.getOriginalRelName(); final SqlNameMatcher nameMatcher = - lookup.bb.scope.getValidator().getCatalogReader().nameMatcher(); + lookup.bb.scope().getValidator().getCatalogReader().nameMatcher(); final SqlValidatorScope.ResolvedImpl resolved = new SqlValidatorScope.ResolvedImpl(); - lookup.bb.scope.resolve(ImmutableList.of(originalRelName), nameMatcher, + lookup.bb.scope().resolve(ImmutableList.of(originalRelName), nameMatcher, false, resolved); SqlValidatorScope ancestorScope = resolved.only().scope; @@ -2862,31 +2896,32 @@ protected List getSystemFields() { } private void convertJoin(Blackboard bb, SqlJoin join) { + SqlValidator validator = validator(); final SqlValidatorScope scope = validator.getJoinScope(join); final Blackboard fromBlackboard = createBlackboard(scope, null, false); SqlNode left = join.getLeft(); SqlNode right = join.getRight(); final SqlValidatorScope leftScope = Util.first(validator.getJoinScope(left), - ((DelegatingScope) bb.scope).getParent()); + ((DelegatingScope) bb.scope()).getParent()); final Blackboard leftBlackboard = createBlackboard(leftScope, null, false); final SqlValidatorScope rightScope = Util.first(validator.getJoinScope(right), - ((DelegatingScope) bb.scope).getParent()); + ((DelegatingScope) bb.scope()).getParent()); final Blackboard rightBlackboard = createBlackboard(rightScope, null, false); convertFrom(leftBlackboard, left); - final RelNode leftRel = leftBlackboard.root; + final RelNode leftRel = requireNonNull(leftBlackboard.root, "leftBlackboard.root"); convertFrom(rightBlackboard, right); - final RelNode tempRightRel = rightBlackboard.root; + final RelNode tempRightRel = requireNonNull(rightBlackboard.root, "rightBlackboard.root"); final JoinConditionType conditionType = join.getConditionType(); final RexNode condition; final RelNode rightRel; if (join.isNatural()) { - condition = convertNaturalCondition(validator.getNamespace(left), - validator.getNamespace(right)); + condition = convertNaturalCondition(getNamespace(left), + getNamespace(right)); rightRel = tempRightRel; } else { switch (conditionType) { @@ -2896,8 +2931,8 @@ private void convertJoin(Blackboard bb, SqlJoin join) { break; case USING: condition = convertUsingCondition(join, - validator.getNamespace(left), - validator.getNamespace(right)); + getNamespace(left), + getNamespace(right)); rightRel = tempRightRel; break; case ON: @@ -2936,7 +2971,8 @@ private RexNode convertUsingCondition( SqlJoin join, SqlValidatorNamespace leftNamespace, SqlValidatorNamespace rightNamespace) { - SqlNode condition = join.getCondition(); + SqlNode condition = requireNonNull(join.getCondition(), + () -> "getCondition for join " + join); final SqlNodeList list = (SqlNodeList) condition; final List nameList = new ArrayList<>(); @@ -2978,7 +3014,8 @@ private Pair convertOnCondition( SqlJoin join, RelNode leftRel, RelNode rightRel) { - SqlNode condition = join.getCondition(); + SqlNode condition = requireNonNull(join.getCondition(), + () -> "getCondition for join " + join); bb.setRoot(ImmutableList.of(leftRel, rightRel)); replaceSubQueries(bb, condition, RelOptUtil.Logic.UNKNOWN_AS_FALSE); @@ -3001,7 +3038,7 @@ private Pair convertOnCondition( * @return Expression to match columns from name list, or true if name list * is empty */ - private @Nonnull RexNode convertUsing(SqlValidatorNamespace leftNamespace, + private RexNode convertUsing(SqlValidatorNamespace leftNamespace, SqlValidatorNamespace rightNamespace, List nameList) { final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); @@ -3013,6 +3050,8 @@ private Pair convertOnCondition( rightNamespace)) { final RelDataType rowType = n.getRowType(); final RelDataTypeField field = nameMatcher.field(rowType, name); + assert field != null : "field " + name + " is not found in " + rowType + + " with " + nameMatcher; operands.add( rexBuilder.makeInputRef(field.getType(), offset + field.getIndex())); @@ -3059,6 +3098,7 @@ protected void convertAgg( assert bb.root != null : "precondition: child != null"; SqlNodeList groupList = select.getGroup(); SqlNodeList selectList = select.getSelectList(); + assert selectList != null : "selectList must not be null for " + select; SqlNode having = select.getHaving(); final AggConverter aggConverter = new AggConverter(bb, select); @@ -3075,8 +3115,8 @@ protected final void createAggImpl( Blackboard bb, final AggConverter aggConverter, SqlNodeList selectList, - SqlNodeList groupList, - SqlNode having, + @Nullable SqlNodeList groupList, + @Nullable SqlNode having, List orderExprList) { // Find aggregate functions in SELECT and HAVING clause final AggregateFinder aggregateFinder = new AggregateFinder(); @@ -3113,7 +3153,9 @@ protected final void createAggImpl( // Calcite allows expressions, not just column references in // group by list. This is not SQL 2003 compliant, but hey. - final AggregatingSelectScope scope = aggConverter.aggregatingSelectScope; + final AggregatingSelectScope scope = requireNonNull( + aggConverter.aggregatingSelectScope, + "aggregatingSelectScope"); final AggregatingSelectScope.Resolved r = scope.resolved.get(); for (SqlNode groupExpr : r.groupExprList) { aggConverter.addGroupExpr(groupExpr); @@ -3142,7 +3184,7 @@ protected final void createAggImpl( } // compute inputs to the aggregator - List> preExprs = aggConverter.getPreExprs(); + List> preExprs = aggConverter.getPreExprs(); if (preExprs.size() == 0) { // Special case for COUNT(*), where we can end up with no inputs @@ -3152,7 +3194,7 @@ protected final void createAggImpl( preExprs = ImmutableList.of(Pair.of(zero, null)); } - final RelNode inputRel = bb.root; + final RelNode inputRel = bb.root(); // Project the expressions required by agg and having. bb.setRoot( @@ -3160,7 +3202,7 @@ protected final void createAggImpl( .projectNamed(Pair.left(preExprs), Pair.right(preExprs), false) .build(), false); - bb.mapRootRelToFieldProjection.put(bb.root, r.groupExprProjection); + bb.mapRootRelToFieldProjection.put(bb.root(), r.groupExprProjection); // REVIEW jvs 31-Oct-2007: doesn't the declaration of // monotonicity here assume sort-based aggregation at @@ -3170,19 +3212,19 @@ protected final void createAggImpl( bb.columnMonotonicities.clear(); for (SqlNode groupItem : groupList) { bb.columnMonotonicities.add( - bb.scope.getMonotonicity(groupItem)); + bb.scope().getMonotonicity(groupItem)); } // Add the aggregator bb.setRoot( createAggregate(bb, r.groupSet, r.groupSets.asList(), aggConverter.getAggCalls()), false); - bb.mapRootRelToFieldProjection.put(bb.root, r.groupExprProjection); + bb.mapRootRelToFieldProjection.put(bb.root(), r.groupExprProjection); // Replace sub-queries in having here and modify having to use // the replaced expressions if (having != null) { - SqlNode newHaving = pushDownNotForIn(bb.scope, having); + SqlNode newHaving = pushDownNotForIn(bb.scope(), having); replaceSubQueries(bb, newHaving, RelOptUtil.Logic.UNKNOWN_AS_FALSE); havingExpr = bb.convertExpression(newHaving); } else { @@ -3209,8 +3251,8 @@ protected final void createAggImpl( final SelectScope selectScope = SqlValidatorUtil.getEnclosingSelectScope(bb.scope); assert selectScope != null; - final SqlValidatorNamespace selectNamespace = - validator.getNamespace(selectScope.getNode()); + final SqlValidatorNamespace selectNamespace = getNamespaceOrNull(selectScope.getNode()); + assert selectNamespace != null : "selectNamespace must not be null for " + selectScope; final List names = selectNamespace.getRowType().getFieldNames(); int sysFieldCount = selectList.size() - names.size(); @@ -3218,21 +3260,21 @@ protected final void createAggImpl( projects.add( Pair.of(bb.convertExpression(expr), k < sysFieldCount - ? validator.deriveAlias(expr, k++) + ? castNonNull(validator().deriveAlias(expr, k++)) : names.get(k++ - sysFieldCount))); } for (SqlNode expr : orderExprList) { projects.add( Pair.of(bb.convertExpression(expr), - validator.deriveAlias(expr, k++))); + castNonNull(validator().deriveAlias(expr, k++)))); } } finally { bb.agg = null; } // implement HAVING (we have already checked that it is non-trivial) - relBuilder.push(bb.root); + relBuilder.push(bb.root()); if (havingExpr != null) { relBuilder.filter(havingExpr); } @@ -3246,7 +3288,7 @@ protected final void createAggImpl( bb.columnMonotonicities.clear(); for (SqlNode selectItem : selectList) { bb.columnMonotonicities.add( - bb.scope.getMonotonicity(selectItem)); + bb.scope().getMonotonicity(selectItem)); } } @@ -3270,7 +3312,7 @@ protected final void createAggImpl( */ protected RelNode createAggregate(Blackboard bb, ImmutableBitSet groupSet, ImmutableList groupSets, List aggCalls) { - relBuilder.push(bb.root); + relBuilder.push(bb.root()); final RelBuilder.GroupKey groupKey = relBuilder.groupKey(groupSet, (Iterable) groupSets); return relBuilder.aggregate(groupKey, aggCalls) @@ -3312,7 +3354,7 @@ public RexDynamicParam convertDynamicParam( protected void gatherOrderExprs( Blackboard bb, SqlSelect select, - SqlNodeList orderList, + @Nullable SqlNodeList orderList, List extraOrderExprs, List collationList) { // TODO: add validation rules to SqlValidator also @@ -3326,8 +3368,7 @@ protected void gatherOrderExprs( SqlNode offset = select.getOffset(); if ((offset == null || (offset instanceof SqlLiteral - && ((SqlLiteral) offset).bigDecimalValue() - .equals(BigDecimal.ZERO))) + && Objects.equals(((SqlLiteral) offset).bigDecimalValue(), BigDecimal.ZERO))) && select.getFetch() == null) { return; } @@ -3374,11 +3415,11 @@ protected RelFieldCollation convertOrderItem( break; } - SqlNode converted = validator.expandOrderExpr(select, orderItem); + SqlNode converted = validator().expandOrderExpr(select, orderItem); switch (nullDirection) { case UNSPECIFIED: - nullDirection = validator.config().defaultNullCollation().last(desc(direction)) + nullDirection = validator().config().defaultNullCollation().last(desc(direction)) ? RelFieldCollation.NullDirection.LAST : RelFieldCollation.NullDirection.FIRST; break; @@ -3387,9 +3428,12 @@ protected RelFieldCollation convertOrderItem( } // Scan the select list and order exprs for an identical expression. - final SelectScope selectScope = validator.getRawSelectScope(select); + final SelectScope selectScope = requireNonNull( + validator().getRawSelectScope(select), + () -> "getRawSelectScope is not found for " + select); int ordinal = -1; - for (SqlNode selectItem : selectScope.getExpandedSelectList()) { + List expandedSelectList = selectScope.getExpandedSelectList(); + for (SqlNode selectItem : requireNonNull(expandedSelectList, "expandedSelectList")) { ++ordinal; if (converted.equalsDeep(stripAs(selectItem), Litmus.IGNORE)) { return new RelFieldCollation(ordinal, direction, nullDirection); @@ -3451,7 +3495,7 @@ public boolean isTrimUnusedFields() { * @return Relational expression */ protected RelRoot convertQueryRecursive(SqlNode query, boolean top, - RelDataType targetRowType) { + @Nullable RelDataType targetRowType) { final SqlKind kind = query.getKind(); switch (kind) { case SELECT: @@ -3512,7 +3556,7 @@ protected RelNode convertInsert(SqlInsert call) { RelOptTable targetTable = getTargetTable(call); final RelDataType targetRowType = - validator.getValidatedNodeType(call); + validator().getValidatedNodeType(call); assert targetRowType != null; RelNode sourceRel = convertQueryRecursive(call.getSource(), true, targetRowType).project(); @@ -3601,7 +3645,7 @@ private RelOptTable.ToRelContext createToRelContext(List hints) { return ViewExpanders.toRelContext(viewExpander, cluster, hints); } - public RelNode toRel(final RelOptTable table, @Nonnull final List hints) { + public RelNode toRel(final RelOptTable table, final List hints) { final RelNode scan = table.toRel(createToRelContext(hints)); final InitializerExpressionFactory ief = @@ -3633,8 +3677,10 @@ public RelNode toRel(final RelOptTable table, @Nonnull final List hints relBuilder.push(scan); relBuilder.project(list); final RelNode project = relBuilder.build(); - if (ief.postExpressionConversionHook() != null) { - return ief.postExpressionConversionHook().apply(bb, project); + BiFunction postConversionHook = + ief.postExpressionConversionHook(); + if (postConversionHook != null) { + return postConversionHook.apply(bb, project); } else { return project; } @@ -3644,14 +3690,15 @@ public RelNode toRel(final RelOptTable table, @Nonnull final List hints } protected RelOptTable getTargetTable(SqlNode call) { - final SqlValidatorNamespace targetNs = validator.getNamespace(call); + final SqlValidatorNamespace targetNs = getNamespace(call); + SqlValidatorNamespace namespace; if (targetNs.isWrapperFor(SqlValidatorImpl.DmlNamespace.class)) { - final SqlValidatorImpl.DmlNamespace dmlNamespace = - targetNs.unwrap(SqlValidatorImpl.DmlNamespace.class); - return SqlValidatorUtil.getRelOptTable(dmlNamespace, catalogReader, null, null); + namespace = targetNs.unwrap(SqlValidatorImpl.DmlNamespace.class); + } else { + namespace = targetNs.resolve(); } - final SqlValidatorNamespace resolvedNamespace = targetNs.resolve(); - return SqlValidatorUtil.getRelOptTable(resolvedNamespace, catalogReader, null, null); + RelOptTable table = SqlValidatorUtil.getRelOptTable(namespace, catalogReader, null, null); + return requireNonNull(table, "no table found for " + call); } /** @@ -3680,15 +3727,15 @@ protected RelNode convertColumnList(final SqlInsert call, RelNode source) { final RelOptTable targetTable = getTargetTable(call); final RelDataType targetRowType = RelOptTableImpl.realRowType(targetTable); final List targetFields = targetRowType.getFieldList(); - final List sourceExps = + final List<@Nullable RexNode> sourceExps = new ArrayList<>( Collections.nCopies(targetFields.size(), null)); - final List fieldNames = + final List<@Nullable String> fieldNames = new ArrayList<>( Collections.nCopies(targetFields.size(), null)); final InitializerExpressionFactory initializerFactory = - getInitializerFactory(validator.getNamespace(call).getTable()); + getInitializerFactory(getNamespace(call).getTable()); // Walk the name list and place the associated value in the // expression list according to the ordinal value returned from @@ -3711,19 +3758,25 @@ protected RelNode convertColumnList(final SqlInsert call, RelNode source) { final RelDataTypeField field = targetFields.get(i); final String fieldName = field.getName(); fieldNames.set(i, fieldName); - if (sourceExps.get(i) == null - || sourceExps.get(i).getKind() == SqlKind.DEFAULT) { - sourceExps.set(i, - initializerFactory.newColumnDefaultValue(targetTable, i, bb.get())); - + RexNode sourceExpression = sourceExps.get(i); + if (sourceExpression == null + || sourceExpression.getKind() == SqlKind.DEFAULT) { + sourceExpression = + initializerFactory.newColumnDefaultValue(targetTable, i, bb.get()); // bare nulls are dangerous in the wrong hands - sourceExps.set(i, - castNullLiteralIfNeeded(sourceExps.get(i), field.getType())); + sourceExpression = + castNullLiteralIfNeeded(sourceExpression, field.getType()); + + sourceExps.set(i, sourceExpression); } } + // sourceExps should not contain nulls (see the loop above) + @SuppressWarnings("assignment.type.incompatible") + List nonNullExprs = sourceExps; + return relBuilder.push(source) - .projectNamed(sourceExps, fieldNames, false) + .projectNamed(nonNullExprs, fieldNames, false) .build(); } @@ -3752,7 +3805,7 @@ private Blackboard createInsertBlackboard(RelOptTable targetTable, } private InitializerExpressionFactory getInitializerFactory( - SqlValidatorTable validatorTable) { + @Nullable SqlValidatorTable validatorTable) { // We might unwrap a null instead of a InitializerExpressionFactory. final Table table = unwrap(validatorTable, Table.class); if (table != null) { @@ -3765,7 +3818,7 @@ private InitializerExpressionFactory getInitializerFactory( return NullInitializerExpressionFactory.INSTANCE; } - private static T unwrap(Object o, Class clazz) { + private static @Nullable T unwrap(@Nullable Object o, Class clazz) { if (o instanceof Wrapper) { return ((Wrapper) o).unwrap(clazz); } @@ -3798,7 +3851,7 @@ protected void collectInsertTargets( final RelDataType tableRowType = targetTable.getRowType(); SqlNodeList targetColumnList = call.getTargetColumnList(); if (targetColumnList == null) { - if (validator.config().sqlConformance().isInsertSubsetColumnsAllowed()) { + if (validator().config().sqlConformance().isInsertSubsetColumnsAllowed()) { final RelDataType targetRowType = typeFactory.createStructType( tableRowType.getFieldList() @@ -3837,9 +3890,11 @@ protected void collectInsertTargets( expr = null; break; default: - expr = bb.nameToNodeMap.get(columnName); + expr = requireNonNull(bb.nameToNodeMap, "nameToNodeMap") + .get(columnName); } - columnExprs.add(expr); + // expr is nullable, however, all the nulls will be removed in the loop below + columnExprs.add(castNonNull(expr)); } // Remove virtual columns from the list. @@ -3854,13 +3909,16 @@ protected void collectInsertTargets( private RelNode convertDelete(SqlDelete call) { RelOptTable targetTable = getTargetTable(call); - RelNode sourceRel = convertSelect(call.getSourceSelect(), false); + RelNode sourceRel = convertSelect( + requireNonNull(call.getSourceSelect(), () -> "sourceSelect for " + call), + false); return LogicalTableModify.create(targetTable, catalogReader, sourceRel, LogicalTableModify.Operation.DELETE, null, null, false); } private RelNode convertUpdate(SqlUpdate call) { - final SqlValidatorScope scope = validator.getWhereScope(call.getSourceSelect()); + final SqlValidatorScope scope = validator().getWhereScope( + requireNonNull(call.getSourceSelect(), () -> "sourceSelect for " + call)); Blackboard bb = createBlackboard(scope, null, false); replaceSubQueries(bb, call, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN); @@ -3879,7 +3937,8 @@ private RelNode convertUpdate(SqlUpdate call) { targetColumnNameList.add(field.getName()); } - RelNode sourceRel = convertSelect(call.getSourceSelect(), false); + RelNode sourceRel = convertSelect( + requireNonNull(call.getSourceSelect(), () -> "sourceSelect for " + call), false); bb.setRoot(sourceRel, false); ImmutableList.Builder rexNodeSourceExpressionListBuilder = ImmutableList.builder(); @@ -3920,7 +3979,8 @@ private RelNode convertMerge(SqlMerge call) { // first, convert the merge's source select to construct the columns // from the target table and the set expressions in the update call - RelNode mergeSourceRel = convertSelect(call.getSourceSelect(), false); + RelNode mergeSourceRel = convertSelect( + requireNonNull(call.getSourceSelect(), () -> "sourceSelect for " + call), false); // then, convert the insert statement so we can get the insert // values expressions @@ -3952,6 +4012,7 @@ private RelNode convertMerge(SqlMerge call) { int nSourceFields = join.getLeft().getRowType().getFieldCount(); final List projects = new ArrayList<>(); for (int level1Idx = 0; level1Idx < nLevel1Exprs; level1Idx++) { + requireNonNull(level1InsertExprs, "level1InsertExprs"); if ((level2InsertExprs != null) && (level1InsertExprs.get(level1Idx) instanceof RexInputRef)) { int level2Idx = @@ -3999,11 +4060,15 @@ private RexNode convertIdentifier( } else { qualified = SqlQualified.create(null, 1, null, identifier); } - final Pair> e0 = bb.lookupExp(qualified); + final Pair> e0 = requireNonNull( + bb.lookupExp(qualified), + () -> "no expression found for " + qualified); RexNode e = e0.left; for (String name : qualified.suffix()) { if (e == e0.left && e0.right != null) { - int i = e0.right.get(name); + Integer i = requireNonNull( + e0.right.get(name), + () -> "e0.right.get(name) produced null for " + name); e = rexBuilder.makeFieldAccess(e, i); } else { final boolean caseSensitive = true; // name already fully-qualified @@ -4106,22 +4171,22 @@ private RelNode convertMultisets(final List operands, case ARRAY_VALUE_CONSTRUCTOR: final SqlNodeList list = new SqlNodeList(call.getOperandList(), call.getParserPosition()); - CollectNamespace nss = - (CollectNamespace) validator.getNamespace(call); + CollectNamespace nss = getNamespaceOrNull(call); Blackboard usedBb; if (null != nss) { usedBb = createBlackboard(nss.getScope(), null, false); } else { usedBb = - createBlackboard(new ListScope(bb.scope) { + createBlackboard(new ListScope(bb.scope()) { @Override public SqlNode getNode() { return call; } }, null, false); } - RelDataType multisetType = validator.getValidatedNodeType(call); - validator.setValidatedNodeType(list, - multisetType.getComponentType()); + RelDataType multisetType = validator().getValidatedNodeType(call); + validator().setValidatedNodeType(list, + requireNonNull(multisetType.getComponentType(), + () -> "componentType for multisetType " + multisetType)); input = convertQueryOrInList(usedBb, list, null); break; case MULTISET_QUERY_CONSTRUCTOR: @@ -4142,8 +4207,8 @@ private RelNode convertMultisets(final List operands, new Collect( cluster, cluster.traitSetOf(Convention.NONE), - input, - validator.deriveAlias(call, i)); + requireNonNull(input, "input"), + castNonNull(validator().deriveAlias(call, i))); joinList.add(collect); } @@ -4199,8 +4264,10 @@ private void convertSelectList( Blackboard bb, SqlSelect select, List orderList) { - SqlNodeList selectList = select.getSelectList(); - selectList = validator.expandStar(selectList, select, false); + SqlNodeList selectList = requireNonNull( + select.getSelectList(), + () -> "null selectList for " + select); + selectList = validator().expandStar(selectList, select, false); replaceSubQueries(bb, selectList, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN); @@ -4230,7 +4297,7 @@ private void convertSelectList( // Project extra fields for sorting. for (SqlNode expr : orderList) { ++i; - SqlNode expr2 = validator.expandOrderExpr(select, expr); + SqlNode expr2 = validator().expandOrderExpr(select, expr); exprs.add(bb.convertExpression(expr2)); fieldNames.add(deriveAlias(expr, aliases, i)); } @@ -4238,7 +4305,7 @@ private void convertSelectList( fieldNames = SqlValidatorUtil.uniquify(fieldNames, catalogReader.nameMatcher().isCaseSensitive()); - relBuilder.push(bb.root) + relBuilder.push(bb.root()) .projectNamed(exprs, fieldNames, true); bb.setRoot(relBuilder.build(), false); @@ -4276,7 +4343,7 @@ private String deriveAlias( final SqlNode node, Collection aliases, final int ordinal) { - String alias = validator.deriveAlias(node, ordinal); + String alias = validator().deriveAlias(node, ordinal); if ((alias == null) || aliases.contains(alias)) { String aliasBase = (alias == null) ? "EXPR$" : alias; for (int j = 0;; j++) { @@ -4302,12 +4369,12 @@ public RelRoot convertWith(SqlWith with, boolean top) { */ public RelNode convertValues( SqlCall values, - RelDataType targetRowType) { - final SqlValidatorScope scope = validator.getOverScope(values); + @Nullable RelDataType targetRowType) { + final SqlValidatorScope scope = validator().getOverScope(values); assert scope != null; final Blackboard bb = createBlackboard(scope, null, false); convertValuesImpl(bb, values, targetRowType); - return bb.root; + return bb.root(); } /** @@ -4321,7 +4388,7 @@ public RelNode convertValues( private void convertValuesImpl( Blackboard bb, SqlCall values, - RelDataType targetRowType) { + @Nullable RelDataType targetRowType) { // Attempt direct conversion to LogicalValues; if that fails, deal with // fancy stuff like sub-queries below. RelNode valuesRel = @@ -4347,7 +4414,7 @@ private void convertValuesImpl( exps.add( Pair.of( tmpBb.convertExpression(operand.e), - validator.deriveAlias(operand.e, operand.i))); + castNonNull(validator().deriveAlias(operand.e, operand.i)))); } RelNode in = (null == tmpBb.root) @@ -4379,9 +4446,9 @@ private void convertValuesImpl( private static class RegisterArgs { final RelNode rel; final JoinRelType joinType; - final List leftKeys; + final @Nullable List leftKeys; - RegisterArgs(RelNode rel, JoinRelType joinType, List leftKeys) { + RegisterArgs(RelNode rel, JoinRelType joinType, @Nullable List leftKeys) { this.rel = rel; this.joinType = joinType; this.leftKeys = leftKeys; @@ -4397,10 +4464,10 @@ protected class Blackboard implements SqlRexContext, SqlVisitor, * Collection of {@link RelNode} objects which correspond to a SELECT * statement. */ - public final SqlValidatorScope scope; - private final Map nameToNodeMap; - public RelNode root; - private List inputs; + public final @Nullable SqlValidatorScope scope; + private final @Nullable Map nameToNodeMap; + public @Nullable RelNode root; + private @Nullable List inputs; private final Map mapCorrelateToRex = new HashMap<>(); private List registered = new ArrayList<>(); @@ -4418,13 +4485,13 @@ protected class Blackboard implements SqlRexContext, SqlVisitor, /** * Workspace for building aggregates. */ - AggConverter agg; + @Nullable AggConverter agg; /** * When converting window aggregate, we need to know if the window is * guaranteed to be non-empty. */ - SqlWindow window; + @Nullable SqlWindow window; /** * Project the groupby expressions out of the root of this sub-select. @@ -4454,13 +4521,21 @@ protected class Blackboard implements SqlRexContext, SqlVisitor, * null otherwise * @param top Whether this is the root of the query */ - protected Blackboard(SqlValidatorScope scope, - Map nameToNodeMap, boolean top) { + protected Blackboard(@Nullable SqlValidatorScope scope, + @Nullable Map nameToNodeMap, boolean top) { this.scope = scope; this.nameToNodeMap = nameToNodeMap; this.top = top; } + public RelNode root() { + return requireNonNull(root, "root"); + } + + public SqlValidatorScope scope() { + return requireNonNull(scope, "scope"); + } + public void setPatternVarRef(boolean isVarRef) { this.isPatternVarRef = isVarRef; } @@ -4484,14 +4559,14 @@ public RexNode register( public RexNode register( RelNode rel, JoinRelType joinType, - List leftKeys) { - assert joinType != null; + @Nullable List leftKeys) { + requireNonNull(joinType, "joinType"); registered.add(new RegisterArgs(rel, joinType, leftKeys)); if (root == null) { - assert leftKeys == null; + assert leftKeys == null : "leftKeys must be null"; setRoot(rel, false); return rexBuilder.makeRangeReference( - root.getRowType(), + root().getRowType(), 0, false); } @@ -4501,7 +4576,7 @@ public RexNode register( if (leftKeys != null) { List newLeftInputExprs = new ArrayList<>(); for (int i = 0; i < origLeftInputCount; i++) { - newLeftInputExprs.add(rexBuilder.makeInputRef(root, i)); + newLeftInputExprs.add(rexBuilder.makeInputRef(root(), i)); } final List leftJoinKeys = new ArrayList<>(); @@ -4515,21 +4590,22 @@ public RexNode register( } RelNode newLeftInput = - relBuilder.push(root) + relBuilder.push(root()) .project(newLeftInputExprs) .build(); // maintain the group by mapping in the new LogicalProject - if (mapRootRelToFieldProjection.containsKey(root)) { + Map currentProjection = mapRootRelToFieldProjection.get(root()); + if (currentProjection != null) { mapRootRelToFieldProjection.put( newLeftInput, - mapRootRelToFieldProjection.get(root)); + currentProjection); } setRoot(newLeftInput, false); // right fields appear after the LHS fields. - final int rightOffset = root.getRowType().getFieldCount() + final int rightOffset = root().getRowType().getFieldCount() - newLeftInput.getRowType().getFieldCount(); final List rightKeys = Util.range(rightOffset, rightOffset + leftKeys.size()); @@ -4541,11 +4617,11 @@ public RexNode register( joinCond = rexBuilder.makeLiteral(true); } - int leftFieldCount = root.getRowType().getFieldCount(); + int leftFieldCount = root().getRowType().getFieldCount(); final RelNode join = createJoin( this, - root, + root(), rel, joinCond, joinType); @@ -4600,7 +4676,7 @@ public RelNode reRegister(RelNode root) { for (RegisterArgs reg: registerCopy) { register(reg.rel, reg.joinType, reg.leftKeys); } - return this.root; + return requireNonNull(this.root, "root"); } /** @@ -4626,7 +4702,7 @@ public void setRoot(RelNode root, boolean leaf) { private void setRoot( List inputs, - RelNode root, + @Nullable RelNode root, boolean hasSystemFields) { this.inputs = inputs; this.root = root; @@ -4646,7 +4722,7 @@ private void setRoot( * * @param datasetName Dataset name */ - public void setDataset(String datasetName) { + public void setDataset(@Nullable String datasetName) { } void setRoot(List inputs) { @@ -4660,7 +4736,7 @@ void setRoot(List inputs) { * @return a {@link RexFieldAccess} or {@link RexRangeRef}, or null if * not found */ - Pair> lookupExp(SqlQualified qualified) { + @Nullable Pair> lookupExp(SqlQualified qualified) { if (nameToNodeMap != null && qualified.prefixLength == 1) { RexNode node = nameToNodeMap.get(qualified.identifier.names.get(0)); if (node == null) { @@ -4670,10 +4746,10 @@ Pair> lookupExp(SqlQualified qualified) { return Pair.of(node, null); } final SqlNameMatcher nameMatcher = - scope.getValidator().getCatalogReader().nameMatcher(); + scope().getValidator().getCatalogReader().nameMatcher(); final SqlValidatorScope.ResolvedImpl resolved = new SqlValidatorScope.ResolvedImpl(); - scope.resolve(qualified.prefix(), nameMatcher, false, resolved); + scope().resolve(qualified.prefix(), nameMatcher, false, resolved); if (!(resolved.count() == 1)) { return null; } @@ -4714,7 +4790,8 @@ Pair> lookupExp(SqlQualified qualified) { return Pair.of(rexBuilder.makeCorrel(rowType, correlId), null); } else { final RelDataTypeFactory.Builder builder = typeFactory.builder(); - final ListScope ancestorScope1 = (ListScope) resolve.scope; + final ListScope ancestorScope1 = (ListScope) + requireNonNull(resolve.scope, "resolve.scope"); final ImmutableMap.Builder fields = ImmutableMap.builder(); int i = 0; @@ -4750,7 +4827,8 @@ RexNode lookup( false); } - RelDataTypeField getRootField(RexInputRef inputRef) { + @Nullable RelDataTypeField getRootField(RexInputRef inputRef) { + List inputs = this.inputs; if (inputs == null) { return null; } @@ -4804,7 +4882,7 @@ void registerSubQuery(SqlNode node, RelOptUtil.Logic logic) { subQueryList.add(new SubQuery(node, logic)); } - SubQuery getSubQuery(SqlNode expr) { + @Nullable SubQuery getSubQuery(SqlNode expr) { for (SubQuery subQuery : subQueryList) { // Compare the reference to make sure the matched node has // exact scope where it belongs. @@ -4827,11 +4905,12 @@ ImmutableList retrieveCursors() { @Override public RexNode convertExpression(SqlNode expr) { // If we're in aggregation mode and this is an expression in the // GROUP BY clause, return a reference to the field. + AggConverter agg = this.agg; if (agg != null) { - final SqlNode expandedGroupExpr = validator.expand(expr, scope); + final SqlNode expandedGroupExpr = validator().expand(expr, scope()); final int ref = agg.lookupGroupExpr(expandedGroupExpr); if (ref >= 0) { - return rexBuilder.makeInputRef(root, ref); + return rexBuilder.makeInputRef(root(), ref); } if (expr instanceof SqlCall) { final RexNode rex = agg.lookupAggregates((SqlCall) expr); @@ -4934,10 +5013,10 @@ ImmutableList retrieveCursors() { case CURSOR: case IN: case NOT_IN: - subQuery = Objects.requireNonNull(getSubQuery(expr)); - rex = Objects.requireNonNull(subQuery.expr); + subQuery = requireNonNull(getSubQuery(expr)); + rex = requireNonNull(subQuery.expr); return StandardConvertletTable.castToValidatedType(expr, rex, - validator, rexBuilder); + validator(), rexBuilder); case SELECT: case EXISTS: @@ -4982,7 +5061,7 @@ && isConvertedSubq(rex)) { // Apply standard conversions. rex = expr.accept(this); - return Objects.requireNonNull(rex); + return requireNonNull(rex); } /** @@ -5014,12 +5093,12 @@ public RexFieldCollation convertSortExpression(SqlNode expr, switch (nullDirection) { case UNSPECIFIED: final RelFieldCollation.NullDirection nullDefaultDirection = - validator.config().defaultNullCollation().last(desc(direction)) + validator().config().defaultNullCollation().last(desc(direction)) ? RelFieldCollation.NullDirection.LAST : RelFieldCollation.NullDirection.FIRST; if (nullDefaultDirection != direction.defaultNullDirection()) { SqlKind nullDirectionSqlKind = - validator.config().defaultNullCollation().last(desc(direction)) + validator().config().defaultNullCollation().last(desc(direction)) ? SqlKind.NULLS_LAST : SqlKind.NULLS_FIRST; flags.add(nullDirectionSqlKind); @@ -5086,7 +5165,7 @@ private boolean isConvertedSubq(RexNode rex) { @Override public RexRangeRef getSubQueryExpr(SqlCall call) { final SubQuery subQuery = getSubQuery(call); assert subQuery != null; - return (RexRangeRef) subQuery.expr; + return (RexRangeRef) requireNonNull(subQuery.expr, () -> "subQuery.expr for " + call); } @Override public RelDataTypeFactory getTypeFactory() { @@ -5098,7 +5177,7 @@ private boolean isConvertedSubq(RexNode rex) { } @Override public SqlValidator getValidator() { - return validator; + return validator(); } @Override public RexNode convertLiteral(SqlLiteral literal) { @@ -5120,11 +5199,12 @@ public RexNode convertInterval(SqlIntervalQualifier intervalQualifier) { && (op.isAggregator() || op.getKind() == SqlKind.FILTER || op.getKind() == SqlKind.WITHIN_GROUP)) { - return agg.lookupAggregates(call); + return requireNonNull(agg.lookupAggregates(call), + () -> "agg.lookupAggregates for call " + call); } } return exprConverter.convertCall(this, - new SqlCallBinding(validator, scope, call).permutedCall()); + new SqlCallBinding(validator(), scope, call).permutedCall()); } @Override public RexNode visit(SqlNodeList nodeList) { @@ -5171,7 +5251,9 @@ private static class DeferredLookup { } public RexFieldAccess getFieldAccess(CorrelationId name) { - return (RexFieldAccess) bb.mapCorrelateToRex.get(name); + return (RexFieldAccess) requireNonNull( + bb.mapCorrelateToRex.get(name), + () -> "Correlation " + name + " is not found"); } public String getOriginalRelName() { @@ -5217,7 +5299,7 @@ private static class NoOpSubQueryConverter implements SubQueryConverter { */ protected class AggConverter implements SqlVisitor { private final Blackboard bb; - public final AggregatingSelectScope aggregatingSelectScope; + public final @Nullable AggregatingSelectScope aggregatingSelectScope; private final Map nameMap = new HashMap<>(); @@ -5240,7 +5322,7 @@ protected class AggConverter implements SqlVisitor { * aggregates. The right field of each pair is the name of the expression, * where the expressions are simple mappings to input fields. */ - private final List> convertedInputExprs = + private final List> convertedInputExprs = new ArrayList<>(); /** Expressions to be evaluated as rows are being placed into the @@ -5255,7 +5337,7 @@ protected class AggConverter implements SqlVisitor { /** Whether we are directly inside a windowed aggregate. */ private boolean inOver = false; - AggConverter(Blackboard bb, AggregatingSelectScope aggregatingSelectScope) { + AggConverter(Blackboard bb, @Nullable AggregatingSelectScope aggregatingSelectScope) { this.bb = bb; this.aggregatingSelectScope = aggregatingSelectScope; } @@ -5275,7 +5357,9 @@ public AggConverter(Blackboard bb, SqlSelect select) { // Collect all expressions used in the select list so that aggregate // calls can be named correctly. - final SqlNodeList selectList = select.getSelectList(); + final SqlNodeList selectList = requireNonNull( + select.getSelectList(), + () -> "selectList must not be null in " + select); for (int i = 0; i < selectList.size(); i++) { SqlNode selectItem = selectList.get(i); String name = null; @@ -5287,7 +5371,8 @@ public AggConverter(Blackboard bb, SqlSelect select) { name = call.operand(1).toString(); } if (name == null) { - name = validator.deriveAlias(selectItem, i); + name = validator().deriveAlias(selectItem, i); + assert name != null : "alias must not be null for " + selectItem + ", i=" + i; } nameMap.put(selectItem.toString(), name); } @@ -5331,10 +5416,10 @@ void addAuxiliaryGroupExpr(SqlNode node, int index, * @param expr Expression * @param name Suggested name */ - private void addExpr(RexNode expr, String name) { + private void addExpr(RexNode expr, @Nullable String name) { if ((name == null) && (expr instanceof RexInputRef)) { final int i = ((RexInputRef) expr).getIndex(); - name = bb.root.getRowType().getFieldList().get(i).getName(); + name = bb.root().getRowType().getFieldList().get(i).getName(); } if (Pair.right(convertedInputExprs).contains(name)) { // In case like 'SELECT ... GROUP BY x, y, x', don't add @@ -5432,8 +5517,8 @@ private void translateAgg(SqlCall call) { translateAgg(call, null, null, false, call); } - private void translateAgg(SqlCall call, SqlNode filter, - SqlNodeList orderList, boolean ignoreNulls, SqlCall outerCall) { + private void translateAgg(SqlCall call, @Nullable SqlNode filter, + @Nullable SqlNodeList orderList, boolean ignoreNulls, SqlCall outerCall) { assert bb.agg == this; assert outerCall != null; final List operands = call.getOperandList(); @@ -5544,7 +5629,7 @@ private void translateAgg(SqlCall call, SqlNode filter, SqlAggFunction aggFunction = (SqlAggFunction) call.getOperator(); - final RelDataType type = validator.deriveType(bb.scope, call); + final RelDataType type = validator().deriveType(bb.scope(), call); boolean distinct = false; SqlLiteral quantifier = call.getFunctionQuantifier(); if ((null != quantifier) @@ -5625,7 +5710,7 @@ public int lookupGroupExpr(SqlNode expr) { return -1; } - public RexNode lookupAggregates(SqlCall call) { + public @Nullable RexNode lookupAggregates(SqlCall call) { // assert call.getOperator().isAggregator(); assert bb.agg == this; @@ -5636,14 +5721,14 @@ public RexNode lookupAggregates(SqlCall call) { final int groupOrdinal = e.getValue().i; return converter.convert(rexBuilder, convertedInputExprs.get(groupOrdinal).left, - rexBuilder.makeInputRef(bb.root, groupOrdinal)); + rexBuilder.makeInputRef(castNonNull(bb.root), groupOrdinal)); } } return aggMapping.get(call); } - public List> getPreExprs() { + public List> getPreExprs() { return convertedInputExprs; } @@ -5864,7 +5949,7 @@ private class HistogramShuttle extends RexShuttle { * @param aggFunction An aggregate function * @return Its histogram function, or null */ - SqlFunction getHistogramOp(SqlAggFunction aggFunction) { + @Nullable SqlFunction getHistogramOp(SqlAggFunction aggFunction) { if (aggFunction == SqlStdOperatorTable.MIN) { return SqlStdOperatorTable.HISTOGRAM_MIN; } else if (aggFunction == SqlStdOperatorTable.MAX) { @@ -5900,7 +5985,7 @@ private RelDataType computeHistogramType(RelDataType type) { private static class SubQuery { final SqlNode node; final RelOptUtil.Logic logic; - RexNode expr; + @Nullable RexNode expr; private SubQuery(SqlNode node, RelOptUtil.Logic logic) { this.node = node; diff --git a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java index eb4ecc597e1a..b5e28f6e7864 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java @@ -76,6 +76,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; @@ -437,7 +439,10 @@ public RexNode convertMultiset( cx.getRexBuilder().makeInputRef( msType, rr.getOffset()); - assert msType.getComponentType().isStruct(); + assert msType.getComponentType() != null && msType.getComponentType().isStruct() + : "componentType of " + msType + " must be struct"; + assert originalType.getComponentType() != null + : "componentType of " + originalType + " must be struct"; if (!originalType.getComponentType().isStruct()) { // If the type is not a struct, the multiset operator will have // wrapped the type as a record. Add a call to the $SLICE operator @@ -479,7 +484,10 @@ public RexNode convertMultisetQuery( cx.getRexBuilder().makeInputRef( msType, rr.getOffset()); - assert msType.getComponentType().isStruct(); + assert msType.getComponentType() != null && msType.getComponentType().isStruct() + : "componentType of " + msType + " must be struct"; + assert originalType.getComponentType() != null + : "componentType of " + originalType + " must be struct"; if (!originalType.getComponentType().isStruct()) { // If the type is not a struct, the multiset operator will have // wrapped the type as a record. Add a call to the $SLICE operator @@ -553,8 +561,14 @@ protected RexNode convertCast( } if (null != dataType.getCollectionsTypeName()) { final RelDataType argComponentType = - arg.getType().getComponentType(); - final RelDataType componentType = type.getComponentType(); + Objects.requireNonNull( + arg.getType().getComponentType(), + () -> "componentType of " + arg); + + RelDataType typeFinal = type; + final RelDataType componentType = Objects.requireNonNull( + type.getComponentType(), + () -> "componentType of " + typeFinal); if (argComponentType.isStruct() && !componentType.isStruct()) { RelDataType tt = @@ -720,6 +734,7 @@ public RexNode convertJsonValueFunction( SqlOperandTypeChecker.Consistency.NONE); RelDataType returnType = cx.getValidator().getValidatedNodeTypeIfKnown(call); + Objects.requireNonNull(returnType, () -> "Unable to get type of " + call); return cx.getRexBuilder().makeCall(returnType, fun, exprs); } @@ -870,7 +885,7 @@ private static List convertExpressionList(SqlRexContext cx, return exprs; } - private static RelDataType consistentType(SqlRexContext cx, + private static @Nullable RelDataType consistentType(SqlRexContext cx, SqlOperandTypeChecker.Consistency consistency, List types) { switch (consistency) { case COMPARE: @@ -1211,7 +1226,7 @@ private SqlNode expandRegrSzz( private SqlNode expandCovariance( final SqlNode arg0Input, final SqlNode arg1Input, - final SqlNode dependent, + final @Nullable SqlNode dependent, final RelDataType varType, final SqlRexContext cx, boolean biased) { @@ -1272,7 +1287,7 @@ private SqlNode expandCovariance( } private SqlNode getCastedSqlNode(SqlNode argInput, RelDataType varType, - SqlParserPos pos, RexNode argRex) { + SqlParserPos pos, @Nullable RexNode argRex) { SqlNode arg; if (argRex != null && !argRex.getType().equals(varType)) { arg = SqlStdOperatorTable.CAST.createCall( @@ -1413,7 +1428,7 @@ private SqlNode expandVariance( } private SqlNode getCastedSqlNode(SqlNode argInput, RelDataType varType, - SqlParserPos pos, RexNode argRex) { + SqlParserPos pos, @Nullable RexNode argRex) { SqlNode arg; if (argRex != null && !argRex.getType().equals(varType)) { arg = SqlStdOperatorTable.CAST.createCall( @@ -1512,7 +1527,7 @@ private static class TimestampAddConvertlet implements SqlRexConvertlet { // => timestamp + count * INTERVAL '1' UNIT final RexBuilder rexBuilder = cx.getRexBuilder(); final SqlLiteral unitLiteral = call.operand(0); - final TimeUnit unit = unitLiteral.symbolValue(TimeUnit.class); + final TimeUnit unit = unitLiteral.getValueAs(TimeUnit.class); RexNode interval2Add; SqlIntervalQualifier qualifier = new SqlIntervalQualifier(unit, null, unitLiteral.getParserPosition()); @@ -1544,7 +1559,7 @@ private static class TimestampDiffConvertlet implements SqlRexConvertlet { // => (t2 - t1) UNIT final RexBuilder rexBuilder = cx.getRexBuilder(); final SqlLiteral unitLiteral = call.operand(0); - TimeUnit unit = unitLiteral.symbolValue(TimeUnit.class); + TimeUnit unit = unitLiteral.getValueAs(TimeUnit.class); BigDecimal multiplier = BigDecimal.ONE; BigDecimal divider = BigDecimal.ONE; SqlTypeName sqlTypeName = unit == TimeUnit.NANOSECOND diff --git a/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java b/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java index 6f510811d406..1afb8dc390c4 100644 --- a/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java +++ b/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java @@ -39,12 +39,13 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; -import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; import javax.sql.DataSource; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link SqlStatisticProvider} that generates and executes * SQL queries. @@ -76,12 +77,12 @@ public class QuerySqlStatisticProvider implements SqlStatisticProvider { * @param sqlConsumer Called when each SQL statement is generated */ public QuerySqlStatisticProvider(Consumer sqlConsumer) { - this.sqlConsumer = Objects.requireNonNull(sqlConsumer); + this.sqlConsumer = requireNonNull(sqlConsumer); } @Override public double tableCardinality(RelOptTable table) { - final SqlDialect dialect = table.unwrap(SqlDialect.class); - final DataSource dataSource = table.unwrap(DataSource.class); + final SqlDialect dialect = table.unwrapOrThrow(SqlDialect.class); + final DataSource dataSource = table.unwrapOrThrow(DataSource.class); return withBuilder( (cluster, relOptSchema, relBuilder) -> { // Generate: @@ -109,8 +110,8 @@ public QuerySqlStatisticProvider(Consumer sqlConsumer) { @Override public boolean isForeignKey(RelOptTable fromTable, List fromColumns, RelOptTable toTable, List toColumns) { - final SqlDialect dialect = fromTable.unwrap(SqlDialect.class); - final DataSource dataSource = fromTable.unwrap(DataSource.class); + final SqlDialect dialect = fromTable.unwrapOrThrow(SqlDialect.class); + final DataSource dataSource = fromTable.unwrapOrThrow(DataSource.class); return withBuilder( (cluster, relOptSchema, relBuilder) -> { // EMP(DEPTNO) is a foreign key to DEPT(DEPTNO) if the following @@ -153,8 +154,8 @@ public QuerySqlStatisticProvider(Consumer sqlConsumer) { } @Override public boolean isKey(RelOptTable table, List columns) { - final SqlDialect dialect = table.unwrap(SqlDialect.class); - final DataSource dataSource = table.unwrap(DataSource.class); + final SqlDialect dialect = table.unwrapOrThrow(SqlDialect.class); + final DataSource dataSource = table.unwrapOrThrow(DataSource.class); return withBuilder( (cluster, relOptSchema, relBuilder) -> { // The collection of columns ['DEPTNO'] is a key for 'EMP' if the diff --git a/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java b/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java index 63db0e8d0517..40844f3b7939 100644 --- a/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java +++ b/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Interface that describes how to configure planning sessions generated * using the Frameworks tools. @@ -58,12 +60,12 @@ public interface FrameworkConfig { * Returns the default schema that should be checked before looking at the * root schema. Returns null to only consult the root schema. */ - SchemaPlus getDefaultSchema(); + @Nullable SchemaPlus getDefaultSchema(); /** * Returns the executor used to evaluate constant expressions. */ - RexExecutor getExecutor(); + @Nullable RexExecutor getExecutor(); /** * Returns a list of one or more programs used during the course of query @@ -94,7 +96,7 @@ public interface FrameworkConfig { * Returns the cost factory that should be used when creating the planner. * If null, use the default cost factory for that planner. */ - RelOptCostFactory getCostFactory(); + @Nullable RelOptCostFactory getCostFactory(); /** * Returns a list of trait definitions. @@ -108,7 +110,7 @@ public interface FrameworkConfig { * the order of this list. The most important trait comes first in the list, * followed by the second most important one, etc.

    */ - ImmutableList getTraitDefs(); + @Nullable ImmutableList getTraitDefs(); /** * Returns the convertlet table that should be used when converting from SQL @@ -144,5 +146,5 @@ public interface FrameworkConfig { /** * Returns a view expander. */ - RelOptTable.ViewExpander getViewExpander(); + RelOptTable.@Nullable ViewExpander getViewExpander(); } diff --git a/core/src/main/java/org/apache/calcite/tools/Frameworks.java b/core/src/main/java/org/apache/calcite/tools/Frameworks.java index 5c3d0ffec132..fcea840e9457 100644 --- a/core/src/main/java/org/apache/calcite/tools/Frameworks.java +++ b/core/src/main/java/org/apache/calcite/tools/Frameworks.java @@ -44,6 +44,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.Connection; import java.sql.DriverManager; import java.util.List; @@ -217,17 +219,17 @@ public static class ConfigBuilder { private SqlOperatorTable operatorTable; private ImmutableList programs; private Context context; - private ImmutableList traitDefs; + private @Nullable ImmutableList traitDefs; private SqlParser.Config parserConfig; private SqlValidator.Config sqlValidatorConfig; private SqlToRelConverter.Config sqlToRelConverterConfig; - private SchemaPlus defaultSchema; - private RexExecutor executor; - private RelOptCostFactory costFactory; + private @Nullable SchemaPlus defaultSchema; + private @Nullable RexExecutor executor; + private @Nullable RelOptCostFactory costFactory; private RelDataTypeSystem typeSystem; private boolean evolveLattice; private SqlStatisticProvider statisticProvider; - private RelOptTable.ViewExpander viewExpander; + private RelOptTable.@Nullable ViewExpander viewExpander; /** Creates a ConfigBuilder, initializing to defaults. */ private ConfigBuilder() { @@ -289,7 +291,7 @@ public ConfigBuilder operatorTable(SqlOperatorTable operatorTable) { return this; } - public ConfigBuilder traitDefs(List traitDefs) { + public ConfigBuilder traitDefs(@Nullable List traitDefs) { if (traitDefs == null) { this.traitDefs = null; } else { @@ -379,33 +381,33 @@ static class StdFrameworkConfig implements FrameworkConfig { private final SqlRexConvertletTable convertletTable; private final SqlOperatorTable operatorTable; private final ImmutableList programs; - private final ImmutableList traitDefs; + private final @Nullable ImmutableList traitDefs; private final SqlParser.Config parserConfig; private final SqlValidator.Config sqlValidatorConfig; private final SqlToRelConverter.Config sqlToRelConverterConfig; - private final SchemaPlus defaultSchema; - private final RelOptCostFactory costFactory; + private final @Nullable SchemaPlus defaultSchema; + private final @Nullable RelOptCostFactory costFactory; private final RelDataTypeSystem typeSystem; - private final RexExecutor executor; + private final @Nullable RexExecutor executor; private final boolean evolveLattice; private final SqlStatisticProvider statisticProvider; - private final RelOptTable.ViewExpander viewExpander; + private final RelOptTable.@Nullable ViewExpander viewExpander; StdFrameworkConfig(Context context, SqlRexConvertletTable convertletTable, SqlOperatorTable operatorTable, ImmutableList programs, - ImmutableList traitDefs, + @Nullable ImmutableList traitDefs, SqlParser.Config parserConfig, SqlValidator.Config sqlValidatorConfig, SqlToRelConverter.Config sqlToRelConverterConfig, - SchemaPlus defaultSchema, - RelOptCostFactory costFactory, + @Nullable SchemaPlus defaultSchema, + @Nullable RelOptCostFactory costFactory, RelDataTypeSystem typeSystem, - RexExecutor executor, + @Nullable RexExecutor executor, boolean evolveLattice, SqlStatisticProvider statisticProvider, - RelOptTable.ViewExpander viewExpander) { + RelOptTable.@Nullable ViewExpander viewExpander) { this.context = context; this.convertletTable = convertletTable; this.operatorTable = operatorTable; @@ -435,11 +437,11 @@ static class StdFrameworkConfig implements FrameworkConfig { return sqlToRelConverterConfig; } - @Override public SchemaPlus getDefaultSchema() { + @Override public @Nullable SchemaPlus getDefaultSchema() { return defaultSchema; } - @Override public RexExecutor getExecutor() { + @Override public @Nullable RexExecutor getExecutor() { return executor; } @@ -447,11 +449,11 @@ static class StdFrameworkConfig implements FrameworkConfig { return programs; } - @Override public RelOptCostFactory getCostFactory() { + @Override public @Nullable RelOptCostFactory getCostFactory() { return costFactory; } - @Override public ImmutableList getTraitDefs() { + @Override public @Nullable ImmutableList getTraitDefs() { return traitDefs; } @@ -479,7 +481,7 @@ static class StdFrameworkConfig implements FrameworkConfig { return statisticProvider; } - @Override public RelOptTable.ViewExpander getViewExpander() { + @Override public RelOptTable.@Nullable ViewExpander getViewExpander() { return viewExpander; } } diff --git a/core/src/main/java/org/apache/calcite/tools/Hoist.java b/core/src/main/java/org/apache/calcite/tools/Hoist.java index 5111bb5c06ab..6265ee2386c1 100644 --- a/core/src/main/java/org/apache/calcite/tools/Hoist.java +++ b/core/src/main/java/org/apache/calcite/tools/Hoist.java @@ -30,12 +30,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.sql.PreparedStatement; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Function; -import javax.annotation.Nonnull; /** * Utility that extracts constants from a SQL query. @@ -108,7 +109,7 @@ public Hoisted hoist(String sql) { throw new RuntimeException(e); } node.accept(new SqlShuttle() { - @Override public SqlNode visit(SqlLiteral literal) { + @Override public @Nullable SqlNode visit(SqlLiteral literal) { variables.add(new Variable(sql, variables.size(), literal)); return super.visit(literal); } @@ -120,7 +121,6 @@ public Hoisted hoist(String sql) { public interface Config { /** Returns the configuration for the SQL parser. */ @ImmutableBeans.Property - @Nonnull SqlParser.Config parserConfig(); /** Sets {@link #parserConfig()}. */ diff --git a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java index f41f1518fdd4..e11bac273fb0 100644 --- a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java @@ -30,17 +30,21 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Extension to {@link RelBuilder} for Pig relational operators. */ public class PigRelBuilder extends RelBuilder { - private String lastAlias; + private @Nullable String lastAlias; protected PigRelBuilder(Context context, RelOptCluster cluster, - RelOptSchema relOptSchema) { + @Nullable RelOptSchema relOptSchema) { super(context, cluster, relOptSchema); } @@ -140,7 +144,7 @@ public PigRelBuilder group(GroupOption option, Partitioner partitioner, aggregate(groupKey.e, aggregateCall(SqlStdOperatorTable.COLLECT, row).as(getAlias())); if (groupKey.i < n - 1) { - push(r); + push(requireNonNull(r, "r")); List predicates = new ArrayList<>(); for (int key : Util.range(groupCount)) { predicates.add(equals(field(2, 0, key), field(2, 1, key))); @@ -163,13 +167,14 @@ protected void validateGroupList(List groupKeyList) { } } - public String getAlias() { + public @Nullable String getAlias() { if (lastAlias != null) { return lastAlias; } else { RelNode top = peek(); if (top instanceof TableScan) { - return Util.last(top.getTable().getQualifiedName()); + TableScan scan = (TableScan) top; + return Util.last(scan.getTable().getQualifiedName()); } else { return null; } diff --git a/core/src/main/java/org/apache/calcite/tools/Programs.java b/core/src/main/java/org/apache/calcite/tools/Programs.java index 48d06380b1b7..dda92e0f4f1d 100644 --- a/core/src/main/java/org/apache/calcite/tools/Programs.java +++ b/core/src/main/java/org/apache/calcite/tools/Programs.java @@ -52,6 +52,8 @@ import java.util.Arrays; import java.util.List; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Utilities for creating {@link Program}s. */ @@ -238,9 +240,10 @@ public static Program subQuery(RelMetadataProvider metadataProvider) { return of(builder.build(), true, metadataProvider); } + @Deprecated public static Program getProgram() { return (planner, rel, requiredOutputTraits, materializations, lattices) -> - null; + castNonNull(null); } /** Returns the standard program used by Prepare. */ diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java index 8209e7673257..4b2056fb0cee 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -111,6 +111,9 @@ import com.google.common.collect.Multiset; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.AbstractList; import java.util.ArrayDeque; @@ -128,10 +131,12 @@ import java.util.TreeSet; import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import javax.annotation.Nonnull; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; +import static java.util.Objects.requireNonNull; + /** * Builder for relational expressions. * @@ -150,15 +155,15 @@ */ public class RelBuilder { protected final RelOptCluster cluster; - protected final RelOptSchema relOptSchema; + protected final @Nullable RelOptSchema relOptSchema; private final Deque stack = new ArrayDeque<>(); private final RexSimplify simplifier; private final Config config; private final RelOptTable.ViewExpander viewExpander; private RelFactories.Struct struct; - protected RelBuilder(Context context, RelOptCluster cluster, - RelOptSchema relOptSchema) { + protected RelBuilder(@Nullable Context context, RelOptCluster cluster, + @Nullable RelOptSchema relOptSchema) { this.cluster = cluster; this.relOptSchema = relOptSchema; if (context == null) { @@ -167,7 +172,7 @@ protected RelBuilder(Context context, RelOptCluster cluster, this.config = getConfig(context); this.viewExpander = getViewExpander(cluster, context); this.struct = - Objects.requireNonNull(RelFactories.Struct.fromContext(context)); + requireNonNull(RelFactories.Struct.fromContext(context)); final RexExecutor executor = Util.first(context.unwrap(RexExecutor.class), Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR)); @@ -188,7 +193,7 @@ protected RelBuilder(Context context, RelOptCluster cluster, * *

    The default view expander does not support expanding views. */ - private RelOptTable.ViewExpander getViewExpander(RelOptCluster cluster, Context context) { + private static RelOptTable.ViewExpander getViewExpander(RelOptCluster cluster, Context context) { return Util.first(context.unwrap(RelOptTable.ViewExpander.class), ViewExpanders.simpleContext(cluster)); } @@ -198,7 +203,7 @@ private RelOptTable.ViewExpander getViewExpander(RelOptCluster cluster, Context *

    Overrides {@link RelBuilder.Config#simplify} if * {@link Hook#REL_BUILDER_SIMPLIFY} is set. */ - private Config getConfig(Context context) { + private static Config getConfig(Context context) { final Config config = Util.first(context.unwrap(Config.class), Config.DEFAULT); boolean simplify = Hook.REL_BUILDER_SIMPLIFY.get(config.simplify()); @@ -260,7 +265,7 @@ public RelOptCluster getCluster() { return cluster; } - public RelOptSchema getRelOptSchema() { + public @Nullable RelOptSchema getRelOptSchema() { return relOptSchema; } @@ -308,10 +313,10 @@ public RelNode build() { /** Returns the relational expression at the top of the stack, but does not * remove it. */ public RelNode peek() { - return peek_().rel; + return castNonNull(peek_()).rel; } - private Frame peek_() { + private @Nullable Frame peek_() { return stack.peek(); } @@ -352,7 +357,7 @@ private int inputOffset(int inputCount, int inputOrdinal) { // Methods that return scalar expressions /** Creates a literal (constant expression). */ - public RexNode literal(Object value) { + public RexNode literal(@Nullable Object value) { final RexBuilder rexBuilder = cluster.getRexBuilder(); if (value == null) { final RelDataType type = getTypeFactory().createSqlType(SqlTypeName.NULL); @@ -472,8 +477,8 @@ public RexNode field(String alias, String fieldName) { * given alias. Searches for the relation starting at the top of the * stack. */ public RexNode field(int inputCount, String alias, String fieldName) { - Objects.requireNonNull(alias); - Objects.requireNonNull(fieldName); + requireNonNull(alias); + requireNonNull(fieldName); final List fields = new ArrayList<>(); for (int inputOrdinal = 0; inputOrdinal < inputCount; ++inputOrdinal) { final Frame frame = peek_(inputOrdinal); @@ -567,7 +572,7 @@ public ImmutableList fields(Iterable fieldNames) { /** Returns references to fields identified by a mapping. */ public ImmutableList fields(Mappings.TargetMapping mapping) { - return fields(Mappings.asList(mapping)); + return fields(Mappings.asListNonNull(mapping)); } /** Creates an access to a field by name. */ @@ -583,12 +588,12 @@ public RexNode dot(RexNode node, int fieldOrdinal) { } /** Creates a call to a scalar operator. */ - public @Nonnull RexNode call(SqlOperator operator, RexNode... operands) { + public RexNode call(SqlOperator operator, RexNode... operands) { return call(operator, ImmutableList.copyOf(operands)); } /** Creates a call to a scalar operator. */ - private @Nonnull RexCall call(SqlOperator operator, List operandList) { + private RexCall call(SqlOperator operator, List operandList) { switch (operator.getKind()) { case BETWEEN: assert operandList.size() == 3; @@ -603,7 +608,7 @@ public RexNode dot(RexNode node, int fieldOrdinal) { } /** Creates a call to a scalar operator. */ - public @Nonnull RexNode call(SqlOperator operator, + public RexNode call(SqlOperator operator, Iterable operands) { return call(operator, ImmutableList.copyOf(operands)); } @@ -704,7 +709,7 @@ public RexNode cast(RexNode expr, SqlTypeName typeName, int precision, * * @see #project */ - public RexNode alias(RexNode expr, String alias) { + public RexNode alias(RexNode expr, @Nullable String alias) { final RexNode aliasLiteral = literal(alias); switch (expr.getKind()) { case AS: @@ -795,7 +800,7 @@ public GroupKey groupKey(String... fieldNames) { *

    This method of creating a group key does not allow you to group on new * expressions, only column projections, but is efficient, especially when you * are coming from an existing {@link Aggregate}. */ - public GroupKey groupKey(@Nonnull ImmutableBitSet groupSet) { + public GroupKey groupKey(ImmutableBitSet groupSet) { return groupKey_(groupSet, ImmutableList.of(groupSet)); } @@ -806,7 +811,7 @@ public GroupKey groupKey(@Nonnull ImmutableBitSet groupSet) { * expressions, only column projections, but is efficient, especially when you * are coming from an existing {@link Aggregate}. */ public GroupKey groupKey(ImmutableBitSet groupSet, - @Nonnull Iterable groupSets) { + Iterable groupSets) { return groupKey_(groupSet, ImmutableList.copyOf(groupSets)); } @@ -815,7 +820,7 @@ public GroupKey groupKey(ImmutableBitSet groupSet, * or {@link #groupKey(ImmutableBitSet, Iterable)}. */ @Deprecated // to be removed before 2.0 public GroupKey groupKey(ImmutableBitSet groupSet, - ImmutableList groupSets) { + @Nullable ImmutableList groupSets) { return groupKey_(groupSet, groupSets == null ? ImmutableList.of(groupSet) : ImmutableList.copyOf(groupSets)); } @@ -824,46 +829,46 @@ public GroupKey groupKey(ImmutableBitSet groupSet, /** @deprecated Use {@link #groupKey(ImmutableBitSet, Iterable)}. */ @Deprecated // to be removed before 2.0 public GroupKey groupKey(ImmutableBitSet groupSet, boolean indicator, - ImmutableList groupSets) { + @Nullable ImmutableList groupSets) { Aggregate.checkIndicator(indicator); return groupKey_(groupSet, groupSets == null ? ImmutableList.of(groupSet) : ImmutableList.copyOf(groupSets)); } private GroupKey groupKey_(ImmutableBitSet groupSet, - @Nonnull ImmutableList groupSets) { + ImmutableList groupSets) { if (groupSet.length() > peek().getRowType().getFieldCount()) { throw new IllegalArgumentException("out of bounds: " + groupSet); } - Objects.requireNonNull(groupSets); + requireNonNull(groupSets); final ImmutableList nodes = fields(groupSet); return groupKey_(nodes, Util.transform(groupSets, bitSet -> fields(bitSet))); } @Deprecated // to be removed before 2.0 public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct, - RexNode filter, String alias, RexNode... operands) { + RexNode filter, @Nullable String alias, RexNode... operands) { return aggregateCall(aggFunction, distinct, false, false, filter, ImmutableList.of(), alias, ImmutableList.copyOf(operands)); } @Deprecated // to be removed before 2.0 public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct, - boolean approximate, RexNode filter, String alias, RexNode... operands) { + boolean approximate, RexNode filter, @Nullable String alias, RexNode... operands) { return aggregateCall(aggFunction, distinct, approximate, false, filter, ImmutableList.of(), alias, ImmutableList.copyOf(operands)); } @Deprecated // to be removed before 2.0 public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct, - RexNode filter, String alias, Iterable operands) { + RexNode filter, @Nullable String alias, Iterable operands) { return aggregateCall(aggFunction, distinct, false, false, filter, ImmutableList.of(), alias, ImmutableList.copyOf(operands)); } @Deprecated // to be removed before 2.0 public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct, - boolean approximate, RexNode filter, String alias, + boolean approximate, RexNode filter, @Nullable String alias, Iterable operands) { return aggregateCall(aggFunction, distinct, approximate, false, filter, ImmutableList.of(), alias, ImmutableList.copyOf(operands)); @@ -917,8 +922,9 @@ public AggCall aggregateCall(AggregateCall a, Mapping mapping) { /** Creates a call to an aggregate function with all applicable operands. */ protected AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct, - boolean approximate, boolean ignoreNulls, RexNode filter, ImmutableList orderKeys, - String alias, ImmutableList operands) { + boolean approximate, boolean ignoreNulls, @Nullable RexNode filter, + ImmutableList orderKeys, + @Nullable String alias, ImmutableList operands) { return new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls, filter, alias, operands, orderKeys); } @@ -935,21 +941,21 @@ public AggCall count(Iterable operands) { /** Creates a call to the {@code COUNT} aggregate function, * optionally distinct and with an alias. */ - public AggCall count(boolean distinct, String alias, RexNode... operands) { + public AggCall count(boolean distinct, @Nullable String alias, RexNode... operands) { return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, false, null, ImmutableList.of(), alias, ImmutableList.copyOf(operands)); } /** Creates a call to the {@code COUNT} aggregate function, * optionally distinct and with an alias. */ - public AggCall count(boolean distinct, String alias, + public AggCall count(boolean distinct, @Nullable String alias, Iterable operands) { return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, false, null, ImmutableList.of(), alias, ImmutableList.copyOf(operands)); } /** Creates a call to the {@code COUNT(*)} aggregate function. */ - public AggCall countStar(String alias) { + public AggCall countStar(@Nullable String alias) { return count(false, alias); } @@ -960,7 +966,7 @@ public AggCall sum(RexNode operand) { /** Creates a call to the {@code SUM} aggregate function, * optionally distinct and with an alias. */ - public AggCall sum(boolean distinct, String alias, RexNode operand) { + public AggCall sum(boolean distinct, @Nullable String alias, RexNode operand) { return aggregateCall(SqlStdOperatorTable.SUM, distinct, false, false, null, ImmutableList.of(), alias, ImmutableList.of(operand)); } @@ -972,7 +978,7 @@ public AggCall avg(RexNode operand) { /** Creates a call to the {@code AVG} aggregate function, * optionally distinct and with an alias. */ - public AggCall avg(boolean distinct, String alias, RexNode operand) { + public AggCall avg(boolean distinct, @Nullable String alias, RexNode operand) { return aggregateCall(SqlStdOperatorTable.AVG, distinct, false, false, null, ImmutableList.of(), alias, ImmutableList.of(operand)); } @@ -984,7 +990,7 @@ public AggCall min(RexNode operand) { /** Creates a call to the {@code MIN} aggregate function, * optionally with an alias. */ - public AggCall min(String alias, RexNode operand) { + public AggCall min(@Nullable String alias, RexNode operand) { return aggregateCall(SqlStdOperatorTable.MIN, false, false, false, null, ImmutableList.of(), alias, ImmutableList.of(operand)); } @@ -996,7 +1002,7 @@ public AggCall max(RexNode operand) { } /** Creates a call to the {@code MAX} aggregate function. */ - public AggCall max(String alias, RexNode operand) { + public AggCall max(@Nullable String alias, RexNode operand) { return aggregateCall(SqlStdOperatorTable.MAX, false, false, false, null, ImmutableList.of(), alias, ImmutableList.of(operand)); } @@ -1097,6 +1103,7 @@ public RexNode patternExclude(RexNode node) { */ public RelBuilder scan(Iterable tableNames) { final List names = ImmutableList.copyOf(tableNames); + requireNonNull(relOptSchema, "relOptSchema"); final RelOptTable relOptTable = relOptSchema.getTableForMember(names); if (relOptTable == null) { throw RESOURCE.tableNotFound(String.join(".", names)).ex(); @@ -1150,7 +1157,7 @@ public RelBuilder snapshot(RexNode period) { * @param op operator instance * @return column mappings associated with this function */ - private Set getColumnMappings(SqlOperator op) { + private @Nullable Set getColumnMappings(SqlOperator op) { SqlReturnTypeInference inference = op.getReturnTypeInference(); if (inference instanceof TableFunctionReturnTypeInference) { return ((TableFunctionReturnTypeInference) inference).getColumnMappings(); @@ -1285,7 +1292,7 @@ public RelBuilder project(Iterable nodes) { * @param fieldNames field names for expressions */ public RelBuilder project(Iterable nodes, - Iterable fieldNames) { + Iterable fieldNames) { return project(nodes, fieldNames, false); } @@ -1313,7 +1320,7 @@ public RelBuilder project(Iterable nodes, * @param force create project even if it is identity */ public RelBuilder project(Iterable nodes, - Iterable fieldNames, boolean force) { + Iterable fieldNames, boolean force) { return project_(nodes, fieldNames, ImmutableList.of(), force); } @@ -1386,10 +1393,10 @@ public RelBuilder projectExcept(Iterable expressions) { */ private RelBuilder project_( Iterable nodes, - Iterable fieldNames, + Iterable fieldNames, Iterable hints, boolean force) { - final Frame frame = stack.peek(); + final Frame frame = requireNonNull(peek_(), "frame stack is empty"); final RelDataType inputRowType = frame.rel.getRowType(); final List nodeList = Lists.newArrayList(nodes); @@ -1400,7 +1407,7 @@ private RelBuilder project_( return this; } - final List fieldNameList = Lists.newArrayList(fieldNames); + final List<@Nullable String> fieldNameList = Lists.newArrayList(fieldNames); while (fieldNameList.size() < nodeList.size()) { fieldNameList.add(null); } @@ -1548,12 +1555,12 @@ private RelBuilder project_( * projections are trivial */ public RelBuilder projectNamed(Iterable nodes, - Iterable fieldNames, boolean force) { + @Nullable Iterable fieldNames, boolean force) { @SuppressWarnings("unchecked") final List nodeList = nodes instanceof List ? (List) nodes : ImmutableList.copyOf(nodes); - final List fieldNameList = + final List<@Nullable String> fieldNameList = fieldNames == null ? null - : fieldNames instanceof List ? (List) fieldNames + : fieldNames instanceof List ? (List<@Nullable String>) fieldNames : ImmutableNullableList.copyOf(fieldNames); final RelNode input = peek(); final RelDataType rowType = @@ -1591,7 +1598,7 @@ public RelBuilder uncollect(List itemAliases, boolean withOrdinality) { cluster.traitSetOf(Convention.NONE), frame.rel, withOrdinality, - Objects.requireNonNull(itemAliases)))); + requireNonNull(itemAliases)))); return this; } @@ -1607,7 +1614,7 @@ public RelBuilder uncollect(List itemAliases, boolean withOrdinality) { * @param fieldNames List of desired field names; may contain null values or * have fewer fields than the current row type */ - public RelBuilder rename(List fieldNames) { + public RelBuilder rename(List fieldNames) { final List oldFieldNames = peek().getRowType().getFieldNames(); Preconditions.checkArgument(fieldNames.size() <= oldFieldNames.size(), "More names than fields"); @@ -1640,11 +1647,12 @@ public RelBuilder rename(List fieldNames) { *

    If the expression was created by {@link #alias}, replaces the expression * in the project list. */ - private String inferAlias(List exprList, RexNode expr, int i) { + private @Nullable String inferAlias(List exprList, RexNode expr, int i) { switch (expr.getKind()) { case INPUT_REF: final RexInputRef ref = (RexInputRef) expr; - return stack.peek().fields.get(ref.getIndex()).getValue().getName(); + return requireNonNull(stack.peek(), "empty frame stack") + .fields.get(ref.getIndex()).getValue().getName(); case CAST: return inferAlias(exprList, ((RexCall) expr).getOperands().get(0), -1); case AS: @@ -1652,7 +1660,8 @@ private String inferAlias(List exprList, RexNode expr, int i) { if (i >= 0) { exprList.set(i, call.getOperands().get(0)); } - return ((NlsString) ((RexLiteral) call.getOperands().get(1)).getValue()) + NlsString value = (NlsString) ((RexLiteral) call.getOperands().get(1)).getValue(); + return castNonNull(value) .getValue(); default: return null; @@ -1818,7 +1827,7 @@ public RelBuilder aggregate(GroupKey groupKey, Iterable aggCalls) { // There are duplicate aggregate calls. Rebuild the list to eliminate // duplicates, then add a Project. final Set callSet = new HashSet<>(); - final List> projects = new ArrayList<>(); + final List> projects = new ArrayList<>(); Util.range(groupSet.cardinality()) .forEach(i -> projects.add(Pair.of(i, null))); final List distinctAggregateCalls = new ArrayList<>(); @@ -1944,7 +1953,7 @@ private RelBuilder rewriteAggregateWithGroupId(ImmutableBitSet groupSet, for (int groupId = 0; groupId <= maxGroupId; groupId++) { // Create the Aggregate node without GROUP_ID() call stack.push(frame); - aggregate(groupKey(groupSet, groupIdToGroupSets.get(groupId)), + aggregate(groupKey(groupSet, castNonNull(groupIdToGroupSets.get(groupId))), aggregateCallsWithoutGroupId); final List selectList = new ArrayList<>(); @@ -2076,6 +2085,7 @@ public RelBuilder transientScan(String tableName) { @Experimental public RelBuilder transientScan(String tableName, RelDataType rowType) { TransientTable transientTable = new ListTransientTable(tableName, rowType); + requireNonNull(relOptSchema, "relOptSchema"); RelOptTable relOptTable = RelOptTableImpl.create( relOptSchema, rowType, @@ -2167,7 +2177,7 @@ public RelBuilder repeatUnion(String tableName, boolean all, int iterationLimit) * Auxiliary class to find a certain RelOptTable based on its name. */ private static final class RelOptTableFinder extends RelHomogeneousShuttle { - private RelOptTable relOptTable = null; + private @MonotonicNonNull RelOptTable relOptTable = null; private final String tableName; private RelOptTableFinder(String tableName) { @@ -2414,7 +2424,7 @@ public RelBuilder as(final String alias) { * @param fieldNames Field names * @param values Values */ - public RelBuilder values(String[] fieldNames, Object... values) { + public RelBuilder values(@Nullable String[] fieldNames, @Nullable Object... values) { if (fieldNames == null || fieldNames.length == 0 || values.length % fieldNames.length != 0 @@ -2423,17 +2433,18 @@ public RelBuilder values(String[] fieldNames, Object... values) { "Value count must be a positive multiple of field count"); } final int rowCount = values.length / fieldNames.length; - for (Ord fieldName : Ord.zip(fieldNames)) { + for (Ord<@Nullable String> fieldName : Ord.zip(fieldNames)) { if (allNull(values, fieldName.i, fieldNames.length)) { throw new IllegalArgumentException("All values of field '" + fieldName.e - + "' are null; cannot deduce type"); + + "' (field index " + fieldName.i + ")" + + " are null; cannot deduce type"); } } final ImmutableList> tupleList = tupleList(fieldNames.length, values); final RelDataTypeFactory typeFactory = cluster.getTypeFactory(); final RelDataTypeFactory.Builder builder = typeFactory.builder(); - for (final Ord fieldName : Ord.zip(fieldNames)) { + for (final Ord<@Nullable String> fieldName : Ord.zip(fieldNames)) { final String name = fieldName.e != null ? fieldName.e : "expr$" + fieldName.i; final RelDataType type = typeFactory.leastRestrictive( @@ -2446,6 +2457,7 @@ public RelBuilder values(String[] fieldNames, Object... values) { return rowCount; } }); + assert type != null : "can't infer type for field " + fieldName.i + ", " + fieldName.e; builder.add(name, type); } final RelDataType rowType = builder.build(); @@ -2453,7 +2465,7 @@ public RelBuilder values(String[] fieldNames, Object... values) { } private ImmutableList> tupleList(int columnCount, - Object[] values) { + @Nullable Object[] values) { final ImmutableList.Builder> listBuilder = ImmutableList.builder(); final List valueList = new ArrayList<>(); @@ -2469,7 +2481,7 @@ private ImmutableList> tupleList(int columnCount, } /** Returns whether all values for a given column are null. */ - private boolean allNull(Object[] values, int column, int columnCount) { + private boolean allNull(@Nullable Object[] values, int column, int columnCount) { for (int i = column; i < values.length; i += columnCount) { if (values[i] != null) { return false; @@ -2697,7 +2709,7 @@ public RelBuilder sortLimit(int offset, int fetch, private static RelFieldCollation collation(RexNode node, RelFieldCollation.Direction direction, - RelFieldCollation.NullDirection nullDirection, List extraNodes) { + RelFieldCollation.@Nullable NullDirection nullDirection, List extraNodes) { switch (node.getKind()) { case INPUT_REF: return new RelFieldCollation(((RexInputRef) node).getIndex(), direction, @@ -2908,7 +2920,7 @@ public RelBuilder hints(RelHint... hints) { * {@link org.apache.calcite.rel.hint.Hintable} */ public RelBuilder hints(Iterable hints) { - Objects.requireNonNull(hints); + requireNonNull(hints); final List relHintList = hints instanceof List ? (List) hints : Lists.newArrayList(hints); if (relHintList.isEmpty()) { @@ -2935,7 +2947,7 @@ public void clear() { public interface AggCall { /** Returns a copy of this AggCall that applies a filter before aggregating * values. */ - AggCall filter(RexNode condition); + AggCall filter(@Nullable RexNode condition); /** Returns a copy of this AggCall that sorts its input values by * {@code orderKeys} before aggregating, as in SQL's {@code WITHIN GROUP} @@ -2955,7 +2967,7 @@ public interface AggCall { AggCall ignoreNulls(boolean ignoreNulls); /** Returns a copy of this AggCall with a given alias. */ - AggCall as(String alias); + AggCall as(@Nullable String alias); /** Returns a copy of this AggCall that is optionally distinct. */ AggCall distinct(boolean distinct); @@ -2970,7 +2982,7 @@ private interface AggCallPlus extends AggCall { SqlAggFunction op(); /** Returns the alias. */ - String alias(); + @Nullable String alias(); /** Returns an {@link AggregateCall} that is approximately equivalent * to this {@code AggCall} and is good for certain things, such as deriving @@ -2992,7 +3004,7 @@ public interface GroupKey { /** Assigns an alias to this group key. * *

    Used to assign field names in the {@code group} operation. */ - GroupKey alias(String alias); + GroupKey alias(@Nullable String alias); /** Returns the number of columns in the group key. */ int groupKeyCount(); @@ -3001,12 +3013,13 @@ public interface GroupKey { /** Implementation of {@link RelBuilder.GroupKey}. */ static class GroupKeyImpl implements GroupKey { final ImmutableList nodes; - final ImmutableList> nodeLists; - final String alias; + final @Nullable ImmutableList> nodeLists; + final @Nullable String alias; GroupKeyImpl(ImmutableList nodes, - ImmutableList> nodeLists, String alias) { - this.nodes = Objects.requireNonNull(nodes); + @Nullable ImmutableList> nodeLists, + @Nullable String alias) { + this.nodes = requireNonNull(nodes); this.nodeLists = nodeLists; this.alias = alias; } @@ -3019,7 +3032,7 @@ static class GroupKeyImpl implements GroupKey { return nodes.size(); } - @Override public GroupKey alias(String alias) { + @Override public GroupKey alias(@Nullable String alias) { return Objects.equals(this.alias, alias) ? this : new GroupKeyImpl(nodes, nodeLists, alias); @@ -3036,16 +3049,16 @@ private class AggCallImpl implements AggCallPlus { private final boolean distinct; private final boolean approximate; private final boolean ignoreNulls; - private final RexNode filter; // may be null - private final String alias; // may be null + private final @Nullable RexNode filter; // may be null + private final @Nullable String alias; // may be null private final ImmutableList operands; // may be empty, never null private final ImmutableList orderKeys; // may be empty, never null AggCallImpl(SqlAggFunction aggFunction, boolean distinct, - boolean approximate, boolean ignoreNulls, RexNode filter, - String alias, ImmutableList operands, + boolean approximate, boolean ignoreNulls, @Nullable RexNode filter, + @Nullable String alias, ImmutableList operands, ImmutableList orderKeys) { - this.aggFunction = Objects.requireNonNull(aggFunction); + this.aggFunction = requireNonNull(aggFunction); // If the aggregate function ignores DISTINCT, // make the DISTINCT flag FALSE. this.distinct = distinct @@ -3053,8 +3066,8 @@ private class AggCallImpl implements AggCallPlus { this.approximate = approximate; this.ignoreNulls = ignoreNulls; this.alias = alias; - this.operands = Objects.requireNonNull(operands); - this.orderKeys = Objects.requireNonNull(orderKeys); + this.operands = requireNonNull(operands); + this.orderKeys = requireNonNull(orderKeys); if (filter != null) { if (filter.getType().getSqlTypeName() != SqlTypeName.BOOLEAN) { throw RESOURCE.filterMustBeBoolean().ex(); @@ -3092,13 +3105,16 @@ private class AggCallImpl implements AggCallPlus { return aggFunction; } - @Override public String alias() { + @Override public @Nullable String alias() { return alias; } @Override public AggregateCall aggregateCall() { return AggregateCall.create(aggFunction, distinct, approximate, - ignoreNulls, ImmutableList.of(), -1, null, null, alias); + ignoreNulls, ImmutableList.of(), -1, + requireNonNull(null, "CALCITE-4234: collation is null"), + requireNonNull(null, "CALCITE-4234: type is null"), + alias); } @Override public AggregateCall aggregateCall(Registrar registrar, @@ -3152,14 +3168,14 @@ private class AggCallImpl implements AggCallPlus { filter, alias, operands, orderKeys); } - @Override public AggCall filter(RexNode condition) { + @Override public AggCall filter(@Nullable RexNode condition) { return Objects.equals(condition, this.filter) ? this : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls, condition, alias, operands, orderKeys); } - @Override public AggCall as(String alias) { + @Override public AggCall as(@Nullable String alias) { return Objects.equals(alias, this.alias) ? this : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls, @@ -3191,7 +3207,7 @@ private static class AggCallImpl2 implements AggCallPlus { private final AggregateCall aggregateCall; AggCallImpl2(AggregateCall aggregateCall) { - this.aggregateCall = Objects.requireNonNull(aggregateCall); + this.aggregateCall = requireNonNull(aggregateCall); } @Override public String toString() { @@ -3202,7 +3218,7 @@ private static class AggCallImpl2 implements AggCallPlus { return aggregateCall.getAggregation(); } - @Override public String alias() { + @Override public @Nullable String alias() { return aggregateCall.name; } @@ -3231,11 +3247,11 @@ private static class AggCallImpl2 implements AggCallPlus { throw new UnsupportedOperationException(); } - @Override public AggCall filter(RexNode condition) { + @Override public AggCall filter(@Nullable RexNode condition) { throw new UnsupportedOperationException(); } - @Override public AggCall as(String alias) { + @Override public AggCall as(@Nullable String alias) { throw new UnsupportedOperationException(); } @@ -3260,7 +3276,7 @@ private static class AggCallImpl2 implements AggCallPlus { private static class Registrar { final List originalExtraNodes; final List extraNodes; - final List names = new ArrayList<>(); + final List<@Nullable String> names = new ArrayList<>(); Registrar(Iterable fields) { this(fields, ImmutableList.of()); @@ -3350,9 +3366,10 @@ private Frame(RelNode rel) { return rel + ": " + fields; } - private static String deriveAlias(RelNode rel) { + private static @Nullable String deriveAlias(RelNode rel) { if (rel instanceof TableScan) { - final List names = rel.getTable().getQualifiedName(); + TableScan scan = (TableScan) rel; + final List names = scan.getTable().getQualifiedName(); if (!names.isEmpty()) { return Util.last(names); } @@ -3534,7 +3551,7 @@ default ConfigBuilder toBuilder() { public static class ConfigBuilder { private Config config; - private ConfigBuilder(@Nonnull Config config) { + private ConfigBuilder(Config config) { this.config = config; } diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java b/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java index d2d65d2f9325..57f716a8eef6 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java @@ -21,6 +21,8 @@ import org.apache.calcite.plan.RelOptSchema; import org.apache.calcite.rel.core.RelFactories; +import org.checkerframework.checker.nullness.qual.Nullable; + /** A partially-created RelBuilder. * *

    Add a cluster, and optionally a schema, @@ -35,5 +37,5 @@ */ public interface RelBuilderFactory { /** Creates a RelBuilder. */ - RelBuilder create(RelOptCluster cluster, RelOptSchema schema); + RelBuilder create(RelOptCluster cluster, @Nullable RelOptSchema schema); } diff --git a/core/src/main/java/org/apache/calcite/tools/RuleSets.java b/core/src/main/java/org/apache/calcite/tools/RuleSets.java index 73aff87ad0be..8ad896148d21 100644 --- a/core/src/main/java/org/apache/calcite/tools/RuleSets.java +++ b/core/src/main/java/org/apache/calcite/tools/RuleSets.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Iterator; /** @@ -53,7 +55,7 @@ private static class ListRuleSet implements RuleSet { return rules.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof ListRuleSet && rules.equals(((ListRuleSet) obj).rules); diff --git a/core/src/main/java/org/apache/calcite/util/BarfingInvocationHandler.java b/core/src/main/java/org/apache/calcite/util/BarfingInvocationHandler.java index 636bc5f1c4ce..a02cf186c7f3 100644 --- a/core/src/main/java/org/apache/calcite/util/BarfingInvocationHandler.java +++ b/core/src/main/java/org/apache/calcite/util/BarfingInvocationHandler.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; @@ -39,10 +41,10 @@ protected BarfingInvocationHandler() { //~ Methods ---------------------------------------------------------------- - @Override public Object invoke( + @Override public @Nullable Object invoke( Object proxy, Method method, - Object[] args) throws Throwable { + @Nullable Object[] args) throws Throwable { Class clazz = getClass(); Method matchingMethod; try { diff --git a/core/src/main/java/org/apache/calcite/util/BitSets.java b/core/src/main/java/org/apache/calcite/util/BitSets.java index c8c613bb276e..197ac8365e04 100644 --- a/core/src/main/java/org/apache/calcite/util/BitSets.java +++ b/core/src/main/java/org/apache/calcite/util/BitSets.java @@ -26,6 +26,8 @@ import java.util.SortedMap; import java.util.TreeMap; +import static java.util.Objects.requireNonNull; + /** * Utility functions for {@link BitSet}. */ @@ -325,7 +327,7 @@ private static class Closure { private SortedMap equivalence; private final NavigableMap closure = new TreeMap<>(); - @SuppressWarnings("JdkObsolete") + @SuppressWarnings({"JdkObsolete", "method.invocation.invalid"}) Closure(SortedMap equivalence) { this.equivalence = equivalence; final ImmutableIntList keys = @@ -341,7 +343,8 @@ private BitSet computeClosure(int pos) { if (o != null) { return o; } - BitSet b = equivalence.get(pos); + BitSet b = requireNonNull(equivalence.get(pos), + () -> "equivalence.get(pos) for " + pos); o = (BitSet) b.clone(); int i = b.nextSetBit(pos + 1); for (; i >= 0; i = b.nextSetBit(i + 1)) { diff --git a/core/src/main/java/org/apache/calcite/util/BitString.java b/core/src/main/java/org/apache/calcite/util/BitString.java index 1f2b80883bd9..7a7ed33e65e6 100644 --- a/core/src/main/java/org/apache/calcite/util/BitString.java +++ b/core/src/main/java/org/apache/calcite/util/BitString.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigInteger; import java.util.List; import java.util.Objects; @@ -95,7 +97,7 @@ public static BitString createFromBitString(String s) { return bits.hashCode() + bitCount; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof BitString && bits.equals(((BitString) o).bits) diff --git a/core/src/main/java/org/apache/calcite/util/BlackholeMap.java b/core/src/main/java/org/apache/calcite/util/BlackholeMap.java index cd7b9e0d3332..f22c72a2d029 100644 --- a/core/src/main/java/org/apache/calcite/util/BlackholeMap.java +++ b/core/src/main/java/org/apache/calcite/util/BlackholeMap.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Iterator; @@ -92,10 +94,12 @@ public static Set of() { private BlackholeMap() {} - @Override public V put(K key, V value) { + @SuppressWarnings("contracts.postcondition.not.satisfied") + @Override public @Nullable V put(K key, V value) { return null; } + @SuppressWarnings("override.return.invalid") @Override public Set> entrySet() { return BHSet.of(); } diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java index e7831252de83..f6dbfa96e190 100644 --- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java +++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java @@ -112,6 +112,8 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -135,6 +137,8 @@ import java.util.function.Predicate; import javax.sql.DataSource; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Built-in methods. */ @@ -638,10 +642,11 @@ public enum BuiltInMethod { MAP = builder.build(); } - BuiltInMethod(Method method, Constructor constructor, Field field) { - this.method = method; - this.constructor = constructor; - this.field = field; + BuiltInMethod(@Nullable Method method, @Nullable Constructor constructor, @Nullable Field field) { + // TODO: split enum in three different ones + this.method = castNonNull(method); + this.constructor = castNonNull(constructor); + this.field = castNonNull(field); } /** Defines a method. */ @@ -661,6 +666,6 @@ public enum BuiltInMethod { } public String getMethodName() { - return method.getName(); + return castNonNull(method).getName(); } } diff --git a/core/src/main/java/org/apache/calcite/util/CastingList.java b/core/src/main/java/org/apache/calcite/util/CastingList.java index 3e52ac9a95e9..5bdcb264d4c9 100644 --- a/core/src/main/java/org/apache/calcite/util/CastingList.java +++ b/core/src/main/java/org/apache/calcite/util/CastingList.java @@ -19,6 +19,8 @@ import java.util.AbstractList; import java.util.List; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Converts a list whose members are automatically down-cast to a given type. * @@ -48,7 +50,8 @@ protected CastingList(List list, Class clazz) { //~ Methods ---------------------------------------------------------------- @Override public E get(int index) { - return clazz.cast(list.get(index)); + Object o = list.get(index); + return clazz.cast(castNonNull(o)); } @Override public int size() { @@ -57,11 +60,12 @@ protected CastingList(List list, Class clazz) { @Override public E set(int index, E element) { final Object o = list.set(index, element); - return clazz.cast(o); + return clazz.cast(castNonNull(o)); } @Override public E remove(int index) { - return clazz.cast(list.remove(index)); + Object o = list.remove(index); + return clazz.cast(castNonNull(o)); } @Override public void add(int pos, E o) { diff --git a/core/src/main/java/org/apache/calcite/util/ChunkList.java b/core/src/main/java/org/apache/calcite/util/ChunkList.java index 7839043b78e5..7f3ef18be956 100644 --- a/core/src/main/java/org/apache/calcite/util/ChunkList.java +++ b/core/src/main/java/org/apache/calcite/util/ChunkList.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractSequentialList; import java.util.Arrays; import java.util.Collection; @@ -23,6 +25,10 @@ import java.util.ListIterator; import java.util.NoSuchElementException; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * Implementation of list similar to {@link LinkedList}, but stores elements * in chunks of 32 elements. @@ -44,8 +50,8 @@ public class ChunkList extends AbstractSequentialList { } private int size; - private Object[] first; - private Object[] last; + private E @Nullable [] first; + private E @Nullable [] last; /** * Creates an empty ChunkList. @@ -57,7 +63,8 @@ public ChunkList() { * Creates a ChunkList whose contents are a given Collection. */ public ChunkList(Collection collection) { - addAll(collection); + @SuppressWarnings({"method.invocation.invalid", "unused"}) + boolean ignore = addAll(collection); } /** @@ -83,8 +90,8 @@ boolean isValid(boolean fail) { assert !fail; return false; } - Object[] prev = null; - for (Object[] chunk = first; chunk != null; chunk = next(chunk)) { + E[] prev = null; + for (E[] chunk = first; chunk != null; chunk = next(chunk)) { if (prev(chunk) != prev) { assert !fail; return false; @@ -113,16 +120,18 @@ boolean isValid(boolean fail) { } @Override public boolean add(E element) { - Object[] chunk = last; + E[] chunk = last; int occupied; if (chunk == null) { - chunk = first = last = new Object[CHUNK_SIZE + HEADER_SIZE]; + //noinspection unchecked + chunk = first = last = (E[]) new Object[CHUNK_SIZE + HEADER_SIZE]; occupied = 0; } else { occupied = occupied(chunk); if (occupied == CHUNK_SIZE) { - chunk = new Object[CHUNK_SIZE + HEADER_SIZE]; - setNext(last, chunk); + //noinspection unchecked + chunk = (E[]) new Object[CHUNK_SIZE + HEADER_SIZE]; + setNext(requireNonNull(last, "last"), chunk); setPrev(chunk, last); occupied = 0; last = chunk; @@ -142,37 +151,42 @@ boolean isValid(boolean fail) { } } - private static Object[] prev(Object[] chunk) { - return (Object[]) chunk[0]; + private static E @Nullable [] prev(E[] chunk) { + //noinspection unchecked + return (E @Nullable []) chunk[0]; } - private static void setPrev(Object[] chunk, Object[] prev) { - chunk[0] = prev; + private static void setPrev(E[] chunk, E @Nullable [] prev) { + //noinspection unchecked + chunk[0] = (E) prev; } - private static Object[] next(Object[] chunk) { - return (Object[]) chunk[1]; + private static E @Nullable [] next(E[] chunk) { + //noinspection unchecked + return (E @Nullable []) chunk[1]; } - private static void setNext(Object[] chunk, Object[] next) { + private static void setNext(E[] chunk, E @Nullable [] next) { assert chunk != next; - chunk[1] = next; + //noinspection unchecked + chunk[1] = (E) next; } - private static int occupied(Object[] chunk) { - return (Integer) chunk[2]; + private static int occupied(E[] chunk) { + return (Integer) requireNonNull(chunk[2], "chunk[2] (number of occupied entries)"); } - private static void setOccupied(Object[] chunk, int size) { - chunk[2] = INTEGERS[size]; + @SuppressWarnings("unchecked") + private static void setOccupied(E[] chunk, int size) { + chunk[2] = (E) INTEGERS[size]; } - private static Object element(Object[] chunk, int index) { + private static E element(E[] chunk, int index) { return chunk[index]; } - private static void setElement(Object[] chunk, int index, Object element) { - chunk[index] = element; + private static void setElement(E[] chunk, int index, @Nullable E element) { + chunk[index] = castNonNull(element); } private ChunkListIterator locate(int index) { @@ -184,10 +198,10 @@ private ChunkListIterator locate(int index) { return new ChunkListIterator(null, 0, 0, -1, 0); } int n = 0; - for (Object[] chunk = first;;) { + for (E[] chunk = first;;) { final int occupied = occupied(chunk); final int nextN = n + occupied; - final Object[] next = next(chunk); + final E[] next = next(chunk); if (nextN >= index || next == null) { return new ChunkListIterator(chunk, n, index, -1, n + occupied); } @@ -198,7 +212,7 @@ private ChunkListIterator locate(int index) { /** Iterator over a {@link ChunkList}. */ private class ChunkListIterator implements ListIterator { - private Object[] chunk; + private E @Nullable [] chunk; /** Offset in the list of the first element of this chunk. */ private int start; /** Offset within current chunk of the next element to return. */ @@ -209,7 +223,7 @@ private class ChunkListIterator implements ListIterator { /** Offset of the first unoccupied location in the current chunk. */ private int end; - ChunkListIterator(Object[] chunk, int start, int cursor, int lastRet, + ChunkListIterator(E @Nullable [] chunk, int start, int cursor, int lastRet, int end) { this.chunk = chunk; this.start = start; @@ -218,6 +232,10 @@ private class ChunkListIterator implements ListIterator { this.end = end; } + private E[] currentChunk() { + return castNonNull(chunk); + } + @Override public boolean hasNext() { return cursor < size; } @@ -240,7 +258,7 @@ private class ChunkListIterator implements ListIterator { } } @SuppressWarnings("unchecked") - final E element = (E) element(chunk, + final E element = (E) element(currentChunk(), HEADER_SIZE + (lastRet = cursor++) - start); return element; } @@ -262,7 +280,7 @@ private class ChunkListIterator implements ListIterator { assert cursor == end - 1; } //noinspection unchecked - return (E) element(chunk, cursor - start); + return (E) element(currentChunk(), cursor - start); } @Override public int nextIndex() { @@ -281,8 +299,8 @@ private class ChunkListIterator implements ListIterator { --cursor; if (end == start + 1) { // Chunk is now empty. - final Object[] prev = prev(chunk); - final Object[] next = ChunkList.next(chunk); + final E[] prev = prev(currentChunk()); + final E[] next = ChunkList.next(currentChunk()); if (next == null) { last = prev; if (prev == null) { @@ -296,13 +314,13 @@ private class ChunkListIterator implements ListIterator { if (prev == null) { chunk = first = next; setPrev(next, null); - end = occupied(chunk); + end = occupied(requireNonNull(chunk, "chunk")); } else { setNext(prev, next); setPrev(next, prev); chunk = prev; end = start; - start -= occupied(chunk); + start -= occupied(requireNonNull(chunk, "chunk")); } } lastRet = -1; @@ -313,24 +331,24 @@ private class ChunkListIterator implements ListIterator { if (r < start) { // Element we wish to eliminate is the last element in the previous // block. - Object[] c = chunk; + E[] c = chunk; if (c == null) { c = last; } - int o = occupied(c); + int o = occupied(castNonNull(c)); if (o == 1) { // Block is now empty; remove it - final Object[] prev = prev(c); + final E[] prev = prev(c); if (prev == null) { if (chunk == null) { first = last = null; } else { first = chunk; - setPrev(chunk, null); + setPrev(requireNonNull(chunk, "chunk"), null); } } else { - setNext(prev, chunk); - setPrev(chunk, prev); + setNext(requireNonNull(prev, "prev"), chunk); + setPrev(requireNonNull(chunk, "chunk"), prev); } } else { --o; @@ -339,12 +357,12 @@ private class ChunkListIterator implements ListIterator { } } else { // Move existing contents down one. - System.arraycopy(chunk, HEADER_SIZE + r - start + 1, - chunk, HEADER_SIZE + r - start, end - r - 1); + System.arraycopy(currentChunk(), HEADER_SIZE + r - start + 1, + currentChunk(), HEADER_SIZE + r - start, end - r - 1); --end; final int o = end - start; - setElement(chunk, HEADER_SIZE + o, null); // allow gc - setOccupied(chunk, o); + setElement(currentChunk(), HEADER_SIZE + o, null); // allow gc + setOccupied(currentChunk(), o); } } @@ -352,23 +370,24 @@ private class ChunkListIterator implements ListIterator { if (lastRet < 0) { throw new IllegalStateException(); } - Object[] c = chunk; + E[] c = currentChunk(); int p = lastRet; int s = start; if (p < start) { // The element is at the end of the previous chunk c = prev(c); - s -= occupied(c); + s -= occupied(castNonNull(c)); } setElement(c, HEADER_SIZE + p - s, e); } @Override public void add(E e) { if (chunk == null) { - Object[] newChunk = new Object[CHUNK_SIZE + HEADER_SIZE]; + //noinspection unchecked + E[] newChunk = (E[]) new Object[CHUNK_SIZE + HEADER_SIZE]; if (first != null) { setNext(newChunk, first); - setPrev(first, newChunk); + setPrev(requireNonNull(first, "first"), newChunk); } first = newChunk; if (last == null) { @@ -379,10 +398,11 @@ private class ChunkListIterator implements ListIterator { } else if (end == start + CHUNK_SIZE) { // FIXME We create a new chunk, but the next chunk might be // less than half full. We should consider using it. - Object[] newChunk = new Object[CHUNK_SIZE + HEADER_SIZE]; - final Object[] next = ChunkList.next(chunk); + //noinspection unchecked + E[] newChunk = (E[]) new Object[CHUNK_SIZE + HEADER_SIZE]; + final E[] next = ChunkList.next(chunk); setPrev(newChunk, chunk); - setNext(chunk, newChunk); + setNext(requireNonNull(chunk, "chunk"), newChunk); if (next == null) { last = newChunk; @@ -391,9 +411,9 @@ private class ChunkListIterator implements ListIterator { setNext(newChunk, next); } - setOccupied(chunk, CHUNK_SIZE / 2); + setOccupied(requireNonNull(chunk, "chunk"), CHUNK_SIZE / 2); setOccupied(newChunk, CHUNK_SIZE / 2); - System.arraycopy(chunk, HEADER_SIZE + CHUNK_SIZE / 2, + System.arraycopy(requireNonNull(chunk, "chunk"), HEADER_SIZE + CHUNK_SIZE / 2, newChunk, HEADER_SIZE, CHUNK_SIZE / 2); Arrays.fill(chunk, HEADER_SIZE + CHUNK_SIZE / 2, HEADER_SIZE + CHUNK_SIZE, null); diff --git a/core/src/main/java/org/apache/calcite/util/Compatible.java b/core/src/main/java/org/apache/calcite/util/Compatible.java index 97e5f16c0bc4..924621d2f9a4 100644 --- a/core/src/main/java/org/apache/calcite/util/Compatible.java +++ b/core/src/main/java/org/apache/calcite/util/Compatible.java @@ -22,6 +22,8 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import static java.util.Objects.requireNonNull; + /** Compatibility layer. * *

    Allows to use advanced functionality if the latest JDK or Guava version @@ -45,7 +47,7 @@ Compatible create() { // Use MethodHandles.privateLookupIn if it is available (JDK 9 // and above) @SuppressWarnings("rawtypes") - final Class clazz = (Class) args[0]; + final Class clazz = (Class) requireNonNull(args[0], "args[0]"); try { final Method privateLookupMethod = MethodHandles.class.getMethod("privateLookupIn", diff --git a/core/src/main/java/org/apache/calcite/util/CompositeMap.java b/core/src/main/java/org/apache/calcite/util/CompositeMap.java index 6f0d5c36d236..b5f7acd0d7c9 100644 --- a/core/src/main/java/org/apache/calcite/util/CompositeMap.java +++ b/core/src/main/java/org/apache/calcite/util/CompositeMap.java @@ -19,6 +19,9 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.KeyFor; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.LinkedHashSet; import java.util.Map; @@ -68,7 +71,8 @@ private static ImmutableList list(E e, E[] es) { return true; } - @Override public boolean containsKey(Object key) { + @SuppressWarnings("contracts.conditional.postcondition.not.satisfied") + @Override public boolean containsKey(@Nullable Object key) { for (Map map : maps) { if (map.containsKey(key)) { return true; @@ -77,7 +81,7 @@ private static ImmutableList list(E e, E[] es) { return false; } - @Override public boolean containsValue(Object value) { + @Override public boolean containsValue(@Nullable Object value) { for (Map map : maps) { if (map.containsValue(value)) { return true; @@ -86,7 +90,7 @@ private static ImmutableList list(E e, E[] es) { return false; } - @Override public V get(Object key) { + @Override public @Nullable V get(@Nullable Object key) { for (Map map : maps) { //noinspection SuspiciousMethodCalls if (map.containsKey(key)) { @@ -101,7 +105,7 @@ private static ImmutableList list(E e, E[] es) { throw new UnsupportedOperationException(); } - @Override public V remove(Object key) { + @Override public V remove(@Nullable Object key) { // we are an unmodifiable view on the maps throw new UnsupportedOperationException(); } @@ -116,7 +120,8 @@ private static ImmutableList list(E e, E[] es) { throw new UnsupportedOperationException(); } - @Override public Set keySet() { + @SuppressWarnings("return.type.incompatible") + @Override public Set<@KeyFor("this") K> keySet() { final Set keys = new LinkedHashSet<>(); for (Map map : maps) { keys.addAll(map.keySet()); @@ -141,7 +146,8 @@ private Map combinedMap() { return combinedMap().values(); } - @Override public Set> entrySet() { + @SuppressWarnings("return.type.incompatible") + @Override public Set> entrySet() { return combinedMap().entrySet(); } } diff --git a/core/src/main/java/org/apache/calcite/util/ConversionUtil.java b/core/src/main/java/org/apache/calcite/util/ConversionUtil.java index 6646e237a9ac..72c418db3e87 100644 --- a/core/src/main/java/org/apache/calcite/util/ConversionUtil.java +++ b/core/src/main/java/org/apache/calcite/util/ConversionUtil.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.nio.ByteOrder; import java.text.NumberFormat; import java.util.Locale; @@ -118,7 +120,7 @@ public static String toStringFromApprox(double d, boolean isFloat) { /** * Converts a string into a BOOLEAN. */ - public static Boolean toBoolean(String str) { + public static @Nullable Boolean toBoolean(@Nullable String str) { if (str == null) { return null; } diff --git a/core/src/main/java/org/apache/calcite/util/DateString.java b/core/src/main/java/org/apache/calcite/util/DateString.java index 3467c415f083..33e3675353d5 100644 --- a/core/src/main/java/org/apache/calcite/util/DateString.java +++ b/core/src/main/java/org/apache/calcite/util/DateString.java @@ -20,9 +20,10 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Calendar; import java.util.regex.Pattern; -import javax.annotation.Nonnull; /** * Date literal. @@ -41,6 +42,7 @@ private DateString(String v, @SuppressWarnings("unused") boolean ignore) { } /** Creates a DateString. */ + @SuppressWarnings("method.invocation.invalid") public DateString(String v) { this(v, false); Preconditions.checkArgument(PATTERN.matcher(v).matches(), @@ -75,7 +77,7 @@ private static String ymd(int year, int month, int day) { return v; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { // The value is in canonical form. return o == this || o instanceof DateString @@ -86,7 +88,7 @@ private static String ymd(int year, int month, int day) { return v.hashCode(); } - @Override public int compareTo(@Nonnull DateString o) { + @Override public int compareTo(DateString o) { return v.compareTo(o.v); } diff --git a/core/src/main/java/org/apache/calcite/util/DelegatingInvocationHandler.java b/core/src/main/java/org/apache/calcite/util/DelegatingInvocationHandler.java index b957698ef2a6..a2375d504b0b 100644 --- a/core/src/main/java/org/apache/calcite/util/DelegatingInvocationHandler.java +++ b/core/src/main/java/org/apache/calcite/util/DelegatingInvocationHandler.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -51,10 +53,10 @@ public abstract class DelegatingInvocationHandler implements InvocationHandler { //~ Methods ---------------------------------------------------------------- - @Override public Object invoke( + @Override public @Nullable Object invoke( Object proxy, Method method, - Object[] args) throws Throwable { + @Nullable Object[] args) throws Throwable { Class clazz = getClass(); Method matchingMethod; try { @@ -76,7 +78,7 @@ public abstract class DelegatingInvocationHandler implements InvocationHandler { args); } } catch (InvocationTargetException e) { - throw e.getTargetException(); + throw Util.first(e.getCause(), e); } } diff --git a/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java b/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java index 8a7f73319c19..2b43f94aacfe 100644 --- a/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java +++ b/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java @@ -71,14 +71,14 @@ public E add(E e) { public E equiv(E e, E f) { final E eParent = add(e); if (!eParent.equals(e)) { - assert parents.get(eParent).equals(eParent); + assert Objects.equals(parents.get(eParent), eParent); final E root = equiv(eParent, f); parents.put(e, root); return root; } final E fParent = add(f); if (!fParent.equals(f)) { - assert parents.get(fParent).equals(fParent); + assert Objects.equals(parents.get(fParent), fParent); final E root = equiv(e, fParent); parents.put(f, root); return root; diff --git a/core/src/main/java/org/apache/calcite/util/Filterator.java b/core/src/main/java/org/apache/calcite/util/Filterator.java index c2a4fdf4af87..c605a0b851d1 100644 --- a/core/src/main/java/org/apache/calcite/util/Filterator.java +++ b/core/src/main/java/org/apache/calcite/util/Filterator.java @@ -16,9 +16,13 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Iterator; import java.util.NoSuchElementException; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Filtered iterator class: an iterator that includes only elements that are * instanceof a specified class. @@ -30,12 +34,12 @@ * * @param Element type */ -public class Filterator implements Iterator { +public class Filterator implements Iterator { //~ Instance fields -------------------------------------------------------- Class includeFilter; - Iterator iterator; - E lookAhead; + Iterator iterator; + @Nullable E lookAhead; boolean ready; //~ Constructors ----------------------------------------------------------- @@ -68,7 +72,7 @@ public Filterator(Iterator iterator, Class includeFilter) { if (ready) { E o = lookAhead; ready = false; - return o; + return castNonNull(o); } while (iterator.hasNext()) { diff --git a/core/src/main/java/org/apache/calcite/util/Glossary.java b/core/src/main/java/org/apache/calcite/util/Glossary.java index d3c84ca62186..91f7c6c3aa0f 100644 --- a/core/src/main/java/org/apache/calcite/util/Glossary.java +++ b/core/src/main/java/org/apache/calcite/util/Glossary.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A collection of terms. * @@ -320,21 +322,21 @@ public interface Glossary { *

    */ // CHECKSTYLE: ON - Glossary PATTERN = null; + @Nullable Glossary PATTERN = null; /** * Provide an interface for creating families of related or dependent * objects without specifying their concrete classes. (See GoF.) */ - Glossary ABSTRACT_FACTORY_PATTERN = null; + @Nullable Glossary ABSTRACT_FACTORY_PATTERN = null; /** * Separate the construction of a complex object from its representation so * that the same construction process can create different representations. * (See GoF.) */ - Glossary BUILDER_PATTERN = null; + @Nullable Glossary BUILDER_PATTERN = null; /** * Define an interface for creating an object, but let subclasses decide @@ -342,14 +344,14 @@ public interface Glossary { * subclasses. (See * GoF.) */ - Glossary FACTORY_METHOD_PATTERN = null; + @Nullable Glossary FACTORY_METHOD_PATTERN = null; /** * Specify the kinds of objects to create using a prototypical instance, and * create new objects by copying this prototype. (See GoF.) */ - Glossary PROTOTYPE_PATTERN = null; + @Nullable Glossary PROTOTYPE_PATTERN = null; /** * Ensure a class only has one instance, and provide a global point of @@ -361,7 +363,7 @@ public interface Glossary { * double-checked locking pattern, is fatally flawed in Java. Don't use * it!

    */ - Glossary SINGLETON_PATTERN = null; + @Nullable Glossary SINGLETON_PATTERN = null; /** * Convert the interface of a class into another interface clients expect. @@ -369,14 +371,14 @@ public interface Glossary { * incompatible interfaces. (See GoF.) */ - Glossary ADAPTER_PATTERN = null; + @Nullable Glossary ADAPTER_PATTERN = null; /** * Decouple an abstraction from its implementation so that the two can very * independently. (See * GoF.) */ - Glossary BRIDGE_PATTERN = null; + @Nullable Glossary BRIDGE_PATTERN = null; /** * Compose objects into tree structures to represent part-whole hierarchies. @@ -384,33 +386,33 @@ public interface Glossary { * uniformly. (See * GoF.) */ - Glossary COMPOSITE_PATTERN = null; + @Nullable Glossary COMPOSITE_PATTERN = null; /** * Attach additional responsibilities to an object dynamically. Provides a * flexible alternative to subclassing for extending functionality. (See GoF.) */ - Glossary DECORATOR_PATTERN = null; + @Nullable Glossary DECORATOR_PATTERN = null; /** * Provide a unified interface to a set of interfaces in a subsystem. * Defines a higher-level interface that makes the subsystem easier to use. * (See GoF.) */ - Glossary FACADE_PATTERN = null; + @Nullable Glossary FACADE_PATTERN = null; /** * Use sharing to support large numbers of fine-grained objects efficiently. * (See GoF.) */ - Glossary FLYWEIGHT_PATTERN = null; + @Nullable Glossary FLYWEIGHT_PATTERN = null; /** * Provide a surrogate or placeholder for another object to control access * to it. (See GoF.) */ - Glossary PROXY_PATTERN = null; + @Nullable Glossary PROXY_PATTERN = null; /** * Avoid coupling the sender of a request to its receiver by giving more @@ -419,7 +421,7 @@ public interface Glossary { * (See * GoF.) */ - Glossary CHAIN_OF_RESPONSIBILITY_PATTERN = null; + @Nullable Glossary CHAIN_OF_RESPONSIBILITY_PATTERN = null; /** * Encapsulate a request as an object, thereby letting you parameterize @@ -427,7 +429,7 @@ public interface Glossary { * undoable operations. (See GoF.) */ - Glossary COMMAND_PATTERN = null; + @Nullable Glossary COMMAND_PATTERN = null; /** * Given a language, define a representation for its grammar along with an @@ -435,14 +437,14 @@ public interface Glossary { * language. (See * GoF.) */ - Glossary INTERPRETER_PATTERN = null; + @Nullable Glossary INTERPRETER_PATTERN = null; /** * Provide a way to access the elements of an aggregate object sequentially * without exposing its underlying representation. (See GoF.) */ - Glossary ITERATOR_PATTERN = null; + @Nullable Glossary ITERATOR_PATTERN = null; /** * Define an object that encapsulates how a set of objects interact. @@ -450,35 +452,35 @@ public interface Glossary { * explicitly, and it lets you vary their interaction independently. (See GoF.) */ - Glossary MEDIATOR_PATTERN = null; + @Nullable Glossary MEDIATOR_PATTERN = null; /** * Without violating encapsulation, capture and externalize an objects's * internal state so that the object can be restored to this state later. * (See GoF.) */ - Glossary MEMENTO_PATTERN = null; + @Nullable Glossary MEMENTO_PATTERN = null; /** * Define a one-to-many dependency between objects so that when one object * changes state, all its dependents are notified and updated automatically. * (See GoF.) */ - Glossary OBSERVER_PATTERN = null; + @Nullable Glossary OBSERVER_PATTERN = null; /** * Allow an object to alter its behavior when its internal state changes. * The object will appear to change its class. (See GoF.) */ - Glossary STATE_PATTERN = null; + @Nullable Glossary STATE_PATTERN = null; /** * Define a family of algorithms, encapsulate each one, and make them * interchangeable. Lets the algorithm vary independently from clients that * use it. (See GoF.) */ - Glossary STRATEGY_PATTERN = null; + @Nullable Glossary STRATEGY_PATTERN = null; /** * Define the skeleton of an algorithm in an operation, deferring some steps @@ -486,7 +488,7 @@ public interface Glossary { * without changing the algorithm's structure. (See GoF.) */ - Glossary TEMPLATE_METHOD_PATTERN = null; + @Nullable Glossary TEMPLATE_METHOD_PATTERN = null; /** * Represent an operation to be performed on the elements of an object @@ -494,7 +496,7 @@ public interface Glossary { * of the elements on which it operates. (See GoF.) */ - Glossary VISITOR_PATTERN = null; + @Nullable Glossary VISITOR_PATTERN = null; /** * The official SQL-92 standard (ISO/IEC 9075:1992). To reference this @@ -521,7 +523,7 @@ public interface Glossary { *

    Note that this tag is a block tag (like @see) and cannot be used * inline. */ - Glossary SQL92 = null; + @Nullable Glossary SQL92 = null; /** * The official SQL:1999 standard (ISO/IEC 9075:1999), which is broken up @@ -550,7 +552,7 @@ public interface Glossary { *

    Note that this tag is a block tag (like @see) and cannot be used * inline. */ - Glossary SQL99 = null; + @Nullable Glossary SQL99 = null; /** * The official SQL:2003 standard (ISO/IEC 9075:2003), which is broken up @@ -579,5 +581,5 @@ public interface Glossary { *

    Note that this tag is a block tag (like @see) and cannot be used * inline. */ - Glossary SQL2003 = null; + @Nullable Glossary SQL2003 = null; } diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableBeans.java b/core/src/main/java/org/apache/calcite/util/ImmutableBeans.java index 24eff35ac941..ce915951e7fa 100644 --- a/core/src/main/java/org/apache/calcite/util/ImmutableBeans.java +++ b/core/src/main/java/org/apache/calcite/util/ImmutableBeans.java @@ -26,6 +26,8 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.util.concurrent.UncheckedExecutionException; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -45,7 +47,8 @@ import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ExecutionException; -import javax.annotation.Nonnull; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; /** Utilities for creating immutable beans. */ public class ImmutableBeans { @@ -59,7 +62,7 @@ public class ImmutableBeans { .weakKeys() .softValues() .build(new CacheLoader() { - @Override public Def load(@Nonnull Class key) { + @Override public Def load(Class key) { //noinspection unchecked return makeDef(key); } @@ -68,19 +71,19 @@ public class ImmutableBeans { private ImmutableBeans() {} /** Creates an immutable bean that implements a given interface. */ - public static T create(Class beanClass) { + public static T create(Class beanClass) { return create_(beanClass, ImmutableMap.of()); } /** Creates a bean of a given class whose contents are the same as this bean. * *

    You typically use this to downcast a bean to a sub-class. */ - public static T copy(Class beanClass, @Nonnull Object o) { + public static T copy(Class beanClass, Object o) { final BeanImpl bean = (BeanImpl) Proxy.getInvocationHandler(o); return create_(beanClass, bean.map); } - private static T create_(Class beanClass, + private static T create_(Class beanClass, ImmutableMap valueMap) { if (!beanClass.isInterface()) { throw new IllegalArgumentException("must be interface"); @@ -98,7 +101,7 @@ private static T create_(Class beanClass, } } - private static Def makeDef(Class beanClass) { + private static Def makeDef(Class beanClass) { final ImmutableSortedMap.Builder propertyNameBuilder = ImmutableSortedMap.naturalOrder(); final ImmutableMap.Builder> handlers = @@ -115,7 +118,8 @@ private static Def makeDef(Class beanClass) { if (property == null) { continue; } - final boolean hasNonnull = hasAnnotation(method, "javax.annotation.Nonnull"); + final boolean hasNonnull = + hasAnnotation(method, "org.checkerframework.checker.nullness.qual.NonNull"); final Mode mode; final Object defaultValue = getDefault(method); final String methodName = method.getName(); @@ -336,7 +340,7 @@ private static Object value(boolean copy, Object o) { /** Looks for an annotation by class name. * Useful if you don't want to depend on the class - * (e.g. "javax.annotation.Nonnull") at compile time. */ + * (e.g. "org.checkerframework.checker.nullness.qual.NonNull") at compile time. */ private static boolean hasAnnotation(Method method, String className) { for (Annotation annotation : method.getDeclaredAnnotations()) { if (annotation.annotationType().getName().equals(className)) { @@ -346,7 +350,7 @@ private static boolean hasAnnotation(Method method, String className) { return false; } - private static Object getDefault(Method method) { + private static @Nullable Object getDefault(Method method) { Object defaultValue = null; final IntDefault intDefault = method.getAnnotation(IntDefault.class); if (intDefault != null) { @@ -370,7 +374,7 @@ private static Object getDefault(Method method) { return defaultValue; } - private static Object convertDefault(Object defaultValue, String propertyName, + private static @Nullable Object convertDefault(@Nullable Object defaultValue, String propertyName, Class propertyType) { if (propertyType.equals(SqlConformance.class)) { // Workaround for SqlConformance because it is actually not a Enum. @@ -379,7 +383,8 @@ private static Object convertDefault(Object defaultValue, String propertyName, if (defaultValue == null || !propertyType.isEnum()) { return defaultValue; } - for (Object enumConstant : propertyType.getEnumConstants()) { + // checkerframework does not infer "isEnum" here, so castNonNull + for (Object enumConstant : castNonNull(propertyType.getEnumConstants())) { if (((Enum) enumConstant).name().equals(defaultValue)) { return enumConstant; } @@ -406,8 +411,8 @@ private enum Mode { /** Handler for a particular method call; called with "this" and arguments. * * @param Bean type */ - private interface Handler { - Object apply(BeanImpl bean, Object[] args); + private interface Handler { + @Nullable Object apply(BeanImpl bean, @Nullable Object[] args); } /** Property of a bean. Apply this annotation to the "get" method. */ @@ -467,7 +472,7 @@ private interface Handler { * so that it can retrieve calls from a reflective proxy. * * @param Bean type */ - private static class BeanImpl implements InvocationHandler { + private static class BeanImpl implements InvocationHandler { private final Def def; private final ImmutableMap map; @@ -476,7 +481,7 @@ private static class BeanImpl implements InvocationHandler { this.map = Objects.requireNonNull(map); } - @Override public Object invoke(Object proxy, Method method, Object[] args) { + @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) { final Handler handler = def.handlers.get(method); if (handler == null) { throw new IllegalArgumentException("no handler for method " + method); @@ -501,7 +506,7 @@ T asBean() { /** Definition of a bean. Consists of its class and handlers. * * @param Class of bean */ - private static class Def { + private static class Def { private final Class beanClass; private final ImmutableMap> handlers; diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java b/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java index 836f4cfc0fcb..a896263e4c80 100644 --- a/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java +++ b/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java @@ -23,6 +23,11 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Ordering; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; +import org.checkerframework.dataflow.qual.Pure; + import java.io.Serializable; import java.nio.LongBuffer; import java.util.AbstractList; @@ -39,7 +44,10 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.stream.Collector; -import javax.annotation.Nonnull; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; /** * An immutable list of bits. @@ -253,6 +261,7 @@ public static ImmutableBitSet range(int toIndex) { /** * Given a bit index, return word index containing it. */ + @Pure private static int wordIndex(int bitIndex) { return bitIndex >> ADDRESS_BITS_PER_WORD; } @@ -453,7 +462,7 @@ public int size() { * {@code false} otherwise * @see #size() */ - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -470,7 +479,7 @@ public int size() { *

    Bit sets {@code (), (0), (0, 1), (0, 1, 3), (1), (2, 3)} are in sorted * order.

    */ - @Override public int compareTo(@Nonnull ImmutableBitSet o) { + @Override public int compareTo(ImmutableBitSet o) { int i = 0; for (;;) { int n0 = nextSetBit(i); @@ -626,7 +635,7 @@ public List asList() { return cardinality(); } - @Nonnull @Override public Iterator iterator() { + @Override public Iterator iterator() { return ImmutableBitSet.this.iterator(); } }; @@ -638,7 +647,7 @@ public List asList() { * iterator is efficient. */ public Set asSet() { return new AbstractSet() { - @Override @Nonnull public Iterator iterator() { + @Override public Iterator iterator() { return ImmutableBitSet.this.iterator(); } @@ -646,8 +655,8 @@ public Set asSet() { return cardinality(); } - @Override public boolean contains(Object o) { - return ImmutableBitSet.this.get((Integer) o); + @Override public boolean contains(@Nullable Object o) { + return ImmutableBitSet.this.get((Integer) requireNonNull(o, "o")); } }; } @@ -885,7 +894,11 @@ public BitSet toBitSet() { public ImmutableBitSet permute(Map map) { final Builder builder = builder(); for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) { - builder.set(map.get(i)); + Integer value = map.get(i); + if (value == null) { + throw new NullPointerException("Index " + i + " is not mapped in " + map); + } + builder.set(value); } return builder.build(); } @@ -933,7 +946,7 @@ public static boolean allContain(Collection bitSets, int bit) { */ @SuppressWarnings("JdkObsolete") private static class Closure { - private SortedMap equivalence; + private final SortedMap equivalence; private final SortedMap closure = new TreeMap<>(); @@ -946,12 +959,16 @@ private static class Closure { } } - private ImmutableBitSet computeClosure(int pos) { + @RequiresNonNull("equivalence") + private ImmutableBitSet computeClosure( + @UnderInitialization Closure this, + int pos + ) { ImmutableBitSet o = closure.get(pos); if (o != null) { return o; } - final ImmutableBitSet b = equivalence.get(pos); + final ImmutableBitSet b = castNonNull(equivalence.get(pos)); o = b; int i = b.nextSetBit(pos + 1); for (; i >= 0; i = b.nextSetBit(i + 1)) { @@ -968,7 +985,7 @@ private ImmutableBitSet computeClosure(int pos) { /** Builder. */ public static class Builder { - private long[] words; + private long @Nullable [] words; private Builder(long[] words) { this.words = words; @@ -1049,6 +1066,9 @@ public boolean get(int bitIndex) { } private void trim(int wordCount) { + if (words == null) { + throw new IllegalArgumentException("can only use builder once"); + } while (wordCount > 0 && words[wordCount - 1] == 0L) { --wordCount; } @@ -1063,6 +1083,9 @@ private void trim(int wordCount) { } public Builder clear(int bit) { + if (words == null) { + throw new IllegalArgumentException("can only use builder once"); + } int wordIndex = wordIndex(bit); if (wordIndex < words.length) { words[wordIndex] &= ~(1L << bit); @@ -1090,18 +1113,25 @@ public int cardinality() { /** Merges another builder. Does not modify the other builder. */ public Builder combine(Builder builder) { - if (words.length < builder.words.length) { + if (words == null) { + throw new IllegalArgumentException("can only use builder once"); + } + long[] otherWords = builder.words; + if (otherWords == null) { + throw new IllegalArgumentException("Given builder is empty"); + } + if (this.words.length < otherWords.length) { // Right has more bits. Copy the right and OR in the words of the // previous left. - final long[] newWords = builder.words.clone(); - for (int i = 0; i < words.length; i++) { - newWords[i] |= words[i]; + final long[] newWords = otherWords.clone(); + for (int i = 0; i < this.words.length; i++) { + newWords[i] |= this.words[i]; } - words = newWords; + this.words = newWords; } else { // Left has same or more bits. OR in the words of the right. - for (int i = 0; i < builder.words.length; i++) { - words[i] |= builder.words[i]; + for (int i = 0; i < otherWords.length; i++) { + this.words[i] |= otherWords[i]; } } return this; @@ -1143,10 +1173,14 @@ public Builder removeAll(ImmutableBitSet bitSet) { /** Sets a range of bits, from {@code from} to {@code to} - 1. */ public Builder set(int fromIndex, int toIndex) { if (fromIndex > toIndex) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("fromIndex(" + fromIndex + ")" + + " > toIndex(" + toIndex + ")"); } if (toIndex < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("toIndex(" + toIndex + ") < 0"); + } + if (words == null) { + throw new IllegalArgumentException("can only use builder once"); } if (fromIndex < toIndex) { // Increase capacity if necessary @@ -1174,10 +1208,16 @@ public Builder set(int fromIndex, int toIndex) { } public boolean isEmpty() { + if (words == null) { + throw new IllegalArgumentException("can only use builder once"); + } return words.length == 0; } public void intersect(ImmutableBitSet that) { + if (words == null) { + throw new IllegalArgumentException("can only use builder once"); + } int x = Math.min(words.length, that.words.length); for (int i = 0; i < x; i++) { words[i] &= that.words[i]; diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java b/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java index 81e4dac421ce..7dc305b5f7ed 100644 --- a/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java +++ b/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java @@ -25,6 +25,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.UnmodifiableListIterator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; @@ -35,6 +37,10 @@ import java.util.ListIterator; import java.util.NoSuchElementException; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + /** * An immutable list of {@link Integer} values backed by an array of * {@code int}s. @@ -112,12 +118,13 @@ private static ImmutableIntList copyFromCollection( return Arrays.hashCode(ints); } - @Override public boolean equals(Object obj) { - return this == obj - || obj instanceof ImmutableIntList + @SuppressWarnings("contracts.conditional.postcondition.not.satisfied") + @Override public boolean equals(@Nullable Object obj) { + return ((this == obj) + || (obj instanceof ImmutableIntList)) ? Arrays.equals(ints, ((ImmutableIntList) obj).ints) - : obj instanceof List - && obj.equals(this); + : ((obj instanceof List) + && obj.equals(this)); } @Override public String toString() { @@ -140,14 +147,14 @@ private static ImmutableIntList copyFromCollection( return objects; } - @Override public T[] toArray(T[] a) { + @Override public @Nullable T[] toArray(T @Nullable [] a) { final int size = ints.length; - if (a.length < size) { + if (castNonNull(a).length < size) { // Make a new array of a's runtime type, but my contents: a = a.getClass() == Object[].class ? (T[]) new Object[size] : (T[]) Array.newInstance( - a.getClass().getComponentType(), size); + requireNonNull(a.getClass().getComponentType()), size); } if ((Class) a.getClass() == Integer[].class) { final Integer[] integers = (Integer[]) a; @@ -158,7 +165,7 @@ private static ImmutableIntList copyFromCollection( System.arraycopy(toArray(), 0, a, 0, size); } if (a.length > size) { - a[size] = null; + a[size] = castNonNull(null); } return a; } @@ -201,7 +208,7 @@ public int getInt(int index) { }; } - @Override public int indexOf(Object o) { + @Override public int indexOf(@Nullable Object o) { if (o instanceof Integer) { return indexOf((int) (Integer) o); } @@ -217,7 +224,7 @@ public int indexOf(int seek) { return -1; } - @Override public int lastIndexOf(Object o) { + @Override public int lastIndexOf(@Nullable Object o) { if (o instanceof Integer) { return lastIndexOf((int) (Integer) o); } @@ -293,9 +300,9 @@ private static class EmptyImmutableIntList extends ImmutableIntList { return EMPTY_ARRAY; } - @Override public T[] toArray(T[] a) { - if (a.length > 0) { - a[0] = null; + @Override public @Nullable T[] toArray(T @Nullable [] a) { + if (castNonNull(a).length > 0) { + a[0] = castNonNull(null); } return a; } diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableNullableList.java b/core/src/main/java/org/apache/calcite/util/ImmutableNullableList.java index 8a6b2ff9988e..45fa17795bb3 100644 --- a/core/src/main/java/org/apache/calcite/util/ImmutableNullableList.java +++ b/core/src/main/java/org/apache/calcite/util/ImmutableNullableList.java @@ -170,7 +170,8 @@ public static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { /** Creates an immutable list of 8 or more elements. */ public static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E... others) { - Object[] array = new Object[8 + others.length]; + @SuppressWarnings("unchecked") + E[] array = (E[]) new Object[8 + others.length]; array[0] = e1; array[1] = e2; array[2] = e3; @@ -180,8 +181,7 @@ public static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, array[6] = e7; array[7] = e8; System.arraycopy(others, 0, array, 8, others.length); - //noinspection unchecked - return new ImmutableNullableList<>((E[]) array); + return new ImmutableNullableList<>(array); } @Override public E get(int index) { diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableNullableMap.java b/core/src/main/java/org/apache/calcite/util/ImmutableNullableMap.java index 5bca457ec215..94f731e7ba25 100644 --- a/core/src/main/java/org/apache/calcite/util/ImmutableNullableMap.java +++ b/core/src/main/java/org/apache/calcite/util/ImmutableNullableMap.java @@ -21,6 +21,7 @@ import java.util.AbstractMap; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; @@ -60,7 +61,12 @@ public static Map copyOf(Map map) { if (map instanceof SortedMap) { final SortedMap sortedMap = (SortedMap) map; try { - return ImmutableSortedMap.copyOf(sortedMap, sortedMap.comparator()); + Comparator comparator = sortedMap.comparator(); + if (comparator == null) { + return ImmutableSortedMap.copyOf(sortedMap); + } else { + return ImmutableSortedMap.copyOf(sortedMap, comparator); + } } catch (NullPointerException e) { // Make an effectively immutable map by creating a mutable copy // and wrapping it to prevent modification. Unfortunately, if we see @@ -98,7 +104,12 @@ public static Map copyOf( } final SortedMap sortedMap = (SortedMap) map; try { - return ImmutableSortedMap.copyOf(sortedMap, sortedMap.comparator()); + Comparator comparator = sortedMap.comparator(); + if (comparator == null) { + return ImmutableSortedMap.copyOf(sortedMap); + } else { + return ImmutableSortedMap.copyOf(sortedMap, comparator); + } } catch (NullPointerException e) { // Make an effectively immutable map by creating a mutable copy // and wrapping it to prevent modification. Unfortunately, if we see diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableNullableSet.java b/core/src/main/java/org/apache/calcite/util/ImmutableNullableSet.java index 0386e23d6b99..434c5ddb5680 100644 --- a/core/src/main/java/org/apache/calcite/util/ImmutableNullableSet.java +++ b/core/src/main/java/org/apache/calcite/util/ImmutableNullableSet.java @@ -23,6 +23,9 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; @@ -33,6 +36,8 @@ import java.util.Objects; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * An immutable set that may contain null values. * @@ -56,19 +61,19 @@ private ImmutableNullableSet(ImmutableSet elements) { } @Override public Iterator iterator() { - return Iterators.transform(elements.iterator(), e -> - e == NullSentinel.INSTANCE ? null : (E) e); + return Util.transform(elements.iterator(), e -> + e == NullSentinel.INSTANCE ? castNonNull(null) : (E) e); } @Override public int size() { return elements.size(); } - @Override public boolean contains(Object o) { + @Override public boolean contains(@Nullable Object o) { return elements.contains(o == null ? NullSentinel.INSTANCE : o); } - @Override public boolean remove(Object o) { + @Override public boolean remove(@Nullable Object o) { throw new UnsupportedOperationException(); } @@ -135,7 +140,7 @@ private static Set copyOf(E[] elements, boolean needCopy) { return ImmutableSet.copyOf(elements); } - final Object[] objects = + final @Nullable Object[] objects = needCopy ? Arrays.copyOf(elements, elements.length, Object[].class) : elements; for (int i = 0; i < objects.length; i++) { @@ -143,7 +148,9 @@ private static Set copyOf(E[] elements, boolean needCopy) { objects[i] = NullSentinel.INSTANCE; } } - return new ImmutableNullableSet(ImmutableSet.copyOf(objects)); + @SuppressWarnings({"nullness", "NullableProblems"}) + @NonNull Object[] nonNullObjects = objects; + return new ImmutableNullableSet(ImmutableSet.copyOf(nonNullObjects)); } private static boolean containsNull(E[] elements) { @@ -182,14 +189,14 @@ public static Set of(E e1, E e2, E e3, E e4) { /** Creates an immutable set of 5 or more elements. */ @SuppressWarnings("unchecked") public static Set of(E e1, E e2, E e3, E e4, E e5, E... others) { - Object[] elements = new Object[5 + others.length]; + E[] elements = (E[]) new Object[5 + others.length]; elements[0] = e1; elements[1] = e2; elements[2] = e3; elements[3] = e4; elements[4] = e5; System.arraycopy(others, 0, elements, 5, others.length); - return copyOf((E []) elements, false); + return copyOf(elements, false); } /** diff --git a/core/src/main/java/org/apache/calcite/util/IntegerIntervalSet.java b/core/src/main/java/org/apache/calcite/util/IntegerIntervalSet.java index 0bdf1324a861..00ad88c262aa 100644 --- a/core/src/main/java/org/apache/calcite/util/IntegerIntervalSet.java +++ b/core/src/main/java/org/apache/calcite/util/IntegerIntervalSet.java @@ -19,6 +19,8 @@ import org.apache.calcite.linq4j.Enumerator; import org.apache.calcite.linq4j.Linq4j; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractSet; import java.util.Iterator; import java.util.Set; @@ -125,7 +127,7 @@ private Enumerator enumerator() { }; } - @Override public boolean contains(Object o) { + @Override public boolean contains(@Nullable Object o) { return o instanceof Number && contains(((Number) o).intValue()); } diff --git a/core/src/main/java/org/apache/calcite/util/JsonBuilder.java b/core/src/main/java/org/apache/calcite/util/JsonBuilder.java index 280f890480ba..531fb97a8e58 100644 --- a/core/src/main/java/org/apache/calcite/util/JsonBuilder.java +++ b/core/src/main/java/org/apache/calcite/util/JsonBuilder.java @@ -18,6 +18,8 @@ import org.apache.calcite.avatica.util.Spaces; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -31,7 +33,7 @@ public class JsonBuilder { /** * Creates a JSON object (represented by a {@link Map}). */ - public Map map() { + public Map map() { // Use LinkedHashMap to preserve order. return new LinkedHashMap<>(); } @@ -39,14 +41,14 @@ public Map map() { /** * Creates a JSON object (represented by a {@link List}). */ - public List list() { + public List<@Nullable Object> list() { return new ArrayList<>(); } /** * Adds a key/value pair to a JSON object. */ - public JsonBuilder put(Map map, String name, Object value) { + public JsonBuilder put(Map map, String name, @Nullable Object value) { map.put(name, value); return this; } @@ -55,7 +57,7 @@ public JsonBuilder put(Map map, String name, Object value) { * Adds a key/value pair to a JSON object if the value is not null. */ public JsonBuilder putIf( - Map map, String name, Object value) { + Map map, String name, @Nullable Object value) { if (value != null) { map.put(name, value); } @@ -78,15 +80,14 @@ public String toJsonString(Object o) { /** * Appends a JSON object to a string builder. */ - public void append(StringBuilder buf, int indent, Object o) { + public void append(StringBuilder buf, int indent, @Nullable Object o) { if (o == null) { buf.append("null"); } else if (o instanceof Map) { //noinspection unchecked appendMap(buf, indent, (Map) o); } else if (o instanceof List) { - //noinspection unchecked - appendList(buf, indent, (List) o); + appendList(buf, indent, (List) o); } else if (o instanceof String) { buf.append('"') .append( @@ -100,7 +101,7 @@ public void append(StringBuilder buf, int indent, Object o) { } private void appendMap( - StringBuilder buf, int indent, Map map) { + StringBuilder buf, int indent, Map map) { if (map.isEmpty()) { buf.append("{}"); return; @@ -108,7 +109,7 @@ private void appendMap( buf.append("{"); newline(buf, indent + 1); int n = 0; - for (Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { if (n++ > 0) { buf.append(","); newline(buf, indent + 1); @@ -126,7 +127,7 @@ private void newline(StringBuilder buf, int indent) { } private void appendList( - StringBuilder buf, int indent, List list) { + StringBuilder buf, int indent, List list) { if (list.isEmpty()) { buf.append("[]"); return; diff --git a/core/src/main/java/org/apache/calcite/util/Litmus.java b/core/src/main/java/org/apache/calcite/util/Litmus.java index fc19049473d1..dd62f50e471c 100644 --- a/core/src/main/java/org/apache/calcite/util/Litmus.java +++ b/core/src/main/java/org/apache/calcite/util/Litmus.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.helpers.MessageFormatter; /** @@ -25,7 +26,7 @@ public interface Litmus { /** Implementation of {@link org.apache.calcite.util.Litmus} that throws * an {@link java.lang.AssertionError} on failure. */ Litmus THROW = new Litmus() { - @Override public boolean fail(String message, Object... args) { + @Override public boolean fail(@Nullable String message, @Nullable Object... args) { final String s = message == null ? null : MessageFormatter.arrayFormat(message, args).getMessage(); throw new AssertionError(s); @@ -35,7 +36,8 @@ public interface Litmus { return true; } - @Override public boolean check(boolean condition, String message, Object... args) { + @Override public boolean check(boolean condition, @Nullable String message, + @Nullable Object... args) { if (condition) { return succeed(); } else { @@ -47,7 +49,7 @@ public interface Litmus { /** Implementation of {@link org.apache.calcite.util.Litmus} that returns * a status code but does not throw. */ Litmus IGNORE = new Litmus() { - @Override public boolean fail(String message, Object... args) { + @Override public boolean fail(@Nullable String message, @Nullable Object... args) { return false; } @@ -55,7 +57,8 @@ public interface Litmus { return true; } - @Override public boolean check(boolean condition, String message, Object... args) { + @Override public boolean check(boolean condition, @Nullable String message, + @Nullable Object... args) { return condition; } }; @@ -65,7 +68,7 @@ public interface Litmus { * @param message Message * @param args Arguments */ - boolean fail(String message, Object... args); + boolean fail(@Nullable String message, @Nullable Object... args); /** Called when test succeeds. Returns true. */ boolean succeed(); @@ -76,5 +79,5 @@ public interface Litmus { * if the condition is false, calls {@link #fail}, * converting {@code info} into a string message. */ - boolean check(boolean condition, String message, Object... args); + boolean check(boolean condition, @Nullable String message, @Nullable Object... args); } diff --git a/core/src/main/java/org/apache/calcite/util/NameMap.java b/core/src/main/java/org/apache/calcite/util/NameMap.java index 3a5fa1daf7ee..0a63332e8169 100644 --- a/core/src/main/java/org/apache/calcite/util/NameMap.java +++ b/core/src/main/java/org/apache/calcite/util/NameMap.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableSortedMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.Map; import java.util.NavigableMap; @@ -53,7 +55,7 @@ public NameMap() { return map.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof NameMap && map.equals(((NameMap) obj).map); @@ -97,7 +99,7 @@ public NavigableMap map() { } @Experimental - public V remove(String key) { + public @Nullable V remove(String key) { return map.remove(key); } } diff --git a/core/src/main/java/org/apache/calcite/util/NameMultimap.java b/core/src/main/java/org/apache/calcite/util/NameMultimap.java index 5278f9b5cefb..8d1505746eb6 100644 --- a/core/src/main/java/org/apache/calcite/util/NameMultimap.java +++ b/core/src/main/java/org/apache/calcite/util/NameMultimap.java @@ -18,6 +18,8 @@ import org.apache.calcite.linq4j.function.Experimental; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -54,7 +56,7 @@ public NameMultimap() { return map.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof NameMultimap && map.equals(((NameMultimap) obj).map); diff --git a/core/src/main/java/org/apache/calcite/util/NameSet.java b/core/src/main/java/org/apache/calcite/util/NameSet.java index 0849aea80d71..26621ee868ad 100644 --- a/core/src/main/java/org/apache/calcite/util/NameSet.java +++ b/core/src/main/java/org/apache/calcite/util/NameSet.java @@ -18,6 +18,8 @@ import com.google.common.collect.Maps; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -54,7 +56,7 @@ public static NameSet immutableCopyOf(Set names) { return names.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof NameSet && names.equals(((NameSet) obj).names); @@ -68,7 +70,11 @@ public void add(String name) { * name. If case-sensitive, that iterable will have 0 or 1 elements; if * case-insensitive, it may have 0 or more. */ public Collection range(String name, boolean caseSensitive) { - return names.range(name, caseSensitive).keySet(); + // This produces checkerframework false-positive + // type of expression: Set<@KeyFor("this.names.range(name, caseSensitive)") String> + // method return type: Collection + //noinspection RedundantCast + return (Collection) names.range(name, caseSensitive).keySet(); } /** Returns whether this set contains the given name, with a given diff --git a/core/src/main/java/org/apache/calcite/util/NlsString.java b/core/src/main/java/org/apache/calcite/util/NlsString.java index ee7bbd346b26..e6ceb1bf8516 100644 --- a/core/src/main/java/org/apache/calcite/util/NlsString.java +++ b/core/src/main/java/org/apache/calcite/util/NlsString.java @@ -27,6 +27,9 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; @@ -36,7 +39,6 @@ import java.util.List; import java.util.Locale; import java.util.Objects; -import javax.annotation.Nonnull; import static org.apache.calcite.util.Static.RESOURCE; @@ -53,7 +55,7 @@ public class NlsString implements Comparable, Cloneable { .softValues() .build( new CacheLoader, String>() { - @Override public String load(@Nonnull Pair key) { + @Override public String load(Pair key) { final Charset charset = key.right; final CharsetDecoder decoder = charset.newDecoder(); final byte[] bytes = key.left.getBytes(); @@ -69,11 +71,11 @@ public class NlsString implements Comparable, Cloneable { } }); - private final String stringValue; - private final ByteString bytesValue; - private final String charsetName; - private final Charset charset; - private final SqlCollation collation; + private final @Nullable String stringValue; + private final @Nullable ByteString bytesValue; + private final @Nullable String charsetName; + private final @Nullable Charset charset; + private final @Nullable SqlCollation collation; //~ Constructors ----------------------------------------------------------- @@ -91,7 +93,7 @@ public class NlsString implements Comparable, Cloneable { * given charset */ public NlsString(ByteString bytesValue, String charsetName, - SqlCollation collation) { + @Nullable SqlCollation collation) { this(null, Objects.requireNonNull(bytesValue), Objects.requireNonNull(charsetName), collation); } @@ -109,14 +111,14 @@ public NlsString(ByteString bytesValue, String charsetName, * @throws RuntimeException If the given value cannot be represented in the * given charset */ - public NlsString(String stringValue, String charsetName, - SqlCollation collation) { + public NlsString(String stringValue, @Nullable String charsetName, + @Nullable SqlCollation collation) { this(Objects.requireNonNull(stringValue), null, charsetName, collation); } /** Internal constructor; other constructors must call it. */ - private NlsString(String stringValue, ByteString bytesValue, - String charsetName, SqlCollation collation) { + private NlsString(@Nullable String stringValue, @Nullable ByteString bytesValue, + @Nullable String charsetName, @Nullable SqlCollation collation) { if (charsetName != null) { this.charsetName = charsetName.toUpperCase(Locale.ROOT); this.charset = SqlUtil.getCharset(charsetName); @@ -128,15 +130,19 @@ private NlsString(String stringValue, ByteString bytesValue, throw new IllegalArgumentException("Specify stringValue or bytesValue"); } if (bytesValue != null) { - if (charsetName == null) { + if (charset == null) { throw new IllegalArgumentException("Bytes value requires charset"); } SqlUtil.validateCharset(bytesValue, charset); } else { + //noinspection ConstantConditions + assert stringValue != null : "stringValue must not be null"; // Java string can be malformed if LATIN1 is required. if (this.charsetName != null && (this.charsetName.equals("LATIN1") || this.charsetName.equals("ISO-8859-1"))) { + //noinspection ConstantConditions + assert charset != null : "charset must not be null"; if (!charset.newEncoder().canEncode(stringValue)) { throw RESOURCE.charsetEncoding(stringValue, charset.name()).ex(); } @@ -161,7 +167,7 @@ private NlsString(String stringValue, ByteString bytesValue, return Objects.hash(stringValue, bytesValue, charsetName, collation); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof NlsString && Objects.equals(stringValue, ((NlsString) obj).stringValue) @@ -177,21 +183,25 @@ private NlsString(String stringValue, ByteString bytesValue, return getValue().compareTo(other.getValue()); } - public String getCharsetName() { + @Pure + public @Nullable String getCharsetName() { return charsetName; } - public Charset getCharset() { + @Pure + public @Nullable Charset getCharset() { return charset; } - public SqlCollation getCollation() { + @Pure + public @Nullable SqlCollation getCollation() { return collation; } public String getValue() { if (stringValue == null) { - assert bytesValue != null; + assert bytesValue != null : "bytesValue must not be null"; + assert charset != null : "charset must not be null"; return DECODE_MAP.getUnchecked(Pair.of(bytesValue, charset)); } return stringValue; @@ -296,7 +306,8 @@ public NlsString copy(String value) { } /** Returns the value as a {@link ByteString}. */ - public ByteString getValueBytes() { + @Pure + public @Nullable ByteString getValueBytes() { return bytesValue; } } diff --git a/core/src/main/java/org/apache/calcite/util/NumberUtil.java b/core/src/main/java/org/apache/calcite/util/NumberUtil.java index 23ef7d7852d0..7c18a6a9b050 100644 --- a/core/src/main/java/org/apache/calcite/util/NumberUtil.java +++ b/core/src/main/java/org/apache/calcite/util/NumberUtil.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; @@ -24,6 +26,8 @@ import java.text.NumberFormat; import java.util.Locale; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Utility functions for working with numbers. */ @@ -86,7 +90,7 @@ public static BigInteger getMinUnscaled(int precision) { return BIG_INT_MIN_UNSCALED[precision]; } - public static BigDecimal rescaleBigDecimal(BigDecimal bd, int scale) { + public static @PolyNull BigDecimal rescaleBigDecimal(@PolyNull BigDecimal bd, int scale) { if (bd != null) { bd = bd.setScale(scale, RoundingMode.HALF_UP); } @@ -98,9 +102,9 @@ public static BigDecimal toBigDecimal(Number number, int scale) { return rescaleBigDecimal(bd, scale); } - public static BigDecimal toBigDecimal(Number number) { + public static @PolyNull BigDecimal toBigDecimal(@PolyNull Number number) { if (number == null) { - return null; + return castNonNull(null); } if (number instanceof BigDecimal) { return (BigDecimal) number; @@ -135,15 +139,15 @@ public static long round(double d) { } } - public static Double add(Double a, Double b) { + public static @PolyNull Double add(@PolyNull Double a, @PolyNull Double b) { if ((a == null) || (b == null)) { - return null; + return castNonNull(null); } return a + b; } - public static Double subtract(Double a, Double b) { + public static @PolyNull Double subtract(@PolyNull Double a, @PolyNull Double b) { if ((a == null) || (b == null)) { return null; } @@ -151,24 +155,24 @@ public static Double subtract(Double a, Double b) { return a - b; } - public static Double divide(Double a, Double b) { + public static @PolyNull Double divide(@PolyNull Double a, @PolyNull Double b) { if ((a == null) || (b == null) || (b == 0D)) { - return null; + return castNonNull(null); } return a / b; } - public static Double multiply(Double a, Double b) { + public static @PolyNull Double multiply(@PolyNull Double a, @PolyNull Double b) { if ((a == null) || (b == null)) { - return null; + return castNonNull(null); } return a * b; } /** Like {@link Math#min} but null safe. */ - public static Double min(Double a, Double b) { + public static @PolyNull Double min(@PolyNull Double a, @PolyNull Double b) { if (a == null) { return b; } else if (b == null) { @@ -177,4 +181,18 @@ public static Double min(Double a, Double b) { return Math.min(a, b); } } + + /** + * Maximum of the input values or null if any of the inputs is null. + * + * @param a first value + * @param b second value + * @return maximum of the input values or null if any of the inputs is null + */ + public static @PolyNull Double max(@PolyNull Double a, @PolyNull Double b) { + if ((a == null) || (b == null)) { + return castNonNull(null); + } + return Math.max(a, b); + } } diff --git a/core/src/main/java/org/apache/calcite/util/Pair.java b/core/src/main/java/org/apache/calcite/util/Pair.java index c98f2033f367..26cffeaa4346 100644 --- a/core/src/main/java/org/apache/calcite/util/Pair.java +++ b/core/src/main/java/org/apache/calcite/util/Pair.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.Serializable; import java.util.AbstractList; import java.util.Collections; @@ -26,7 +28,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.BiConsumer; -import javax.annotation.Nonnull; /** * Pair of objects. @@ -87,7 +88,7 @@ public static Pair of(Map.Entry entry) { //~ Methods ---------------------------------------------------------------- - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || (obj instanceof Pair) && Objects.equals(this.left, ((Pair) obj).left) @@ -104,7 +105,7 @@ public static Pair of(Map.Entry entry) { return keyHash ^ valueHash; } - @Override public int compareTo(@Nonnull Pair that) { + @Override public int compareTo(Pair that) { //noinspection unchecked int c = NULLS_FIRST_COMPARATOR.compare(this.left, that.left); if (c == 0) { @@ -269,7 +270,7 @@ public static List> zipMutable( public static void forEach( final Iterable ks, final Iterable vs, - BiConsumer consumer) { + BiConsumer consumer) { final Iterator leftIterator = ks.iterator(); final Iterator rightIterator = vs.iterator(); while (leftIterator.hasNext() && rightIterator.hasNext()) { diff --git a/core/src/main/java/org/apache/calcite/util/PartiallyOrderedSet.java b/core/src/main/java/org/apache/calcite/util/PartiallyOrderedSet.java index 5b87c6e79351..eb85c317064b 100644 --- a/core/src/main/java/org/apache/calcite/util/PartiallyOrderedSet.java +++ b/core/src/main/java/org/apache/calcite/util/PartiallyOrderedSet.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractSet; import java.util.ArrayDeque; import java.util.ArrayList; @@ -31,10 +33,14 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.function.Function; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + +import static java.util.Objects.requireNonNull; + + /** * Partially-ordered set. * @@ -75,9 +81,9 @@ public class PartiallyOrderedSet extends AbstractSet { ImmutableBitSet::contains; private final Map> map; - private final Function> parentFunction; + private final @Nullable Function> parentFunction; @SuppressWarnings("unused") - private final Function> childFunction; + private final @Nullable Function> childFunction; private final Ordering ordering; /** @@ -124,6 +130,7 @@ public PartiallyOrderedSet(Ordering ordering, * @param ordering Ordering relation * @param collection Initial contents of partially-ordered set */ + @SuppressWarnings("method.invocation.invalid") public PartiallyOrderedSet(Ordering ordering, Collection collection) { this(ordering, new HashMap<>(collection.size() * 3 / 2), null, null); addAll(collection); @@ -137,8 +144,8 @@ public PartiallyOrderedSet(Ordering ordering, Collection collection) { * @param parentFunction Function to compute parents of a node; may be null */ private PartiallyOrderedSet(Ordering ordering, Map> map, - Function> childFunction, - Function> parentFunction) { + @Nullable Function> childFunction, + @Nullable Function> parentFunction) { this.ordering = ordering; this.map = map; this.childFunction = childFunction; @@ -153,7 +160,7 @@ private PartiallyOrderedSet(Ordering ordering, Map> map, @Override public Iterator iterator() { final Iterator iterator = map.keySet().iterator(); return new Iterator() { - E previous; + @Nullable E previous; @Override public boolean hasNext() { return iterator.hasNext(); @@ -179,13 +186,12 @@ private PartiallyOrderedSet(Ordering ordering, Map> map, return map.size(); } - @Override public boolean contains(Object o) { + @Override public boolean contains(@Nullable Object o) { //noinspection SuspiciousMethodCalls return map.containsKey(o); } - @Override public boolean remove(Object o) { - @SuppressWarnings("SuspiciousMethodCalls") + @Override public boolean remove(@Nullable Object o) { final Node node = map.remove(o); if (node == null) { return false; @@ -478,14 +484,14 @@ public boolean isValid(boolean fail) { } } if (lt12 && !lt21) { - if (!nodeAncestors.get(node1).contains(node2.e)) { + if (!get(nodeAncestors, node1, "nodeAncestors").contains(node2.e)) { assert !fail : node1.e + " is less than " + node2.e + " but " + node2.e + " is not in the ancestor set of " + node1.e; return false; } - if (!nodeDescendants.get(node2).contains(node1.e)) { + if (!get(nodeDescendants, node2, "nodeDescendants").contains(node1.e)) { assert !fail : node1.e + " is less than " + node2.e + " but " + node1.e + " is not in the descendant set of " @@ -494,14 +500,14 @@ public boolean isValid(boolean fail) { } } if (lt21 && !lt12) { - if (!nodeAncestors.get(node2).contains(node1.e)) { + if (!get(nodeAncestors, node2, "nodeAncestors").contains(node1.e)) { assert !fail : node2.e + " is less than " + node1.e + " but " + node1.e + " is not in the ancestor set of " + node2.e; return false; } - if (!nodeDescendants.get(node1).contains(node2.e)) { + if (!get(nodeDescendants, node1, "nodeDescendants").contains(node2.e)) { assert !fail : node2.e + " is less than " + node1.e + " but " + node2.e + " is not in the descendant set of " @@ -514,6 +520,11 @@ public boolean isValid(boolean fail) { return true; } + private static Set get(Map, Set> map, Node node, String label) { + return requireNonNull(map.get(node), + () -> label + " for node " + node); + } + private void distanceRecurse( Map distanceToRoot, Node node, @@ -554,9 +565,11 @@ public void out(StringBuilder buf) { buf.append(children); buf.append("\n"); - for (E child : children) { - if (seen.add(child)) { - unseen.add(child); + if (children != null) { + for (E child : children) { + if (seen.add(child)) { + unseen.add(child); + } } } } @@ -575,7 +588,7 @@ public void out(StringBuilder buf) { * @return List of values in this set that are directly less than the given * value */ - public List getChildren(E e) { + public @Nullable List getChildren(E e) { return getChildren(e, false); } @@ -593,7 +606,7 @@ public List getChildren(E e) { * @return List of values in this set that are directly less than the given * value */ - public List getChildren(E e, boolean hypothetical) { + public @Nullable List getChildren(E e, boolean hypothetical) { final Node node = map.get(e); if (node == null) { if (hypothetical) { @@ -618,7 +631,7 @@ public List getChildren(E e, boolean hypothetical) { * @return List of values in this set that are directly greater than the * given value */ - public List getParents(E e) { + public @Nullable List getParents(E e) { return getParents(e, false); } @@ -636,7 +649,7 @@ public List getParents(E e) { * @return List of values in this set that are directly greater than the * given value */ - public List getParents(E e, boolean hypothetical) { + public @Nullable List getParents(E e, boolean hypothetical) { final Node node = map.get(e); if (node == null) { if (hypothetical) { @@ -657,7 +670,7 @@ public List getParents(E e, boolean hypothetical) { private void closure(Function> generator, E e, ImmutableList.Builder list, Set set) { - for (E p : Objects.requireNonNull(generator.apply(e))) { + for (E p : requireNonNull(generator.apply(e))) { if (set.add(e)) { if (map.containsKey(p)) { list.add(p); @@ -807,7 +820,7 @@ private static class Node { } @Override public String toString() { - return e.toString(); + return String.valueOf(e); } } @@ -821,7 +834,7 @@ private static class TopBottomNode extends Node { private final String description; TopBottomNode(boolean top) { - super(null); + super(castNonNull(null)); this.description = top ? "top" : "bottom"; } diff --git a/core/src/main/java/org/apache/calcite/util/Permutation.java b/core/src/main/java/org/apache/calcite/util/Permutation.java index 1a126f15dadd..530ad98ad0e3 100644 --- a/core/src/main/java/org/apache/calcite/util/Permutation.java +++ b/core/src/main/java/org/apache/calcite/util/Permutation.java @@ -21,6 +21,10 @@ import org.apache.calcite.util.mapping.MappingType; import org.apache.calcite.util.mapping.Mappings; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; + import java.util.Arrays; import java.util.Iterator; @@ -42,6 +46,7 @@ public class Permutation implements Mapping, Mappings.TargetMapping { * * @param size Number of elements in the permutation */ + @SuppressWarnings("method.invocation.invalid") public Permutation(int size) { targets = new int[size]; sources = new int[size]; @@ -433,7 +438,8 @@ private void setInternal(int source, int target) { * @param fail Whether to assert if invalid * @return Whether valid */ - private boolean isValid(boolean fail) { + @RequiresNonNull({"sources", "targets"}) + private boolean isValid(@UnknownInitialization Permutation this, boolean fail) { final int size = targets.length; if (sources.length != size) { assert !fail : "different lengths"; @@ -473,7 +479,7 @@ private boolean isValid(boolean fail) { return toString().hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { // not very efficient return (obj instanceof Permutation) && toString().equals(obj.toString()); diff --git a/core/src/main/java/org/apache/calcite/util/PrecedenceClimbingParser.java b/core/src/main/java/org/apache/calcite/util/PrecedenceClimbingParser.java index 940ab155aaf6..4af196b47ede 100644 --- a/core/src/main/java/org/apache/calcite/util/PrecedenceClimbingParser.java +++ b/core/src/main/java/org/apache/calcite/util/PrecedenceClimbingParser.java @@ -20,20 +20,23 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.Predicate; +import static java.util.Objects.requireNonNull; + /** * Parser that takes a collection of tokens (atoms and operators) * and groups them together according to the operators' precedence * and associativity. */ public class PrecedenceClimbingParser { - private Token first; - private Token last; + private @Nullable Token first; + private @Nullable Token last; private PrecedenceClimbingParser(List tokens) { Token p = null; @@ -76,7 +79,7 @@ public SpecialOp special(Object o, int leftPrec, int rightPrec, return new SpecialOp(o, leftPrec * 2, rightPrec * 2, special); } - public Token parse() { + public @Nullable Token parse() { partialParse(); if (first != last) { throw new AssertionError("could not find next operator to reduce: " @@ -93,23 +96,31 @@ public void partialParse() { } final Token t; switch (op.type) { - case POSTFIX: - t = call(op, ImmutableList.of(op.previous)); - replace(t, op.previous.previous, op.next); + case POSTFIX: { + Token previous = requireNonNull(op.previous, () -> "previous of " + op); + t = call(op, ImmutableList.of(previous)); + replace(t, previous.previous, op.next); break; - case PREFIX: - t = call(op, ImmutableList.of(op.next)); - replace(t, op.previous, op.next.next); + } + case PREFIX: { + Token next = requireNonNull(op.next, () -> "next of " + op); + t = call(op, ImmutableList.of(next)); + replace(t, op.previous, next.next); break; - case INFIX: - t = call(op, ImmutableList.of(op.previous, op.next)); - replace(t, op.previous.previous, op.next.next); + } + case INFIX: { + Token previous = requireNonNull(op.previous, () -> "previous of " + op); + Token next = requireNonNull(op.next, () -> "next of " + op); + t = call(op, ImmutableList.of(previous, next)); + replace(t, previous.previous, next.next); break; - case SPECIAL: + } + case SPECIAL: { Result r = ((SpecialOp) op).special.apply(this, (SpecialOp) op); - Objects.requireNonNull(r); + requireNonNull(r); replace(r.replacement, r.first.previous, r.last.next); break; + } default: throw new AssertionError(); } @@ -126,7 +137,7 @@ public List all() { return new TokenList(); } - private void replace(Token t, Token previous, Token next) { + private void replace(Token t, @Nullable Token previous, @Nullable Token next) { t.previous = previous; t.next = next; if (previous == null) { @@ -141,7 +152,7 @@ private void replace(Token t, Token previous, Token next) { } } - private Op highest() { + private @Nullable Op highest() { int p = -1; Op highest = null; for (Token t = first; t != null; t = t.next) { @@ -156,7 +167,7 @@ private Op highest() { } /** Returns the right precedence of the preceding operator token. */ - private int prevRight(Token token) { + private int prevRight(@Nullable Token token) { for (; token != null; token = token.previous) { if (token.type == Type.POSTFIX) { return Integer.MAX_VALUE; @@ -169,7 +180,7 @@ private int prevRight(Token token) { } /** Returns the left precedence of the following operator token. */ - private int nextLeft(Token token) { + private int nextLeft(@Nullable Token token) { for (; token != null; token = token.next) { if (token.type == Type.PREFIX) { return Integer.MAX_VALUE; @@ -209,14 +220,14 @@ public enum Type { /** A token: either an atom, a call to an operator with arguments, * or an unmatched operator. */ public static class Token { - Token previous; - Token next; + @Nullable Token previous; + @Nullable Token next; public final Type type; - public final Object o; + public final @Nullable Object o; final int left; final int right; - Token(Type type, Object o, int left, int right) { + Token(Type type, @Nullable Object o, int left, int right) { this.type = type; this.o = o; this.left = left; @@ -224,7 +235,7 @@ public static class Token { } @Override public String toString() { - return o.toString(); + return String.valueOf(o); } protected StringBuilder print(StringBuilder b) { @@ -238,7 +249,7 @@ public Token copy() { /** An operator token. */ public static class Op extends Token { - Op(Type type, Object o, int left, int right) { + Op(Type type, @Nullable Object o, int left, int right) { super(type, o, left, right); } @@ -251,7 +262,7 @@ public static class Op extends Token { public static class SpecialOp extends Op { public final Special special; - SpecialOp(Object o, int left, int right, Special special) { + SpecialOp(@Nullable Object o, int left, int right, Special special) { super(Type.SPECIAL, o, left, right); this.special = special; } @@ -315,7 +326,7 @@ public static class Call extends Token { private StringBuilder printOp(StringBuilder b, boolean leftSpace, boolean rightSpace) { - String s = op.o.toString(); + String s = String.valueOf(op.o); if (leftSpace) { b.append(' '); } diff --git a/core/src/main/java/org/apache/calcite/util/ReflectUtil.java b/core/src/main/java/org/apache/calcite/util/ReflectUtil.java index 09cf6cebccd2..15e30b18860f 100644 --- a/core/src/main/java/org/apache/calcite/util/ReflectUtil.java +++ b/core/src/main/java/org/apache/calcite/util/ReflectUtil.java @@ -21,6 +21,9 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -32,6 +35,8 @@ import java.util.List; import java.util.Map; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Static utilities for Java reflection. */ @@ -100,7 +105,7 @@ public abstract class ReflectUtil { */ public static Method getByteBufferReadMethod(Class clazz) { assert clazz.isPrimitive(); - return primitiveToByteBufferReadMethod.get(clazz); + return castNonNull(primitiveToByteBufferReadMethod.get(clazz)); } /** @@ -112,7 +117,7 @@ public static Method getByteBufferReadMethod(Class clazz) { */ public static Method getByteBufferWriteMethod(Class clazz) { assert clazz.isPrimitive(); - return primitiveToByteBufferWriteMethod.get(clazz); + return castNonNull(primitiveToByteBufferWriteMethod.get(clazz)); } /** @@ -124,7 +129,7 @@ public static Method getByteBufferWriteMethod(Class clazz) { */ public static Class getBoxingClass(Class primitiveClass) { assert primitiveClass.isPrimitive(); - return primitiveToBoxingMap.get(primitiveClass); + return castNonNull(primitiveToBoxingMap.get(primitiveClass)); } /** @@ -279,7 +284,7 @@ private static boolean invokeVisitorInternal( * @param visitMethodName name of visit method * @return method found, or null if none found */ - public static Method lookupVisitMethod( + public static @Nullable Method lookupVisitMethod( Class visitorClass, Class visiteeClass, String visitMethodName) { @@ -303,7 +308,7 @@ public static Method lookupVisitMethod( * @return method found, or null if none found * @see #createDispatcher(Class, Class) */ - public static Method lookupVisitMethod( + public static @Nullable Method lookupVisitMethod( Class visitorClass, Class visiteeClass, String visitMethodName, @@ -311,8 +316,7 @@ public static Method lookupVisitMethod( // Prepare an array to re-use in recursive calls. The first argument // will have the visitee class substituted into it. Class[] paramTypes = new Class[1 + additionalParameterTypes.size()]; - int iParam = 0; - paramTypes[iParam++] = null; + int iParam = 1; for (Class paramType : additionalParameterTypes) { paramTypes[iParam++] = paramType; } @@ -321,7 +325,7 @@ public static Method lookupVisitMethod( // the original visiteeClass has a diamond-shaped interface inheritance // graph. (This is common, for example, in JMI.) The idea is to avoid // iterating over a single interface's method more than once in a call. - Map, Method> cache = new HashMap<>(); + Map, @Nullable Method> cache = new HashMap<>(); return lookupVisitMethod( visitorClass, @@ -331,12 +335,12 @@ public static Method lookupVisitMethod( cache); } - private static Method lookupVisitMethod( + private static @Nullable Method lookupVisitMethod( final Class visitorClass, final Class visiteeClass, final String visitMethodName, final Class[] paramTypes, - final Map, Method> cache) { + final Map, @Nullable Method> cache) { // Use containsKey since the result for a Class might be null. if (cache.containsKey(visiteeClass)) { return cache.get(visiteeClass); @@ -411,15 +415,16 @@ private static Method lookupVisitMethod( * @param visiteeBaseClazz Visitee base class * @return cache of methods */ - public static ReflectiveVisitDispatcher createDispatcher( + public static ReflectiveVisitDispatcher createDispatcher( final Class visitorBaseClazz, final Class visiteeBaseClazz) { assert ReflectiveVisitor.class.isAssignableFrom(visitorBaseClazz); assert Object.class.isAssignableFrom(visiteeBaseClazz); return new ReflectiveVisitDispatcher() { - final Map, Method> map = new HashMap<>(); + final Map, @Nullable Method> map = new HashMap<>(); - @Override public Method lookupVisitMethod( + @Override public @Nullable Method lookupVisitMethod( Class visitorClass, Class visiteeClass, String visitMethodName) { @@ -430,7 +435,7 @@ public static ReflectiveVisitDispatcher c Collections.emptyList()); } - @Override public Method lookupVisitMethod( + @Override public @Nullable Method lookupVisitMethod( Class visitorClass, Class visiteeClass, String visitMethodName, @@ -505,7 +510,7 @@ public static ReflectiveVisitDispatcher c * @param arg0Clazz Base type of argument zero * @param otherArgClasses Types of remaining arguments */ - public static MethodDispatcher createMethodDispatcher( + public static MethodDispatcher createMethodDispatcher( final Class returnClazz, final ReflectiveVisitor visitor, final String methodName, @@ -519,10 +524,12 @@ public static MethodDispatcher createMethodDispatcher( createDispatcher( (Class) visitor.getClass(), arg0Clazz); return new MethodDispatcher() { - @Override public T invoke(Object... args) { - Method method = lookupMethod(args[0]); + @Override public T invoke(@Nullable Object... args) { + Method method = lookupMethod(castNonNull(args[0])); try { - final Object o = method.invoke(visitor, args); + // castNonNull is here because method.invoke can return null, and we don't know if + // T is nullable + final Object o = castNonNull(method.invoke(visitor, args)); return returnClazz.cast(o); } catch (IllegalAccessException e) { throw new RuntimeException("While invoking method '" + method + "'", @@ -651,6 +658,6 @@ public interface MethodDispatcher { * @param args Arguments to method * @return Return value of method */ - T invoke(Object... args); + T invoke(@Nullable Object... args); } } diff --git a/core/src/main/java/org/apache/calcite/util/ReflectiveVisitDispatcher.java b/core/src/main/java/org/apache/calcite/util/ReflectiveVisitDispatcher.java index cfe11a3f4ceb..146f74631bef 100644 --- a/core/src/main/java/org/apache/calcite/util/ReflectiveVisitDispatcher.java +++ b/core/src/main/java/org/apache/calcite/util/ReflectiveVisitDispatcher.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.util.List; @@ -32,7 +35,8 @@ * @param Argument type * @param Return type */ -public interface ReflectiveVisitDispatcher { +public interface ReflectiveVisitDispatcher { //~ Methods ---------------------------------------------------------------- /** @@ -47,7 +51,7 @@ public interface ReflectiveVisitDispatcher { * @param additionalParameterTypes list of additional parameter types * @return method found, or null if none found */ - Method lookupVisitMethod( + @Nullable Method lookupVisitMethod( Class visitorClass, Class visiteeClass, String visitMethodName, @@ -62,7 +66,7 @@ Method lookupVisitMethod( * @param visitMethodName name of visit method * @return method found, or null if none found */ - Method lookupVisitMethod( + @Nullable Method lookupVisitMethod( Class visitorClass, Class visiteeClass, String visitMethodName); diff --git a/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java b/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java index 4ba7edc3e5ca..dd8f2379a2f6 100644 --- a/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java +++ b/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java @@ -28,6 +28,8 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE; +import static java.util.Objects.requireNonNull; + /** * Utilities used by multiple dialect for RelToSql conversion. */ @@ -43,7 +45,9 @@ public static void unparseHiveTrim( int leftPrec, int rightPrec) { final SqlLiteral valueToTrim = call.operand(1); - if (valueToTrim.toValue().matches("\\s+")) { + String value = requireNonNull(valueToTrim.toValue(), + () -> "call.operand(1).toValue() for call " + call); + if (value.matches("\\s+")) { unparseTrimWithSpace(writer, call, leftPrec, rightPrec); } else { // SELECT TRIM(both 'A' from "ABC") -> SELECT REGEXP_REPLACE("ABC", '^(A)*', '') @@ -98,7 +102,8 @@ private static void unparseTrimWithSpace( * @return the regex pattern of the character to be trimmed */ public static SqlCharStringLiteral createRegexPatternLiteral(SqlNode call, SqlLiteral trimFlag) { - final String regexPattern = ((SqlCharStringLiteral) call).toValue(); + final String regexPattern = requireNonNull(((SqlCharStringLiteral) call).toValue(), + () -> "null value for SqlNode " + call); String escaped = escapeSpecialChar(regexPattern); final StringBuilder builder = new StringBuilder(); switch (trimFlag.getValueAs(SqlTrimFunction.Flag.class)) { diff --git a/core/src/main/java/org/apache/calcite/util/SaffronProperties.java b/core/src/main/java/org/apache/calcite/util/SaffronProperties.java index 78513579950d..9d29235173af 100644 --- a/core/src/main/java/org/apache/calcite/util/SaffronProperties.java +++ b/core/src/main/java/org/apache/calcite/util/SaffronProperties.java @@ -27,6 +27,7 @@ import java.io.InputStream; import java.security.AccessControlException; import java.util.Collections; +import java.util.Objects; import java.util.Properties; /** @@ -126,7 +127,7 @@ static SaffronProperties instance() { Properties properties = new Properties(); // read properties from the file "saffron.properties", if it exists in classpath - try (InputStream stream = Helper.class.getClassLoader() + try (InputStream stream = Objects.requireNonNull(Helper.class.getClassLoader(), "classLoader") .getResourceAsStream("saffron.properties")) { if (stream != null) { properties.load(stream); @@ -141,7 +142,9 @@ static SaffronProperties instance() { Properties source = System.getProperties(); for (Object objectKey : Collections.list(source.keys())) { String key = (String) objectKey; - String value = source.getProperty(key); + String value = Objects.requireNonNull( + source.getProperty(key), + () -> "value for " + key); if (key.startsWith("saffron.") || key.startsWith("net.sf.saffron.")) { properties.setProperty(key, value); } diff --git a/core/src/main/java/org/apache/calcite/util/Sarg.java b/core/src/main/java/org/apache/calcite/util/Sarg.java index b356b1e8cc7e..d5ceb6998d74 100644 --- a/core/src/main/java/org/apache/calcite/util/Sarg.java +++ b/core/src/main/java/org/apache/calcite/util/Sarg.java @@ -24,6 +24,8 @@ import com.google.common.collect.Range; import com.google.common.collect.RangeSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; import java.util.function.BiConsumer; @@ -124,11 +126,11 @@ public StringBuilder printTo(StringBuilder sb, return RangeSets.hashCode(rangeSet) * 31 + (containsNull ? 2 : 3); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof Sarg - && rangeSet.equals(((Sarg) o).rangeSet) - && containsNull == ((Sarg) o).containsNull; + && containsNull == ((Sarg) o).containsNull + && rangeSet.equals(((Sarg) o).rangeSet); } /** Returns whether this Sarg is a collection of 1 or more points (and perhaps diff --git a/core/src/main/java/org/apache/calcite/util/SerializableCharset.java b/core/src/main/java/org/apache/calcite/util/SerializableCharset.java index ed042abf2027..8138af8e23c9 100644 --- a/core/src/main/java/org/apache/calcite/util/SerializableCharset.java +++ b/core/src/main/java/org/apache/calcite/util/SerializableCharset.java @@ -16,12 +16,16 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.charset.Charset; +import static java.util.Objects.requireNonNull; + /** * Serializable wrapper around a {@link Charset}. * @@ -67,7 +71,8 @@ private void writeObject(ObjectOutputStream out) throws IOException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { charsetName = (String) in.readObject(); - charset = Charset.availableCharsets().get(this.charsetName); + charset = requireNonNull(Charset.availableCharsets().get(this.charsetName), + () -> "charset is not found: " + charsetName); } /** @@ -86,7 +91,7 @@ public Charset getCharset() { * @param charset Character set to wrap, or null * @return Wrapped charset */ - public static SerializableCharset forCharset(Charset charset) { + public static @PolyNull SerializableCharset forCharset(@PolyNull Charset charset) { if (charset == null) { return null; } diff --git a/core/src/main/java/org/apache/calcite/util/SimpleNamespaceContext.java b/core/src/main/java/org/apache/calcite/util/SimpleNamespaceContext.java index 55a872cc084f..68bab8f134f5 100644 --- a/core/src/main/java/org/apache/calcite/util/SimpleNamespaceContext.java +++ b/core/src/main/java/org/apache/calcite/util/SimpleNamespaceContext.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -34,6 +36,7 @@ public class SimpleNamespaceContext implements NamespaceContext { private final Map prefixToNamespaceUri = new HashMap<>(); private final Map> namespaceUriToPrefixes = new HashMap<>(); + @SuppressWarnings({"method.invocation.invalid", "methodref.receiver.bound.invalid"}) public SimpleNamespaceContext(Map bindings) { bindNamespaceUri(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI); bindNamespaceUri(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI); @@ -48,7 +51,7 @@ public SimpleNamespaceContext(Map bindings) { return ""; } - @Override public String getPrefix(String namespaceUri) { + @Override public @Nullable String getPrefix(String namespaceUri) { Set prefixes = getPrefixesSet(namespaceUri); return !prefixes.isEmpty() ? prefixes.iterator().next() : null; } diff --git a/core/src/main/java/org/apache/calcite/util/Source.java b/core/src/main/java/org/apache/calcite/util/Source.java index 59d9c665c3bc..001d48568566 100644 --- a/core/src/main/java/org/apache/calcite/util/Source.java +++ b/core/src/main/java/org/apache/calcite/util/Source.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -41,7 +43,7 @@ public interface Source { /** Looks for a suffix on a path and returns * either the path with the suffix removed * or null. */ - Source trimOrNull(String suffix); + @Nullable Source trimOrNull(String suffix); /** Returns a source whose path concatenates this with a child. * diff --git a/core/src/main/java/org/apache/calcite/util/SourceStringReader.java b/core/src/main/java/org/apache/calcite/util/SourceStringReader.java index d4096e7955f5..013e62729c4d 100644 --- a/core/src/main/java/org/apache/calcite/util/SourceStringReader.java +++ b/core/src/main/java/org/apache/calcite/util/SourceStringReader.java @@ -16,9 +16,9 @@ */ package org.apache.calcite.util; + import java.io.StringReader; import java.util.Objects; -import javax.annotation.Nonnull; /** * Extension to {@link StringReader} that allows the original string to be @@ -32,13 +32,13 @@ public class SourceStringReader extends StringReader { * * @param s String providing the character stream */ - public SourceStringReader(@Nonnull String s) { + public SourceStringReader(String s) { super(Objects.requireNonNull(s)); this.s = s; } /** Returns the source string. */ - public @Nonnull String getSourceString() { + public String getSourceString() { return s; } } diff --git a/core/src/main/java/org/apache/calcite/util/Sources.java b/core/src/main/java/org/apache/calcite/util/Sources.java index 98713e55731a..9790110a9487 100644 --- a/core/src/main/java/org/apache/calcite/util/Sources.java +++ b/core/src/main/java/org/apache/calcite/util/Sources.java @@ -20,6 +20,8 @@ import com.google.common.io.CharSource; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -51,7 +53,7 @@ public static Source of(URL url) { } - public static Source file(File baseDirectory, String fileName) { + public static Source file(@Nullable File baseDirectory, String fileName) { final File file = new File(fileName); if (baseDirectory != null && !file.isAbsolute()) { return of(new File(baseDirectory, fileName)); @@ -84,7 +86,7 @@ public static Source url(String url) { /** Looks for a suffix on a path and returns * either the path with the suffix removed * or null. */ - private static String trimOrNull(String s, String suffix) { + private static @Nullable String trimOrNull(String s, String suffix) { return s.endsWith(suffix) ? s.substring(0, s.length() - suffix.length()) : null; @@ -136,7 +138,7 @@ private UnsupportedOperationException unsupported() { throw unsupported(); } - @Override public Source trimOrNull(final String suffix) { + @Override public @Nullable Source trimOrNull(final String suffix) { throw unsupported(); } @@ -156,7 +158,7 @@ private UnsupportedOperationException unsupported() { /** Implementation of {@link Source} on the top of a {@link File} or * {@link URL}. */ private static class FileSource implements Source { - private final File file; + private final @Nullable File file; private final URL url; /** @@ -176,7 +178,11 @@ private FileSource(File file) { this.urlGenerated = true; } - private static File urlToFile(URL url) { + private File fileNonNull() { + return Objects.requireNonNull(file, "file"); + } + + private static @Nullable File urlToFile(URL url) { if (!"file".equals(url.getProtocol())) { return null; } @@ -227,7 +233,7 @@ private static URL fileToUrl(File file) { } @Override public String toString() { - return (urlGenerated ? file : url).toString(); + return (urlGenerated ? fileNonNull() : url).toString(); } @Override public URL url() { @@ -281,12 +287,12 @@ private static URL fileToUrl(File file) { return x == null ? this : x; } - @Override public Source trimOrNull(String suffix) { + @Override public @Nullable Source trimOrNull(String suffix) { if (!urlGenerated) { final String s = Sources.trimOrNull(url.toExternalForm(), suffix); return s == null ? null : Sources.url(s); } else { - final String s = Sources.trimOrNull(file.getPath(), suffix); + final String s = Sources.trimOrNull(fileNonNull().getPath(), suffix); return s == null ? null : of(new File(s)); } } @@ -320,8 +326,8 @@ private static URL fileToUrl(File file) { @Override public Source relative(Source parent) { if (isFile(parent)) { if (isFile(this) - && file.getPath().startsWith(parent.file().getPath())) { - String rest = file.getPath().substring(parent.file().getPath().length()); + && fileNonNull().getPath().startsWith(parent.file().getPath())) { + String rest = fileNonNull().getPath().substring(parent.file().getPath().length()); if (rest.startsWith(File.separator)) { return Sources.file(null, rest.substring(File.separator.length())); } diff --git a/core/src/main/java/org/apache/calcite/util/Template.java b/core/src/main/java/org/apache/calcite/util/Template.java index a0e89b2733c1..11047261af4f 100644 --- a/core/src/main/java/org/apache/calcite/util/Template.java +++ b/core/src/main/java/org/apache/calcite/util/Template.java @@ -18,6 +18,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; @@ -218,7 +220,7 @@ private static void makeFormat( * object */ public String format(Map argMap) { - Object[] args = new Object[parameterNames.size()]; + @Nullable Object[] args = new Object[parameterNames.size()]; for (int i = 0; i < parameterNames.size(); i++) { args[i] = getArg(argMap, i); } @@ -232,7 +234,7 @@ public String format(Map argMap) { * @param ordinal Ordinal of argument * @return Value of argument */ - private Object getArg(Map argMap, int ordinal) { + private @Nullable Object getArg(Map argMap, int ordinal) { // First get by name. String parameterName = parameterNames.get(ordinal); Object arg = argMap.get(parameterName); diff --git a/core/src/main/java/org/apache/calcite/util/TimeString.java b/core/src/main/java/org/apache/calcite/util/TimeString.java index 316f0d50c5ff..1e8f8d56ff13 100644 --- a/core/src/main/java/org/apache/calcite/util/TimeString.java +++ b/core/src/main/java/org/apache/calcite/util/TimeString.java @@ -21,9 +21,10 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Calendar; import java.util.regex.Pattern; -import javax.annotation.Nonnull; /** * Time literal. @@ -43,6 +44,7 @@ private TimeString(String v, @SuppressWarnings("unused") boolean ignore) { } /** Creates a TimeString. */ + @SuppressWarnings("method.invocation.invalid") public TimeString(String v) { this(v, false); Preconditions.checkArgument(PATTERN.matcher(v).matches(), @@ -118,7 +120,7 @@ public TimeString withFraction(String fraction) { return v; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { // The value is in canonical form (no trailing zeros). return o == this || o instanceof TimeString @@ -129,7 +131,7 @@ public TimeString withFraction(String fraction) { return v.hashCode(); } - @Override public int compareTo(@Nonnull TimeString o) { + @Override public int compareTo(TimeString o) { return v.compareTo(o.v); } diff --git a/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java b/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java index f5b3d5cb1c7b..f651574e0766 100644 --- a/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java +++ b/core/src/main/java/org/apache/calcite/util/TimeWithTimeZoneString.java @@ -20,6 +20,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; @@ -145,7 +147,7 @@ public TimeWithTimeZoneString withTimeZone(TimeZone timeZone) { return v; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { // The value is in canonical form (no trailing zeros). return o == this || o instanceof TimeWithTimeZoneString diff --git a/core/src/main/java/org/apache/calcite/util/TimestampString.java b/core/src/main/java/org/apache/calcite/util/TimestampString.java index 46862ed02ed4..44ad844c72d6 100644 --- a/core/src/main/java/org/apache/calcite/util/TimestampString.java +++ b/core/src/main/java/org/apache/calcite/util/TimestampString.java @@ -21,6 +21,8 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Calendar; import java.util.regex.Pattern; @@ -98,7 +100,7 @@ public TimestampString withFraction(String fraction) { return v; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { // The value is in canonical form (no trailing zeros). return o == this || o instanceof TimestampString diff --git a/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java b/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java index 9272adc5b7b3..eaabd0ae4a05 100644 --- a/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java +++ b/core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java @@ -20,6 +20,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; @@ -141,7 +143,7 @@ public TimestampWithTimeZoneString withTimeZone(TimeZone timeZone) { return v; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { // The value is in canonical form (no trailing zeros). return o == this || o instanceof TimestampWithTimeZoneString diff --git a/core/src/main/java/org/apache/calcite/util/TryThreadLocal.java b/core/src/main/java/org/apache/calcite/util/TryThreadLocal.java index 2a03ea0b6043..b8ff5d7c492a 100644 --- a/core/src/main/java/org/apache/calcite/util/TryThreadLocal.java +++ b/core/src/main/java/org/apache/calcite/util/TryThreadLocal.java @@ -16,12 +16,14 @@ */ package org.apache.calcite.util; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Thread-local variable that returns a handle that can be closed. * * @param Value type */ -public class TryThreadLocal extends ThreadLocal { +public class TryThreadLocal<@Nullable T> extends ThreadLocal { private final T initialValue; /** Creates a TryThreadLocal. diff --git a/core/src/main/java/org/apache/calcite/util/Util.java b/core/src/main/java/org/apache/calcite/util/Util.java index 6820ffa8c418..4f88d02d065d 100644 --- a/core/src/main/java/org/apache/calcite/util/Util.java +++ b/core/src/main/java/org/apache/calcite/util/Util.java @@ -42,6 +42,9 @@ import com.google.common.collect.Sets; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.dataflow.qual.Pure; import org.slf4j.Logger; import java.io.BufferedReader; @@ -106,7 +109,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collector; -import javax.annotation.Nonnull; + +import static org.apache.calcite.linq4j.Nullness.castNonNull; /** * Miscellaneous utility functions. @@ -209,7 +213,7 @@ public static boolean isSingleValue(SqlCall call) { * you are not interested in, but you don't want the compiler to warn that * you are not using it. */ - public static void discard(Object o) { + public static void discard(@Nullable Object o) { if (false) { discard(o); } @@ -257,7 +261,7 @@ public static void discard(double d) { */ public static void swallow( Throwable e, - Logger logger) { + @Nullable Logger logger) { if (logger != null) { logger.debug("Discarding exception", e); } @@ -306,7 +310,7 @@ public static int hash( @Deprecated // to be removed before 2.0 public static int hash( int h, - Object o) { + @Nullable Object o) { int k = (o == null) ? 0 : o.hashCode(); return ((h << 4) | h) ^ k; } @@ -378,7 +382,7 @@ public static void print( @SuppressWarnings("JdkObsolete") public static void print( PrintWriter pw, - Object o, + @Nullable Object o, int indent) { if (o == null) { pw.print("null"); @@ -496,7 +500,7 @@ public static void print( */ public static void printJavaString( Appendable appendable, - String s, + @Nullable String s, boolean nullMeansNull) { try { if (s == null) { @@ -773,7 +777,7 @@ public static List toList(Iterator iter) { /** * Returns whether s == null or if s.length() == 0. */ - public static boolean isNullOrEmpty(String s) { + public static boolean isNullOrEmpty(@Nullable String s) { return (null == s) || (s.length() == 0); } @@ -798,7 +802,7 @@ public static String sepList(List list, String sep) { case -1: return ""; case 0: - return list.get(0).toString(); + return String.valueOf(list.get(0)); default: break; } @@ -1072,7 +1076,7 @@ public static void permAssert(boolean b, String description) { * overridden and a subclass forgot to do so. * @return an {@link UnsupportedOperationException}. */ - public static RuntimeException needToImplement(Object o) { + public static RuntimeException needToImplement(@Nullable Object o) { String description = null; if (o != null) { description = o.getClass().toString() + ": " + o.toString(); @@ -1192,7 +1196,7 @@ public static String readAllAsString(Reader reader) throws IOException { * @param jar jar to close */ @Deprecated // to be removed before 2.0 - public static void squelchJar(JarFile jar) { + public static void squelchJar(@Nullable JarFile jar) { try { if (jar != null) { jar.close(); @@ -1210,7 +1214,7 @@ public static void squelchJar(JarFile jar) { * @param stream stream to close */ @Deprecated // to be removed before 2.0 - public static void squelchStream(InputStream stream) { + public static void squelchStream(@Nullable InputStream stream) { try { if (stream != null) { stream.close(); @@ -1230,7 +1234,7 @@ public static void squelchStream(InputStream stream) { * @param stream stream to close */ @Deprecated // to be removed before 2.0 - public static void squelchStream(OutputStream stream) { + public static void squelchStream(@Nullable OutputStream stream) { try { if (stream != null) { stream.close(); @@ -1248,7 +1252,7 @@ public static void squelchStream(OutputStream stream) { * @param reader reader to close */ @Deprecated // to be removed before 2.0 - public static void squelchReader(Reader reader) { + public static void squelchReader(@Nullable Reader reader) { try { if (reader != null) { reader.close(); @@ -1268,7 +1272,7 @@ public static void squelchReader(Reader reader) { * @param writer writer to close */ @Deprecated // to be removed before 2.0 - public static void squelchWriter(Writer writer) { + public static void squelchWriter(@Nullable Writer writer) { try { if (writer != null) { writer.close(); @@ -1286,7 +1290,7 @@ public static void squelchWriter(Writer writer) { * @param stmt stmt to close */ @Deprecated // to be removed before 2.0 - public static void squelchStmt(Statement stmt) { + public static void squelchStmt(@Nullable Statement stmt) { try { if (stmt != null) { stmt.close(); @@ -1304,7 +1308,7 @@ public static void squelchStmt(Statement stmt) { * @param connection connection to close */ @Deprecated // to be removed before 2.0 - public static void squelchConnection(Connection connection) { + public static void squelchConnection(@Nullable Connection connection) { try { if (connection != null) { connection.close(); @@ -1501,18 +1505,18 @@ public static String toPosix(TimeZone tz, boolean verbose) { + tzString); } int j = 0; - int startMode = Integer.valueOf(matcher.group(++j)); - int startMonth = Integer.valueOf(matcher.group(++j)); - int startDay = Integer.valueOf(matcher.group(++j)); - int startDayOfWeek = Integer.valueOf(matcher.group(++j)); - int startTime = Integer.valueOf(matcher.group(++j)); - int startTimeMode = Integer.valueOf(matcher.group(++j)); - int endMode = Integer.valueOf(matcher.group(++j)); - int endMonth = Integer.valueOf(matcher.group(++j)); - int endDay = Integer.valueOf(matcher.group(++j)); - int endDayOfWeek = Integer.valueOf(matcher.group(++j)); - int endTime = Integer.valueOf(matcher.group(++j)); - int endTimeMode = Integer.valueOf(matcher.group(++j)); + int startMode = groupAsInt(matcher, ++j); + int startMonth = groupAsInt(matcher, ++j); + int startDay = groupAsInt(matcher, ++j); + int startDayOfWeek = groupAsInt(matcher, ++j); + int startTime = groupAsInt(matcher, ++j); + int startTimeMode = groupAsInt(matcher, ++j); + int endMode = groupAsInt(matcher, ++j); + int endMonth = groupAsInt(matcher, ++j); + int endDay = groupAsInt(matcher, ++j); + int endDayOfWeek = groupAsInt(matcher, ++j); + int endTime = groupAsInt(matcher, ++j); + int endTimeMode = groupAsInt(matcher, ++j); appendPosixDaylightTransition( tz, buf, @@ -1538,6 +1542,13 @@ public static String toPosix(TimeZone tz, boolean verbose) { return buf.toString(); } + private static int groupAsInt(Matcher matcher, int index) { + String value = Objects.requireNonNull( + matcher.group(index), + () -> "no group for index " + index + ", matcher " + matcher); + return Integer.parseInt(value); + } + /** * Writes a daylight savings time transition to a POSIX timezone * description. @@ -1726,10 +1737,10 @@ public static List cast(List list, Class clazz) { * @param clazz Class to cast to. * @return An iterator whose members are of the desired type. */ - public static Iterator cast( - final Iterator iter, + public static Iterator cast( + final Iterator iter, final Class clazz) { - return transform(iter, clazz::cast); + return transform(iter, x -> clazz.cast(castNonNull(x))); } /** @@ -1929,7 +1940,7 @@ public static > Map enumConstants( * @param Enum class type * @return Enum constant or null */ - public static synchronized > T enumVal( + public static synchronized > @Nullable T enumVal( Class clazz, String name) { return clazz.cast(ENUM_CONSTANTS.getUnchecked(clazz).get(name)); @@ -1945,7 +1956,7 @@ public static synchronized > T enumVal( * @return Enum constant, never null */ public static synchronized > T enumVal(T default_, - String name) { + @Nullable String name) { final Class clazz = default_.getDeclaringClass(); final T t = clazz.cast(ENUM_CONSTANTS.getUnchecked(clazz).get(name)); if (t == null) { @@ -2001,59 +2012,59 @@ public static List> pairs(final List list) { * *

    Equivalent to the Elvis operator ({@code ?:}) of languages such as * Groovy or PHP. */ - public static T first(T v0, T v1) { + public static @PolyNull T first(@Nullable T v0, @PolyNull T v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Double} value, * using a given default value if it is null. */ - public static double first(Double v0, double v1) { + public static double first(@Nullable Double v0, double v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Float} value, * using a given default value if it is null. */ - public static float first(Float v0, float v1) { + public static float first(@Nullable Float v0, float v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Integer} value, * using a given default value if it is null. */ - public static int first(Integer v0, int v1) { + public static int first(@Nullable Integer v0, int v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Long} value, * using a given default value if it is null. */ - public static long first(Long v0, long v1) { + public static long first(@Nullable Long v0, long v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Boolean} value, * using a given default value if it is null. */ - public static boolean first(Boolean v0, boolean v1) { + public static boolean first(@Nullable Boolean v0, boolean v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Short} value, * using a given default value if it is null. */ - public static short first(Short v0, short v1) { + public static short first(@Nullable Short v0, short v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Character} value, * using a given default value if it is null. */ - public static char first(Character v0, char v1) { + public static char first(@Nullable Character v0, char v1) { return v0 != null ? v0 : v1; } /** Unboxes a {@link Byte} value, * using a given default value if it is null. */ - public static byte first(Byte v0, byte v1) { + public static byte first(@Nullable Byte v0, byte v1) { return v0 != null ? v0 : v1; } - public static Iterable orEmpty(Iterable v0) { + public static Iterable orEmpty(@Nullable Iterable v0) { return v0 != null ? v0 : ImmutableList.of(); } @@ -2199,7 +2210,7 @@ private static boolean allSameElement(List list) { final Iterator iterator = list.iterator(); final E first = iterator.next(); while (iterator.hasNext()) { - if (!first.equals(iterator.next())) { + if (!Objects.equals(first, iterator.next())) { return false; } } @@ -2403,6 +2414,7 @@ public static Map asIndexMapJ( } }; return new AbstractMap() { + @SuppressWarnings("override.return.invalid") @Override public Set> entrySet() { return entrySet; } @@ -2587,7 +2599,7 @@ public static List transform(List list, } /** Transforms a iterator, applying a function to each element. */ - @API(since = "1.26", status = API.Status.EXPERIMENTAL) + @API(since = "1.27", status = API.Status.EXPERIMENTAL) public static Iterable transform(Iterable iterable, java.util.function.Function function) { // FluentIterable provides toString @@ -2599,14 +2611,14 @@ public static Iterable transform(Iterable iterable, } /** Transforms an iterator. */ - @API(since = "1.26", status = API.Status.EXPERIMENTAL) + @API(since = "1.27", status = API.Status.EXPERIMENTAL) public static Iterator transform(Iterator iterator, java.util.function.Function function) { return new TransformingIterator<>(iterator, function); } /** Filters an iterable. */ - @API(since = "1.26", status = API.Status.EXPERIMENTAL) + @API(since = "1.27", status = API.Status.EXPERIMENTAL) public static Iterable filter(Iterable iterable, Predicate predicate) { // FluentIterable provides toString @@ -2618,7 +2630,7 @@ public static Iterable filter(Iterable iterable, } /** Filters an iterator. */ - @API(since = "1.26", status = API.Status.EXPERIMENTAL) + @API(since = "1.27", status = API.Status.EXPERIMENTAL) public static Iterator filter(Iterator iterator, Predicate predicate) { return new FilteringIterator<>(iterator, predicate); @@ -2689,17 +2701,18 @@ public static Map blackholeMap() { * Exception used to interrupt a tree walk of any kind. */ public static class FoundOne extends ControlFlowException { - private final Object node; + private final @Nullable Object node; /** Singleton instance. Can be used if you don't care about node. */ @SuppressWarnings("ThrowableInstanceNeverThrown") public static final FoundOne NULL = new FoundOne(null); - public FoundOne(Object node) { + public FoundOne(@Nullable Object node) { this.node = node; } - public Object getNode() { + @Pure + public @Nullable Object getNode() { return node; } } @@ -2743,7 +2756,7 @@ private static class TransformingList extends AbstractList { return list.size(); } - @Override @Nonnull public Iterator iterator() { + @Override public Iterator iterator() { return listIterator(); } } @@ -2775,7 +2788,9 @@ private static class FilteringIterator implements Iterator { Predicate predicate) { this.iterator = iterator; this.predicate = predicate; - current = moveNext(); + @SuppressWarnings("method.invocation.invalid") + T current = moveNext(); + this.current = current; } @Override public boolean hasNext() { diff --git a/core/src/main/java/org/apache/calcite/util/XmlOutput.java b/core/src/main/java/org/apache/calcite/util/XmlOutput.java index 75e4c857541d..e20ea6a19624 100644 --- a/core/src/main/java/org/apache/calcite/util/XmlOutput.java +++ b/core/src/main/java/org/apache/calcite/util/XmlOutput.java @@ -18,12 +18,17 @@ import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.PrintWriter; import java.io.Writer; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; +import java.util.List; + +import static java.util.Objects.requireNonNull; /** * Streaming XML output. @@ -178,7 +183,7 @@ public void print(String s) { * @param attributes an XMLAttrVector containing the attributes to include * in the tag. */ - public void beginTag(String tagName, XMLAttrVector attributes) { + public void beginTag(String tagName, @Nullable XMLAttrVector attributes) { beginBeginTag(tagName); if (attributes != null) { attributes.display(out, indent); @@ -342,7 +347,7 @@ public void cdata(String data) { * ... ]]> regardless of the content of * data; if false, quote only if the content needs it */ - public void cdata(String data, boolean quote) { + public void cdata(@Nullable String data, boolean quote) { if (inTag) { // complete the parent's start tag if (compact) { @@ -397,7 +402,7 @@ public void stringTag(String name, String data) { /** * Writes content. */ - public void content(String content) { + public void content(@Nullable String content) { // This method previously used a LineNumberReader, but that class is // susceptible to a form of DoS attack. It uses lots of memory and CPU if a // malicious client gives it input with very long lines. @@ -456,7 +461,7 @@ public int numTagsWritten() { } /** Prints an XML attribute name and value for string {@code val}. */ - private static void printAtt(PrintWriter pw, String name, String val) { + private static void printAtt(PrintWriter pw, String name, @Nullable String val) { if (val != null /* && !val.equals("") */) { pw.print(" "); pw.print(name); @@ -541,8 +546,8 @@ private static boolean stringHasXMLSpecials(String input) { * use one of the global mappings pre-defined here.

    */ static class StringEscaper implements Cloneable { - private ArrayList translationVector; - private String [] translationTable; + private @Nullable List<@Nullable String> translationVector; + private String @Nullable [] translationTable; public static final StringEscaper XML_ESCAPER; public static final StringEscaper XML_NUMERIC_ESCAPER; @@ -560,6 +565,8 @@ static class StringEscaper implements Cloneable { */ public void defineEscape(char from, String to) { int i = (int) from; + List<@Nullable String> translationVector = requireNonNull(this.translationVector, + "translationVector"); if (i >= translationVector.size()) { // Extend list by adding the requisite number of nulls. final int count = i + 1 - translationVector.size(); @@ -572,9 +579,10 @@ public void defineEscape(char from, String to) { * Call this before attempting to escape strings; after this, * defineEscape may not be called again. */ + @SuppressWarnings("assignment.type.incompatible") public void makeImmutable() { translationTable = - translationVector.toArray(new String[0]); + requireNonNull(translationVector, "translationVector").toArray(new String[0]); translationVector = null; } @@ -590,7 +598,7 @@ public String escapeString(String s) { // codes >= 128 (e.g. Euro sign) are always escaped if (c > 127) { escape = "&#" + Integer.toString(c) + ";"; - } else if (c >= translationTable.length) { + } else if (c >= requireNonNull(translationTable, "translationTable").length) { escape = null; } else { escape = translationTable[c]; @@ -633,7 +641,8 @@ public String escapeString(String s) { public StringEscaper getMutableClone() { StringEscaper clone = clone(); if (clone.translationVector == null) { - clone.translationVector = Lists.newArrayList(clone.translationTable); + clone.translationVector = Lists.newArrayList( + requireNonNull(clone.translationTable, "clone.translationTable")); clone.translationTable = null; } return clone; diff --git a/core/src/main/java/org/apache/calcite/util/graph/AttributedDirectedGraph.java b/core/src/main/java/org/apache/calcite/util/graph/AttributedDirectedGraph.java index 51f93f2e0d84..15c15a70c3d0 100644 --- a/core/src/main/java/org/apache/calcite/util/graph/AttributedDirectedGraph.java +++ b/core/src/main/java/org/apache/calcite/util/graph/AttributedDirectedGraph.java @@ -18,6 +18,9 @@ import org.apache.calcite.util.Util; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -30,7 +33,7 @@ public class AttributedDirectedGraph extends DefaultDirectedGraph { /** Creates an attributed graph. */ - public AttributedDirectedGraph(AttributedEdgeFactory edgeFactory) { + public AttributedDirectedGraph(@UnknownInitialization AttributedEdgeFactory edgeFactory) { super(edgeFactory); } @@ -40,8 +43,8 @@ public static AttributedDirectedGraph create( } /** Returns the first edge between one vertex to another. */ - @Override public E getEdge(V source, V target) { - final VertexInfo info = vertexMap.get(source); + @Override public @Nullable E getEdge(V source, V target) { + final VertexInfo info = getVertex(source); for (E outEdge : info.outEdges) { if (outEdge.target.equals(target)) { return outEdge; @@ -52,20 +55,14 @@ public static AttributedDirectedGraph create( // CHECKSTYLE: IGNORE 1 /** @deprecated Use {@link #addEdge(Object, Object, Object...)}. */ - @Override @Deprecated - public E addEdge(V vertex, V targetVertex) { + @Deprecated + @Override public @Nullable E addEdge(V vertex, V targetVertex) { return super.addEdge(vertex, targetVertex); } - public E addEdge(V vertex, V targetVertex, Object... attributes) { - final VertexInfo info = vertexMap.get(vertex); - if (info == null) { - throw new IllegalArgumentException("no vertex " + vertex); - } - final VertexInfo targetInfo = vertexMap.get(targetVertex); - if (targetInfo == null) { - throw new IllegalArgumentException("no vertex " + targetVertex); - } + public @Nullable E addEdge(V vertex, V targetVertex, Object... attributes) { + final VertexInfo info = getVertex(vertex); + final VertexInfo targetInfo = getVertex(targetVertex); @SuppressWarnings("unchecked") final AttributedEdgeFactory f = (AttributedEdgeFactory) this.edgeFactory; @@ -81,7 +78,7 @@ public E addEdge(V vertex, V targetVertex, Object... attributes) { /** Returns all edges between one vertex to another. */ public Iterable getEdges(V source, final V target) { - final VertexInfo info = vertexMap.get(source); + final VertexInfo info = getVertex(source); return Util.filter(info.outEdges, outEdge -> outEdge.target.equals(target)); } @@ -89,7 +86,7 @@ public Iterable getEdges(V source, final V target) { * Returns whether any were removed. */ @Override public boolean removeEdge(V source, V target) { // remove out edges - final List outEdges = vertexMap.get(source).outEdges; + final List outEdges = getVertex(source).outEdges; int removeOutCount = 0; for (int i = 0, size = outEdges.size(); i < size; i++) { E edge = outEdges.get(i); @@ -101,7 +98,7 @@ public Iterable getEdges(V source, final V target) { } // remove in edges - final List inEdges = vertexMap.get(target).inEdges; + final List inEdges = getVertex(target).inEdges; int removeInCount = 0; for (int i = 0, size = inEdges.size(); i < size; i++) { E edge = inEdges.get(i); diff --git a/core/src/main/java/org/apache/calcite/util/graph/DefaultDirectedGraph.java b/core/src/main/java/org/apache/calcite/util/graph/DefaultDirectedGraph.java index 3f3bc153eba0..af63a8d4b367 100644 --- a/core/src/main/java/org/apache/calcite/util/graph/DefaultDirectedGraph.java +++ b/core/src/main/java/org/apache/calcite/util/graph/DefaultDirectedGraph.java @@ -16,8 +16,14 @@ */ package org.apache.calcite.util.graph; + import com.google.common.collect.Ordering; +import org.apiguardian.api.API; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -28,6 +34,8 @@ import java.util.Map; import java.util.Set; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** * Default implementation of {@link DirectedGraph}. * @@ -38,10 +46,10 @@ public class DefaultDirectedGraph implements DirectedGraph { final Set edges = new LinkedHashSet<>(); final Map> vertexMap = new LinkedHashMap<>(); - final EdgeFactory edgeFactory; + final @NotOnlyInitialized EdgeFactory edgeFactory; /** Creates a graph. */ - public DefaultDirectedGraph(EdgeFactory edgeFactory) { + public DefaultDirectedGraph(@UnknownInitialization EdgeFactory edgeFactory) { this.edgeFactory = edgeFactory; } @@ -74,7 +82,7 @@ public String toStringUnordered() { private String toString(Ordering vertexOrdering, Ordering edgeOrdering) { return "graph(" - + "vertices: " + vertexOrdering.sortedCopy(vertexMap.keySet()) + + "vertices: " + vertexOrdering.sortedCopy((Set) vertexMap.keySet()) + ", edges: " + edgeOrdering.sortedCopy(edges) + ")"; } @@ -87,19 +95,23 @@ private String toString(Ordering vertexOrdering, } } - @Override public Set edgeSet() { - return Collections.unmodifiableSet(edges); - } - - @Override public E addEdge(V vertex, V targetVertex) { + @API(since = "1.26", status = API.Status.EXPERIMENTAL) + protected final VertexInfo getVertex(V vertex) { + @SuppressWarnings("argument.type.incompatible") final VertexInfo info = vertexMap.get(vertex); if (info == null) { throw new IllegalArgumentException("no vertex " + vertex); } - final VertexInfo targetInfo = vertexMap.get(targetVertex); - if (targetInfo == null) { - throw new IllegalArgumentException("no vertex " + targetVertex); - } + return info; + } + + @Override public Set edgeSet() { + return Collections.unmodifiableSet(edges); + } + + @Override public @Nullable E addEdge(V vertex, V targetVertex) { + final VertexInfo info = getVertex(vertex); + final VertexInfo targetInfo = getVertex(targetVertex); final E edge = edgeFactory.createEdge(vertex, targetVertex); if (edges.add(edge)) { info.outEdges.add(edge); @@ -110,9 +122,9 @@ private String toString(Ordering vertexOrdering, } } - @Override public E getEdge(V source, V target) { + @Override public @Nullable E getEdge(V source, V target) { // REVIEW: could instead use edges.get(new DefaultEdge(source, target)) - final VertexInfo info = vertexMap.get(source); + final VertexInfo info = getVertex(source); for (E outEdge : info.outEdges) { if (outEdge.target.equals(target)) { return outEdge; @@ -123,7 +135,7 @@ private String toString(Ordering vertexOrdering, @Override public boolean removeEdge(V source, V target) { // remove out edges - final List outEdges = vertexMap.get(source).outEdges; + final List outEdges = getVertex(source).outEdges; boolean outRemoved = false; for (int i = 0, size = outEdges.size(); i < size; i++) { E edge = outEdges.get(i); @@ -136,7 +148,7 @@ private String toString(Ordering vertexOrdering, } // remove in edges - final List inEdges = vertexMap.get(target).inEdges; + final List inEdges = getVertex(target).inEdges; boolean inRemoved = false; for (int i = 0, size = inEdges.size(); i < size; i++) { E edge = inEdges.get(i); @@ -150,7 +162,9 @@ private String toString(Ordering vertexOrdering, return outRemoved; } + @SuppressWarnings("return.type.incompatible") @Override public Set vertexSet() { + // Set -> Set return vertexMap.keySet(); } @@ -175,6 +189,7 @@ private String toString(Ordering vertexOrdering, * if {@code collection} is a small fraction of the set of vertices. */ private void removeMinorityVertices(Collection collection) { for (V v : collection) { + @SuppressWarnings("argument.type.incompatible") // nullable keys are supported by .get final VertexInfo info = vertexMap.get(v); if (info == null) { continue; @@ -182,15 +197,17 @@ private void removeMinorityVertices(Collection collection) { // remove all edges pointing to v for (E edge : info.inEdges) { + @SuppressWarnings("unchecked") final V source = (V) edge.source; - final VertexInfo sourceInfo = vertexMap.get(source); + final VertexInfo sourceInfo = getVertex(source); sourceInfo.outEdges.removeIf(e -> e.target.equals(v)); } // remove all edges starting from v for (E edge : info.outEdges) { + @SuppressWarnings("unchecked") final V target = (V) edge.target; - final VertexInfo targetInfo = vertexMap.get(target); + final VertexInfo targetInfo = getVertex(target); targetInfo.inEdges.removeIf(e -> e.source.equals(v)); } } @@ -203,17 +220,17 @@ private void removeMinorityVertices(Collection collection) { private void removeMajorityVertices(Set vertexSet) { vertexMap.keySet().removeAll(vertexSet); for (VertexInfo info : vertexMap.values()) { - info.outEdges.removeIf(e -> vertexSet.contains((V) e.target)); - info.inEdges.removeIf(e -> vertexSet.contains((V) e.source)); + info.outEdges.removeIf(e -> vertexSet.contains(castNonNull((V) e.target))); + info.inEdges.removeIf(e -> vertexSet.contains(castNonNull((V) e.source))); } } @Override public List getOutwardEdges(V source) { - return vertexMap.get(source).outEdges; + return getVertex(source).outEdges; } @Override public List getInwardEdges(V target) { - return vertexMap.get(target).inEdges; + return getVertex(target).inEdges; } final V source(E edge) { diff --git a/core/src/main/java/org/apache/calcite/util/graph/DefaultEdge.java b/core/src/main/java/org/apache/calcite/util/graph/DefaultEdge.java index e13f81b5426c..00f130a79a6c 100644 --- a/core/src/main/java/org/apache/calcite/util/graph/DefaultEdge.java +++ b/core/src/main/java/org/apache/calcite/util/graph/DefaultEdge.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util.graph; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -34,14 +36,16 @@ public DefaultEdge(Object source, Object target) { return source.hashCode() * 31 + target.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return this == obj || obj instanceof DefaultEdge && ((DefaultEdge) obj).source.equals(source) && ((DefaultEdge) obj).target.equals(target); } - public static DirectedGraph.EdgeFactory factory() { - return DefaultEdge::new; + public static DirectedGraph.EdgeFactory factory() { + // see https://github.com/typetools/checker-framework/issues/3637 + //noinspection Convert2MethodRef + return (source1, target1) -> new DefaultEdge(source1, target1); } } diff --git a/core/src/main/java/org/apache/calcite/util/graph/DepthFirstIterator.java b/core/src/main/java/org/apache/calcite/util/graph/DepthFirstIterator.java index 466a98079d3b..080b6814bf21 100644 --- a/core/src/main/java/org/apache/calcite/util/graph/DepthFirstIterator.java +++ b/core/src/main/java/org/apache/calcite/util/graph/DepthFirstIterator.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Set; + /** * Iterates over the vertices in a directed graph in depth-first order. * diff --git a/core/src/main/java/org/apache/calcite/util/graph/DirectedGraph.java b/core/src/main/java/org/apache/calcite/util/graph/DirectedGraph.java index 24e3d3064200..eb15a96b7afe 100644 --- a/core/src/main/java/org/apache/calcite/util/graph/DirectedGraph.java +++ b/core/src/main/java/org/apache/calcite/util/graph/DirectedGraph.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.util.graph; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collection; import java.util.List; import java.util.Set; @@ -41,13 +43,13 @@ public interface DirectedGraph { * @return New edge, if added, otherwise null * @throws IllegalArgumentException if either vertex is not already in graph */ - E addEdge(V vertex, V targetVertex); + @Nullable E addEdge(V vertex, V targetVertex); - E getEdge(V source, V target); + @Nullable E getEdge(V source, V target); boolean removeEdge(V vertex, V targetVertex); - Set vertexSet(); + Set vertexSet(); /** Removes from this graph all vertices that are in {@code collection}, * and the edges into and out of those vertices. */ diff --git a/core/src/main/java/org/apache/calcite/util/graph/Graphs.java b/core/src/main/java/org/apache/calcite/util/graph/Graphs.java index 3e2230d23c07..e9727e3f96ad 100644 --- a/core/src/main/java/org/apache/calcite/util/graph/Graphs.java +++ b/core/src/main/java/org/apache/calcite/util/graph/Graphs.java @@ -29,6 +29,8 @@ import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Miscellaneous graph utilities. */ @@ -75,7 +77,8 @@ public static FrozenGraph makeImmutable( if (edge.target.equals(edge2.left)) { final Pair key = Pair.of(graph1.source(edge), edge2.right); int[] bestDistance = shortestDistances.get(key); - int[] arc2Distance = shortestDistances.get(edge2); + int[] arc2Distance = requireNonNull(shortestDistances.get(edge2), + () -> "shortestDistances.get(edge2) for " + edge2); if ((bestDistance == null) || (bestDistance[0] > (arc2Distance[0] + 1))) { shortestDistances.put(key, new int[] {arc2Distance[0] + 1}); @@ -97,7 +100,7 @@ public static FrozenGraph makeImmutable( * @param Vertex type * @param Edge type */ - public static class FrozenGraph { + public static class FrozenGraph { private final DefaultDirectedGraph graph; private final Map, int[]> shortestDistances; diff --git a/core/src/main/java/org/apache/calcite/util/graph/TopologicalOrderIterator.java b/core/src/main/java/org/apache/calcite/util/graph/TopologicalOrderIterator.java index 9ea1c1dc2707..ada55196790a 100644 --- a/core/src/main/java/org/apache/calcite/util/graph/TopologicalOrderIterator.java +++ b/core/src/main/java/org/apache/calcite/util/graph/TopologicalOrderIterator.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.util.graph; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; + import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -23,6 +26,9 @@ import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + + /** * Iterates over the edges of a graph in topological order. * @@ -45,7 +51,10 @@ public static Iterable of( return () -> new TopologicalOrderIterator<>(graph); } - private void populate(Map countMap, List empties) { + @RequiresNonNull("graph") + private void populate( + @UnderInitialization TopologicalOrderIterator this, + Map countMap, List empties) { for (V v : graph.vertexMap.keySet()) { countMap.put(v, new int[] {0}); } @@ -53,7 +62,9 @@ private void populate(Map countMap, List empties) { : graph.vertexMap.values()) { for (E edge : info.outEdges) { //noinspection SuspiciousMethodCalls - final int[] ints = countMap.get(edge.target); + final int[] ints = requireNonNull( + countMap.get(edge.target), + () -> "no value for " + edge.target); ++ints[0]; } } @@ -71,10 +82,16 @@ private void populate(Map countMap, List empties) { @Override public V next() { V v = empties.remove(0); - for (E o : graph.vertexMap.get(v).outEdges) { + DefaultDirectedGraph.VertexInfo vertexInfo = requireNonNull( + graph.vertexMap.get(v), + () -> "no vertex " + v); + for (E o : vertexInfo.outEdges) { //noinspection unchecked final V target = (V) o.target; - if (--countMap.get(target)[0] == 0) { + int[] ints = requireNonNull( + countMap.get(target), + () -> "no counts found for target " + target); + if (--ints[0] == 0) { countMap.remove(target); empties.add(target); } @@ -90,6 +107,7 @@ Set findCycles() { while (hasNext()) { next(); } - return countMap.keySet(); + //noinspection RedundantCast + return (Set) countMap.keySet(); } } diff --git a/core/src/main/java/org/apache/calcite/util/javac/JaninoCompiler.java b/core/src/main/java/org/apache/calcite/util/javac/JaninoCompiler.java index 47508616423f..001a0bf16117 100644 --- a/core/src/main/java/org/apache/calcite/util/javac/JaninoCompiler.java +++ b/core/src/main/java/org/apache/calcite/util/javac/JaninoCompiler.java @@ -18,6 +18,7 @@ import org.apache.calcite.config.CalciteSystemProperty; +import org.checkerframework.checker.nullness.qual.Nullable; import org.codehaus.janino.JavaSourceClassLoader; import org.codehaus.janino.util.ClassFile; import org.codehaus.janino.util.resource.MapResourceFinder; @@ -30,6 +31,8 @@ import java.util.HashMap; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * JaninoCompiler implements the {@link JavaCompiler} interface by * calling Janino. @@ -40,7 +43,7 @@ public class JaninoCompiler implements JavaCompiler { public JaninoCompilerArgs args = new JaninoCompilerArgs(); // REVIEW jvs 28-June-2004: pool this instance? Is it thread-safe? - private AccountingClassLoader classLoader; + private @Nullable AccountingClassLoader classLoader; //~ Constructors ----------------------------------------------------------- @@ -57,9 +60,9 @@ public JaninoCompiler() { // class and its callers to specify all code to compile in one // go, we could probably just use a single AccountingClassLoader. - assert args.destdir != null; - assert args.fullClassName != null; - assert args.source != null; + String destdir = requireNonNull(args.destdir, "args.destdir"); + String fullClassName = requireNonNull(args.fullClassName, "args.fullClassName"); + String source = requireNonNull(args.source, "args.source"); ClassLoader parentClassLoader = args.getClassLoader(); if (classLoader != null) { @@ -68,24 +71,24 @@ public JaninoCompiler() { Map sourceMap = new HashMap<>(); sourceMap.put( - ClassFile.getSourceResourceName(args.fullClassName), - args.source.getBytes(StandardCharsets.UTF_8)); + ClassFile.getSourceResourceName(fullClassName), + source.getBytes(StandardCharsets.UTF_8)); MapResourceFinder sourceFinder = new MapResourceFinder(sourceMap); - classLoader = + AccountingClassLoader classLoader = this.classLoader = new AccountingClassLoader( parentClassLoader, sourceFinder, null, - args.destdir == null ? null : new File(args.destdir)); + destdir == null ? null : new File(destdir)); if (CalciteSystemProperty.DEBUG.value()) { // Add line numbers to the generated janino class classLoader.setDebuggingInfo(true, true, true); } try { - classLoader.loadClass(args.fullClassName); + classLoader.loadClass(fullClassName); } catch (ClassNotFoundException ex) { - throw new RuntimeException("while compiling " + args.fullClassName, ex); + throw new RuntimeException("while compiling " + fullClassName, ex); } } @@ -96,12 +99,16 @@ public JaninoCompiler() { // implement JavaCompiler @Override public ClassLoader getClassLoader() { - return classLoader; + return getAccountingClassLoader(); + } + + private AccountingClassLoader getAccountingClassLoader() { + return requireNonNull(classLoader, "classLoader is null. Need to call #compile()"); } // implement JavaCompiler @Override public int getTotalByteCodeSize() { - return classLoader.getTotalByteCodeSize(); + return getAccountingClassLoader().getTotalByteCodeSize(); } //~ Inner Classes ---------------------------------------------------------- @@ -110,9 +117,9 @@ public JaninoCompiler() { * Arguments to an invocation of the Janino compiler. */ public static class JaninoCompilerArgs extends JavaCompilerArgs { - String destdir; - String fullClassName; - String source; + @Nullable String destdir; + @Nullable String fullClassName; + @Nullable String source; public JaninoCompilerArgs() { } @@ -141,14 +148,14 @@ public JaninoCompilerArgs() { * bytecode length of the classes it has compiled. */ private static class AccountingClassLoader extends JavaSourceClassLoader { - private final File destDir; + private final @Nullable File destDir; private int nBytes; AccountingClassLoader( ClassLoader parentClassLoader, ResourceFinder sourceFinder, - String optionalCharacterEncoding, - File destDir) { + @Nullable String optionalCharacterEncoding, + @Nullable File destDir) { super( parentClassLoader, sourceFinder, @@ -160,7 +167,7 @@ int getTotalByteCodeSize() { return nBytes; } - @Override public Map generateBytecodes(String name) + @Override public @Nullable Map generateBytecodes(String name) throws ClassNotFoundException { final Map map = super.generateBytecodes(name); if (map == null) { diff --git a/core/src/main/java/org/apache/calcite/util/javac/JavaCompilerArgs.java b/core/src/main/java/org/apache/calcite/util/javac/JavaCompilerArgs.java index 0c3287dc0155..6b93d7310f9f 100644 --- a/core/src/main/java/org/apache/calcite/util/javac/JavaCompilerArgs.java +++ b/core/src/main/java/org/apache/calcite/util/javac/JavaCompilerArgs.java @@ -20,6 +20,8 @@ import java.util.List; import java.util.StringTokenizer; +import static java.util.Objects.requireNonNull; + /** * A JavaCompilerArgs holds the arguments for a * {@link JavaCompiler}. @@ -40,7 +42,8 @@ public class JavaCompilerArgs { //~ Constructors ----------------------------------------------------------- public JavaCompilerArgs() { - classLoader = getClass().getClassLoader(); + classLoader = requireNonNull(getClass().getClassLoader(), + () -> "getClassLoader is null for " + getClass()); } //~ Methods ---------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/mapping/AbstractSourceMapping.java b/core/src/main/java/org/apache/calcite/util/mapping/AbstractSourceMapping.java index 84ac5b57ad50..805bda3c725a 100644 --- a/core/src/main/java/org/apache/calcite/util/mapping/AbstractSourceMapping.java +++ b/core/src/main/java/org/apache/calcite/util/mapping/AbstractSourceMapping.java @@ -59,6 +59,7 @@ protected AbstractSourceMapping(int sourceCount, int targetCount) { return MappingType.INVERSE_PARTIAL_FUNCTION; } + @SuppressWarnings("method.invocation.invalid") @Override public Iterator iterator() { return new Iterator() { int source; diff --git a/core/src/main/java/org/apache/calcite/util/mapping/AbstractTargetMapping.java b/core/src/main/java/org/apache/calcite/util/mapping/AbstractTargetMapping.java index 202fa9e28537..c9b7b9103c5c 100644 --- a/core/src/main/java/org/apache/calcite/util/mapping/AbstractTargetMapping.java +++ b/core/src/main/java/org/apache/calcite/util/mapping/AbstractTargetMapping.java @@ -59,6 +59,7 @@ protected AbstractTargetMapping(int sourceCount, int targetCount) { return MappingType.PARTIAL_FUNCTION; } + @SuppressWarnings("method.invocation.invalid") @Override public Iterator iterator() { return new Iterator() { int source = -1; diff --git a/core/src/main/java/org/apache/calcite/util/mapping/IntPair.java b/core/src/main/java/org/apache/calcite/util/mapping/IntPair.java index 4abf84f2a494..e73bc47fa8cf 100644 --- a/core/src/main/java/org/apache/calcite/util/mapping/IntPair.java +++ b/core/src/main/java/org/apache/calcite/util/mapping/IntPair.java @@ -22,6 +22,8 @@ import com.google.common.base.Function; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractList; import java.util.Comparator; import java.util.List; @@ -95,7 +97,7 @@ public static IntPair of(int left, int right) { return source + "-" + target; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { if (obj instanceof IntPair) { IntPair that = (IntPair) obj; return (this.source == that.source) && (this.target == that.target); diff --git a/core/src/main/java/org/apache/calcite/util/mapping/Mappings.java b/core/src/main/java/org/apache/calcite/util/mapping/Mappings.java index d0d70b812d6e..a218e95b76af 100644 --- a/core/src/main/java/org/apache/calcite/util/mapping/Mappings.java +++ b/core/src/main/java/org/apache/calcite/util/mapping/Mappings.java @@ -23,6 +23,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.primitives.Ints; +import com.google.errorprone.annotations.CheckReturnValue; + +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.AbstractList; import java.util.ArrayList; @@ -310,10 +314,12 @@ public static List permute(final List list, * {@code mapping.getSourceCount()}. * *

    Converse of {@link #target(List, int)}

    + * @see #asListNonNull(TargetMapping) */ - public static List asList(final TargetMapping mapping) { - return new AbstractList() { - @Override public Integer get(int source) { + @CheckReturnValue + public static List<@Nullable Integer> asList(final TargetMapping mapping) { + return new AbstractList<@Nullable Integer>() { + @Override public @Nullable Integer get(int source) { int target = mapping.getTargetOpt(source); return target < 0 ? null : target; } @@ -334,6 +340,7 @@ public static List asList(final TargetMapping mapping) { *

    Converse of {@link #target(List, int)}

    * @see #asList(TargetMapping) */ + @CheckReturnValue public static List asListNonNull(final TargetMapping mapping) { return new AbstractList() { @Override public Integer get(int source) { @@ -368,7 +375,7 @@ public static TargetMapping target( } public static TargetMapping target( - IntFunction function, + IntFunction function, int sourceCount, int targetCount) { final PartialFunctionImpl mapping = @@ -652,7 +659,7 @@ public static TargetMapping offsetSource( throw new IllegalArgumentException("new source count too low"); } return target( - (IntFunction) source -> { + (IntFunction<@Nullable Integer>) source -> { int source2 = source - offset; return source2 < 0 || source2 >= mapping.getSourceCount() ? null @@ -696,7 +703,7 @@ public static TargetMapping offsetTarget( throw new IllegalArgumentException("new target count too low"); } return target( - (IntFunction) source -> { + (IntFunction<@Nullable Integer>) source -> { int target = mapping.getTargetOpt(source); return target < 0 ? null : target + offset; }, @@ -724,7 +731,7 @@ public static TargetMapping offset( throw new IllegalArgumentException("new source count too low"); } return target( - (IntFunction) source -> { + (IntFunction<@Nullable Integer>) source -> { final int source2 = source - offset; if (source2 < 0 || source2 >= mapping.getSourceCount()) { return null; @@ -1047,7 +1054,7 @@ public abstract static class FiniteAbstractMapping extends AbstractMapping { return toString().hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { // not very efficient return (obj instanceof Mapping) && toString().equals(obj.toString()); @@ -1340,7 +1347,9 @@ private class MappingItr implements Iterator { return i < targets.length; } - private void advance() { + private void advance( + @UnknownInitialization MappingItr this + ) { do { ++i; } while (i < targets.length && targets[i] == -1); @@ -1699,6 +1708,7 @@ private static class PartialFunctionImpl extends AbstractMapping return size; } + @SuppressWarnings("method.invocation.invalid") @Override public Iterator iterator() { return new Iterator() { int i = -1; diff --git a/core/src/main/java/org/apache/calcite/util/trace/CalciteLogger.java b/core/src/main/java/org/apache/calcite/util/trace/CalciteLogger.java index 8cb1c504e4ad..06a9f787d538 100644 --- a/core/src/main/java/org/apache/calcite/util/trace/CalciteLogger.java +++ b/core/src/main/java/org/apache/calcite/util/trace/CalciteLogger.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.util.trace; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; /** @@ -62,7 +63,7 @@ public CalciteLogger(Logger logger) { /** * Logs a WARN message with two Object parameters. */ - public void warn(String format, Object arg1, Object arg2) { + public void warn(String format, @Nullable Object arg1, @Nullable Object arg2) { // slf4j already avoids the array creation for 1 or 2 arg invocations logger.warn(format, arg1, arg2); } @@ -70,7 +71,8 @@ public void warn(String format, Object arg1, Object arg2) { /** * Conditionally logs a WARN message with three Object parameters. */ - public void warn(String format, Object arg1, Object arg2, Object arg3) { + public void warn(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3) { if (logger.isWarnEnabled()) { logger.warn(format, arg1, arg2, arg3); } @@ -79,7 +81,8 @@ public void warn(String format, Object arg1, Object arg2, Object arg3) { /** * Conditionally logs a WARN message with four Object parameters. */ - public void warn(String format, Object arg1, Object arg2, Object arg3, Object arg4) { + public void warn(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3, @Nullable Object arg4) { if (logger.isWarnEnabled()) { logger.warn(format, arg1, arg2, arg3, arg4); } @@ -96,7 +99,7 @@ public void warn(String format, Object... args) { /** * Logs an INFO message with two Object parameters. */ - public void info(String format, Object arg1, Object arg2) { + public void info(String format, @Nullable Object arg1, @Nullable Object arg2) { // slf4j already avoids the array creation for 1 or 2 arg invocations logger.info(format, arg1, arg2); } @@ -104,7 +107,8 @@ public void info(String format, Object arg1, Object arg2) { /** * Conditionally logs an INFO message with three Object parameters. */ - public void info(String format, Object arg1, Object arg2, Object arg3) { + public void info(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3) { if (logger.isInfoEnabled()) { logger.info(format, arg1, arg2, arg3); } @@ -113,7 +117,8 @@ public void info(String format, Object arg1, Object arg2, Object arg3) { /** * Conditionally logs an INFO message with four Object parameters. */ - public void info(String format, Object arg1, Object arg2, Object arg3, Object arg4) { + public void info(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3, @Nullable Object arg4) { if (logger.isInfoEnabled()) { logger.info(format, arg1, arg2, arg3, arg4); } @@ -130,7 +135,7 @@ public void info(String format, Object... args) { /** * Logs a DEBUG message with two Object parameters. */ - public void debug(String format, Object arg1, Object arg2) { + public void debug(String format, @Nullable Object arg1, @Nullable Object arg2) { // slf4j already avoids the array creation for 1 or 2 arg invocations logger.debug(format, arg1, arg2); } @@ -138,7 +143,8 @@ public void debug(String format, Object arg1, Object arg2) { /** * Conditionally logs a DEBUG message with three Object parameters. */ - public void debug(String format, Object arg1, Object arg2, Object arg3) { + public void debug(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3) { if (logger.isDebugEnabled()) { logger.debug(format, arg1, arg2, arg3); } @@ -147,7 +153,8 @@ public void debug(String format, Object arg1, Object arg2, Object arg3) { /** * Conditionally logs a DEBUG message with four Object parameters. */ - public void debug(String format, Object arg1, Object arg2, Object arg3, Object arg4) { + public void debug(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3, @Nullable Object arg4) { if (logger.isDebugEnabled()) { logger.debug(format, arg1, arg2, arg3, arg4); } @@ -164,7 +171,7 @@ public void debug(String format, Object... args) { /** * Logs a TRACE message with two Object parameters. */ - public void trace(String format, Object arg1, Object arg2) { + public void trace(String format, @Nullable Object arg1, @Nullable Object arg2) { // slf4j already avoids the array creation for 1 or 2 arg invocations logger.trace(format, arg1, arg2); } @@ -172,7 +179,8 @@ public void trace(String format, Object arg1, Object arg2) { /** * Conditionally logs a TRACE message with three Object parameters. */ - public void trace(String format, Object arg1, Object arg2, Object arg3) { + public void trace(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3) { if (logger.isTraceEnabled()) { logger.trace(format, arg1, arg2, arg3); } @@ -181,13 +189,14 @@ public void trace(String format, Object arg1, Object arg2, Object arg3) { /** * Conditionally logs a TRACE message with four Object parameters. */ - public void trace(String format, Object arg1, Object arg2, Object arg3, Object arg4) { + public void trace(String format, @Nullable Object arg1, @Nullable Object arg2, + @Nullable Object arg3, @Nullable Object arg4) { if (logger.isTraceEnabled()) { logger.trace(format, arg1, arg2, arg3, arg4); } } - public void trace(String format, Object... args) { + public void trace(String format, @Nullable Object... args) { if (logger.isTraceEnabled()) { logger.trace(format, args); } diff --git a/core/src/main/java/org/apache/calcite/util/trace/CalciteTimingTracer.java b/core/src/main/java/org/apache/calcite/util/trace/CalciteTimingTracer.java index ad5ade2768c9..26101a3f572a 100644 --- a/core/src/main/java/org/apache/calcite/util/trace/CalciteTimingTracer.java +++ b/core/src/main/java/org/apache/calcite/util/trace/CalciteTimingTracer.java @@ -18,6 +18,7 @@ import org.apache.calcite.util.NumberUtil; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import java.text.DecimalFormat; @@ -34,7 +35,7 @@ public class CalciteTimingTracer { //~ Instance fields -------------------------------------------------------- - private final Logger logger; + private final @Nullable Logger logger; private long lastNanoTime; diff --git a/core/src/main/java/org/apache/calcite/util/trace/CalciteTrace.java b/core/src/main/java/org/apache/calcite/util/trace/CalciteTrace.java index bf94a6f09f8a..e2b0fcfbbd8b 100644 --- a/core/src/main/java/org/apache/calcite/util/trace/CalciteTrace.java +++ b/core/src/main/java/org/apache/calcite/util/trace/CalciteTrace.java @@ -23,6 +23,7 @@ import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.prepare.Prepare; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +57,7 @@ public abstract class CalciteTrace { */ public static final Logger PARSER_LOGGER = getParserTracer(); - private static final ThreadLocal> DYNAMIC_HANDLER = + private static final ThreadLocal<@Nullable Function2> DYNAMIC_HANDLER = ThreadLocal.withInitial(Functions::ignore2); //~ Methods ---------------------------------------------------------------- @@ -145,7 +146,7 @@ public static Logger getTestTracer(Class testClass) { * It exists for unit-testing. * The handler is never null; the default handler does nothing. */ - public static ThreadLocal> getDynamicHandler() { + public static ThreadLocal<@Nullable Function2> getDynamicHandler() { return DYNAMIC_HANDLER; } } diff --git a/core/src/test/java/org/apache/calcite/adapter/generate/RangeTable.java b/core/src/test/java/org/apache/calcite/adapter/generate/RangeTable.java index c62ea21ed929..46eb8afd74e0 100644 --- a/core/src/test/java/org/apache/calcite/adapter/generate/RangeTable.java +++ b/core/src/test/java/org/apache/calcite/adapter/generate/RangeTable.java @@ -27,6 +27,8 @@ import org.apache.calcite.schema.impl.AbstractTableQueryable; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; import java.util.NoSuchElementException; @@ -102,7 +104,7 @@ public RangeTable create( SchemaPlus schema, String name, Map operand, - RelDataType rowType) { + @Nullable RelDataType rowType) { final String columnName = (String) operand.get("column"); final int start = (Integer) operand.get("start"); final int end = (Integer) operand.get("end"); diff --git a/core/src/test/java/org/apache/calcite/plan/volcano/CollationConversionTest.java b/core/src/test/java/org/apache/calcite/plan/volcano/CollationConversionTest.java index 02caab20d3ff..dc18a6d65881 100644 --- a/core/src/test/java/org/apache/calcite/plan/volcano/CollationConversionTest.java +++ b/core/src/test/java/org/apache/calcite/plan/volcano/CollationConversionTest.java @@ -37,6 +37,7 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.util.List; @@ -258,7 +259,7 @@ public RelCollation getDefault() { return LEAF_COLLATION; } - public RelNode convert(RelOptPlanner planner, RelNode rel, + public @Nullable RelNode convert(RelOptPlanner planner, RelNode rel, RelCollation toCollation, boolean allowInfiniteCostConverters) { if (toCollation.getFieldCollations().isEmpty()) { // An empty sort doesn't make sense. diff --git a/core/src/test/java/org/apache/calcite/plan/volcano/TraitConversionTest.java b/core/src/test/java/org/apache/calcite/plan/volcano/TraitConversionTest.java index 0906a1e0dc27..c2485e850031 100644 --- a/core/src/test/java/org/apache/calcite/plan/volcano/TraitConversionTest.java +++ b/core/src/test/java/org/apache/calcite/plan/volcano/TraitConversionTest.java @@ -30,6 +30,7 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.util.List; @@ -257,7 +258,7 @@ private static class ConvertRelDistributionTraitDef return "ConvertRelDistributionTraitDef"; } - @Override public RelNode convert(RelOptPlanner planner, RelNode rel, + @Override public @Nullable RelNode convert(RelOptPlanner planner, RelNode rel, SimpleDistribution toTrait, boolean allowInfiniteCostConverters) { if (toTrait == SIMPLE_DISTRIBUTION_ANY) { return rel; diff --git a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java index 39b339956c6f..8c8b12dd5eeb 100644 --- a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java +++ b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java @@ -74,6 +74,7 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.sql.Connection; @@ -348,7 +349,7 @@ private static class PhysAgg extends Aggregate implements Phys { public Aggregate copy(RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, - List groupSets, List aggCalls) { + @Nullable List groupSets, List aggCalls) { return new PhysAgg(getCluster(), traitSet, input, groupSet, groupSets, aggCalls); } diff --git a/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTraitTest.java b/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTraitTest.java index 174beff933eb..82eb7f666d26 100644 --- a/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTraitTest.java +++ b/core/src/test/java/org/apache/calcite/plan/volcano/VolcanoPlannerTraitTest.java @@ -44,6 +44,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -322,7 +323,7 @@ public AltTrait getDefault() { return ALT_TRAIT; } - public RelNode convert( + public @Nullable RelNode convert( RelOptPlanner planner, RelNode rel, AltTrait toTrait, diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterStructsTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterStructsTest.java index 7f9740c242ea..e70699284ae7 100644 --- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterStructsTest.java +++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterStructsTest.java @@ -37,6 +37,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.util.Collection; @@ -81,7 +82,7 @@ class RelToSqlConverterStructsTest { return ImmutableSet.of(); } - @Override public Expression getExpression(SchemaPlus parentSchema, String name) { + @Override public Expression getExpression(@Nullable SchemaPlus parentSchema, String name) { return null; } @@ -145,8 +146,8 @@ class RelToSqlConverterStructsTest { @Override public boolean rolledUpColumnValidInsideAgg( String column, SqlCall call, - SqlNode parent, - CalciteConnectionConfig config) { + @Nullable SqlNode parent, + @Nullable CalciteConnectionConfig config) { return false; } }; diff --git a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java index a8a8226fad76..e59611c68d5b 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java +++ b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java @@ -42,6 +42,7 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; @@ -402,15 +403,15 @@ public SchemaPlus getRootSchema() { throw new RuntimeException("Unsupported"); } - public JavaTypeFactory getTypeFactory() { + public @Nullable JavaTypeFactory getTypeFactory() { throw new RuntimeException("Unsupported"); } - public QueryProvider getQueryProvider() { + public @Nullable QueryProvider getQueryProvider() { throw new RuntimeException("Unsupported"); } - public Object get(String name) { + public @Nullable Object get(String name) { if (this.name.equals(name)) { return value; } else { diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramBuilderBase.java b/core/src/test/java/org/apache/calcite/rex/RexProgramBuilderBase.java index 603554492d2b..2516ce4c3c1e 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexProgramBuilderBase.java +++ b/core/src/test/java/org/apache/calcite/rex/RexProgramBuilderBase.java @@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.BeforeEach; import java.math.BigDecimal; @@ -103,15 +104,15 @@ public SchemaPlus getRootSchema() { return null; } - public JavaTypeFactory getTypeFactory() { + public @Nullable JavaTypeFactory getTypeFactory() { return null; } - public QueryProvider getQueryProvider() { + public @Nullable QueryProvider getQueryProvider() { return null; } - public Object get(String name) { + public @Nullable Object get(String name) { return map.get(name); } } diff --git a/core/src/test/java/org/apache/calcite/schemas/HrClusteredSchema.java b/core/src/test/java/org/apache/calcite/schemas/HrClusteredSchema.java index 32c2cac068bb..08a644304f58 100644 --- a/core/src/test/java/org/apache/calcite/schemas/HrClusteredSchema.java +++ b/core/src/test/java/org/apache/calcite/schemas/HrClusteredSchema.java @@ -34,6 +34,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -119,7 +121,7 @@ private static class PkClusteredTable extends AbstractTable implements Scannable return typeBuilder.apply(typeFactory); } - @Override public Enumerable scan(final DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(final DataContext root) { return Linq4j.asEnumerable(data); } diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java index 45c5641e72ba..99f174ef119b 100644 --- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java +++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java @@ -70,7 +70,6 @@ import java.util.function.Consumer; import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; @@ -668,7 +667,7 @@ public void describeTo(Description description) { /** Returns a {@link Matcher} that succeeds if the given {@link SqlNode} is a * VALUES that contains a ROW that contains an identifier whose {@code i}th * element is quoted. */ - @Nonnull private static Matcher isQuoted(final int i, + private static Matcher isQuoted(final int i, final boolean quoted) { return new CustomTypeSafeMatcher("quoting") { protected boolean matchesSafely(SqlNode item) { @@ -7278,7 +7277,7 @@ public void subTestIntervalSecondFailsValidation() { .ok("INTERVAL '1:1' MINUTE TO SECOND"); } - @Nonnull private Consumer> checkWarnings( + private Consumer> checkWarnings( String... tokens) { final List messages = new ArrayList<>(); for (String token : tokens) { diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java index 15dfbc83e7b4..8eaf03a646a5 100644 --- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java +++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java @@ -82,6 +82,7 @@ import net.hydromatic.scott.data.hsqldb.ScottHsqldb; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hamcrest.Matcher; import java.lang.reflect.InvocationTargetException; @@ -111,8 +112,6 @@ import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import javax.sql.DataSource; import static org.apache.calcite.test.Matchers.compose; @@ -1736,7 +1735,7 @@ public AssertQuery planUpdateHasSql(String expected, int count) { return planContains(checkUpdateCount(count), JavaSql.fromSql(expected)); } - @Nonnull private AssertQuery planContains(Consumer checkUpdate, + private AssertQuery planContains(Consumer checkUpdate, JavaSql expected) { ensurePlan(checkUpdate); if (expected.sql != null) { @@ -2190,7 +2189,7 @@ public List extractSql() { return unwrap(java); } - static @Nonnull List unwrap(String java) { + static List unwrap(String java) { final List sqlList = new ArrayList<>(); final StringBuilder b = new StringBuilder(); hLoop: diff --git a/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java b/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java index b95a69a31b8a..06c0678cde18 100644 --- a/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java +++ b/core/src/test/java/org/apache/calcite/test/CollectionTypeTest.java @@ -35,6 +35,7 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.sql.Connection; @@ -395,7 +396,7 @@ public Schema.TableType getJdbcTableType() { return Schema.TableType.TABLE; } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return new AbstractEnumerable() { public Enumerator enumerator() { return nestedRecordsEnumerator(); @@ -407,9 +408,9 @@ public Enumerator enumerator() { return false; } - @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, - CalciteConnectionConfig config) { + @Override public boolean rolledUpColumnValidInsideAgg( + String column, SqlCall call, @Nullable SqlNode parent, + @Nullable CalciteConnectionConfig config) { return false; } } @@ -435,7 +436,7 @@ public Schema.TableType getJdbcTableType() { return Schema.TableType.TABLE; } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return new AbstractEnumerable() { public Enumerator enumerator() { return nestedRecordsEnumerator(); @@ -448,7 +449,7 @@ public Enumerator enumerator() { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } } diff --git a/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java b/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java index 8740a6823d54..dab24ef9bfe1 100644 --- a/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java +++ b/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java @@ -33,6 +33,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** A table function that returns all countries in the world. * *

    Has same content as @@ -290,7 +292,7 @@ private CountriesTableFunction() {} public static ScannableTable eval(boolean b) { return new ScannableTable() { - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return Linq4j.asEnumerable(ROWS); }; @@ -317,7 +319,7 @@ public boolean isRolledUp(String column) { } public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } }; diff --git a/core/src/test/java/org/apache/calcite/test/InterpreterTest.java b/core/src/test/java/org/apache/calcite/test/InterpreterTest.java index 8d0d5b72f1b4..66852c93fa1c 100644 --- a/core/src/test/java/org/apache/calcite/test/InterpreterTest.java +++ b/core/src/test/java/org/apache/calcite/test/InterpreterTest.java @@ -37,6 +37,7 @@ import org.apache.calcite.tools.ValidationException; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -70,15 +71,15 @@ public SchemaPlus getRootSchema() { return rootSchema; } - public JavaTypeFactory getTypeFactory() { + public @Nullable JavaTypeFactory getTypeFactory() { return (JavaTypeFactory) planner.getTypeFactory(); } - public QueryProvider getQueryProvider() { + public @Nullable QueryProvider getQueryProvider() { return null; } - public Object get(String name) { + public @Nullable Object get(String name) { return null; } } diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java index d01a09ef7647..6bc6abe04857 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java @@ -99,6 +99,7 @@ import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hamcrest.Matcher; import org.hamcrest.comparator.ComparatorMatcherBuilder; import org.hsqldb.jdbcDriver; @@ -8073,7 +8074,7 @@ public Table create( SchemaPlus schema, String name, Map operand, - RelDataType rowType) { + @Nullable RelDataType rowType) { final Class clazz; final Object[] array; switch (name) { diff --git a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java index cc5ecbaa6907..8b16d142897a 100644 --- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java +++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java @@ -41,6 +41,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -370,7 +371,7 @@ List> run(RelNode input) { return names; } - @Override public void visit(RelNode node, int ordinal, RelNode parent) { + @Override public void visit(RelNode node, int ordinal, @Nullable RelNode parent) { if (node instanceof TableScan) { RelOptTable table = node.getTable(); List qName = table.getQualifiedName(); diff --git a/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java b/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java index a357fceb1ae2..a1b0e84476a9 100644 --- a/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java +++ b/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -66,7 +68,7 @@ public void setRoot(RelNode rel) { } // implement RelOptPlanner - public RelNode getRoot() { + public @Nullable RelNode getRoot() { return root; } @@ -188,7 +190,7 @@ private boolean match( // implement RelOptPlanner public RelNode register( RelNode rel, - RelNode equivRel) { + @Nullable RelNode equivRel) { return rel; } diff --git a/core/src/test/java/org/apache/calcite/test/ModelTest.java b/core/src/test/java/org/apache/calcite/test/ModelTest.java index 4f54da7f88ac..43c569bf1fe1 100644 --- a/core/src/test/java/org/apache/calcite/test/ModelTest.java +++ b/core/src/test/java/org/apache/calcite/test/ModelTest.java @@ -77,6 +77,7 @@ private ObjectMapper mapper() { + " tables: [\n" + " {\n" + " name: 'time_by_day',\n" + + " factory: 'com.test',\n" + " columns: [\n" + " {\n" + " name: 'time_id'\n" @@ -85,6 +86,7 @@ private ObjectMapper mapper() { + " },\n" + " {\n" + " name: 'sales_fact_1997',\n" + + " factory: 'com.test',\n" + " columns: [\n" + " {\n" + " name: 'time_id'\n" @@ -151,13 +153,14 @@ private ObjectMapper mapper() { + " factory: 'com.acme.MySchemaFactory',\n" + " operand: {a: 'foo', b: [1, 3.5] },\n" + " tables: [\n" - + " { type: 'custom', name: 'T1' },\n" - + " { type: 'custom', name: 'T2', operand: {} },\n" - + " { type: 'custom', name: 'T3', operand: {a: 'foo'} }\n" + + " { type: 'custom', name: 'T1', factory: 'com.test' },\n" + + " { type: 'custom', name: 'T2', factory: 'com.test', operand: {} },\n" + + " { type: 'custom', name: 'T3', factory: 'com.test', operand: {a: 'foo'} }\n" + " ]\n" + " },\n" + " {\n" + " type: 'custom',\n" + + " factory: 'com.acme.MySchemaFactory',\n" + " name: 'has-no-operand'\n" + " }\n" + " ]\n" @@ -230,7 +233,7 @@ private ObjectMapper mapper() { + " } ]\n" + "}"; CalciteAssert.model(model) - .connectThrows("Field 'name' is required in JsonMapSchema"); + .connectThrows("Missing required creator property 'name'"); } @Test void testCustomSchemaWithoutFactory() throws Exception { @@ -243,7 +246,7 @@ private ObjectMapper mapper() { + " } ]\n" + "}"; CalciteAssert.model(model) - .connectThrows("Field 'factory' is required in JsonCustomSchema"); + .connectThrows("Missing required creator property 'factory'"); } /** Tests a model containing a lattice and some views. */ @@ -257,6 +260,7 @@ private ObjectMapper mapper() { + " tables: [\n" + " {\n" + " name: 'time_by_day',\n" + + " factory: 'com.test',\n" + " columns: [\n" + " {\n" + " name: 'time_id'\n" @@ -265,6 +269,7 @@ private ObjectMapper mapper() { + " },\n" + " {\n" + " name: 'sales_fact_1997',\n" + + " factory: 'com.test',\n" + " columns: [\n" + " {\n" + " name: 'time_id'\n" diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java index 024f1f163cc2..0cf0ed7bc1a0 100644 --- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java @@ -2853,7 +2853,7 @@ private RelNode buildRelWithDuplicateAggregates( fail("expected error, got " + root); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), - is("All values of field 'b' are null; cannot deduce type")); + is("All values of field 'b' (field index 1) are null; cannot deduce type")); } } diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java index bfda2047f769..adbd68da1cf0 100644 --- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java @@ -115,6 +115,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.NonNull; import org.hamcrest.CoreMatchers; import org.hamcrest.Matcher; import org.junit.jupiter.api.Disabled; @@ -136,7 +137,6 @@ import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; -import javax.annotation.Nonnull; import static org.apache.calcite.test.Matchers.within; @@ -1338,7 +1338,7 @@ private static ImmutableBitSet bitSetOf(int... bits) { convertProjectAsCalc()); } - @Nonnull + @NonNull private Function convertProjectAsCalc() { return s -> { Project project = (Project) convertSql(s); diff --git a/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java b/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java index c3bbf8d8c306..6294931a260d 100644 --- a/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java +++ b/core/src/test/java/org/apache/calcite/test/ScannableTableTest.java @@ -44,6 +44,7 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.math.BigDecimal; @@ -415,7 +416,7 @@ private Enumerable superScan(DataContext root) { return super.scan(root); } - @Override public Enumerable + @Override public Enumerable<@Nullable Object[]> scan(final DataContext root) { scanCount.incrementAndGet(); return new AbstractEnumerable() { @@ -506,7 +507,7 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { .build(); } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return new AbstractEnumerable() { public Enumerator enumerator() { return tens(); @@ -526,7 +527,7 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { .build(); } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return new AbstractEnumerable() { public Enumerator enumerator() { return beatles(new StringBuilder(), null, null); @@ -555,7 +556,7 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { .build(); } - public Enumerable scan(DataContext root, List filters) { + public Enumerable<@Nullable Object[]> scan(DataContext root, List filters) { final Pair filter = getFilter(cooperative, filters); return new AbstractEnumerable() { public Enumerator enumerator() { @@ -586,8 +587,8 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { .build(); } - public Enumerable scan(DataContext root, List filters, - final int[] projects) { + public Enumerable<@Nullable Object[]> scan(DataContext root, List filters, + final int @Nullable [] projects) { final Pair filter = getFilter(cooperative, filters); return new AbstractEnumerable() { public Enumerator enumerator() { diff --git a/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java index 5b48fb3bbebb..4deb4e7b6372 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlHintsConverterTest.java @@ -66,6 +66,7 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -580,7 +581,7 @@ private static class ValidateHintVisitor extends RelVisitor { @Override public void visit( RelNode node, int ordinal, - RelNode parent) { + @Nullable RelNode parent) { if (clazz.isInstance(node)) { Hintable rel = (Hintable) node; assertThat(rel.getHints().size(), is(1)); diff --git a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java index 0b019d2628f7..79b4df1f9f04 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java @@ -44,7 +44,6 @@ import java.util.Objects; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; @@ -61,6 +60,11 @@ class SqlJsonFunctionsTest { is(JsonFunctions.JsonValueContext.withJavaObj(Collections.emptyMap()))); } + @Test void testJsonNullExpression() { + assertJsonValueExpression("null", + is(JsonFunctions.JsonValueContext.withJavaObj(null))); + } + @Test void testJsonApiCommonSyntax() { assertJsonApiCommonSyntax("{\"foo\": \"bar\"}", "$.foo", contextMatches( @@ -895,7 +899,7 @@ private Matcher errorMatches(Throwable expected) { }; } - @Nonnull private BaseMatcher contextMatches( + private BaseMatcher contextMatches( JsonFunctions.JsonPathContext expected) { return new BaseMatcher() { @Override public boolean matches(Object item) { diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java index 7a384e3ed30c..97f2377abe88 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java @@ -56,6 +56,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -4158,7 +4159,7 @@ public Set correlationIds() { return builder.build(); } - public void visit(RelNode node, int ordinal, RelNode parent) { + public void visit(RelNode node, int ordinal, @Nullable RelNode parent) { try { stack.push(node); if (!node.isValid(Litmus.THROW, this)) { diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java index 53e834639d93..8554281c9840 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java @@ -45,7 +45,6 @@ import java.nio.charset.Charset; import java.util.Objects; import java.util.function.UnaryOperator; -import javax.annotation.Nonnull; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -324,7 +323,7 @@ Sql ok() { /** * Checks that a SQL expression gives a particular error. */ - Sql fails(@Nonnull String expected) { + Sql fails(String expected) { Objects.requireNonNull(expected); tester.assertExceptionIsThrown(toSql(true), expected); return this; diff --git a/core/src/test/java/org/apache/calcite/test/StatesTableFunction.java b/core/src/test/java/org/apache/calcite/test/StatesTableFunction.java index 2d72fd0ec771..3d40787ab03d 100644 --- a/core/src/test/java/org/apache/calcite/test/StatesTableFunction.java +++ b/core/src/test/java/org/apache/calcite/test/StatesTableFunction.java @@ -33,6 +33,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** A table function that returns states and their boundaries; also national * parks. * @@ -88,7 +90,7 @@ public static ScannableTable parks(boolean b) { private static ScannableTable eval(final Object[][] rows) { return new ScannableTable() { - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return Linq4j.asEnumerable(rows); } @@ -113,7 +115,7 @@ public boolean isRolledUp(String column) { } public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } }; diff --git a/core/src/test/java/org/apache/calcite/test/StreamTest.java b/core/src/test/java/org/apache/calcite/test/StreamTest.java index 11271d72fde2..b106958adfd6 100644 --- a/core/src/test/java/org/apache/calcite/test/StreamTest.java +++ b/core/src/test/java/org/apache/calcite/test/StreamTest.java @@ -41,6 +41,7 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hamcrest.comparator.ComparatorMatcherBuilder; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -377,7 +378,7 @@ public Schema.TableType getJdbcTableType() { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } } @@ -390,7 +391,7 @@ public OrdersStreamTableFactory() { } public Table create(SchemaPlus schema, String name, - Map operand, RelDataType rowType) { + Map operand, @Nullable RelDataType rowType) { return new OrdersTable(getRowList()); } @@ -419,7 +420,7 @@ public OrdersTable(ImmutableList rows) { this.rows = rows; } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return Linq4j.asEnumerable(rows); } @@ -432,7 +433,7 @@ public Enumerable scan(DataContext root) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } } @@ -447,7 +448,7 @@ public InfiniteOrdersStreamTableFactory() { } public Table create(SchemaPlus schema, String name, - Map operand, RelDataType rowType) { + Map operand, @Nullable RelDataType rowType) { return new InfiniteOrdersTable(); } } @@ -457,7 +458,7 @@ public Table create(SchemaPlus schema, String name, */ public static class InfiniteOrdersTable extends BaseOrderStreamTable implements StreamableTable { - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return Linq4j.asEnumerable(() -> new Iterator() { private final String[] items = {"paint", "paper", "brush"}; private int counter = 0; @@ -491,7 +492,7 @@ public OrdersHistoryTable(ImmutableList rows) { this.rows = rows; } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return Linq4j.asEnumerable(rows); } } @@ -501,7 +502,7 @@ public Enumerable scan(DataContext root) { */ public static class ProductsTableFactory implements TableFactory { public Table create(SchemaPlus schema, String name, - Map operand, RelDataType rowType) { + Map operand, @Nullable RelDataType rowType) { final Object[][] rows = { {"paint", 1}, {"paper", 0}, @@ -526,7 +527,7 @@ public ProductsTable(ImmutableList rows) { .add("SUPPLIER", SqlTypeName.INTEGER) .build(); - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return Linq4j.asEnumerable(rows); } @@ -547,7 +548,7 @@ public Schema.TableType getJdbcTableType() { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } } @@ -589,7 +590,7 @@ public static class ProductsTemporalTable implements TemporalTable { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return false; } } diff --git a/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReader.java b/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReader.java index 93dc2c2b8c88..553b1c3fd0b8 100644 --- a/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReader.java +++ b/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReader.java @@ -94,6 +94,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.AbstractList; @@ -436,7 +438,7 @@ protected ModifiableTable(String tableName) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { // For testing return call.getKind() != SqlKind.MAX && (parent.getKind() == SqlKind.SELECT || parent.getKind() == SqlKind.FILTER); @@ -1101,7 +1103,7 @@ public RelDistribution getDistribution() { } @Override public boolean rolledUpColumnValidInsideAgg(String column, - SqlCall call, SqlNode parent, CalciteConnectionConfig config) { + SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { // For testing return call.getKind() != SqlKind.MAX && (parent.getKind() == SqlKind.SELECT || parent.getKind() == SqlKind.FILTER); diff --git a/core/src/test/java/org/apache/calcite/test/fuzzer/RexProgramFuzzyTest.java b/core/src/test/java/org/apache/calcite/test/fuzzer/RexProgramFuzzyTest.java index 41f95f0e4b10..4b38a675c4f1 100644 --- a/core/src/test/java/org/apache/calcite/test/fuzzer/RexProgramFuzzyTest.java +++ b/core/src/test/java/org/apache/calcite/test/fuzzer/RexProgramFuzzyTest.java @@ -44,7 +44,6 @@ import java.util.PriorityQueue; import java.util.Random; import java.util.Set; -import javax.annotation.Nonnull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -312,7 +311,7 @@ private void checkUnknownAs(RexNode node, RexUnknownAs unknownAs) { } } - @Nonnull private String unknownAsString(RexUnknownAs unknownAs) { + private String unknownAsString(RexUnknownAs unknownAs) { switch (unknownAs) { case UNKNOWN: default: diff --git a/core/src/test/java/org/apache/calcite/tools/FrameworksTest.java b/core/src/test/java/org/apache/calcite/tools/FrameworksTest.java index 552e48aff42c..601ea37b5381 100644 --- a/core/src/test/java/org/apache/calcite/tools/FrameworksTest.java +++ b/core/src/test/java/org/apache/calcite/tools/FrameworksTest.java @@ -75,6 +75,7 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.lang.reflect.Type; @@ -493,8 +494,8 @@ public Statistic getStatistic() { ImmutableList.of()); } - public Enumerable scan(DataContext root, List filters, - int[] projects) { + public Enumerable<@Nullable Object[]> scan(DataContext root, List filters, + int @Nullable [] projects) { throw new UnsupportedOperationException(); } diff --git a/core/src/test/java/org/apache/calcite/util/ImmutableBeanTest.java b/core/src/test/java/org/apache/calcite/util/ImmutableBeanTest.java index bebf0c05eb74..4ecb69ac35f6 100644 --- a/core/src/test/java/org/apache/calcite/util/ImmutableBeanTest.java +++ b/core/src/test/java/org/apache/calcite/util/ImmutableBeanTest.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.NonNull; import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; @@ -32,7 +33,6 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import javax.annotation.Nonnull; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -574,12 +574,12 @@ interface Bean2 { /** Property is required because it has 'Nonnull' annotation. */ @ImmutableBeans.Property - @Nonnull String getNonnullString(); + @NonNull String getNonnullString(); Bean2 withNonnullString(String s); @ImmutableBeans.Property @ImmutableBeans.StringDefault("abc") - @Nonnull String getStringWithDefault(); + @NonNull String getStringWithDefault(); Bean2 withStringWithDefault(String s); @ImmutableBeans.Property @@ -589,7 +589,7 @@ interface Bean2 { @ImmutableBeans.Property @ImmutableBeans.EnumDefault("RED") - @Nonnull Color getColorWithDefault(); + @NonNull Color getColorWithDefault(); Bean2 withColorWithDefault(Color color); @ImmutableBeans.Property diff --git a/core/src/test/java/org/apache/calcite/util/Smalls.java b/core/src/test/java/org/apache/calcite/util/Smalls.java index 77859e15510f..03527b75b711 100644 --- a/core/src/test/java/org/apache/calcite/util/Smalls.java +++ b/core/src/test/java/org/apache/calcite/util/Smalls.java @@ -50,6 +50,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.lang.reflect.Method; import java.math.BigDecimal; @@ -245,7 +247,7 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { return typeFactory.builder().add("N", SqlTypeName.BIGINT).build(); } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { return new AbstractEnumerable() { public Enumerator enumerator() { return new Enumerator() { @@ -291,7 +293,7 @@ public boolean isRolledUp(String column) { } public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; @@ -925,7 +927,7 @@ public RelDataType getRowType(RelDataTypeFactory typeFactory) { .build(); } - public Enumerable scan(DataContext root) { + public Enumerable<@Nullable Object[]> scan(DataContext root) { Object[][] rows = {{"abcde"}, {"xyz"}, {content}}; return Linq4j.asEnumerable(rows); } diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/CeilOperatorConversion.java b/druid/src/main/java/org/apache/calcite/adapter/druid/CeilOperatorConversion.java index 3207759d6eb7..c0faeb25dcfa 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/CeilOperatorConversion.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/CeilOperatorConversion.java @@ -26,8 +26,9 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.TimeZone; -import javax.annotation.Nullable; /** * DruidSqlOperatorConverter implementation that handles Ceil operations diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DimensionSpec.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DimensionSpec.java index 5b5fb16f8a7f..333c8768b1a4 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DimensionSpec.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DimensionSpec.java @@ -16,7 +16,7 @@ */ package org.apache.calcite.adapter.druid; -import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Interface for Druid DimensionSpec. diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java index 15465cfe4038..9f1be21881b5 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java @@ -37,6 +37,7 @@ import com.google.common.collect.Range; import com.google.common.collect.TreeRangeSet; +import org.checkerframework.checker.nullness.qual.Nullable; import org.joda.time.Interval; import org.joda.time.Period; import org.joda.time.chrono.ISOChronology; @@ -44,7 +45,6 @@ import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; /** * Utilities for generating intervals from RexNode. @@ -399,8 +399,7 @@ public static String toISOPeriodFormat(Granularity.Type type) { * * @return Druid Granularity or null */ - @Nullable - public static Granularity.Type toDruidGranularity(TimeUnitRange timeUnit) { + public static Granularity.@Nullable Type toDruidGranularity(TimeUnitRange timeUnit) { if (timeUnit == null) { return null; } diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidExpressions.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidExpressions.java index 5bd22c90f483..2ad4b136675b 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidExpressions.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidExpressions.java @@ -31,13 +31,14 @@ import com.google.common.io.BaseEncoding; import com.google.common.primitives.Chars; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TimeZone; -import javax.annotation.Nullable; /** * Expression utility class to transform Calcite expressions to Druid expressions when possible. diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidJsonFilter.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidJsonFilter.java index 78447b29d463..0d4d919cf64b 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidJsonFilter.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidJsonFilter.java @@ -32,13 +32,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; -import javax.annotation.Nullable; import static org.apache.calcite.util.DateTimeStringUtils.ISO_DATETIME_FRACTIONAL_SECOND_FORMAT; import static org.apache.calcite.util.DateTimeStringUtils.getDateFormatter; diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java index 239b72cf563d..0fdb4f5ec3b4 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java @@ -73,6 +73,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Maps; +import org.checkerframework.checker.nullness.qual.Nullable; import org.joda.time.Interval; import java.io.IOException; @@ -86,8 +87,6 @@ import java.util.Set; import java.util.TimeZone; import java.util.regex.Pattern; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * Relational expression representing a scan of a Druid data set. @@ -629,7 +628,7 @@ private double getQueryTypeCostMultiplier() { return Object[].class; } - @Override public Enumerable bind(DataContext dataContext) { + @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) { return table.unwrap(ScannableTable.class).scan(dataContext); } @@ -1135,7 +1134,7 @@ protected QuerySpec getQuery(RelDataType rowType, Filter filter, Project project * @param numericCollationIndexes flag of to determine sort comparator * @param queryOutputFieldNames query output fields */ - private @Nonnull JsonLimit computeSort(@Nullable Integer fetch, + private JsonLimit computeSort(@Nullable Integer fetch, List collationIndexes, List collationDirections, ImmutableBitSet numericCollationIndexes, List queryOutputFieldNames) { @@ -1350,7 +1349,7 @@ private static class ScanQuery { this.fetchLimit = fetchLimit; } - @Nonnull public String toQuery() { + public String toQuery() { final StringWriter sw = new StringWriter(); try { final JsonFactory factory = new JsonFactory(); diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidSqlOperatorConverter.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidSqlOperatorConverter.java index 18c904804805..974cb52090fd 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidSqlOperatorConverter.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidSqlOperatorConverter.java @@ -20,7 +20,7 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.SqlOperator; -import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Defines how to convert a {@link RexNode} with a given Calcite SQL operator to diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java index 0c8c7d6b0512..1461a74bca79 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java @@ -42,6 +42,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.chrono.ISOChronology; @@ -180,7 +181,7 @@ public ComplexMetric resolveComplexMetric(String alias, AggregateCall call) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { assert isRolledUp(column); // Our rolled up columns are only allowed in COUNT(DISTINCT ...) aggregate functions. // We only allow this when approximate results are acceptable. diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java index d1fe59aa051b..77b4d2d67295 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTableFactory.java @@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.joda.time.Interval; import org.joda.time.chrono.ISOChronology; @@ -49,7 +50,7 @@ private DruidTableFactory() {} // name that is also the same name as a complex metric @Override public Table create(SchemaPlus schema, String name, Map operand, - RelDataType rowType) { + @Nullable RelDataType rowType) { final DruidSchema druidSchema = schema.unwrap(DruidSchema.class); // If "dataSource" operand is present it overrides the table name. final String dataSource = (String) operand.get("dataSource"); diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java b/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java index e5f2e517ab39..ae94ee318836 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java @@ -18,9 +18,10 @@ import com.fasterxml.jackson.core.JsonGenerator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.util.Objects; -import javax.annotation.Nullable; import static org.apache.calcite.adapter.druid.DruidQuery.writeField; import static org.apache.calcite.adapter.druid.DruidQuery.writeFieldIf; diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/FloorOperatorConversion.java b/druid/src/main/java/org/apache/calcite/adapter/druid/FloorOperatorConversion.java index 2d3c093a5f17..762d50a0e5ab 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/FloorOperatorConversion.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/FloorOperatorConversion.java @@ -24,8 +24,9 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.SqlTypeName; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.TimeZone; -import javax.annotation.Nullable; /** * DruidSqlOperatorConverter implementation that handles Floor operations diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/Granularities.java b/druid/src/main/java/org/apache/calcite/adapter/druid/Granularities.java index c0411236a71c..22e9eb17a0c1 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/Granularities.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/Granularities.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.Objects; -import javax.annotation.Nonnull; import static org.apache.calcite.adapter.druid.DruidQuery.writeFieldIf; @@ -42,7 +41,7 @@ public static Granularity all() { * *

    When used in a query, Druid will rollup and round time values based on * specified period and timezone. */ - @Nonnull public static Granularity createGranularity(TimeUnitRange timeUnit, + public static Granularity createGranularity(TimeUnitRange timeUnit, String timeZone) { switch (timeUnit) { case YEAR: @@ -75,7 +74,7 @@ private enum AllGranularity implements Granularity { generator.writeObject("all"); } - @Override @Nonnull public Type getType() { + @Override public Type getType() { return Type.ALL; } } @@ -101,7 +100,7 @@ private PeriodGranularity(Type type, String period, String timeZone) { generator.writeEndObject(); } - @Override @Nonnull public Type getType() { + @Override public Type getType() { return type; } } diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/Granularity.java b/druid/src/main/java/org/apache/calcite/adapter/druid/Granularity.java index 57d42e095c4a..b9877dcde42b 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/Granularity.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/Granularity.java @@ -16,8 +16,8 @@ */ package org.apache.calcite.adapter.druid; + import java.util.Locale; -import javax.annotation.Nonnull; /** * A strategy by which Druid rolls up rows into sub-totals based on their @@ -49,5 +49,5 @@ enum Type { public final String lowerName = name().toLowerCase(Locale.ROOT); } - @Nonnull Type getType(); + Type getType(); } diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/NaryOperatorConverter.java b/druid/src/main/java/org/apache/calcite/adapter/druid/NaryOperatorConverter.java index 8297abde8613..539c8dcd6256 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/NaryOperatorConverter.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/NaryOperatorConverter.java @@ -21,9 +21,10 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.SqlOperator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; -import javax.annotation.Nullable; /** * Converts Calcite n-ary operators to Druid expressions, for example diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/SubstringOperatorConversion.java b/druid/src/main/java/org/apache/calcite/adapter/druid/SubstringOperatorConversion.java index f6b36019e7ab..5d7506a38fc6 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/SubstringOperatorConversion.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/SubstringOperatorConversion.java @@ -24,7 +24,7 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; -import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Converts Calcite SUBSTRING call to Druid Expression when possible. diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java index dcfb7d98db77..099fd0218029 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java @@ -28,10 +28,11 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.util.Locale; import java.util.TimeZone; -import javax.annotation.Nullable; import static org.apache.calcite.adapter.druid.DruidQuery.writeFieldIf; import static org.apache.calcite.util.DateTimeStringUtils.ISO_DATETIME_FRACTIONAL_SECOND_FORMAT; diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java index f663056f36f9..0749f69ef119 100644 --- a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java +++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java @@ -20,6 +20,8 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.time.LocalDate; import java.time.ZoneOffset; import java.util.Locale; @@ -29,7 +31,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nullable; /** * Stores Elasticsearch diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFilterableTable.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFilterableTable.java index 6f0474d5189d..3f61d03737e1 100644 --- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFilterableTable.java +++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvFilterableTable.java @@ -19,6 +19,7 @@ import org.apache.calcite.DataContext; import org.apache.calcite.adapter.file.CsvEnumerator; import org.apache.calcite.adapter.file.CsvFieldType; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; @@ -32,9 +33,13 @@ import org.apache.calcite.util.ImmutableIntList; import org.apache.calcite.util.Source; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Objects.requireNonNull; + /** * Table based on a CSV file that can implement simple filtering. * @@ -52,21 +57,22 @@ public CsvFilterableTable(Source source, RelProtoDataType protoRowType) { return "CsvFilterableTable"; } - @Override public Enumerable scan(DataContext root, List filters) { - final List fieldTypes = getFieldTypes(root.getTypeFactory()); - final String[] filterValues = new String[fieldTypes.size()]; + @Override public Enumerable<@Nullable Object[]> scan(DataContext root, List filters) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "typeFactory"); + final List fieldTypes = getFieldTypes(typeFactory); + final @Nullable String[] filterValues = new String[fieldTypes.size()]; filters.removeIf(filter -> addFilter(filter, filterValues)); final List fields = ImmutableIntList.identity(fieldTypes.size()); final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { return new CsvEnumerator<>(source, cancelFlag, false, filterValues, CsvEnumerator.arrayConverter(fieldTypes, fields, false)); } }; } - private boolean addFilter(RexNode filter, Object[] filterValues) { + private boolean addFilter(RexNode filter, @Nullable Object[] filterValues) { if (filter.isA(SqlKind.AND)) { // We cannot refine(remove) the operands of AND, // it will cause o.a.c.i.TableScanNode.createFilterable filters check failed. diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvScannableTable.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvScannableTable.java index 00827ebb29bf..95a46600f62e 100644 --- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvScannableTable.java +++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvScannableTable.java @@ -19,6 +19,7 @@ import org.apache.calcite.DataContext; import org.apache.calcite.adapter.file.CsvEnumerator; import org.apache.calcite.adapter.file.CsvFieldType; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; @@ -27,9 +28,13 @@ import org.apache.calcite.util.ImmutableIntList; import org.apache.calcite.util.Source; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Objects.requireNonNull; + /** * Table based on a CSV file. * @@ -47,12 +52,13 @@ public class CsvScannableTable extends CsvTable return "CsvScannableTable"; } - @Override public Enumerable scan(DataContext root) { - final List fieldTypes = getFieldTypes(root.getTypeFactory()); + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); + final List fieldTypes = getFieldTypes(typeFactory); final List fields = ImmutableIntList.identity(fieldTypes.size()); final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { return new CsvEnumerator<>(source, cancelFlag, false, null, CsvEnumerator.arrayConverter(fieldTypes, fields, false)); } diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamScannableTable.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamScannableTable.java index 2854be93965b..a665daaf4cbf 100644 --- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamScannableTable.java +++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamScannableTable.java @@ -19,6 +19,7 @@ import org.apache.calcite.DataContext; import org.apache.calcite.adapter.file.CsvEnumerator; import org.apache.calcite.adapter.file.CsvFieldType; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; @@ -29,9 +30,13 @@ import org.apache.calcite.util.ImmutableIntList; import org.apache.calcite.util.Source; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Objects.requireNonNull; + /** * Table based on a CSV file. * @@ -53,12 +58,13 @@ public class CsvStreamScannableTable extends CsvScannableTable return "CsvStreamScannableTable"; } - @Override public Enumerable scan(DataContext root) { - final List fieldTypes = getFieldTypes(root.getTypeFactory()); + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); + final List fieldTypes = getFieldTypes(typeFactory); final List fields = ImmutableIntList.identity(fieldTypes.size()); final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { return new CsvEnumerator<>(source, cancelFlag, true, null, CsvEnumerator.arrayConverter(fieldTypes, fields, true)); } diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamTableFactory.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamTableFactory.java index 01c25904dd70..49bb2e08232b 100644 --- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamTableFactory.java +++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvStreamTableFactory.java @@ -25,6 +25,8 @@ import org.apache.calcite.util.Source; import org.apache.calcite.util.Sources; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.util.Map; @@ -41,7 +43,7 @@ public CsvStreamTableFactory() { } @Override public CsvTable create(SchemaPlus schema, String name, - Map operand, RelDataType rowType) { + Map operand, @Nullable RelDataType rowType) { String fileName = (String) operand.get("file"); File file = new File(fileName); final File base = diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTable.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTable.java index 1163cfe6ed09..9af670fe72ba 100644 --- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTable.java +++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTable.java @@ -25,6 +25,8 @@ import org.apache.calcite.schema.impl.AbstractTable; import org.apache.calcite.util.Source; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -33,12 +35,12 @@ */ public abstract class CsvTable extends AbstractTable { protected final Source source; - protected final RelProtoDataType protoRowType; - private RelDataType rowType; - private List fieldTypes; + protected final @Nullable RelProtoDataType protoRowType; + private @Nullable RelDataType rowType; + private @Nullable List fieldTypes; /** Creates a CsvTable. */ - CsvTable(Source source, RelProtoDataType protoRowType) { + CsvTable(Source source, @Nullable RelProtoDataType protoRowType) { this.source = source; this.protoRowType = protoRowType; } diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableFactory.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableFactory.java index 1e2c63704805..fed7ddb338e6 100644 --- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableFactory.java +++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableFactory.java @@ -25,6 +25,8 @@ import org.apache.calcite.util.Source; import org.apache.calcite.util.Sources; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.util.Map; @@ -41,7 +43,7 @@ public CsvTableFactory() { } @Override public CsvTable create(SchemaPlus schema, String name, - Map operand, RelDataType rowType) { + Map operand, @Nullable RelDataType rowType) { String fileName = (String) operand.get("file"); final File base = (File) operand.get(ModelHandler.ExtraOperand.BASE_DIRECTORY.camelName); diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTranslatableTable.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTranslatableTable.java index 703c00faee0b..c83125c851dd 100644 --- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTranslatableTable.java +++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTranslatableTable.java @@ -18,6 +18,7 @@ import org.apache.calcite.DataContext; import org.apache.calcite.adapter.file.CsvEnumerator; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; @@ -37,6 +38,8 @@ import java.lang.reflect.Type; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Objects.requireNonNull; + /** * Table based on a CSV file. */ @@ -58,10 +61,11 @@ public Enumerable project(final DataContext root, final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root); return new AbstractEnumerable() { @Override public Enumerator enumerator() { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); return new CsvEnumerator<>( source, cancelFlag, - getFieldTypes(root.getTypeFactory()), + getFieldTypes(typeFactory), ImmutableIntList.of(fields)); } }; diff --git a/example/function/src/main/java/org/apache/calcite/example/maze/MazeTable.java b/example/function/src/main/java/org/apache/calcite/example/maze/MazeTable.java index 64052dd1fab9..0818cc0a74ea 100644 --- a/example/function/src/main/java/org/apache/calcite/example/maze/MazeTable.java +++ b/example/function/src/main/java/org/apache/calcite/example/maze/MazeTable.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Util; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.PrintWriter; import java.util.Random; import java.util.Set; @@ -85,7 +87,7 @@ public static ScannableTable solve(int width, int height, int seed) { .build(); } - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { final Random random = seed >= 0 ? new Random(seed) : new Random(); final Maze maze = new Maze(width, height); final PrintWriter pw = Util.printWriter(System.out); @@ -93,8 +95,8 @@ public static ScannableTable solve(int width, int height, int seed) { if (Maze.DEBUG) { maze.print(pw, true); } - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { final Set solutionSet; if (solution) { solutionSet = maze.solve(0, 0); diff --git a/file/src/main/java/org/apache/calcite/adapter/file/CsvEnumerator.java b/file/src/main/java/org/apache/calcite/adapter/file/CsvEnumerator.java index 46c9923d7b36..6537d87a3ae0 100644 --- a/file/src/main/java/org/apache/calcite/adapter/file/CsvEnumerator.java +++ b/file/src/main/java/org/apache/calcite/adapter/file/CsvEnumerator.java @@ -30,6 +30,8 @@ import au.com.bytecode.opencsv.CSVReader; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; @@ -39,16 +41,18 @@ import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; +import static org.apache.calcite.linq4j.Nullness.castNonNull; + /** Enumerator that reads from a CSV file. * * @param Row type */ public class CsvEnumerator implements Enumerator { private final CSVReader reader; - private final List filterValues; + private final @Nullable List<@Nullable String> filterValues; private final AtomicBoolean cancelFlag; private final RowConverter rowConverter; - private E current; + private @Nullable E current; private static final FastDateFormat TIME_FORMAT_DATE; private static final FastDateFormat TIME_FORMAT_TIME; @@ -70,7 +74,7 @@ public CsvEnumerator(Source source, AtomicBoolean cancelFlag, } public CsvEnumerator(Source source, AtomicBoolean cancelFlag, boolean stream, - String[] filterValues, RowConverter rowConverter) { + @Nullable String @Nullable [] filterValues, RowConverter rowConverter) { this.cancelFlag = cancelFlag; this.rowConverter = rowConverter; this.filterValues = filterValues == null ? null @@ -97,7 +101,7 @@ private static RowConverter converter(List fieldTypes, } } - public static RowConverter arrayConverter( + public static RowConverter<@Nullable Object[]> arrayConverter( List fieldTypes, List fields, boolean stream) { return new ArrayRowConverter(fieldTypes, fields, stream); } @@ -112,7 +116,7 @@ static RelDataType deduceRowType(JavaTypeFactory typeFactory, Source source, /** Deduces the names and types of a table's columns by reading the first line * of a CSV file. */ public static RelDataType deduceRowType(JavaTypeFactory typeFactory, - Source source, List fieldTypes, Boolean stream) { + Source source, @Nullable List fieldTypes, Boolean stream) { final List types = new ArrayList<>(); final List names = new ArrayList<>(); if (stream) { @@ -170,7 +174,7 @@ static CSVReader openCsv(Source source) throws IOException { } @Override public E current() { - return current; + return castNonNull(current); } @Override public boolean moveNext() { @@ -237,11 +241,11 @@ public static int[] identityList(int n) { * * @param element type */ abstract static class RowConverter { - abstract E convertRow(String[] rows); + abstract E convertRow(@Nullable String[] rows); @SuppressWarnings("JdkObsolete") - protected Object convert(CsvFieldType fieldType, String string) { - if (fieldType == null) { + protected @Nullable Object convert(@Nullable CsvFieldType fieldType, @Nullable String string) { + if (fieldType == null || string == null) { return string; } switch (fieldType) { @@ -318,7 +322,7 @@ protected Object convert(CsvFieldType fieldType, String string) { } /** Array row converter. */ - static class ArrayRowConverter extends RowConverter { + static class ArrayRowConverter extends RowConverter<@Nullable Object[]> { /** Field types. List must not be null, but any element may be null. */ private final List fieldTypes; private final ImmutableIntList fields; @@ -332,7 +336,7 @@ static class ArrayRowConverter extends RowConverter { this.stream = stream; } - @Override public Object[] convertRow(String[] strings) { + @Override public @Nullable Object[] convertRow(@Nullable String[] strings) { if (stream) { return convertStreamRow(strings); } else { @@ -340,8 +344,8 @@ static class ArrayRowConverter extends RowConverter { } } - public Object[] convertNormalRow(String[] strings) { - final Object[] objects = new Object[fields.size()]; + public @Nullable Object[] convertNormalRow(@Nullable String[] strings) { + final @Nullable Object[] objects = new Object[fields.size()]; for (int i = 0; i < fields.size(); i++) { int field = fields.get(i); objects[i] = convert(fieldTypes.get(field), strings[field]); @@ -349,8 +353,8 @@ public Object[] convertNormalRow(String[] strings) { return objects; } - public Object[] convertStreamRow(String[] strings) { - final Object[] objects = new Object[fields.size() + 1]; + public @Nullable Object[] convertStreamRow(@Nullable String[] strings) { + final @Nullable Object[] objects = new Object[fields.size() + 1]; objects[0] = System.currentTimeMillis(); for (int i = 0; i < fields.size(); i++) { int field = fields.get(i); @@ -370,7 +374,7 @@ private SingleColumnRowConverter(CsvFieldType fieldType, int fieldIndex) { this.fieldIndex = fieldIndex; } - @Override public Object convertRow(String[] strings) { + @Override public @Nullable Object convertRow(@Nullable String[] strings) { return convert(fieldType, strings[fieldIndex]); } } diff --git a/file/src/main/java/org/apache/calcite/adapter/file/CsvFieldType.java b/file/src/main/java/org/apache/calcite/adapter/file/CsvFieldType.java index 9f1d28bcbc4a..ff11225a50ac 100644 --- a/file/src/main/java/org/apache/calcite/adapter/file/CsvFieldType.java +++ b/file/src/main/java/org/apache/calcite/adapter/file/CsvFieldType.java @@ -20,6 +20,8 @@ import org.apache.calcite.linq4j.tree.Primitive; import org.apache.calcite.rel.type.RelDataType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.HashMap; import java.util.Map; @@ -56,7 +58,7 @@ public enum CsvFieldType { } CsvFieldType(Primitive primitive) { - this(primitive.boxClass, primitive.primitiveName); + this(primitive.getBoxClass(), primitive.getPrimitiveName()); } CsvFieldType(Class clazz, String simpleName) { @@ -70,7 +72,7 @@ public RelDataType toType(JavaTypeFactory typeFactory) { return typeFactory.createTypeWithNullability(sqlType, true); } - public static CsvFieldType of(String typeString) { + public static @Nullable CsvFieldType of(String typeString) { return MAP.get(typeString); } } diff --git a/file/src/main/java/org/apache/calcite/adapter/file/CsvTableFactory.java b/file/src/main/java/org/apache/calcite/adapter/file/CsvTableFactory.java index 3809bad5d8ad..82e636cb61fa 100644 --- a/file/src/main/java/org/apache/calcite/adapter/file/CsvTableFactory.java +++ b/file/src/main/java/org/apache/calcite/adapter/file/CsvTableFactory.java @@ -25,6 +25,8 @@ import org.apache.calcite.util.Source; import org.apache.calcite.util.Sources; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.util.Map; @@ -41,7 +43,7 @@ public CsvTableFactory() { } @Override public CsvTable create(SchemaPlus schema, String name, - Map operand, RelDataType rowType) { + Map operand, @Nullable RelDataType rowType) { String fileName = (String) operand.get("file"); final File base = (File) operand.get(ModelHandler.ExtraOperand.BASE_DIRECTORY.camelName); diff --git a/file/src/main/java/org/apache/calcite/adapter/file/CsvTranslatableTable.java b/file/src/main/java/org/apache/calcite/adapter/file/CsvTranslatableTable.java index 2175f2d332e4..c564df775c42 100644 --- a/file/src/main/java/org/apache/calcite/adapter/file/CsvTranslatableTable.java +++ b/file/src/main/java/org/apache/calcite/adapter/file/CsvTranslatableTable.java @@ -17,6 +17,7 @@ package org.apache.calcite.adapter.file; import org.apache.calcite.DataContext; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; @@ -36,6 +37,8 @@ import java.lang.reflect.Type; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Objects.requireNonNull; + /** * Table based on a CSV file. * @@ -60,8 +63,9 @@ public Enumerable project(final DataContext root, final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root); return new AbstractEnumerable() { @Override public Enumerator enumerator() { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); return new CsvEnumerator<>(source, cancelFlag, - getFieldTypes(root.getTypeFactory()), ImmutableIntList.of(fields)); + getFieldTypes(typeFactory), ImmutableIntList.of(fields)); } }; } diff --git a/file/src/main/java/org/apache/calcite/adapter/file/JsonEnumerator.java b/file/src/main/java/org/apache/calcite/adapter/file/JsonEnumerator.java index 25fe0ec94f81..256c8e0f587c 100644 --- a/file/src/main/java/org/apache/calcite/adapter/file/JsonEnumerator.java +++ b/file/src/main/java/org/apache/calcite/adapter/file/JsonEnumerator.java @@ -27,6 +27,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -37,12 +39,12 @@ /** * Enumerator that reads from a Object List. */ -public class JsonEnumerator implements Enumerator { +public class JsonEnumerator implements Enumerator<@Nullable Object[]> { - private Enumerator enumerator; + private final Enumerator<@Nullable Object[]> enumerator; - public JsonEnumerator(List list) { - List objs = new ArrayList(); + public JsonEnumerator(List list) { + List<@Nullable Object[]> objs = new ArrayList<>(); for (Object obj : list) { if (obj instanceof Collection) { //noinspection unchecked diff --git a/file/src/main/java/org/apache/calcite/adapter/file/JsonScannableTable.java b/file/src/main/java/org/apache/calcite/adapter/file/JsonScannableTable.java index fe6010ad066e..757b46446f59 100644 --- a/file/src/main/java/org/apache/calcite/adapter/file/JsonScannableTable.java +++ b/file/src/main/java/org/apache/calcite/adapter/file/JsonScannableTable.java @@ -17,12 +17,17 @@ package org.apache.calcite.adapter.file; import org.apache.calcite.DataContext; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; import org.apache.calcite.schema.ScannableTable; import org.apache.calcite.util.Source; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + /** * Table based on a JSON file. * @@ -42,10 +47,11 @@ public JsonScannableTable(Source source) { return "JsonScannableTable"; } - @Override public Enumerable scan(DataContext root) { - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { - return new JsonEnumerator(getDataList(root.getTypeFactory())); + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); + return new JsonEnumerator(getDataList(typeFactory)); } }; } diff --git a/file/src/main/java/org/apache/calcite/adapter/file/JsonTable.java b/file/src/main/java/org/apache/calcite/adapter/file/JsonTable.java index 17f2da8bdf7d..e26f81d8c325 100644 --- a/file/src/main/java/org/apache/calcite/adapter/file/JsonTable.java +++ b/file/src/main/java/org/apache/calcite/adapter/file/JsonTable.java @@ -24,6 +24,8 @@ import org.apache.calcite.schema.impl.AbstractTable; import org.apache.calcite.util.Source; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -31,8 +33,8 @@ */ public class JsonTable extends AbstractTable { private final Source source; - private RelDataType rowType; - protected List dataList; + private @Nullable RelDataType rowType; + protected @Nullable List dataList; public JsonTable(Source source) { this.source = source; diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java index ddeeab2b25fb..aa31c3766972 100644 --- a/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java +++ b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java @@ -27,6 +27,8 @@ import org.apache.geode.cache.client.ClientCache; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.apache.calcite.adapter.geode.util.GeodeUtils.convertToRowValues; /** @@ -55,11 +57,11 @@ public GeodeSimpleScannableTable(String regionName, RelDataType relDataType, return relDataType; } - @Override public Enumerable scan(DataContext root) { - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { - return new GeodeSimpleEnumerator(clientCache, regionName) { - @Override public Object[] convert(Object obj) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { + return new GeodeSimpleEnumerator<@Nullable Object[]>(clientCache, regionName) { + @Override public @Nullable Object[] convert(Object obj) { Object values = convertToRowValues(relDataType.getFieldList(), obj); if (values instanceof Object[]) { return (Object[]) values; diff --git a/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilter.java b/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilter.java index 85d997dff42f..0c2bdf721fd2 100644 --- a/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilter.java +++ b/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilter.java @@ -29,8 +29,9 @@ import com.alibaba.innodb.java.reader.schema.TableDef; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; -import javax.annotation.Nullable; /** * Implementation of a {@link org.apache.calcite.rel.core.Filter} diff --git a/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilterTranslator.java b/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilterTranslator.java index c79072ba9edc..4f2c41dbe32c 100644 --- a/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilterTranslator.java +++ b/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbFilterTranslator.java @@ -39,6 +39,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -46,7 +48,6 @@ import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nullable; /** * Translates {@link RexNode} expressions into {@link IndexCondition} diff --git a/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbTableScan.java b/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbTableScan.java index 6c1ea819faf5..c144704a5a51 100644 --- a/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbTableScan.java +++ b/innodb/src/main/java/org/apache/calcite/adapter/innodb/InnodbTableScan.java @@ -35,10 +35,11 @@ import com.alibaba.innodb.java.reader.Constants; import com.alibaba.innodb.java.reader.schema.KeyMeta; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Optional; import java.util.Set; -import javax.annotation.Nullable; /** * Relational expression representing a scan of an InnoDB data source. diff --git a/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaMessageEnumerator.java b/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaMessageEnumerator.java index 8069aeeab47e..af091a58d3ae 100644 --- a/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaMessageEnumerator.java +++ b/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaMessageEnumerator.java @@ -23,11 +23,15 @@ import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.time.Duration; import java.util.ArrayDeque; import java.util.Deque; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Objects.requireNonNull; + /** * Enumerator to read data from {@link Consumer}, * and converted into SQL rows with {@link KafkaRowConverter}. @@ -37,14 +41,14 @@ * @param Type for Kafka message value, * refer to {@link ConsumerConfig#VALUE_DESERIALIZER_CLASS_CONFIG}; */ -public class KafkaMessageEnumerator implements Enumerator { +public class KafkaMessageEnumerator implements Enumerator<@Nullable Object[]> { final Consumer consumer; final KafkaRowConverter rowConverter; private final AtomicBoolean cancelFlag; //runtime private final Deque> bufferedRecords = new ArrayDeque<>(); - private ConsumerRecord curRecord; + private @Nullable ConsumerRecord curRecord; KafkaMessageEnumerator(final Consumer consumer, final KafkaRowConverter rowConverter, @@ -58,7 +62,7 @@ public class KafkaMessageEnumerator implements Enumerator { * It returns an Array of Object, with each element represents a field of row. */ @Override public Object[] current() { - return rowConverter.toRow(curRecord); + return rowConverter.toRow(requireNonNull(curRecord, "curRecord")); } @Override public boolean moveNext() { diff --git a/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaStreamTable.java b/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaStreamTable.java index 334e7a6baaea..ea5f257bcb0d 100644 --- a/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaStreamTable.java +++ b/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaStreamTable.java @@ -39,6 +39,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Collections; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; @@ -56,10 +58,10 @@ public class KafkaStreamTable implements ScannableTable, StreamableTable { this.tableOptions = tableOptions; } - @Override public Enumerable scan(final DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(final DataContext root) { final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { if (tableOptions.getConsumer() != null) { return new KafkaMessageEnumerator(tableOptions.getConsumer(), tableOptions.getRowConverter(), cancelFlag); @@ -99,8 +101,8 @@ public class KafkaStreamTable implements ScannableTable, StreamableTable { } @Override public boolean rolledUpColumnValidInsideAgg(final String column, final SqlCall call, - final SqlNode parent, - final CalciteConnectionConfig config) { + final @Nullable SqlNode parent, + final @Nullable CalciteConnectionConfig config) { return false; } diff --git a/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaTableFactory.java b/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaTableFactory.java index 2271d4cb5047..51441bd17f21 100644 --- a/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaTableFactory.java +++ b/kafka/src/main/java/org/apache/calcite/adapter/kafka/KafkaTableFactory.java @@ -23,6 +23,8 @@ import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.OffsetResetStrategy; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationTargetException; import java.util.Locale; import java.util.Map; @@ -38,7 +40,7 @@ public KafkaTableFactory() { @Override public KafkaStreamTable create(SchemaPlus schema, String name, Map operand, - RelDataType rowType) { + @Nullable RelDataType rowType) { final KafkaTableOptions tableOptionBuilder = new KafkaTableOptions(); tableOptionBuilder.setBootstrapServers( diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/BaseQueryable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/BaseQueryable.java index 35535a72a490..b707fad12d45 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/BaseQueryable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/BaseQueryable.java @@ -18,6 +18,8 @@ import org.apache.calcite.linq4j.tree.Expression; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Iterator; @@ -34,10 +36,10 @@ public abstract class BaseQueryable extends AbstractQueryable { protected final QueryProvider provider; protected final Type elementType; - protected final Expression expression; + protected final @Nullable Expression expression; protected BaseQueryable(QueryProvider provider, Type elementType, - Expression expression) { + @Nullable Expression expression) { this.provider = provider; this.elementType = elementType; this.expression = expression; @@ -51,7 +53,7 @@ protected BaseQueryable(QueryProvider provider, Type elementType, return elementType; } - @Override public Expression getExpression() { + @Override public @Nullable Expression getExpression() { return expression; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java index 17cafd2b4967..2fc8e6ff9ce6 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java @@ -33,6 +33,9 @@ import org.apache.calcite.linq4j.function.Predicate1; import org.apache.calcite.linq4j.function.Predicate2; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.math.BigDecimal; import java.util.Collection; import java.util.Comparator; @@ -69,7 +72,7 @@ protected OrderedEnumerable getThisOrdered() { return this; } - @Override public R foreach(Function1 func) { + @Override public @Nullable R foreach(Function1 func) { R result = null; try (Enumerator enumerator = enumerator()) { while (enumerator.moveNext()) { @@ -89,12 +92,12 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.asOrderedQueryable(this); } - @Override public T aggregate(Function2 func) { + @Override public @Nullable T aggregate(Function2<@Nullable T, T, T> func) { return EnumerableDefaults.aggregate(getThis(), func); } - @Override public TAccumulate aggregate(TAccumulate seed, - Function2 func) { + @Override public @PolyNull TAccumulate aggregate(@PolyNull TAccumulate seed, + Function2<@PolyNull TAccumulate, T, @PolyNull TAccumulate> func) { return EnumerableDefaults.aggregate(getThis(), seed, func); } @@ -191,11 +194,11 @@ protected OrderedQueryable asOrderedQueryable() { keySelector, comparator, descending); } - @Override public Enumerable defaultIfEmpty() { + @Override public Enumerable<@Nullable T> defaultIfEmpty() { return EnumerableDefaults.defaultIfEmpty(getThis()); } - @Override public Enumerable defaultIfEmpty(T value) { + @Override public Enumerable<@PolyNull T> defaultIfEmpty(@PolyNull T value) { return EnumerableDefaults.defaultIfEmpty(getThis(), value); } @@ -211,7 +214,7 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.elementAt(getThis(), index); } - @Override public T elementAtOrDefault(int index) { + @Override public @Nullable T elementAtOrDefault(int index) { return EnumerableDefaults.elementAtOrDefault(getThis(), index); } @@ -241,11 +244,11 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.first(getThis(), predicate); } - @Override public T firstOrDefault() { + @Override public @Nullable T firstOrDefault() { return EnumerableDefaults.firstOrDefault(getThis()); } - @Override public T firstOrDefault(Predicate1 predicate) { + @Override public @Nullable T firstOrDefault(Predicate1 predicate) { return EnumerableDefaults.firstOrDefault(getThis(), predicate); } @@ -407,7 +410,7 @@ protected OrderedQueryable asOrderedQueryable() { Function2 resultSelector, EqualityComparer comparer, boolean generateNullsOnLeft, boolean generateNullsOnRight, - Predicate2 predicate) { + @Nullable Predicate2 predicate) { return EnumerableDefaults.hashJoin(getThis(), inner, outerKeySelector, innerKeySelector, resultSelector, comparer, generateNullsOnLeft, generateNullsOnRight, predicate); @@ -428,11 +431,11 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.last(getThis(), predicate); } - @Override public T lastOrDefault() { + @Override public @Nullable T lastOrDefault() { return EnumerableDefaults.lastOrDefault(getThis()); } - @Override public T lastOrDefault(Predicate1 predicate) { + @Override public @Nullable T lastOrDefault(Predicate1 predicate) { return EnumerableDefaults.lastOrDefault(getThis(), predicate); } @@ -444,15 +447,16 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.longCount(getThis(), predicate); } - @Override public T max() { - return (T) EnumerableDefaults.max((Enumerable) getThis()); + @SuppressWarnings("unchecked") + @Override public @Nullable T max() { + return (@Nullable T) EnumerableDefaults.max((Enumerable) getThis()); } - @Override public BigDecimal max(BigDecimalFunction1 selector) { + @Override public @Nullable BigDecimal max(BigDecimalFunction1 selector) { return EnumerableDefaults.max(getThis(), selector); } - @Override public BigDecimal max(NullableBigDecimalFunction1 selector) { + @Override public @Nullable BigDecimal max(NullableBigDecimalFunction1 selector) { return EnumerableDefaults.max(getThis(), selector); } @@ -460,7 +464,7 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.max(getThis(), selector); } - @Override public Double max(NullableDoubleFunction1 selector) { + @Override public @Nullable Double max(NullableDoubleFunction1 selector) { return EnumerableDefaults.max(getThis(), selector); } @@ -468,7 +472,7 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.max(getThis(), selector); } - @Override public Integer max(NullableIntegerFunction1 selector) { + @Override public @Nullable Integer max(NullableIntegerFunction1 selector) { return EnumerableDefaults.max(getThis(), selector); } @@ -476,7 +480,7 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.max(getThis(), selector); } - @Override public Long max(NullableLongFunction1 selector) { + @Override public @Nullable Long max(NullableLongFunction1 selector) { return EnumerableDefaults.max(getThis(), selector); } @@ -484,24 +488,25 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.max(getThis(), selector); } - @Override public Float max(NullableFloatFunction1 selector) { + @Override public @Nullable Float max(NullableFloatFunction1 selector) { return EnumerableDefaults.max(getThis(), selector); } - @Override public > TResult max( + @Override public > @Nullable TResult max( Function1 selector) { return EnumerableDefaults.max(getThis(), selector); } - @Override public T min() { - return (T) EnumerableDefaults.min((Enumerable) getThis()); + @SuppressWarnings("unchecked") + @Override public @Nullable T min() { + return (@Nullable T) EnumerableDefaults.min((Enumerable) getThis()); } - @Override public BigDecimal min(BigDecimalFunction1 selector) { + @Override public @Nullable BigDecimal min(BigDecimalFunction1 selector) { return EnumerableDefaults.min(getThis(), selector); } - @Override public BigDecimal min(NullableBigDecimalFunction1 selector) { + @Override public @Nullable BigDecimal min(NullableBigDecimalFunction1 selector) { return EnumerableDefaults.min(getThis(), selector); } @@ -509,7 +514,7 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.min(getThis(), selector); } - @Override public Double min(NullableDoubleFunction1 selector) { + @Override public @Nullable Double min(NullableDoubleFunction1 selector) { return EnumerableDefaults.min(getThis(), selector); } @@ -517,7 +522,7 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.min(getThis(), selector); } - @Override public Integer min(NullableIntegerFunction1 selector) { + @Override public @Nullable Integer min(NullableIntegerFunction1 selector) { return EnumerableDefaults.min(getThis(), selector); } @@ -525,7 +530,7 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.min(getThis(), selector); } - @Override public Long min(NullableLongFunction1 selector) { + @Override public @Nullable Long min(NullableLongFunction1 selector) { return EnumerableDefaults.min(getThis(), selector); } @@ -533,11 +538,11 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.min(getThis(), selector); } - @Override public Float min(NullableFloatFunction1 selector) { + @Override public @Nullable Float min(NullableFloatFunction1 selector) { return EnumerableDefaults.min(getThis(), selector); } - @Override public > TResult min( + @Override public > @Nullable TResult min( Function1 selector) { return EnumerableDefaults.min(getThis(), selector); } @@ -621,11 +626,11 @@ protected OrderedQueryable asOrderedQueryable() { return EnumerableDefaults.single(getThis(), predicate); } - @Override public T singleOrDefault() { + @Override public @Nullable T singleOrDefault() { return EnumerableDefaults.singleOrDefault(getThis()); } - @Override public T singleOrDefault(Predicate1 predicate) { + @Override public @Nullable T singleOrDefault(Predicate1 predicate) { return EnumerableDefaults.singleOrDefault(getThis(), predicate); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultQueryable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultQueryable.java index 4c1eddfa7877..65c79bacb553 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultQueryable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultQueryable.java @@ -33,6 +33,8 @@ import org.apache.calcite.linq4j.function.Predicate2; import org.apache.calcite.linq4j.tree.FunctionExpression; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.Comparator; @@ -148,7 +150,7 @@ protected OrderedQueryable getThisOrderedQueryable() { return factory.ofType(getThis(), clazz); } - @Override public Queryable defaultIfEmpty() { + @Override public Queryable<@Nullable T> defaultIfEmpty() { return factory.defaultIfEmpty(getThis()); } @@ -162,7 +164,8 @@ protected OrderedQueryable getThisOrderedQueryable() { // End disambiguate - @Override public T aggregate(FunctionExpression> selector) { + @Override public @Nullable T aggregate( + FunctionExpression> selector) { return factory.aggregate(getThis(), selector); } @@ -243,7 +246,7 @@ protected OrderedQueryable getThisOrderedQueryable() { return factory.first(getThis(), predicate); } - @Override public T firstOrDefault(FunctionExpression> predicate) { + @Override public @Nullable T firstOrDefault(FunctionExpression> predicate) { return factory.firstOrDefault(getThis(), predicate); } @@ -421,7 +424,7 @@ protected OrderedQueryable getThisOrderedQueryable() { return factory.single(getThis(), predicate); } - @Override public T singleOrDefault(FunctionExpression> predicate) { + @Override public @Nullable T singleOrDefault(FunctionExpression> predicate) { return factory.singleOrDefault(getThis(), predicate); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerable.java index 5d4300b8f00d..72824b9d4aa0 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerable.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.framework.qual.Covariant; + /** * Exposes the enumerator, which supports a simple iteration over a collection. * @@ -26,6 +28,7 @@ * * @param Element type */ +@Covariant(0) public interface Enumerable extends RawEnumerable, Iterable, ExtendedEnumerable { /** diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java index 3d43814017bc..669e28fa6e20 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java @@ -41,6 +41,11 @@ import com.google.common.collect.Sets; import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.KeyFor; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.framework.qual.HasQualifierParameter; import java.math.BigDecimal; import java.util.AbstractList; @@ -65,8 +70,11 @@ import static org.apache.calcite.linq4j.Linq4j.CollectionEnumerable; import static org.apache.calcite.linq4j.Linq4j.ListEnumerable; +import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.linq4j.function.Functions.adapt; +import static java.util.Objects.requireNonNull; + /** * Default implementations of methods in the {@link Enumerable} interface. */ @@ -75,10 +83,13 @@ public abstract class EnumerableDefaults { /** * Applies an accumulator function over a sequence. */ - public static TSource aggregate(Enumerable source, - Function2 func) { - TSource result = null; + public static @Nullable TSource aggregate(Enumerable source, + Function2<@Nullable TSource, TSource, TSource> func) { try (Enumerator os = source.enumerator()) { + if (!os.moveNext()) { + return null; + } + TSource result = os.current(); while (os.moveNext()) { TSource o = os.current(); result = func.apply(result, o); @@ -347,7 +358,7 @@ public static boolean contains(Enumerable enumerable, try (Enumerator os = enumerable.enumerator()) { while (os.moveNext()) { TSource o = os.current(); - if (o.equals(element)) { + if (Objects.equals(o, element)) { return true; } } @@ -391,7 +402,7 @@ public static int count(Enumerable enumerable, * the type parameter's default value in a singleton collection if * the sequence is empty. */ - public static Enumerable defaultIfEmpty( + public static Enumerable<@Nullable TSource> defaultIfEmpty( Enumerable enumerable) { return defaultIfEmpty(enumerable, null); } @@ -401,24 +412,25 @@ public static Enumerable defaultIfEmpty( * the specified value in a singleton collection if the sequence * is empty. */ - public static Enumerable defaultIfEmpty( + @SuppressWarnings("return.type.incompatible") + public static Enumerable<@PolyNull TSource> defaultIfEmpty( Enumerable enumerable, - TSource value) { + @PolyNull TSource value) { try (Enumerator os = enumerable.enumerator()) { if (os.moveNext()) { - return Linq4j.asEnumerable(() -> new Iterator() { + return Linq4j.asEnumerable(() -> new Iterator() { private boolean nonFirst; - private Iterator rest; + private @Nullable Iterator rest; @Override public boolean hasNext() { - return !nonFirst || rest.hasNext(); + return !nonFirst || requireNonNull(rest).hasNext(); } @Override public TSource next() { if (nonFirst) { - return rest.next(); + return requireNonNull(rest).next(); } else { final TSource first = os.current(); nonFirst = true; @@ -501,7 +513,7 @@ public static TSource elementAt(Enumerable enumerable, * sequence or a default value if the index is out of * range. */ - public static TSource elementAtOrDefault( + public static @Nullable TSource elementAtOrDefault( Enumerable enumerable, int index) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) @@ -552,7 +564,8 @@ public static Enumerable except( try (Enumerator os = source1.enumerator()) { while (os.moveNext()) { TSource o = os.current(); - collection.remove(o); + @SuppressWarnings("argument.type.incompatible") + boolean unused = collection.remove(o); } return Linq4j.asEnumerable(collection); } @@ -625,7 +638,7 @@ public static TSource first(Enumerable enumerable, * Returns the first element of a sequence, or a * default value if the sequence contains no elements. */ - public static TSource firstOrDefault( + public static @Nullable TSource firstOrDefault( Enumerable enumerable) { try (Enumerator os = enumerable.enumerator()) { if (os.moveNext()) { @@ -640,7 +653,7 @@ public static TSource firstOrDefault( * satisfies a condition or a default value if no such element is * found. */ - public static TSource firstOrDefault(Enumerable enumerable, + public static @Nullable TSource firstOrDefault(Enumerable enumerable, Predicate1 predicate) { for (TSource o : enumerable) { if (predicate.apply(o)) { @@ -861,9 +874,9 @@ private static class SortedAggregateEnumerator comparator; private boolean isInitialized; private boolean isLastMoveNextFalse; - private TAccumulate curAccumulator; + private @Nullable TAccumulate curAccumulator; private Enumerator enumerator; - private TResult curResult; + private @Nullable TResult curResult; SortedAggregateEnumerator( Enumerable enumerable, @@ -889,7 +902,7 @@ private static class SortedAggregateEnumerator Enumerable groupBy while (os.moveNext()) { TSource o = os.current(); TKey key = keySelector.apply(o); + @SuppressWarnings("argument.type.incompatible") TAccumulate accumulator = map.get(key); if (accumulator == null) { accumulator = accumulatorInitializer.apply(); @@ -990,6 +1006,7 @@ private static Enumerable groupBy for (Function1 keySelector : keySelectors) { TSource o = os.current(); TKey key = keySelector.apply(o); + @SuppressWarnings("argument.type.incompatible") TAccumulate accumulator = map.get(key); if (accumulator == null) { accumulator = accumulatorInitializer.apply(); @@ -1043,6 +1060,7 @@ public static Enumerable groupJoin( return new Enumerator() { @Override public TResult current() { final Map.Entry entry = entries.current(); + @SuppressWarnings("argument.type.incompatible") final Enumerable inners = innerLookup.get(entry.getKey()); return resultSelector.apply(entry.getValue(), inners == null ? Linq4j.emptyEnumerable() : inners); @@ -1084,6 +1102,7 @@ public static Enumerable groupJoin( return new Enumerator() { @Override public TResult current() { final Map.Entry entry = entries.current(); + @SuppressWarnings("argument.type.incompatible") final Enumerable inners = innerLookup.get(entry.getKey()); return resultSelector.apply(entry.getValue(), inners == null ? Linq4j.emptyEnumerable() : inners); @@ -1128,7 +1147,9 @@ public static Enumerable intersect( try (Enumerator os = source0.enumerator()) { while (os.moveNext()) { TSource o = os.current(); - if (set1.remove(o)) { + @SuppressWarnings("argument.type.incompatible") + boolean removed = set1.remove(o); + if (removed) { resultCollection.add(o); } } @@ -1227,7 +1248,7 @@ public static Enumerable hashJoin( Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector, - EqualityComparer comparer, boolean generateNullsOnLeft, + @Nullable EqualityComparer comparer, boolean generateNullsOnLeft, boolean generateNullsOnRight) { return hashEquiJoin_( outer, @@ -1250,9 +1271,9 @@ public static Enumerable hashJoin( Function1 outerKeySelector, Function1 innerKeySelector, Function2 resultSelector, - EqualityComparer comparer, boolean generateNullsOnLeft, + @Nullable EqualityComparer comparer, boolean generateNullsOnLeft, boolean generateNullsOnRight, - Predicate2 predicate) { + @Nullable Predicate2 predicate) { if (predicate == null) { return hashEquiJoin_( outer, @@ -1283,7 +1304,8 @@ private static Enumerable hashEquiJoin final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2 resultSelector, - final EqualityComparer comparer, final boolean generateNullsOnLeft, + final @Nullable EqualityComparer comparer, + final boolean generateNullsOnLeft, final boolean generateNullsOnRight) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { @@ -1295,7 +1317,7 @@ private static Enumerable hashEquiJoin return new Enumerator() { Enumerator outers = outer.enumerator(); Enumerator inners = Linq4j.emptyEnumerator(); - Set unmatchedKeys = + @Nullable Set unmatchedKeys = generateNullsOnLeft ? new HashSet<>(innerLookup.keySet()) : null; @@ -1316,7 +1338,9 @@ private static Enumerable hashEquiJoin // not the left. List list = new ArrayList<>(); for (TKey key : unmatchedKeys) { - for (TInner tInner : innerLookup.get(key)) { + @SuppressWarnings("argument.type.incompatible") + Enumerable innerValues = requireNonNull(innerLookup.get(key)); + for (TInner tInner : innerValues) { list.add(tInner); } } @@ -1376,7 +1400,8 @@ private static Enumerable hashJoinWith final Function1 outerKeySelector, final Function1 innerKeySelector, final Function2 resultSelector, - final EqualityComparer comparer, final boolean generateNullsOnLeft, + final @Nullable EqualityComparer comparer, + final boolean generateNullsOnLeft, final boolean generateNullsOnRight, final Predicate2 predicate) { return new AbstractEnumerable() { @@ -1399,7 +1424,7 @@ private static Enumerable hashJoinWith return new Enumerator() { Enumerator outers = outer.enumerator(); Enumerator inners = Linq4j.emptyEnumerator(); - List innersUnmatched = + @Nullable List innersUnmatched = generateNullsOnLeft ? new ArrayList<>(innerToLookUp.toList()) : null; @@ -1485,7 +1510,7 @@ private static Enumerable hashJoinWith public static Enumerable correlateJoin( final JoinType joinType, final Enumerable outer, final Function1> inner, - final Function2 resultSelector) { + final Function2 resultSelector) { if (joinType == JoinType.RIGHT || joinType == JoinType.FULL) { throw new IllegalArgumentException("JoinType " + joinType + " is not valid for correlation"); } @@ -1493,14 +1518,14 @@ public static Enumerable correlateJoin( return new AbstractEnumerable() { @Override public Enumerator enumerator() { return new Enumerator() { - private Enumerator outerEnumerator = outer.enumerator(); - private Enumerator innerEnumerator; - TSource outerValue; - TInner innerValue; + private final Enumerator outerEnumerator = outer.enumerator(); + private @Nullable Enumerator innerEnumerator; + @Nullable TSource outerValue; + @Nullable TInner innerValue; int state = 0; // 0 -- moving outer, 1 moving inner; @Override public TResult current() { - return resultSelector.apply(outerValue, innerValue); + return resultSelector.apply(castNonNull(outerValue), innerValue); } @Override public boolean moveNext() { @@ -1550,6 +1575,7 @@ public static Enumerable correlateJoin( continue; case 1: // subsequent move inner + Enumerator innerEnumerator = requireNonNull(this.innerEnumerator); if (innerEnumerator.moveNext()) { innerValue = innerEnumerator.current(); return true; @@ -1644,18 +1670,19 @@ public static Enumerable correlateBatchJoin( return new AbstractEnumerable() { @Override public Enumerator enumerator() { return new Enumerator() { - Enumerator outerEnumerator = outer.enumerator(); - List outerValues = new ArrayList<>(batchSize); - List innerValues = new ArrayList<>(); - TSource outerValue; - TInner innerValue; - Enumerable innerEnumerable; - Enumerator innerEnumerator; + final Enumerator outerEnumerator = outer.enumerator(); + final List outerValues = new ArrayList<>(batchSize); + final List innerValues = new ArrayList<>(); + @Nullable TSource outerValue; + @Nullable TInner innerValue; + @Nullable Enumerable innerEnumerable; + @Nullable Enumerator innerEnumerator; boolean innerEnumHasNext = false; boolean atLeastOneResult = false; int i = -1; // outer position int j = -1; // inner position + @SuppressWarnings("argument.type.incompatible") @Override public TResult current() { return resultSelector.apply(outerValue, innerValue); } @@ -1703,7 +1730,7 @@ public static Enumerable correlateBatchJoin( outerValue = outerValues.get(i); // get current outer value nextInnerValue(); // Compare current block row to current inner value - if (predicate.apply(outerValue, innerValue)) { + if (predicate.apply(castNonNull(outerValue), castNonNull(innerValue))) { atLeastOneResult = true; // Skip the rest of inner values in case of // ANTI and SEMI when a match is found @@ -1711,6 +1738,7 @@ public static Enumerable correlateBatchJoin( // Two ways of skipping inner values, // enumerator way and ArrayList way if (i == 0) { + Enumerator innerEnumerator = requireNonNull(this.innerEnumerator); while (innerEnumHasNext) { innerValues.add(innerEnumerator.current()); innerEnumHasNext = innerEnumerator.moveNext(); @@ -1746,6 +1774,7 @@ public void nextOuterValue() { private void nextInnerValue() { if (i == 0) { + Enumerator innerEnumerator = requireNonNull(this.innerEnumerator); innerValue = innerEnumerator.current(); innerValues.add(innerValue); innerEnumHasNext = innerEnumerator.moveNext(); // next enumerator inner value @@ -1892,10 +1921,11 @@ private static Enumerable semiJoinWithPredicate final Predicate1 predicate = v0 -> { TKey key = outerKeySelector.apply(v0); - if (!innerLookup.get().containsKey(key)) { + @SuppressWarnings("argument.type.incompatible") + Enumerable innersOfKey = innerLookup.get().get(key); + if (innersOfKey == null) { return anti; } - Enumerable innersOfKey = innerLookup.get().get(key); try (Enumerator os = innersOfKey.enumerator()) { while (os.moveNext()) { TInner v1 = os.current(); @@ -1920,7 +1950,7 @@ private static Enumerable semiEquiJoin_( final Enumerable outer, final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, - final EqualityComparer comparer, + final @Nullable EqualityComparer comparer, final boolean anti) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { @@ -1946,7 +1976,7 @@ private static Enumerable semiEquiJoin_( public static Enumerable nestedLoopJoin( final Enumerable outer, final Enumerable inner, final Predicate2 predicate, - Function2 resultSelector, + Function2 resultSelector, final JoinType joinType) { if (!joinType.generatesNullsOnLeft()) { return nestedLoopJoinOptimized(outer, inner, predicate, resultSelector, joinType); @@ -1961,7 +1991,7 @@ public static Enumerable nestedLoopJoin( private static Enumerable nestedLoopJoinAsList( final Enumerable outer, final Enumerable inner, final Predicate2 predicate, - Function2 resultSelector, + Function2 resultSelector, final JoinType joinType) { final boolean generateNullsOnLeft = joinType.generatesNullsOnLeft(); final boolean generateNullsOnRight = joinType.generatesNullsOnRight(); @@ -1985,7 +2015,8 @@ private static Enumerable nestedLoopJoinAsLi break; } else { if (rightUnmatched != null) { - rightUnmatched.remove(right); + @SuppressWarnings("argument.type.incompatible") + boolean unused = rightUnmatched.remove(right); } result.add(resultSelector.apply(left, right)); if (joinType == JoinType.SEMI) { @@ -2016,7 +2047,7 @@ private static Enumerable nestedLoopJoinAsLi private static Enumerable nestedLoopJoinOptimized( final Enumerable outer, final Enumerable inner, final Predicate2 predicate, - Function2 resultSelector, + Function2 resultSelector, final JoinType joinType) { if (joinType == JoinType.RIGHT || joinType == JoinType.FULL) { throw new IllegalArgumentException("JoinType " + joinType + " is unsupported"); @@ -2026,14 +2057,14 @@ private static Enumerable nestedLoopJoinOpti @Override public Enumerator enumerator() { return new Enumerator() { private Enumerator outerEnumerator = outer.enumerator(); - private Enumerator innerEnumerator = null; + private @Nullable Enumerator innerEnumerator = null; private boolean outerMatch = false; // whether the outerValue has matched an innerValue - private TSource outerValue; - private TInner innerValue; + private @Nullable TSource outerValue; + private @Nullable TInner innerValue; private int state = 0; // 0 moving outer, 1 moving inner @Override public TResult current() { - return resultSelector.apply(outerValue, innerValue); + return resultSelector.apply(castNonNull(outerValue), innerValue); } @Override public boolean moveNext() { @@ -2052,9 +2083,11 @@ private static Enumerable nestedLoopJoinOpti continue; case 1: // move inner + Enumerator innerEnumerator = requireNonNull(this.innerEnumerator); if (innerEnumerator.moveNext()) { - innerValue = innerEnumerator.current(); - if (predicate.apply(outerValue, innerValue)) { + TInner innerValue = innerEnumerator.current(); + this.innerValue = innerValue; + if (predicate.apply(castNonNull(outerValue), innerValue)) { outerMatch = true; switch (joinType) { case ANTI: // try next outer row @@ -2120,7 +2153,7 @@ private void closeInner() { final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, - final Function2 resultSelector, + final Function2 resultSelector, boolean generateNullsOnLeft, boolean generateNullsOnRight) { if (generateNullsOnLeft) { @@ -2160,7 +2193,7 @@ public static boolean isMergeJoinSupported(JoinType joinType) { final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, - final Function2 resultSelector, + final Function2 resultSelector, final JoinType joinType, final Comparator comparator) { return mergeJoin(outer, inner, outerKeySelector, innerKeySelector, null, resultSelector, @@ -2189,10 +2222,10 @@ public static boolean isMergeJoinSupported(JoinType joinType) { final Enumerable inner, final Function1 outerKeySelector, final Function1 innerKeySelector, - final Predicate2 extraPredicate, - final Function2 resultSelector, + final @Nullable Predicate2 extraPredicate, + final Function2 resultSelector, final JoinType joinType, - final Comparator comparator) { + final @Nullable Comparator comparator) { if (!isMergeJoinSupported(joinType)) { throw new UnsupportedOperationException("MergeJoin unsupported for join type " + joinType); } @@ -2245,7 +2278,7 @@ public static TSource last(Enumerable enumerable, * Returns the last element of a sequence, or a * default value if the sequence contains no elements. */ - public static TSource lastOrDefault( + public static @Nullable TSource lastOrDefault( Enumerable enumerable) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) @@ -2275,7 +2308,7 @@ public static TSource lastOrDefault( * satisfies a condition or a default value if no such element is * found. */ - public static TSource lastOrDefault(Enumerable enumerable, + public static @Nullable TSource lastOrDefault(Enumerable enumerable, Predicate1 predicate) { final ListEnumerable list = enumerable instanceof ListEnumerable ? ((ListEnumerable) enumerable) @@ -2344,8 +2377,7 @@ public static long longCount(Enumerable enumerable, */ public static > TSource max( Enumerable source) { - Function2 max = maxFunction(); - return aggregate(source, null, max); + return aggregate(source, maxFunction()); } /** @@ -2354,8 +2386,7 @@ public static > TSource max( */ public static BigDecimal max(Enumerable source, BigDecimalFunction1 selector) { - Function2 max = maxFunction(); - return aggregate(source.select(selector), null, max); + return aggregate(source.select(selector), maxFunction()); } /** @@ -2365,8 +2396,7 @@ public static BigDecimal max(Enumerable source, */ public static BigDecimal max(Enumerable source, NullableBigDecimalFunction1 selector) { - Function2 max = maxFunction(); - return aggregate(source.select(selector), null, max); + return aggregate(source.select(selector), maxFunction()); } /** @@ -2375,8 +2405,7 @@ public static BigDecimal max(Enumerable source, */ public static double max(Enumerable source, DoubleFunction1 selector) { - return aggregate(source.select(adapt(selector)), null, - Extensions.DOUBLE_MAX); + return requireNonNull(aggregate(source.select(adapt(selector)), Extensions.DOUBLE_MAX)); } /** @@ -2386,7 +2415,7 @@ public static double max(Enumerable source, */ public static Double max(Enumerable source, NullableDoubleFunction1 selector) { - return aggregate(source.select(selector), null, Extensions.DOUBLE_MAX); + return aggregate(source.select(selector), Extensions.DOUBLE_MAX); } /** @@ -2395,8 +2424,7 @@ public static Double max(Enumerable source, */ public static int max(Enumerable source, IntegerFunction1 selector) { - return aggregate(source.select(adapt(selector)), null, - Extensions.INTEGER_MAX); + return requireNonNull(aggregate(source.select(adapt(selector)), Extensions.INTEGER_MAX)); } /** @@ -2406,7 +2434,7 @@ public static int max(Enumerable source, */ public static Integer max(Enumerable source, NullableIntegerFunction1 selector) { - return aggregate(source.select(selector), null, Extensions.INTEGER_MAX); + return aggregate(source.select(selector), Extensions.INTEGER_MAX); } /** @@ -2415,7 +2443,7 @@ public static Integer max(Enumerable source, */ public static long max(Enumerable source, LongFunction1 selector) { - return aggregate(source.select(adapt(selector)), null, Extensions.LONG_MAX); + return requireNonNull(aggregate(source.select(adapt(selector)), Extensions.LONG_MAX)); } /** @@ -2423,9 +2451,9 @@ public static long max(Enumerable source, * sequence and returns the maximum nullable long value. (Defined * by Enumerable.) */ - public static Long max(Enumerable source, + public static @Nullable Long max(Enumerable source, NullableLongFunction1 selector) { - return aggregate(source.select(selector), null, Extensions.LONG_MAX); + return aggregate(source.select(selector), Extensions.LONG_MAX); } /** @@ -2434,8 +2462,7 @@ public static Long max(Enumerable source, */ public static float max(Enumerable source, FloatFunction1 selector) { - return aggregate(source.select(adapt(selector)), null, - Extensions.FLOAT_MAX); + return requireNonNull(aggregate(source.select(adapt(selector)), Extensions.FLOAT_MAX)); } /** @@ -2443,9 +2470,9 @@ public static float max(Enumerable source, * sequence and returns the maximum nullable Float * value. */ - public static Float max(Enumerable source, + public static @Nullable Float max(Enumerable source, NullableFloatFunction1 selector) { - return aggregate(source.select(selector), null, Extensions.FLOAT_MAX); + return aggregate(source.select(selector), Extensions.FLOAT_MAX); } /** @@ -2453,20 +2480,18 @@ public static Float max(Enumerable source, * generic sequence and returns the maximum resulting * value. */ - public static > TResult max( + public static > @Nullable TResult max( Enumerable source, Function1 selector) { - Function2 max = maxFunction(); - return aggregate(source.select(selector), null, max); + return aggregate(source.select(selector), maxFunction()); } /** * Returns the minimum value in a generic * sequence. */ - public static > TSource min( + public static > @Nullable TSource min( Enumerable source) { - Function2 min = minFunction(); - return aggregate(source, null, min); + return aggregate(source, minFunction()); } @SuppressWarnings("unchecked") @@ -2498,8 +2523,7 @@ public static BigDecimal min(Enumerable source, */ public static BigDecimal min(Enumerable source, NullableBigDecimalFunction1 selector) { - Function2 min = minFunction(); - return aggregate(source.select(selector), null, min); + return aggregate(source.select(selector), minFunction()); } /** @@ -2508,8 +2532,7 @@ public static BigDecimal min(Enumerable source, */ public static double min(Enumerable source, DoubleFunction1 selector) { - return aggregate(source.select(adapt(selector)), null, - Extensions.DOUBLE_MIN); + return requireNonNull(aggregate(source.select(adapt(selector)), Extensions.DOUBLE_MIN)); } /** @@ -2519,7 +2542,7 @@ public static double min(Enumerable source, */ public static Double min(Enumerable source, NullableDoubleFunction1 selector) { - return aggregate(source.select(selector), null, Extensions.DOUBLE_MIN); + return aggregate(source.select(selector), Extensions.DOUBLE_MIN); } /** @@ -2528,8 +2551,7 @@ public static Double min(Enumerable source, */ public static int min(Enumerable source, IntegerFunction1 selector) { - return aggregate(source.select(adapt(selector)), null, - Extensions.INTEGER_MIN); + return requireNonNull(aggregate(source.select(adapt(selector)), Extensions.INTEGER_MIN)); } /** @@ -2586,7 +2608,7 @@ public static Float min(Enumerable source, * generic sequence and returns the minimum resulting * value. */ - public static > TResult min( + public static > @Nullable TResult min( Enumerable source, Function1 selector) { Function2 min = minFunction(); return aggregate(source.select(selector), null, min); @@ -2625,7 +2647,7 @@ public static Enumerable orderBy( */ public static Enumerable orderBy( Enumerable source, Function1 keySelector, - Comparator comparator) { + @Nullable Comparator comparator) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { // NOTE: TreeMap allows null comparator. But the caller of this method @@ -2672,13 +2694,15 @@ public static Enumerable orderBy( TKey key = keySelector.apply(o); if (needed >= 0 && size >= needed) { // the current row will never appear in the output, so just skip it - if (comparator.compare(key, map.lastKey()) >= 0) { + @KeyFor("map") TKey lastKey = map.lastKey(); + if (comparator.compare(key, lastKey) >= 0) { continue; } // remove last entry from tree map, so that we keep at most 'needed' rows - List l = map.get(map.lastKey()); + @SuppressWarnings("argument.type.incompatible") + List l = map.get(lastKey); if (l.size() == 1) { - map.remove(map.lastKey()); + map.remove(lastKey); } else { l.remove(l.size() - 1); } @@ -3067,9 +3091,9 @@ public static boolean sequenceEqual(Enumerable first, * {@code EqualityComparer}. */ public static boolean sequenceEqual(Enumerable first, - Enumerable second, EqualityComparer comparer) { - Objects.requireNonNull(first); - Objects.requireNonNull(second); + Enumerable second, @Nullable EqualityComparer comparer) { + requireNonNull(first, "first"); + requireNonNull(second, "second"); if (comparer == null) { comparer = new EqualityComparer() { @Override public boolean equal(TSource v1, TSource v2) { @@ -3159,7 +3183,7 @@ public static TSource single(Enumerable source, * exception if there is more than one element in the * sequence. */ - public static TSource singleOrDefault(Enumerable source) { + public static @Nullable TSource singleOrDefault(Enumerable source) { TSource toRet = null; try (Enumerator os = source.enumerator()) { if (os.moveNext()) { @@ -3180,7 +3204,7 @@ public static TSource singleOrDefault(Enumerable source) { * element exists; this method throws an exception if more than * one element satisfies the condition. */ - public static TSource singleOrDefault(Enumerable source, + public static @Nullable TSource singleOrDefault(Enumerable source, Predicate1 predicate) { TSource toRet = null; for (TSource s : source) { @@ -3576,6 +3600,7 @@ static LookupImpl toLookup_( while (os.moveNext()) { TSource o = os.current(); final TKey key = keySelector.apply(o); + @SuppressWarnings("nullness") List list = map.get(key); if (list == null) { // for first entry, use a singleton list to save space @@ -3774,7 +3799,7 @@ public static OrderedQueryable asOrderedQueryable( return source instanceof OrderedQueryable ? ((OrderedQueryable) source) : new EnumerableOrderedQueryable<>( - source, (Class) Object.class, null, null); + source, (Class) Object.class, requireNonNull(null), null); } /** Default implementation of {@link ExtendedEnumerable#into(Collection)}. */ @@ -3932,12 +3957,15 @@ static class SkipWhileEnumerator implements Enumerator { /** Enumerator that casts each value. * - * @param element type */ - static class CastingEnumerator implements Enumerator { - private final Enumerator enumerator; + * @param source element type + * @param element type*/ + @HasQualifierParameter(Nullable.class) + static class CastingEnumerator + implements Enumerator { + private final Enumerator enumerator; private final Class clazz; - CastingEnumerator(Enumerator enumerator, Class clazz) { + CastingEnumerator(Enumerator enumerator, Class clazz) { this.enumerator = enumerator; this.clazz = clazz; } @@ -3979,7 +4007,7 @@ static Wrapped upAs(EqualityComparer comparer, T element) { return comparer.hashCode(element); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { //noinspection unchecked return obj == this || obj instanceof Wrapped && comparer.equal(element, ((Wrapped) obj).element); @@ -4003,8 +4031,9 @@ protected WrapMap(Function0, V>> mapProvider, EqualityComparer this.comparer = comparer; } - @Override public Set> entrySet() { - return new AbstractSet>() { + @Override public Set> entrySet() { + return new AbstractSet>() { + @SuppressWarnings("override.return.invalid") @Override public Iterator> iterator() { final Iterator, V>> iterator = map.entrySet().iterator(); @@ -4031,23 +4060,26 @@ protected WrapMap(Function0, V>> mapProvider, EqualityComparer }; } - @Override public boolean containsKey(Object key) { + @SuppressWarnings("contracts.conditional.postcondition.not.satisfied") + @Override public boolean containsKey(@Nullable Object key) { return map.containsKey(wrap((K) key)); } + @Pure private Wrapped wrap(K key) { return Wrapped.upAs(comparer, key); } - @Override public V get(Object key) { + @Override public @Nullable V get(@Nullable Object key) { return map.get(wrap((K) key)); } - @Override public V put(K key, V value) { + @SuppressWarnings("contracts.postcondition.not.satisfied") + @Override public @Nullable V put(K key, V value) { return map.put(wrap(key), value); } - @Override public V remove(Object key) { + @Override public @Nullable V remove(@Nullable Object key) { return map.remove(wrap((K) key)); } @@ -4110,30 +4142,31 @@ private static class MergeJoinEnumerator rights = new ArrayList<>(); private final Enumerable leftEnumerable; private final Enumerable rightEnumerable; - private Enumerator leftEnumerator = null; - private Enumerator rightEnumerator = null; + private @Nullable Enumerator leftEnumerator = null; + private @Nullable Enumerator rightEnumerator = null; private final Function1 outerKeySelector; private final Function1 innerKeySelector; // extra predicate in case of non equi-join, in case of equi-join it will be null - private final Predicate2 extraPredicate; - private final Function2 resultSelector; + private final @Nullable Predicate2 extraPredicate; + private final Function2 resultSelector; private final JoinType joinType; // key comparator, possibly null (Comparable#compareTo to be used in that case) - private final Comparator comparator; + private final @Nullable Comparator comparator; private boolean done; - private Enumerator results = null; + private @Nullable Enumerator results = null; // used for LEFT/ANTI join: if right input is over, all remaining elements from left are results private boolean remainingLeft; private TResult current = (TResult) DUMMY; + @SuppressWarnings("method.invocation.invalid") MergeJoinEnumerator(Enumerable leftEnumerable, Enumerable rightEnumerable, Function1 outerKeySelector, Function1 innerKeySelector, - Predicate2 extraPredicate, - Function2 resultSelector, + @Nullable Predicate2 extraPredicate, + Function2 resultSelector, JoinType joinType, - Comparator comparator) { + @Nullable Comparator comparator) { this.leftEnumerable = leftEnumerable; this.rightEnumerable = rightEnumerable; this.outerKeySelector = outerKeySelector; @@ -4226,9 +4259,9 @@ private int compareNullsLast(TKey v0, TKey v1) { * enumerator. */ private boolean advance() { for (;;) { - TSource left = leftEnumerator.current(); + TSource left = requireNonNull(leftEnumerator, "leftEnumerator").current(); TKey leftKey = outerKeySelector.apply(left); - TInner right = rightEnumerator.current(); + TInner right = requireNonNull(rightEnumerator, "rightEnumerator").current(); TKey rightKey = innerKeySelector.apply(right); // iterate until finding matching keys (or ANTI join results) for (;;) { @@ -4422,7 +4455,9 @@ private boolean advanceRight(TInner right, TKey rightKey) { results = null; current = (TResult) DUMMY; remainingLeft = false; - leftEnumerator.reset(); + if (leftEnumerator != null) { + leftEnumerator.reset(); + } if (rightEnumerator != null) { rightEnumerator.reset(); } @@ -4430,7 +4465,9 @@ private boolean advanceRight(TInner right, TKey rightKey) { } @Override public void close() { - leftEnumerator.close(); + if (leftEnumerator != null) { + leftEnumerator.close(); + } if (rightEnumerator != null) { rightEnumerator.close(); } @@ -4444,10 +4481,10 @@ private boolean advanceRight(TInner right, TKey rightKey) { * @param right input record type */ private static class CartesianProductJoinEnumerator extends CartesianProductEnumerator { - private final Function2 resultSelector; + private final Function2 resultSelector; @SuppressWarnings("unchecked") - CartesianProductJoinEnumerator(Function2 resultSelector, + CartesianProductJoinEnumerator(Function2 resultSelector, Enumerator outer, Enumerator inner) { super(ImmutableList.of((Enumerator) outer, (Enumerator) inner)); this.resultSelector = resultSelector; @@ -4491,7 +4528,7 @@ public static Enumerable repeatUnion( private boolean seedProcessed = false; private int currentIteration = 0; private final Enumerator seedEnumerator = seed.enumerator(); - private Enumerator iterativeEnumerator = null; + private @Nullable Enumerator iterativeEnumerator = null; // Set to control duplicates, only used if "all" is false private final Set> processed = new HashSet<>(); @@ -4541,8 +4578,9 @@ private boolean checkValue(TSource value) { return false; } + Enumerator iterativeEnumerator = this.iterativeEnumerator; if (iterativeEnumerator == null) { - iterativeEnumerator = iteration.enumerator(); + this.iterativeEnumerator = iterativeEnumerator = iteration.enumerator(); } while (iterativeEnumerator.moveNext()) { @@ -4561,7 +4599,7 @@ private boolean checkValue(TSource value) { // current iteration level (which returned some values) is finished, go to next one current = (TSource) DUMMY; iterativeEnumerator.close(); - iterativeEnumerator = null; + this.iterativeEnumerator = null; currentIteration++; } } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableOrderedQueryable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableOrderedQueryable.java index a53e006f6745..2684c0246d78 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableOrderedQueryable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableOrderedQueryable.java @@ -20,6 +20,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.FunctionExpression; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Comparator; /** @@ -31,7 +33,7 @@ class EnumerableOrderedQueryable extends EnumerableQueryable implements OrderedQueryable { EnumerableOrderedQueryable(Enumerable enumerable, Class rowType, - QueryProvider provider, Expression expression) { + QueryProvider provider, @Nullable Expression expression) { super(provider, rowType, expression, enumerable); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableQueryable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableQueryable.java index a6724b7825e0..7a37a57c5f04 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableQueryable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableQueryable.java @@ -34,6 +34,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.FunctionExpression; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.Comparator; @@ -49,10 +51,10 @@ class EnumerableQueryable extends DefaultEnumerable private final QueryProvider provider; private final Class elementType; private final Enumerable enumerable; - private final Expression expression; + private final @Nullable Expression expression; EnumerableQueryable(QueryProvider provider, Class elementType, - Expression expression, Enumerable enumerable) { + @Nullable Expression expression, Enumerable enumerable) { this.enumerable = enumerable; this.elementType = elementType; this.provider = provider; @@ -153,7 +155,7 @@ protected Queryable queryable() { return EnumerableDefaults.ofType(getThis(), clazz).asQueryable(); } - @Override public Queryable defaultIfEmpty() { + @Override public Queryable<@Nullable T> defaultIfEmpty() { return EnumerableDefaults.defaultIfEmpty(getThis()).asQueryable(); } @@ -167,7 +169,7 @@ protected Queryable queryable() { return elementType; } - @Override public Expression getExpression() { + @Override public @Nullable Expression getExpression() { return expression; } @@ -177,7 +179,8 @@ protected Queryable queryable() { // ............. - @Override public T aggregate(FunctionExpression> selector) { + @Override public @Nullable T aggregate( + FunctionExpression> selector) { return EnumerableDefaults.aggregate(getThis(), selector.getFunction()); } @@ -260,7 +263,7 @@ protected Queryable queryable() { return EnumerableDefaults.first(getThis(), predicate.getFunction()); } - @Override public T firstOrDefault(FunctionExpression> predicate) { + @Override public @Nullable T firstOrDefault(FunctionExpression> predicate) { return EnumerableDefaults.firstOrDefault(getThis(), predicate.getFunction()); } @@ -373,7 +376,7 @@ protected Queryable queryable() { return EnumerableDefaults.last(getThis(), predicate.getFunction()); } - @Override public T lastOrDefault(FunctionExpression> predicate) { + @Override public @Nullable T lastOrDefault(FunctionExpression> predicate) { return EnumerableDefaults.lastOrDefault(getThis(), predicate.getFunction()); } @@ -381,12 +384,12 @@ protected Queryable queryable() { return EnumerableDefaults.longCount(getThis(), predicate.getFunction()); } - @Override public > TResult max( + @Override public > @Nullable TResult max( FunctionExpression> selector) { return EnumerableDefaults.max(getThis(), selector.getFunction()); } - @Override public > TResult min( + @Override public > @Nullable TResult min( FunctionExpression> selector) { return EnumerableDefaults.min(getThis(), selector.getFunction()); } @@ -466,7 +469,7 @@ protected Queryable queryable() { return EnumerableDefaults.single(getThis(), predicate.getFunction()); } - @Override public T singleOrDefault(FunctionExpression> predicate) { + @Override public @Nullable T singleOrDefault(FunctionExpression> predicate) { return EnumerableDefaults.singleOrDefault(getThis(), predicate.getFunction()); } @@ -557,7 +560,7 @@ protected Queryable queryable() { resultSelector.getFunction()).asQueryable(); } - @Override public T aggregate(Function2 func) { + @Override public @Nullable T aggregate(Function2<@Nullable T, T, T> func) { return EnumerableDefaults.aggregate(getThis(), func); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerator.java b/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerator.java index bd9e28be31fc..e026ed33022d 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerator.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Enumerator.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.framework.qual.Covariant; + /** * Supports a simple iteration over a collection. * @@ -26,6 +28,7 @@ * * @param Element type */ +@Covariant(0) public interface Enumerator extends AutoCloseable { /** * Gets the current element in the collection. diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java index 5ed792424254..60d5ee5b4945 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java @@ -33,6 +33,10 @@ import org.apache.calcite.linq4j.function.Predicate1; import org.apache.calcite.linq4j.function.Predicate2; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.framework.qual.Covariant; + import java.math.BigDecimal; import java.util.Collection; import java.util.Comparator; @@ -44,6 +48,7 @@ * * @param Element type */ +@Covariant(0) public interface ExtendedEnumerable { /** @@ -55,21 +60,21 @@ public interface ExtendedEnumerable { * @param func Operation * @param Return type */ - R foreach(Function1 func); + @Nullable R foreach(Function1 func); /** * Applies an accumulator function over a * sequence. */ - TSource aggregate(Function2 func); + @Nullable TSource aggregate(Function2<@Nullable TSource, TSource, TSource> func); /** * Applies an accumulator function over a * sequence. The specified seed value is used as the initial * accumulator value. */ - TAccumulate aggregate(TAccumulate seed, - Function2 func); + @PolyNull TAccumulate aggregate(@PolyNull TAccumulate seed, + Function2<@PolyNull TAccumulate, TSource, @PolyNull TAccumulate> func); /** * Applies an accumulator function over a @@ -263,14 +268,14 @@ TResult aggregate(TAccumulate seed, * the type parameter's default value in a singleton collection if * the sequence is empty. */ - Enumerable defaultIfEmpty(); + Enumerable<@Nullable TSource> defaultIfEmpty(); /** * Returns the elements of the specified sequence or * the specified value in a singleton collection if the sequence * is empty. */ - Enumerable defaultIfEmpty(TSource value); + Enumerable<@PolyNull TSource> defaultIfEmpty(@PolyNull TSource value); /** * Returns distinct elements from a sequence by using @@ -295,7 +300,7 @@ TResult aggregate(TAccumulate seed, * sequence or a default value if the index is out of * range. */ - TSource elementAtOrDefault(int index); + @Nullable TSource elementAtOrDefault(int index); /** * Produces the set difference of two sequences by @@ -344,14 +349,14 @@ Enumerable except(Enumerable enumerable1, * Returns the first element of a sequence, or a * default value if the sequence contains no elements. */ - TSource firstOrDefault(); + @Nullable TSource firstOrDefault(); /** * Returns the first element of the sequence that * satisfies a condition or a default value if no such element is * found. */ - TSource firstOrDefault(Predicate1 predicate); + @Nullable TSource firstOrDefault(Predicate1 predicate); /** * Groups the elements of a sequence according to a @@ -644,14 +649,14 @@ Enumerable correlateJoin( * Returns the last element of a sequence, or a * default value if the sequence contains no elements. */ - TSource lastOrDefault(); + @Nullable TSource lastOrDefault(); /** * Returns the last element of a sequence that * satisfies a condition or a default value if no such element is * found. */ - TSource lastOrDefault(Predicate1 predicate); + @Nullable TSource lastOrDefault(Predicate1 predicate); /** * Returns an long that represents the total number @@ -669,20 +674,20 @@ Enumerable correlateJoin( * Returns the maximum value in a generic * sequence. */ - TSource max(); + @Nullable TSource max(); /** * Invokes a transform function on each element of a * sequence and returns the maximum Decimal value. */ - BigDecimal max(BigDecimalFunction1 selector); + @Nullable BigDecimal max(BigDecimalFunction1 selector); /** * Invokes a transform function on each element of a * sequence and returns the maximum nullable Decimal * value. */ - BigDecimal max(NullableBigDecimalFunction1 selector); + @Nullable BigDecimal max(NullableBigDecimalFunction1 selector); /** * Invokes a transform function on each element of a @@ -695,7 +700,7 @@ Enumerable correlateJoin( * sequence and returns the maximum nullable Double * value. */ - Double max(NullableDoubleFunction1 selector); + @Nullable Double max(NullableDoubleFunction1 selector); /** * Invokes a transform function on each element of a @@ -708,7 +713,7 @@ Enumerable correlateJoin( * sequence and returns the maximum nullable int value. (Defined * by Enumerable.) */ - Integer max(NullableIntegerFunction1 selector); + @Nullable Integer max(NullableIntegerFunction1 selector); /** * Invokes a transform function on each element of a @@ -721,7 +726,7 @@ Enumerable correlateJoin( * sequence and returns the maximum nullable long value. (Defined * by Enumerable.) */ - Long max(NullableLongFunction1 selector); + @Nullable Long max(NullableLongFunction1 selector); /** * Invokes a transform function on each element of a @@ -734,34 +739,34 @@ Enumerable correlateJoin( * sequence and returns the maximum nullable Float * value. */ - Float max(NullableFloatFunction1 selector); + @Nullable Float max(NullableFloatFunction1 selector); /** * Invokes a transform function on each element of a * generic sequence and returns the maximum resulting * value. */ - > TResult max( + > @Nullable TResult max( Function1 selector); /** * Returns the minimum value in a generic * sequence. */ - TSource min(); + @Nullable TSource min(); /** * Invokes a transform function on each element of a * sequence and returns the minimum Decimal value. */ - BigDecimal min(BigDecimalFunction1 selector); + @Nullable BigDecimal min(BigDecimalFunction1 selector); /** * Invokes a transform function on each element of a * sequence and returns the minimum nullable Decimal * value. */ - BigDecimal min(NullableBigDecimalFunction1 selector); + @Nullable BigDecimal min(NullableBigDecimalFunction1 selector); /** * Invokes a transform function on each element of a @@ -774,7 +779,7 @@ > TResult max( * sequence and returns the minimum nullable Double * value. */ - Double min(NullableDoubleFunction1 selector); + @Nullable Double min(NullableDoubleFunction1 selector); /** * Invokes a transform function on each element of a @@ -787,7 +792,7 @@ > TResult max( * sequence and returns the minimum nullable int value. (Defined * by Enumerable.) */ - Integer min(NullableIntegerFunction1 selector); + @Nullable Integer min(NullableIntegerFunction1 selector); /** * Invokes a transform function on each element of a @@ -800,7 +805,7 @@ > TResult max( * sequence and returns the minimum nullable long value. (Defined * by Enumerable.) */ - Long min(NullableLongFunction1 selector); + @Nullable Long min(NullableLongFunction1 selector); /** * Invokes a transform function on each element of a @@ -813,14 +818,14 @@ > TResult max( * sequence and returns the minimum nullable Float * value. */ - Float min(NullableFloatFunction1 selector); + @Nullable Float min(NullableFloatFunction1 selector); /** * Invokes a transform function on each element of a * generic sequence and returns the minimum resulting * value. */ - > TResult min( + > @Nullable TResult min( Function1 selector); /** @@ -956,7 +961,7 @@ boolean sequenceEqual(Enumerable enumerable1, * exception if there is more than one element in the * sequence. */ - TSource singleOrDefault(); + @Nullable TSource singleOrDefault(); /** * Returns the only element of a sequence that @@ -964,7 +969,7 @@ boolean sequenceEqual(Enumerable enumerable1, * element exists; this method throws an exception if more than * one element satisfies the condition. */ - TSource singleOrDefault(Predicate1 predicate); + @Nullable TSource singleOrDefault(Predicate1 predicate); /** * Bypasses a specified number of elements in a diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedQueryable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedQueryable.java index 1f5db1369582..ef3c2f7919a6 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedQueryable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedQueryable.java @@ -33,6 +33,9 @@ import org.apache.calcite.linq4j.function.Predicate2; import org.apache.calcite.linq4j.tree.FunctionExpression; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.Covariant; + import java.math.BigDecimal; import java.util.Comparator; @@ -41,13 +44,14 @@ * * @param Element type */ +@Covariant(0) interface ExtendedQueryable extends ExtendedEnumerable { /** * Applies an accumulator function over a sequence. */ - TSource aggregate( - FunctionExpression> selector); + @Nullable TSource aggregate( + FunctionExpression> selector); /** * Applies an accumulator function over a @@ -172,7 +176,7 @@ Long averageNullableLong( * the type parameter's default value in a singleton collection if * the sequence is empty. */ - @Override Queryable defaultIfEmpty(); + @Override Queryable<@Nullable TSource> defaultIfEmpty(); /** * Returns distinct elements from a sequence by using @@ -227,7 +231,7 @@ Long averageNullableLong( * satisfies a specified condition or a default value if no such * element is found. */ - TSource firstOrDefault(FunctionExpression> predicate); + @Nullable TSource firstOrDefault(FunctionExpression> predicate); /** * Groups the elements of a sequence according to a @@ -400,7 +404,7 @@ Queryable join(Enumerable inner, * satisfies a condition or a default value if no such element is * found. */ - TSource lastOrDefault(FunctionExpression> predicate); + @Nullable TSource lastOrDefault(FunctionExpression> predicate); /** * Returns an long that represents the number of @@ -413,7 +417,7 @@ Queryable join(Enumerable inner, * generic {@code IQueryable} and returns the maximum resulting * value. */ - > TResult max( + > @Nullable TResult max( FunctionExpression> selector); /** @@ -421,7 +425,7 @@ > TResult max( * generic {@code IQueryable} and returns the minimum resulting * value. */ - > TResult min( + > @Nullable TResult min( FunctionExpression> selector); /** @@ -564,7 +568,7 @@ Queryable selectManyN( * exception if there is more than one element in the * sequence. */ - @Override TSource singleOrDefault(); + @Override @Nullable TSource singleOrDefault(); /** * Returns the only element of a sequence that @@ -572,7 +576,7 @@ Queryable selectManyN( * element exists; this method throws an exception if more than * one element satisfies the condition. */ - TSource singleOrDefault(FunctionExpression> predicate); + @Nullable TSource singleOrDefault(FunctionExpression> predicate); /** * Bypasses a specified number of elements in a diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Grouping.java b/linq4j/src/main/java/org/apache/calcite/linq4j/Grouping.java index f3091afc27dc..aede1d0254f9 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/Grouping.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Grouping.java @@ -16,12 +16,15 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.framework.qual.Covariant; + /** * Represents a collection of objects that have a common key. * * @param Key type * @param Element type */ +@Covariant(0) public interface Grouping extends Enumerable { /** * Gets the key of this Grouping. diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/GroupingImpl.java b/linq4j/src/main/java/org/apache/calcite/linq4j/GroupingImpl.java index a03d2d73b85e..68098a8d94ed 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/GroupingImpl.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/GroupingImpl.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Map; import java.util.Objects; @@ -26,7 +28,8 @@ * @param Key type * @param Value type */ -class GroupingImpl extends AbstractEnumerable +@SuppressWarnings("type.argument.type.incompatible") +class GroupingImpl extends AbstractEnumerable implements Grouping, Map.Entry> { private final K key; private final List values; @@ -48,7 +51,7 @@ class GroupingImpl extends AbstractEnumerable return key.hashCode() ^ values.hashCode(); } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj instanceof GroupingImpl && key.equals(((GroupingImpl) obj).key) && values.equals(((GroupingImpl) obj).values); diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java b/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java index dab1d5edfc6d..964df314dd02 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java @@ -18,6 +18,8 @@ import org.apache.calcite.linq4j.function.Function1; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -36,7 +38,7 @@ private Linq4j() {} private static final Object DUMMY = new Object(); - public static Method getMethod(String className, String methodName, + public static @Nullable Method getMethod(String className, String methodName, Class... parameterTypes) { try { return Class.forName(className).getMethod(methodName, parameterTypes); @@ -203,8 +205,8 @@ private static Enumerator listEnumerator(List list) { * @param Element type * @return Enumerator */ - public static Enumerator transform(Enumerator enumerator, - final Function1 func) { + public static Enumerator transform(Enumerator enumerator, + final Function1 func) { return new TransformedEnumerator(enumerator) { @Override protected E transform(F from) { return func.apply(from); @@ -423,7 +425,7 @@ public static T requireNonNull(T o) { } /** Closes an iterator, if it can be closed. */ - private static void closeIterator(Iterator iterator) { + private static void closeIterator(@Nullable Iterator iterator) { if (iterator instanceof AutoCloseable) { try { ((AutoCloseable) iterator).close(); @@ -441,7 +443,7 @@ private static void closeIterator(Iterator iterator) { @SuppressWarnings("unchecked") static class IterableEnumerator implements Enumerator { private final Iterable iterable; - Iterator iterator; + @Nullable Iterator iterator; T current; IterableEnumerator(Iterable iterable) { @@ -458,7 +460,7 @@ static class IterableEnumerator implements Enumerator { } @Override public boolean moveNext() { - if (iterator.hasNext()) { + if (Objects.requireNonNull(iterator, "iterator").hasNext()) { current = iterator.next(); return true; } @@ -564,6 +566,7 @@ protected Collection getCollection() { return getCollection().size(); } + @SuppressWarnings("argument.type.incompatible") @Override public boolean contains(T element) { return getCollection().contains(element); } @@ -644,7 +647,7 @@ private static class SingletonEnumerator implements Enumerator { /** Enumerator that returns one null element. * * @param element type */ - private static class SingletonNullEnumerator implements Enumerator { + private static class SingletonNullEnumerator<@Nullable E> implements Enumerator { int i = 0; @Override public E current() { diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/LookupImpl.java b/linq4j/src/main/java/org/apache/calcite/linq4j/LookupImpl.java index 7a5c4d4f41f3..2e1d125e091b 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/LookupImpl.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/LookupImpl.java @@ -18,6 +18,9 @@ import org.apache.calcite.linq4j.function.Function2; +import org.checkerframework.checker.nullness.qual.KeyFor; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; @@ -81,27 +84,29 @@ class LookupImpl extends AbstractEnumerable> return map.isEmpty(); } - @Override public boolean containsKey(Object key) { + @SuppressWarnings("contracts.conditional.postcondition.not.satisfied") + @Override public boolean containsKey(@Nullable Object key) { return map.containsKey(key); } - @Override public boolean containsValue(Object value) { + @Override public boolean containsValue(@Nullable Object value) { @SuppressWarnings("unchecked") List list = (List) value; return map.containsValue(list); } - @Override public Enumerable get(Object key) { + @Override public @Nullable Enumerable get(@Nullable Object key) { final List list = map.get(key); return list == null ? null : Linq4j.asEnumerable(list); } - @Override public Enumerable put(K key, Enumerable value) { + @SuppressWarnings("contracts.postcondition.not.satisfied") + @Override public @Nullable Enumerable put(K key, Enumerable value) { final List list = map.put(key, value.toList()); return list == null ? null : Linq4j.asEnumerable(list); } - @Override public Enumerable remove(Object key) { + @Override public @Nullable Enumerable remove(@Nullable Object key) { final List list = map.remove(key); return list == null ? null : Linq4j.asEnumerable(list); } @@ -116,7 +121,8 @@ class LookupImpl extends AbstractEnumerable> map.clear(); } - @Override public Set keySet() { + @SuppressWarnings("return.type.incompatible") + @Override public Set<@KeyFor("this") K> keySet() { return map.keySet(); } @@ -147,8 +153,9 @@ class LookupImpl extends AbstractEnumerable> }; } - @Override public Set>> entrySet() { - final Set>> entries = map.entrySet(); + @SuppressWarnings("return.type.incompatible") + @Override public Set>> entrySet() { + final Set>> entries = map.entrySet(); return new AbstractSet>>() { @Override public Iterator>> iterator() { final Iterator>> iterator = entries.iterator(); diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryEnumerator.java b/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryEnumerator.java index 13167b122844..c09661cde38b 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryEnumerator.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryEnumerator.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.concurrent.atomic.AtomicInteger; /** @@ -23,7 +25,7 @@ * * @param Row value */ -public class MemoryEnumerator implements Enumerator> { +public class MemoryEnumerator<@Nullable E> implements Enumerator> { private final Enumerator enumerator; private final MemoryFactory memoryFactory; private final AtomicInteger prevCounter; diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryFactory.java b/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryFactory.java index 45d641a6c051..f68fa91777e7 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryFactory.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/MemoryFactory.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Arrays; /** @@ -30,7 +32,7 @@ public class MemoryFactory { // Index: 0 1 2 3 4 // Idea -2 -1 0 +1 +2 ModularInteger offset; - private Object[] values; + private final @Nullable Object[] values; public MemoryFactory(int history, int future) { this.history = history; @@ -62,10 +64,10 @@ public static class Memory { private final int history; private final int future; private final ModularInteger offset; - private final Object[] values; + private final @Nullable Object[] values; public Memory(int history, int future, - ModularInteger offset, Object[] values) { + ModularInteger offset, @Nullable Object[] values) { this.history = history; this.future = future; this.offset = offset; diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/ModularInteger.java b/linq4j/src/main/java/org/apache/calcite/linq4j/ModularInteger.java index e6b7695eefe7..12cc470c468d 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/ModularInteger.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/ModularInteger.java @@ -18,6 +18,8 @@ import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Represents an integer in modular arithmetic. * Its {@code value} is between 0 and {@code m - 1} for some modulus {@code m}. @@ -35,7 +37,7 @@ class ModularInteger { this.modulus = modulus; } - @Override public boolean equals(Object obj) { + @Override public boolean equals(@Nullable Object obj) { return obj == this || obj instanceof ModularInteger && value == ((ModularInteger) obj).value diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Nullness.java b/linq4j/src/main/java/org/apache/calcite/linq4j/Nullness.java new file mode 100644 index 000000000000..1248b63cae9f --- /dev/null +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Nullness.java @@ -0,0 +1,57 @@ +/* + * 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.calcite.linq4j; + +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; + +/** + * The methods in this class allow to cast nullable reference to a non-nullable one. + * This is an internal class, and it is not meant to be used as a public API. + *

    The class enables to remove checker-qual runtime dependency, and helps IDEs to see + * the resulting types of {@code castNonNull} better

    + */ +@SuppressWarnings({"cast.unsafe", "NullableProblems", "contracts.postcondition.not.satisfied"}) +public class Nullness { + private Nullness() { + } + + /** + * Enables to threat nullable type as non-nullable with no assertions. + * + *

    It is useful in the case you have a nullable lately-initialized field like the following: + * {@code class Wrapper { @Nullable T value; }}. + * That signature allows to use {@code Wrapper} with both nullable or non-nullable types: + * {@code Wrapper<@Nullable Integer>} vs {@code Wrapper}. Suppose you need to implement + * {@code T get() { return value; }} The issue is checkerframework does not permit that + * because {@code T} has unknown nullability, so the following needs to be used: + * {@code T get() { return sneakyNull(value); }}

    + * + * @param the type of the reference + * @param ref a reference of @Nullable type, that is non-null at run time + * @return the argument, casted to have the type qualifier @NonNull + */ + @Pure + public static @EnsuresNonNull("#1") + @NonNull T castNonNull( + @Nullable T ref) { + //noinspection ConstantConditions + return (@NonNull T) ref; + } +} diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Queryable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/Queryable.java index afdda6a16640..9f9f049744ee 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/Queryable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Queryable.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.framework.qual.Covariant; + /** * Provides functionality to evaluate queries against a specific data source * wherein the type of the data is known. @@ -24,5 +26,6 @@ * * @param Element type */ +@Covariant(0) public interface Queryable extends RawQueryable, ExtendedQueryable { } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableDefaults.java b/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableDefaults.java index b4f5a0694292..bef7d183e0ec 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableDefaults.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableDefaults.java @@ -36,11 +36,15 @@ import org.apache.calcite.linq4j.tree.Expressions; import org.apache.calcite.linq4j.tree.FunctionExpression; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.Comparator; import java.util.Iterator; +import static java.util.Objects.requireNonNull; + /** * Default implementations for methods in the {@link Queryable} interface. */ @@ -741,13 +745,13 @@ public static Queryable reverse(Queryable source) { public static Queryable select(Queryable source, FunctionExpression> selector) { return source.getProvider().createQuery( - Expressions.call(source.getExpression(), "select", selector), + Expressions.call(requireNonNull(source.getExpression()), "select", selector), functionResultType(selector)); } private static Type functionResultType( FunctionExpression> selector) { - return selector.body.getType(); + return requireNonNull(selector.body, "selector.body").getType(); } /** @@ -1202,7 +1206,7 @@ protected NonLeafReplayableQueryable(Queryable original) { return original.getElementType(); } - @Override public Expression getExpression() { + @Override public @Nullable Expression getExpression() { return original.getExpression(); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableFactory.java b/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableFactory.java index 9db00aefed06..2fb884735830 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableFactory.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableFactory.java @@ -33,6 +33,10 @@ import org.apache.calcite.linq4j.function.Predicate2; import org.apache.calcite.linq4j.tree.FunctionExpression; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.framework.qual.Covariant; + import java.math.BigDecimal; import java.util.Comparator; @@ -41,13 +45,14 @@ * * @param Element type */ +@Covariant(0) public interface QueryableFactory { /** * Applies an accumulator function over a sequence. */ - T aggregate(Queryable source, - FunctionExpression> selector); + @Nullable T aggregate(Queryable source, + FunctionExpression> selector); /** * Applies an accumulator function over a @@ -201,14 +206,14 @@ boolean contains(Queryable source, T element, * the type parameter's default value in a singleton collection if * the sequence is empty. */ - Queryable defaultIfEmpty(Queryable source); + Queryable<@Nullable T> defaultIfEmpty(Queryable source); /** * Returns the elements of the specified sequence or * the specified value in a singleton collection if the sequence * is empty. */ - Queryable defaultIfEmpty(Queryable source, T value); + Queryable<@PolyNull T> defaultIfEmpty(Queryable source, @PolyNull T value); /** * Returns distinct elements from a sequence by using @@ -282,14 +287,14 @@ Queryable except(Queryable source, Enumerable enumerable, * Returns the first element of a sequence, or a * default value if the sequence contains no elements. */ - T firstOrDefault(Queryable source); + @Nullable T firstOrDefault(Queryable source); /** * Returns the first element of a sequence that * satisfies a specified condition or a default value if no such * element is found. */ - T firstOrDefault(Queryable source, + @Nullable T firstOrDefault(Queryable source, FunctionExpression> predicate); /** diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableRecorder.java b/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableRecorder.java index c7c47b92d9e7..8e0dd8f3a33e 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableRecorder.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/QueryableRecorder.java @@ -33,12 +33,18 @@ import org.apache.calcite.linq4j.function.Predicate2; import org.apache.calcite.linq4j.tree.FunctionExpression; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; +import org.checkerframework.framework.qual.Covariant; + import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.Comparator; import static org.apache.calcite.linq4j.QueryableDefaults.NonLeafReplayableQueryable; +import static java.util.Objects.requireNonNull; + /** * Implementation of {@link QueryableFactory} that records each event * and returns an object that can replay the event when you call its @@ -47,6 +53,7 @@ * * @param Element type */ +@Covariant(0) public class QueryableRecorder implements QueryableFactory { private static final QueryableRecorder INSTANCE = new QueryableRecorder(); @@ -55,8 +62,8 @@ public static QueryableRecorder instance() { return INSTANCE; } - @Override public T aggregate(final Queryable source, - final FunctionExpression> func) { + @Override public @Nullable T aggregate(final Queryable source, + final FunctionExpression> func) { return new QueryableDefaults.NonLeafReplayableQueryable(source) { @Override public void replay(QueryableFactory factory) { factory.aggregate(source, func); @@ -253,7 +260,7 @@ public static QueryableRecorder instance() { }.castSingle(); // CHECKSTYLE: IGNORE 0 } - @Override public Queryable defaultIfEmpty(final Queryable source) { + @Override public Queryable<@Nullable T> defaultIfEmpty(final Queryable source) { return new NonLeafReplayableQueryable(source) { @Override public void replay(QueryableFactory factory) { factory.defaultIfEmpty(source); @@ -261,7 +268,9 @@ public static QueryableRecorder instance() { }; } - @Override public Queryable defaultIfEmpty(final Queryable source, final T value) { + @SuppressWarnings("return.type.incompatible") + @Override public Queryable<@PolyNull T> defaultIfEmpty(final Queryable source, + final @PolyNull T value) { return new NonLeafReplayableQueryable(source) { @Override public void replay(QueryableFactory factory) { factory.defaultIfEmpty(source, value); @@ -347,7 +356,7 @@ public static QueryableRecorder instance() { }.single(); // CHECKSTYLE: IGNORE 0 } - @Override public T firstOrDefault(final Queryable source) { + @Override public @Nullable T firstOrDefault(final Queryable source) { return new NonLeafReplayableQueryable(source) { @Override public void replay(QueryableFactory factory) { factory.firstOrDefault(source); @@ -355,7 +364,7 @@ public static QueryableRecorder instance() { }.single(); // CHECKSTYLE: IGNORE 0 } - @Override public T firstOrDefault(final Queryable source, + @Override public @Nullable T firstOrDefault(final Queryable source, final FunctionExpression> predicate) { return new NonLeafReplayableQueryable(source) { @Override public void replay(QueryableFactory factory) { @@ -688,7 +697,7 @@ public static QueryableRecorder instance() { } @Override public Type getElementType() { - return selector.body.type; + return requireNonNull(selector.body, "selector.body").type; } }.castQueryable(); // CHECKSTYLE: IGNORE 0 } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/RawEnumerable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/RawEnumerable.java index 4dc6538f6bc8..35d3ac1db50e 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/RawEnumerable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/RawEnumerable.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j; +import org.checkerframework.framework.qual.Covariant; + /** * Exposes the enumerator, which supports a simple iteration over a collection, * without the extension methods. @@ -29,6 +31,7 @@ * @param Element type * @see Enumerable */ +@Covariant(0) public interface RawEnumerable { /** * Returns an enumerator that iterates through a collection. diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/RawQueryable.java b/linq4j/src/main/java/org/apache/calcite/linq4j/RawQueryable.java index 7a2bacfe3867..953f6a07411c 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/RawQueryable.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/RawQueryable.java @@ -18,6 +18,9 @@ import org.apache.calcite.linq4j.tree.Expression; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.Covariant; + import java.lang.reflect.Type; /** @@ -29,6 +32,7 @@ * * @param Element type */ +@Covariant(0) public interface RawQueryable extends Enumerable { /** * Gets the type of the element(s) that are returned when the expression @@ -38,8 +42,9 @@ public interface RawQueryable extends Enumerable { /** * Gets the expression tree that is associated with this Queryable. + * @return null if the expression is not available */ - Expression getExpression(); + @Nullable Expression getExpression(); /** * Gets the query provider that is associated with this data source. diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/TransformedEnumerator.java b/linq4j/src/main/java/org/apache/calcite/linq4j/TransformedEnumerator.java index 6fc726d4a56d..f731529556f0 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/TransformedEnumerator.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/TransformedEnumerator.java @@ -23,9 +23,9 @@ * @param Element type */ public abstract class TransformedEnumerator implements Enumerator { - protected final Enumerator enumerator; + protected final Enumerator enumerator; - protected TransformedEnumerator(Enumerator enumerator) { + protected TransformedEnumerator(Enumerator enumerator) { this.enumerator = enumerator; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/function/Functions.java b/linq4j/src/main/java/org/apache/calcite/linq4j/function/Functions.java index ba7e261553b5..c3b9877a6bb5 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/function/Functions.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/function/Functions.java @@ -16,6 +16,10 @@ */ package org.apache.calcite.linq4j.function; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; + import java.io.Serializable; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -72,10 +76,11 @@ private Functions() {} private static final EqualityComparer IDENTITY_COMPARER = new IdentityEqualityComparer(); - private static final EqualityComparer ARRAY_COMPARER = + private static final EqualityComparer<@Nullable Object[]> ARRAY_COMPARER = new ArrayEqualityComparer(); - private static final Function1 CONSTANT_NULL_FUNCTION1 = s -> null; + private static final Function1 CONSTANT_NULL_FUNCTION1 = + (Function1) s -> null; private static final Function1 TO_STRING_FUNCTION1 = (Function1) Object::toString; @@ -483,12 +488,12 @@ public static EqualityComparer selectorComparer( /** Array equality comparer. */ private static class ArrayEqualityComparer - implements EqualityComparer { - @Override public boolean equal(Object[] v1, Object[] v2) { + implements EqualityComparer<@Nullable Object[]> { + @Override public boolean equal(@Nullable Object[] v1, @Nullable Object[] v2) { return Arrays.deepEquals(v1, v2); } - @Override public int hashCode(Object[] t) { + @Override public int hashCode(@Nullable Object[] t) { return Arrays.deepHashCode(t); } } @@ -525,7 +530,7 @@ private static final class SelectorEqualityComparer } @Override public int hashCode(T t) { - return t == null ? 0x789d : selector.apply(t).hashCode(); + return t == null ? 0x789d : Objects.hashCode(selector.apply(t)); } } @@ -606,7 +611,7 @@ private static class NullsLastReverseComparator * @param result type * @param first argument type * @param second argument type */ - private static final class Ignore + private static final class Ignore<@Nullable R, T0, T1> implements Function0, Function1, Function2 { @Override public R apply() { return null; @@ -620,7 +625,13 @@ private static final class Ignore return null; } - static final Ignore INSTANCE = new Ignore(); + @DefaultQualifier( + value = Nullable.class, + locations = { + TypeUseLocation.LOWER_BOUND, + TypeUseLocation.UPPER_BOUND, + }) + static final Ignore INSTANCE = new Ignore<>(); } /** List that generates each element using a function. diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/package-info.java b/linq4j/src/main/java/org/apache/calcite/linq4j/package-info.java index cf57933faeaa..726df9815271 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/package-info.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/package-info.java @@ -18,4 +18,11 @@ /** * Language-integrated query for Java (linq4j) main package. */ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.FIELD) +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.PARAMETER) +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.RETURN) package org.apache.calcite.linq4j; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/AbstractNode.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/AbstractNode.java index dbbb0286077e..611eedec2408 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/AbstractNode.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/AbstractNode.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Objects; @@ -70,12 +72,12 @@ void accept(ExpressionWriter writer, int lprec, int rprec) { "visit not supported: " + getClass() + ":" + nodeType); } - public Object evaluate(Evaluator evaluator) { + public @Nullable Object evaluate(Evaluator evaluator) { throw new RuntimeException( "evaluation not supported: " + getClass() + ":" + nodeType); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ArrayLengthRecordField.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ArrayLengthRecordField.java index cabb59bb11e3..a5dbb5681e65 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ArrayLengthRecordField.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ArrayLengthRecordField.java @@ -16,10 +16,14 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Array; import java.lang.reflect.Type; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * Length field of a RecordType. */ @@ -50,15 +54,15 @@ public ArrayLengthRecordField(String fieldName, Class clazz) { return 0; } - @Override public Object get(Object o) throws IllegalAccessException { - return Array.getLength(o); + @Override public Object get(@Nullable Object o) throws IllegalAccessException { + return Array.getLength(requireNonNull(o)); } @Override public Type getDeclaringClass() { return clazz; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BinaryExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BinaryExpression.java index bd41b048d134..9fba3ec5e73a 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BinaryExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BinaryExpression.java @@ -16,16 +16,20 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * Represents an expression that has a binary operator. */ public class BinaryExpression extends Expression { public final Expression expression0; public final Expression expression1; - private final Primitive primitive; + private final @Nullable Primitive primitive; BinaryExpression(ExpressionType nodeType, Type type, Expression expression0, Expression expression1) { @@ -51,9 +55,12 @@ public class BinaryExpression extends Expression { @Override public Object evaluate(Evaluator evaluator) { switch (nodeType) { case AndAlso: - return (Boolean) expression0.evaluate(evaluator) - && (Boolean) expression1.evaluate(evaluator); + return evaluateBoolean(evaluator, expression0) + && evaluateBoolean(evaluator, expression1); case Add: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) + evaluateInt(expression1, evaluator); @@ -71,6 +78,9 @@ public class BinaryExpression extends Expression { throw cannotEvaluate(); } case Divide: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) / evaluateInt(expression1, evaluator); @@ -88,9 +98,11 @@ public class BinaryExpression extends Expression { throw cannotEvaluate(); } case Equal: - return expression0.evaluate(evaluator) - .equals(expression1.evaluate(evaluator)); + return Objects.equals(expression0.evaluate(evaluator), expression1.evaluate(evaluator)); case GreaterThan: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) > evaluateInt(expression1, evaluator); @@ -108,6 +120,9 @@ public class BinaryExpression extends Expression { throw cannotEvaluate(); } case GreaterThanOrEqual: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) >= evaluateInt(expression1, evaluator); @@ -125,6 +140,9 @@ public class BinaryExpression extends Expression { throw cannotEvaluate(); } case LessThan: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) < evaluateInt(expression1, evaluator); @@ -142,6 +160,9 @@ public class BinaryExpression extends Expression { throw cannotEvaluate(); } case LessThanOrEqual: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) <= evaluateInt(expression1, evaluator); @@ -159,6 +180,9 @@ public class BinaryExpression extends Expression { throw cannotEvaluate(); } case Multiply: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) * evaluateInt(expression1, evaluator); @@ -176,12 +200,14 @@ public class BinaryExpression extends Expression { throw cannotEvaluate(); } case NotEqual: - return !expression0.evaluate(evaluator) - .equals(expression1.evaluate(evaluator)); + return !Objects.equals(expression0.evaluate(evaluator), expression1.evaluate(evaluator)); case OrElse: - return (Boolean) expression0.evaluate(evaluator) - || (Boolean) expression1.evaluate(evaluator); + return evaluateBoolean(evaluator, expression0) + || evaluateBoolean(evaluator, expression1); case Subtract: + if (primitive == null) { + throw cannotEvaluate(); + } switch (primitive) { case INT: return evaluateInt(expression0, evaluator) - evaluateInt(expression1, evaluator); @@ -217,31 +243,43 @@ private RuntimeException cannotEvaluate() { + nodeType + ", primitive=" + primitive); } + private boolean evaluateBoolean(Evaluator evaluator, Expression expression) { + return (Boolean) requireNonNull( + expression.evaluate(evaluator), + () -> "boolean expected, got null while evaluating " + expression); + } + + private Number evaluateNumber(Expression expression, Evaluator evaluator) { + return (Number) requireNonNull( + expression.evaluate(evaluator), + () -> "number expected, got null while evaluating " + expression); + } + private int evaluateInt(Expression expression, Evaluator evaluator) { - return ((Number) expression.evaluate(evaluator)).intValue(); + return evaluateNumber(expression, evaluator).intValue(); } private short evaluateShort(Expression expression, Evaluator evaluator) { - return ((Number) expression.evaluate(evaluator)).shortValue(); + return evaluateNumber(expression, evaluator).shortValue(); } private long evaluateLong(Expression expression, Evaluator evaluator) { - return ((Number) expression.evaluate(evaluator)).longValue(); + return evaluateNumber(expression, evaluator).longValue(); } private byte evaluateByte(Expression expression, Evaluator evaluator) { - return ((Number) expression.evaluate(evaluator)).byteValue(); + return evaluateNumber(expression, evaluator).byteValue(); } private float evaluateFloat(Expression expression, Evaluator evaluator) { - return ((Number) expression.evaluate(evaluator)).floatValue(); + return evaluateNumber(expression, evaluator).floatValue(); } private double evaluateDouble(Expression expression, Evaluator evaluator) { - return ((Number) expression.evaluate(evaluator)).doubleValue(); + return evaluateNumber(expression, evaluator).doubleValue(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockBuilder.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockBuilder.java index 7651d3895e43..d8f72bf68f0c 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockBuilder.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockBuilder.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; @@ -26,6 +29,8 @@ import java.util.Map; import java.util.Set; +import static java.util.Objects.requireNonNull; + /** * Builder for {@link BlockStatement}. * @@ -41,7 +46,7 @@ public class BlockBuilder { new HashMap<>(); private final boolean optimizing; - private final BlockBuilder parent; + private final @Nullable BlockBuilder parent; private static final Shuttle OPTIMIZE_SHUTTLE = new OptimizeShuttle(); @@ -66,7 +71,7 @@ public BlockBuilder(boolean optimizing) { * * @param optimizing Whether to eliminate common sub-expressions */ - public BlockBuilder(boolean optimizing, BlockBuilder parent) { + public BlockBuilder(boolean optimizing, @Nullable BlockBuilder parent) { this.optimizing = optimizing; this.parent = parent; } @@ -153,7 +158,8 @@ public Expression append(String name, BlockStatement block, result = ((DeclarationStatement) statement).parameter; } else if (statement instanceof GotoStatement) { statements.remove(statements.size() - 1); - result = append_(name, ((GotoStatement) statement).expression, + result = append_(name, + requireNonNull(((GotoStatement) statement).expression, "expression"), optimize); if (isSimpleExpression(result)) { // already simple; no need to declare a variable or @@ -169,7 +175,7 @@ public Expression append(String name, BlockStatement block, } } } - return result; + return requireNonNull(result, () -> "empty result when appending name=" + name + ", " + block); } /** @@ -184,7 +190,7 @@ public Expression append(String name, Expression expression) { /** * Appends an expression to a list of statements, if it is not null. */ - public Expression appendIfNotNull(String name, Expression expression) { + public @PolyNull Expression appendIfNotNull(String name, @PolyNull Expression expression) { if (expression == null) { return null; } @@ -233,7 +239,7 @@ private Expression append_(String name, Expression expression, * @param expr expression to test * @return true when given expression is safe to always inline */ - protected boolean isSimpleExpression(Expression expr) { + protected boolean isSimpleExpression(@Nullable Expression expr) { if (expr instanceof ParameterExpression || expr instanceof ConstantExpression) { return true; @@ -285,7 +291,7 @@ private Expression normalizeDeclaration(DeclarationStatement decl) { * @param expr expression to test * @return existing ParameterExpression or null */ - public DeclarationStatement getComputedExpression(Expression expr) { + public @Nullable DeclarationStatement getComputedExpression(Expression expr) { if (parent != null) { DeclarationStatement decl = parent.getComputedExpression(expr); if (decl != null) { diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockStatement.java index 8d8945c53584..1bec4f4e250e 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/BlockStatement.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.HashSet; import java.util.List; @@ -38,7 +41,10 @@ public class BlockStatement extends Statement { assert distinctVariables(true); } - private boolean distinctVariables(boolean fail) { + private boolean distinctVariables( + @UnderInitialization(BlockStatement.class) BlockStatement this, + boolean fail + ) { Set names = new HashSet<>(); for (Statement statement : statements) { if (statement instanceof DeclarationStatement) { @@ -75,7 +81,7 @@ private boolean distinctVariables(boolean fail) { writer.end("}\n"); } - @Override public Object evaluate(Evaluator evaluator) { + @Override public @Nullable Object evaluate(Evaluator evaluator) { Object o = null; for (Statement statement : statements) { o = statement.evaluate(evaluator); @@ -83,7 +89,7 @@ private boolean distinctVariables(boolean fail) { return o; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Blocks.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Blocks.java index 05e3201e661e..993470ca6c1f 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Blocks.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Blocks.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import static java.util.Objects.requireNonNull; + /** *

    Helper methods concerning {@link BlockStatement}s.

    * @@ -71,7 +73,7 @@ public static Expression simple(BlockStatement block) { if (block.statements.size() == 1) { Statement statement = block.statements.get(0); if (statement instanceof GotoStatement) { - return ((GotoStatement) statement).expression; + return requireNonNull(((GotoStatement) statement).expression); } } throw new AssertionError("not a simple block: " + block); diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/CatchBlock.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/CatchBlock.java index 732d63b6b88b..21aff57d96f7 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/CatchBlock.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/CatchBlock.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -31,7 +33,7 @@ public CatchBlock(ParameterExpression parameter, this.body = body; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclaration.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclaration.java index 6920caad9dce..0df5618948d6 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclaration.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclaration.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.List; @@ -29,10 +31,10 @@ public class ClassDeclaration extends MemberDeclaration { public final String classClass = "class"; public final String name; public final List memberDeclarations; - public final Type extended; + public final @Nullable Type extended; public final List implemented; - public ClassDeclaration(int modifier, String name, Type extended, + public ClassDeclaration(int modifier, String name, @Nullable Type extended, List implemented, List memberDeclarations) { assert name != null : "name should not be null"; this.modifier = modifier; @@ -70,7 +72,7 @@ public ClassDeclaration(int modifier, String name, Type extended, return visitor.visit(this); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclarationFinder.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclarationFinder.java index 6373931df3da..fe1a4554d3d6 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclarationFinder.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ClassDeclarationFinder.java @@ -18,6 +18,8 @@ import org.apache.calcite.linq4j.function.Function1; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -30,7 +32,7 @@ * created for optimizing a new expression tree. */ public class ClassDeclarationFinder extends Shuttle { - protected final ClassDeclarationFinder parent; + protected final @Nullable ClassDeclarationFinder parent; /** * The list of new final static fields to be added to the current class. @@ -152,7 +154,7 @@ protected ClassDeclarationFinder(ClassDeclarationFinder parent) { } @Override public Expression visit(NewExpression newExpression, - List arguments, List memberDeclarations) { + List arguments, @Nullable List memberDeclarations) { if (parent == null) { // Unable to optimize since no wrapper class exists to put fields to. arguments = newExpression.arguments; @@ -254,7 +256,7 @@ protected boolean isConstant(Iterable list) { * @param expression input expression * @return always returns null */ - protected ParameterExpression findDeclaredExpression(Expression expression) { + protected @Nullable ParameterExpression findDeclaredExpression(Expression expression) { return null; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalExpression.java index 64400e60015b..e33e2fa98cd7 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; import java.util.Objects; @@ -59,7 +61,7 @@ public ConditionalExpression(List expressionList, Type type) { } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalStatement.java index 952e435197e3..f83a3bbd42fc 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConditionalStatement.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -72,7 +74,7 @@ private static E last(List collection) { return collection.get(collection.size() - 1); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantExpression.java index 78cf208f33c0..881cda6694df 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Type; @@ -28,22 +30,21 @@ import java.util.Set; import java.util.stream.Collectors; +import static java.util.Objects.requireNonNull; + /** * Represents an expression that has a constant value. */ public class ConstantExpression extends Expression { - public final Object value; + public final @Nullable Object value; - public ConstantExpression(Type type, Object value) { + public ConstantExpression(Type type, @Nullable Object value) { super(ExpressionType.Constant, type); this.value = value; if (value != null) { if (type instanceof Class) { Class clazz = (Class) type; - Primitive primitive = Primitive.of(clazz); - if (primitive != null) { - clazz = primitive.boxClass; - } + clazz = Primitive.box(clazz); if (!clazz.isInstance(value) && !((clazz == Float.class || clazz == Double.class) && value instanceof BigDecimal)) { @@ -54,7 +55,7 @@ public ConstantExpression(Type type, Object value) { } } - @Override public Object evaluate(Evaluator evaluator) { + @Override public @Nullable Object evaluate(Evaluator evaluator) { return value; } @@ -77,15 +78,13 @@ public ConstantExpression(Type type, Object value) { } private static ExpressionWriter write(ExpressionWriter writer, - final Object value, Type type) { + final Object value, @Nullable Type type) { if (value == null) { return writer.append("null"); } if (type == null) { type = value.getClass(); - if (Primitive.isBox(type)) { - type = Primitive.ofBox(type).primitiveClass; - } + type = Primitive.unbox(type); } if (value instanceof String) { escapeString(writer.getBuf(), (String) value); @@ -171,7 +170,7 @@ private static ExpressionWriter write(ExpressionWriter writer, return writer.append(recordType.getName()).append(".class"); } if (value.getClass().isArray()) { - writer.append("new ").append(value.getClass().getComponentType()); + writer.append("new ").append(requireNonNull(value.getClass().getComponentType())); list(writer, Primitive.asList(value), "[] {\n", ",\n", "}"); return writer; } @@ -195,7 +194,8 @@ private static ExpressionWriter write(ExpressionWriter writer, writer.append("new ").append(value.getClass()); list(writer, Arrays.stream(value.getClass().getFields()) - .map(field -> { + // <@Nullable Object> is needed for CheckerFramework + .<@Nullable Object>map(field -> { try { return field.get(value); } catch (IllegalAccessException e) { @@ -275,7 +275,7 @@ private static ExpressionWriter set(ExpressionWriter writer, Set set, return writer.append(end); } - private static Constructor matchingConstructor(Object value) { + private static @Nullable Constructor matchingConstructor(Object value) { final Field[] fields = value.getClass().getFields(); for (Constructor constructor : value.getClass().getConstructors()) { if (argsMatchFields(fields, constructor.getParameterTypes())) { @@ -328,7 +328,7 @@ private static void escapeString(StringBuilder buf, String s) { buf.append('"'); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { // REVIEW: Should constants with the same value and different type // (e.g. 3L and 3) be considered equal. if (this == o) { diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantUntypedNull.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantUntypedNull.java index 48ab4ee8452b..204f82d8e901 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantUntypedNull.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstantUntypedNull.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Represents a constant null of unknown type * Java allows type inference for such nulls, thus "null" cannot always be @@ -36,7 +38,7 @@ private ConstantUntypedNull() { writer.append("null"); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return o == INSTANCE; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstructorDeclaration.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstructorDeclaration.java index aff9d788d075..66f73aab393b 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstructorDeclaration.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ConstructorDeclaration.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -77,7 +78,7 @@ public ConstructorDeclaration(int modifier, Type declaredAgainst, writer.newlineAndIndent(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeclarationStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeclarationStatement.java index 84e2eba433d3..86937912c4ad 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeclarationStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeclarationStatement.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.util.Objects; @@ -25,10 +27,10 @@ public class DeclarationStatement extends Statement { public final int modifiers; public final ParameterExpression parameter; - public final Expression initializer; + public final @Nullable Expression initializer; public DeclarationStatement(int modifiers, ParameterExpression parameter, - Expression initializer) { + @Nullable Expression initializer) { super(ExpressionType.Declaration, Void.TYPE); assert parameter != null : "parameter should not be null"; this.modifiers = modifiers; @@ -78,7 +80,7 @@ public void accept2(ExpressionWriter writer, boolean withType) { } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeterministicCodeOptimizer.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeterministicCodeOptimizer.java index 430bcf5193b9..7fddb85a4a62 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeterministicCodeOptimizer.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/DeterministicCodeOptimizer.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableSet; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -167,7 +169,7 @@ && isMethodDeterministic(methodCallExpression.method)) { } @Override public Expression visit(MethodCallExpression methodCallExpression, - Expression targetExpression, List expressions) { + @Nullable Expression targetExpression, List expressions) { Expression result = super.visit(methodCallExpression, targetExpression, expressions); @@ -176,7 +178,7 @@ && isMethodDeterministic(methodCallExpression.method)) { } @Override public Expression visit(MemberExpression memberExpression, - Expression expression) { + @Nullable Expression expression) { Expression result = super.visit(memberExpression, expression); if (isConstant(expression) @@ -187,7 +189,7 @@ && isMethodDeterministic(methodCallExpression.method)) { } @Override public MemberDeclaration visit(FieldDeclaration fieldDeclaration, - Expression initializer) { + @Nullable Expression initializer) { if (Modifier.isStatic(fieldDeclaration.modifier)) { // Avoid optimization of static fields, since we'll have to track order // of static declarations. @@ -224,7 +226,7 @@ && isMethodDeterministic(methodCallExpression.method)) { * @param expression input expression * @return parameter of the already existing declaration, or null */ - @Override protected ParameterExpression findDeclaredExpression(Expression expression) { + @Override protected @Nullable ParameterExpression findDeclaredExpression(Expression expression) { if (!dedup.isEmpty()) { ParameterExpression pe = dedup.get(expression); if (pe != null) { @@ -294,7 +296,7 @@ protected String inventFieldName(Expression expression) { * @param expression expression to test * @return true when the expression is known to be constant */ - @Override protected boolean isConstant(Expression expression) { + @Override protected boolean isConstant(@Nullable Expression expression) { return expression == null || expression instanceof ConstantExpression || !constants.isEmpty() && constants.containsKey(expression) @@ -330,7 +332,7 @@ protected boolean isConstructorDeterministic(NewExpression newExpression) { && constructor.isAnnotationPresent(Deterministic.class); } - private Constructor getConstructor(Class klass) { + private @Nullable Constructor getConstructor(Class klass) { try { return klass.getConstructor(); } catch (NoSuchMethodException e) { @@ -347,7 +349,7 @@ private Constructor getConstructor(Class klass) { */ protected boolean allMethodsDeterministic(Class klass) { return DETERMINISTIC_CLASSES.contains(klass) - || klass.getCanonicalName().equals("org.apache.calcite.avatica.util.DateTimeUtils") + || "org.apache.calcite.avatica.util.DateTimeUtils".equals(klass.getCanonicalName()) || klass.isAnnotationPresent(Deterministic.class); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Evaluator.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Evaluator.java index a7fc34d5a042..f6cc41b7dd2c 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Evaluator.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Evaluator.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; @@ -24,12 +26,12 @@ */ class Evaluator { final List parameters = new ArrayList<>(); - final List values = new ArrayList<>(); + final List<@Nullable Object> values = new ArrayList<>(); Evaluator() { } - void push(ParameterExpression parameter, Object value) { + void push(ParameterExpression parameter, @Nullable Object value) { parameters.add(parameter); values.add(value); } @@ -42,7 +44,7 @@ void pop(int n) { } } - Object peek(ParameterExpression param) { + @Nullable Object peek(ParameterExpression param) { for (int i = parameters.size() - 1; i >= 0; i--) { if (parameters.get(i) == param) { return values.get(i); @@ -51,7 +53,7 @@ Object peek(ParameterExpression param) { throw new RuntimeException("parameter " + param + " not on stack"); } - Object evaluate(Node expression) { + @Nullable Object evaluate(Node expression) { return ((AbstractNode) expression).evaluate(this); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionType.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionType.java index 66450e61cf5e..b532aba2c4bf 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionType.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionType.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + /** *

    Analogous to LINQ's System.Linq.Expressions.ExpressionType.

    */ @@ -596,8 +598,8 @@ public enum ExpressionType { */ While; - final String op; - final String op2; + final @Nullable String op; + final @Nullable String op2; final boolean postfix; final int lprec; final int rprec; @@ -607,16 +609,16 @@ public enum ExpressionType { this(null, false, 0, false); } - ExpressionType(String op, boolean postfix, int prec, boolean right) { + ExpressionType(@Nullable String op, boolean postfix, int prec, boolean right) { this(op, null, postfix, prec, right); } - ExpressionType(String op, String op2, boolean postfix, int prec, + ExpressionType(@Nullable String op, @Nullable String op2, boolean postfix, int prec, boolean right) { this(op, op2, postfix, prec, right, false); } - ExpressionType(String op, String op2, boolean postfix, int prec, + ExpressionType(@Nullable String op, @Nullable String op2, boolean postfix, int prec, boolean right, boolean modifiesLvalue) { this.op = op; this.op2 = op2; diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionWriter.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionWriter.java index cb7c9113d90e..8bc640bac6e4 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionWriter.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ExpressionWriter.java @@ -18,6 +18,8 @@ import org.apache.calcite.avatica.util.Spacer; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Iterator; @@ -130,13 +132,13 @@ public ExpressionWriter append(AbstractNode o) { return this; } - public ExpressionWriter append(Object o) { + public ExpressionWriter append(@Nullable Object o) { checkIndent(); buf.append(o); return this; } - public ExpressionWriter append(String s) { + public ExpressionWriter append(@Nullable String s) { checkIndent(); buf.append(s); return this; diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java index 12082a7d7717..e604a31046fa 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java @@ -26,6 +26,9 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.PolyNull; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; @@ -38,9 +41,10 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.UUID; +import static java.util.Objects.requireNonNull; + /** * Utility methods for expressions, including a lot of factory methods. */ @@ -283,7 +287,7 @@ public static BlockStatement block(Statement... statements) { * Creates a BlockExpression that contains the given expressions, * has no variables and has specific result type. */ - public static BlockStatement block(Type type, + public static BlockStatement block(@Nullable Type type, Iterable expressions) { List list = toList(expressions); if (type == null) { @@ -360,7 +364,7 @@ public static MethodCallExpression call(Method method, * Creates a MethodCallExpression that represents a call to a * method that takes arguments. */ - public static MethodCallExpression call(Expression expression, Method method, + public static MethodCallExpression call(@Nullable Expression expression, Method method, Iterable arguments) { return new MethodCallExpression(method, expression, toList(arguments)); } @@ -369,7 +373,7 @@ public static MethodCallExpression call(Expression expression, Method method, * Creates a MethodCallExpression that represents a call to a * method that takes arguments, using varargs. */ - public static MethodCallExpression call(Expression expression, Method method, + public static MethodCallExpression call(@Nullable Expression expression, Method method, Expression... arguments) { return new MethodCallExpression(method, expression, toList(arguments)); } @@ -385,7 +389,7 @@ public static MethodCallExpression call(Expression expression, Method method, * is static.

    */ public static MethodCallExpression call(Type returnType, - Expression expression, Method method, + @Nullable Expression expression, Method method, Iterable arguments) { return new MethodCallExpression(returnType, method, expression, toList(arguments)); @@ -402,7 +406,7 @@ public static MethodCallExpression call(Type returnType, * is static.

    */ public static MethodCallExpression call(Type returnType, - Expression expression, Method method, + @Nullable Expression expression, Method method, Expression... arguments) { return new MethodCallExpression(returnType, method, expression, toList(arguments)); @@ -501,14 +505,6 @@ public static Expression condition(Expression test, Expression ifTrue, return makeTernary(ExpressionType.Conditional, test, ifTrue, ifFalse); } - private static Type box(Type type) { - Primitive primitive = Primitive.of(type); - if (primitive != null) { - return primitive.boxClass; - } - return type; - } - /** Returns whether an expression always evaluates to null. */ public static boolean isConstantNull(Expression e) { return e instanceof ConstantExpression @@ -541,19 +537,11 @@ public static ConditionalExpression condition(Expression test, * classes that have a constructor with a parameter for each field, and * arrays.

    */ - public static ConstantExpression constant(Object value) { - Class type; + public static ConstantExpression constant(@Nullable Object value) { if (value == null) { return ConstantUntypedNull.INSTANCE; - } else { - final Class clazz = value.getClass(); - final Primitive primitive = Primitive.ofBox(clazz); - if (primitive != null) { - type = primitive.primitiveClass; - } else { - type = clazz; - } } + Class type = Primitive.unbox(value.getClass()); return new ConstantExpression(type, value); } @@ -561,13 +549,13 @@ public static ConstantExpression constant(Object value) { * Creates a ConstantExpression that has the Value and Type * properties set to the specified values. */ - public static ConstantExpression constant(Object value, Type type) { + public static ConstantExpression constant(@Nullable Object value, Type type) { if (value != null && type instanceof Class) { // Fix up value so that it matches type. - Class clazz = (Class) type; + Class clazz = (Class) type; Primitive primitive = Primitive.ofBoxOr(clazz); if (primitive != null) { - clazz = primitive.boxClass; + clazz = requireNonNull(primitive.boxClass, "boxClass"); } if ((clazz == Float.class || clazz == Double.class) && value instanceof BigDecimal) { @@ -832,14 +820,14 @@ public static BinaryExpression exclusiveOrAssign(Expression left, /** * Creates a MemberExpression that represents accessing a field. */ - public static MemberExpression field(Expression expression, Field field) { + public static MemberExpression field(@Nullable Expression expression, Field field) { return makeMemberAccess(expression, Types.field(field)); } /** * Creates a MemberExpression that represents accessing a field. */ - public static MemberExpression field(Expression expression, + public static MemberExpression field(@Nullable Expression expression, PseudoField field) { return makeMemberAccess(expression, field); } @@ -857,7 +845,7 @@ public static MemberExpression field(Expression expression, /** * Creates a MemberExpression that represents accessing a field. */ - public static MemberExpression field(Expression expression, Type type, + public static MemberExpression field(@Nullable Expression expression, Type type, String fieldName) { PseudoField field = Types.getField(fieldName, type); return makeMemberAccess(expression, field); @@ -1384,7 +1372,7 @@ public static ListInitExpression listInit(NewExpression newExpression, */ public static ForStatement for_( Iterable declarations, - Expression condition, Expression post, Statement body) { + @Nullable Expression condition, @Nullable Expression post, Statement body) { return new ForStatement(toList(declarations), condition, post, body); } @@ -1393,7 +1381,7 @@ public static ForStatement for_( */ public static ForStatement for_( DeclarationStatement declaration, - Expression condition, Expression post, Statement body) { + @Nullable Expression condition, @Nullable Expression post, Statement body) { return new ForStatement(Collections.singletonList(declaration), condition, post, body); } @@ -1434,7 +1422,7 @@ public static BinaryExpression makeBinary(ExpressionType binaryType, /** Returns an expression to box the value of a primitive expression. * E.g. {@code box(e, Primitive.INT)} returns {@code Integer.valueOf(e)}. */ public static Expression box(Expression expression, Primitive primitive) { - return call(primitive.boxClass, "valueOf", expression); + return call(requireNonNull(primitive.boxClass), "valueOf", expression); } /** Converts e.g. "anInteger" to "Integer.valueOf(anInteger)". */ @@ -1450,7 +1438,7 @@ public static Expression box(Expression expression) { * E.g. {@code unbox(e, Primitive.INT)} returns {@code e.intValue()}. * It is assumed that e is of the right box type (or {@link Number})."Value */ public static Expression unbox(Expression expression, Primitive primitive) { - return call(expression, primitive.primitiveName + "Value"); + return call(expression, requireNonNull(primitive.primitiveName) + "Value"); } /** Converts e.g. "anInteger" to "anInteger.intValue()". */ @@ -1518,12 +1506,12 @@ public static TernaryExpression makeTernary(ExpressionType ternaryType, switch (ternaryType) { case Conditional: if (e1 instanceof ConstantUntypedNull) { - type = box(e2.getType()); + type = Primitive.box(e2.getType()); if (e1.getType() != type) { e1 = constant(null, type); } } else if (e2 instanceof ConstantUntypedNull) { - type = box(e1.getType()); + type = Primitive.box(e1.getType()); if (e2.getType() != type) { e2 = constant(null, type); } @@ -1577,7 +1565,7 @@ public static GotoStatement makeGoto(GotoExpressionKind kind, /** * Creates a MemberExpression that represents accessing a field. */ - public static MemberExpression makeMemberAccess(Expression expression, + public static MemberExpression makeMemberAccess(@Nullable Expression expression, PseudoField member) { return new MemberExpression(expression, member); } @@ -1626,7 +1614,7 @@ public static UnaryExpression makeUnary(ExpressionType expressionType, * method, by calling the appropriate factory method. */ public static UnaryExpression makeUnary(ExpressionType expressionType, - Expression expression, Type type, Method method) { + Expression expression, Type type, @Nullable Method method) { assert type != null; return new UnaryExpression(expressionType, type, expression); } @@ -1711,7 +1699,7 @@ public static ConstructorDeclaration constructorDecl(int modifier, * Declares a field with an initializer. */ public static FieldDeclaration fieldDecl(int modifier, - ParameterExpression parameter, Expression initializer) { + ParameterExpression parameter, @Nullable Expression initializer) { return new FieldDeclaration(modifier, parameter, initializer); } @@ -1727,7 +1715,7 @@ public static FieldDeclaration fieldDecl(int modifier, * Declares a class. */ public static ClassDeclaration classDecl(int modifier, String name, - Type extended, List implemented, + @Nullable Type extended, List implemented, List memberDeclarations) { return new ClassDeclaration(modifier, name, extended, implemented, memberDeclarations); @@ -1894,7 +1882,8 @@ public static UnaryExpression negate(Expression expression) { * negation operation. */ public static UnaryExpression negate(Expression expression, Method method) { - return makeUnary(ExpressionType.Negate, expression, null, method); + // TODO: use method + return negate(expression); } /** @@ -1912,7 +1901,8 @@ public static UnaryExpression negateChecked(Expression expression) { */ public static UnaryExpression negateChecked(Expression expression, Method method) { - return makeUnary(ExpressionType.NegateChecked, expression, null, method); + throw new UnsupportedOperationException("not implemented"); + //return makeUnary(ExpressionType.NegateChecked, expression, null, method); } /** @@ -1961,7 +1951,7 @@ public static NewExpression new_(Type type, Expression... arguments) { */ public static NewExpression new_(Type type, Iterable arguments, - Iterable memberDeclarations) { + @Nullable Iterable memberDeclarations) { return new NewExpression(type, toList(arguments), toList(memberDeclarations)); } @@ -2033,7 +2023,7 @@ public static NewExpression new_(Constructor constructor, * that has a specified rank. */ public static NewArrayExpression newArrayBounds(Type type, int dimension, - Expression bound) { + @Nullable Expression bound) { return new NewArrayExpression(type, dimension, bound, null); } @@ -2098,7 +2088,8 @@ public static UnaryExpression not(Expression expression) { * operation. The implementing method can be specified. */ public static UnaryExpression not(Expression expression, Method method) { - return makeUnary(ExpressionType.Not, expression, null, method); + // TODO: use method + return not(expression); } /** @@ -2504,13 +2495,13 @@ public static GotoStatement return_(LabelTarget labelTarget) { * Creates a GotoExpression representing a return statement. The * value passed to the label upon jumping can be specified. */ - public static GotoStatement return_(LabelTarget labelTarget, - Expression expression) { + public static GotoStatement return_(@Nullable LabelTarget labelTarget, + @Nullable Expression expression) { return makeGoto(GotoExpressionKind.Return, labelTarget, expression); } public static GotoStatement makeGoto(GotoExpressionKind kind, - LabelTarget labelTarget, Expression expression) { + @Nullable LabelTarget labelTarget, @Nullable Expression expression) { return new GotoStatement(kind, labelTarget, expression); } @@ -2693,6 +2684,7 @@ public static BinaryExpression subtractChecked(Expression left, * Creates a SwitchExpression that represents a switch statement * without a default case. */ + @SuppressWarnings("nullness") public static SwitchStatement switch_(Expression switchValue, SwitchCase... cases) { return switch_(switchValue, null, null, toList(cases)); @@ -2702,6 +2694,7 @@ public static SwitchStatement switch_(Expression switchValue, * Creates a SwitchExpression that represents a switch statement * that has a default case. */ + @SuppressWarnings("nullness") public static SwitchStatement switch_(Expression switchValue, Expression defaultBody, SwitchCase... cases) { return switch_(switchValue, defaultBody, null, toList(cases)); @@ -2932,7 +2925,7 @@ public static WhileStatement while_(Expression condition, Statement body) { * Creates a statement that declares a variable. */ public static DeclarationStatement declare(int modifiers, - ParameterExpression parameter, Expression initializer) { + ParameterExpression parameter, @Nullable Expression initializer) { return new DeclarationStatement(modifiers, parameter, initializer); } @@ -2954,7 +2947,7 @@ public static DeclarationStatement declare(int modifiers, String name, /** * Creates a statement that executes an expression. */ - public static Statement statement(Expression expression) { + public static Statement statement(@Nullable Expression expression) { return new GotoStatement(GotoExpressionKind.Sequence, null, expression); } @@ -3050,8 +3043,8 @@ public static FluentList list(Iterable ts) { /** * Evaluates an expression and returns the result. */ - public static Object evaluate(Node node) { - Objects.requireNonNull(node); + public static @Nullable Object evaluate(Node node) { + requireNonNull(node); final Evaluator evaluator = new Evaluator(); return ((AbstractNode) node).evaluate(evaluator); } @@ -3079,7 +3072,7 @@ private static Class deduceType(List parameterList, } } - private static List toList(Iterable iterable) { + private static @PolyNull List toList(@PolyNull Iterable iterable) { if (iterable == null) { return null; } @@ -3108,16 +3101,18 @@ private static Collection toCollection(Iterable iterable) { return toList(iterable); } - static Expression accept(T node, Shuttle shuttle) { + static @PolyNull Expression accept( + @PolyNull T node, Shuttle shuttle) { if (node == null) { - return null; + return node; } return node.accept(shuttle); } - static Statement accept(T node, Shuttle shuttle) { + static @PolyNull Statement accept( + @PolyNull T node, Shuttle shuttle) { if (node == null) { - return null; + return node; } return node.accept(shuttle); } @@ -3178,8 +3173,8 @@ static List acceptDeclarations( return declarations1; } - static List acceptMemberDeclarations( - List memberDeclarations, Shuttle shuttle) { + static @PolyNull List acceptMemberDeclarations( + @PolyNull List memberDeclarations, Shuttle shuttle) { if (memberDeclarations == null || memberDeclarations.isEmpty()) { return memberDeclarations; // short cut } @@ -3202,7 +3197,8 @@ static List acceptExpressions(List expressions, return expressions1; } - static R acceptNodes(List nodes, Visitor visitor) { + static @Nullable R acceptNodes(@Nullable List nodes, + Visitor visitor) { R r = null; if (nodes != null) { for (Node node : nodes) { @@ -3237,7 +3233,7 @@ public interface FluentList extends List { FluentList appendIf(boolean condition, T t); - FluentList appendIfNotNull(T t); + FluentList appendIfNotNull(@Nullable T t); FluentList appendAll(Iterable ts); @@ -3269,7 +3265,7 @@ private static class FluentArrayList extends ArrayList return this; } - @Override public FluentList appendIfNotNull(T t) { + @Override public FluentList appendIfNotNull(@Nullable T t) { if (t != null) { add(t); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FieldDeclaration.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FieldDeclaration.java index ca235274ac38..bdc3f2f9090f 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FieldDeclaration.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FieldDeclaration.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.util.Objects; @@ -25,10 +27,10 @@ public class FieldDeclaration extends MemberDeclaration { public final int modifier; public final ParameterExpression parameter; - public final Expression initializer; + public final @Nullable Expression initializer; public FieldDeclaration(int modifier, ParameterExpression parameter, - Expression initializer) { + @Nullable Expression initializer) { assert parameter != null : "parameter should not be null"; this.modifier = modifier; this.parameter = parameter; @@ -61,7 +63,7 @@ public FieldDeclaration(int modifier, ParameterExpression parameter, writer.newlineAndIndent(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForEachStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForEachStatement.java index a76ee68901bb..77fd5500bc5e 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForEachStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForEachStatement.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -59,7 +61,7 @@ public ForEachStatement(ParameterExpression parameter, Expression iterable, .append(Blocks.toBlock(body)); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o || o instanceof ForEachStatement && parameter.equals(((ForEachStatement) o).parameter) diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForStatement.java index 3b7c9a5d1a19..adae3913fb86 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ForStatement.java @@ -18,6 +18,8 @@ import org.apache.calcite.linq4j.Ord; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; @@ -26,14 +28,14 @@ */ public class ForStatement extends Statement { public final List declarations; - public final Expression condition; - public final Expression post; + public final @Nullable Expression condition; + public final @Nullable Expression post; public final Statement body; /** Cached hash code for the expression. */ private int hash; public ForStatement(List declarations, - Expression condition, Expression post, Statement body) { + @Nullable Expression condition, @Nullable Expression post, Statement body) { super(ExpressionType.For, Void.TYPE); assert declarations != null; assert body != null; @@ -74,7 +76,7 @@ public ForStatement(List declarations, writer.append(") ").append(Blocks.toBlock(body)); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FunctionExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FunctionExpression.java index 8e1afaeab071..6e121b7970d7 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FunctionExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/FunctionExpression.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.Type; @@ -30,6 +32,8 @@ import java.util.List; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * Represents a strongly typed lambda expression as a data structure in the form * of an expression tree. This class cannot be inherited. @@ -38,14 +42,14 @@ */ public final class FunctionExpression> extends LambdaExpression { - public final F function; - public final BlockStatement body; + public final @Nullable F function; + public final @Nullable BlockStatement body; public final List parameterList; - private F dynamicFunction; + private @Nullable F dynamicFunction; /** Cached hash code for the expression. */ private int hash; - private FunctionExpression(Class type, F function, BlockStatement body, + private FunctionExpression(Class type, @Nullable F function, @Nullable BlockStatement body, List parameterList) { super(ExpressionType.Lambda, type); assert type != null : "type should not be null"; @@ -68,7 +72,7 @@ public FunctionExpression(Class type, BlockStatement body, @Override public Expression accept(Shuttle shuttle) { shuttle = shuttle.preVisit(this); - BlockStatement body = this.body.accept(shuttle); + BlockStatement body = this.body == null ? null : this.body.accept(shuttle); return shuttle.visit(this, body); } @@ -82,7 +86,7 @@ public Invokable compile() { for (int i = 0; i < args.length; i++) { evaluator.push(parameterList.get(i), args[i]); } - return evaluator.evaluate(body); + return evaluator.evaluate(requireNonNull(body)); }; } @@ -93,8 +97,9 @@ public F getFunction() { if (dynamicFunction == null) { final Invokable x = compile(); + ClassLoader classLoader = requireNonNull(requireNonNull(getClass().getClassLoader())); //noinspection unchecked - dynamicFunction = (F) Proxy.newProxyInstance(getClass().getClassLoader(), + dynamicFunction = (F) Proxy.newProxyInstance(classLoader, new Class[]{Types.toClass(type)}, (proxy, method, args) -> x.dynamicInvoke(args)); } return dynamicFunction; @@ -142,9 +147,10 @@ public F getFunction() { boxBridgeParams.add(parameterExpression.declString(parameterBoxType)); boxBridgeArgs.add(parameterExpression.name + (Primitive.is(parameterType) - ? "." + Primitive.of(parameterType).primitiveName + "Value()" + ? "." + requireNonNull(Primitive.of(parameterType)).primitiveName + "Value()" : "")); } + requireNonNull(body); Type bridgeResultType = Functions.FUNCTION_RESULT_TYPES.get(this.type); if (bridgeResultType == null) { bridgeResultType = body.getType(); @@ -202,13 +208,11 @@ public F getFunction() { private boolean isAbstractMethodPrimitive() { Method method = getAbstractMethod(); - assert method != null; return Primitive.is(method.getReturnType()); } private String getAbstractMethodName() { final Method abstractMethod = getAbstractMethod(); - assert abstractMethod != null; return abstractMethod.getName(); } @@ -222,10 +226,10 @@ private Method getAbstractMethod() { return declaredMethods.get(0); } } - return null; + throw new IllegalStateException("Method not found, type = " + type); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -266,6 +270,6 @@ private Method getAbstractMethod() { /** Function that can be invoked with a variable number of arguments. */ public interface Invokable { - Object dynamicInvoke(Object... args); + @Nullable Object dynamicInvoke(@Nullable Object... args); } } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/GotoStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/GotoStatement.java index 8d0332f1b85e..745a74956a8a 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/GotoStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/GotoStatement.java @@ -16,19 +16,23 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * Represents an unconditional jump. This includes return statements, break and * continue statements, and other jumps. */ public class GotoStatement extends Statement { public final GotoExpressionKind kind; - public final LabelTarget labelTarget; - public final Expression expression; + public final @Nullable LabelTarget labelTarget; + public final @Nullable Expression expression; - GotoStatement(GotoExpressionKind kind, LabelTarget labelTarget, - Expression expression) { + GotoStatement(GotoExpressionKind kind, @Nullable LabelTarget labelTarget, + @Nullable Expression expression) { super(ExpressionType.Goto, expression == null ? Void.TYPE : expression.getType()); assert kind != null : "kind should not be null"; @@ -88,19 +92,19 @@ public class GotoStatement extends Statement { writer.append(';').newlineAndIndent(); } - @Override public Object evaluate(Evaluator evaluator) { + @Override public @Nullable Object evaluate(Evaluator evaluator) { switch (kind) { case Return: case Sequence: // NOTE: We ignore control flow. This is only correct if "return" // is the last statement in the block. - return expression.evaluate(evaluator); + return requireNonNull(expression).evaluate(evaluator); default: throw new AssertionError("evaluate not implemented"); } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/IndexExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/IndexExpression.java index 50bb8633d46c..c66221e1e450 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/IndexExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/IndexExpression.java @@ -16,9 +16,13 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * Represents indexing a property or array. */ @@ -27,8 +31,10 @@ public class IndexExpression extends Expression { public final List indexExpressions; public IndexExpression(Expression array, List indexExpressions) { - super(ExpressionType.ArrayIndex, Types.getComponentType(array.getType())); - assert array != null : "array should not be null"; + super(ExpressionType.ArrayIndex, + requireNonNull( + Types.getComponentType(array.getType()), + () -> "component type for " + array)); assert indexExpressions != null : "indexExpressions should not be null"; assert !indexExpressions.isEmpty() : "indexExpressions should not be empty"; this.array = array; @@ -52,7 +58,7 @@ public IndexExpression(Expression array, List indexExpressions) { writer.list("[", ", ", "]", indexExpressions); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelStatement.java index a65e78616b98..f861325b37bb 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelStatement.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -41,7 +43,7 @@ public LabelStatement(Expression defaultValue, ExpressionType nodeType) { return visitor.visit(this); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelTarget.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelTarget.java index 49cbb4d03b6b..16679ab02343 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelTarget.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/LabelTarget.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -28,7 +30,7 @@ public LabelTarget(String name) { this.name = name; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MemberExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MemberExpression.java index 945f49a335ed..bb132628a9ba 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MemberExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MemberExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Objects; @@ -24,14 +26,14 @@ * Represents accessing a field or property. */ public class MemberExpression extends Expression { - public final Expression expression; + public final @Nullable Expression expression; public final PseudoField field; public MemberExpression(Expression expression, Field field) { this(expression, Types.field(field)); } - public MemberExpression(Expression expression, PseudoField field) { + public MemberExpression(@Nullable Expression expression, PseudoField field) { super(ExpressionType.MemberAccess, field.getType()); assert field != null : "field should not be null"; assert expression != null || Modifier.isStatic(field.getModifiers()) @@ -52,7 +54,7 @@ public MemberExpression(Expression expression, PseudoField field) { return visitor.visit(this); } - @Override public Object evaluate(Evaluator evaluator) { + @Override public @Nullable Object evaluate(Evaluator evaluator) { final Object o = expression == null ? null : expression.evaluate(evaluator); @@ -76,7 +78,7 @@ public MemberExpression(Expression expression, PseudoField field) { writer.append('.').append(field.getName()); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodCallExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodCallExpression.java index 5317367685b3..8f7ed69fe556 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodCallExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodCallExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -28,13 +30,13 @@ */ public class MethodCallExpression extends Expression { public final Method method; - public final Expression targetExpression; // null for call to static method + public final @Nullable Expression targetExpression; // null for call to static method public final List expressions; /** Cached hash code for the expression. */ private int hash; MethodCallExpression(Type returnType, Method method, - Expression targetExpression, List expressions) { + @Nullable Expression targetExpression, List expressions) { super(ExpressionType.Call, returnType); assert expressions != null : "expressions should not be null"; assert method != null : "method should not be null"; @@ -46,7 +48,7 @@ public class MethodCallExpression extends Expression { this.expressions = expressions; } - MethodCallExpression(Method method, Expression targetExpression, + MethodCallExpression(Method method, @Nullable Expression targetExpression, List expressions) { this(method.getReturnType(), method, targetExpression, expressions); } @@ -64,14 +66,14 @@ public class MethodCallExpression extends Expression { return visitor.visit(this); } - @Override public Object evaluate(Evaluator evaluator) { + @Override public @Nullable Object evaluate(Evaluator evaluator) { final Object target; if (targetExpression == null) { target = null; } else { target = targetExpression.evaluate(evaluator); } - final Object[] args = new Object[expressions.size()]; + final @Nullable Object[] args = new Object[expressions.size()]; for (int i = 0; i < expressions.size(); i++) { Expression expression = expressions.get(i); args[i] = expression.evaluate(evaluator); @@ -105,7 +107,7 @@ public class MethodCallExpression extends Expression { writer.append(')'); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodDeclaration.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodDeclaration.java index a864f61c98f2..6dd81d046ba9 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodDeclaration.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/MethodDeclaration.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -75,7 +76,7 @@ public MethodDeclaration(int modifier, String name, Type resultType, writer.newlineAndIndent(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewArrayExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewArrayExpression.java index 59850c7ea2c6..6653b8603b8d 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewArrayExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewArrayExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; import java.util.Objects; @@ -26,13 +28,13 @@ */ public class NewArrayExpression extends Expression { public final int dimension; - public final Expression bound; - public final List expressions; + public final @Nullable Expression bound; + public final @Nullable List expressions; /** Cached hash code for the expression. */ private int hash; - public NewArrayExpression(Type type, int dimension, Expression bound, - List expressions) { + public NewArrayExpression(Type type, int dimension, @Nullable Expression bound, + @Nullable List expressions) { super(ExpressionType.NewArrayInit, Types.arrayType(type, dimension)); this.dimension = dimension; this.bound = bound; @@ -67,7 +69,7 @@ public NewArrayExpression(Type type, int dimension, Expression bound, } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewExpression.java index 0fc4b53c59dc..b9de9b78a146 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/NewExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.List; import java.util.Objects; @@ -30,12 +32,12 @@ public class NewExpression extends Expression { @SuppressWarnings("HidingField") public final Type type; public final List arguments; - public final List memberDeclarations; + public final @Nullable List memberDeclarations; /** Cached hash code for the expression. */ private int hash; public NewExpression(Type type, List arguments, - List memberDeclarations) { + @Nullable List memberDeclarations) { super(ExpressionType.New, type); this.type = type; this.arguments = arguments; @@ -62,7 +64,7 @@ public NewExpression(Type type, List arguments, } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/OptimizeShuttle.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/OptimizeShuttle.java index 04214c18da66..871fe887ebbd 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/OptimizeShuttle.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/OptimizeShuttle.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -224,7 +226,7 @@ && eq(cmp.expression1, expression2)) { return super.visit(binary, expression0, expression1); } - private Expression visit0( + private @Nullable Expression visit0( BinaryExpression binary, Expression expression0, Expression expression1) { @@ -375,7 +377,7 @@ && isKnownNotNull(expression0)) { } @Override public Expression visit(MethodCallExpression methodCallExpression, - Expression targetExpression, + @Nullable Expression targetExpression, List expressions) { if (BOOLEAN_VALUEOF_BOOL.equals(methodCallExpression.method)) { Boolean always = always(expressions.get(0)); @@ -395,7 +397,7 @@ private boolean isConstantNull(Expression expression) { * Returns whether an expression always evaluates to true or false. * Assumes that expression has already been optimized. */ - private static Boolean always(Expression x) { + private static @Nullable Boolean always(Expression x) { if (x.equals(FALSE_EXPR) || x.equals(BOXED_FALSE_EXPR)) { return Boolean.FALSE; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ParameterExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ParameterExpression.java index c51283731c07..2b2e1fc1758f 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ParameterExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ParameterExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.concurrent.atomic.AtomicInteger; @@ -51,7 +53,7 @@ public ParameterExpression(int modifier, Type type, String name) { return visitor.visit(this); } - @Override public Object evaluate(Evaluator evaluator) { + @Override public @Nullable Object evaluate(Evaluator evaluator) { return evaluator.peek(this); } @@ -69,7 +71,7 @@ String declString(Type type) { + " " + name; } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { return this == o; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java index 612b97d7f538..0e89bdeebed6 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Primitive.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.linq4j.tree; +import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Type; @@ -28,6 +31,8 @@ import java.util.List; import java.util.Map; +import static java.util.Objects.requireNonNull; + /** * Enumeration of Java's primitive types. * @@ -54,35 +59,35 @@ public enum Primitive { VOID(Void.TYPE, Void.class, 3, null, null, null, null, null, -1), OTHER(null, null, 4, null, null, null, null, null, -1); - public final Class primitiveClass; - public final Class boxClass; - public final String primitiveName; // e.g. "int" - public final String boxName; + public final @Nullable Class primitiveClass; + public final @Nullable Class boxClass; + public final @Nullable String primitiveName; // e.g. "int" + public final @Nullable String boxName; private final int family; /** The default value of this primitive class. This is the value * taken by uninitialized fields, for instance; 0 for {@code int}, false for * {@code boolean}, etc. */ @SuppressWarnings("ImmutableEnumChecker") - public final Object defaultValue; + public final @Nullable Object defaultValue; /** The minimum value of this primitive class. */ @SuppressWarnings("ImmutableEnumChecker") - public final Object min; + public final @Nullable Object min; /** The largest value that is less than zero. Null if not applicable for this * type. */ @SuppressWarnings("ImmutableEnumChecker") - public final Object maxNegative; + public final @Nullable Object maxNegative; /** The smallest value that is greater than zero. Null if not applicable for * this type. */ @SuppressWarnings("ImmutableEnumChecker") - public final Object minPositive; + public final @Nullable Object minPositive; /** The maximum value of this primitive class. */ @SuppressWarnings("ImmutableEnumChecker") - public final Object max; + public final @Nullable Object max; /** The size of a value of this type, in bits. Null if not applicable for this * type. */ @@ -103,9 +108,9 @@ public enum Primitive { } } - Primitive(Class primitiveClass, Class boxClass, int family, - Object defaultValue, Object min, Object maxNegative, Object minPositive, - Object max, int size) { + Primitive(@Nullable Class primitiveClass, @Nullable Class boxClass, int family, + @Nullable Object defaultValue, @Nullable Object min, @Nullable Object maxNegative, + @Nullable Object minPositive, @Nullable Object max, int size) { this.primitiveClass = primitiveClass; this.family = family; this.primitiveName = @@ -128,7 +133,7 @@ public enum Primitive { * of(Long.class) and of(String.class) return * {@code null}. */ - public static Primitive of(Type type) { + public static @Nullable Primitive of(Type type) { //noinspection SuspiciousMethodCalls return PRIMITIVE_MAP.get(type); } @@ -139,7 +144,7 @@ public static Primitive of(Type type) { *

    For example, ofBox(java.util.Long.class) * returns {@link #LONG}. */ - public static Primitive ofBox(Type type) { + public static @Nullable Primitive ofBox(Type type) { //noinspection SuspiciousMethodCalls return BOX_MAP.get(type); } @@ -150,7 +155,7 @@ public static Primitive ofBox(Type type) { *

    For example, ofBoxOr(Long.class) and * ofBoxOr(long.class) both return {@link #LONG}. */ - public static Primitive ofBoxOr(Type type) { + public static @Nullable Primitive ofBoxOr(Type type) { Primitive primitive = of(type); if (primitive == null) { primitive = ofBox(type); @@ -221,7 +226,7 @@ public boolean isFixedNumeric() { */ public static Type box(Type type) { Primitive primitive = of(type); - return primitive == null ? type : primitive.boxClass; + return primitive == null ? type : requireNonNull(primitive.boxClass); } /** @@ -230,7 +235,7 @@ public static Type box(Type type) { */ public static Class box(Class type) { Primitive primitive = of(type); - return primitive == null ? type : primitive.boxClass; + return primitive == null ? type : requireNonNull(primitive.boxClass); } /** @@ -239,7 +244,7 @@ public static Class box(Class type) { */ public static Type unbox(Type type) { Primitive primitive = ofBox(type); - return primitive == null ? type : primitive.primitiveClass; + return primitive == null ? type : requireNonNull(primitive.primitiveClass); } /** @@ -248,7 +253,28 @@ public static Type unbox(Type type) { */ public static Class unbox(Class type) { Primitive primitive = ofBox(type); - return primitive == null ? type : primitive.primitiveClass; + return primitive == null ? type : requireNonNull(primitive.primitiveClass); + } + + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public Class getPrimitiveClass() { + return requireNonNull(primitiveClass, () -> "no primitiveClass for " + this); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public Class getBoxClass() { + return requireNonNull(boxClass, () -> "no boxClass for " + this); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public String getPrimitiveName() { + return requireNonNull(primitiveName, () -> "no primitiveName for " + this); + } + + @API(since = "1.27", status = API.Status.EXPERIMENTAL) + public String getBoxName() { + return requireNonNull(boxName, () -> "no boxName for " + this); } /** @@ -701,7 +727,7 @@ public void send(Field field, Object o, Sink sink) /** * Gets an item from an array. */ - public Object arrayItem(Object dataSet, int ordinal) { + public @Nullable Object arrayItem(Object dataSet, int ordinal) { // Plain old Array.get doesn't cut it when you have an array of // Integer values but you want to read Short values. Array.getShort // does the right thing. @@ -732,6 +758,7 @@ public Object arrayItem(Object dataSet, int ordinal) { /** * Reads value from a source into an array. */ + @SuppressWarnings("argument.type.incompatible") public void arrayItem(Source source, Object dataSet, int ordinal) { switch (this) { case DOUBLE: @@ -809,7 +836,7 @@ public void arrayItem(Object dataSet, int ordinal, Sink sink) { * @param resultSet Result set * @param i Ordinal of column (1-based, per JDBC) */ - public Object jdbcGet(ResultSet resultSet, int i) throws SQLException { + public @Nullable Object jdbcGet(ResultSet resultSet, int i) throws SQLException { switch (this) { case BOOLEAN: return resultSet.getBoolean(i); @@ -981,7 +1008,7 @@ public interface Sink { void set(double v); - void set(Object v); + void set(@Nullable Object v); } /** @@ -1004,7 +1031,7 @@ public interface Source { double getDouble(); - Object getObject(); + @Nullable Object getObject(); } /** Whether a type is primitive (e.g. {@code int}), diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/PseudoField.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/PseudoField.java index ec1192334106..93f781954a7d 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/PseudoField.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/PseudoField.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; /** @@ -29,7 +31,7 @@ public interface PseudoField { int getModifiers(); - Object get(Object o) throws IllegalAccessException; + @Nullable Object get(@Nullable Object o) throws IllegalAccessException; Type getDeclaringClass(); } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ReflectedPseudoField.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ReflectedPseudoField.java index 4a5ace11f623..738420d5aa12 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ReflectedPseudoField.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ReflectedPseudoField.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Field; import java.lang.reflect.Type; @@ -43,7 +45,7 @@ public ReflectedPseudoField(Field field) { return field.getModifiers(); } - @Override public Object get(Object o) throws IllegalAccessException { + @Override public @Nullable Object get(@Nullable Object o) throws IllegalAccessException { return field.get(o); } @@ -51,7 +53,7 @@ public ReflectedPseudoField(Field field) { return field.getDeclaringClass(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Shuttle.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Shuttle.java index 52806c0a3bca..8e5533c81936 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Shuttle.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Shuttle.java @@ -16,9 +16,13 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * Extension to {@link Visitor} that returns a mutated tree. */ @@ -61,7 +65,7 @@ public Shuttle preVisit(GotoStatement gotoStatement) { return this; } - public Statement visit(GotoStatement gotoStatement, Expression expression) { + public Statement visit(GotoStatement gotoStatement, @Nullable Expression expression) { return expression == gotoStatement.expression ? gotoStatement : Expressions.makeGoto( @@ -78,8 +82,8 @@ public Shuttle preVisit(ForStatement forStatement) { } public ForStatement visit(ForStatement forStatement, - List declarations, Expression condition, - Expression post, Statement body) { + List declarations, @Nullable Expression condition, + @Nullable Expression post, Statement body) { return declarations.equals(forStatement.declarations) && condition == forStatement.condition && post == forStatement.post @@ -116,7 +120,7 @@ public Shuttle preVisit(DeclarationStatement declarationStatement) { } public DeclarationStatement visit(DeclarationStatement declarationStatement, - Expression initializer) { + @Nullable Expression initializer) { return declarationStatement.initializer == initializer ? declarationStatement : Expressions.declare( @@ -133,10 +137,12 @@ public Shuttle preVisit(FunctionExpression functionExpression) { } public Expression visit(FunctionExpression functionExpression, - BlockStatement body) { - return functionExpression.body.equals(body) + @Nullable BlockStatement body) { + return Objects.equals(body, functionExpression.body) ? functionExpression - : Expressions.lambda(body, functionExpression.parameterList); + : Expressions.lambda( + requireNonNull(body, "body"), + functionExpression.parameterList); } public Shuttle preVisit(BinaryExpression binaryExpression) { @@ -195,7 +201,7 @@ public Shuttle preVisit(MethodCallExpression methodCallExpression) { } public Expression visit(MethodCallExpression methodCallExpression, - Expression targetExpression, List expressions) { + @Nullable Expression targetExpression, List expressions) { return methodCallExpression.targetExpression == targetExpression && methodCallExpression.expressions.equals(expressions) ? methodCallExpression @@ -216,7 +222,7 @@ public Shuttle preVisit(MemberExpression memberExpression) { } public Expression visit(MemberExpression memberExpression, - Expression expression) { + @Nullable Expression expression) { return memberExpression.expression == expression ? memberExpression : Expressions.field(expression, memberExpression.field); @@ -231,7 +237,7 @@ public Shuttle preVisit(NewArrayExpression newArrayExpression) { } public Expression visit(NewArrayExpression newArrayExpression, int dimension, - Expression bound, List expressions) { + @Nullable Expression bound, @Nullable List expressions) { return Objects.equals(expressions, newArrayExpression.expressions) && Objects.equals(bound, newArrayExpression.bound) ? newArrayExpression @@ -252,7 +258,7 @@ public Shuttle preVisit(NewExpression newExpression) { } public Expression visit(NewExpression newExpression, - List arguments, List memberDeclarations) { + List arguments, @Nullable List memberDeclarations) { return arguments.equals(newExpression.arguments) && Objects.equals(memberDeclarations, newExpression.memberDeclarations) ? newExpression @@ -268,7 +274,7 @@ public Shuttle preVisit(TryStatement tryStatement) { } public Statement visit(TryStatement tryStatement, - Statement body, List catchBlocks, Statement fynally) { + Statement body, List catchBlocks, @Nullable Statement fynally) { return body.equals(tryStatement.body) && Objects.equals(catchBlocks, tryStatement.catchBlocks) && Objects.equals(fynally, tryStatement.fynally) @@ -310,7 +316,7 @@ public Shuttle preVisit(FieldDeclaration fieldDeclaration) { } public MemberDeclaration visit(FieldDeclaration fieldDeclaration, - Expression initializer) { + @Nullable Expression initializer) { return Objects.equals(initializer, fieldDeclaration.initializer) ? fieldDeclaration : Expressions.fieldDecl(fieldDeclaration.modifier, diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TernaryExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TernaryExpression.java index f42011dbaeb1..10ae031c728a 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TernaryExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TernaryExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Objects; @@ -61,7 +63,7 @@ public class TernaryExpression extends Expression { expression2.accept(writer, nodeType.rprec, rprec); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ThrowStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ThrowStatement.java index 8b3504bff287..b9298824efb2 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ThrowStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/ThrowStatement.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -43,7 +45,7 @@ public ThrowStatement(Expression expression) { writer.append("throw ").append(expression).append(';').newlineAndIndent(); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TryStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TryStatement.java index cf3d4dd28915..b3736e9cd001 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TryStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TryStatement.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -26,10 +28,10 @@ public class TryStatement extends Statement { public final Statement body; public final List catchBlocks; - public final Statement fynally; + public final @Nullable Statement fynally; public TryStatement(Statement body, List catchBlocks, - Statement fynally) { + @Nullable Statement fynally) { super(ExpressionType.Try, body.getType()); this.body = Objects.requireNonNull(body); this.catchBlocks = Objects.requireNonNull(catchBlocks); @@ -67,7 +69,7 @@ public TryStatement(Statement body, List catchBlocks, } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TypeBinaryExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TypeBinaryExpression.java index 108944aa7bb1..c906a1216972 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TypeBinaryExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TypeBinaryExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Objects; @@ -54,7 +56,7 @@ public TypeBinaryExpression(ExpressionType nodeType, Expression expression, writer.append(type); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java index d0041b21d76e..8db3a6994fa3 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java @@ -18,6 +18,8 @@ import org.apache.calcite.linq4j.Enumerator; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -33,6 +35,8 @@ import java.util.Iterator; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Utilities for converting between {@link Expression}, {@link Type} and * {@link Class}. @@ -60,7 +64,7 @@ public static Type of(Type type, Type... typeArguments) { * *

    Returns null if the type is not one of these.

    */ - public static Type getElementType(Type type) { + public static @Nullable Type getElementType(Type type) { if (type instanceof ArrayType) { return ((ArrayType) type).getComponentType(); } @@ -167,7 +171,7 @@ public static Class[] toClassArray(Iterable arguments) { /** * Returns the component type of an array. */ - public static Type getComponentType(Type type) { + public static @Nullable Type getComponentType(Type type) { if (type instanceof Class) { return ((Class) type).getComponentType(); } @@ -189,30 +193,20 @@ public static Type getComponentType(Type type) { static Type getComponentTypeN(Type type) { for (;;) { - final Type oldType = type; - type = getComponentType(type); - if (type == null) { - return oldType; + Type componentType = getComponentType(type); + if (componentType == null) { + return type; } + type = componentType; } } public static Type box(Type type) { - Primitive primitive = Primitive.of(type); - if (primitive != null) { - return primitive.boxClass; - } else { - return type; - } + return Primitive.box(type); } public static Type unbox(Type type) { - Primitive primitive = Primitive.ofBox(type); - if (primitive != null) { - return primitive.primitiveClass; - } else { - return type; - } + return Primitive.unbox(type); } static String className(Type type) { @@ -289,6 +283,7 @@ public static boolean allAssignable(boolean varArgs, Class[] parameterTypes, * * @return Whether parameter can be assigned from argument */ + @SuppressWarnings("nullness") private static boolean assignableFrom(Class parameter, Class argument) { return parameter.isAssignableFrom(argument) || parameter.isPrimitive() @@ -403,7 +398,7 @@ static Type gcd(Type... types) { return Object.class; } } - return bestPrimitive.primitiveClass; + return requireNonNull(bestPrimitive.primitiveClass); } else { for (int i = 1; i < types.length; i++) { if (types[i] != types[0]) { @@ -437,7 +432,7 @@ public static Expression castIfNecessary(Type returnType, // Integer foo(BigDecimal o) { // return o.intValue(); // } - return Expressions.unbox(expression, Primitive.ofBox(returnType)); + return Expressions.unbox(expression, requireNonNull(Primitive.ofBox(returnType))); } if (Primitive.is(returnType) && !Primitive.is(type)) { // E.g. @@ -446,7 +441,7 @@ public static Expression castIfNecessary(Type returnType, // } return Expressions.unbox( Expressions.convert_(expression, Types.box(returnType)), - Primitive.of(returnType)); + requireNonNull(Primitive.of(returnType))); } if (!Primitive.is(returnType) && Primitive.is(type)) { // E.g. @@ -525,10 +520,10 @@ public static Type stripGenerics(Type type) { static class ParameterizedTypeImpl implements ParameterizedType { private final Type rawType; private final List typeArguments; - private final Type ownerType; + private final @Nullable Type ownerType; ParameterizedTypeImpl(Type rawType, List typeArguments, - Type ownerType) { + @Nullable Type ownerType) { super(); this.rawType = rawType; this.typeArguments = typeArguments; @@ -562,7 +557,7 @@ static class ParameterizedTypeImpl implements ParameterizedType { return rawType; } - @Override public Type getOwnerType() { + @Override public @Nullable Type getOwnerType() { return ownerType; } } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java index 3409bbaf5038..4e30d69f4272 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnaryExpression.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.lang.reflect.Type; import java.util.Objects; @@ -61,7 +63,7 @@ public class UnaryExpression extends Expression { } } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/VisitorImpl.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/VisitorImpl.java index 877175af5af7..18025e3c791f 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/VisitorImpl.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/VisitorImpl.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -25,7 +27,7 @@ * @param Return type */ @SuppressWarnings("unused") -public class VisitorImpl implements Visitor { +public class VisitorImpl<@Nullable R> implements Visitor { public VisitorImpl() { super(); } @@ -85,8 +87,8 @@ public VisitorImpl() { @Override public R visit(ForStatement forStatement) { R r0 = Expressions.acceptNodes(forStatement.declarations, this); - R r1 = forStatement.condition.accept(this); - R r2 = forStatement.post.accept(this); + R r1 = forStatement.condition == null ? null : forStatement.condition.accept(this); + R r2 = forStatement.post == null ? null : forStatement.post.accept(this); return forStatement.body.accept(this); } @@ -100,7 +102,7 @@ public VisitorImpl() { @SuppressWarnings("unchecked") final List parameterList = functionExpression.parameterList; R r0 = Expressions.acceptNodes(parameterList, this); - return functionExpression.body.accept(this); + return functionExpression.body == null ? null : functionExpression.body.accept(this); } @Override public R visit(GotoStatement gotoStatement) { diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/WhileStatement.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/WhileStatement.java index aa77d634bd85..0db9b14584c1 100644 --- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/WhileStatement.java +++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/WhileStatement.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.linq4j.tree; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Objects; /** @@ -49,7 +51,7 @@ public WhileStatement(Expression condition, Statement body) { Blocks.toBlock(body)); } - @Override public boolean equals(Object o) { + @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/pig/src/main/java/org/apache/calcite/adapter/pig/PigTableFactory.java b/pig/src/main/java/org/apache/calcite/adapter/pig/PigTableFactory.java index 7604f3a7e9fd..70a5c5e8fdd7 100644 --- a/pig/src/main/java/org/apache/calcite/adapter/pig/PigTableFactory.java +++ b/pig/src/main/java/org/apache/calcite/adapter/pig/PigTableFactory.java @@ -21,6 +21,8 @@ import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.schema.TableFactory; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.File; import java.util.List; import java.util.Map; @@ -37,7 +39,7 @@ public PigTableFactory() { @SuppressWarnings("unchecked") @Override public PigTable create(SchemaPlus schema, String name, - Map operand, RelDataType rowType) { + Map operand, @Nullable RelDataType rowType) { String fileName = (String) operand.get("file"); File file = new File(fileName); final File base = diff --git a/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java b/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java index 19a4aa92e0f6..49974f2e2dd5 100644 --- a/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java +++ b/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java @@ -58,6 +58,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.UnaryOperator; /** @@ -231,6 +232,7 @@ void replaceTop(RelNode newRel) { public RelBuilder scan(RelOptTable userSchema, String... tableNames) { // First, look up the database schema to find the table schema with the given names final List names = ImmutableList.copyOf(tableNames); + Objects.requireNonNull(relOptSchema, "relOptSchema"); final RelOptTable systemSchema = relOptSchema.getTableForMember(names); // Now we may end up with two different schemas. diff --git a/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java b/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java index 5a4a3c628057..0ab303a5aeaf 100644 --- a/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java +++ b/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java @@ -31,6 +31,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; /** @@ -68,7 +70,7 @@ public static RelOptTable createRelOptTable(RelOptSchema schema, return DUMMY_STATISTICS; } - @Override public Enumerable scan(final DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(final DataContext root) { return null; } } diff --git a/piglet/src/main/java/org/apache/calcite/piglet/PigUdfFinder.java b/piglet/src/main/java/org/apache/calcite/piglet/PigUdfFinder.java index 19422484a8a6..633be3afacd5 100644 --- a/piglet/src/main/java/org/apache/calcite/piglet/PigUdfFinder.java +++ b/piglet/src/main/java/org/apache/calcite/piglet/PigUdfFinder.java @@ -23,7 +23,6 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -import javax.annotation.Nonnull; /** * Utility class to find the implementation method object for a given Pig UDF @@ -61,7 +60,7 @@ class PigUdfFinder { * * @throws IllegalArgumentException if not found */ - @Nonnull Method findPigUdfImplementationMethod(Class clazz) { + Method findPigUdfImplementationMethod(Class clazz) { // Find implementation method in the wrapper map Method returnedMethod = udfWrapper.get(clazz.getSimpleName().toLowerCase(Locale.US)); diff --git a/plus/src/main/java/org/apache/calcite/adapter/os/DuTableFunction.java b/plus/src/main/java/org/apache/calcite/adapter/os/DuTableFunction.java index 3a0a72369b45..75bdc29ba5eb 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/os/DuTableFunction.java +++ b/plus/src/main/java/org/apache/calcite/adapter/os/DuTableFunction.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Table function that executes the OS "du" ("disk usage") command * to compute file sizes. @@ -41,7 +43,7 @@ private DuTableFunction() {} public static ScannableTable eval(boolean b) { return new ScannableTable() { - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { return Processes.processLines("du", "-ak") .select(a0 -> { final String[] fields = a0.split("\t"); @@ -69,7 +71,7 @@ public static ScannableTable eval(boolean b) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; diff --git a/plus/src/main/java/org/apache/calcite/adapter/os/FilesTableFunction.java b/plus/src/main/java/org/apache/calcite/adapter/os/FilesTableFunction.java index c02cd8801a6a..5efb150547c8 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/os/FilesTableFunction.java +++ b/plus/src/main/java/org/apache/calcite/adapter/os/FilesTableFunction.java @@ -17,6 +17,7 @@ package org.apache.calcite.adapter.os; import org.apache.calcite.DataContext; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.config.CalciteConnectionConfig; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; @@ -35,10 +36,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.Arrays; import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Table function that executes the OS "find" command to find files under a * particular path. @@ -144,8 +149,9 @@ private Enumerable sourceMacOs() { return Processes.processLines('\n', args); } - @Override public Enumerable scan(DataContext root) { - final RelDataType rowType = getRowType(root.getTypeFactory()); + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); + final RelDataType rowType = getRowType(typeFactory); final List fieldNames = ImmutableList.copyOf(rowType.getFieldNames()); final String osName = System.getProperty("os.name"); @@ -159,14 +165,14 @@ private Enumerable sourceMacOs() { default: enumerable = sourceLinux(); } - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { final Enumerator e = enumerable.enumerator(); - return new Enumerator() { - Object[] current; + return new Enumerator<@Nullable Object[]>() { + @Nullable Object @Nullable [] current; @Override public Object[] current() { - return current; + return requireNonNull(current, "current"); } @Override public boolean moveNext() { @@ -188,7 +194,7 @@ private Enumerable sourceMacOs() { case "Mac OS X": // Strip leading "./" String path = (String) current[14]; - if (path.equals(".")) { + if (".".equals(path)) { current[14] = path = ""; current[3] = 0; // depth } else if (path.startsWith("./")) { @@ -208,9 +214,9 @@ private Enumerable sourceMacOs() { // Make type values more like those on Linux final String type = (String) current[19]; - current[19] = type.equals("/") ? "d" - : type.equals("") || type.equals("*") ? "f" - : type.equals("@") ? "l" + current[19] = "/".equals(type) ? "d" + : "".equals(type) || "*".equals(type) ? "f" + : "@".equals(type) ? "l" : type; break; default: @@ -275,7 +281,7 @@ private Object field(String field, String value) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; diff --git a/plus/src/main/java/org/apache/calcite/adapter/os/GitCommitsTableFunction.java b/plus/src/main/java/org/apache/calcite/adapter/os/GitCommitsTableFunction.java index e92751f3f2f7..6b5fa2a1ee5a 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/os/GitCommitsTableFunction.java +++ b/plus/src/main/java/org/apache/calcite/adapter/os/GitCommitsTableFunction.java @@ -34,6 +34,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.NoSuchElementException; /** @@ -53,17 +55,17 @@ private GitCommitsTableFunction() {} public static ScannableTable eval(boolean b) { return new ScannableTable() { - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { final Enumerable enumerable = Processes.processLines("git", "log", "--pretty=raw"); - return new AbstractEnumerable() { - @Override public Enumerator enumerator() { + return new AbstractEnumerable<@Nullable Object[]>() { + @Override public Enumerator<@Nullable Object[]> enumerator() { final Enumerator e = enumerable.enumerator(); - return new Enumerator() { - private Object[] objects; + return new Enumerator<@Nullable Object[]>() { + private @Nullable Object @Nullable [] objects; private final StringBuilder b = new StringBuilder(); - @Override public Object[] current() { + @Override public @Nullable Object[] current() { if (objects == null) { throw new NoSuchElementException(); } @@ -166,7 +168,7 @@ public static ScannableTable eval(boolean b) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; diff --git a/plus/src/main/java/org/apache/calcite/adapter/os/JpsTableFunction.java b/plus/src/main/java/org/apache/calcite/adapter/os/JpsTableFunction.java index fda75aa7378d..8380773a6e24 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/os/JpsTableFunction.java +++ b/plus/src/main/java/org/apache/calcite/adapter/os/JpsTableFunction.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Table function that executes the OS "jps" ("Java Virtual Machine Process * Status Tool") command to list all java processes of a user. @@ -42,7 +44,7 @@ private JpsTableFunction() { public static ScannableTable eval(boolean b) { return new ScannableTable() { - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { return Processes.processLines("jps", "-mlvV") .select(a0 -> { final String[] fields = a0.split(" "); @@ -70,7 +72,7 @@ public static ScannableTable eval(boolean b) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; diff --git a/plus/src/main/java/org/apache/calcite/adapter/os/PsTableFunction.java b/plus/src/main/java/org/apache/calcite/adapter/os/PsTableFunction.java index 5962c6b6c941..49102b5f3e9d 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/os/PsTableFunction.java +++ b/plus/src/main/java/org/apache/calcite/adapter/os/PsTableFunction.java @@ -17,6 +17,7 @@ package org.apache.calcite.adapter.os; import org.apache.calcite.DataContext; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.config.CalciteConnectionConfig; import org.apache.calcite.linq4j.Enumerable; @@ -35,10 +36,14 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.util.Objects.requireNonNull; + /** * Table function that executes the OS "ps" command * to list processes. @@ -53,8 +58,9 @@ private PsTableFunction() {} public static ScannableTable eval(boolean b) { return new ScannableTable() { - @Override public Enumerable scan(DataContext root) { - final RelDataType rowType = getRowType(root.getTypeFactory()); + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); + final RelDataType rowType = getRowType(typeFactory); final List fieldNames = ImmutableList.copyOf(rowType.getFieldNames()); final String[] args; @@ -171,7 +177,7 @@ private Object field(String field, String value) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; diff --git a/plus/src/main/java/org/apache/calcite/adapter/os/StdinTableFunction.java b/plus/src/main/java/org/apache/calcite/adapter/os/StdinTableFunction.java index 26e36cd3ca99..624b07853460 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/os/StdinTableFunction.java +++ b/plus/src/main/java/org/apache/calcite/adapter/os/StdinTableFunction.java @@ -34,6 +34,8 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -58,7 +60,7 @@ public static ScannableTable eval(boolean b) { final BufferedReader br = new BufferedReader(in); @Override public Enumerator enumerator() { return new Enumerator() { - String line; + @Nullable String line; int i; @Override public Object[] current() { @@ -114,7 +116,7 @@ public static ScannableTable eval(boolean b) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; diff --git a/plus/src/main/java/org/apache/calcite/adapter/os/VmstatTableFunction.java b/plus/src/main/java/org/apache/calcite/adapter/os/VmstatTableFunction.java index 042b715b481a..6b800f60aeb5 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/os/VmstatTableFunction.java +++ b/plus/src/main/java/org/apache/calcite/adapter/os/VmstatTableFunction.java @@ -17,6 +17,7 @@ package org.apache.calcite.adapter.os; import org.apache.calcite.DataContext; +import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.config.CalciteConnectionConfig; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.function.Function1; @@ -34,8 +35,12 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.List; +import static java.util.Objects.requireNonNull; + /** * Table function that executes the OS "vmstat" command * to share memory statistics. @@ -46,8 +51,9 @@ private VmstatTableFunction() {} public static ScannableTable eval(boolean b) { return new ScannableTable() { - @Override public Enumerable scan(DataContext root) { - final RelDataType rowType = getRowType(root.getTypeFactory()); + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { + JavaTypeFactory typeFactory = requireNonNull(root.getTypeFactory(), "root.getTypeFactory"); + final RelDataType rowType = getRowType(typeFactory); final List fieldNames = ImmutableList.copyOf(rowType.getFieldNames()); final String[] args; @@ -162,7 +168,7 @@ private Object field(@SuppressWarnings("unused") String field, String value) { } @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, - SqlNode parent, CalciteConnectionConfig config) { + @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) { return true; } }; diff --git a/plus/src/main/java/org/apache/calcite/adapter/tpcds/TpcdsSchema.java b/plus/src/main/java/org/apache/calcite/adapter/tpcds/TpcdsSchema.java index 68840bad5ca1..e0253e834f7d 100644 --- a/plus/src/main/java/org/apache/calcite/adapter/tpcds/TpcdsSchema.java +++ b/plus/src/main/java/org/apache/calcite/adapter/tpcds/TpcdsSchema.java @@ -43,6 +43,8 @@ import com.teradata.tpcds.column.Column; import com.teradata.tpcds.column.ColumnType; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -109,7 +111,7 @@ public TpcdsSchema(double scaleFactor) { return tableMap; } - private static Object convert(String string, Column column) { + private static @Nullable Object convert(@Nullable String string, Column column) { if (string == null) { return null; } @@ -154,9 +156,9 @@ private class TpcdsQueryableTable @Override public Queryable asQueryable(final QueryProvider queryProvider, final SchemaPlus schema, final String tableName) { //noinspection unchecked - return (Queryable) new AbstractTableQueryable(queryProvider, + return (Queryable) new AbstractTableQueryable<@Nullable Object[]>(queryProvider, schema, this, tableName) { - @Override public Enumerator enumerator() { + @Override public Enumerator<@Nullable Object[]> enumerator() { final Session session = Session.getDefaultSession() .withTable(tpcdsTable) @@ -164,14 +166,14 @@ private class TpcdsQueryableTable final Results results = Results.constructResults(tpcdsTable, session); return Linq4j.asEnumerable(results) .selectMany( - new Function1>, Enumerable>() { + new Function1>, Enumerable<@Nullable Object[]>>() { final Column[] columns = tpcdsTable.getColumns(); - @Override public Enumerable apply( - List> inRows) { - final List rows = new ArrayList<>(); - for (List strings : inRows) { - final Object[] values = new Object[columns.length]; + @Override public Enumerable<@Nullable Object[]> apply( + List> inRows) { + final List<@Nullable Object[]> rows = new ArrayList<>(); + for (List<@Nullable String> strings : inRows) { + final @Nullable Object[] values = new Object[columns.length]; for (int i = 0; i < strings.size(); i++) { values[i] = convert(strings.get(i), columns[i]); } diff --git a/plus/src/main/java/org/apache/calcite/chinook/PreferredAlbumsTableFactory.java b/plus/src/main/java/org/apache/calcite/chinook/PreferredAlbumsTableFactory.java index 801eae898941..d94b95476bc5 100644 --- a/plus/src/main/java/org/apache/calcite/chinook/PreferredAlbumsTableFactory.java +++ b/plus/src/main/java/org/apache/calcite/chinook/PreferredAlbumsTableFactory.java @@ -30,6 +30,8 @@ import com.google.common.collect.DiscreteDomain; import com.google.common.collect.Range; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; /** @@ -45,7 +47,7 @@ public class PreferredAlbumsTableFactory implements TableFactory operand, - RelDataType rowType) { + @Nullable RelDataType rowType) { return new AbstractQueryableTable(Integer.class) { @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) { return typeFactory.builder().add("ID", SqlTypeName.INTEGER).build(); diff --git a/plus/src/main/java/org/apache/calcite/chinook/PreferredGenresTableFactory.java b/plus/src/main/java/org/apache/calcite/chinook/PreferredGenresTableFactory.java index 30e6b0b70d4a..9881efc01d7c 100644 --- a/plus/src/main/java/org/apache/calcite/chinook/PreferredGenresTableFactory.java +++ b/plus/src/main/java/org/apache/calcite/chinook/PreferredGenresTableFactory.java @@ -30,6 +30,8 @@ import com.google.common.collect.DiscreteDomain; import com.google.common.collect.Range; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; /** @@ -45,7 +47,7 @@ public class PreferredGenresTableFactory implements TableFactory operand, - RelDataType rowType) { + @Nullable RelDataType rowType) { return new AbstractQueryableTable(Integer.class) { @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) { return typeFactory.builder().add("ID", SqlTypeName.INTEGER).build(); diff --git a/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTable.java b/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTable.java index f6ddbdf9f402..ddb3587958de 100644 --- a/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTable.java +++ b/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTable.java @@ -30,6 +30,8 @@ import com.google.common.collect.ImmutableMap; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -100,7 +102,7 @@ static Table create( return create(schema, tableName, redisConfig, protoRowType); } - @Override public Enumerable scan(DataContext root) { + @Override public Enumerable<@Nullable Object[]> scan(DataContext root) { return new AbstractEnumerable() { @Override public Enumerator enumerator() { return new RedisEnumerator(redisConfig, schema, tableName); diff --git a/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTableFactory.java b/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTableFactory.java index 962ad6304f18..de4042c71c60 100644 --- a/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTableFactory.java +++ b/redis/src/main/java/org/apache/calcite/adapter/redis/RedisTableFactory.java @@ -23,6 +23,8 @@ import org.apache.calcite.schema.Table; import org.apache.calcite.schema.TableFactory; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.Map; /** @@ -39,7 +41,7 @@ private RedisTableFactory() { // name that is also the same name as a complex metric @Override public Table create(SchemaPlus schema, String tableName, Map operand, - RelDataType rowType) { + @Nullable RelDataType rowType) { final RedisSchema redisSchema = schema.unwrap(RedisSchema.class); final RelProtoDataType protoRowType = rowType != null ? RelDataTypeImpl.proto(rowType) : null;