Skip to content

Commit

Permalink
Allow whitespace around transaction statements
Browse files Browse the repository at this point in the history
Give transaction statements have the same flexibility wrt whitespace as
for regular cypher statements.
  • Loading branch information
cleishm committed Jun 12, 2017
1 parent df04439 commit 16702b8
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 30 deletions.
Expand Up @@ -21,6 +21,7 @@


import java.time.Clock; import java.time.Clock;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern;


import org.neo4j.bolt.security.auth.AuthenticationResult; import org.neo4j.bolt.security.auth.AuthenticationResult;
import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark;
Expand All @@ -38,9 +39,9 @@


public class TransactionStateMachine implements StatementProcessor public class TransactionStateMachine implements StatementProcessor
{ {
private static final String BEGIN = "BEGIN"; private static final Pattern BEGIN = Pattern.compile("(?i)^\\s*BEGIN\\s*;?\\s*$");
private static final String COMMIT = "COMMIT"; private static final Pattern COMMIT = Pattern.compile("(?i)^\\s*COMMIT\\s*;?\\s*$");
private static final String ROLLBACK = "ROLLBACK"; private static final Pattern ROLLBACK = Pattern.compile("(?i)^\\s*ROLLBACK\\s*;?\\s*$");


final SPI spi; final SPI spi;
final MutableTransactionState ctx; final MutableTransactionState ctx;
Expand Down Expand Up @@ -148,7 +149,7 @@ State run( MutableTransactionState ctx, SPI spi, String statement,
Map<String, Object> params ) throws KernelException Map<String, Object> params ) throws KernelException


{ {
if ( statement.equalsIgnoreCase( BEGIN ) ) if ( BEGIN.matcher( statement ).matches() )
{ {
ctx.currentTransaction = spi.beginTransaction( ctx.securityContext ); ctx.currentTransaction = spi.beginTransaction( ctx.securityContext );


Expand All @@ -165,12 +166,12 @@ State run( MutableTransactionState ctx, SPI spi, String statement,


return EXPLICIT_TRANSACTION; return EXPLICIT_TRANSACTION;
} }
else if ( statement.equalsIgnoreCase( COMMIT ) ) else if ( COMMIT.matcher( statement ).matches() )
{ {
throw new QueryExecutionKernelException( throw new QueryExecutionKernelException(
new InvalidSemanticsException( "No current transaction to commit." ) ); new InvalidSemanticsException( "No current transaction to commit." ) );
} }
else if ( statement.equalsIgnoreCase( ROLLBACK ) ) else if ( ROLLBACK.matcher( statement ).matches() )
{ {
throw new QueryExecutionKernelException( throw new QueryExecutionKernelException(
new InvalidSemanticsException( "No current transaction to rollback." ) ); new InvalidSemanticsException( "No current transaction to rollback." ) );
Expand Down Expand Up @@ -235,12 +236,12 @@ void streamResult( MutableTransactionState ctx,
State run( MutableTransactionState ctx, SPI spi, String statement, Map<String, Object> params ) State run( MutableTransactionState ctx, SPI spi, String statement, Map<String, Object> params )
throws KernelException throws KernelException
{ {
if ( statement.equalsIgnoreCase( BEGIN ) ) if ( BEGIN.matcher( statement ).matches() )
{ {
throw new QueryExecutionKernelException( throw new QueryExecutionKernelException(
new InvalidSemanticsException( "Nested transactions are not supported." ) ); new InvalidSemanticsException( "Nested transactions are not supported." ) );
} }
else if ( statement.equalsIgnoreCase( COMMIT ) ) else if ( COMMIT.matcher( statement ).matches() )
{ {
closeTransaction( ctx, true ); closeTransaction( ctx, true );
long txId = spi.newestEncounteredTxId(); long txId = spi.newestEncounteredTxId();
Expand All @@ -249,7 +250,7 @@ else if ( statement.equalsIgnoreCase( COMMIT ) )


return AUTO_COMMIT; return AUTO_COMMIT;
} }
else if ( statement.equalsIgnoreCase( ROLLBACK ) ) else if ( ROLLBACK.matcher( statement ).matches() )
{ {
closeTransaction( ctx, false ); closeTransaction( ctx, false );
ctx.currentResult = BoltResult.EMPTY; ctx.currentResult = BoltResult.EMPTY;
Expand Down
Expand Up @@ -19,14 +19,18 @@
*/ */
package org.neo4j.bolt.v1.runtime; package org.neo4j.bolt.v1.runtime;


import org.junit.Before;
import org.junit.Test; import org.junit.Test;


import java.util.Collections;
import java.util.Map; import java.util.Map;


import org.neo4j.kernel.impl.query.QueryExecutionKernelException;
import org.neo4j.time.FakeClock; import org.neo4j.time.FakeClock;


import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
Expand All @@ -36,59 +40,170 @@


public class TransactionStateMachineTest public class TransactionStateMachineTest
{ {
private TransactionStateMachineSPI stateMachineSPI;
private TransactionStateMachine.MutableTransactionState mutableState;
private TransactionStateMachine stateMachine;

@Before
public void createMocks()
{
stateMachineSPI = mock( TransactionStateMachineSPI.class );
mutableState = mock(TransactionStateMachine.MutableTransactionState.class);
stateMachine = new TransactionStateMachine( stateMachineSPI, AUTH_DISABLED, new FakeClock() );
}

@Test @Test
public void shouldNotWaitWhenNoBookmarkSupplied() throws Exception public void shouldTransitionToExplicitTransactionOnBegin() throws Exception
{ {
TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class ); assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run(
TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); mutableState, stateMachineSPI, "begin", Collections.emptyMap() ),
TransactionStateMachine.State.EXPLICIT_TRANSACTION );
assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run(
mutableState, stateMachineSPI, "BEGIN", Collections.emptyMap() ),
TransactionStateMachine.State.EXPLICIT_TRANSACTION );
assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run(
mutableState, stateMachineSPI, " begin ", Collections.emptyMap() ),
TransactionStateMachine.State.EXPLICIT_TRANSACTION );
assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run(
mutableState, stateMachineSPI, " BeGiN ; ", Collections.emptyMap() ),
TransactionStateMachine.State.EXPLICIT_TRANSACTION );
}


stateMachine.run( "BEGIN", emptyMap() ); @Test
public void shouldTransitionToAutoCommitOnCommit() throws Exception
{
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, "commit", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, "COMMIT", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, " commit ", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, " CoMmIt ; ", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
}


@Test
public void shouldTransitionToAutoCommitOnRollback() throws Exception
{
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, "rollback", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, "ROLLBACK", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, " rollback ", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, " RoLlBaCk ; ", Collections.emptyMap() ),
TransactionStateMachine.State.AUTO_COMMIT );
}

@Test
public void shouldThrowOnBeginInExplicitTransaction() throws Exception
{
try
{
TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, "begin", Collections.emptyMap() );
}
catch ( QueryExecutionKernelException ex )
{
assertEquals("Nested transactions are not supported.", ex.getMessage() );
}
try
{
TransactionStateMachine.State.EXPLICIT_TRANSACTION.run(
mutableState, stateMachineSPI, " BEGIN ", Collections.emptyMap() );
}
catch ( QueryExecutionKernelException ex )
{
assertEquals("Nested transactions are not supported.", ex.getMessage() );
}
}

@Test
public void shouldThrowOnRollbackInAutoCommit() throws Exception
{
try
{
TransactionStateMachine.State.AUTO_COMMIT.run(
mutableState, stateMachineSPI, "rollback", Collections.emptyMap() );
}
catch ( QueryExecutionKernelException ex )
{
assertEquals("No current transaction to rollback.", ex.getMessage() );
}
try
{
TransactionStateMachine.State.AUTO_COMMIT.run(
mutableState, stateMachineSPI, " ROLLBACK ", Collections.emptyMap() );
}
catch ( QueryExecutionKernelException ex )
{
assertEquals("No current transaction to rollback.", ex.getMessage() );
}
}

@Test
public void shouldThrowOnCommitInAutoCommit() throws Exception
{
try
{
TransactionStateMachine.State.AUTO_COMMIT.run(
mutableState, stateMachineSPI, "commit", Collections.emptyMap() );
}
catch ( QueryExecutionKernelException ex )
{
assertEquals("No current transaction to commit.", ex.getMessage() );
}
try
{
TransactionStateMachine.State.AUTO_COMMIT.run(
mutableState, stateMachineSPI, " COMMIT ", Collections.emptyMap() );
}
catch ( QueryExecutionKernelException ex )
{
assertEquals("No current transaction to commit.", ex.getMessage() );
}
}

@Test
public void shouldNotWaitWhenNoBookmarkSupplied() throws Exception
{
stateMachine.run( "BEGIN", emptyMap() );
verify( stateMachineSPI, never() ).awaitUpToDate( anyLong() ); verify( stateMachineSPI, never() ).awaitUpToDate( anyLong() );
} }


@Test @Test
public void shouldAwaitSingleBookmark() throws Exception public void shouldAwaitSingleBookmark() throws Exception
{ {
TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class );
TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI );

stateMachine.run( "BEGIN", map( "bookmark", "neo4j:bookmark:v1:tx15" ) ); stateMachine.run( "BEGIN", map( "bookmark", "neo4j:bookmark:v1:tx15" ) );

verify( stateMachineSPI ).awaitUpToDate( 15 ); verify( stateMachineSPI ).awaitUpToDate( 15 );
} }


@Test @Test
public void shouldAwaitMultipleBookmarks() throws Exception public void shouldAwaitMultipleBookmarks() throws Exception
{ {
TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class );
TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI );

Map<String,Object> params = map( "bookmarks", asList( Map<String,Object> params = map( "bookmarks", asList(
"neo4j:bookmark:v1:tx15", "neo4j:bookmark:v1:tx5", "neo4j:bookmark:v1:tx92", "neo4j:bookmark:v1:tx9" ) "neo4j:bookmark:v1:tx15", "neo4j:bookmark:v1:tx5", "neo4j:bookmark:v1:tx92", "neo4j:bookmark:v1:tx9" )
); );
stateMachine.run( "BEGIN", params ); stateMachine.run( "BEGIN", params );

verify( stateMachineSPI ).awaitUpToDate( 92 ); verify( stateMachineSPI ).awaitUpToDate( 92 );
} }


