diff --git a/engine/src/main/grammar/SQLGrammar.jjt b/engine/src/main/grammar/SQLGrammar.jjt index 351b132f51..de8ee102ea 100644 --- a/engine/src/main/grammar/SQLGrammar.jjt +++ b/engine/src/main/grammar/SQLGrammar.jjt @@ -4557,7 +4557,7 @@ ExportDatabaseStatement ExportDatabaseStatement(): ( jjtThis.url = Url() - [ jjtThis.format = PString() ] + [ jjtThis.format = Identifier() ] [ ( { jjtThis.overwrite = BooleanExpression.TRUE;} | { jjtThis.overwrite = BooleanExpression.FALSE;} ) ] ) {return jjtThis; } diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/FetchFromSchemaTypesStep.java b/engine/src/main/java/com/arcadedb/query/sql/executor/FetchFromSchemaTypesStep.java index 25fcf9ad63..614b0b69b9 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/FetchFromSchemaTypesStep.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/FetchFromSchemaTypesStep.java @@ -91,7 +91,7 @@ else if (type.getType() == Edge.RECORD_TYPE) propRes.setProperty("name", typeIndexInternal.getName()); propRes.setProperty("typeName", typeIndexInternal.getTypeName()); propRes.setProperty("type", typeIndexInternal.getType()); - propRes.setProperty("properties", Arrays.asList(typeIndexInternal.getPropertyNames())); + propRes.setProperty("properties", typeIndexInternal.getPropertyNames()); propRes.setProperty("automatic", typeIndexInternal.isAutomatic()); propRes.setProperty("unique", typeIndexInternal.isUnique()); return propRes; diff --git a/engine/src/main/java/com/arcadedb/query/sql/parser/DropPropertyStatement.java b/engine/src/main/java/com/arcadedb/query/sql/parser/DropPropertyStatement.java index d4b0d698a7..544c33ed25 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/parser/DropPropertyStatement.java +++ b/engine/src/main/java/com/arcadedb/query/sql/parser/DropPropertyStatement.java @@ -17,8 +17,16 @@ /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=O,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_USERTYPE_VISIBILITY_PUBLIC=true */ package com.arcadedb.query.sql.parser; +import com.arcadedb.database.Database; +import com.arcadedb.exception.CommandExecutionException; +import com.arcadedb.index.Index; +import com.arcadedb.index.IndexInternal; +import com.arcadedb.index.TypeIndex; import com.arcadedb.query.sql.executor.CommandContext; +import com.arcadedb.query.sql.executor.InternalResultSet; +import com.arcadedb.query.sql.executor.ResultInternal; import com.arcadedb.query.sql.executor.ResultSet; +import com.arcadedb.schema.DocumentType; import java.util.*; @@ -27,7 +35,7 @@ public class DropPropertyStatement extends DDLStatement { protected Identifier typeName; protected Identifier propertyName; protected boolean ifExists = false; - protected boolean force = false; + protected boolean force = false; public DropPropertyStatement(int id) { super(id); @@ -37,85 +45,75 @@ public DropPropertyStatement(SqlParser p, int id) { super(p, id); } - @Override public ResultSet executeDDL(CommandContext ctx) { - - throw new UnsupportedOperationException(); -// InternalResultSet rs = new InternalResultSet(); -// final Database database = ctx.getDatabase(); -// final OClassImpl sourceClass = (OClassImpl) database.getMetadata().getSchema().getClass(className.getStringValue()); -// if (sourceClass == null) -// throw new PCommandExecutionException("Source class '" + className + "' not found"); -// -// if (sourceClass.getProperty(propertyName.getStringValue()) == null) { -// if(ifExists){ -// return rs; -// } -// throw new PCommandExecutionException("Property '" + propertyName + "' not found on class " + className); -// } -// final List> indexes = relatedIndexes(propertyName.getStringValue(), database); -// if (!indexes.isEmpty()) { -// if (force) { -// for (final OIndex index : indexes) { -// index.delete(); -// OResultInternal result = new OResultInternal(); -// result.setProperty("operation", "cascade drop index"); -// result.setProperty("indexName", index.getName()); -// rs.add(result); -// } -// } else { -// final StringBuilder indexNames = new StringBuilder(); -// -// boolean first = true; -// for (final OIndex index : sourceClass.getClassInvolvedIndexes(propertyName.getStringValue())) { -// if (!first) { -// indexNames.append(", "); -// } else { -// first = false; -// } -// indexNames.append(index.getName()); -// } -// -// throw new PCommandExecutionException("Property used in indexes (" + indexNames.toString() -// + "). Please drop these indexes before removing property or use FORCE parameter."); -// } -// } -// -// // REMOVE THE PROPERTY -// sourceClass.dropProperty(propertyName.getStringValue()); -// -// OResultInternal result = new OResultInternal(); -// result.setProperty("operation", "drop property"); -// result.setProperty("typeName", className.getStringValue()); -// result.setProperty("propertyname", propertyName.getStringValue()); -// rs.add(result); -// return rs; + @Override + public ResultSet executeDDL(CommandContext ctx) { + InternalResultSet rs = new InternalResultSet(); + final Database database = ctx.getDatabase(); + final DocumentType sourceClass = database.getSchema().getType(typeName.getStringValue()); + if (sourceClass == null) + throw new CommandExecutionException("Source class '" + typeName + "' not found"); + + if (sourceClass.getProperty(propertyName.getStringValue()) == null) { + if (ifExists) { + return rs; + } + throw new CommandExecutionException("Property '" + propertyName + "' not found on class " + typeName); + } + final List indexes = sourceClass.getIndexesByProperty(propertyName.getStringValue()); + if (!indexes.isEmpty()) { + if (force) { + for (final Index index : indexes) { + ((IndexInternal) index).drop(); + ResultInternal result = new ResultInternal(); + result.setProperty("operation", "cascade drop index"); + result.setProperty("indexName", index.getName()); + rs.add(result); + } + } else { + final StringBuilder indexNames = new StringBuilder(); + + boolean first = true; + for (final TypeIndex index : indexes) { + if (!first) { + indexNames.append(", "); + } else { + first = false; + } + indexNames.append(index.getName()); + } + + throw new CommandExecutionException( + "Property used in indexes (" + indexNames + "). Please drop these indexes before removing property or use FORCE parameter."); + } + } + + // REMOVE THE PROPERTY + sourceClass.dropProperty(propertyName.getStringValue()); + + ResultInternal result = new ResultInternal(); + result.setProperty("operation", "drop property"); + result.setProperty("typeName", typeName.getStringValue()); + result.setProperty("propertyName", propertyName.getStringValue()); + rs.add(result); + return rs; } -// private List> relatedIndexes(final String fieldName, ODatabase database) { -// final List> result = new ArrayList>(); -// for (final OIndex oIndex : database.getMetadata().getIndexManager().getClassIndexes(className.getStringValue())) { -// if (OCollections.indexOf(oIndex.getDefinition().getFields(), fieldName, new OCaseInsentiveComparator()) > -1) { -// result.add(oIndex); -// } -// } -// -// return result; -// } - - @Override public void toString(Map params, StringBuilder builder) { + @Override + public void toString(Map params, StringBuilder builder) { builder.append("DROP PROPERTY "); typeName.toString(params, builder); builder.append("."); propertyName.toString(params, builder); - if(ifExists){ + if (ifExists) { builder.append(" IF EXISTS"); } - if(force){ + if (force) { builder.append(" FORCE"); } } - @Override public DropPropertyStatement copy() { + @Override + public DropPropertyStatement copy() { DropPropertyStatement result = new DropPropertyStatement(-1); result.typeName = typeName == null ? null : typeName.copy(); result.propertyName = propertyName == null ? null : propertyName.copy(); @@ -124,7 +122,8 @@ public DropPropertyStatement(SqlParser p, int id) { return result; } - @Override public boolean equals(Object o) { + @Override + public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) @@ -134,7 +133,7 @@ public DropPropertyStatement(SqlParser p, int id) { if (force != that.force) return false; - if(ifExists!=that.ifExists){ + if (ifExists != that.ifExists) { return false; } if (typeName != null ? !typeName.equals(that.typeName) : that.typeName != null) @@ -142,7 +141,8 @@ public DropPropertyStatement(SqlParser p, int id) { return propertyName != null ? propertyName.equals(that.propertyName) : that.propertyName == null; } - @Override public int hashCode() { + @Override + public int hashCode() { int result = typeName != null ? typeName.hashCode() : 0; result = 31 * result + (propertyName != null ? propertyName.hashCode() : 0); result = 31 * result + (force ? 1 : 0); diff --git a/engine/src/main/java/com/arcadedb/query/sql/parser/ExportDatabaseStatement.java b/engine/src/main/java/com/arcadedb/query/sql/parser/ExportDatabaseStatement.java index dc4fdee850..3fe9ab03d3 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/parser/ExportDatabaseStatement.java +++ b/engine/src/main/java/com/arcadedb/query/sql/parser/ExportDatabaseStatement.java @@ -30,7 +30,7 @@ public class ExportDatabaseStatement extends SimpleExecStatement { protected Url url; - protected String format = "jsonl"; + protected Identifier format = new Identifier("jsonl"); protected BooleanExpression overwrite = BooleanExpression.FALSE; public ExportDatabaseStatement(int id) { @@ -58,7 +58,7 @@ public ResultSet executeSimple(CommandContext ctx) { final Class clazz = Class.forName("com.arcadedb.integration.exporter.Exporter"); final Object exporter = clazz.getConstructor(Database.class, String.class).newInstance(ctx.getDatabase(), fileName); - String formatExport = format; + String formatExport = format.getStringValue(); if ((formatExport.startsWith("'") && formatExport.endsWith("'")) ||// formatExport.startsWith("\"") && formatExport.endsWith("\"")) { formatExport = formatExport.substring(1, formatExport.length() - 1); diff --git a/engine/src/main/java/com/arcadedb/query/sql/parser/SqlParser.java b/engine/src/main/java/com/arcadedb/query/sql/parser/SqlParser.java index efe1e4092a..fb7e12c88d 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/parser/SqlParser.java +++ b/engine/src/main/java/com/arcadedb/query/sql/parser/SqlParser.java @@ -17672,7 +17672,7 @@ final public ExportDatabaseStatement ExportDatabaseStatement() throws ParseExcep switch (jj_ntk == -1 ? jj_ntk_f() : jj_ntk) { case FORMAT:{ jj_consume_token(FORMAT); - jjtn000.format = PString(); + jjtn000.format = Identifier(); break; } default: @@ -21278,7 +21278,7 @@ private boolean jj_3_88() private boolean jj_3R_706() { if (jj_scan_token(FORMAT)) return true; - if (jj_3R_490()) return true; + if (jj_3R_128()) return true; return false; } diff --git a/engine/src/main/java/com/arcadedb/schema/DocumentType.java b/engine/src/main/java/com/arcadedb/schema/DocumentType.java index 0adb534916..fb56deb217 100644 --- a/engine/src/main/java/com/arcadedb/schema/DocumentType.java +++ b/engine/src/main/java/com/arcadedb/schema/DocumentType.java @@ -310,6 +310,21 @@ public List getPolymorphicBucketIndexByBucketId(final int bucketId) { return result; } + public List getIndexesByProperty(final String property) { + final List result = new ArrayList<>(); + + for (Map.Entry, TypeIndex> entry : indexesByProperties.entrySet()) { + for (String prop : entry.getKey()) { + if (property.equals(prop)) { + result.add(entry.getValue()); + break; + } + } + } + + return result; + } + public TypeIndex getPolymorphicIndexByProperties(final String... properties) { return getPolymorphicIndexByProperties(Arrays.asList(properties)); } diff --git a/server/src/main/resources/static/js/studio-database.js b/server/src/main/resources/static/js/studio-database.js index b288a19365..eef1531a4c 100644 --- a/server/src/main/resources/static/js/studio-database.js +++ b/server/src/main/resources/static/js/studio-database.js @@ -41,6 +41,8 @@ function editorFocus(){ } function updateDatabases( callback ){ + let selected = $("#inputDatabase").val(); + jQuery.ajax({ type: "GET", url: "/api/v1/databases", @@ -56,6 +58,9 @@ function updateDatabases( callback ){ } $("#inputDatabase").html(databases); + if( selected != null && selected != "" ) + $("#inputDatabase").val(selected); + $("#currentDatabase").html( $("#inputDatabase").val() ); $("#user").html(data.user); @@ -149,7 +154,6 @@ function dropDatabase(){ }); } - function backupDatabase(){ let database = escapeHtml( $("#inputDatabase").val().trim() ); if( database == "" ){ @@ -181,6 +185,69 @@ function backupDatabase(){ }); } +function dropProperty(type, property){ + let database = escapeHtml( $("#inputDatabase").val().trim() ); + if( database == "" ){ + globalNotify( "Error", "Database not selected", "danger"); + return; + } + + globalConfirm("Drop property", "Are you sure you want to drop the property '"+property+"' on type '"+type+"'?
WARNING: The operation cannot be undone.", "warning", function(){ + jQuery.ajax({ + type: "POST", + url: "/api/v1/command/" + database, + data: JSON.stringify( + { + language: "sql", + command: "drop property `" + type + "`.`" + property + "`", + serializer: "record" + } + ), + beforeSend: function (xhr){ + xhr.setRequestHeader('Authorization', globalCredentials); + } + }) + .done(function(data){ + updateDatabases(); + }) + .fail(function( jqXHR, textStatus, errorThrown ){ + globalNotifyError( jqXHR.responseText ); + }); + }); +} + + +function dropIndex(indexName){ + let database = escapeHtml( $("#inputDatabase").val().trim() ); + if( database == "" ){ + globalNotify( "Error", "Database not selected", "danger"); + return; + } + + globalConfirm("Drop index", "Are you sure you want to drop the index '"+indexName+"'?
WARNING: The operation cannot be undone.", "warning", function(){ + jQuery.ajax({ + type: "POST", + url: "/api/v1/command/" + database, + data: JSON.stringify( + { + language: "sql", + command: "drop index `" + indexName + "`", + serializer: "record" + } + ), + beforeSend: function (xhr){ + xhr.setRequestHeader('Authorization', globalCredentials); + } + }) + .done(function(data){ + updateDatabases(); + }) + .fail(function( jqXHR, textStatus, errorThrown ){ + globalNotifyError( jqXHR.responseText ); + }); + }); +} + function executeCommand(language, query){ globalResultset = null; @@ -324,7 +391,6 @@ function displaySchema(){ } }) .done(function(data){ - let tabVHtml = ""; let tabEHtml = ""; let tabDHtml = ""; @@ -350,19 +416,25 @@ function displaySchema(){ panelHtml += ""; } - panelHtml += "

Properties
"; - panelHtml += ""; + panelHtml += "

Properties
"; + //panelHtml += ""; + panelHtml += "
NameTypeIndexed
"; + panelHtml += ""; panelHtml += ""; for( k in row.properties ) { let property = row.properties[k]; panelHtml += ""; + let actionHtml = ""; + let propIndexes = []; if( row.indexes != null && row.indexes.length > 0 ) { - propIndexes.push( row.indexes.filter(i => i.properties.includes( property.name )).map(i => (i.unique ? "" : "Not ") + "Unique, Type(" + i.type + ")" + ( i.properties.length > 1 ? ", on multi properties " + i.properties : "" )) ); + propIndexes.push( row.indexes.filter(i => i.properties.includes( property.name )).map(i => (i.name + " " + i.unique ? "" : "Not ") + "Unique, Type(" + i.type + ")" + ( i.properties.length > 1 ? ", on multi properties " + i.properties : "" )) ); + actionHtml += row.indexes.filter(i => i.properties.includes( property.name )).map(i => ( "" ) ); } panelHtml += ""; + panelHtml += ""; } panelHtml += "
NameTypeIndexedActions
"+property.name+"" + property.type + "" + ( propIndexes.length > 0 ? propIndexes : "" ) + "" + actionHtml + "
";