All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.cypher.internal.compiler.CypherParsing.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.cypher.internal.compiler

import org.neo4j.configuration.GraphDatabaseInternalSettings
import org.neo4j.configuration.GraphDatabaseInternalSettings.ExtractLiteral
import org.neo4j.cypher.internal.ast.semantics.SemanticFeature
import org.neo4j.cypher.internal.compiler.helpers.ParameterValueTypeHelper
import org.neo4j.cypher.internal.compiler.phases.BaseContextImpl
import org.neo4j.cypher.internal.compiler.phases.CompilationPhases
import org.neo4j.cypher.internal.compiler.phases.CompilationPhases.ParsingConfig
import org.neo4j.cypher.internal.compiler.phases.CompilationPhases.defaultSemanticFeatures
import org.neo4j.cypher.internal.config.CypherConfiguration
import org.neo4j.cypher.internal.frontend.phases.BaseState
import org.neo4j.cypher.internal.frontend.phases.CompilationPhaseTracer
import org.neo4j.cypher.internal.frontend.phases.InitialState
import org.neo4j.cypher.internal.frontend.phases.InternalSyntaxUsageStats
import org.neo4j.cypher.internal.frontend.phases.Monitors
import org.neo4j.cypher.internal.frontend.phases.ScopedProcedureSignatureResolver
import org.neo4j.cypher.internal.macros.AssertMacros
import org.neo4j.cypher.internal.options.CypherVersion
import org.neo4j.cypher.internal.parser.v25.Cypher25ParserUtil
import org.neo4j.cypher.internal.parser.v5.Cypher5ParserUtil
import org.neo4j.cypher.internal.planner.spi.IDPPlannerName
import org.neo4j.cypher.internal.planner.spi.PlannerNameFor
import org.neo4j.cypher.internal.util.AnonymousVariableNameGenerator
import org.neo4j.cypher.internal.util.CancellationChecker
import org.neo4j.cypher.internal.util.InputPosition
import org.neo4j.cypher.internal.util.InternalNotificationLogger
import org.neo4j.kernel.database.DatabaseReference
import org.neo4j.values.virtual.MapValue

class CypherParsing(
  monitors: Monitors,
  config: CypherParsingConfig,
  internalSyntaxUsageStats: InternalSyntaxUsageStats
) {

  def parseQuery(
    queryText: String,
    rawQueryText: String,
    cypherVersion: CypherVersion,
    notificationLogger: InternalNotificationLogger,
    plannerNameText: String = IDPPlannerName.name,
    offset: Option[InputPosition],
    tracer: CompilationPhaseTracer,
    params: MapValue,
    cancellationChecker: CancellationChecker,
    resolver: Option[ScopedProcedureSignatureResolver] = None,
    sessionDatabase: DatabaseReference
  ): BaseState = {
    val plannerName = PlannerNameFor(plannerNameText)
    val startState = InitialState(queryText, plannerName, new AnonymousVariableNameGenerator)
    val context = BaseContextImpl(
      tracer,
      notificationLogger,
      rawQueryText,
      offset,
      monitors,
      cancellationChecker,
      internalSyntaxUsageStats,
      sessionDatabase
    )
    val paramTypes = ParameterValueTypeHelper.asCypherTypeMap(params, config.useParameterSizeHint)

    val features = CypherParsingConfig.getEnabledFeatures(
      config.semanticFeatures,
      if (sessionDatabase == null) None else Some(sessionDatabase.isComposite),
      config.queryRouterForCompositeEnabled
    )
    CompilationPhases.parsing(
      ParsingConfig(
        cypherVersion = cypherVersion.actualVersion,
        extractLiterals = config.extractLiterals,
        parameterTypeMapping = paramTypes,
        semanticFeatures = features,
        obfuscateLiterals = config.obfuscateLiterals(),
        antlrParserEnabled = config.cypherParserAntlrEnabled()
      ),
      resolver = resolver
    ).transform(startState, context)
  }

  /*
   * The DFA cache is an internal ANTLR cache, of which it exist one instance per Cypher version.
   * This cache grow significantly when parsing a lot of queries covering large part of the language,
   * in worst case leading to OOM of the Java heap.
   */
  def clearDFACaches(): Unit = {
    Cypher5ParserUtil.clearDFACache()
    Cypher25ParserUtil.clearDFACache()
  }
}

case class CypherParsingConfig(
  extractLiterals: ExtractLiteral = ExtractLiteral.ALWAYS,
  useParameterSizeHint: Boolean = true,
  semanticFeatures: Seq[SemanticFeature] = defaultSemanticFeatures,
  obfuscateLiterals: () => Boolean = () => false,
  cypherParserAntlrEnabled: () => Boolean = () => false,
  queryRouterForCompositeEnabled: Boolean = false
)

object CypherParsingConfig {

  def fromCypherConfiguration(cypherConfiguration: CypherConfiguration): CypherParsingConfig = {
    def obfuscateLiterals(): Boolean = {
      // Is dynamic, but documented to not affect caching.
      cypherConfiguration.obfuscateLiterals
    }

    val extractLiterals: ExtractLiteral = {
      AssertMacros.checkOnlyWhenAssertionsAreEnabled(
        !GraphDatabaseInternalSettings.extract_literals.dynamic()
      )
      cypherConfiguration.extractLiterals
    }

    val useParameterSizeHint: Boolean = {
      AssertMacros.checkOnlyWhenAssertionsAreEnabled(
        !GraphDatabaseInternalSettings.cypher_size_hint_parameters.dynamic()
      )
      cypherConfiguration.useParameterSizeHint
    }

    val enabledSemanticFeatures: Seq[SemanticFeature] = {
      AssertMacros.checkOnlyWhenAssertionsAreEnabled(
        !GraphDatabaseInternalSettings.cypher_enable_extra_semantic_features.dynamic()
      )
      CompilationPhases.enabledSemanticFeatures(
        cypherConfiguration.enableExtraSemanticFeatures ++ cypherConfiguration.toggledFeatures(Map(
          GraphDatabaseInternalSettings.show_setting -> SemanticFeature.ShowSetting.productPrefix,
          GraphDatabaseInternalSettings.composable_commands -> SemanticFeature.ComposableCommands.productPrefix,
          GraphDatabaseInternalSettings.graph_type_enabled -> SemanticFeature.GraphTypes.productPrefix
        ))
      )
    }

    def cypherParserAntlrEnabled(): Boolean = {
      // Is dynamic, note that it needs to be combined with clearing query caches to take effect.
      cypherConfiguration.cypherParserAntlrEnabled
    }

    val queryRouterForCompositeQueriesEnabled: Boolean = cypherConfiguration.allowCompositeQueries

    CypherParsingConfig(
      extractLiterals,
      useParameterSizeHint,
      enabledSemanticFeatures,
      () => obfuscateLiterals(),
      () => cypherParserAntlrEnabled(),
      queryRouterForCompositeQueriesEnabled
    )
  }

  def getEnabledFeatures(
    semanticFeatures: Seq[SemanticFeature],
    targetsCompositeInQueryRouter: Option[Boolean],
    queryRouterForCompositeEnabled: Boolean
  ): Seq[SemanticFeature] = {
    if (queryRouterForCompositeEnabled && targetsCompositeInQueryRouter.getOrElse(false))
      semanticFeatures ++ Seq(SemanticFeature.UseAsMultipleGraphsSelector, SemanticFeature.MultipleGraphs)
    else
      semanticFeatures ++ Seq(SemanticFeature.UseAsSingleGraphSelector)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy