Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

import ai.chat2db.plugin.postgresql.PostgreSQLMetaData;
import ai.chat2db.spi.IDbMetaData;
import ai.chat2db.community.domain.api.model.metadata.TableIndex;
import ai.chat2db.spi.DefaultSQLExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.sql.Connection;
import java.util.List;

import static ai.chat2db.plugin.gaussdb.constant.GaussDBMetaDataConstants.SEARCH_PATH_STATEMENT_PREFIX;
import static ai.chat2db.plugin.gaussdb.constant.GaussDBMetaDataConstants.TABLE_DDL_SQL;
Expand Down Expand Up @@ -58,11 +55,4 @@ static String stripLeadingSearchPath(String ddl) {
}
return ddl;
}

@Override
public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {
return DefaultSQLExecutor.getInstance().indexes(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

import ai.chat2db.spi.ISqlSyntaxPlugin;
import ai.chat2db.community.domain.api.enums.parser.DatabaseTypeEnum;
import ai.chat2db.community.domain.api.model.completion.SqlCompletionEditorHint;
import ai.chat2db.community.domain.api.model.completion.request.DbSqlCompletionRequest;
import ai.chat2db.spi.ISQLParser;
import ai.chat2db.plugin.gaussdb.parser.GaussDBSqlParser;
import ai.chat2db.plugin.postgresql.completion.PostgreSqlInsertEditorHintProvider;

import java.util.List;

public class GaussDBSyntaxPlugin implements ISqlSyntaxPlugin {
@Override
Expand All @@ -15,4 +20,9 @@ public String getDatabaseType() {
public ISQLParser getSQLParser() {
return new GaussDBSqlParser();
}

@Override
public List<SqlCompletionEditorHint> getSqlEditorHints(DbSqlCompletionRequest request) {
return new PostgreSqlInsertEditorHintProvider().build(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
"jdbcDriverClass": "org.postgresql.Driver"
}
],
"systemSchemas": ["blockchain", "cstore", "db4ai", "dbe_application_info", "dbe_compression", "dbe_file", "dbe_heat_map", "dbe_ilm", "dbe_ilm_admin", "dbe_lob", "dbe_match", "dbe_output", "dbe_perf", "dbe_pldebugger", "dbe_pldeveloper", "dbe_profiler", "dbe_random", "dbe_raw", "dbe_scheduler", "dbe_session", "dbe_sql", "dbe_sql_util", "dbe_stats", "dbe_task", "dbe_utility", "dbe_xml", "dbe_xmldom", "dbe_xmlparser", "information_schema", "pg_catalog", "pkg_service", "pkg_util", "prvt_ilm", "public", "snapshot", "sqladvisor", "sys"],
"systemSchemas": ["blockchain", "cstore", "db4ai", "dbe_application_info", "dbe_compression", "dbe_file", "dbe_heat_map", "dbe_ilm", "dbe_ilm_admin", "dbe_lob", "dbe_match", "dbe_output", "dbe_perf", "dbe_pldebugger", "dbe_pldeveloper", "dbe_profiler", "dbe_random", "dbe_raw", "dbe_scheduler", "dbe_session", "dbe_sql", "dbe_sql_util", "dbe_stats", "dbe_task", "dbe_utility", "dbe_xml", "dbe_xmldom", "dbe_xmlparser", "information_schema", "pg_catalog", "pkg_service", "pkg_util", "prvt_ilm", "snapshot", "sqladvisor", "sys"],
"name": "GaussDB"
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package ai.chat2db.plugin.gaussdb;

import ai.chat2db.plugin.postgresql.PostgreSQLMetaData;
import org.junit.jupiter.api.Test;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
Expand All @@ -11,9 +15,32 @@

import static ai.chat2db.plugin.gaussdb.constant.GaussDBMetaDataConstants.TABLE_DDL_SQL;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;

class GaussDBMetaDataTest {

@Test
void indexesInheritsPostgreSqlImplementationWithTypesAndForeignKeys() throws Exception {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scope inherited constraint joins to the selected schema

In a GaussDB database where two schemas reuse a foreign-key constraint name, the inherited SELECT_KEY_INDEX joins key_column_usage and constraint_column_usage to the filtered constraint only by constraint_name (PostgreSQLMetaDataConstants.java:54). Rows from the other schema are consequently mixed into the selected table's foreign-key metadata, potentially changing the reported target schema/table and duplicating columns. Qualify both joins by constraint catalog/schema and name, or provide a GaussDB-specific query, and cover repeated constraint names across schemas.

AGENTS.md reference: AGENTS.md:L144-L145

Useful? React with 👍 / 👎.

Method indexes = GaussDBMetaData.class.getMethod(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind inherited index-query names as parameters

For a valid quoted GaussDB schema or table whose name contains a single quote, the old JDBC metadata call accepted the name as a value, but the inherited implementation now interpolates it into the single-quoted SELECT_KEY_INDEX and SELECT_TABLE_INDEX predicates with String.format (PostgreSQLMetaData.java:628,658). Opening the index list for an object such as "team's_data" therefore produces malformed SQL, and request-supplied names can also alter the predicate. Execute both metadata queries with bound schema/table parameters and add coverage using quote-containing identifiers.

AGENTS.md reference: AGENTS.md:L144-L145

Useful? React with 👍 / 👎.

"indexes", Connection.class, String.class, String.class, String.class);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve null-schema index lookups

When the table-detail or index-list API identifies a GaussDB table without a schema, which TableDetailQueryRequest permits, the removed override passed null to JDBC metadata so the schema was not restricted. The inherited implementation instead formats the null value into TABLE_SCHEMA = 'null' and nspname = 'null', returning an empty index list even for a table on the connection's active search path. Retain the previous null-schema behavior or resolve the active schema before running the PostgreSQL queries, and cover this compatibility case.

AGENTS.md reference: AGENTS.md:L56-L58

Useful? React with 👍 / 👎.


assertEquals(PostgreSQLMetaData.class, indexes.getDeclaringClass());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Populate the referenced side of inherited foreign keys

When a GaussDB foreign key uses different local and referenced column names, such as customer_id referencing customer.id, the newly inherited PostgreSQLMetaData.indexes() selects both Column_name and Foreign_column_name but stores Column_name in foreignColumnNamelist (PostgreSQLMetaData.java:638-647). The GaussDB metadata response therefore reports the local column as the referenced column, and the table editor/SQL builder can produce an invalid REFERENCES clause. Correct the mapping and verify the local columnList with a result-set-focused GaussDB test instead of only asserting the declaring class.

AGENTS.md reference: AGENTS.md:L144-L145

Useful? React with 👍 / 👎.

}

@Test
void gaussdbJsonDoesNotMarkPublicSchemaAsSystem() throws Exception {
String json;
try (InputStream in = GaussDBMetaDataTest.class.getResourceAsStream(
"/ai/chat2db/plugin/gaussdb/gaussdb.json")) {
assertNotNull(in, "gaussdb.json must be on the test classpath");
json = new String(in.readAllBytes(), StandardCharsets.UTF_8);
}

assertFalse(json.contains("\"public\""),
"the default user schema 'public' must not be listed in systemSchemas");
}

@Test
void tableDdlUsesTableOidAndRemovesSearchPath() {
String nativeDdl = """
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ai.chat2db.plugin.gaussdb;

import ai.chat2db.community.domain.api.enums.completion.SqlCompletionCandidateTypeEnum;
import ai.chat2db.community.domain.api.enums.completion.SqlCompletionEditorHintTypeEnum;
import ai.chat2db.community.domain.api.model.completion.SqlCompletionCandidate;
import ai.chat2db.community.domain.api.model.completion.SqlCompletionEditorHint;
import ai.chat2db.community.domain.api.model.completion.request.DbSqlCompletionRequest;
import ai.chat2db.community.domain.api.model.completion.result.SqlCompletionMetadataResponse;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

class GaussDBSyntaxPluginTest {

@Test
void providesInsertEditorHintsLikePostgreSqlSibling() {
String sql = "INSERT INTO app.demo (id, enabled, created_at) VALUES (";
List<SqlCompletionCandidate> columns = List.of(
column("id", "integer", 1),
column("enabled", "boolean", 2),
column("created_at", "timestamp", 3));
DbSqlCompletionRequest request = DbSqlCompletionRequest.of(
sql, sql.length(), "GAUSSDB", 0,
metadataRequest -> SqlCompletionMetadataResponse.of(columns));

List<SqlCompletionEditorHint> hints = new GaussDBSyntaxPlugin().getSqlEditorHints(request);

assertEquals(1, hints.size());
assertEquals(SqlCompletionEditorHintTypeEnum.INSERT_VALUE, hints.get(0).getType());
assertEquals(List.of("0", "FALSE", "CURRENT_TIMESTAMP"),
hints.get(0).getItems().stream()
.map(SqlCompletionEditorHint.Item::getDefaultValue).toList());
}

private SqlCompletionCandidate column(String name, String type, int rank) {
SqlCompletionCandidate candidate = SqlCompletionCandidate.of(SqlCompletionCandidateTypeEnum.COLUMN, name);
candidate.setColumnName(name);
candidate.setDataType(type);
candidate.setSortRank(rank);
return candidate;
}
}
Loading