Skip to content

Commit

Permalink
fix rollback on failure in case of graph batch opertation, added test…
Browse files Browse the repository at this point in the history
… for failing references, issue #4045
  • Loading branch information
tglman committed May 5, 2015
1 parent 2c82f68 commit 9ac59f1
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 116 deletions.
Expand Up @@ -165,105 +165,110 @@ protected Object executeSQLScript(final String iText, final ODatabaseDocument db

for (int retry = 1; retry <= maxRetry; retry++) {
try {
int txBegunAtLine = -1;
int txBegunAtPart = -1;
lastResult = null;
try {
int txBegunAtLine = -1;
int txBegunAtPart = -1;
lastResult = null;

final BufferedReader reader = new BufferedReader(new StringReader(iText));
final BufferedReader reader = new BufferedReader(new StringReader(iText));

int line = 0;
int linePart = 0;
String lastLine;
boolean txBegun = false;
int line = 0;
int linePart = 0;
String lastLine;
boolean txBegun = false;

for (; line < txBegunAtLine; ++line)
// SKIP PREVIOUS COMMAND AND JUMP TO THE BEGIN IF ANY
reader.readLine();
for (; line < txBegunAtLine; ++line)
// SKIP PREVIOUS COMMAND AND JUMP TO THE BEGIN IF ANY
reader.readLine();

for (; (lastLine = reader.readLine()) != null; ++line) {
lastLine = lastLine.trim();
for (; (lastLine = reader.readLine()) != null; ++line) {
lastLine = lastLine.trim();

final List<String> lineParts = OStringSerializerHelper.smartSplit(lastLine, ';');
final List<String> lineParts = OStringSerializerHelper.smartSplit(lastLine, ';');

if (line == txBegunAtLine)
// SKIP PREVIOUS COMMAND PART AND JUMP TO THE BEGIN IF ANY
linePart = txBegunAtPart;
else
linePart = 0;
if (line == txBegunAtLine)
// SKIP PREVIOUS COMMAND PART AND JUMP TO THE BEGIN IF ANY
linePart = txBegunAtPart;
else
linePart = 0;

for (; linePart < lineParts.size(); ++linePart) {
final String lastCommand = lineParts.get(linePart);
for (; linePart < lineParts.size(); ++linePart) {
final String lastCommand = lineParts.get(linePart);

if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "let ")) {
lastResult = executeLet(lastCommand, db);
if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "let ")) {
lastResult = executeLet(lastCommand, db);

} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "begin")) {
} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "begin")) {

if (txBegun)
throw new OCommandSQLParsingException("Transaction already begun");
if (txBegun)
throw new OCommandSQLParsingException("Transaction already begun");

if (db.getTransaction().isActive())
// COMMIT ANY ACTIVE TX
db.commit();
if (db.getTransaction().isActive())
// COMMIT ANY ACTIVE TX
db.commit();

txBegun = true;
txBegunAtLine = line;
txBegunAtPart = linePart;
txBegun = true;
txBegunAtLine = line;
txBegunAtPart = linePart;

db.begin();
db.begin();

if (lastCommand.length() > "begin ".length()) {
String next = lastCommand.substring("begin ".length()).trim();
if (OStringSerializerHelper.startsWithIgnoreCase(next, "isolation ")) {
next = next.substring("isolation ".length()).trim();
db.getTransaction().setIsolationLevel(OTransaction.ISOLATION_LEVEL.valueOf(next.toUpperCase()));
if (lastCommand.length() > "begin ".length()) {
String next = lastCommand.substring("begin ".length()).trim();
if (OStringSerializerHelper.startsWithIgnoreCase(next, "isolation ")) {
next = next.substring("isolation ".length()).trim();
db.getTransaction().setIsolationLevel(OTransaction.ISOLATION_LEVEL.valueOf(next.toUpperCase()));
}
}
}

} else if ("rollback".equalsIgnoreCase(lastCommand)) {
} else if ("rollback".equalsIgnoreCase(lastCommand)) {

if (!txBegun)
throw new OCommandSQLParsingException("Transaction not begun");
if (!txBegun)
throw new OCommandSQLParsingException("Transaction not begun");

db.rollback();
db.rollback();

txBegun = false;
txBegunAtLine = -1;
txBegunAtPart = -1;
txBegun = false;
txBegunAtLine = -1;
txBegunAtPart = -1;

} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "commit")) {
if (txBegunAtLine < 0)
throw new OCommandSQLParsingException("Transaction not begun");
} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "commit")) {
if (txBegunAtLine < 0)
throw new OCommandSQLParsingException("Transaction not begun");

if (retry == 1 && lastCommand.length() > "commit ".length()) {
// FIRST CYCLE: PARSE RETRY TIMES OVERWRITING DEFAULT = 1
String next = lastCommand.substring("commit ".length()).trim();
if (OStringSerializerHelper.startsWithIgnoreCase(next, "retry ")) {
next = next.substring("retry ".length()).trim();
maxRetry = Integer.parseInt(next);
if (retry == 1 && lastCommand.length() > "commit ".length()) {
// FIRST CYCLE: PARSE RETRY TIMES OVERWRITING DEFAULT = 1
String next = lastCommand.substring("commit ".length()).trim();
if (OStringSerializerHelper.startsWithIgnoreCase(next, "retry ")) {
next = next.substring("retry ".length()).trim();
maxRetry = Integer.parseInt(next);
}
}
}

db.commit();
db.commit();

txBegun = false;
txBegunAtLine = -1;
txBegunAtPart = -1;
txBegun = false;
txBegunAtLine = -1;
txBegunAtPart = -1;

} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "sleep ")) {
executeSleep(lastCommand);
} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "sleep ")) {
executeSleep(lastCommand);

} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "return ")) {
lastResult = executeReturn(lastCommand, lastResult);
} else if (OStringSerializerHelper.startsWithIgnoreCase(lastCommand, "return ")) {
lastResult = executeReturn(lastCommand, lastResult);

// END OF SCRIPT
break;
// END OF SCRIPT
break;

} else if (lastCommand != null && lastCommand.length() > 0)
lastResult = executeCommand(lastCommand, db);
} else if (lastCommand != null && lastCommand.length() > 0)
lastResult = executeCommand(lastCommand, db);
}
}
} catch (RuntimeException ex) {
if (db.getTransaction().isActive())
db.rollback();
throw ex;
}

