diff --git a/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java b/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java index 1f5a8d19e7..a5231da1a2 100644 --- a/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java +++ b/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java @@ -24,6 +24,7 @@ import com.arcadedb.query.sql.executor.Result; import com.arcadedb.query.sql.executor.ResultSet; import com.arcadedb.remote.RemoteDatabase; +import com.arcadedb.schema.Schema; import com.arcadedb.utility.CollectionUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -136,9 +137,11 @@ void createTypesAndDataWithSqlScripts() { database.command("sqlscript", """ - BEGIN;LET photo = CREATE VERTEX Photos SET id = "p5555", name = "download3.jpg"; + BEGIN; + LET photo = CREATE VERTEX Photos SET id = "p5555", name = "download3.jpg"; LET user = SELECT FROM Users WHERE id = "u1111"; - LET userEdge = CREATE EDGE HasUploaded FROM $user TO $photo SET kind = "User_Photos"; + LET edgeType = 'HasUploaded'; + LET userEdge = CREATE EDGE $edgeType FROM $user TO $photo SET kind = "User_Photos"; COMMIT RETRY 30; RETURN $photo;"""); @@ -164,6 +167,47 @@ void renameTypeAndAliases() { } + @Test + void createSchemaWithDynamicSqlScript() { + database.command("sqlscript", """ + BEGIN; + + LET vTypes = ['V1', 'V2', 'V3']; + FOREACH ($vType IN $vTypes) { + CREATE VERTEX TYPE $vType; + } + + LET eTypes = ['E1', 'E2', 'E3']; + FOREACH ($eType IN $eTypes) { + CREATE EDGE TYPE $eType; + } + + LET dTypes = ['D1', 'D2', 'D3']; + FOREACH ($dType IN $dTypes) { + CREATE DOCUMENT TYPE $dType ; + } + + LET types = ['V1', 'V2', 'V3','E1', 'E2', 'E3','D1', 'D2', 'D3']; + FOREACH ($type IN $types) { + CREATE PROPERTY $type.id STRING; + CREATE INDEX ON $type (id) UNIQUE NULL_STRATEGY SKIP; + } + COMMIT; + """); + + Schema schema = database.getSchema(); + System.out.println("schema.toString() = " + schema.toString()); + assertThat(schema.existsType("V1")).isTrue(); + assertThat(schema.existsType("V2")).isTrue(); + assertThat(schema.existsType("V3")).isTrue(); + assertThat(schema.existsType("D1")).isTrue(); + assertThat(schema.existsType("D2")).isTrue(); + assertThat(schema.existsType("D3")).isTrue(); + assertThat(schema.existsType("E1")).isTrue(); + assertThat(schema.existsType("E2")).isTrue(); + assertThat(schema.existsType("E3")).isTrue(); + } + @Test @Disabled void testMultipleInsert() throws SQLException, ClassNotFoundException { diff --git a/engine/src/main/java/com/arcadedb/query/sql/SQLScriptQueryEngine.java b/engine/src/main/java/com/arcadedb/query/sql/SQLScriptQueryEngine.java index 4e451adcbc..b0580c3a19 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/SQLScriptQueryEngine.java +++ b/engine/src/main/java/com/arcadedb/query/sql/SQLScriptQueryEngine.java @@ -213,8 +213,9 @@ private ResultSet executeInternal(final List statements, final Comman throw new CommandSQLParsingException("Found COMMIT statement without a BEGIN"); } - if (stm instanceof LetStatement letStatement) + if (stm instanceof LetStatement letStatement) { scriptContext.declareScriptVariable(letStatement.getVariableName().getStringValue()); + } } return new LocalResultSet(plan); diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/BasicCommandContext.java b/engine/src/main/java/com/arcadedb/query/sql/executor/BasicCommandContext.java index 879264feba..f8ae31c278 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/BasicCommandContext.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/BasicCommandContext.java @@ -23,7 +23,10 @@ import com.arcadedb.database.DatabaseInternal; import com.arcadedb.database.Document; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * Basic implementation of CommandContext interface that stores variables in a map. Supports parent/child context to build a tree @@ -109,47 +112,62 @@ public Object getVariable(final String name) { return getVariable(name, null); } - public Object getVariable(String name, final Object iDefault) { + public Object getVariable(String name, final Object defaultValue) { if (name == null) - return iDefault; + return defaultValue; - final Object result; + Object result; if (name.startsWith("$")) name = name.substring(1); - final String firstPart; - firstPart = name; + String fieldName = null; + if (name.contains(".")) { + final String[] parts = name.split("\\."); + if (parts.length > 2) { + throw new com.arcadedb.exception.CommandSQLParsingException( + "Nested property access is not supported in this context: " + name); + } + name = parts[0]; + if (parts.length > 1) { + fieldName = parts[1]; + } + } - if (firstPart.equalsIgnoreCase("CONTEXT")) + if (name.equalsIgnoreCase("CONTEXT")) result = getVariables(); - else if (firstPart.equalsIgnoreCase("PARENT")) + else if (name.equalsIgnoreCase("PARENT")) result = parent; - else if (firstPart.equalsIgnoreCase("ROOT")) { + else if (name.equalsIgnoreCase("ROOT")) { CommandContext p = this; while (p.getParent() != null) p = p.getParent(); result = p; + } else { - if (variables != null && variables.containsKey(firstPart)) - result = variables.get(firstPart); + if (variables != null && variables.containsKey(name)) + result = variables.get(name); else { if (child != null) - result = child.getVariable(firstPart); + result = child.getVariable(name); else - result = getVariableFromParentHierarchy(firstPart); + result = getVariableFromParentHierarchy(name); } } - return result != null ? result : iDefault; + if (fieldName != null) { + if (result instanceof Result result1) + result = result1.getProperty(fieldName); + } + return result != null ? result : defaultValue; } - protected Object getVariableFromParentHierarchy(final String varName) { - if (this.variables != null && variables.containsKey(varName)) { - return variables.get(varName); + protected Object getVariableFromParentHierarchy(final String name) { + if (this.variables != null && variables.containsKey(name)) { + return variables.get(name); } if (parent != null && parent instanceof BasicCommandContext context) { - return context.getVariableFromParentHierarchy(varName); + return context.getVariableFromParentHierarchy(name); } return null; } diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/CommandContext.java b/engine/src/main/java/com/arcadedb/query/sql/executor/CommandContext.java index 81dd33ec21..8722e60c2e 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/CommandContext.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/CommandContext.java @@ -35,11 +35,11 @@ public interface CommandContext { Object getVariablePath(String name, Object iDefault); - Object getVariable(String iName); + Object getVariable(String name); - Object getVariable(String iName, Object iDefaultValue); + Object getVariable(String name, Object iDefaultValue); - CommandContext setVariable(String iName, Object iValue); + CommandContext setVariable(String name, Object iValue); CommandContext incrementVariable(String getNeighbors); @@ -47,7 +47,7 @@ public interface CommandContext { CommandContext getParent(); - CommandContext setParent(CommandContext iParentContext); + CommandContext setParent(CommandContext parentContext); CommandContext setChild(CommandContext context); diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java index 10a9f28561..74671659fa 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java @@ -20,7 +20,6 @@ import com.arcadedb.database.Database; import com.arcadedb.exception.CommandSQLParsingException; -import com.arcadedb.index.TypeIndex; import com.arcadedb.query.sql.parser.CreateEdgeStatement; import com.arcadedb.query.sql.parser.Expression; import com.arcadedb.query.sql.parser.Identifier; @@ -29,7 +28,6 @@ import com.arcadedb.query.sql.parser.JsonArray; import com.arcadedb.query.sql.parser.UpdateItem; import com.arcadedb.schema.DocumentType; -import com.arcadedb.schema.EdgeType; import java.util.ArrayList; import java.util.List; @@ -39,7 +37,7 @@ */ public class CreateEdgeExecutionPlanner { - protected Identifier targetClass; + protected Identifier targetType; protected final Identifier targetBucketName; protected final Expression leftExpression; protected final Expression rightExpression; @@ -48,7 +46,7 @@ public class CreateEdgeExecutionPlanner { protected final InsertBody body; public CreateEdgeExecutionPlanner(final CreateEdgeStatement statement) { - this.targetClass = statement.getTargetType() == null ? null : statement.getTargetType().copy(); + this.targetType = statement.getTargetType() == null ? null : statement.getTargetType().copy(); this.targetBucketName = statement.getTargetBucketName() == null ? null : statement.getTargetBucketName().copy(); this.leftExpression = statement.getLeftExpression() == null ? null : statement.getLeftExpression().copy(); this.rightExpression = statement.getRightExpression() == null ? null : statement.getRightExpression().copy(); @@ -65,7 +63,12 @@ public CreateEdgeExecutionPlanner(final CreateEdgeStatement statement) { public InsertExecutionPlan createExecutionPlan(final CommandContext context) { - if (targetClass == null) { + if (targetType.getStringValue().startsWith("$")) { + String variable = (String) context.getVariable(targetType.getStringValue()); + targetType = new Identifier(variable); + } + + if (targetType == null) { if (targetBucketName == null) { throw new CommandSQLParsingException("Missing target"); } else { @@ -73,7 +76,7 @@ public InsertExecutionPlan createExecutionPlan(final CommandContext context) { final DocumentType typez = db.getSchema() .getTypeByBucketId((db.getSchema().getBucketByName(targetBucketName.getStringValue()).getFileId())); if (typez != null) { - targetClass = new Identifier(typez.getName()); + targetType = new Identifier(typez.getName()); } else { throw new CommandSQLParsingException("Missing target"); } @@ -100,10 +103,10 @@ public InsertExecutionPlan createExecutionPlan(final CommandContext context) { // .findFirst() // .orElse(null); // } else - uniqueIndexName = null; + uniqueIndexName = null; result.chain( - new CreateEdgesStep(targetClass, targetBucketName, uniqueIndexName, new Identifier("$__ARCADEDB_CREATE_EDGE_fromV"), + new CreateEdgesStep(targetType, targetBucketName, uniqueIndexName, new Identifier("$__ARCADEDB_CREATE_EDGE_fromV"), new Identifier("$__ARCADEDB_CREATE_EDGE_toV"), unidirectional, ifNotExists, context)); handleSetFields(result, body, context); @@ -118,8 +121,8 @@ private void handleGlobalLet(final InsertExecutionPlan result, final Identifier } private void handleCheckType(final InsertExecutionPlan result, final CommandContext context) { - if (targetClass != null) { - result.chain(new CheckIsEdgeTypeStep(targetClass.getStringValue(), context)); + if (targetType != null) { + result.chain(new CheckIsEdgeTypeStep(targetType.getStringValue(), context)); } } diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateVertexExecutionPlanner.java b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateVertexExecutionPlanner.java index 088adbff95..fc869b8b73 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateVertexExecutionPlanner.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateVertexExecutionPlanner.java @@ -20,13 +20,16 @@ import com.arcadedb.exception.CommandSQLParsingException; import com.arcadedb.query.sql.parser.CreateVertexStatement; +import com.arcadedb.query.sql.parser.Identifier; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** * @author Luigi Dell'Aquila (luigi.dellaquila-(at)-gmail.com) */ public class CreateVertexExecutionPlanner extends InsertExecutionPlanner { + public CreateVertexExecutionPlanner(final CreateVertexStatement statement) { this.targetType = statement.getTargetType() == null ? null : statement.getTargetType().copy(); this.targetBucketName = statement.getTargetBucketName() == null ? null : statement.getTargetBucketName().copy(); @@ -40,6 +43,7 @@ public CreateVertexExecutionPlanner(final CreateVertexStatement statement) { @Override public InsertExecutionPlan createExecutionPlan(final CommandContext context) { + final InsertExecutionPlan prev = super.createExecutionPlan(context); final List steps = new ArrayList<>(prev.getSteps()); final InsertExecutionPlan result = new InsertExecutionPlan(context); diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/DeleteExecutionPlanner.java b/engine/src/main/java/com/arcadedb/query/sql/executor/DeleteExecutionPlanner.java index eb514d4989..51590443a3 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/DeleteExecutionPlanner.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/DeleteExecutionPlanner.java @@ -168,10 +168,12 @@ private void handleLimit(final UpdateExecutionPlan plan, final CommandContext co plan.chain(new LimitExecutionStep(limit, context)); } - private void handleTarget(final UpdateExecutionPlan result, final CommandContext context, final FromClause target, + private void handleTarget(final UpdateExecutionPlan result, + final CommandContext context, + final FromClause fromClause, final WhereClause whereClause) { final SelectStatement sourceStatement = new SelectStatement(-1); - sourceStatement.setTarget(target); + sourceStatement.setTarget(fromClause); sourceStatement.setWhereClause(whereClause); final SelectExecutionPlanner planner = new SelectExecutionPlanner(sourceStatement); result.chain( diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/InsertExecutionPlanner.java b/engine/src/main/java/com/arcadedb/query/sql/executor/InsertExecutionPlanner.java index a077b32389..6606ebc52b 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/InsertExecutionPlanner.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/InsertExecutionPlanner.java @@ -28,7 +28,8 @@ import com.arcadedb.query.sql.parser.SelectStatement; import com.arcadedb.query.sql.parser.UpdateItem; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** * Created by luigidellaquila on 08/08/16. @@ -55,6 +56,12 @@ public InsertExecutionPlanner(final InsertStatement statement) { } public InsertExecutionPlan createExecutionPlan(final CommandContext context) { + + if (targetType != null && targetType.getStringValue().startsWith("$")) { + String variable = (String) context.getVariable(targetType.getStringValue()); + targetType = new Identifier(variable); + } + final InsertExecutionPlan result = new InsertExecutionPlan(context); if (selectStatement != null) { @@ -62,7 +69,7 @@ public InsertExecutionPlan createExecutionPlan(final CommandContext context) { } else { handleCreateRecord(result, this.insertBody, context); } - handleTargetClass(result, targetType, context); + handleTargetType(result, targetType, context); handleSetFields(result, insertBody, context); if (targetBucket != null) { String name = targetBucket.getBucketName(); @@ -113,9 +120,9 @@ private void handleSetFields(final InsertExecutionPlan result, final InsertBody } } - private void handleTargetClass(final InsertExecutionPlan result, final Identifier targetClass, final CommandContext context) { - if (targetClass != null) - result.chain(new SetDocumentStepStep(targetClass, context)); + private void handleTargetType(final InsertExecutionPlan result, final Identifier targetType, final CommandContext context) { + if (targetType != null) + result.chain(new SetDocumentStepStep(targetType, context)); } private void handleCreateRecord(final InsertExecutionPlan result, final InsertBody body, final CommandContext context) { diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/SelectExecutionPlanner.java b/engine/src/main/java/com/arcadedb/query/sql/executor/SelectExecutionPlanner.java index e38bb3ad0d..7caf71d778 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/SelectExecutionPlanner.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/SelectExecutionPlanner.java @@ -72,8 +72,18 @@ import com.arcadedb.schema.Type; import com.arcadedb.utility.Pair; -import java.util.*; -import java.util.stream.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; import static com.arcadedb.schema.Property.RID_PROPERTY; import static com.arcadedb.schema.Schema.INDEX_TYPE.FULL_TEXT; @@ -432,13 +442,24 @@ protected static void optimizeQuery(final QueryPlanningInfo info, final CommandC } private static void rewriteIndexChainsAsSubqueries(QueryPlanningInfo info, CommandContext context) { - if (context == null || context.getDatabase() == null) { + if (context == null || + context.getDatabase() == null) { return; } - if (info.whereClause != null && info.target != null && info.target.getItem().getIdentifier() != null) { - String className = info.target.getItem().getIdentifier().getStringValue(); + + if (info.whereClause != null && + info.target != null && + info.target.getItem().getIdentifier() != null) { + + String typeName = info.target.getItem().getIdentifier().getStringValue(); + if (typeName.startsWith("$")) { + typeName = (String) context.getVariable(typeName); + info.target.getItem().setIdentifier(new Identifier(typeName)); + } + Schema schema = context.getDatabase().getSchema(); - DocumentType type = schema.getType(className); + DocumentType type = schema.getType(typeName); + info.whereClause.getBaseExpression().rewriteIndexChainsAsSubqueries(context, type); } } diff --git a/engine/src/main/java/com/arcadedb/query/sql/parser/CreateIndexStatement.java b/engine/src/main/java/com/arcadedb/query/sql/parser/CreateIndexStatement.java index 344b5268bb..1744efcfb4 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/parser/CreateIndexStatement.java +++ b/engine/src/main/java/com/arcadedb/query/sql/parser/CreateIndexStatement.java @@ -52,21 +52,32 @@ public CreateIndexStatement(final int id) { @Override public void validate() throws CommandSQLParsingException { - final String typeAsString = type.getStringValue(); - if (typeAsString.equalsIgnoreCase("FULL_TEXT")) + final String typeAsString = type.getStringValue().toUpperCase(); + switch (typeAsString) { + case "FULL_TEXT" -> { ; - else if (typeAsString.equalsIgnoreCase("UNIQUE")) + } + case "UNIQUE" -> { ; - else if (typeAsString.equalsIgnoreCase("NOTUNIQUE")) + } + case "NOTUNIQUE" -> { ; - else - throw new CommandSQLParsingException("Index type '" + typeAsString + "' is not supported"); + } + default -> throw new CommandSQLParsingException("Index type '" + typeAsString + "' is not supported"); + } } @Override public ResultSet executeDDL(final CommandContext context) { final Database database = context.getDatabase(); + Identifier prevName= typeName; + if (typeName.getStringValue().startsWith("$")) { + String variable = (String) context.getVariable(typeName.getStringValue()); + typeName = new Identifier(variable); + name = null; + } + if (name == null) // GENERATE THE NAME AUTOMATICALLY name = new Identifier(typeName.getStringValue() + propertyList.toString().replace(", ", ",")); @@ -114,6 +125,8 @@ else if (typeAsString.equalsIgnoreCase("UNIQUE")) { } }).create(); + typeName = prevName; + final InternalResultSet rs = new InternalResultSet(); final ResultInternal result = new ResultInternal(context.getDatabase()); result.setProperty("operation", "create index"); diff --git a/engine/src/main/java/com/arcadedb/query/sql/parser/CreatePropertyStatement.java b/engine/src/main/java/com/arcadedb/query/sql/parser/CreatePropertyStatement.java index 2a9576fbd3..206f433b6d 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/parser/CreatePropertyStatement.java +++ b/engine/src/main/java/com/arcadedb/query/sql/parser/CreatePropertyStatement.java @@ -61,6 +61,12 @@ public ResultSet executeDDL(final CommandContext context) { private void executeInternal(final CommandContext context, final ResultInternal result) { final Database db = context.getDatabase(); + Identifier prevType= typeName; + if (typeName.getStringValue().startsWith("$")) { + String variable = (String) context.getVariable(typeName.getStringValue()); + typeName = new Identifier(variable); + } + final DocumentType typez = db.getSchema().getType(typeName.getStringValue()); if (typez == null) throw new CommandExecutionException("Type '" + typeName.getStringValue() + "' not found"); @@ -81,6 +87,7 @@ private void executeInternal(final CommandContext context, final ResultInternal final Object val = attr.setOnProperty(internalProp, context); result.setProperty(attr.settingName.getStringValue(), val); } + typeName = prevType; } @Override diff --git a/engine/src/main/java/com/arcadedb/query/sql/parser/CreateTypeAbstractStatement.java b/engine/src/main/java/com/arcadedb/query/sql/parser/CreateTypeAbstractStatement.java index c0f0394c42..91d5a95cd6 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/parser/CreateTypeAbstractStatement.java +++ b/engine/src/main/java/com/arcadedb/query/sql/parser/CreateTypeAbstractStatement.java @@ -67,6 +67,12 @@ public CreateTypeAbstractStatement(final int id) { @Override public ResultSet executeDDL(final CommandContext context) { + Identifier prevName= name; + if (name.getStringValue().startsWith("$")) { + String variable = (String) context.getVariable(name.getStringValue()); + name = new Identifier(variable); + } + final Schema schema = context.getDatabase().getSchema(); if (schema.existsType(name.getStringValue())) { if (ifNotExists) { @@ -88,6 +94,7 @@ public ResultSet executeDDL(final CommandContext context) { for (final DocumentType c : superclasses) type.addSuperType(c); + name = prevName; return new InternalResultSet(result); } diff --git a/engine/src/main/java/com/arcadedb/schema/TypeIndexBuilder.java b/engine/src/main/java/com/arcadedb/schema/TypeIndexBuilder.java index 0012bbffe3..522a15e7b4 100644 --- a/engine/src/main/java/com/arcadedb/schema/TypeIndexBuilder.java +++ b/engine/src/main/java/com/arcadedb/schema/TypeIndexBuilder.java @@ -30,6 +30,7 @@ import com.arcadedb.index.IndexException; import com.arcadedb.index.IndexInternal; import com.arcadedb.index.TypeIndex; +import com.arcadedb.query.sql.parser.Identifier; import com.arcadedb.security.SecurityDatabaseUser; import java.util.*; diff --git a/engine/src/test/java/com/arcadedb/query/sql/BatchTest.java b/engine/src/test/java/com/arcadedb/query/sql/BatchTest.java index de828f451d..8d855182e6 100644 --- a/engine/src/test/java/com/arcadedb/query/sql/BatchTest.java +++ b/engine/src/test/java/com/arcadedb/query/sql/BatchTest.java @@ -20,6 +20,7 @@ import com.arcadedb.TestHelper; import com.arcadedb.exception.CommandSQLParsingException; +import com.arcadedb.graph.Edge; import com.arcadedb.query.sql.executor.Result; import com.arcadedb.query.sql.executor.ResultSet; import org.junit.jupiter.api.Test; @@ -256,6 +257,75 @@ public void testForeachResultSet() { assertThat((Integer) result.next().getProperty("value")).isEqualTo(100); } + @Test + public void testFromSingleResultReadValueFromField() { + database.command("sql", "CREATE DOCUMENT TYPE DocumentType"); + database.transaction(() -> { + database.command("sql", "INSERT INTO DocumentType set field = 'aaaa' "); + }); + + final ResultSet result = database.command("sqlscript", """ + LET row = select from DocumentType; + LET fieldValue = $row.field; + RETURN $fieldValue; + """); + assertThat(result.hasNext()).isTrue(); + assertThat(result.next().getProperty("value")).isEqualTo("aaaa"); + } + + @Test + public void testDynamicDocumentTypeName() { + database.command("sql", "CREATE DOCUMENT TYPE TheDoc"); + + database.transaction(() -> { + database.command("sqlscript", """ + LET docType = 'TheDoc'; + LET d =INSERT INTO $docType SET id = 1; + """); + }); + + assertThat(database.query("sql", "SELECT count() AS value FROM TheDoc").next().getProperty("value")).isEqualTo(1); + } + @Test + public void testDynamicGraphTypesNames() { + database.command("sql", "CREATE VERTEX TYPE V1"); + database.command("sql", "CREATE VERTEX TYPE V2"); + database.command("sql", "CREATE EDGE TYPE HasSource"); + + database.transaction(() -> { + database.command("sqlscript", """ + LET numbers = [1, 2, 3]; + LET vTypes = ['V1', 'V2']; + FOREACH ($i IN $numbers) { + FOREACH ($vType IN $vTypes) { + CREATE VERTEX $vType SET id = $i, vType = 'V2'; + } + } + """); + }); + + assertThat(database.query("sql", "SELECT count() AS value FROM V1").next().getProperty("value")).isEqualTo(3); + assertThat(database.query("sql", "SELECT count() AS value FROM V2").next().getProperty("value")).isEqualTo(3); + + final ResultSet resultSet = database.command("sqlscript", """ + BEGIN; + LET sources = SELECT FROM V1 WHERE id = '1'; + LET source = $sources[0]; + LET type = $source.vType; + LET target = SELECT FROM $type WHERE id = '3'; + LET edgeType = 'HasSource'; + LET e = CREATE EDGE $edgeType FROM $target TO $source IF NOT EXISTS ; + COMMIT; + RETURN $e; + """); + + assertThat(resultSet.hasNext()).isTrue(); + Edge edge = resultSet.next().getEdge().get(); + assertThat(edge.getInVertex().getInteger("id")).isEqualTo(1); + assertThat(edge.getOutVertex().getInteger("id")).isEqualTo(3); + + } + @Test public void testUsingReservedVariableNames() { try { diff --git a/engine/src/test/java/com/arcadedb/query/sql/DDLTest.java b/engine/src/test/java/com/arcadedb/query/sql/DDLTest.java index a41c8a44ea..eefa29e203 100644 --- a/engine/src/test/java/com/arcadedb/query/sql/DDLTest.java +++ b/engine/src/test/java/com/arcadedb/query/sql/DDLTest.java @@ -19,6 +19,7 @@ package com.arcadedb.query.sql; import com.arcadedb.TestHelper; +import com.arcadedb.schema.Schema; import org.junit.jupiter.api.Test; import java.util.stream.IntStream; @@ -36,6 +37,56 @@ protected void beginTest() { } + @Test + void testDynamicSchemaCreation() { + database.command("sqlscript", """ + BEGIN; + + LET vTypes = ['V1', 'V2', 'V3']; + FOREACH ($vType IN $vTypes) { + CREATE VERTEX TYPE $vType EXTENDS V; + } + + LET eTypes = ['E1', 'E2', 'E3']; + FOREACH ($eType IN $eTypes) { + CREATE EDGE TYPE $eType EXTENDS E; + } + + LET dTypes = ['D1', 'D2', 'D3']; + FOREACH ($dType IN $dTypes) { + CREATE DOCUMENT TYPE $dType ; + } + + LET types = ['V1', 'V2', 'V3','E1', 'E2', 'E3','D1', 'D2', 'D3']; + FOREACH ($type IN $types) { + CREATE PROPERTY $type.id STRING; + CREATE INDEX ON $type (id) UNIQUE NULL_STRATEGY SKIP; + } + COMMIT; + """); + + Schema schema = database.getSchema(); + assertThat(schema.existsType("V1")).isTrue(); + assertThat(schema.existsIndex("V1[id]")).isTrue(); + assertThat(schema.existsType("V2")).isTrue(); + assertThat(schema.existsIndex("V2[id]")).isTrue(); + assertThat(schema.existsType("V3")).isTrue(); + assertThat(schema.existsIndex("V3[id]")).isTrue(); + assertThat(schema.existsType("D1")).isTrue(); + assertThat(schema.existsIndex("D1[id]")).isTrue(); + assertThat(schema.existsType("D2")).isTrue(); + assertThat(schema.existsIndex("D2[id]")).isTrue(); + assertThat(schema.existsType("D3")).isTrue(); + assertThat(schema.existsIndex("D3[id]")).isTrue(); + assertThat(schema.existsType("E1")).isTrue(); + assertThat(schema.existsIndex("E1[id]")).isTrue(); + assertThat(schema.existsType("E2")).isTrue(); + assertThat(schema.existsIndex("E2[id]")).isTrue(); + assertThat(schema.existsType("E3")).isTrue(); + assertThat(schema.existsIndex("E3[id]")).isTrue(); + + } + @Test void testGraphWithSql() { diff --git a/engine/src/test/java/com/arcadedb/query/sql/parser/BatchScriptTest.java b/engine/src/test/java/com/arcadedb/query/sql/parser/BatchScriptTest.java index 0a91e2f843..d4a8bc5355 100755 --- a/engine/src/test/java/com/arcadedb/query/sql/parser/BatchScriptTest.java +++ b/engine/src/test/java/com/arcadedb/query/sql/parser/BatchScriptTest.java @@ -35,49 +35,52 @@ public void testPlain() { checkRightSyntax("begin;\nselect from foo;/*foo bar*/ return bar;"); checkRightSyntax("/*foo bar*/ begin;\nselect from foo;return bar;/*foo bar*/ "); - String s = ""// - + "begin;"// - + "let $a = select from foo let a = 13 where bar = 'baz';"// - + "let $b = insert into foo set name = 'baz';"// - + "let $c = update v set name = 'lkajsd';"// - + "if($c < $a){"// - + " update v set surname = baz;"// - + " if($c < $b){"// - + " return 0;"// - + " }"// - + "}"// - + "return 1;"; + String s = """ + begin; + let $a = select from foo let a = 13 where bar = 'baz'; + let $b = insert into foo set name = 'baz'; + let $c = update v set name = 'lkajsd'; + if($c < $a){ + update v set surname = baz; + if($c < $b){ + return 0; + } + } + return 1;"""; checkRightSyntax(s); - s = ""// - + "begin;\n"// - + "let $a = select from foo let a = 13 where bar = 'baz';\n"// - + "let $b = insert into foo set name = 'baz';\n"// - + "let $c = update v set name = 'lkajsd';\n"// - + "if($c < $a){\n"// - + " update v set surname = baz;\n"// - + " if($c < $b){\n"// - + " return 0;\n"// - + " }\n"// - + "}\n"// - + "return 1;\n"; + s = """ + + begin; + let $a = select from foo let a = 13 where bar = 'baz'; + let $b = insert into foo set name = 'baz'; + let $c = update v set name = 'lkajsd'; + if($c < $a){ + update v set surname = baz; + if($c < $b){ + return 0; + } + } + return 1; + """; checkRightSyntax(s); - s = ""// - + "begin;\n"// - + "let $a = select from foo let a = 13 where bar = 'baz';\n"// - + "let $b = insert into foo set name = 'baz';\n"// - + "let $c = update v set \n"// - + "/** foo bar */\n"// - + "name = 'lkajsd';\n"// - + "if($c < $a){\n"// - + " update v set surname = baz;\n"// - + " if($c < $b){\n"// - + " return 0;\n"// - + " }\n"// - + "}\n"// - + "return 1;\n"; + s = """ + begin; + let $a = select from foo let a = 13 where bar = 'baz'; + let $b = insert into foo set name = 'baz'; + let $c = update v set + /** foo bar */ + name = 'lkajsd'; + if($c < $a){ + update v set surname = baz; + if($c < $b){ + return 0; + } + } + return 1; + """; checkRightSyntax(s); s = "let a = select 1 as result;let b = select 2 as result;return [$a,$b];";