Skip to content
Merged
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 @@ -34,5 +34,10 @@
<artifactId>hibernate-validator</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public enum ExportFileSuffixEnum {

CSV(".csv"),

XLXS(".xlxs"),
XLSX(".xlsx"),

XLS(".xls"),

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package ai.chat2db.community.domain.api.enums.plugin;

import ai.chat2db.community.tools.enums.IBaseEnum;
import ai.chat2db.community.tools.util.EasyStringUtils;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;


@Getter
Expand Down Expand Up @@ -113,7 +113,7 @@ public String getSqlValue(String value) {
if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
return value;
} else {
return "'" + value + "'";
return getStringValue(value);
}
}
if (this == DataTypeEnum.NUMERIC) {
Expand All @@ -123,49 +123,43 @@ public String getSqlValue(String value) {
return getStringValue(value);
}
if (this == DataTypeEnum.DATETIME) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.BINARY) {
return "''";
}
if (this == DataTypeEnum.CONTENT) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.STRUCT) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.DOCUMENT) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.ARRAY) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.OBJECT) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.REFERENCE) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.ROWID) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.ANY) {
return "'" + value + "'";
return getStringValue(value);
}
if (this == DataTypeEnum.UNKNOWN) {
return "'" + value + "'";
return getStringValue(value);
}
return "'" + value + "'";
return getStringValue(value);
}