@Test @Test
public void shouldAwaitMultipleBookmarksWhenBothSingleAndMultipleSupplied() throws Exception public void shouldAwaitMultipleBookmarksWhenBothSingleAndMultipleSupplied() throws Exception
{ {
TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class );
TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI );

Map<String,Object> params = map( Map<String,Object> params = map(
"bookmark", "neo4j:bookmark:v1:tx42", "bookmark", "neo4j:bookmark:v1:tx42",
"bookmarks", asList( "neo4j:bookmark:v1:tx47", "neo4j:bookmark:v1:tx67", "neo4j:bookmark:v1:tx45" ) "bookmarks", asList( "neo4j:bookmark:v1:tx47", "neo4j:bookmark:v1:tx67", "neo4j:bookmark:v1:tx45" )
); );
stateMachine.run( "BEGIN", params ); stateMachine.run( "BEGIN", params );

verify( stateMachineSPI ).awaitUpToDate( 67 ); verify( stateMachineSPI ).awaitUpToDate( 67 );
} }

private static TransactionStateMachine newTransactionStateMachine( TransactionStateMachineSPI stateMachineSPI )
{
return new TransactionStateMachine( stateMachineSPI, AUTH_DISABLED, new FakeClock() );
}
} }

0 comments on commit 16702b8

Please sign in to comment.