// COMPLETED
break;

Expand Down
Expand Up @@ -216,7 +216,7 @@ public String getSyntax() {
@Override
public void end() {
if (graph != null && shutdownFlag.getValue())
graph.shutdown(false);
graph.shutdown(false, false);
}

@Override
Expand Down
@@ -0,0 +1,184 @@
package com.orientechnologies.orient.graph;

import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import com.orientechnologies.orient.core.command.script.OCommandScript;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import com.orientechnologies.orient.core.storage.ORecordDuplicatedException;

public class TestGraphTransactionOnBatch {

@Test
public void testDuplicateRollback() {
ODatabaseDocument db = new ODatabaseDocumentTx("memory:" + TestGraphTransactionOnBatch.class.getSimpleName());
try {
db.create();
db.getMetadata().getSchema().createClass("E");
OClass V = db.getMetadata().getSchema().createClass("V");
OClass clazz = db.getMetadata().getSchema().createClass("Test");
clazz.setSuperClass(V);
clazz.createProperty("id", OType.STRING).createIndex(INDEX_TYPE.UNIQUE);
try {
db.command(
new OCommandScript(
"sql",
"BEGIN \n LET a = create vertex Test SET id = \"12345678\" \n LET b = create vertex Test SET id = \"4kkrPhGe\" \n LET c =create vertex Test SET id = \"4kkrPhGe\" \n RETURN $b \n COMMIT"))
.execute();
Assert.fail("expected record duplicate exception");
} catch (ORecordDuplicatedException ex) {

}
try {
db.command(
new OCommandScript(
"sql",
"BEGIN \n LET a = create vertex Test content {\"id\": \"12345678\"} \n LET b = create vertex Test content {\"id\": \"4kkrPhGe\"} \n LET c =create vertex Test content { \"id\": \"4kkrPhGe\"} \n RETURN $b \n COMMIT"))
.execute();
Assert.fail("expected record duplicate exception");
} catch (ORecordDuplicatedException ex) {

}

List<ODocument> res = db.query(new OSQLSynchQuery("select from Test"));
Assert.assertEquals(res.size(), 0);
} finally {
db.drop();
}
}

@Test
public void testDuplicateEggeRollback() {
ODatabaseDocument db = new ODatabaseDocumentTx("memory:" + TestGraphTransactionOnBatch.class.getSimpleName());
try {
db.create();
OClass E = db.getMetadata().getSchema().createClass("E");
db.getMetadata().getSchema().createClass("V");
OClass clazz = db.getMetadata().getSchema().createClass("Test");
clazz.setSuperClass(E);
clazz.createProperty("aKey", OType.STRING).createIndex(INDEX_TYPE.UNIQUE);
try {
db.command(
new OCommandScript(
"sql",
"BEGIN \n LET a = create vertex V \n LET b = create vertex V \n LET c =create edge Test from $a to $b SET aKey = \"12345\" \n LET d =create edge Test from $a to $b SET aKey = \"12345\" \n RETURN $c \n COMMIT"))
.execute();
Assert.fail("expected record duplicate exception");
} catch (ORecordDuplicatedException ex) {

}
List<ODocument> res = db.query(new OSQLSynchQuery("select from Test"));
Assert.assertEquals(res.size(), 0);
} finally {
db.drop();
}
}

// @Test disabled because failing
public void testReferInTxDeleteVertex() {

ODatabaseDocument db = new ODatabaseDocumentTx("memory:" + TestGraphTransactionOnBatch.class.getSimpleName());
try {
db.create();
db.getMetadata().getSchema().createClass("E");
OClass V = db.getMetadata().getSchema().createClass("V");
try {
db.command(new OCommandSQL("create vertex V set Mid = '1' ")).execute();
db.command(
new OCommandScript("sql", "begin \n LET t0 = select from V where Mid='1' \n"
+ "LET t1 = delete vertex V where Mid = '1' \n LET t2 = create vertex V set Mid = '2' \n"
+ "LET t4 = create edge E from $t2 to $t0 \n commit \n return [$t4] ")).execute();
Assert.fail("it should go in exception because referring to a in transaction delete vertex");
} catch (Exception ex) {
}
List<ODocument> res = db.query(new OSQLSynchQuery("select from E"));
Assert.assertEquals(res.size(), 0);
} finally {
db.drop();
}
}

// @Test disabled because failing
public void testReferToInTxCreatedAndDeletedVertex() {

ODatabaseDocument db = new ODatabaseDocumentTx("memory:" + TestGraphTransactionOnBatch.class.getSimpleName());
try {
db.create();
db.getMetadata().getSchema().createClass("E");
OClass V = db.getMetadata().getSchema().createClass("V");
try {
db.command(
new OCommandScript("sql", "begin \n LET t0 = create vertex V set Mid = '1' \n"
+ "LET t1 = delete vertex V where Mid = '1' \n LET t2 = create vertex V set Mid = '2' \n"
+ "LET t4 = create edge E from $t2 to $t0 \n commit \n return [$t4] ")).execute();
Assert.fail("it should go in exception because referring to a in transaction delete vertex");
} catch (Exception ex) {
}
List<ODocument> res = db.query(new OSQLSynchQuery("select from E"));
Assert.assertEquals(res.size(), 0);
} finally {
db.drop();
}
}

// @Test disabled because failing
public void testReferToNotExistingVertex() {

ODatabaseDocument db = new ODatabaseDocumentTx("memory:" + TestGraphTransactionOnBatch.class.getSimpleName());
try {
db.create();
db.getMetadata().getSchema().createClass("E");
OClass V = db.getMetadata().getSchema().createClass("V");
try {
db.command(
new OCommandScript("sql", "begin \n \n LET t2 = create vertex V set Mid = \"2\" \n"
+ "LET t5 = select from V where Mid = '123456789' \n LET t3 = create edge E from $t5 to $t2 \n"
+ "\n commit \n return [$t3] ")).execute();
Assert.fail("it should go in exception because referring to a not existing vertex");
} catch (Exception ex) {
}
List<ODocument> res = db.query(new OSQLSynchQuery("select from E"));
Assert.assertEquals(res.size(), 0);
} finally {
db.drop();
}
}

@Test
public void testReferToNotExistingVariableInTx() {
ODatabaseDocument db = new ODatabaseDocumentTx("memory:" + TestGraphTransactionOnBatch.class.getSimpleName());
try {
db.create();
db.getMetadata().getSchema().createClass("E");
db.getMetadata().getSchema().createClass("V");
db.command(new OCommandSQL(" create vertex V set Mid ='2'")).execute();
List<ODocument> res = db.query(new OSQLSynchQuery("select from V"));
Assert.assertEquals(1, res.size());

try {
db.command(
new OCommandScript("sql",
"begin \n Let t0 = delete vertex V where Mid='2' \n LET t1 = create edge E from $t2 to $t3 \n commit \n return $t1 "))
.execute();
Assert.fail("it should go in exception because referring to not existing variable");
} catch (Exception ex) {
}
Assert.assertFalse(db.getTransaction().isActive());

res = db.query(new OSQLSynchQuery("select from V"));
Assert.assertEquals(1, res.size());
} finally {
db.drop();
}
}

}

0 comments on commit 9ac59f1

Please sign in to comment.