public static String getStringValue(String value) {
if (StringUtils.isBlank(value)) {
return "'" + value + "'";
}
value = value.replace("\\", "\\\\");
value = value.replace("'", "\\'");
value = value.replace("\"", "\\\"");
return "'" + value + "'";
return EasyStringUtils.escapeAndQuoteString(value);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ public class AsyncContext {

protected ITaskAsyncCall call;

protected boolean finish;
protected volatile boolean finish;

protected Integer progress;
protected volatile Integer progress;

private StringBuffer info = new StringBuffer();
private volatile StringBuffer info = new StringBuffer();

private StringBuffer error = new StringBuffer();
private volatile StringBuffer error = new StringBuffer();

public AsyncContext(ITaskAsyncCall call, Context context, File writeFile, boolean containsData) {
this.call = call;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import jakarta.validation.constraints.NotNull;
import lombok.Data;

import java.util.Objects;

@Data
public class PinTable {

Expand Down Expand Up @@ -36,6 +38,11 @@ public boolean equals(Object o) {
return tableName != null ? tableName.equals(pinTable.tableName) : pinTable.tableName == null;
}

@Override
public int hashCode() {
return Objects.hash(dataSourceId, databaseName, schemaName, tableName);
}

public boolean select(PinTable pinTable) {
if (dataSourceId != null ? !dataSourceId.equals(pinTable.dataSourceId) : pinTable.dataSourceId != null)
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ai.chat2db.community.domain.api.enums;

import org.junit.jupiter.api.Test;

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

/**
* Regression test for code-review finding core:domain-api-4:
* the misspelled XLXS(".xlxs") constant must be XLSX(".xlsx").
*/
class ExportFileSuffixEnumTest {

@Test
void xlsxConstantExistsWithCorrectSuffix() {
assertEquals(".xlsx", ExportFileSuffixEnum.XLSX.getSuffix());
}

@Test
void misspelledConstantRemoved() {
for (ExportFileSuffixEnum value : ExportFileSuffixEnum.values()) {
if (".xlxs".equals(value.getSuffix()) || "XLXS".equals(value.name())) {
throw new AssertionError("misspelled XLXS/.xlxs constant still present");
}
}
assertNotNull(ExportFileSuffixEnum.valueOf("XLSX"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package ai.chat2db.community.domain.api.enums.plugin;

import org.junit.jupiter.api.Test;

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

/**
* Regression tests for code-review finding core:domain-api-3:
* quoted branches of getSqlValue must escape like the STRING branch.
*/
class DataTypeEnumTest {

@Test
void stringBranchEscapesLiteralDelimitersAndBackslashes() {
assertEquals("'O''Brien'", DataTypeEnum.STRING.getSqlValue("O'Brien"));
assertEquals("'a\\\\b'", DataTypeEnum.STRING.getSqlValue("a\\b"));
assertEquals("'a\"b'", DataTypeEnum.STRING.getSqlValue("a\"b"));
}

@Test
void datetimeBranchEscapesLikeString() {
assertEquals(DataTypeEnum.STRING.getSqlValue("2024-01-01 '; DROP TABLE t; --"),
DataTypeEnum.DATETIME.getSqlValue("2024-01-01 '; DROP TABLE t; --"));
assertEquals("'O''Brien'", DataTypeEnum.DATETIME.getSqlValue("O'Brien"));
}

@Test
void unknownBranchEscapesLikeString() {
assertEquals(DataTypeEnum.STRING.getSqlValue("x'y\\z"),
DataTypeEnum.UNKNOWN.getSqlValue("x'y\\z"));
// getByCode falls back to UNKNOWN for unrecognized types
assertEquals(DataTypeEnum.UNKNOWN, DataTypeEnum.getByCode("SOME_UNLISTED_TYPE"));
assertEquals(DataTypeEnum.STRING.getSqlValue("O'Brien"),
DataTypeEnum.getByCode("SOME_UNLISTED_TYPE").getSqlValue("O'Brien"));
}

@Test
void allQuotedBranchesEscapeLikeString() {
String value = "O'Brien\\";
String expected = DataTypeEnum.STRING.getSqlValue(value);
assertEquals(expected, DataTypeEnum.CONTENT.getSqlValue(value));
assertEquals(expected, DataTypeEnum.STRUCT.getSqlValue(value));
assertEquals(expected, DataTypeEnum.DOCUMENT.getSqlValue(value));
assertEquals(expected, DataTypeEnum.ARRAY.getSqlValue(value));
assertEquals(expected, DataTypeEnum.OBJECT.getSqlValue(value));
assertEquals(expected, DataTypeEnum.REFERENCE.getSqlValue(value));
assertEquals(expected, DataTypeEnum.ROWID.getSqlValue(value));
assertEquals(expected, DataTypeEnum.ANY.getSqlValue(value));
assertEquals(expected, DataTypeEnum.UNKNOWN.getSqlValue(value));
assertEquals(expected, DataTypeEnum.BIT.getSqlValue(value));
assertEquals(expected, DataTypeEnum.CHAT2DB_ROW_NUMBER.getSqlValue(value));
}

@Test
void nonQuotedBranchesUnchanged() {
assertEquals("true", DataTypeEnum.BOOLEAN.getSqlValue("true"));
assertEquals("FALSE", DataTypeEnum.BOOLEAN.getSqlValue("FALSE"));
assertEquals("42", DataTypeEnum.NUMERIC.getSqlValue("42"));
assertEquals("''", DataTypeEnum.BINARY.getSqlValue("anything"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ai.chat2db.community.domain.api.model.async;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Modifier;

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

/**
* Regression tests for code-review finding core:domain-api-2:
* the finish flag (and the progress/info/error state read by the callback
* thread) must be safely published so the polling thread terminates.
*/
class AsyncContextTest {

@Test
void sharedStateFieldsAreVolatile() throws Exception {
assertTrue(Modifier.isVolatile(AsyncContext.class.getDeclaredField("finish").getModifiers()),
"finish must be volatile so the polling thread observes stop()/finish()");
assertTrue(Modifier.isVolatile(AsyncContext.class.getDeclaredField("progress").getModifiers()),
"progress is written by the task thread and read by the callback thread");
assertTrue(Modifier.isVolatile(AsyncContext.class.getDeclaredField("info").getModifiers()),
"info is reassigned in callUpdate() and appended by the task thread");
assertTrue(Modifier.isVolatile(AsyncContext.class.getDeclaredField("error").getModifiers()),
"error is reassigned in callUpdate() and appended by the task thread");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ai.chat2db.community.domain.api.model.pin;

import org.junit.jupiter.api.Test;

import java.util.HashSet;
import java.util.Set;

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

/**
* Regression test for code-review finding core:domain-api-5:
* instances equal by the four key fields must have equal hash codes,
* even when their ids differ.
*/
class PinTableTest {

private PinTable build(Long id) {
PinTable pinTable = new PinTable();
pinTable.setId(id);
pinTable.setDataSourceId(1L);
pinTable.setDatabaseName("db");
pinTable.setSchemaName("schema");
pinTable.setTableName("table");
return pinTable;
}

@Test
void equalInstancesHaveEqualHashCodesDespiteDifferentIds() {
PinTable a = build(1L);
PinTable b = build(2L);
assertEquals(a, b);
assertEquals(a.hashCode(), b.hashCode());
}

@Test
void hashSetDoesNotDuplicateEqualInstances() {
Set<PinTable> set = new HashSet<>();
set.add(build(1L));
set.add(build(2L));
assertEquals(1, set.size());
assertTrue(set.contains(build(3L)));
}
}
Loading