diff --git a/.gitignore b/.gitignore
index 32849a44ac..c4b3f618af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ hs_err_pid*
# mac os #
.DS_Store
+Library
# idea #
.idea
diff --git a/configure/clean/clean-dependencie.sh b/configure/clean/clean-dependencie.sh
index 444af636f8..58e7197346 100644
--- a/configure/clean/clean-dependencie.sh
+++ b/configure/clean/clean-dependencie.sh
@@ -1,6 +1,8 @@
#!/bin/bash
+HOME=$(pwd)
echo "Clean dependencies"
+cd "$HOME/core/datacap-ui"
echo "Task: check dependencies"
depcheck --json > depcheck-output.json
@@ -20,4 +22,6 @@ done
echo "Task: install dependencies again"
pnpm install --fix
+
echo "Clean dependencies done"
+cd "$HOME"
\ No newline at end of file
diff --git a/core/datacap-condor/pom.xml b/core/datacap-condor/pom.xml
new file mode 100644
index 0000000000..ed0e4fc2a5
--- /dev/null
+++ b/core/datacap-condor/pom.xml
@@ -0,0 +1,25 @@
+
+ 4.0.0
+
+ io.edurt.datacap
+ datacap
+ 2024.4.1-SNAPSHOT
+ ../../pom.xml
+
+
+ datacap-condor
+ DataCap - Database
+
+
+
+ io.edurt.datacap
+ datacap-parser
+ ${project.version}
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/ComparisonOperator.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/ComparisonOperator.java
new file mode 100644
index 0000000000..93c5e1d5cb
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/ComparisonOperator.java
@@ -0,0 +1,8 @@
+package io.edurt.datacap.condor;
+
+public enum ComparisonOperator
+{
+ EQUALS,
+ GREATER_THAN,
+ LESS_THAN
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/DataType.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/DataType.java
new file mode 100644
index 0000000000..6d2d870960
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/DataType.java
@@ -0,0 +1,9 @@
+package io.edurt.datacap.condor;
+
+public enum DataType
+{
+ INTEGER,
+ VARCHAR,
+ BOOLEAN,
+ DOUBLE
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/DatabaseException.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/DatabaseException.java
new file mode 100644
index 0000000000..60bbd06f72
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/DatabaseException.java
@@ -0,0 +1,10 @@
+package io.edurt.datacap.condor;
+
+public class DatabaseException
+ extends Exception
+{
+ public DatabaseException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/SQLExecutor.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/SQLExecutor.java
new file mode 100644
index 0000000000..7a273701d6
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/SQLExecutor.java
@@ -0,0 +1,272 @@
+package io.edurt.datacap.condor;
+
+import io.edurt.datacap.condor.manager.DatabaseManager;
+import io.edurt.datacap.condor.manager.TableManager;
+import io.edurt.datacap.condor.metadata.ColumnDefinition;
+import io.edurt.datacap.condor.metadata.DatabaseDefinition;
+import io.edurt.datacap.condor.metadata.RowDefinition;
+import io.edurt.datacap.condor.metadata.TableDefinition;
+import io.edurt.datacap.sql.SQLParser;
+import io.edurt.datacap.sql.node.ColumnConstraint;
+import io.edurt.datacap.sql.node.ConstraintType;
+import io.edurt.datacap.sql.node.TableConstraint;
+import io.edurt.datacap.sql.node.element.ColumnElement;
+import io.edurt.datacap.sql.node.element.SelectElement;
+import io.edurt.datacap.sql.node.element.TableElement;
+import io.edurt.datacap.sql.statement.CreateDatabaseStatement;
+import io.edurt.datacap.sql.statement.CreateTableStatement;
+import io.edurt.datacap.sql.statement.DropDatabaseStatement;
+import io.edurt.datacap.sql.statement.DropTableStatement;
+import io.edurt.datacap.sql.statement.InsertStatement;
+import io.edurt.datacap.sql.statement.SQLStatement;
+import io.edurt.datacap.sql.statement.SelectStatement;
+import io.edurt.datacap.sql.statement.UseDatabaseStatement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class SQLExecutor
+{
+ private final DatabaseManager databaseManager;
+ private TableManager tableManager;
+
+ public SQLExecutor(DatabaseManager databaseManager)
+ {
+ this.databaseManager = databaseManager;
+ }
+
+ public SQLResult execute(String sql)
+ {
+ try {
+ SQLStatement statement = SQLParser.parse(sql);
+
+ if (statement instanceof CreateDatabaseStatement) {
+ CreateDatabaseStatement createDatabaseStatement = (CreateDatabaseStatement) statement;
+ return (SQLResult) executeCreateDatabase(createDatabaseStatement);
+ }
+
+ if (statement instanceof DropDatabaseStatement) {
+ DropDatabaseStatement dropDatabaseStatement = (DropDatabaseStatement) statement;
+ return (SQLResult) executeDropDatabase(dropDatabaseStatement);
+ }
+
+ if (statement instanceof UseDatabaseStatement) {
+ UseDatabaseStatement useDatabaseStatement = (UseDatabaseStatement) statement;
+ return (SQLResult) executeUseDatabase(useDatabaseStatement);
+ }
+
+ if (statement instanceof CreateTableStatement) {
+ ensureCurrentTableManager();
+ CreateTableStatement createTableStatement = (CreateTableStatement) statement;
+ return (SQLResult) executeCreateTable(createTableStatement);
+ }
+
+ if (statement instanceof DropTableStatement) {
+ ensureCurrentTableManager();
+ DropTableStatement dropTableStatement = (DropTableStatement) statement;
+ return (SQLResult) executeDropTable(dropTableStatement);
+ }
+
+ if (statement instanceof InsertStatement) {
+ ensureCurrentTableManager();
+ InsertStatement insertStatement = (InsertStatement) statement;
+ return (SQLResult) executeInsert(insertStatement);
+ }
+
+ if (statement instanceof SelectStatement) {
+ ensureCurrentTableManager();
+ SelectStatement selectStatement = (SelectStatement) statement;
+ return (SQLResult) executeSelect(selectStatement);
+ }
+
+ return new SQLResult<>(false, String.format("Unsupported SQL statement: %s", statement));
+ }
+ catch (Exception e) {
+ return new SQLResult<>(false, e.getMessage());
+ }
+ }
+
+ private void ensureCurrentTableManager()
+ throws DatabaseException
+ {
+ DatabaseDefinition currentDatabase = databaseManager.getCurrentDatabase();
+ if (tableManager == null && currentDatabase != null) {
+ tableManager = currentDatabase.getTableManager();
+ }
+ }
+
+ private SQLResult executeCreateDatabase(CreateDatabaseStatement statement)
+ {
+ try {
+ String databaseName = statement.getDatabaseName();
+
+ // 检查是否带有 IF NOT EXISTS
+ // Check if IF NOT EXISTS is present
+ if (statement.isIfNotExists() && databaseManager.databaseExists(databaseName)) {
+ return new SQLResult<>(true, "Database already exists");
+ }
+
+ // 执行创建数据库
+ // Execute database creation
+ databaseManager.createDatabase(databaseName);
+ return new SQLResult<>(true, "Database created successfully");
+ }
+ catch (DatabaseException e) {
+ return new SQLResult<>(false, "Failed to create database: " + e.getMessage());
+ }
+ }
+
+ private SQLResult executeDropDatabase(DropDatabaseStatement statement)
+ {
+ try {
+ if (statement.isIfNotExists() && !databaseManager.databaseExists(statement.getDatabaseName())) {
+ return new SQLResult<>(true, "Database does not exist");
+ }
+
+ databaseManager.dropDatabase(statement.getDatabaseName());
+ return new SQLResult<>(true, "Database dropped successfully");
+ }
+ catch (DatabaseException e) {
+ return new SQLResult<>(false, "Failed to drop database: " + e.getMessage());
+ }
+ }
+
+ private SQLResult executeUseDatabase(UseDatabaseStatement statement)
+ {
+ try {
+ String databaseName = statement.getDatabaseName();
+ databaseManager.useDatabase(databaseName);
+ return new SQLResult<>(true, "Database changed");
+ }
+ catch (DatabaseException e) {
+ return new SQLResult<>(false, "Failed to use database: " + e.getMessage());
+ }
+ }
+
+ private SQLResult executeCreateTable(CreateTableStatement statement)
+ {
+ try {
+ List columns = convertToColumns(statement.getColumns());
+
+ TableDefinition metadata = new TableDefinition(statement.getTableName(), columns);
+ tableManager.createTable(metadata);
+ return new SQLResult<>(true, "Table created successfully");
+ }
+ catch (Exception e) {
+ return new SQLResult<>(false, "Failed to create table: " + e.getMessage());
+ }
+ }
+
+ private SQLResult executeDropTable(DropTableStatement statement)
+ {
+ try {
+ tableManager.dropTable(statement.getTableNames().get(0));
+ return new SQLResult<>(true, "Table dropped successfully");
+ }
+ catch (Exception e) {
+ return new SQLResult<>(false, "Failed to drop table: " + e.getMessage());
+ }
+ }
+
+ private SQLResult executeInsert(InsertStatement statement)
+ {
+ try {
+ // TODO: Support check is multiple insert for InsertStatement
+ if (statement.getSimpleValues().size() == 1) {
+ tableManager.insert(
+ statement.getTableName(),
+ statement.getColumns(),
+ statement.getSimpleValues().get(0)
+ );
+ }
+ else {
+ tableManager.batchInsert(
+ statement.getTableName(),
+ statement.getColumns(),
+ statement.getSimpleValues()
+ );
+ }
+
+ return new SQLResult<>(true, String.format("Inserted %d rows", statement.getSimpleValues().size()));
+ }
+ catch (Exception e) {
+ return new SQLResult<>(false, "Failed to insert rows: " + e.getMessage());
+ }
+ }
+
+ private SQLResult> executeSelect(SelectStatement statement)
+ {
+ try {
+ List rows = tableManager.select(
+ statement.getFromSources().get(0).getTableName(),
+ statement.getSelectElements().stream()
+ .map(SelectElement::getColumn)
+ .collect(Collectors.toList()),
+ null
+ );
+ return new SQLResult<>(
+ true,
+ String.format("Selected %d rows", rows.size()),
+ rows
+ );
+ }
+ catch (Exception e) {
+ return new SQLResult<>(false, "Failed to select rows: " + e.getMessage());
+ }
+ }
+
+ private List convertToColumns(List elements)
+ {
+ List columns = new ArrayList<>();
+
+ // First pass: collect all columns and primary key constraints
+ Set primaryKeyColumns = new HashSet<>();
+ for (TableElement element : elements) {
+ if (element instanceof TableConstraint) {
+ TableConstraint constraint = (TableConstraint) element;
+ if (constraint.getType() == ConstraintType.PRIMARY_KEY && constraint.getColumns() != null) {
+ primaryKeyColumns.addAll(Arrays.asList(constraint.getColumns()));
+ }
+ }
+ }
+
+ // Second pass: create column definitions
+ for (TableElement element : elements) {
+ if (element instanceof ColumnElement) {
+ ColumnElement col = (ColumnElement) element;
+ boolean isPrimaryKey = primaryKeyColumns.contains(col.getColumnName());
+ boolean isNullable = true;
+
+ // Check column constraints
+ for (ColumnConstraint constraint : col.getConstraints()) {
+ if (constraint.getType() == ConstraintType.PRIMARY_KEY) {
+ isPrimaryKey = true;
+ }
+ else if (constraint.getType() == ConstraintType.NOT_NULL) {
+ isNullable = false;
+ }
+ }
+
+ DataType type = convertDataType(col.getDataType());
+ columns.add(new ColumnDefinition(
+ col.getColumnName(),
+ type,
+ isPrimaryKey,
+ isNullable));
+ }
+ }
+
+ return columns;
+ }
+
+ private DataType convertDataType(io.edurt.datacap.sql.node.DataType sourceType)
+ {
+ // Implement conversion logic from source DataType to target DataType
+ // This depends on your DataType enum definition
+ return DataType.valueOf(sourceType.getBaseType());
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/SQLResult.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/SQLResult.java
new file mode 100644
index 0000000000..dc7243b6b0
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/SQLResult.java
@@ -0,0 +1,26 @@
+package io.edurt.datacap.condor;
+
+import lombok.Getter;
+import lombok.ToString;
+
+@Getter
+@ToString
+public class SQLResult
+{
+ private final boolean success;
+ private final String message;
+ private T data;
+
+ public SQLResult(boolean success, String message)
+ {
+ this.success = success;
+ this.message = message;
+ }
+
+ public SQLResult(boolean success, String message, T data)
+ {
+ this.success = success;
+ this.message = message;
+ this.data = data;
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/TableException.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/TableException.java
new file mode 100644
index 0000000000..ed96a7318f
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/TableException.java
@@ -0,0 +1,10 @@
+package io.edurt.datacap.condor;
+
+public class TableException
+ extends Exception
+{
+ public TableException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/condition/Condition.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/condition/Condition.java
new file mode 100644
index 0000000000..a3831fbf3d
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/condition/Condition.java
@@ -0,0 +1,8 @@
+package io.edurt.datacap.condor.condition;
+
+import io.edurt.datacap.condor.metadata.RowDefinition;
+
+public interface Condition
+{
+ boolean evaluate(RowDefinition row);
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/condition/SimpleCondition.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/condition/SimpleCondition.java
new file mode 100644
index 0000000000..7d7098833b
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/condition/SimpleCondition.java
@@ -0,0 +1,42 @@
+package io.edurt.datacap.condor.condition;
+
+import io.edurt.datacap.condor.ComparisonOperator;
+import io.edurt.datacap.condor.metadata.RowDefinition;
+
+import java.util.Comparator;
+
+public class SimpleCondition
+ implements Condition
+{
+ private final String columnName;
+ private final Object value;
+ private final ComparisonOperator operator;
+
+ public SimpleCondition(String columnName, Object value, ComparisonOperator operator)
+ {
+ this.columnName = columnName;
+ this.value = value;
+ this.operator = operator;
+ }
+
+ @Override
+ public boolean evaluate(RowDefinition row)
+ {
+ Object rowValue = row.getValue(columnName);
+ if (rowValue == null || value == null) {
+ return false;
+ }
+
+ switch (operator) {
+ case EQUALS:
+ return value.equals(rowValue);
+ case GREATER_THAN:
+ return Comparator.comparing(Object::toString).compare(rowValue, value) > 0;
+ case LESS_THAN:
+ return Comparator.comparing(Object::toString).compare(rowValue, value) < 0;
+// return ((Comparable) rowValue).compareTo(value) < 0;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/io/AppendableObjectInputStream.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/io/AppendableObjectInputStream.java
new file mode 100644
index 0000000000..ca8c508466
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/io/AppendableObjectInputStream.java
@@ -0,0 +1,30 @@
+package io.edurt.datacap.condor.io;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+
+@Slf4j
+public class AppendableObjectInputStream
+ extends ObjectInputStream
+{
+ private boolean firstObject = true;
+
+ public AppendableObjectInputStream(InputStream in)
+ throws IOException
+ {
+ super(in);
+ }
+
+ @Override
+ protected void readStreamHeader()
+ throws IOException
+ {
+ if (firstObject) {
+ super.readStreamHeader();
+ firstObject = false;
+ }
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/io/AppendableObjectOutputStream.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/io/AppendableObjectOutputStream.java
new file mode 100644
index 0000000000..d2983ad6d7
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/io/AppendableObjectOutputStream.java
@@ -0,0 +1,27 @@
+package io.edurt.datacap.condor.io;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+public class AppendableObjectOutputStream
+ extends ObjectOutputStream
+{
+ private boolean firstObject = true;
+
+ public AppendableObjectOutputStream(OutputStream out)
+ throws IOException
+ {
+ super(out);
+ }
+
+ @Override
+ protected void writeStreamHeader()
+ throws IOException
+ {
+ if (firstObject) {
+ super.writeStreamHeader();
+ firstObject = false;
+ }
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/manager/DatabaseManager.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/manager/DatabaseManager.java
new file mode 100644
index 0000000000..ac7b8417c7
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/manager/DatabaseManager.java
@@ -0,0 +1,206 @@
+package io.edurt.datacap.condor.manager;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import io.edurt.datacap.condor.DatabaseException;
+import io.edurt.datacap.condor.metadata.DatabaseDefinition;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Stream;
+
+@Slf4j
+@SuppressFBWarnings(value = {"RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", "RV_NEGATING_RESULT_OF_COMPARETO"})
+public class DatabaseManager
+{
+ private static final String ROOT_DIR = "data";
+ private Map databases;
+ private String currentDatabase;
+
+ public static DatabaseManager createManager()
+ {
+ return new DatabaseManager();
+ }
+
+ private DatabaseManager()
+ {
+ this.databases = new HashMap<>();
+ initializeRootDirectory();
+ loadExistingDatabases();
+ }
+
+ private void initializeRootDirectory()
+ {
+ File directory = new File(ROOT_DIR);
+ if (!directory.exists()) {
+ directory.mkdirs();
+ }
+ }
+
+ private void loadExistingDatabases()
+ {
+ log.info("Loading existing databases from {}", ROOT_DIR);
+ Path rootDir = Path.of(ROOT_DIR);
+ try (Stream stream = Files.walk(rootDir)) {
+ stream.filter(path -> Files.isDirectory(path)
+ && Files.exists(path.resolve("metadata/db.properties")))
+ .forEach(path -> {
+ String dbName = path.getFileName().toString();
+ log.debug("Found database: {}", dbName);
+ databases.put(dbName, new DatabaseDefinition(dbName, path));
+ });
+ }
+ catch (IOException e) {
+ log.error("Failed to load existing databases", e);
+ }
+ }
+
+ public void createDatabase(String databaseName)
+ throws DatabaseException
+ {
+ // 验证数据库名称
+ // Validate database name
+ validateDatabaseName(databaseName);
+
+ // 检查数据库是否已存在
+ // Check if database already exists
+ if (databases.containsKey(databaseName)) {
+ log.debug("Database '{}' already exists", databaseName);
+ throw new DatabaseException("Database '" + databaseName + "' already exists");
+ }
+
+ // 创建数据库目录
+ // Create database directory
+ try {
+ log.info("Creating database directory: {}", databaseName);
+ Path dbPath = Paths.get(ROOT_DIR, databaseName);
+ Files.createDirectory(dbPath);
+
+ // 创建必要的子目录
+ // Create necessary subdirectories
+ log.info("Creating database metadata: {}", databaseName);
+ Files.createDirectory(dbPath.resolve("tables"));
+ Files.createDirectory(dbPath.resolve("metadata"));
+
+ // 创建并保存数据库配置
+ // Create and save database configuration
+ log.info("Creating database configuration for database: {}", databaseName);
+ Properties dbConfig = new Properties();
+ dbConfig.setProperty("created_time", String.valueOf(System.currentTimeMillis()));
+ dbConfig.setProperty("version", "1.0");
+ Path configPath = dbPath.resolve("metadata/db.properties");
+ try (OutputStream os = Files.newOutputStream(configPath)) {
+ dbConfig.store(os, "Database Configuration");
+ }
+
+ // 创建数据库对象并添加到管理器
+ // Create database object and add to manager
+ DatabaseDefinition database = new DatabaseDefinition(databaseName, dbPath);
+ databases.put(databaseName, database);
+ log.info("Database '{}' created successfully", databaseName);
+
+ // 设置为当前数据库
+ // Set as current database
+ currentDatabase = databaseName;
+ }
+ catch (IOException e) {
+ throw new DatabaseException("Failed to create database: " + e.getMessage());
+ }
+ }
+
+ private void validateDatabaseName(String name)
+ throws DatabaseException
+ {
+ log.info("Validating database name: {}", name);
+ if (name == null || name.trim().isEmpty()) {
+ throw new DatabaseException("Database name cannot be empty");
+ }
+
+ // 检查数据库名称的合法性
+ // Check database name validity
+ if (!name.matches("^[a-zA-Z][a-zA-Z0-9_]*$")) {
+ throw new DatabaseException("Invalid database name. Database name must start with a letter and can only contain letters, numbers, and underscores");
+ }
+
+ // 检查长度限制
+ // Check length limit
+ if (name.length() > 64) {
+ throw new DatabaseException("Database name is too long (maximum 64 characters)");
+ }
+ }
+
+ public void dropDatabase(String databaseName)
+ throws DatabaseException
+ {
+ if (!databases.containsKey(databaseName)) {
+ log.info("Database '{}' does not exist", databaseName);
+ throw new DatabaseException("Database '" + databaseName + "' does not exist");
+ }
+
+ try {
+ // 删除数据库目录及其所有内容
+ // Delete database directory and its contents
+ Path dbPath = Paths.get(ROOT_DIR, databaseName);
+ try (Stream stream = Files.walk(dbPath)) {
+ stream.sorted((p1, p2) -> -p1.compareTo(p2))
+ .forEach(path -> {
+ try {
+ log.debug("Deleting file: {} on database: {}", path, databaseName);
+ Files.delete(path);
+ }
+ catch (IOException e) {
+ log.error("Failed to delete file: {} on database: {}", path, databaseName, e);
+ }
+ });
+ }
+
+ // 从管理器中移除数据库
+ // Remove database from manager
+ databases.remove(databaseName);
+
+ // 如果删除的是当前数据库,重置当前数据库
+ // Reset current database if deleted database is the current database
+ if (databaseName.equals(currentDatabase)) {
+ currentDatabase = null;
+ }
+ }
+ catch (IOException e) {
+ throw new DatabaseException("Failed to drop database: " + e.getMessage());
+ }
+ }
+
+ public void useDatabase(String databaseName)
+ throws DatabaseException
+ {
+ if (!databases.containsKey(databaseName)) {
+ throw new DatabaseException("Database '" + databaseName + "' does not exist");
+ }
+ currentDatabase = databaseName;
+ }
+
+ public DatabaseDefinition getCurrentDatabase()
+ throws DatabaseException
+ {
+ if (currentDatabase == null) {
+ throw new DatabaseException("No database selected");
+ }
+ return databases.get(currentDatabase);
+ }
+
+ public boolean databaseExists(String databaseName)
+ {
+ return databases.containsKey(databaseName);
+ }
+
+ public String[] listDatabases()
+ {
+ return databases.keySet().toArray(new String[0]);
+ }
+}
diff --git a/core/datacap-condor/src/main/java/io/edurt/datacap/condor/manager/TableManager.java b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/manager/TableManager.java
new file mode 100644
index 0000000000..0d0384a098
--- /dev/null
+++ b/core/datacap-condor/src/main/java/io/edurt/datacap/condor/manager/TableManager.java
@@ -0,0 +1,486 @@
+package io.edurt.datacap.condor.manager;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import io.edurt.datacap.condor.DataType;
+import io.edurt.datacap.condor.TableException;
+import io.edurt.datacap.condor.condition.Condition;
+import io.edurt.datacap.condor.io.AppendableObjectInputStream;
+import io.edurt.datacap.condor.io.AppendableObjectOutputStream;
+import io.edurt.datacap.condor.metadata.ColumnDefinition;
+import io.edurt.datacap.condor.metadata.RowDefinition;
+import io.edurt.datacap.condor.metadata.TableDefinition;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Stream;
+
+@Slf4j
+@SuppressFBWarnings(value = {"DLS_DEAD_LOCAL_STORE", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+public class TableManager
+{
+ private final Path dataDir;
+ private final Map tableMetadataCache;
+ private final Map tableLocks;
+
+ public TableManager(Path databasePath)
+ {
+ this.dataDir = databasePath.resolve("tables");
+ this.tableMetadataCache = new HashMap<>();
+ this.tableLocks = new HashMap<>();
+ initializeDirectory();
+ }
+
+ private void initializeDirectory()
+ {
+ loadExistingTables();
+ }
+
+ private void loadExistingTables()
+ {
+ if (!Files.exists(dataDir)) {
+ return;
+ }
+
+ log.info("Loading existing tables from {}", dataDir);
+ try (Stream stream = Files.walk(dataDir, 1)) {
+ stream.filter(Files::isDirectory)
+ .filter(path -> !path.equals(dataDir))
+ .filter(path -> Files.exists(path.resolve("metadata/table.meta")))
+ .forEach(tableDir -> {
+ String tableName = tableDir.getFileName().toString();
+ try {
+ TableDefinition metadata = loadTableMetadata(tableName);
+ tableMetadataCache.put(tableName, metadata);
+ tableLocks.put(tableName, new ReentrantReadWriteLock());
+ }
+ catch (IOException e) {
+ log.error("Failed to load table metadata: {}", tableName);
+ }
+ });
+ }
+ catch (IOException e) {
+ log.error("Failed to load existing tables", e);
+ }
+ }
+
+ public void createTable(TableDefinition metadata)
+ throws TableException
+ {
+ validateTableName(metadata.getTableName());
+
+ if (tableExists(metadata.getTableName())) {
+ throw new TableException("Table '" + metadata.getTableName() + "' already exists");
+ }
+
+ try {
+ saveTableMetadata(metadata);
+
+ createTableDataFile(metadata.getTableName());
+
+ tableMetadataCache.put(metadata.getTableName(), metadata);
+ tableLocks.put(metadata.getTableName(), new ReentrantReadWriteLock());
+ }
+ catch (IOException e) {
+ throw new TableException("Failed to create table: " + e.getMessage());
+ }
+ }
+
+ public void dropTable(String tableName)
+ throws TableException
+ {
+ if (!tableExists(tableName)) {
+ throw new TableException("Table '" + tableName + "' does not exist");
+ }
+
+ ReadWriteLock lock = tableLocks.get(tableName);
+ lock.writeLock().lock();
+ try {
+ deleteFile(dataDir.resolve(tableName));
+ tableMetadataCache.remove(tableName);
+ tableLocks.remove(tableName);
+ }
+ catch (IOException e) {
+ throw new TableException("Failed to drop table: " + e.getMessage());
+ }
+ finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public void insert(String tableName, List columnNames, List