diff --git a/.gitignore b/.gitignore index a11aeca..4af70df 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ target/ .idea/ run-designite.bat output-designite/ +usr/ +src/main/java/com/dal/distributed/constant/VMConstants.java diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0ca4215..d99c94f 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,11 @@ commons-codec 1.15 + + com.jcraft + jsch + 0.1.55 + diff --git a/src/main/java/com/dal/distributed/analytics/Analytics.java b/src/main/java/com/dal/distributed/analytics/Analytics.java new file mode 100644 index 0000000..e9e2bc6 --- /dev/null +++ b/src/main/java/com/dal/distributed/analytics/Analytics.java @@ -0,0 +1,205 @@ +package com.dal.distributed.analytics; + +import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.QueryRegex; +import com.dal.distributed.constant.VMConstants; +import com.dal.distributed.logger.Logger; +import com.dal.distributed.queryImpl.model.QueryLog; +import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; + +import java.io.File; +import java.util.*; +import java.util.regex.Matcher; + +public class Analytics { + Logger logger = Logger.instance(); + + public void analyze(Scanner scanner) throws Exception { + while (true) { + logger.info("\nPlease choose any one of the following options:"); + logger.info("1. Analytics"); + logger.info("2. Exit"); + String input = scanner.nextLine(); + + switch (input) { + case "1": + logger.info("Please input a query for the selected option: "); + String query = scanner.nextLine(); + analyzeQueries(query); + break; + case "2": + break; + default: + logger.error("Please choose a valid input!"); + } + if ("2".equals(input)) + break; + } + } + + private void analyzeQueries(String queryString) throws Exception { + String[] query = queryString.split(" "); + if (query[1].contains("queries")) { + countQueries(queryString); + } else if (query[1].contains("update")) { + countOperationType("UPDATE"); + } else if (query[1].contains("insert")) { + countOperationType("INSERT"); + } else if (query[1].contains("delete")) { + countOperationType("DELETE"); + } else if (query[1].contains("select")) { + countOperationType("SELECT"); + } else if (query[1].contains("create")) { + countOperationType("CREATE TABLE"); + } + } + + private void countOperationType(String operation) throws Exception { + List queryLogs = getQueryLogFileInformation(); + HashMap tableMap = new HashMap<>(); + for (QueryLog queryLog : queryLogs) { + if (queryLog.getOperation().equalsIgnoreCase(operation)) { + if (null != tableMap.get(queryLog.getTableName())) { + tableMap.put(queryLog.getTableName(), tableMap.get(queryLog.getTableName()) + 1); + } else { + tableMap.put(queryLog.getTableName(), 1); + } + } + } + + Boolean flag = Boolean.FALSE; + for (Map.Entry user : tableMap.entrySet()) { + logger.info("Total " + user.getValue() + " " + operation + " operation(s) are performed on " + user.getKey()); + flag = Boolean.TRUE; + } + + if (!flag) { + logger.info("No " + operation + " queries found."); + } + } + + private void countQueries(String queryString) throws Exception { + String [] query = queryString.split(" "); + Matcher matcher = QueryRegex.countQueriesAnalytics.matcher(queryString); + if (matcher.find()) { + String userName = matcher.group(1).trim(); + String databaseName = matcher.group(2).replace(";",""); + countQueriesForUser(userName, databaseName); + } else if (query.length == 2) { + countTotalQueries(); + } + } + + private void countTotalQueries() throws Exception { + List queryLogs = getQueryLogFileInformation(); + HashMap userMap = new HashMap<>(); + for (QueryLog queryLog : queryLogs) { + String key = queryLog.getSubmittedBy() + "|" + queryLog.getDatabaseName(); + if (null != userMap.get(key)) { + userMap.put(key, userMap.get(key) + 1); + } else { + userMap.put(key, 1); + } + } + + Boolean flag = Boolean.FALSE; + for (Map.Entry user : userMap.entrySet()) { + String[] details = user.getKey().split("\\|"); + if (null != details[1] && !details[1].equalsIgnoreCase("null")) { + logger.info("User " + details[0] + " submitted " + user.getValue() + " query(s) for " + details[1]); + } + flag = Boolean.TRUE; + } + + if (!flag) { + logger.info("No queries found"); + } + } + + private void countQueriesForUser(String userName, String databaseName) throws Exception { + List queryLogs = getQueryLogFileInformation(); + HashMap userMap = new HashMap<>(); + for (QueryLog queryLog : queryLogs) { + if (queryLog.getDatabaseName().equalsIgnoreCase(databaseName) && + queryLog.getSubmittedBy().equalsIgnoreCase(userName)) { + if (null != userMap.get(queryLog.getSubmittedBy())) { + userMap.put(queryLog.getSubmittedBy(), userMap.get(queryLog.getSubmittedBy()) + 1); + } else { + userMap.put(queryLog.getSubmittedBy(), 1); + } + } + } + + Boolean flag = Boolean.FALSE; + for (Map.Entry user : userMap.entrySet()) { + logger.info("User " + user.getKey() + " submitted " + user.getValue() + " query(s) for " + databaseName); + flag = Boolean.TRUE; + } + + if (!flag) { + logger.info("No queries found"); + } + } + + private List getQueryLogFileInformation() throws Exception { + String queryLogFile = FileOperations.readFileContent( + new File(DataConstants.LOGS_FILE_LOCATION + DataConstants.QUERY_LOG_FILE_NAME)); + + String remoteQueryLogFile = RemoteVmUtils.readFileContent(VMConstants.projectPath + DataConstants.LOGS_FILE_LOCATION + DataConstants.QUERY_LOG_FILE_NAME); + StringBuilder sb = new StringBuilder() + .append(queryLogFile) + .append(remoteQueryLogFile); + + Matcher matcher = QueryRegex.valueBetweenQuotes.matcher(sb.toString()); + List queryLogList = new ArrayList<>(); + + //JSON to Java Object List + while (matcher.find()) { + if (matcher.group().equalsIgnoreCase("QueryLog")) { + QueryLog queryLog = new QueryLog(); + while (matcher.find()) { + if (matcher.group().equalsIgnoreCase("flag")) { + matcher.find(); + if (matcher.find()) { + queryLog.setFlag(matcher.group()); + } + } else if (matcher.group().equalsIgnoreCase("query")) { + matcher.find(); + if (matcher.find()) { + queryLog.setQuery(matcher.group()); + } + } else if (matcher.group().equalsIgnoreCase("operation")) { + matcher.find(); + if (matcher.find()) { + queryLog.setOperation(matcher.group()); + } + } else if (matcher.group().equalsIgnoreCase("submissionTimestamp")) { + matcher.find(); + if (matcher.find()) { + queryLog.setSubmissionTimestamp(matcher.group()); + } + } else if (matcher.group().equalsIgnoreCase("submittedBy")) { + matcher.find(); + if (matcher.find()) { + queryLog.setSubmittedBy(matcher.group()); + } + } else if (matcher.group().equalsIgnoreCase("tableName")) { + matcher.find(); + if (matcher.find()) { + queryLog.setTableName(matcher.group()); + } + } else if (matcher.group().equalsIgnoreCase("databaseName")) { + matcher.find(); + if (matcher.find()) { + queryLog.setDatabaseName(matcher.group()); + } + break; + } + } + queryLogList.add(queryLog); + } + } + return queryLogList; + } +} diff --git a/src/main/java/com/dal/distributed/authentication/Login.java b/src/main/java/com/dal/distributed/authentication/Login.java index e654a8d..eeff554 100644 --- a/src/main/java/com/dal/distributed/authentication/Login.java +++ b/src/main/java/com/dal/distributed/authentication/Login.java @@ -1,5 +1,6 @@ package com.dal.distributed.authentication; +import com.dal.distributed.logger.model.EventLog; import com.dal.distributed.main.OperationsMenu; import com.dal.distributed.constant.AuthConstants; import com.dal.distributed.logger.Logger; @@ -16,27 +17,41 @@ public class Login { public static Logger logger = Logger.instance(); - public void flow(Scanner sc) throws IOException { + public void flow(Scanner sc) throws Exception { logger.info("For login, please provide your userId and press enter"); + EventLog loginEvent = new EventLog(); + loginEvent.setLogType("LOGIN"); String userId = sc.nextLine(); if (userId == null || userId.isEmpty()) { + loginEvent.setSuccess(false); + loginEvent.setUserId(userId); + EventLog.logEvent(loginEvent); logger.info("Please type something before enter!"); return; } logger.info("Please provide your password and press enter"); String password = sc.nextLine(); if (password == null || password.isEmpty()) { + loginEvent.setSuccess(false); + loginEvent.setUserId(userId); + EventLog.logEvent(loginEvent); logger.error("Password can't be empty!"); return; } Optional userOpt = AuthFileUtils.readUserDetails(AuthConstants.USER_DETAILS_FILE_LOCATION, getHashedValue(userId)); if(!userOpt.isPresent()) { + loginEvent.setSuccess(false); + loginEvent.setUserId(userId); + EventLog.logEvent(loginEvent); logger.error("Either userId/password is not correct"); return; } UserRegistration user = userOpt.get(); String hashedPassword = getHashedValue(password); if (!hashedPassword.equals(user.getPassword())) { + loginEvent.setSuccess(false); + loginEvent.setUserId(userId); + EventLog.logEvent(loginEvent); logger.error("Either userId/password is not correct"); return; } @@ -46,13 +61,22 @@ public void flow(Scanner sc) throws IOException { logger.info(securityQuestion.getQuestion()); String securingAnsByUser = sc.nextLine(); if (securingAnsByUser == null || securingAnsByUser.isEmpty()) { + loginEvent.setSuccess(false); + loginEvent.setUserId(userId); + EventLog.logEvent(loginEvent); logger.error("Your security answer can't be empty!"); return; } if (!securingAnsByUser.equals(securityQuestion.getAnswer())) { + loginEvent.setSuccess(false); + loginEvent.setUserId(userId); + EventLog.logEvent(loginEvent); logger.info("Invalid answer please try again!"); return; } + loginEvent.setSuccess(true); + loginEvent.setUserId(userId); + EventLog.logEvent(loginEvent); logger.info("You are successfully logged in!!"); OperationsMenu operationsMenu = new OperationsMenu(); operationsMenu.displayOperationsMenu(userId, sc); diff --git a/src/main/java/com/dal/distributed/authentication/Registration.java b/src/main/java/com/dal/distributed/authentication/Registration.java index 70f03dc..6a30bd8 100644 --- a/src/main/java/com/dal/distributed/authentication/Registration.java +++ b/src/main/java/com/dal/distributed/authentication/Registration.java @@ -4,6 +4,7 @@ import com.dal.distributed.logger.Logger; import com.dal.distributed.authentication.model.SecurityQuestions; import com.dal.distributed.authentication.model.UserRegistration; +import com.dal.distributed.logger.model.EventLog; import org.apache.commons.codec.digest.DigestUtils; import java.util.ArrayList; @@ -24,14 +25,19 @@ public class Registration { public void registerUser() { Scanner sc = new Scanner(System.in); + EventLog registerEvent = new EventLog(); + registerEvent.setLogType("REGISTRATION"); String userId; String password; logger.info("Enter a UserID containing 5 to 15 characters: "); userId = sc.nextLine(); + registerEvent.setUserId(userId); Boolean userIdValid = performUserIdValidations(userId); if (!userIdValid) { + registerEvent.setSuccess(false); + EventLog.logEvent(registerEvent); return; } @@ -39,24 +45,32 @@ public void registerUser() { password = sc.nextLine(); Boolean isPasswordValid = performPasswordValidations(password); if (!isPasswordValid) { + registerEvent.setSuccess(false); + EventLog.logEvent(registerEvent); return; } logger.info(AuthConstants.SECURITY_QUESTION_1); final String securityAnswerOne = sc.nextLine(); if (!validateSecurityInput(securityAnswerOne)) { + registerEvent.setSuccess(false); + EventLog.logEvent(registerEvent); return; } logger.info(AuthConstants.SECURITY_QUESTION_2); final String securityAnswerTwo = sc.nextLine(); if (!validateSecurityInput(securityAnswerTwo)) { + registerEvent.setSuccess(false); + EventLog.logEvent(registerEvent); return; } logger.info(AuthConstants.SECURITY_QUESTION_3); final String securityAnswerThree = sc.nextLine(); if (!validateSecurityInput(securityAnswerThree)) { + registerEvent.setSuccess(false); + EventLog.logEvent(registerEvent); return; } @@ -73,7 +87,8 @@ public void registerUser() { AuthFileUtils file = new AuthFileUtils(); file.writeUserDetails(AuthConstants.USER_DETAILS_FILE_LOCATION, user.toString()); - + registerEvent.setSuccess(true); + EventLog.logEvent(registerEvent); logger.info("Registration completed successfully!!! You can now access the system with userID and Password."); } diff --git a/src/main/java/com/dal/distributed/constant/AuthConstants.java b/src/main/java/com/dal/distributed/constant/AuthConstants.java index 6557976..223f15d 100644 --- a/src/main/java/com/dal/distributed/constant/AuthConstants.java +++ b/src/main/java/com/dal/distributed/constant/AuthConstants.java @@ -10,6 +10,12 @@ public final class AuthConstants { public static final String USER_DETAILS_FILE_LOCATION = "usr/dpg9/authentication/User_Profile/"; + public static final String AUTHENTICATION_FOLDER = "usr/dpg9/authentication/"; + + public static final String AUTHENTICATION_FILE = "User_Profile"; + public static final String SUCCESS = "SUCCESS"; + public static final String FAILURE = "FAILURE"; + } diff --git a/src/main/java/com/dal/distributed/constant/DataConstants.java b/src/main/java/com/dal/distributed/constant/DataConstants.java index c0cc98d..7b001d3 100644 --- a/src/main/java/com/dal/distributed/constant/DataConstants.java +++ b/src/main/java/com/dal/distributed/constant/DataConstants.java @@ -8,4 +8,8 @@ public class DataConstants { public static final String QUERY_LOGS_FILE_LOCATION = "usr/dpg9/logs/"; public static final String QUERY_LOG_FILE_NAME = "query_logs"; + + public static final String EVENT_LOG_FILE_NAME = "event_logs"; + + public static final String FILE_FORMAT = ".psv"; } diff --git a/src/main/java/com/dal/distributed/constant/QueryRegex.java b/src/main/java/com/dal/distributed/constant/QueryRegex.java index 7972f57..35e3914 100644 --- a/src/main/java/com/dal/distributed/constant/QueryRegex.java +++ b/src/main/java/com/dal/distributed/constant/QueryRegex.java @@ -6,8 +6,15 @@ public class QueryRegex { public static final Pattern createDatabase = Pattern.compile("create\\s+database\\s+(\\w+)\\;", Pattern.CASE_INSENSITIVE); public static final Pattern useDatabase = Pattern.compile("USE\\s+(\\w+)\\;", Pattern.CASE_INSENSITIVE); public static final Pattern createTable = Pattern.compile("CREATE\\s+TABLE\\s+(\\w+)\\s*\\((.+)\\)\\;", Pattern.CASE_INSENSITIVE); - public static final Pattern updateTable = Pattern.compile("UPDATE\\s+(\\w+)\\s+SET\\s+(\\w+\\=(\\w|\\'\\'|\\'\\w+\\'))\\s+(WHERE\\s+(\\w+\\s*(\\=|\\<|\\>|LIKE|\\<\\=|\\>\\=)\\s*(\\'\\w*\\'|\\w+)))?\\;", Pattern.CASE_INSENSITIVE); - public static final Pattern deleteDataInTable = Pattern.compile("DELETE\\s+FROM\\s+(\\w+)\\s+WHERE\\s+(\\w+\\s*(\\=|\\<|\\>|LIKE|\\<\\=|\\>\\=)\\s*(\\'\\w*\\'|\\w+))\\;", Pattern.CASE_INSENSITIVE); - public static final Pattern insertDataIntoTable = Pattern.compile("insert\\s+into\\s+(\\w+)((.+))?\\s+values\\s*\\((.+)\\);", Pattern.CASE_INSENSITIVE); - public static final Pattern selectDataFromTable = Pattern.compile("select\\s+((\\*)|(\\w+)|(\\w+\\,\\s*)+\\w+)\\s+from\\s+\\w+\\s*(where\\s+(\\w+)\\s*(\\=|LIKE|IN|\\<|\\>|\\!\\=|\\<\\=|\\>\\=)\\s*(\\d+|\\'\\w+\\'))?\\;", Pattern.CASE_INSENSITIVE); + public static final Pattern updateTable = Pattern.compile("UPDATE\\s+(\\w+)\\s+SET\\s+(\\w+\\=(\\w|\\'\\'|\\'\\w+\\'))\\s+(WHERE\\s+(\\w+\\s*(\\=|\\<|\\>|\\<\\=|\\>\\=)\\s*(\\'\\w*\\'|\\w+)))?\\;", Pattern.CASE_INSENSITIVE); + public static final Pattern deleteDataInTable = Pattern.compile("DELETE\\s+FROM\\s+(\\w+)\\s+WHERE\\s+(\\w+\\s*(\\=|\\<|\\>|\\<\\=|\\>\\=)\\s*(\\'\\w*\\'|\\w+))\\;\\s*$", Pattern.CASE_INSENSITIVE); + public static final Pattern insertDataIntoTable = Pattern.compile("insert\\s+into\\s+(\\w+)((.+))?\\s+values\\s*\\((.+)\\);\\s*$", Pattern.CASE_INSENSITIVE); + public static final Pattern selectDataFromTable = Pattern.compile("select\\s+((\\*)|(\\w+)|(\\w+\\,\\s*)+\\w+)\\s+from\\s+(\\w+)\\s*(where\\s+(\\w+)\\s*(\\=|\\<|\\>|\\!\\=|\\<\\=|\\>\\=)\\s*(\\d+|\\'\\w+\\'))?\\;$", Pattern.CASE_INSENSITIVE); + public static final Pattern startTransaction = Pattern.compile("^start\\s+transaction;\\s*$", Pattern.CASE_INSENSITIVE); + public static final Pattern endTransaction = Pattern.compile("^(end\\s+transaction)|(commit);\\s*$", Pattern.CASE_INSENSITIVE); + public static final Pattern valueBetweenQuotes = Pattern.compile("(?<=\").*?(?=\")"); + public static final Pattern countQueriesAnalytics = Pattern.compile("count\\s+queries\\s+by\\s+(.*)for\\s+(.*)", Pattern.CASE_INSENSITIVE); + + public static final Pattern digitOnlyRegex = Pattern.compile("^[0-9]+$"); + } \ No newline at end of file diff --git a/src/main/java/com/dal/distributed/constant/QueryTypes.java b/src/main/java/com/dal/distributed/constant/QueryTypes.java index ec2ea96..d79e093 100644 --- a/src/main/java/com/dal/distributed/constant/QueryTypes.java +++ b/src/main/java/com/dal/distributed/constant/QueryTypes.java @@ -8,4 +8,6 @@ public class QueryTypes { public static final String DELETE = "DELETE"; public static final String INSERT = "INSERT"; public static final String SELECT = "SELECT"; + public static final String START_TRANSACTION = "START_TRANSACTION"; + public static final String END_TRANSACTION = "END_TRANSACTION"; } diff --git a/src/main/java/com/dal/distributed/constant/VMConstants.java b/src/main/java/com/dal/distributed/constant/VMConstants.java new file mode 100644 index 0000000..ddad919 --- /dev/null +++ b/src/main/java/com/dal/distributed/constant/VMConstants.java @@ -0,0 +1,15 @@ +package com.dal.distributed.constant; + +public class VMConstants { + public static final String projectPath = "/home/chanpreets10/csci-5408-group-project-dpg9/"; + public static final String USERNAME = "chanpreets10"; + public static final int port = 22; + public static final String KEYNAME = ""; +// public static final String PRIVATE_KEY = projectPath + "ssh-keys/" + KEYNAME; + public static final String PRIVATE_KEY = projectPath + "ssh-keys/chanpreets10" + KEYNAME; + public static final String EXTERNAL_IP = ""; + public static final String LOCAL="local"; + public static final String REMOTE="remote"; +// VM-1 : 34.132.74.189 +// VM-2 : +} diff --git a/src/main/java/com/dal/distributed/export/ExportDatabase.java b/src/main/java/com/dal/distributed/export/ExportDatabase.java new file mode 100644 index 0000000..dd5d219 --- /dev/null +++ b/src/main/java/com/dal/distributed/export/ExportDatabase.java @@ -0,0 +1,323 @@ +package com.dal.distributed.export; + +import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.MiscConstants; +import com.dal.distributed.constant.VMConstants; +import com.dal.distributed.logger.Logger; +import com.dal.distributed.main.model.Column; +import com.dal.distributed.main.model.Table; +import com.dal.distributed.utils.DatabaseUtils; +import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; + +import java.io.*; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ExportDatabase { + + private static Logger logger = Logger.instance(); + + private static final String DEFAULT_EXPORT_FILE_LOCATION = "usr/dpg9/output/export/"; + + private static final String DEFAULT_EXPORT_FILE_NAME = "%s_%s.sql"; + + private static final String INSERT_GENERIC_QUERY_PREFIX = "INSERT INTO %s VALUES "; + + private static final String CREATE_DATABASE_QUERY_PREFIX = "CREATE DATABASE IF NOT EXISTS %s;"; + + private static final String USE_DATABASE = "USE %s;"; + + private static final String EXPORT_OUTPUT_PROMPT = "Your exported file will be found at: %s"; + + public void flow(Scanner sc) throws Exception { + while (true) { + logger.info("Please choose any one of the following options:"); + logger.info("\n1. Show Databases list"); + logger.info("2. Export Database"); + logger.info("3. Go back to main menu"); + String option = sc.nextLine(); + + switch (option) { + case "1": + displayDatabases(); + break; + case "2": + logger.info("Please choose a database"); + String databaseName = sc.nextLine(); + String fileName = exportStructureAndValue(databaseName); + if (fileName != null) + logger.info(String.format(EXPORT_OUTPUT_PROMPT, DEFAULT_EXPORT_FILE_LOCATION + fileName)); + break; + case "3": + break; + default: + logger.error("Please choose a valid input!"); + } + + if("3".equals(option)) + break; + } + } + + private void displayDatabases() { + File[] files = FileOperations.readFiles(DataConstants.DATABASES_FOLDER_LOCATION); + List databaseNames = new ArrayList<>(); + for (File file: files) { + if(file.isDirectory()) + databaseNames.add(file.getName()); + } + Collections.sort(databaseNames); + databaseNames.stream().forEach(logger::info); + } + + private String exportStructureAndValue(String database) throws Exception { + if (!isDatabaseExists(database)) + return null; + List schemaFiles = DatabaseUtils.getTableSchemaFiles(database); + List remoteTables = DatabaseUtils.getRemoteTables(database); + if ((schemaFiles == null || schemaFiles.isEmpty()) && (remoteTables == null || remoteTables.isEmpty())) { + logger.info("Selected database is empty! Please choose another one to export"); + return null; + } + List
tables = new ArrayList<>(remoteTables); + if (schemaFiles != null || !schemaFiles.isEmpty()) { + System.out.println("Going in if block"); + for (File tableFile: schemaFiles) { + System.out.println("Local schema file: " + tableFile.getName()); + List columnDefs = DatabaseUtils.getColumnDefinitions(database, tableFile); + Table table = Table.createTableModel(tableFile.getName(), database, columnDefs); + tables.add(table); + System.out.println("Table model created for: " + tableFile.getName() + "is: "+ table.getTableName()); + } + } + System.out.println("All tables remote and local:"); + tables.stream().forEach(x -> { + System.out.println(x.getTableName()); + }); + + //sort tables based on the foreign keys + Collections.sort(tables, (o1, o2) -> { + if (o1.getTableName().equals(o2.getTableName())) + return 0; + List o1Columns = o1.getColumns(); + for (Column o1Column : o1Columns) { + List constraints = o1Column.getConstraints(); + if (constraints == null || constraints.isEmpty()) + continue; + for (String constraint: constraints) { + if (constraint.contains(o2.getTableName())) + return 1; + } + } + return -1; + }); + + System.out.println("All tables after sorting remote and local:"); + tables.stream().forEach(x -> { + System.out.println(x.getTableName()); + }); + return exportDataToSqlFile(database, tables); + } + + private boolean isDatabaseExists(String databaseName) { + File[] databases = FileOperations.readFiles(DataConstants.DATABASES_FOLDER_LOCATION); + boolean isExist=false; + for (File file : databases) { + if (file.getName().equalsIgnoreCase(databaseName)) { + isExist=true; + } + } + if(!isExist){ + logger.error("Error Code: 1007. Can't export database '" + databaseName + "'; Database doesn't exists."); + return false; + } + return true; + } + + private String exportDataToSqlFile(String database, List
tables) throws Exception { + FileOperations.createNewFolderRecursively(DEFAULT_EXPORT_FILE_LOCATION); + String fileName = String.format(DEFAULT_EXPORT_FILE_NAME, new Date().getTime(), database); + File exportSqlFile = new File(DEFAULT_EXPORT_FILE_LOCATION + File.separator + fileName); + try ( FileWriter fw = new FileWriter(exportSqlFile); + BufferedWriter bw = new BufferedWriter(fw);){ + bw.write(String.format(CREATE_DATABASE_QUERY_PREFIX, database)); + bw.write("\n"); + bw.write(String.format(USE_DATABASE, database)); + bw.write("\n"); + for (Table table: tables) { + String tableLocation = DatabaseUtils.getTableLocation(database, table.getTableName()); + System.out.println(table.getTableName() + " is in: " + tableLocation); + boolean isLocal = !VMConstants.REMOTE.equals(tableLocation); + System.out.println(table.getTableName() + " is in local: " + isLocal); + String tableQueries = generateCreateTableAlongWithData(database, table, isLocal); + bw.write(tableQueries); + bw.write("\n"); + } + } + catch (IOException e) { + e.printStackTrace(); + } + return fileName; + } + + + private String generateCreateTableAlongWithData(String database, Table table, boolean isLocal) throws Exception { + String createTableQuery = generateCreateTable(database, table); + String insertQueries = isLocal? generateInsertData(database, table): generateInsertDataForRemoteTable(database, table); + StringBuilder createAndInsertQueriesTable = new StringBuilder(createTableQuery); + createAndInsertQueriesTable.append("\n"); + createAndInsertQueriesTable.append(insertQueries); + return createAndInsertQueriesTable.toString(); + } + + private String generateInsertDataForRemoteTable(String database, Table table) throws Exception { + String dataFilePath = DatabaseUtils.getDataFilePathFromTable(database, table.getTableName()); + String data = RemoteVmUtils.readFileContent(VMConstants.projectPath + dataFilePath); + List rowsWithHeader = Arrays.asList(data.split("\n")); + List columnNames = table.getColumns().stream() + .map(Column::getColumnName).collect(Collectors.toList()); + Map columnNameToColumn = table.getColumns().stream() + .collect(Collectors.toMap(Column::getColumnName, Function.identity())); + if (rowsWithHeader.size() == 1) + return "\n"; + String genericInsertQueryPrefix = String.format(INSERT_GENERIC_QUERY_PREFIX, table.getTableName()); + StringBuilder insertQueryBuilder = new StringBuilder(genericInsertQueryPrefix); + for (int i=1; i; + StringBuilder createTableQueryBuilder = new StringBuilder(); + createTableQueryBuilder.append(String.format("CREATE TABLE IF NOT EXISTS %s.%s", database, table.getTableName())); + createTableQueryBuilder.append("("); + createTableQueryBuilder.append("\n"); + createTableQueryBuilder.append(generateColumnDefinitionQuery(table)); + createTableQueryBuilder.append("\n"); + createTableQueryBuilder.append(");"); + return createTableQueryBuilder.toString(); + } + + private String generateColumnDefinitionQuery(Table table) { + StringBuilder columnDefinitions = new StringBuilder(); + for (Column column: table.getColumns()) { + columnDefinitions.append("\t"); + columnDefinitions.append(column.getColumnName()); + columnDefinitions.append(" "); + columnDefinitions.append(column.getDataType()); + columnDefinitions.append(" "); + boolean fkConstraint = false; + if (column.getConstraints() != null && !column.getConstraints().isEmpty()) { + String constraints = column.getConstraints() + .stream() + .filter(constraint -> !constraint.contains("FOREIGN KEY")) + .collect(Collectors.joining(" ")); + fkConstraint = column.getConstraints() + .stream() + .anyMatch(constraint -> constraint.contains("FOREIGN KEY")); + columnDefinitions.append(constraints); + } + columnDefinitions.append(",\n"); + if (fkConstraint) { + String fkConstraintStr = column.getConstraints().stream().filter(constraint -> constraint.contains("FOREIGN KEY")).findAny().get(); + columnDefinitions.append("\t"); + columnDefinitions.append(getForeignKeyConstraint(fkConstraintStr, column.getColumnName())); + columnDefinitions.append(",\n"); + } + } + columnDefinitions.deleteCharAt(columnDefinitions.length()-1); + columnDefinitions.deleteCharAt(columnDefinitions.length()-1); + return columnDefinitions.toString(); + } + + private String getForeignKeyConstraint(String fkConstraint, String columnName) { + String [] fkSplit = fkConstraint.split(" "); + StringBuilder fkConstraintCode = new StringBuilder(); + for (int i=0; i columnNames = DatabaseUtils.getColumnNames(dataFile); + Map columnNameToColumn = table.getColumns().stream() + .collect(Collectors.toMap(Column::getColumnName, Function.identity())); + String genericInsertQueryPrefix = String.format(INSERT_GENERIC_QUERY_PREFIX, table.getTableName()); + boolean isDataPresent = false; + try (FileReader fr = new FileReader(dataFile); + BufferedReader br = new BufferedReader(fr);) { + // Buffered reader will now point after the header row + br.readLine(); + String line; + // This builds the entire insert query + StringBuilder dataQueryBuilder = new StringBuilder(genericInsertQueryPrefix); + while ((line = br.readLine()) != null) { + isDataPresent = true; + String [] rowData = line.split(MiscConstants.PIPE); + //This builds only comma separated list for a single row. + StringBuilder rowDataBuilder = new StringBuilder("("); + for (int i=0; i columnNames, Map columnNameToColumn) { + StringBuilder rowDataBuilder = new StringBuilder("("); + for (int i=0; i transactionQueries = new ArrayList<>(); Logger logger = Logger.instance(); public void displayOperationsMenu(String userId, Scanner scanner) throws Exception { while (true) { - logger.info("Please choose from the following options:"); - logger.info("\n1. Write Queries"); + logger.info("\nPlease choose from the following options:"); + logger.info("1. Write Queries"); logger.info("2. Generate ERD"); logger.info("3. Export"); logger.info("4. Analytics"); @@ -32,15 +40,21 @@ public void displayOperationsMenu(String userId, Scanner scanner) throws Excepti case "2": break; case "3": + ExportDatabase export = new ExportDatabase(); + export.flow(scanner); break; case "4": + Analytics analytics = new Analytics(); + analytics.analyze(scanner); break; case "5": + Main.databaseName = ""; break; default: logger.error("Please choose valid option!"); } if ("5".equals(userInput)) { + EventLog.logLogoutEvent(userId); logger.info("You are logged out"); break; } @@ -56,47 +70,148 @@ public void implementQuery(Scanner sc, String userId) throws Exception { SelectQuery selectQuery = new SelectQuery(); UpdateTable updateTable = new UpdateTable(); DeleteDataFromTable deleteDataFromTable = new DeleteDataFromTable(); + do { + logger.info("Write query for selected option:"); + String query = sc.nextLine(); - logger.info("Write query for selected option:"); - String query = sc.nextLine(); - - QueryLog logQuery = new QueryLog(); - logQuery.setFlag("valid"); - logQuery.setQuery(query); - logQuery.setSubmissionTimestamp(String.valueOf(new Timestamp(System.currentTimeMillis()))); - logQuery.setSubmittedBy(userId); - - Map queryValidatorResults = queryExecutorObj.validateQuery(query); + QueryLog logQuery = new QueryLog(); + logQuery.setFlag("valid"); + logQuery.setQuery(query); + logQuery.setSubmissionTimestamp(String.valueOf(new Timestamp(System.currentTimeMillis()))); + logQuery.setSubmittedBy(userId); - if (((boolean) queryValidatorResults.get("isValidate")) && (queryValidatorResults.get("queryType") == QueryTypes.CREATE_DATABASE)) { - if (createDatabase.execute(query)) { - logger.info("Action: " + query + "\nMessage: 1 row(s) affected.\n"); + Map queryValidatorResults = queryExecutorObj.validateQuery(query); + if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.CREATE_DATABASE)) { + EventLog createDbEvent = new EventLog("CREATE_DB", userId); + logQuery.setOperation(QueryTypes.CREATE_DATABASE); + logQuery.setDatabaseName((String) queryValidatorResults.get("entity")); + Pair res = createDatabase.execute(query); + createDbEvent.setSuccess(res.getFirst()); + createDbEvent.setDatabaseName(res.getSecond()); + if (res.getFirst()) { + logger.info("Action: " + query + "\nMessage: 1 row(s) affected.\n"); + } + EventLog.logEvent(createDbEvent); + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.USE)) { + logQuery.setOperation(QueryTypes.USE); + logQuery.setDatabaseName((String) queryValidatorResults.get("entity")); + if (useDatabase.execute(query)) { + logger.info("Action: " + query + "\nMessage: 0 row(s) affected.\n"); + } else { + logger.info("Database doesn't exist"); + } + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.CREATE_TABLE)) { + EventLog createTableEvent = new EventLog("CREATE_TABLE", userId); + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + logger.error("Please select database first"); + } + createTableEvent.setDatabaseName(Main.databaseName); + logQuery.setOperation(QueryTypes.CREATE_TABLE); + logQuery.setTableName((String) queryValidatorResults.get("entity")); + OperationStatus result = createTable.execute(query); + createTableEvent.setSuccess(result.isStatus()); + createTableEvent.setTableName(logQuery.getTableName()); + if (result.isStatus()) { + logger.info("Action: " + query + "\nMessage: 0 row(s) affected.\n"); + } + logQuery.setDatabaseName(result.getDatabaseName()); + EventLog.logEvent(createTableEvent); + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.INSERT)) { + logQuery.setOperation(QueryTypes.INSERT); + logQuery.setTableName((String) queryValidatorResults.get("entity")); + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + logger.error("Please select database first"); + } else if (Main.isTransaction) { + transactionQueries.add(insertIntoTable.execute(query)); + } else { + OperationStatus result = insertIntoTable.execute(query); + if (result != null && result.isStatus()) { + logger.info("Action: " + query + "\nMessage: 1 row(s) affected.\n"); + logQuery.setDatabaseName(result.getDatabaseName()); + } else { + logger.error("Required table/columns/data does not exist"); + } + } + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.SELECT)) { + logQuery.setOperation(QueryTypes.SELECT); + logQuery.setTableName((String) queryValidatorResults.get("entity")); + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + logger.error("Please select database first"); + } else if (Main.isTransaction) { + transactionQueries.add(selectQuery.execute(query)); + } else { + OperationStatus result = selectQuery.execute(query); + if (result != null && result.isStatus()) { + logQuery.setDatabaseName(result.getDatabaseName()); + } else { + logger.error("Required table/columns/data does not exist"); + } + } + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.UPDATE)) { + logQuery.setOperation(QueryTypes.UPDATE); + logQuery.setTableName((String) queryValidatorResults.get("entity")); + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + logger.error("Please select database first"); + } else if (Main.isTransaction) { + transactionQueries.add(updateTable.execute(query)); + } else { + OperationStatus result = updateTable.execute(query); + if (result != null && result.isStatus()) { + logger.info("Action: " + query + "\nMessage: " + result.getCount() + " row(s) affected.\n"); + logQuery.setDatabaseName(result.getDatabaseName()); + } else { + logger.error("Required table/columns/data does not exist"); + } + } + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.DELETE)) { + logQuery.setOperation(QueryTypes.DELETE); + logQuery.setTableName((String) queryValidatorResults.get("entity")); + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + logger.error("Please select database first"); + } else if (Main.isTransaction) { + transactionQueries.add(deleteDataFromTable.execute(query)); + } else { + OperationStatus result = deleteDataFromTable.execute(query); + if (result != null && result.isStatus()) { + logger.info("Action: " + query + "\nMessage: " + result.getCount() + " row(s) affected.\n"); + logQuery.setDatabaseName(result.getDatabaseName()); + } else { + logger.error("Required table/columns/data does not exist"); + } + } + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.START_TRANSACTION)) { + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + logger.error("Please select database first"); + } else { + logQuery.setOperation(QueryTypes.START_TRANSACTION); + Main.isTransaction = true; + } + } else if (((boolean) queryValidatorResults.get("isValidate")) + && (queryValidatorResults.get("queryType") == QueryTypes.END_TRANSACTION)) { + logQuery.setOperation(QueryTypes.END_TRANSACTION); + TransactionProcessing transactionProcessing = new TransactionProcessing(); + for (int i = 0; i < transactionQueries.size(); i++) { + for (int j = i + 1; j < transactionQueries.size(); j++) { + if (transactionQueries.get(i).getTableName().equals(transactionQueries.get(j).getTableName())) { + transactionQueries.get(j).setRepeatTable(true); + } + } + } + transactionProcessing.execute(transactionQueries); + } else { + logQuery.setFlag("invalid"); + logger.error("Oops.. looks like I encountered error in parsing query"); } - } else if (((boolean) queryValidatorResults.get("isValidate")) && (queryValidatorResults.get("queryType") == QueryTypes.USE)) { - if (useDatabase.execute(query)) { - logger.info("Action: " + query + "\nMessage: 0 row(s) affected.\n"); - } - } else if (((boolean) queryValidatorResults.get("isValidate")) && (queryValidatorResults.get("queryType") == QueryTypes.CREATE_TABLE)) { - if (createTable.execute(query)) { - logger.info("Action: " + query + "\nMessage: 0 row(s) affected.\n"); - } - } else if (((boolean) queryValidatorResults.get("isValidate")) && (queryValidatorResults.get("queryType") == QueryTypes.INSERT)) { - insertIntoTable.execute(query); - } else if (((boolean) queryValidatorResults.get("isValidate")) && (queryValidatorResults.get("queryType") == QueryTypes.SELECT)) { - selectQuery.execute(query); - } else if (((boolean) queryValidatorResults.get("isValidate")) && (queryValidatorResults.get("queryType") == QueryTypes.UPDATE)) { - if (updateTable.execute(query)) { - logger.info("Action: " + query + "\nMessage: 1 row(s) affected.\n"); - } - } else if (((boolean) queryValidatorResults.get("isValidate")) && (queryValidatorResults.get("queryType") == QueryTypes.DELETE)) { - if (deleteDataFromTable.execute(query)) { - logger.info("Action: " + query + "\nMessage: 1 row(s) affected.\n"); - } - } else { - logQuery.setFlag("invalid"); - logger.error("Oops.. looks like I encountered error in parsing query"); - } - FileOperations.writeToExistingFile(logQuery.toString(), - DataConstants.QUERY_LOG_FILE_NAME, DataConstants.QUERY_LOGS_FILE_LOCATION); + FileOperations.writeToExistingFile(logQuery.toString(), + DataConstants.QUERY_LOG_FILE_NAME, DataConstants.QUERY_LOGS_FILE_LOCATION); + } while (Main.isTransaction); } } diff --git a/src/main/java/com/dal/distributed/main/model/Column.java b/src/main/java/com/dal/distributed/main/model/Column.java new file mode 100644 index 0000000..67804a0 --- /dev/null +++ b/src/main/java/com/dal/distributed/main/model/Column.java @@ -0,0 +1,51 @@ +package com.dal.distributed.main.model; + +import com.dal.distributed.constant.MiscConstants; + +import java.util.Arrays; +import java.util.List; + +public class Column { + + private String columnName; + + private String dataType; + + private List constraints; + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public List getConstraints() { + return constraints; + } + + public void setConstraints(List constraints) { + this.constraints = constraints; + } + + public static Column createColumnModel(String columnDef) { + String [] columnsDefSplit = columnDef.split(MiscConstants.PIPE); + Column column = new Column(); + column.setColumnName(columnsDefSplit[0]); + column.setDataType(columnsDefSplit[1]); + if (columnsDefSplit.length > 2) { + List columnDefList = Arrays.asList(columnsDefSplit); + column.setConstraints(columnDefList.subList(2, columnDefList.size())); + } + return column; + } +} diff --git a/src/main/java/com/dal/distributed/main/model/Pair.java b/src/main/java/com/dal/distributed/main/model/Pair.java new file mode 100644 index 0000000..33275f8 --- /dev/null +++ b/src/main/java/com/dal/distributed/main/model/Pair.java @@ -0,0 +1,33 @@ +package com.dal.distributed.main.model; + +public class Pair { + + private T first; + + private V second; + + public Pair() { + + } + + public Pair(T first, V second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public void setFirst(T first) { + this.first = first; + } + + public V getSecond() { + return second; + } + + public void setSecond(V second) { + this.second = second; + } +} diff --git a/src/main/java/com/dal/distributed/main/model/Table.java b/src/main/java/com/dal/distributed/main/model/Table.java new file mode 100644 index 0000000..1d7b9db --- /dev/null +++ b/src/main/java/com/dal/distributed/main/model/Table.java @@ -0,0 +1,69 @@ +package com.dal.distributed.main.model; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class Table implements Comparator
{ + + private String tableName; + + private String databaseName; + + private List columns; + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + public static Table createTableModel(String tableName, String databaseName, List columnDefs) { + Table table = new Table(); + table.setDatabaseName(databaseName); + //to remove .psv with the table name + table.setTableName(tableName.split("_")[0]); + List columns = new ArrayList<>(); + for (String columnDef: columnDefs) { + Column col = Column.createColumnModel(columnDef); + columns.add(col); + } + table.setColumns(columns); + return table; + } + + @Override + public int compare(Table o1, Table o2) { + if (o1.getTableName().equals(o2.getTableName())) + return 0; + List o1Columns = o1.getColumns(); + for (Column o1Column : o1Columns) { + List constraints = o1Column.getConstraints(); + if(constraints == null || constraints.isEmpty()) + continue; + for (String constraint: constraints) { + if (constraint.contains(o2.getTableName())) + return 1; + } + } + return -1; + } +} diff --git a/src/main/java/com/dal/distributed/miscellaneous/MiscOperations.java b/src/main/java/com/dal/distributed/miscellaneous/MiscOperations.java index c43331a..b250236 100644 --- a/src/main/java/com/dal/distributed/miscellaneous/MiscOperations.java +++ b/src/main/java/com/dal/distributed/miscellaneous/MiscOperations.java @@ -1,13 +1,20 @@ package com.dal.distributed.miscellaneous; -import com.dal.distributed.constant.MiscConstants; +import com.dal.distributed.constant.AuthConstants; +import com.dal.distributed.constant.DataConstants; import com.dal.distributed.utils.FileOperations; import java.io.IOException; public class MiscOperations { public static void createInitFolders() throws IOException { - FileOperations.createNewFolderRecursively(MiscConstants.initFolder); - FileOperations.createNewFolderRecursively(MiscConstants.initFolder2); + FileOperations.createNewFolderRecursively(AuthConstants.AUTHENTICATION_FOLDER); + FileOperations.createNewFolderRecursively(DataConstants.LOGS_FILE_LOCATION); + FileOperations.createNewFolderRecursively(DataConstants.DATABASES_FOLDER_LOCATION); + } + + public static void createUserProfileFile() throws IOException { + FileOperations.createNewFile(AuthConstants.AUTHENTICATION_FOLDER, + AuthConstants.AUTHENTICATION_FILE); } } diff --git a/src/main/java/com/dal/distributed/queryImpl/CreateDatabase.java b/src/main/java/com/dal/distributed/queryImpl/CreateDatabase.java index f8a29de..b9d6666 100644 --- a/src/main/java/com/dal/distributed/queryImpl/CreateDatabase.java +++ b/src/main/java/com/dal/distributed/queryImpl/CreateDatabase.java @@ -1,33 +1,42 @@ package com.dal.distributed.queryImpl; -import java.io.File; -import java.io.IOException; - import com.dal.distributed.constant.DataConstants; import com.dal.distributed.logger.Logger; +import com.dal.distributed.main.model.Pair; import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; + +import java.io.File; public class CreateDatabase { Logger logger = Logger.instance(); - public boolean execute(String query) throws IOException { + public Pair execute(String query) throws Exception { + Pair createDbRes = new Pair<>(); + createDbRes.setFirst(false); String[] sql = query.split("\\s+"); if (sql[0].equalsIgnoreCase("create") && sql[1].equalsIgnoreCase("database")) { - //Remove the semicolon from database name String databaseName = sql[2].substring(0, sql[2].length() - 1).toLowerCase(); + createDbRes.setSecond(databaseName); File[] databases = FileOperations.readFiles(DataConstants.DATABASES_FOLDER_LOCATION); for (File file : databases) { if (file.getName().equalsIgnoreCase(databaseName)) { logger.error("Error Code: 1007. Can't create database '" + databaseName + "'; Database exists."); - return false; + return createDbRes; } } FileOperations.createNewFolder(DataConstants.DATABASES_FOLDER_LOCATION, databaseName); - //FileOperations.writeToExistingFile(databaseName+"|","databases.psv", DataConstants.LOGS_FILE_LOCATION); - return true; - } else - return false; + FileOperations.writeToExistingFile("tablename|location", databaseName+".psv", DataConstants.DATABASES_FOLDER_LOCATION); + FileOperations.writeToExistingFile(databaseName + "|", "databases.psv", DataConstants.DATABASES_FOLDER_LOCATION); + + RemoteVmUtils.createNewFolder(DataConstants.DATABASES_FOLDER_LOCATION, databaseName); + RemoteVmUtils.writeToExistingFile(databaseName + "|", "databases.psv", DataConstants.DATABASES_FOLDER_LOCATION); + RemoteVmUtils.writeToExistingFile("tablename|location", databaseName+".psv", DataConstants.DATABASES_FOLDER_LOCATION); + createDbRes.setFirst(true); + return createDbRes; + } else + return createDbRes; } } \ No newline at end of file diff --git a/src/main/java/com/dal/distributed/queryImpl/CreateTable.java b/src/main/java/com/dal/distributed/queryImpl/CreateTable.java index 5032997..50613db 100644 --- a/src/main/java/com/dal/distributed/queryImpl/CreateTable.java +++ b/src/main/java/com/dal/distributed/queryImpl/CreateTable.java @@ -1,38 +1,97 @@ package com.dal.distributed.queryImpl; import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.VMConstants; +import com.dal.distributed.logger.Logger; import com.dal.distributed.main.Main; +import com.dal.distributed.queryImpl.model.OperationStatus; +import com.dal.distributed.utils.DatabaseUtils; import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; + +import java.util.concurrent.ThreadLocalRandom; + + +import java.io.File; +import java.io.IOException; public class CreateTable { - public boolean execute(String query) { - - String[] sql = query.split("\\s+"); - if(sql.length>3&& sql[0].toLowerCase().equals("create")&&sql[1].toLowerCase().equals("table")) - { - String mainStatement=query.substring(query.indexOf(sql[2])); - String tableName=mainStatement.substring(0,mainStatement.indexOf("(")); - String schema=mainStatement.substring(mainStatement.indexOf("(")+1,mainStatement.indexOf(";")-1); - String columnNames=""; - String[] columns= schema.split(","); - int i=0; - for(String col:columns) - { - columnNames+=col.substring(0,col.indexOf(" ")); - if(i!=columns.length-1) - columnNames+="|"; - i++; - } - schema=schema.replaceAll(",", "|"); - FileOperations.writeToExistingFile(columnNames, tableName+".psv", DataConstants.DATABASES_FOLDER_LOCATION+Main.databaseName+"/"); - FileOperations.writeToExistingFile(schema, tableName+"_Schema"+".psv", DataConstants.DATABASES_FOLDER_LOCATION+Main.databaseName+"/"); - //FileOperations.writeToExistingFile(tableName+"|", Main.databaseName+".psv", DataConstants.LOGS_FILE_LOCATION); - return true; + + private static Logger logger = Logger.instance(); + + public OperationStatus execute(String query) throws Exception { + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + System.out.println("No database selected"); + return new OperationStatus(Boolean.FALSE, null); } + int min = 0, max = 1; + String location = ""; + int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1); + if (randomNum == 0) + location = VMConstants.LOCAL; else - return false; + location = VMConstants.REMOTE; + String[] sql = query.split("\\s+"); + if (sql.length > 3 && sql[0].toLowerCase().equals("create") && sql[1].toLowerCase().equals("table")) { + String mainStatement = query.substring(query.indexOf(sql[2])); + String tableName = mainStatement.substring(0, mainStatement.indexOf("(")); + if (DatabaseUtils.getTableLocation(Main.databaseName, tableName) != null) { + logger.error("Table already exists! choose a different name!"); + return new OperationStatus(Boolean.FALSE, Main.databaseName); + } + String schema = mainStatement.substring(mainStatement.indexOf("(") + 1, mainStatement.indexOf(";") - 1); + String columnNames = ""; + String[] columns = schema.split(","); + int i = 0; + for (String col : columns) { + col = col.trim(); + columnNames += col.substring(0, col.indexOf(" ")); + if (i != columns.length - 1) + columnNames += "|"; + i++; + } + columnNames += "\n"; + String schemaRow[] = schema.split(","); + schema = "ColumnName|Datatype|Constraint" + "\n"; + for (int j = 0; j < schemaRow.length; j++) { + String temp = schemaRow[j].trim(); + temp = temp.replaceFirst(" ", "|").replaceFirst(" ", "|"); + schema += temp; + if (j != columns.length - 1) + schema += "\n"; + } + if (randomNum == 0) { + FileOperations.writeToExistingFile(columnNames, tableName + ".psv", DataConstants.DATABASES_FOLDER_LOCATION + Main.databaseName + "/"); + FileOperations.writeToExistingFile(schema, tableName + "_Schema" + ".psv", DataConstants.DATABASES_FOLDER_LOCATION + Main.databaseName + "/"); + FileOperations.writeToExistingFile("\n" + tableName + "|" + VMConstants.LOCAL, Main.databaseName + ".psv", DataConstants.DATABASES_FOLDER_LOCATION); + RemoteVmUtils.writeToExistingFile("\n" + tableName + "|" + VMConstants.REMOTE, Main.databaseName + ".psv", DataConstants.DATABASES_FOLDER_LOCATION); + } else { + RemoteVmUtils.writeToExistingFile(columnNames, tableName + ".psv", DataConstants.DATABASES_FOLDER_LOCATION + Main.databaseName + "/"); + RemoteVmUtils.writeToExistingFile(schema, tableName + "_Schema" + ".psv", DataConstants.DATABASES_FOLDER_LOCATION + Main.databaseName + "/"); + FileOperations.writeToExistingFile("\n" + tableName + "|" + VMConstants.REMOTE, Main.databaseName + ".psv", DataConstants.DATABASES_FOLDER_LOCATION); + RemoteVmUtils.writeToExistingFile("\n" + tableName + "|" + VMConstants.LOCAL, Main.databaseName + ".psv", DataConstants.DATABASES_FOLDER_LOCATION); + } + return new OperationStatus(Boolean.TRUE, Main.databaseName); + } else + return new OperationStatus(Boolean.FALSE, Main.databaseName); } + // private boolean isTableExisted(String databaseName, String tableName) throws Exception { + // String fileContent=FileOperations.readFileContent(new File(DataConstants.DATABASES_FOLDER_LOCATION + databaseName)); + // if(fileContent.contains(tableName)) + // { + // return true; + // } + // else + // { + // fileContent=RemoteVmUtils.readFileContent(DataConstants.DATABASES_FOLDER_LOCATION + databaseName); + // if(fileContent.contains(tableName)) + // { + // return true; + // } + // } + // return false; + // } } diff --git a/src/main/java/com/dal/distributed/queryImpl/DDL.java b/src/main/java/com/dal/distributed/queryImpl/DDL.java deleted file mode 100644 index 1e6ec46..0000000 --- a/src/main/java/com/dal/distributed/queryImpl/DDL.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.dal.distributed.queryImpl; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import com.dal.distributed.constant.AuthConstants; -import com.dal.distributed.main.Main; -import com.dal.distributed.utils.FileOperations; - -public class DDL { - FileOperations fileOperations=new FileOperations(); - public String insertIntoTable(String sql) - { - boolean isTableExist=false; - String[] query = sql.split("\\s+"); - String tablename=query[2]; - File file[]=fileOperations.readFiles(AuthConstants.DATABASES_FOLDER_LOCATION+Main.databaseName); - for(File f:file) - { - if(f.getName().toLowerCase().contains(tablename)) - { - isTableExist=true; - } - } - if(!isTableExist) - return "No table exist"; - else{ - - - return AuthConstants.SUCCESS; - - } - } - - public List selectQuery() - { - List result=new ArrayList<>(); - return result; - } - - public String updateTable(String sql) - { - return AuthConstants.SUCCESS; - } - - public String deleteRow(String sql) - { - return AuthConstants.SUCCESS; - } - -} diff --git a/src/main/java/com/dal/distributed/queryImpl/DatabaseCreation.java b/src/main/java/com/dal/distributed/queryImpl/DatabaseCreation.java deleted file mode 100644 index 4d47bc4..0000000 --- a/src/main/java/com/dal/distributed/queryImpl/DatabaseCreation.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.dal.distributed.queryImpl; - -import java.io.File; -import java.io.IOException; - -import com.dal.distributed.constant.AuthConstants; -import com.dal.distributed.main.Main; -import com.dal.distributed.utils.FileOperations; - -public class DatabaseCreation { - public boolean createDatabase(String sql,String filepath) throws IOException{ - String[] query = sql.split("\\s+"); - if(query[0].toLowerCase().equals("create")&& query[1].toLowerCase().equals("database")){ - String databaseName = query[2]; - File[] databases=FileOperations.readFiles(filepath); - if(databases.length==0) - return false; - for(File file:databases){ - if(file.getName().toLowerCase().equals(databaseName.toLowerCase())){ - System.out.println("Database already exists! Create new one"); - return false; - } - } - FileOperations.createNewFolder(filepath, databaseName); - FileOperations.writeToExistingFile(databaseName+"|","databases.psv", AuthConstants.LOGS_FILE_LOCATION); - return true; - } - else - return false; - -} - - - -public String useDatabase(String sql) throws IOException -{ - String[] query = sql.split("\\s+"); - if(query.length==2&&query[0].toLowerCase().equals("use")){ - String dbName=query[1]; - File f=new File(AuthConstants.LOGS_FILE_LOCATION+"databases.psv"); - String dbNames=FileOperations.readFileContent(f); - if(dbNames.toLowerCase().contains(dbName)) - { - Main.databaseName=dbName; - return AuthConstants.SUCCESS; - } - else - return "No database exist"; -} -else return "Wrong query"; - -} -} \ No newline at end of file diff --git a/src/main/java/com/dal/distributed/queryImpl/DeleteDataFromTable.java b/src/main/java/com/dal/distributed/queryImpl/DeleteDataFromTable.java index 209efe2..e07cd89 100644 --- a/src/main/java/com/dal/distributed/queryImpl/DeleteDataFromTable.java +++ b/src/main/java/com/dal/distributed/queryImpl/DeleteDataFromTable.java @@ -1,45 +1,136 @@ package com.dal.distributed.queryImpl; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.QueryTypes; +import com.dal.distributed.constant.RelationalOperators; +import com.dal.distributed.logger.Logger; import com.dal.distributed.main.Main; +import com.dal.distributed.queryImpl.model.OperationStatus; +import com.dal.distributed.utils.DataUtils; +import com.dal.distributed.utils.DatabaseUtils; import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; + +import java.util.List; public class DeleteDataFromTable { - public boolean execute(String query) throws Exception { - String[] sql = query.split("\\s+"); - String tablename=sql[2]; - String condition=query.substring(query.toLowerCase().indexOf("where")+6,query.indexOf(";")); - String column_name=condition.substring(0,condition.indexOf("=")); - String value=condition.substring(condition.indexOf("=")+1); - String databaseName=Main.databaseName; - int columnIndex=-1; - String filepath=DataConstants.DATABASES_FOLDER_LOCATION+databaseName+"/"+tablename; - List> data=new FileOperations().readDataFromPSV(filepath); - for(int i=0;i> data; + String location = null; + try { + location = DatabaseUtils.getTableLocation(databaseName, tablename); + } catch (IllegalArgumentException ex) { + logger.error("Database does not exist"); + } + if (location == null) { + logger.error("Table does not exist"); + return new OperationStatus(false); + } + if (location.equalsIgnoreCase("local")) { + data = new FileOperations().readDataFromPSV(filepath); + } else { + data = new RemoteVmUtils().readDataFromPSV(filepath); + } + if (data.size() == 1) { + System.out.println("No data present in the table"); + return new OperationStatus(false); + } + int rowLength = data.size(); + int columnLength = data.get(0).size(); + for (int i = 0; i < rowLength; i++) { + isRemoved = false; + for (int j = 0; j < columnLength; j++) { + if (isRemoved) + break; + if (i == 0) + if (data.get(0).get(j).toString().toLowerCase().equals(column_name.toLowerCase())) { + columnIndex = j; break; } - if(data.get(i).get(columnIndex).toString().equals(value)) - { - data.remove(data.get(i)); + try { + switch (relationalOp) { + case RelationalOperators.EQUAL: + if (data.get(i).get(columnIndex).toString().equals(value)) { + data.remove(data.get(i)); + count++; + rowLength -= rowLength; + isRemoved = true; + } + break; + case RelationalOperators.GREATER: + if (Integer.parseInt(data.get(i).get(columnIndex).toString()) > Integer.parseInt(value)) { + data.remove(data.get(i)); + count++; + rowLength -= rowLength; + break; + } + case RelationalOperators.LESS: + if (Integer.parseInt(data.get(i).get(columnIndex).toString()) < Integer.parseInt(value)) { + data.remove(data.get(i)); + count++; + rowLength -= rowLength; + break; + } + case RelationalOperators.GREATEREQUAL: + if (Integer.parseInt(data.get(i).get(columnIndex).toString()) >= Integer.parseInt(value)) { + data.remove(data.get(i)); + count++; + rowLength -= rowLength; + break; + } + case RelationalOperators.LESSEQUAL: + if (Integer.parseInt(data.get(i).get(columnIndex).toString()) <= Integer.parseInt(value)) { + data.remove(data.get(i)); + count++; + rowLength -= rowLength; + break; + } + case RelationalOperators.NOTEQUAL: + case RelationalOperators.NOTEQUAL1: + case RelationalOperators.NOTEQUAL2: + if (Integer.parseInt(data.get(i).get(columnIndex).toString()) != Integer.parseInt(value)) { + data.remove(data.get(i)); + count++; + rowLength -= rowLength; + break; + } + operationStatus = new OperationStatus(true); } - + } catch (NumberFormatException e) { + operationStatus = new OperationStatus(false); } } - new FileOperations().writeDataToPSV(data, filepath); - - return true; + } + if (!Main.isTransaction) { + if (location.equals("local")) + new FileOperations().writeDataToPSV(data, filepath); + else + new RemoteVmUtils().writeDataToPSV(data, filepath); + operationStatus = new OperationStatus(true, data, query, filepath, QueryTypes.UPDATE, tablename, Main.databaseName, count); + } else { + operationStatus = new OperationStatus(true, data, query, filepath, QueryTypes.UPDATE, tablename, Main.databaseName, count); + } + return operationStatus; } } diff --git a/src/main/java/com/dal/distributed/queryImpl/InsertIntoTable.java b/src/main/java/com/dal/distributed/queryImpl/InsertIntoTable.java index 28f316e..366400c 100644 --- a/src/main/java/com/dal/distributed/queryImpl/InsertIntoTable.java +++ b/src/main/java/com/dal/distributed/queryImpl/InsertIntoTable.java @@ -1,7 +1,172 @@ package com.dal.distributed.queryImpl; +import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.QueryRegex; +import com.dal.distributed.constant.QueryTypes; +import com.dal.distributed.logger.Logger; +import com.dal.distributed.main.Main; +import com.dal.distributed.queryImpl.model.OperationStatus; +import com.dal.distributed.utils.DatabaseUtils; +import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + public class InsertIntoTable { - public void execute(String query) { + Logger logger = Logger.instance(); + FileOperations fileOperations = new FileOperations(); + + public OperationStatus execute(String sql) throws Exception { + OperationStatus operationStatus = null; + String[] query = sql.split("\\s+"); + + // If user enters schema.tableName + String[] table = query[2].split("\\."); + String tableName = null; + String databaseName = null; + + // If user has executed "USE databaseName" + if (table.length == 2) { + databaseName = table[0]; + tableName = table[1]; + } else if (!Main.databaseName.isEmpty()) { + databaseName = Main.databaseName; + tableName = table[0]; + } else { + logger.error("No database selected."); + return new OperationStatus(false); + } + + String location = null; + try { + location = DatabaseUtils.getTableLocation(databaseName, tableName); + } catch (IllegalArgumentException ex) { + logger.error("Database does not exist"); + return new OperationStatus(false, databaseName); + } + + List> schema = new ArrayList<>(); + if (null == location) { + logger.error("Table '" + databaseName + "." + query[2] + "' doesn't exist"); + return new OperationStatus(false, databaseName); + } else if (location.equalsIgnoreCase("local")) { + schema = fileOperations.readDataFromPSV( + DataConstants.DATABASES_FOLDER_LOCATION + databaseName + "/" + tableName + "_Schema.psv"); + } else if (location.equalsIgnoreCase("remote")) { + schema = RemoteVmUtils.readDataFromPSV( + DataConstants.DATABASES_FOLDER_LOCATION + databaseName + "/" + tableName + "_Schema.psv"); + } + + String[] values = extractValuesFromQuery(sql); + + if (values.length != schema.size() - 1) { + logger.error("Fields count mismatch: Expected " + (schema.size() - 1) + " fields but received " + + values.length); + return new OperationStatus(false, databaseName); + } + + String tablePath = DataConstants.DATABASES_FOLDER_LOCATION + databaseName + "/" + tableName + ".psv"; + // Primary Key already exists in the database + if (checkForPrimaryKeyConstraint(tablePath, schema, values, location)) { + return new OperationStatus(false, databaseName); + } + + for (int i = 0; i < values.length; i++) { + values[i] = values[i].trim(); + if (schema.get(i + 1).get(1).equals("int")) { + String value; + // store value = 5 instead of "5" + if (values[i].contains("'")) { + Matcher matcher = QueryRegex.valueBetweenQuotes.matcher(values[i]); + value = matcher.group(); + } else { + value = values[i]; + } + try { + Integer.parseInt(value); + } catch (NumberFormatException ex) { + logger.error("Incorrect integer value: '" + values[i] + + "' for column '" + schema.get(i + 1).get(0) + "'"); + return new OperationStatus(false, databaseName); + } + } + } + String finalValue = Arrays.stream(values).collect(Collectors.joining("|")); + + if (!Main.isTransaction) { + if (location.equalsIgnoreCase("local")) { + fileOperations.writeStringToPSV(finalValue, tablePath); + } else { + RemoteVmUtils.writeStringToPSV(finalValue, tablePath); + } + operationStatus = new OperationStatus(true, null, sql, tablePath, QueryTypes.INSERT, tableName, + databaseName, 1); + } else { + List> result = new ArrayList<>(); + List resultVal = new ArrayList(); + resultVal.addAll(Arrays.asList(finalValue.split("|"))); + result.add(resultVal); + operationStatus = new OperationStatus(true, result, sql, tablePath, QueryTypes.INSERT, tableName, + databaseName, 1); + } + + return operationStatus; + } + + private Boolean checkForPrimaryKeyConstraint(String path, List> schema, String[] value, String location) + throws Exception { + String primaryKey = getPrimaryKeyColumnName(schema); + List> existingFile; + if (location.equalsIgnoreCase("local")) { + existingFile = fileOperations.readDataFromPSV(path); + } else { + existingFile = RemoteVmUtils.readDataFromPSV(path); + } + + // Check for primary key location in the file + int primaryKeyIndex = 0; + for (int i = 0; i < existingFile.get(0).size(); i++) { + if (primaryKey.equalsIgnoreCase(existingFile.get(0).get(i).toString())) { + primaryKeyIndex = i; + } + } + + // Check for primary key constraint + for (int i = 1; i < existingFile.size(); i++) { + if (existingFile.get(i).get(primaryKeyIndex).toString().equalsIgnoreCase(value[primaryKeyIndex])) { + logger.error("Duplicate entry '" + value[primaryKeyIndex] + "' for key '" + primaryKey + "'"); + return Boolean.TRUE; + } + } + return Boolean.FALSE; + } + + private String getPrimaryKeyColumnName(List> schema) { + String primaryKey = null; + + String constraint; + for (int i = 1; i < schema.size(); i++) { + if (schema.get(i).size() < 3) + continue; + constraint = (String) schema.get(i).get(2); + if (constraint.equalsIgnoreCase("PRIMARY KEY")) { + primaryKey = schema.get(i).get(0).toString(); + } + } + return primaryKey; + } + + private String[] extractValuesFromQuery(String query) { + String[] values = new String[0]; + Matcher matcher = QueryRegex.insertDataIntoTable.matcher(query); + if (matcher.find()) { + values = matcher.group(4).replaceAll("\"", "").split(","); + } + return values; } } diff --git a/src/main/java/com/dal/distributed/queryImpl/QueryExecutor.java b/src/main/java/com/dal/distributed/queryImpl/QueryExecutor.java deleted file mode 100644 index 1655936..0000000 --- a/src/main/java/com/dal/distributed/queryImpl/QueryExecutor.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.dal.distributed.queryImpl; - -import com.dal.distributed.constant.QueryRegex; -import com.dal.distributed.constant.QueryTypes; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; - -public class QueryExecutor { - public Map validateQuery(String query){ - Map result = new HashMap(){{ - put("isValidate", false); - }}; - Matcher matcher = QueryRegex.createDatabase.matcher(query); - if(matcher.find()){ - result.replace("isValidate", true); - result.put("queryType", QueryTypes.CREATE_DATABASE); - } - - matcher = QueryRegex.useDatabase.matcher(query); - if(matcher.find()){ - result.replace("isValidate", true); - result.put("queryType", QueryTypes.USE); - } - - matcher = QueryRegex.createTable.matcher(query); - if(matcher.find()){ - result.replace("isValidate", true); - result.put("queryType", QueryTypes.CREATE_TABLE); - } - - matcher = QueryRegex.updateTable.matcher(query); - if(matcher.find()){ - result.replace("isValidate", true); - result.put("queryType", QueryTypes.UPDATE); - } - - matcher = QueryRegex.deleteDataInTable.matcher(query); - if(matcher.find()){ - result.replace("isValidate", true); - result.put("queryType", QueryTypes.DELETE); - } - - matcher = QueryRegex.insertDataIntoTable.matcher(query); - if(matcher.find()){ - result.replace("isValidate", true); - result.put("queryType", QueryTypes.INSERT); - } - - matcher = QueryRegex.selectDataFromTable.matcher(query); - if(matcher.find()){ - result.replace("isValidate", true); - result.put("queryType", QueryTypes.SELECT); - } - - return result; - } - - public Map executeQuery(String query){ - // Take care of the logging part - - Map checkQuery = validateQuery(query); - if(checkQuery.get("isValidate")){ - switch(checkQuery.get("queryType")){ - case QueryTypes.CREATE_DATABASE: - // Call - break; - case QueryTypes.USE: - // Call - break; - case QueryTypes.CREATE_TABLE: - // Call - break; - case QueryTypes.UPDATE: - // Call - break; - case QueryTypes.DELETE: - // Call - break; - case QueryTypes.INSERT: - // Call - break; - case QueryTypes.SELECT: - // Call - break; - default: - // return inappropriate message - } - } - - } -} diff --git a/src/main/java/com/dal/distributed/queryImpl/QueryValidator.java b/src/main/java/com/dal/distributed/queryImpl/QueryValidator.java index dd295b0..7df1a7e 100644 --- a/src/main/java/com/dal/distributed/queryImpl/QueryValidator.java +++ b/src/main/java/com/dal/distributed/queryImpl/QueryValidator.java @@ -9,49 +9,72 @@ public class QueryValidator { public Map validateQuery(String query) { - Map result = new HashMap() {{ - put("isValidate", false); - }}; + Map result = new HashMap() { + { + put("isValidate", false); + } + }; Matcher matcher = QueryRegex.createDatabase.matcher(query); if (matcher.find()) { result.replace("isValidate", true); result.put("queryType", QueryTypes.CREATE_DATABASE); + result.put("entity", matcher.group(1)); } matcher = QueryRegex.useDatabase.matcher(query); if (matcher.find()) { result.replace("isValidate", true); result.put("queryType", QueryTypes.USE); + result.put("entity", matcher.group(1)); } matcher = QueryRegex.createTable.matcher(query); if (matcher.find()) { result.replace("isValidate", true); result.put("queryType", QueryTypes.CREATE_TABLE); + result.put("entity", matcher.group(1)); } matcher = QueryRegex.updateTable.matcher(query); if (matcher.find()) { result.replace("isValidate", true); result.put("queryType", QueryTypes.UPDATE); + result.put("entity", matcher.group(1)); } matcher = QueryRegex.deleteDataInTable.matcher(query); if (matcher.find()) { result.replace("isValidate", true); result.put("queryType", QueryTypes.DELETE); + result.put("entity", matcher.group(1)); } matcher = QueryRegex.insertDataIntoTable.matcher(query); if (matcher.find()) { result.replace("isValidate", true); result.put("queryType", QueryTypes.INSERT); + result.put("entity", matcher.group(1)); } matcher = QueryRegex.selectDataFromTable.matcher(query); if (matcher.find()) { result.replace("isValidate", true); result.put("queryType", QueryTypes.SELECT); + result.put("entity", matcher.group(5)); + } + + matcher = QueryRegex.startTransaction.matcher(query); + if (matcher.find()) { + result.replace("isValidate", true); + result.put("queryType", QueryTypes.START_TRANSACTION); + result.put("entity", null); + } + + matcher = QueryRegex.endTransaction.matcher(query); + if (matcher.find()) { + result.replace("isValidate", true); + result.put("queryType", QueryTypes.END_TRANSACTION); + result.put("entity", null); } return result; } diff --git a/src/main/java/com/dal/distributed/queryImpl/SelectQuery.java b/src/main/java/com/dal/distributed/queryImpl/SelectQuery.java index df60800..8e027c0 100644 --- a/src/main/java/com/dal/distributed/queryImpl/SelectQuery.java +++ b/src/main/java/com/dal/distributed/queryImpl/SelectQuery.java @@ -1,14 +1,210 @@ package com.dal.distributed.queryImpl; -import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.MiscConstants; +import com.dal.distributed.constant.QueryRegex; +import com.dal.distributed.constant.QueryTypes; +import com.dal.distributed.constant.RelationalOperators; +import com.dal.distributed.utils.*; +import com.dal.distributed.logger.Logger; +import com.dal.distributed.main.Main; +import com.dal.distributed.queryImpl.model.OperationStatus; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; public class SelectQuery { - public void execute(String query) throws Exception { - ArrayList fileContent = FileOperations.readPsvFileForQueryOps("./usr/dpg9/databases/dbdbdb/Persons.psv"); + Logger logger = Logger.instance(); + public OperationStatus execute(String query) throws Exception { + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + System.out.println("No database selected"); + return new OperationStatus(false); + } + OperationStatus operationStatus = null; + List resultList = new ArrayList(); + List> queryResult = new ArrayList<>(); + Map resultDict; + Matcher matcher = QueryRegex.selectDataFromTable.matcher(query); + if (matcher.find()) { + String tableName = matcher.group(5); + // Query to test: select * from Persons2; + // Uncomment the line when not testing + String filePath = DataConstants.DATABASES_FOLDER_LOCATION + Main.databaseName + "/" + tableName + + DataConstants.FILE_FORMAT; + String location = null; + try { + location = DatabaseUtils.getTableLocation(Main.databaseName, tableName); + } catch (IllegalArgumentException ex) { + logger.error("Database does not exist"); + return new OperationStatus(false, Main.databaseName); + } + ArrayList fileContent = null; + if (null == location) { + logger.error("Table '" + tableName + "' doesn't exist"); + return new OperationStatus(false, Main.databaseName); + } else if (location.equalsIgnoreCase("local")) { + fileContent = FileOperations.readPsvFileForQueryOps(filePath); + } else if (location.equalsIgnoreCase("remote")) { + fileContent = RemoteVmUtils.readPsvFileForQueryOps(filePath); + } + + // ArrayList fileContent = + // FileOperations.readPsvFileForQueryOps(DataConstants.DATABASES_FOLDER_LOCATION + // + "dbdbdb/" + tableName + DataConstants.FILE_FORMAT); + String projections = matcher.group(1); + if (projections == null) + logger.info("Oops.. looks like you did not add projections! Please try again!"); + else { + ArrayList projectionList = new ArrayList(); + projections = projections.trim(); + if (projections.contentEquals("*")) { + Map x = (Map) fileContent.get(0); + projectionList = (ArrayList) x.get("columns"); + } else { + String[] x = projections.split(","); + for (String each : x) { + each = each.trim(); + projectionList.add(each); + } + } + String compareColumn = null, relationOperator = null, value = null, valueType = null; + compareColumn = matcher.group(7); + relationOperator = matcher.group(8); + value = matcher.group(9); + if (value != null) { + value = value.trim(); + valueType = DataUtils.getDataType(value); + if (valueType == "String") { + value = value.substring(value.indexOf("'") + 1, value.lastIndexOf("'")); + value = value.trim(); + } + } + if ((relationOperator != null) && (compareColumn != null) && (value != null)) { + switch (relationOperator) { + case RelationalOperators.EQUAL: + for (int i = 1; i < fileContent.size(); i++) { + Map dataDict = (Map) fileContent.get(i); + if ((dataDict.containsKey(compareColumn)) + && (dataDict.get(compareColumn).equals(value))) + resultList.add(dataDict); + } + break; + case RelationalOperators.GREATER: + for (int i = 1; i < fileContent.size(); i++) { + Map dataDict = (Map) fileContent.get(i); + if (valueType == "int") { + if ((dataDict.containsKey(compareColumn)) && (Integer + .parseInt(dataDict.get(compareColumn)) > Integer.parseInt(value))) + resultList.add(dataDict); + } else + logger.error("I cannot apply " + RelationalOperators.GREATER + + " on datatypes other than int!"); + } + break; + case RelationalOperators.LESS: + for (int i = 1; i < fileContent.size(); i++) { + Map dataDict = (Map) fileContent.get(i); + if (valueType == "int") { + if ((dataDict.containsKey(compareColumn)) && (Integer + .parseInt(dataDict.get(compareColumn)) < Integer.parseInt(value))) + resultList.add(dataDict); + } else + logger.error("I cannot apply " + RelationalOperators.LESS + + " on datatypes other than int!"); + } + break; + case RelationalOperators.GREATEREQUAL: + for (int i = 1; i < fileContent.size(); i++) { + Map dataDict = (Map) fileContent.get(i); + if (valueType == "int") { + if ((dataDict.containsKey(compareColumn)) && (Integer + .parseInt(dataDict.get(compareColumn)) >= Integer.parseInt(value))) + resultList.add(dataDict); + } else + logger.error("I cannot apply " + RelationalOperators.GREATEREQUAL + + " on datatypes other than int!"); + } + break; + case RelationalOperators.LESSEQUAL: + for (int i = 1; i < fileContent.size(); i++) { + Map dataDict = (Map) fileContent.get(i); + if (valueType == "int") { + if ((dataDict.containsKey(compareColumn)) && (Integer + .parseInt(dataDict.get(compareColumn)) >= Integer.parseInt(value))) + resultList.add(dataDict); + } else + logger.error("I cannot apply " + RelationalOperators.LESSEQUAL + + " on datatypes other than int!"); + } + break; + case RelationalOperators.NOTEQUAL: + case RelationalOperators.NOTEQUAL1: + case RelationalOperators.NOTEQUAL2: + for (int i = 1; i < fileContent.size(); i++) { + Map dataDict = (Map) fileContent.get(i); + if (valueType == "int") { + if ((dataDict.containsKey(compareColumn)) && (Integer + .parseInt(dataDict.get(compareColumn)) != Integer.parseInt(value))) + resultList.add(dataDict); + } else { + if ((dataDict.containsKey(compareColumn)) + && (!dataDict.get(compareColumn).equals(value))) + resultList.add(dataDict); + } + } + break; + default: + } + } else { + for (int i = 1; i < fileContent.size(); i++) { + Map dataDict = (Map) fileContent.get(i); + resultList.add(dataDict); + } + } + List mapToList = new ArrayList<>(); + resultList = this.filterProjectionsForOutput(resultList, projectionList); + int c = 0; + for (Map m : resultList) { + if (c == 0) { + for (Map.Entry mp : m.entrySet()) { + mapToList.add(mp.getKey()); + } + queryResult.add(mapToList); + } + mapToList = new ArrayList<>(); + c += 1; + for (Map.Entry mp : m.entrySet()) { + mapToList.add(mp.getValue()); + } + queryResult.add(mapToList); + } + operationStatus = new OperationStatus(true, queryResult, query, filePath, QueryTypes.SELECT, tableName, + Main.databaseName, queryResult.size()); + if (!Main.isTransaction) + Results.printResult(queryResult); + } + } + return operationStatus; + } + + private ArrayList filterProjectionsForOutput(List resultList, ArrayList projectionList) { + ArrayList result = new ArrayList(); + for (int i = 0; i < resultList.size(); i++) { + Map dataDict = (Map) resultList.get(i); + Map resultDict = new HashMap(); + for (Object eachKey : dataDict.keySet()) { + if (projectionList.contains((String) eachKey)) + resultDict.put((String) eachKey, dataDict.get((String) eachKey)); + } + if (resultDict != null) + result.add(resultDict); + } + return result; } -} +} \ No newline at end of file diff --git a/src/main/java/com/dal/distributed/queryImpl/UpdateTable.java b/src/main/java/com/dal/distributed/queryImpl/UpdateTable.java index c978db1..fa087ce 100644 --- a/src/main/java/com/dal/distributed/queryImpl/UpdateTable.java +++ b/src/main/java/com/dal/distributed/queryImpl/UpdateTable.java @@ -3,24 +3,65 @@ import java.util.List; import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.QueryTypes; +import com.dal.distributed.constant.RelationalOperators; +import com.dal.distributed.logger.Logger; import com.dal.distributed.main.Main; +import com.dal.distributed.queryImpl.model.OperationStatus; +import com.dal.distributed.utils.DataUtils; +import com.dal.distributed.utils.DatabaseUtils; import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; public class UpdateTable { - public boolean execute(String query) { + private String relationalOp; + + public OperationStatus execute(String query) throws Exception { + Logger logger = Logger.instance(); + + if (Main.databaseName == null || Main.databaseName.isEmpty()) { + System.out.println("No database selected"); + return null; + } + int count = 0; + OperationStatus operationStatus = null; + relationalOp = DataUtils.checkRelationalOperator(query); String[] sql = query.split("\\s+"); String tableName = sql[1]; String updateStatement = query.substring(query.toLowerCase().indexOf("set") + 4); - String updateColumn = updateStatement.substring(0, updateStatement.indexOf("=")); - String updateValue = updateStatement.substring(updateStatement.indexOf("=") + 1, updateStatement.indexOf("where") - 1); - String condition = query.substring(query.toLowerCase().indexOf("where") + 6,query.indexOf(";")); - String column_name = condition.substring(0, condition.indexOf("=")); - String value = condition.substring(condition.indexOf("=") + 1); + String updateColumn = updateStatement.substring(0, updateStatement.indexOf(relationalOp)); + String updateValue = updateStatement.substring(updateStatement.indexOf(relationalOp) + 1, + updateStatement.indexOf("where") - 1); + String condition = query.substring(query.toLowerCase().indexOf("where") + 6, query.indexOf(";")); + String column_name = condition.substring(0, condition.indexOf(relationalOp)); + String value = condition.substring(condition.indexOf(relationalOp) + 1); + while (updateValue.contains("\'")) { + updateValue = updateValue.replace("\'", ""); + } String databaseName = Main.databaseName; int conditionColumnIndex = -1; int updateColumnIndex = -1; + String location = null; + try { + location = DatabaseUtils.getTableLocation(databaseName, tableName); + } catch (IllegalArgumentException ex) { + logger.error("Database does not exist"); + } + if (location == null) { + logger.error("Table does not exist"); + return new OperationStatus(false); + } String filepath = DataConstants.DATABASES_FOLDER_LOCATION + databaseName + "/" + tableName; - List> data = new FileOperations().readDataFromPSV(filepath); + List> data = null; + if (location.equals("local")) { + data = new FileOperations().readDataFromPSV(filepath); + } else if (location.equalsIgnoreCase("remote")) { + data = RemoteVmUtils.readDataFromPSV(filepath); + } + if (data.size() == 1) { + System.out.println("No data present in the table"); + return new OperationStatus(false); + } for (int i = 0; i < data.size(); i++) { for (int j = 0; j < data.get(0).size(); j++) { if (i == 0) { @@ -31,14 +72,70 @@ public boolean execute(String query) { updateColumnIndex = j; } } - if (data.get(i).get(conditionColumnIndex).toString().equals(value)) { - data.get(i).set(updateColumnIndex, updateValue); + try { + switch (relationalOp) { + case RelationalOperators.EQUAL: + if (data.get(i).get(conditionColumnIndex).toString().equals(value)) { + data.get(i).set(updateColumnIndex, updateValue); + count++; + } + break; + case RelationalOperators.GREATER: + if (Integer.parseInt(data.get(i).get(conditionColumnIndex).toString()) > Integer + .parseInt(value)) { + data.get(i).set(updateColumnIndex, updateValue); + count++; + break; + } + case RelationalOperators.LESS: + if (Integer.parseInt(data.get(i).get(conditionColumnIndex).toString()) < Integer + .parseInt(value)) { + data.get(i).set(updateColumnIndex, updateValue); + count++; + break; + } + case RelationalOperators.GREATEREQUAL: + if (Integer.parseInt(data.get(i).get(conditionColumnIndex).toString()) >= Integer + .parseInt(value)) { + data.get(i).set(updateColumnIndex, updateValue); + count++; + break; + } + case RelationalOperators.LESSEQUAL: + if (Integer.parseInt(data.get(i).get(conditionColumnIndex).toString()) <= Integer + .parseInt(value)) { + data.get(i).set(updateColumnIndex, updateValue); + count++; + break; + } + case RelationalOperators.NOTEQUAL: + case RelationalOperators.NOTEQUAL1: + case RelationalOperators.NOTEQUAL2: + if (Integer.parseInt(data.get(i).get(conditionColumnIndex).toString()) != Integer + .parseInt(value)) { + data.get(i).set(updateColumnIndex, updateValue); + count++; + break; + } + } + operationStatus = new OperationStatus(true); + } catch (NumberFormatException e) { + operationStatus = new OperationStatus(false); } - } } - new FileOperations().writeDataToPSV(data, filepath); - - return true; + if (!Main.isTransaction) { + if (location.equalsIgnoreCase("local")) { + FileOperations.writeDataToPSV(data, filepath); + } else if (location.equalsIgnoreCase("remote")) { + RemoteVmUtils.writeDataToPSV(data, filepath); + } + operationStatus = new OperationStatus(true, data, query, filepath, QueryTypes.UPDATE, tableName, + Main.databaseName, count); + } else { + operationStatus = new OperationStatus(true, data, query, filepath, QueryTypes.UPDATE, tableName, + Main.databaseName, count); + } + return operationStatus; } } diff --git a/src/main/java/com/dal/distributed/queryImpl/UseDatabase.java b/src/main/java/com/dal/distributed/queryImpl/UseDatabase.java index 93a4551..34dd4e2 100644 --- a/src/main/java/com/dal/distributed/queryImpl/UseDatabase.java +++ b/src/main/java/com/dal/distributed/queryImpl/UseDatabase.java @@ -8,21 +8,29 @@ import com.dal.distributed.utils.FileOperations; public class UseDatabase { - public boolean execute(String query) throws IOException - { - String[] sql = query.split("\\s+"); - if(sql.length==2&&sql[0].toLowerCase().equals("use")){ - String dbName=sql[1]; - File f=new File(DataConstants.LOGS_FILE_LOCATION+"databases.psv"); - String dbNames=FileOperations.readFileContent(f); - if(dbNames.toLowerCase().contains(dbName)) - { - Main.databaseName=dbName; - return true; - } - else - return false; -} -else return true; + public boolean execute(String query) throws IOException { + String[] sql = query.split("\\s+"); + if (sql.length == 2 && sql[0].equalsIgnoreCase("use")) { + String dbName = sql[1].substring(0, sql[1].indexOf(";")); + File[] databases = FileOperations.readFiles(DataConstants.DATABASES_FOLDER_LOCATION); + boolean isExist=false; + for (File file : databases) { + if (file.getName().equalsIgnoreCase(dbName)) { + isExist=true; + } + } + if(isExist){ + Main.databaseName=dbName; + return true; + } + // File f = new File(DataConstants.LOGS_FILE_LOCATION + "databases.psv"); + // String dbNames = FileOperations.readFileContent(f); + // if (dbNames.toLowerCase().contains(dbName)) { + // Main.databaseName = dbName; + // return true; + // } + else + return false; + } else return true; } } diff --git a/src/main/java/com/dal/distributed/queryImpl/model/OperationStatus.java b/src/main/java/com/dal/distributed/queryImpl/model/OperationStatus.java new file mode 100644 index 0000000..adbf724 --- /dev/null +++ b/src/main/java/com/dal/distributed/queryImpl/model/OperationStatus.java @@ -0,0 +1,109 @@ +package com.dal.distributed.queryImpl.model; + +import java.util.List; + +public class OperationStatus { + private boolean status; + private List> result; + private String query; + private String filePath; + private String queryType; + private String tableName; + private boolean isRepeatTable; + private String databaseName; + private int count; + + public OperationStatus(boolean status, List> result, String query, String filePath, String queryType, + String tableName, String databaseName, int count) { + this.status = status; + this.result = result; + this.query = query; + this.filePath = filePath; + this.queryType = queryType; + this.tableName = tableName; + this.databaseName = databaseName; + this.count = count; + } + + public OperationStatus(boolean status) { + this.status = status; + } + + public OperationStatus(boolean status, String databaseName) { + this.status = status; + this.databaseName = databaseName; + } + + public String getQueryType() { + return queryType; + } + + public void setQueryType(String queryType) { + this.queryType = queryType; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public List> getResult() { + return result; + } + + public void setResult(List> result) { + this.result = result; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public boolean isRepeatTable() { + return isRepeatTable; + } + + public void setRepeatTable(boolean isRepeatTable) { + this.isRepeatTable = isRepeatTable; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + +} diff --git a/src/main/java/com/dal/distributed/queryImpl/model/QueryLog.java b/src/main/java/com/dal/distributed/queryImpl/model/QueryLog.java index abf89e2..6dcf4df 100644 --- a/src/main/java/com/dal/distributed/queryImpl/model/QueryLog.java +++ b/src/main/java/com/dal/distributed/queryImpl/model/QueryLog.java @@ -3,9 +3,11 @@ public class QueryLog { private String flag; private String query; + private String operation; private String submissionTimestamp; private String submittedBy; private String tableName; + private String databaseName; public String getFlag() { return flag; @@ -23,6 +25,14 @@ public void setQuery(String query) { this.query = query; } + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + public String getSubmissionTimestamp() { return submissionTimestamp; } @@ -47,15 +57,25 @@ public void setTableName(String tableName) { this.tableName = tableName; } + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + @Override public String toString() { return ",\n" + "\"QueryLog\": {\n" + "\t\"flag\": \"" + flag + "\",\n" + "\t\"query\": \"" + query + "\",\n" + - "\t\"submissionTimestamp\": \"" + submissionTimestamp + "\"\n" + - "\t\"submittedBy\": \"" + submittedBy + "\"\n" + - "\t\"tableName\": \"" + tableName + "\"\n" + + "\t\"operation\": \"" + operation + "\",\n" + + "\t\"submissionTimestamp\": \"" + submissionTimestamp + "\",\n" + + "\t\"submittedBy\": \"" + submittedBy + "\",\n" + + "\t\"tableName\": \"" + tableName + "\",\n" + + "\t\"databaseName\": \"" + databaseName + "\"\n" + "}"; } } diff --git a/src/main/java/com/dal/distributed/utils/DataUtils.java b/src/main/java/com/dal/distributed/utils/DataUtils.java new file mode 100644 index 0000000..8b366d0 --- /dev/null +++ b/src/main/java/com/dal/distributed/utils/DataUtils.java @@ -0,0 +1,40 @@ +package com.dal.distributed.utils; + +import com.dal.distributed.constant.QueryRegex; +import com.dal.distributed.constant.RelationalOperators; + +import java.util.regex.Matcher; + +public class DataUtils { + public static String getDataType(String text){ + Matcher matcher = QueryRegex.digitOnlyRegex.matcher(text); + if(matcher.find()) + return "int"; + else + return "String"; + } + + public static String checkRelationalOperator(String query) + { + String relationalOp; + if(query.contains(RelationalOperators.GREATER)) + relationalOp=RelationalOperators.GREATER; + else if(query.contains(RelationalOperators.GREATEREQUAL)) + relationalOp=RelationalOperators.GREATEREQUAL; + else if(query.contains(RelationalOperators.LESS)) + relationalOp=RelationalOperators.LESS; + else if(query.contains(RelationalOperators.LESSEQUAL)) + relationalOp=RelationalOperators.LESSEQUAL; + else if(query.contains(RelationalOperators.NOTEQUAL)) + relationalOp=RelationalOperators.NOTEQUAL; + else if(query.contains(RelationalOperators.NOTEQUAL1)) + relationalOp=RelationalOperators.NOTEQUAL1; + else if(query.contains(RelationalOperators.NOTEQUAL2)) + relationalOp=RelationalOperators.NOTEQUAL2; + else if(query.contains(RelationalOperators.EQUAL)) + relationalOp=RelationalOperators.EQUAL; + else + relationalOp=null; + return relationalOp; + } +} diff --git a/src/main/java/com/dal/distributed/utils/DatabaseUtils.java b/src/main/java/com/dal/distributed/utils/DatabaseUtils.java new file mode 100644 index 0000000..ae6cb83 --- /dev/null +++ b/src/main/java/com/dal/distributed/utils/DatabaseUtils.java @@ -0,0 +1,135 @@ +package com.dal.distributed.utils; + +import com.dal.distributed.constant.DataConstants; +import com.dal.distributed.constant.MiscConstants; +import com.dal.distributed.constant.VMConstants; +import com.dal.distributed.main.model.Table; + +import javax.xml.crypto.Data; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +public class DatabaseUtils { + + private static final String SCHEMA_FILE_SUFFIX = "_Schema.psv"; + + private static final String DATA_FILE_SUFFIX = ".psv"; + + private static final String META_DATA_FILE_SUFFIX = ".psv"; + + public static List getTableSchemaFiles(String database) { + String databaseFolder = DataConstants.DATABASES_FOLDER_LOCATION + File.separator + database; + File[] databaseFiles = FileOperations.readFiles(databaseFolder); + if (databaseFiles.length == 1) + return Collections.emptyList(); + List schemaFiles = new ArrayList<>(); + for (File databaseFile : databaseFiles) { + if (databaseFile.getName().endsWith(SCHEMA_FILE_SUFFIX)) + schemaFiles.add(databaseFile); + } + return schemaFiles; + } + + public static List getColumnDefinitions(String database, File tableSchemaFile) { + try (FileReader fr = new FileReader(tableSchemaFile); + BufferedReader br = new BufferedReader(fr)) { + //The buffered reader will now point after header row + br.readLine(); + List columnDefinitions = new ArrayList<>(); + String colDefinition; + while ((colDefinition = br.readLine()) != null) { + columnDefinitions.add(colDefinition); + } + return columnDefinitions; + } catch (IOException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + public String getTableNameFromFileName(String tableFileName) { + return tableFileName.split("_")[0]; + } + + public static File getDataFileForTable(String database, String tableName) { + String dataFilePath = DataConstants.DATABASES_FOLDER_LOCATION + File.separator + database + File.separator + tableName + DATA_FILE_SUFFIX; + return new File(dataFilePath); + } + + public static String getDataFilePathFromTable(String database, String tableName) { + String dataFilePath = DataConstants.DATABASES_FOLDER_LOCATION + database + File.separator + tableName + DATA_FILE_SUFFIX; + return dataFilePath; + } + + public static List getColumnNames(File tableDataFile) { + try (FileReader fr = new FileReader(tableDataFile); + BufferedReader br = new BufferedReader(fr);) { + return Arrays.asList(br.readLine().split(MiscConstants.PIPE)); + } catch (IOException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + public static String getTableLocation(String databaseName, String tableName) { + String metaDataFileLocation = DataConstants.DATABASES_FOLDER_LOCATION + databaseName + META_DATA_FILE_SUFFIX; + File metaDataFile = new File(metaDataFileLocation); + if (!metaDataFile.exists()) + throw new IllegalArgumentException("Database doesn't exist"); + try (FileReader fr = new FileReader(metaDataFile); + BufferedReader br = new BufferedReader(fr)) { + //Buffered read will point after header row + br.readLine(); + String tableInfo; + while ((tableInfo = br.readLine()) != null) { + String[] tableInfoArr = tableInfo.split(MiscConstants.PIPE); + if (tableInfoArr[0].equalsIgnoreCase(tableName)) { + return tableInfoArr[1]; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + //throw new IllegalArgumentException("table name doesn't exist in the database"); + return null; + } + + public static Map getTableNames(String databaseName) { + String metaDataFileLocation = DataConstants.DATABASES_FOLDER_LOCATION + databaseName + META_DATA_FILE_SUFFIX; + File metaDataFile = new File(metaDataFileLocation); + if (!metaDataFile.exists()) + throw new IllegalArgumentException("Database doesn't exist"); + Map tableNameToLocation = new HashMap<>(); + try (FileReader fr = new FileReader(metaDataFile); + BufferedReader br = new BufferedReader(fr)) { + //Buffered read will point after header row + br.readLine(); + String tableInfo; + while ((tableInfo = br.readLine()) != null) { + String[] tableInfoArr = tableInfo.split(MiscConstants.PIPE); + tableNameToLocation.put(tableInfoArr[0], tableInfoArr[1]); + } + } catch (IOException e) { + e.printStackTrace(); + } + return tableNameToLocation; + } + + public static List
getRemoteTables(String databaseName) throws Exception { + Map tableNameToLocation = getTableNames(databaseName); + List remoteTableNames = tableNameToLocation.entrySet().stream() + .filter(x -> VMConstants.REMOTE.equals(x.getValue())).map(x -> x.getKey()).collect(Collectors.toList()); + List
remoteTables = new ArrayList<>(); + for (String tableName : remoteTableNames) { + String tableSchema = RemoteVmUtils.readFileContent(VMConstants.projectPath + DataConstants.DATABASES_FOLDER_LOCATION + databaseName + File.separator + tableName + SCHEMA_FILE_SUFFIX); + List columnStrWithHeaders = Arrays.asList(tableSchema.split("\n")); + List columnStr = columnStrWithHeaders.subList(1, columnStrWithHeaders.size()); + remoteTables.add(Table.createTableModel(tableName, databaseName, columnStr)); + } + return remoteTables; + } +} diff --git a/src/main/java/com/dal/distributed/utils/FileOperations.java b/src/main/java/com/dal/distributed/utils/FileOperations.java index cd6b365..99f9e97 100644 --- a/src/main/java/com/dal/distributed/utils/FileOperations.java +++ b/src/main/java/com/dal/distributed/utils/FileOperations.java @@ -7,14 +7,7 @@ import com.dal.distributed.constant.MiscConstants; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; +import java.io.*; import java.util.*; @@ -23,6 +16,19 @@ public class FileOperations { static PrintWriter printWriter; // Read files from the directory + public static List getColumnDefinitions(File table) { + try (FileReader fr = new FileReader(table); + BufferedReader br = new BufferedReader(fr)){ + String columnDefLine = br.readLine(); + return Arrays.asList(columnDefLine.split(MiscConstants.PIPE)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + /** * @param dir * @return @@ -79,10 +85,9 @@ public static void writeToNewFile(String fileContent, String filename, String fi public static void writeToExistingFile(String fileContent, String filename, String fileDirectory) { try{ - - File file =new File(fileDirectory+filename); - if(!file.exists()){ - file.createNewFile(); + File file = new File(fileDirectory + filename); + if (!file.exists()) { + file.createNewFile(); } FileWriter fw = new FileWriter(file,true); BufferedWriter bw = new BufferedWriter(fw); @@ -99,41 +104,41 @@ public static void writeToExistingFile(String fileContent, String filename, Stri * @param filepath * @param filename * @return - * @throws IOException */ - public static boolean createNewFile(String filepath, String filename) throws IOException { + public static boolean createNewFile(String filepath, String filename) { boolean createStatus = false; try { - File f = new File(filepath + "/" + filename + ".psv"); - f.createNewFile(); - createStatus = true; + File file = new File(filepath + filename); + if (!file.exists()) { + file.createNewFile(); + } } catch (Exception e) { e.printStackTrace(); } return createStatus; } + private static boolean dotInFileName(String fileName) { + if (fileName == null || fileName.isEmpty()) + return false; + return fileName.contains("."); + } + /** + * * @param filepath * @param folderName * @return - * @throws IOException */ - public static boolean createNewFolder(String filepath, String folderName) throws IOException { - boolean createStatus = false; + public static boolean createNewFolder(String filepath, String folderName) { StringBuilder sb = new StringBuilder(); if (filepath != null) sb.append(filepath).append("/"); if (folderName != null) sb.append(folderName); - try { - File f = new File(sb.toString()); - f.mkdir(); - createStatus = true; - } catch (Exception e) { - e.printStackTrace(); - } - return createStatus; + File f=new File(sb.toString()); + f.mkdir(); + return true; } /** @@ -141,7 +146,7 @@ public static boolean createNewFolder(String filepath, String folderName) throws * @return * @throws IOException */ - public static boolean createNewFolderRecursively(String filePath) throws IOException { + public static boolean createNewFolderRecursively(String filePath) { boolean createStatus = false; String[] folders = filePath.split("/"); if (folders.length >= 2) { @@ -162,9 +167,17 @@ public static boolean createNewFolderRecursively(String filePath) throws IOExcep * @param text * @return */ - public static String[] getArrayForPipeString(String text) { - if (text != null) - return text.split(MiscConstants.PIPE); + public static ArrayList getArrayForPipeString(String text) { + if (text != null){ + ArrayList result = new ArrayList(); + String[] splittedText = text.split(MiscConstants.PIPE); + for(String each:splittedText){ + each = each.trim(); + result.add(each); + } + return result; + } + else return null; } @@ -174,9 +187,9 @@ public static String[] getArrayForPipeString(String text) { * @return ArrayList - first element is {"columns" : []}, second onwards - Map<> * @throws Exception */ - public static ArrayList> readPsvFileForQueryOps(String filePath) throws Exception { + public static ArrayList> readPsvFileForQueryOps(String filePath) throws FileNotFoundException { ArrayList result = new ArrayList(); - String[] columns = new String[0]; + ArrayList columns = new ArrayList(); File fileObject = new File(filePath); Scanner sc = new Scanner(fileObject); int count = 0; @@ -186,22 +199,22 @@ public static ArrayList> readPsvFileForQueryOps(String fileP if (columns == null) break; else { - String[] finalColumns = columns; + ArrayList finalCols = columns; Map dataDict = new HashMap() {{ - put("columns", Arrays.asList(finalColumns)); + put("columns", finalCols); }}; result.add(dataDict); count++; } } else { - if (columns.length == 0) + if (columns.size() == 0) break; else { - String[] rowData = getArrayForPipeString(sc.nextLine()); + ArrayList rowData = getArrayForPipeString(sc.nextLine()); if (rowData != null) { Map dataDict = new HashMap(); - for (int i = 0; i < columns.length; i++) { - dataDict.put(columns[i], rowData[i]); + for (int i = 0; i < columns.size(); i++) { + dataDict.put(columns.get(i), rowData.get(i)); } result.add(dataDict); } @@ -215,8 +228,15 @@ public static ArrayList> readPsvFileForQueryOps(String fileP public List> readDataFromPSV(String filePath) { List> rows = new ArrayList<>(); - List columnValues = new ArrayList<>(); - File file = new File(filePath+".psv"); + List columnValues; + + String path; + if (filePath.contains(".psv")) { + path = filePath; + } else { + path = filePath + ".psv"; + } + File file = new File(path); try (BufferedReader br = new BufferedReader(new FileReader(file))) { String line = ""; while ((line = br.readLine()) != null) { @@ -229,7 +249,7 @@ public List> readDataFromPSV(String filePath) { return rows; } - public void writeDataToPSV(List> rows, String filePath) { + public static void writeDataToPSV(List> rows, String filePath) { FileWriter psvWriter = null; try { psvWriter = new FileWriter(filePath+".psv"); @@ -243,4 +263,17 @@ public void writeDataToPSV(List> rows, String filePath) { e.getCause(); } } + + public void writeStringToPSV(String row, String filePath) { + FileWriter psvWriter = null; + try { + psvWriter = new FileWriter(filePath, true); + psvWriter.append(row); + psvWriter.append("\n"); + psvWriter.flush(); + psvWriter.close(); + } catch (Exception e) { + e.getCause(); + } + } } diff --git a/src/main/java/com/dal/distributed/utils/RemoteVmUtils.java b/src/main/java/com/dal/distributed/utils/RemoteVmUtils.java new file mode 100644 index 0000000..8f45c69 --- /dev/null +++ b/src/main/java/com/dal/distributed/utils/RemoteVmUtils.java @@ -0,0 +1,305 @@ +package com.dal.distributed.utils; + +import com.dal.distributed.constant.MiscConstants; +import com.dal.distributed.constant.VMConstants; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; + +import java.io.*; +import java.util.*; + +public class RemoteVmUtils { + + /** + * + * @param command + * @return + * @throws Exception + */ + public static String getOutput(String command) throws Exception { + Session session = null; + ChannelExec channel = null; + String responseString = null; + try { + JSch jSch = new JSch(); + jSch.addIdentity(VMConstants.PRIVATE_KEY); + session = jSch.getSession(VMConstants.USERNAME, VMConstants.EXTERNAL_IP, VMConstants.port); + session.setConfig("StrictHostKeyChecking", "no"); + session.connect(); + + channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(command); + ByteArrayOutputStream responseStream = new ByteArrayOutputStream(); + channel.setOutputStream(responseStream); + channel.connect(); + + while (channel.isConnected()) { + Thread.sleep(100); + } + + responseString = new String(responseStream.toByteArray()); + } finally { + if (session != null) { + session.disconnect(); + } + if (channel != null) { + channel.disconnect(); + } + } + return responseString; + } + + /** + * + * @param command + * @return + * @throws Exception + */ + public static String runCommand(String command) throws Exception { + Session session = null; + ChannelExec channel = null; + String responseString = null; + try { + JSch jSch = new JSch(); + jSch.addIdentity(VMConstants.PRIVATE_KEY); + session = jSch.getSession(VMConstants.USERNAME, VMConstants.EXTERNAL_IP, VMConstants.port); + session.setConfig("StrictHostKeyChecking", "no"); + session.connect(); + + channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(command); + ByteArrayOutputStream responseStream = new ByteArrayOutputStream(); + channel.setOutputStream(responseStream); + channel.connect(); + + while (channel.isConnected()) { + Thread.sleep(100); + } + + responseString = new String(responseStream.toByteArray()); + } finally { + if (session != null) { + session.disconnect(); + } + if (channel != null) { + channel.disconnect(); + } + } + return responseString; + } + + /** + * + * @param dir + * @return + * { + * "files" : [], + * "folders" : [] + * } + * eg: {folders=[chanpreet], files=[a.txt, b.txt, c.txt]} + * @throws Exception + */ + public static Map readFiles(String dir) throws Exception { + dir = VMConstants.projectPath + dir; + String commandResult = getOutput("ls -al " + dir); + ArrayList folders = new ArrayList(); + ArrayList files = new ArrayList(); + Map result = new HashMap(){{ + put("files", files); + put("folders", folders); + }}; + if(commandResult!=null){ + String[] outputLines = commandResult.split("\\n"); + for(String eachLine:outputLines){ + if((eachLine.startsWith("d")) || (eachLine.startsWith("-"))){ + String entityName = extractNameFromLS(eachLine); + if((!entityName.contentEquals(".")) && (!entityName.contentEquals(".."))){ + if(eachLine.startsWith("d")) + folders.add(entityName); + else + files.add(entityName); + } + } + } + if(!files.isEmpty()) + result.replace("files", files); + if(!folders.isEmpty()) + result.replace("folders", folders); + } + return result; + } + + /** + * + * @param text + * @return + */ + public static String extractNameFromLS(String text){ + String[] splittedText = text.split("\\s"); + return splittedText[splittedText.length - 1]; + } + + /** + * + * @param filePath + * @return + * @throws Exception + */ + public static String readFileContent(String filePath) throws Exception { + return getOutput("cat " + filePath); + } + + /** + * + * @param fileContent + * @param filename + * @param fileDirectory + * @throws Exception + */ + public static void writeToExistingFile(String fileContent, String filename, String fileDirectory) throws Exception { + fileDirectory = VMConstants.projectPath + fileDirectory; + String command = "echo \"" + fileContent + "\" >> " + fileDirectory + filename; + runCommand(command); + } + + /** + * + * @param filepath + * @param folderName + * @return + * @throws Exception + */ + public static boolean createNewFolder(String filepath, String folderName) throws Exception { + filepath = VMConstants.projectPath + filepath; + StringBuilder sb = new StringBuilder(); + if (filepath != null) + sb.append(filepath).append("/"); + if (folderName != null) + sb.append(folderName); + runCommand("mkdir " + sb.toString()); + return true; + } + + /** + * @param text + * @return + */ + public static ArrayList getArrayForPipeString(String text) { + if (text != null){ + ArrayList result = new ArrayList(); + String[] splittedText = text.split(MiscConstants.PIPE); + for(String each:splittedText){ + each = each.trim(); + result.add(each); + } + return result; + } + + else + return null; + } + + /** + * @param filePath + * @return ArrayList - first element is {"columns" : []}, second onwards - Map<> + * @throws Exception + */ + public static ArrayList> readPsvFileForQueryOps(String filePath) throws Exception { + filePath = VMConstants.projectPath + filePath; + ArrayList result = new ArrayList(); + ArrayList columns = new ArrayList(); + String fileContent = readFileContent(filePath); + String[] lines = fileContent.split("\\n"); + int count = 0; + while (count< lines.length) { + if (count == 0) { + columns = getArrayForPipeString(lines[count]); + if (columns == null) + break; + else { + ArrayList finalCols = columns; + Map dataDict = new HashMap() {{ + put("columns", finalCols); + }}; + result.add(dataDict); + count++; + } + } else { + if (columns.size() == 0) + break; + else { + ArrayList rowData = getArrayForPipeString(lines[count]); + if (rowData != null) { + Map dataDict = new HashMap(); + for (int i = 0; i < columns.size(); i++) { + dataDict.put(columns.get(i), rowData.get(i)); + } + result.add(dataDict); + } + } + } + count++; + } + return result; + } + + /** + * + * @param filePath + * @return + * @throws Exception + */ + public static List> readDataFromPSV(String filePath) throws Exception { + filePath = VMConstants.projectPath + filePath; + List> rows = new ArrayList<>(); + List columnValues; + + String path; + if (filePath.contains(".psv")) { + path = filePath; + } else { + path = filePath + ".psv"; + } + String fileContent = readFileContent(path); + String[] lines = fileContent.split("\\n"); + int count = 0; + while(count(Arrays.asList(line.split(MiscConstants.PIPE, -1))); + rows.add(columnValues); + count++; + } + return rows; + } + + /** + * + * @param rows + * @param filePath + */ + public static void writeDataToPSV(List> rows, String filePath) { + filePath = VMConstants.projectPath + filePath; + StringBuilder sb = new StringBuilder(); + try { + filePath = filePath + ".psv"; + System.out.println("FilePath: " + filePath); + for (List rowData : rows) + sb.append(rowData.toString().replace("[", "").replace("]", "").replaceAll(",", "|")).append("\n"); + runCommand("echo \"" + sb.toString() + "\" > " + filePath); + } catch (Exception e) { + e.getCause(); + } + } + + /** + * + * @param row + * @param filePath + * @throws Exception + */ + public static void writeStringToPSV(String row, String filePath) throws Exception { + filePath = VMConstants.projectPath + filePath; + runCommand("echo \"" + row + "\" >> " + filePath); + } +} \ No newline at end of file diff --git a/src/main/java/com/dal/distributed/utils/Results.java b/src/main/java/com/dal/distributed/utils/Results.java new file mode 100644 index 0000000..ee4dacb --- /dev/null +++ b/src/main/java/com/dal/distributed/utils/Results.java @@ -0,0 +1,16 @@ +package com.dal.distributed.utils; + +import java.util.List; + +public class Results { + + public static void printResult(List> resultSet) { + for (List resultVal : resultSet) { + for (Object result : resultVal) { + System.out.format("|%-25s", result.toString()); + } + System.out.println(); + } + System.out.println("\n"); + } +} diff --git a/src/main/java/transactionProcessing/TransactionProcessing.java b/src/main/java/transactionProcessing/TransactionProcessing.java new file mode 100644 index 0000000..1451e94 --- /dev/null +++ b/src/main/java/transactionProcessing/TransactionProcessing.java @@ -0,0 +1,87 @@ +package transactionProcessing; + +import java.util.List; + +import com.dal.distributed.constant.QueryTypes; +import com.dal.distributed.logger.Logger; +import com.dal.distributed.main.Main; +import com.dal.distributed.queryImpl.DeleteDataFromTable; +import com.dal.distributed.queryImpl.InsertIntoTable; +import com.dal.distributed.queryImpl.SelectQuery; +import com.dal.distributed.queryImpl.UpdateTable; +import com.dal.distributed.queryImpl.model.OperationStatus; +import com.dal.distributed.utils.DataUtils; +import com.dal.distributed.utils.DatabaseUtils; +import com.dal.distributed.utils.FileOperations; +import com.dal.distributed.utils.RemoteVmUtils; +import com.dal.distributed.utils.Results; + +public class TransactionProcessing { + FileOperations fileOperations = new FileOperations(); + OperationStatus oStatus = null; + Logger logger = Logger.instance(); + + public boolean execute(List listTransactionQueries) throws Exception { + + Main.isTransaction = true; + for (int i = 0; i < listTransactionQueries.size(); i++) { + String location = null; + try { + location = DatabaseUtils.getTableLocation(listTransactionQueries.get(i).getDatabaseName(), listTransactionQueries.get(i).getTableName()); + } catch (IllegalArgumentException ex) { + logger.error("Database does not exist"); + } + if (location == null) { + logger.error("Table does not exist"); + return false; + } + if (listTransactionQueries.get(i).getQueryType().equals(QueryTypes.DELETE)) { + if (listTransactionQueries.get(i).isRepeatTable()) { + oStatus = new DeleteDataFromTable().execute(listTransactionQueries.get(i).getQuery()); + listTransactionQueries.set(i, oStatus); + + } + if (location.equals("local")) + fileOperations.writeDataToPSV(listTransactionQueries.get(i).getResult(), listTransactionQueries.get(i).getFilePath()); + else + new RemoteVmUtils().writeDataToPSV(listTransactionQueries.get(i).getResult(), listTransactionQueries.get(i).getFilePath()); + + } else if (listTransactionQueries.get(i).getQueryType().equals(QueryTypes.UPDATE)) { + if (listTransactionQueries.get(i).isRepeatTable()) { + oStatus = new UpdateTable().execute(listTransactionQueries.get(i).getQuery()); + listTransactionQueries.set(i, oStatus); + } + if (location.equals("local")) + fileOperations.writeDataToPSV(listTransactionQueries.get(i).getResult(), listTransactionQueries.get(i).getFilePath()); + else + new RemoteVmUtils().writeDataToPSV(listTransactionQueries.get(i).getResult(), listTransactionQueries.get(i).getFilePath()); + } else if (listTransactionQueries.get(i).getQueryType().equals(QueryTypes.INSERT)) { + if (listTransactionQueries.get(i).isRepeatTable()) { + oStatus = new InsertIntoTable().execute(listTransactionQueries.get(i).getQuery()); + listTransactionQueries.set(i, oStatus); + } + String finalValue = ""; + List result = listTransactionQueries.get(i).getResult().get(0); + for (int j = 0; j < result.size(); j++) { + finalValue += result.get(j); + if (j != result.size() - 1) + finalValue += "|"; + } + if (location.equals("local")) + fileOperations.writeStringToPSV(finalValue, listTransactionQueries.get(i).getFilePath()); + else + new RemoteVmUtils().writeStringToPSV(finalValue, listTransactionQueries.get(i).getFilePath()); + } else if (listTransactionQueries.get(i).getQueryType().equals(QueryTypes.SELECT)) { + if (listTransactionQueries.get(i).isRepeatTable()) { + oStatus = new SelectQuery().execute(listTransactionQueries.get(i).getQuery()); + listTransactionQueries.set(i, oStatus); + } + Results.printResult(listTransactionQueries.get(i).getResult()); + } + } + + Main.isTransaction = false; + return true; + + } +} diff --git a/usr/dpg9/databases/dbdbdb.psv b/usr/dpg9/databases/dbdbdb.psv deleted file mode 100644 index 4b315d5..0000000 --- a/usr/dpg9/databases/dbdbdb.psv +++ /dev/null @@ -1 +0,0 @@ -Persons| \ No newline at end of file diff --git a/usr/dpg9/databases/dbdbdb/Persons.psv b/usr/dpg9/databases/dbdbdb/Persons.psv deleted file mode 100644 index 2cc3cc6..0000000 --- a/usr/dpg9/databases/dbdbdb/Persons.psv +++ /dev/null @@ -1,2 +0,0 @@ -PersonID| LastName| FirstName| Address| City -4| abc4| 'Sa'| asdf| myCity diff --git a/usr/dpg9/databases/dbdbdb/Persons2.Psv b/usr/dpg9/databases/dbdbdb/Persons2.Psv deleted file mode 100644 index 02244aa..0000000 --- a/usr/dpg9/databases/dbdbdb/Persons2.Psv +++ /dev/null @@ -1,6 +0,0 @@ -Username|Identifier|One-time-password|Recoverycode|Firstname|Lastname|Department|Location -booker12|9012|12se74|rb9012|Rachel|Booker|Sales|Manchester -grey07|2070|04ap67|lg2070|Laura|Grey|Depot|London -johnson81|4081|30no86|cj4081|Craig|Johnson|Depot|London -jenkins46|9346|14ju73|mj9346|Mary|Jenkins|Engineering|Manchester -smith79|5079|09ja61|js5079|Jamie|Smith|Engineering|Manchester \ No newline at end of file diff --git a/usr/dpg9/logs/databases.psv b/usr/dpg9/logs/databases.psv deleted file mode 100644 index 6bb0c86..0000000 --- a/usr/dpg9/logs/databases.psv +++ /dev/null @@ -1 +0,0 @@ -mmddb|dbdbdb| \ No newline at end of file diff --git a/usr/dpg9/logs/dbdbdb.psv b/usr/dpg9/logs/dbdbdb.psv deleted file mode 100644 index 4b315d5..0000000 --- a/usr/dpg9/logs/dbdbdb.psv +++ /dev/null @@ -1 +0,0 @@ -Persons| \ No newline at end of file