From f1fa7837ec651835791d89d0cd6e22970dad20b9 Mon Sep 17 00:00:00 2001 From: delchev Date: Thu, 16 Jan 2025 13:21:59 +0200 Subject: [PATCH] Fixed: #4569 - Import SQL script in Schema --- .../export/endpoint/DataImportEndpoint.java | 2 +- .../data/export/endpoint/DataSQLEndpoint.java | 110 ++++++++++++++++++ .../export/service/DataImportService.java | 15 +++ .../ide-database/explorer/explorer.js | 16 ++- .../dirigible/ide-import/js/import.js | 10 +- .../dirigible/ide-result/js/result.js | 16 ++- .../dirigible/database/sql/ISqlDialect.java | 9 ++ .../sql/dialects/DefaultSqlDialect.java | 58 +++++++++ 8 files changed, 229 insertions(+), 7 deletions(-) create mode 100644 components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataSQLEndpoint.java diff --git a/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataImportEndpoint.java b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataImportEndpoint.java index 2b0eec49bae..16967ade602 100644 --- a/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataImportEndpoint.java +++ b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataImportEndpoint.java @@ -82,7 +82,7 @@ public ResponseEntity importDataInTable(@Validated @PathVariable("datasource" if (logger.isErrorEnabled()) { logger.error(e.getMessage(), e); } - throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Project upload failed: " + e.getMessage()); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Data upload failed: " + e.getMessage()); } } diff --git a/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataSQLEndpoint.java b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataSQLEndpoint.java new file mode 100644 index 00000000000..2b940b5ea6c --- /dev/null +++ b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/endpoint/DataSQLEndpoint.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 Eclipse Dirigible contributors + * + * All rights reserved. This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.dirigible.components.data.export.endpoint; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.dirigible.components.base.endpoint.BaseEndpoint; +import org.eclipse.dirigible.components.data.export.service.DataImportService; +import org.eclipse.dirigible.components.data.management.service.DatabaseMetadataService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; + +/** + * Front facing REST service serving sql processing. + */ +@RestController +@RequestMapping(BaseEndpoint.PREFIX_ENDPOINT_DATA + "sql") +public class DataSQLEndpoint { + + /** The constant logger. */ + private static final Logger logger = LoggerFactory.getLogger(DataSQLEndpoint.class); + + /** + * The database metadata service. + */ + private final DatabaseMetadataService databaseMetadataService; + + /** + * The database metadata service. + */ + private final DataImportService dataImportService; + + /** + * Instantiates a new data export endpoint. + * + * @param databaseMetadataService the database metadata service + * @param dataImportService the data import service + */ + public DataSQLEndpoint(DatabaseMetadataService databaseMetadataService, DataImportService dataImportService) { + this.databaseMetadataService = databaseMetadataService; + this.dataImportService = dataImportService; + } + + + /** + * Process SQL in schema. + * + * @param datasource the datasource + * @param schema the schema + * @param file the file + * @return the response entity + * @throws Exception the exception + */ + @PostMapping(value = "/{datasource}/{schema}", consumes = "multipart/form-data", produces = "application/json") + public ResponseEntity importDataInTable(@Validated @PathVariable("datasource") String datasource, + @Validated @PathVariable("schema") String schema, @Validated @RequestParam("file") MultipartFile file) throws Exception { + try { + return processSQL(datasource, schema, file); + } catch (IOException e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "SQL upload failed: " + e.getMessage()); + } + } + + + /** + * Process SQL. + * + * @param datasource the datasource + * @param schema the schema + * @param file the file + * @return the response entity + * @throws Exception the exception + */ + private ResponseEntity processSQL(String datasource, String schema, MultipartFile file) throws Exception { + + if (!databaseMetadataService.existsDataSourceMetadata(datasource)) { + String error = format("Datasource {0} does not exist.", datasource); + throw new ResponseStatusException(HttpStatus.NOT_FOUND, error); + } + + InputStream is = file.getInputStream(); + dataImportService.processSQL(datasource, schema, is); + return ResponseEntity.ok() + .build(); + } + +} diff --git a/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java index 677ea5258f2..3bfd25b9e57 100644 --- a/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java +++ b/components/data/data-import/src/main/java/org/eclipse/dirigible/components/data/export/service/DataImportService.java @@ -85,4 +85,19 @@ public void importData(String datasource, String schema, String table, InputStre importData(datasource, schema, table, true, true, ",", "\"", null, false, is); } + /** + * Process SQL. + * + * @param datasource the datasource + * @param schema the schema + * @param is the is + */ + public void processSQL(String datasource, String schema, InputStream is) throws Exception { + DirigibleDataSource dataSource = datasourceManager.getDataSource(datasource); + try (Connection connection = dataSource.getConnection()) { + ISqlDialect dialect = SqlDialectFactory.getDialect(dataSource); + dialect.processSQL(connection, schema, is); + } + } + } diff --git a/components/ide/ide-ui-database/src/main/resources/META-INF/dirigible/ide-database/explorer/explorer.js b/components/ide/ide-ui-database/src/main/resources/META-INF/dirigible/ide-database/explorer/explorer.js index 3e58904efb4..43029da5d60 100644 --- a/components/ide/ide-ui-database/src/main/resources/META-INF/dirigible/ide-database/explorer/explorer.js +++ b/components/ide/ide-ui-database/src/main/resources/META-INF/dirigible/ide-database/explorer/explorer.js @@ -343,7 +343,7 @@ database.controller('DatabaseController', function ($scope, $http, messageHub) { }.bind(this) }; } - + // Export metadata ctxmenu.exportMetadata = { "separator_before": true, @@ -414,8 +414,18 @@ database.controller('DatabaseController', function ($scope, $http, messageHub) { // Schema related actions if (node.original.kind === 'schema') { - ctxmenu.exportData = { + ctxmenu.importScript = { "separator_before": false, + "label": "Import SQL", + "action": function (data) { + let tree = $.jstree.reference(data.reference); + let node = tree.get_node(data.reference); + let sqlCommand = node.original.text; + messageHub.postMessage('database.data.import.sql', sqlCommand); + }.bind(this) + }; + ctxmenu.exportData = { + "separator_before": true, "label": "Export Data", "action": function (data) { let tree = $.jstree.reference(data.reference); @@ -485,7 +495,7 @@ database.controller('DatabaseController', function ($scope, $http, messageHub) { }.bind(this) }; } - + // Collection related actions if (node.original.kind === 'table' && node.original.type === 'collection') { ctxmenu.showContents = { diff --git a/components/ide/ide-ui-import/src/main/resources/META-INF/dirigible/ide-import/js/import.js b/components/ide/ide-ui-import/src/main/resources/META-INF/dirigible/ide-import/js/import.js index a8c7fd4b82e..900cd617a5a 100644 --- a/components/ide/ide-ui-import/src/main/resources/META-INF/dirigible/ide-import/js/import.js +++ b/components/ide/ide-ui-import/src/main/resources/META-INF/dirigible/ide-import/js/import.js @@ -69,6 +69,12 @@ importView.controller('ImportViewController', [ $scope.dropAreaTitle = 'Import data files'; $scope.dropAreaSubtitle = 'Drop file(s) here, or use the "+" button.'; $scope.dropAreaMore = `Files will be imported in "${params.table}"`; + } if (params.importType === 'sql') { + $scope.inputAccept = 'sql'; + $scope.importType = params.importType; + $scope.dropAreaTitle = 'Import SQL files'; + $scope.dropAreaSubtitle = 'Drop file(s) here, or use the "+" button.'; + $scope.dropAreaMore = `Files will be imported in "${params.schema}"`; } else { $scope.dropAreaTitle = 'Import files from zip'; $scope.dropAreaMore = `Files will be extracted in "${params.uploadPath}"`; @@ -85,7 +91,7 @@ importView.controller('ImportViewController', [ name: 'customFilter', fn: function (item /*{File|FileLikeObject}*/, options) { let type = item.type.slice(item.type.lastIndexOf('/') + 1); - if ($scope.importType !== 'file' && $scope.importType !== 'data') + if ($scope.importType !== 'file' && $scope.importType !== 'data' && $scope.importType !== 'sql') if (type != 'zip' && type != 'x-zip' && type != 'x-zip-compressed') { return false; } @@ -102,7 +108,7 @@ importView.controller('ImportViewController', [ 'Dirigible-Editor': 'Editor' }; item.url = new UriBuilder().path(transportApi.getFileImportUrl().split('/')).path($scope.selectedWorkspace.name).path($scope.uploadPath.split('/')).path(item.name).build(); - } else if ($scope.inDialog && $scope.importType === 'data') { + } else if ($scope.inDialog && ($scope.importType === 'data' || $scope.importType === 'sql')) { item.headers = { 'Dirigible-Editor': 'Editor' }; diff --git a/components/ide/ide-ui-result/src/main/resources/META-INF/dirigible/ide-result/js/result.js b/components/ide/ide-ui-result/src/main/resources/META-INF/dirigible/ide-result/js/result.js index 686bbd2d705..246b6d8ea48 100644 --- a/components/ide/ide-ui-result/src/main/resources/META-INF/dirigible/ide-result/js/result.js +++ b/components/ide/ide-ui-result/src/main/resources/META-INF/dirigible/ide-result/js/result.js @@ -436,7 +436,21 @@ resultView.controller('DatabaseResultController', ['$scope', '$http', 'messageHu importType: 'data', uploadPath: url, workspace: "", - table: $scope.datasource + " -> " + artifact[0] + " -> " + artifact[1], + table: $scope.datasource + "." + artifact[0] + "." + artifact[1], + } + ); + }, true); + + messageHub.onDidReceiveMessage("database.data.import.sql", function (command) { + let artifact = command.data.split('.'); + let url = "/services/data/sql/" + $scope.datasource + "/" + artifact[0]; + messageHub.showDialogWindow( + "import", + { + importType: 'sql', + uploadPath: url, + workspace: "", + schema: $scope.datasource + "." + artifact[0], } ); }, true); diff --git a/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialect.java b/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialect.java index 01610984607..a113d299448 100644 --- a/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialect.java +++ b/modules/database/database-sql/src/main/java/org/eclipse/dirigible/database/sql/ISqlDialect.java @@ -275,4 +275,13 @@ public interface ISqlDialect