Skip to content

Commit

Permalink
CREATE UNIQUE is now also checked so that identifiers used with prope…
Browse files Browse the repository at this point in the history
…rties are not already in scope
  • Loading branch information
systay committed Aug 5, 2012
1 parent 7126d0d commit e6c8a2f
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import collection.Map
import collection.mutable
import org.neo4j.cypher.SyntaxException


class CreateNodesAndRelationshipsBuilder(db: GraphDatabaseService) extends PlanBuilder {
def apply(plan: ExecutionPlanInProgress) = {
val q = plan.query
Expand Down Expand Up @@ -66,14 +65,15 @@ class CreateNodesAndRelationshipsBuilder(db: GraphDatabaseService) extends PlanB
case CreateNodeStartItem(key, props)
if createdNodes.contains(key) && props.nonEmpty =>
throw new SyntaxException("Node `%s` has already been created. Can't assign properties to it again.".format(key))

case CreateNodeStartItem(key, _) if createdNodes.contains(key) => None

case x@CreateNodeStartItem(key, _) =>
createdNodes += key
Some(x)
}
}


private def alsoCreateNode(e: (Expression, Map[String,Expression]), symbols: SymbolTable, commands: Seq[UpdateAction]): Seq[CreateNodeStartItem] = e._1 match {
case Entity(name) =>
val nodeFromUnderlyingPipe = symbols.satisfies(Seq(Identifier(name, NodeType())))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class UpdateActionBuilder(db: GraphDatabaseService) extends PlanBuilder {
val startCmds = startItems.map(_.map(_.asInstanceOf[UpdateAction]))
val commands = updateCmds ++ startCmds


val resultPipe = new ExecuteUpdateCommandsPipe(p, db, commands.map(_.token))

plan.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@ case class UniqueLink(start: NamedExpectation, end: NamedExpectation, rel: Named
}).toList

rels match {
case List() =>
case List() =>
val tx = state.transaction.getOrElse(throw new RuntimeException("I need a transaction!"))
Some(this ->Update(createUpdateActions(dir, startNode, end), () => {
Some(this -> Update(createUpdateActions(dir, startNode, end), () => {
Seq(tx.acquireWriteLock(startNode))
}))
case List(r) => Some(this->Traverse(rel.name -> r, end.name -> r.getOtherNode(startNode)))
case _ => throw new UniquePathNotUniqueException("The pattern " + this + " produced multiple possible paths, and that is not allowed")

case List(r) => Some(this -> Traverse(rel.name -> r, end.name -> r.getOtherNode(startNode)))

case _ => throw new UniquePathNotUniqueException("The pattern " + this + " produced multiple possible paths, and that is not allowed")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
*/
package org.neo4j.cypher.internal.pipes

import org.neo4j.cypher.internal.mutation.UpdateAction
import org.neo4j.cypher.internal.mutation.{CreateUniqueAction, NamedExpectation, UpdateAction}
import org.neo4j.graphdb.{GraphDatabaseService, NotInTransactionException}
import org.neo4j.cypher.{CypherTypeException, ParameterWrongTypeException, InternalException}
import org.neo4j.cypher.internal.commands.{Expression, Entity, CreateRelationshipStartItem, CreateNodeStartItem}
import org.neo4j.cypher.{SyntaxException, ParameterWrongTypeException, InternalException}
import org.neo4j.cypher.internal.commands.{Entity, Expression, CreateRelationshipStartItem, CreateNodeStartItem}
import collection.Map

class ExecuteUpdateCommandsPipe(source: Pipe, db: GraphDatabaseService, commands: Seq[UpdateAction])
Expand Down Expand Up @@ -56,28 +56,28 @@ class ExecuteUpdateCommandsPipe(source: Pipe, db: GraphDatabaseService, commands
}


private def extractEntitiesWithProperties(action: UpdateAction): Seq[(String, Iterable[Any])] = action match {
case CreateNodeStartItem(key, props) => Seq((key, props))
case CreateRelationshipStartItem(key, from, to, _, props) => Seq((key, props)) ++ extractIfEntity(from) ++ extractIfEntity(to)
private def extractEntitiesWithProperties(action: UpdateAction): Seq[NamedExpectation] = action match {
case CreateNodeStartItem(key, props) => Seq(NamedExpectation(key, props))
case CreateRelationshipStartItem(key, from, to, _, props) => Seq(NamedExpectation(key, props)) ++ extractIfEntity(from) ++ extractIfEntity(to)
case CreateUniqueAction(links@_*) => links.flatMap(l => Seq(l.start, l.end, l.rel))
case _ => Seq()
}


def extractIfEntity(from: (Expression, Map[String, Expression])): Option[(String, Map[String, Expression])] = {
def extractIfEntity(from: (Expression, Map[String, Expression])): Option[NamedExpectation] = {
from match {
case (Entity(key), props) => Some((key, props))
case (Entity(key), props) => Some(NamedExpectation(key, props))
case _ => None
}
}

private def assertNothingIsCreatedWhenItShouldNot() {
val entitiesAndProps: Seq[(String, Iterable[Any])] = commands.flatMap(cmd => extractEntitiesWithProperties(cmd))
val entitiesWithProps = entitiesAndProps.filter(_._2.nonEmpty)
val entitiesAndProps: Seq[NamedExpectation] = commands.flatMap(cmd => extractEntitiesWithProperties(cmd))
val entitiesWithProps = entitiesAndProps.filter(_.properties.nonEmpty)

entitiesWithProps.foreach {
case (key, props) => if (source.symbols.keys.contains(key))
throw new CypherTypeException("Can't create `%s` with properties here. It already exists in this context".format(key))
}
entitiesWithProps.foreach(l => if (source.symbols.keys.contains(l.name))
throw new SyntaxException("Can't create `%s` with properties here. It already exists in this context".format(l.name))
)
}

def executionPlan() = source.executionPlan() + "\nUpdateGraph(" + commands.mkString + ")"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
*/
package org.neo4j.cypher

import org.junit.{Ignore, Test}
import org.junit.Test
import org.junit.Assert._
import collection.JavaConverters._
import org.scalatest.Assertions
Expand Down Expand Up @@ -471,7 +471,7 @@ return distinct center""")

@Test
def create_with_parameters_is_not_ok_when_identifier_already_exists() {
intercept[CypherTypeException](parseAndExecute("create a with a create (a {name:\"Foo\"})-[:BAR]->()").toList)
intercept[SyntaxException](parseAndExecute("create a with a create (a {name:\"Foo\"})-[:BAR]->()").toList)
}

@Test
Expand All @@ -497,6 +497,10 @@ return distinct center""")
intercept[SyntaxException](parseAndExecute("create a-[:test]->b, (a {name:'a'})-[:test2]->c"))
}

@Test
def cant_set_properties_after_node_is_already_created2() {
intercept[SyntaxException](parseAndExecute("create a-[:test]->b create unique (a {name:'a'})-[:test2]->c"))
}
}
trait StatisticsChecker extends Assertions {
def assertStats(result: ExecutionResult,
Expand Down

0 comments on commit e6c8a2f

Please sign in to comment.