From 2cd7b2712fd72719db24e9860589472c66527ce7 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Fri, 18 Apr 2025 14:58:11 +0800 Subject: [PATCH 1/3] Add GaussDB support for spring data jdbc Signed-off-by: liubao68 --- pom.xml | 1 + spring-data-jdbc/pom.xml | 38 ++ .../jdbc/core/dialect/JdbcGaussDBDialect.java | 74 +++ .../repository/config/DialectResolver.java | 5 +- .../GaussDBDialectIntegrationTests.java | 230 +++++++++ .../data/jdbc/testing/DatabaseType.java | 2 +- .../data/jdbc/testing/GaussDBContainer.java | 159 ++++++ .../testing/GaussDBContainerProvider.java | 52 ++ .../GaussDBDataSourceConfiguration.java | 73 +++ .../testing/HsqlDataSourceConfiguration.java | 2 +- .../jdbc/testing/TestDatabaseFeatures.java | 6 +- ...GaussDBDialectIntegrationTests-gaussdb.sql | 8 + ...regateTemplateIntegrationTests-gaussdb.sql | 476 ++++++++++++++++++ ...TemplateSchemaIntegrationTests-gaussdb.sql | 14 + ...tionManagerRefIntegrationTests-gaussdb.sql | 2 + ...bcRepositoriesIntegrationTests-gaussdb.sql | 2 + ...oryConcurrencyIntegrationTests-gaussdb.sql | 4 + ...stomConversionIntegrationTests-gaussdb.sql | 2 + ...eddedImmutableIntegrationTests-gaussdb.sql | 2 + ...nAggregateRootIntegrationTests-gaussdb.sql | 13 + ...WithCollectionIntegrationTests-gaussdb.sql | 15 + ...dWithReferenceIntegrationTests-gaussdb.sql | 22 + ...ryIdGenerationIntegrationTests-gaussdb.sql | 16 + ...InsertExistingIntegrationTests-gaussdb.sql | 2 + ...JdbcRepositoryIntegrationTests-gaussdb.sql | 58 +++ ...ertyConversionIntegrationTests-gaussdb.sql | 5 + ...ltSetExtractorIntegrationTests-gaussdb.sql | 5 + ...ithCollectionsIntegrationTests-gaussdb.sql | 4 + ...ollectionsNoIdIntegrationTests-gaussdb.sql | 4 + ...itoryWithListsIntegrationTests-gaussdb.sql | 36 ++ ...sitoryWithMapsIntegrationTests-gaussdb.sql | 4 + ...gConfigurationIntegrationTests-gaussdb.sql | 2 + .../core/dialect/GaussDBDialect.java | 173 +++++++ 33 files changed, 1505 insertions(+), 6 deletions(-) create mode 100644 spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcGaussDBDialect.java create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/GaussDBDialectIntegrationTests.java create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainerProvider.java create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBDataSourceConfiguration.java create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core.dialect/GaussDBDialectIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesBrokenTransactionManagerRefIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryInsertExistingIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsNoIdIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-gaussdb.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-gaussdb.sql create mode 100644 spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/GaussDBDialect.java diff --git a/pom.xml b/pom.xml index c2681ab268..40ad76b77f 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ 8.0.33 42.7.4 23.7.0.25.01 + 506.0.0.b058 1.0.7.RELEASE diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index aba16d9e30..504ab53a27 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -137,6 +137,13 @@ true + + com.huaweicloud.gaussdb + gaussdbjdbc + ${gaussdb.version} + true + + org.mariadb.jdbc mariadb-java-client @@ -318,6 +325,37 @@ + + gaussdb + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + gaussdb-test + integration-test + + integration-test + + + + **/*IntegrationTests.java + + + + + + gaussdb + + + + + + + + all-dbs diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcGaussDBDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcGaussDBDialect.java new file mode 100644 index 0000000000..b640071355 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcGaussDBDialect.java @@ -0,0 +1,74 @@ +/* + * Copyright 2021-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.dialect; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import org.springframework.data.relational.core.dialect.GaussDBDialect; +import org.springframework.util.ClassUtils; + +/** + * JDBC specific GaussDBDialect Dialect. + * + * Notes: this file is token from JdbcPostgresDialect and add specific changes for GaussDB + * + * @author liubao + */ +public class JdbcGaussDBDialect extends GaussDBDialect { + + public static final JdbcGaussDBDialect INSTANCE = new JdbcGaussDBDialect(); + + private static final Set> SIMPLE_TYPES; + + static { + + Set> simpleTypes = new HashSet<>(GaussDBDialect.INSTANCE.simpleTypes()); + List simpleTypeNames = Arrays.asList( // + "com.huawei.gaussdb.jdbc.util.PGobject", // + "com.huawei.gaussdb.jdbc.geometric.PGpoint", // + "com.huawei.gaussdb.jdbc.geometric.PGbox", // + "com.huawei.gaussdb.jdbc.geometric.PGcircle", // + "com.huawei.gaussdb.jdbc.geometric.PGline", // + "com.huawei.gaussdb.jdbc.geometric.PGpath", // + "com.huawei.gaussdb.jdbc.geometric.PGpolygon", // + "com.huawei.gaussdb.jdbc.geometric.PGlseg" // + ); + simpleTypeNames.forEach(name -> ifClassPresent(name, simpleTypes::add)); + SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes); + } + + @Override + public Set> simpleTypes() { + return SIMPLE_TYPES; + } + + /** + * If the class is present on the class path, invoke the specified consumer {@code action} with the class object, + * otherwise do nothing. + * + * @param action block to be executed if a value is present. + */ + private static void ifClassPresent(String className, Consumer> action) { + if (ClassUtils.isPresent(className, GaussDBDialect.class.getClassLoader())) { + action.accept(ClassUtils.resolveClassName(className, GaussDBDialect.class.getClassLoader())); + } + } +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java index 99eb15cf61..b837ed749d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java @@ -29,6 +29,7 @@ import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.dao.NonTransientDataAccessException; import org.springframework.data.jdbc.core.dialect.JdbcDb2Dialect; +import org.springframework.data.jdbc.core.dialect.JdbcGaussDBDialect; import org.springframework.data.jdbc.core.dialect.JdbcMySqlDialect; import org.springframework.data.jdbc.core.dialect.JdbcPostgresDialect; import org.springframework.data.jdbc.core.dialect.JdbcSqlServerDialect; @@ -139,7 +140,9 @@ private static Dialect getDialect(Connection connection) throws SQLException { if (name.contains("oracle")) { return OracleDialect.INSTANCE; } - + if (name.contains("gaussdb")) { + return JdbcGaussDBDialect.INSTANCE; + } LOG.info(String.format("Couldn't determine Dialect for \"%s\"", name)); return null; } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/GaussDBDialectIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/GaussDBDialectIntegrationTests.java new file mode 100644 index 0000000000..29af47d1bf --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/GaussDBDialectIntegrationTests.java @@ -0,0 +1,230 @@ +/* + * Copyright 2021-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.dialect; + +import static org.assertj.core.api.Assertions.*; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.Import; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.annotation.Id; +import org.springframework.data.convert.CustomConversions; +import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; +import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.data.jdbc.testing.DatabaseType; +import org.springframework.data.jdbc.testing.EnabledOnDatabase; +import org.springframework.data.jdbc.testing.IntegrationTest; +import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.mapping.Table; +import org.springframework.data.repository.CrudRepository; + +import com.huawei.gaussdb.jdbc.util.PGobject; + +/** + * Integration tests for GaussDB Dialect. Start this test with {@code -Dspring.profiles.active=gaussdb}. + * + * Notes: this file is token from PostgresDialectIntegrationTests and add specific changes for GaussDB + * + * @author liubao + */ +@IntegrationTest +@EnabledOnDatabase(DatabaseType.GAUSSDB) +public class GaussDBDialectIntegrationTests { + + @Autowired CustomerRepository customerRepository; + + @Test + void shouldSaveAndLoadJson() throws SQLException { + + PGobject sessionData = new PGobject(); + sessionData.setType("jsonb"); + sessionData.setValue("{\"hello\": \"json\"}"); + + Customer saved = customerRepository + .save(new Customer(null, "Adam Smith", new JsonHolder("{\"hello\": \"world\"}"), sessionData)); + + Optional loaded = customerRepository.findById(saved.getId()); + + assertThat(loaded).hasValueSatisfying(actual -> { + + assertThat(actual.getPersonData().getContent()).isEqualTo("{\"hello\": \"world\"}"); + assertThat(actual.getSessionData().getValue()).isEqualTo("{\"hello\": \"json\"}"); + }); + } + + @Configuration + @Import(TestConfiguration.class) + @EnableJdbcRepositories(considerNestedRepositories = true, + includeFilters = @ComponentScan.Filter(value = CustomerRepository.class, type = FilterType.ASSIGNABLE_TYPE)) + static class Config { + + @Bean + CustomConversions jdbcCustomConversions(Dialect dialect) { + SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER); + + return new JdbcCustomConversions( + CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)), userConverters()); + } + + private List storeConverters(Dialect dialect) { + + List converters = new ArrayList<>(); + converters.addAll(dialect.getConverters()); + converters.addAll(JdbcCustomConversions.storeConverters()); + return converters; + } + + private List userConverters() { + return Arrays.asList(JsonHolderToPGobjectConverter.INSTANCE, PGobjectToJsonHolderConverter.INSTANCE); + } + } + + enum JsonHolderToPGobjectConverter implements Converter { + + INSTANCE; + + @Override + public PGobject convert(JsonHolder source) { + PGobject result = new PGobject(); + result.setType("jsonb"); + try { + result.setValue(source.getContent()); + } catch (SQLException e) { + throw new RuntimeException(e); + } + return result; + } + } + + enum PGobjectToJsonHolderConverter implements Converter { + + INSTANCE; + + @Override + public JsonHolder convert(PGobject source) { + return new JsonHolder(source.getValue()); + } + } + + @Table("customers") + public static final class Customer { + + @Id private final Long id; + private final String name; + private final JsonHolder personData; + private final PGobject sessionData; + + public Customer(Long id, String name, JsonHolder personData, PGobject sessionData) { + this.id = id; + this.name = name; + this.personData = personData; + this.sessionData = sessionData; + } + + public Long getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public JsonHolder getPersonData() { + return this.personData; + } + + public PGobject getSessionData() { + return this.sessionData; + } + + public boolean equals(final Object o) { + if (o == this) + return true; + if (!(o instanceof final Customer other)) + return false; + final Object this$id = this.getId(); + final Object other$id = other.getId(); + if (!Objects.equals(this$id, other$id)) + return false; + final Object this$name = this.getName(); + final Object other$name = other.getName(); + if (!Objects.equals(this$name, other$name)) + return false; + final Object this$personData = this.getPersonData(); + final Object other$personData = other.getPersonData(); + if (!Objects.equals(this$personData, other$personData)) + return false; + final Object this$sessionData = this.getSessionData(); + final Object other$sessionData = other.getSessionData(); + return Objects.equals(this$sessionData, other$sessionData); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.getId(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + final Object $name = this.getName(); + result = result * PRIME + ($name == null ? 43 : $name.hashCode()); + final Object $personData = this.getPersonData(); + result = result * PRIME + ($personData == null ? 43 : $personData.hashCode()); + final Object $sessionData = this.getSessionData(); + result = result * PRIME + ($sessionData == null ? 43 : $sessionData.hashCode()); + return result; + } + + public String toString() { + return "PostgresDialectIntegrationTests.Customer(id=" + this.getId() + ", name=" + this.getName() + + ", personData=" + this.getPersonData() + ", sessionData=" + this.getSessionData() + ")"; + } + } + + public static class JsonHolder { + String content; + + public JsonHolder(String content) { + this.content = content; + } + + public JsonHolder() {} + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + } + + interface CustomerRepository extends CrudRepository {} + +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseType.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseType.java index f72bcf0b38..2bd69d1397 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseType.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseType.java @@ -25,7 +25,7 @@ */ public enum DatabaseType { - DB2, HSQL, H2, MARIADB, MYSQL, ORACLE, POSTGRES, SQL_SERVER("mssql"); + DB2, HSQL, H2, MARIADB, MYSQL, ORACLE, POSTGRES, SQL_SERVER("mssql"), GAUSSDB; private final String profile; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java new file mode 100644 index 0000000000..7b8d13a3f3 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java @@ -0,0 +1,159 @@ +/* + * Copyright 2019-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.testing; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; +import org.testcontainers.utility.DockerImageName; + +/** + * Testcontainers implementation for GaussDB. + * + * Exposed ports: 8000 + */ +public class GaussDBContainer> extends JdbcDatabaseContainer { + + public static final String NAME = "gaussdb"; + + public static final String IMAGE = "opengauss/opengauss"; + + public static final String DEFAULT_TAG = "latest"; + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("opengauss/opengauss") + .asCompatibleSubstituteFor("gaussdb"); + + public static final Integer GaussDB_PORT = 8000; + + public static final String DEFAULT_USER_NAME = "r2dbc_test"; + + public static final String DEFAULT_PASSWORD = "R2dbc_test@12"; + + private String databaseName = "postgres"; + + private String username = DEFAULT_USER_NAME; + + private String password = DEFAULT_PASSWORD; + + public GaussDBContainer() { + this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); + } + + public GaussDBContainer(final String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public GaussDBContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + setWaitStrategy(new WaitStrategy() { + @Override + public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { + Wait.forListeningPort().waitUntilReady(waitStrategyTarget); + try { + // Open Gauss will set up users and password when ports are ready. + Wait.forLogMessage(".*gs_ctl stopped.*", 1).waitUntilReady(waitStrategyTarget); + // Not enough and no idea + TimeUnit.SECONDS.sleep(3); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public WaitStrategy withStartupTimeout(Duration duration) { + return Wait.forListeningPort().withStartupTimeout(duration); + } + }); + } + + @Override + protected void configure() { + // Disable Postgres driver use of java.util.logging to reduce noise at startup time + withUrlParam("loggerLevel", "OFF"); + withDatabaseName(databaseName); + addExposedPorts(GaussDB_PORT); + addFixedExposedPort(GaussDB_PORT, GaussDB_PORT); + addEnv("GS_PORT", String.valueOf(GaussDB_PORT)); + addEnv("GS_USERNAME", username); + addEnv("GS_PASSWORD", password); + } + + @Override + public String getDriverClassName() { + return "com.huawei.gaussdb.jdbc.Driver"; + } + + @Override + public String getJdbcUrl() { + String additionalUrlParams = constructUrlParameters("?", "&"); + return ( + "jdbc:gaussdb://" + + getHost() + + ":" + + getMappedPort(GaussDB_PORT) + + "/" + + databaseName + + additionalUrlParams + ); + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getTestQueryString() { + return "SELECT 1"; + } + + @Override + public SELF withDatabaseName(final String databaseName) { + this.databaseName = databaseName; + return self(); + } + + @Override + public SELF withUsername(final String username) { + this.username = username; + return self(); + } + + @Override + public SELF withPassword(final String password) { + this.password = password; + return self(); + } + + @Override + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); + } +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainerProvider.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainerProvider.java new file mode 100644 index 0000000000..be5b50a0fe --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainerProvider.java @@ -0,0 +1,52 @@ +/* + * Copyright 2019-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.testing; + +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.JdbcDatabaseContainerProvider; +import org.testcontainers.jdbc.ConnectionUrl; +import org.testcontainers.utility.DockerImageName; + +/** + * Factory for GaussDB containers. + */ +@SuppressWarnings("rawtypes") +public class GaussDBContainerProvider extends JdbcDatabaseContainerProvider { + + public static final String USER_PARAM = "user"; + + public static final String PASSWORD_PARAM = "password"; + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(GaussDBContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(GaussDBContainer.DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new GaussDBContainer(DockerImageName.parse(GaussDBContainer.IMAGE).withTag(tag)); + } + + @Override + public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + } +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBDataSourceConfiguration.java new file mode 100644 index 0000000000..cc87af2326 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBDataSourceConfiguration.java @@ -0,0 +1,73 @@ +/* + * Copyright 2017-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.jdbc.testing; + +import javax.sql.DataSource; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; + +import com.huawei.gaussdb.jdbc.ds.PGSimpleDataSource; +import com.huawei.gaussdb.jdbc.util.PSQLException; + +/** + * {@link DataSource} setup for GaussDB. Starts a docker container with a GaussDB database. + *

+ * Notes: this file is token from PostgresDataSourceConfiguration and add specific changes for GaussDB + * + * @author liubao + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnDatabase(DatabaseType.GAUSSDB) +public class GaussDBDataSourceConfiguration extends DataSourceConfiguration { + + private static GaussDBContainer GAUSSDB_CONTAINER; + + public GaussDBDataSourceConfiguration(TestClass testClass, Environment environment) { + super(testClass, environment); + } + + @Override + protected DataSource createDataSource() { + + if (GAUSSDB_CONTAINER == null) { + + GaussDBContainer container = new GaussDBContainer<>(); + container.start(); + + GAUSSDB_CONTAINER = container; + } + + try { + PGSimpleDataSource dataSource = new PGSimpleDataSource(); + dataSource.setUrl(GAUSSDB_CONTAINER.getJdbcUrl()); + dataSource.setUser(GAUSSDB_CONTAINER.getUsername()); + dataSource.setPassword(GAUSSDB_CONTAINER.getPassword()); + + return dataSource; + } catch (PSQLException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void customizePopulator(ResourceDatabasePopulator populator) { + populator.setIgnoreFailedDrops(true); + } + +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java index 85f730a909..3a62ed1d17 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java @@ -30,7 +30,7 @@ * @author Oliver Gierke */ @Configuration(proxyBeanMethods = false) -@Profile({ "hsql", "!h2 && !mysql && !mariadb && !postgres && !oracle && !db2 && !mssql" }) +@Profile({ "hsql", "!h2 && !mysql && !mariadb && !postgres && !oracle && !db2 && !mssql && !gaussdb" }) class HsqlDataSourceConfiguration { private final TestClass testClass; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java index 0a985bd5ad..18ac27b3be 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java @@ -62,12 +62,12 @@ private void supportsGeneratedIdsInReferencedEntities() { private void supportsArrays() { - assumeThat(database).isNotIn(Database.MySql, Database.MariaDb, Database.SqlServer, Database.Db2, Database.Oracle); + assumeThat(database).isNotIn(Database.MySql, Database.MariaDb, Database.SqlServer, Database.Db2, Database.Oracle, Database.GaussDB); } private void supportsNanosecondPrecision() { - assumeThat(database).isNotIn(Database.MySql, Database.PostgreSql, Database.MariaDb, Database.SqlServer); + assumeThat(database).isNotIn(Database.MySql, Database.PostgreSql, Database.MariaDb, Database.SqlServer, Database.GaussDB); } private void supportsMultiDimensionalArrays() { @@ -93,7 +93,7 @@ public void databaseIs(Database database) { } public enum Database { - Hsql, H2, MySql, MariaDb, PostgreSql, SqlServer("microsoft"), Db2, Oracle; + Hsql, H2, MySql, MariaDb, PostgreSql, SqlServer("microsoft"), Db2, Oracle, GaussDB; private final String identification; diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core.dialect/GaussDBDialectIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core.dialect/GaussDBDialectIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..0d5df184f1 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core.dialect/GaussDBDialectIntegrationTests-gaussdb.sql @@ -0,0 +1,8 @@ +DROP TABLE customers; + +CREATE TABLE customers ( + id BIGSERIAL PRIMARY KEY, + name TEXT NOT NULL, + person_data JSONB, + session_data JSONB +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..d4760f65a7 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-gaussdb.sql @@ -0,0 +1,476 @@ +DROP TABLE MANUAL; +DROP TABLE LEGO_SET; +DROP TABLE ONE_TO_ONE_PARENT; +DROP TABLE Child_No_Id; +DROP TABLE element_no_id; +DROP TABLE "LIST_PARENT"; +DROP TABLE SIMPLE_LIST_PARENT; +DROP TABLE "ARRAY_OWNER"; +DROP TABLE DOUBLE_LIST_OWNER; +DROP TABLE FLOAT_LIST_OWNER; +DROP TABLE BYTE_ARRAY_OWNER; +DROP TABLE CHAIN0; +DROP TABLE CHAIN1; +DROP TABLE CHAIN2; +DROP TABLE CHAIN3; +DROP TABLE CHAIN4; +DROP TABLE NO_ID_CHAIN0; +DROP TABLE NO_ID_CHAIN1; +DROP TABLE NO_ID_CHAIN2; +DROP TABLE NO_ID_CHAIN3; +DROP TABLE NO_ID_CHAIN4; +DROP TABLE NO_ID_LIST_CHAIN0; +DROP TABLE NO_ID_LIST_CHAIN1; +DROP TABLE NO_ID_LIST_CHAIN2; +DROP TABLE NO_ID_LIST_CHAIN3; +DROP TABLE NO_ID_LIST_CHAIN4; +DROP TABLE NO_ID_MAP_CHAIN0; +DROP TABLE NO_ID_MAP_CHAIN1; +DROP TABLE NO_ID_MAP_CHAIN2; +DROP TABLE NO_ID_MAP_CHAIN3; +DROP TABLE NO_ID_MAP_CHAIN4; +DROP TABLE "VERSIONED_AGGREGATE"; +DROP TABLE WITH_READ_ONLY; +DROP TABLE WITH_LOCAL_DATE_TIME; +DROP TABLE WITH_ID_ONLY; +DROP TABLE WITH_INSERT_ONLY; + +DROP TABLE MULTIPLE_COLLECTIONS; +DROP TABLE MAP_ELEMENT; +DROP TABLE LIST_ELEMENT; +DROP TABLE SET_ELEMENT; + +DROP TABLE BOOK; +DROP TABLE AUTHOR; + +DROP TABLE ENUM_MAP_OWNER; +DROP SEQUENCE ENUM_MAP_OWNER_ID_SEQ; + +DROP TABLE REFERENCED; +DROP TABLE WITH_ONE_TO_ONE; + +DROP TABLE THIRD; +DROP TABLE SEC; +DROP TABLE FIRST; + +CREATE TABLE LEGO_SET +( + "id1" SERIAL PRIMARY KEY, + NAME VARCHAR(30) +); +CREATE TABLE MANUAL +( + "id2" SERIAL PRIMARY KEY, + LEGO_SET BIGINT, + ALTERNATIVE BIGINT, + CONTENT VARCHAR(2000) +); + +ALTER TABLE MANUAL + ADD FOREIGN KEY (LEGO_SET) + REFERENCES LEGO_SET ("id1"); + +CREATE TABLE ONE_TO_ONE_PARENT +( + "id3" SERIAL PRIMARY KEY, + content VARCHAR(30) +); +CREATE TABLE Child_No_Id +( + ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, + content VARCHAR(30) +); + +CREATE TABLE "LIST_PARENT" +( + "id4" SERIAL PRIMARY KEY, + NAME VARCHAR(100) +); + +CREATE TABLE SIMPLE_LIST_PARENT +( + id SERIAL PRIMARY KEY, + NAME VARCHAR(100) +); + +CREATE TABLE element_no_id +( + content VARCHAR(100), + LIST_PARENT_key BIGINT, + SIMPLE_LIST_PARENT_key BIGINT, + "LIST_PARENT" INTEGER, + SIMPLE_LIST_PARENT INTEGER +); + +CREATE TABLE "ARRAY_OWNER" +( + ID SERIAL PRIMARY KEY, + DIGITS VARCHAR(20)[10], + MULTIDIMENSIONAL VARCHAR(20)[10][10] +); + +CREATE TABLE DOUBLE_LIST_OWNER +( + ID SERIAL PRIMARY KEY, + DIGITS DOUBLE PRECISION[10] +); + +CREATE TABLE FLOAT_LIST_OWNER +( + ID SERIAL PRIMARY KEY, + DIGITS REAL[10] +); + +CREATE TABLE BYTE_ARRAY_OWNER +( + ID SERIAL PRIMARY KEY, + BINARY_DATA BYTEA NOT NULL +); + +CREATE TABLE CHAIN4 +( + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + +CREATE TABLE CHAIN3 +( + THREE SERIAL PRIMARY KEY, + THREE_VALUE VARCHAR(20), + CHAIN4 BIGINT, + FOREIGN KEY (CHAIN4) REFERENCES CHAIN4 (FOUR) +); + +CREATE TABLE CHAIN2 +( + TWO SERIAL PRIMARY KEY, + TWO_VALUE VARCHAR(20), + CHAIN3 BIGINT, + FOREIGN KEY (CHAIN3) REFERENCES CHAIN3 (THREE) +); + +CREATE TABLE CHAIN1 +( + ONE SERIAL PRIMARY KEY, + ONE_VALUE VARCHAR(20), + CHAIN2 BIGINT, + FOREIGN KEY (CHAIN2) REFERENCES CHAIN2 (TWO) +); + +CREATE TABLE CHAIN0 +( + ZERO SERIAL PRIMARY KEY, + ZERO_VALUE VARCHAR(20), + CHAIN1 BIGINT, + FOREIGN KEY (CHAIN1) REFERENCES CHAIN1 (ONE) +); + +CREATE TABLE NO_ID_CHAIN4 +( + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + +CREATE TABLE NO_ID_CHAIN3 +( + THREE_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_CHAIN2 +( + TWO_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_CHAIN1 +( + ONE_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_CHAIN0 +( + ZERO_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + + +CREATE TABLE NO_ID_LIST_CHAIN4 +( + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + +CREATE TABLE NO_ID_LIST_CHAIN3 +( + THREE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY), + FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_LIST_CHAIN2 +( + TWO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) REFERENCES NO_ID_LIST_CHAIN3 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) +); + +CREATE TABLE NO_ID_LIST_CHAIN1 +( + ONE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + NO_ID_LIST_CHAIN2_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) REFERENCES NO_ID_LIST_CHAIN2 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) +); + +CREATE TABLE NO_ID_LIST_CHAIN0 +( + ZERO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + NO_ID_LIST_CHAIN2_KEY BIGINT, + NO_ID_LIST_CHAIN1_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY, + NO_ID_LIST_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) REFERENCES NO_ID_LIST_CHAIN1 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) +); + + + +CREATE TABLE NO_ID_MAP_CHAIN4 +( + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + +CREATE TABLE NO_ID_MAP_CHAIN3 +( + THREE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY), + FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_MAP_CHAIN2 +( + TWO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) REFERENCES NO_ID_MAP_CHAIN3 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) +); + +CREATE TABLE NO_ID_MAP_CHAIN1 +( + ONE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) REFERENCES NO_ID_MAP_CHAIN2 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) +); + +CREATE TABLE NO_ID_MAP_CHAIN0 +( + ZERO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + NO_ID_MAP_CHAIN1_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY, + NO_ID_MAP_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) REFERENCES NO_ID_MAP_CHAIN1 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) +); + +CREATE TABLE "VERSIONED_AGGREGATE" +( + ID SERIAL PRIMARY KEY, + VERSION BIGINT +); + +CREATE TABLE WITH_READ_ONLY +( + ID SERIAL PRIMARY KEY, + NAME VARCHAR(200), + READ_ONLY VARCHAR(200) DEFAULT 'from-db' +); + +CREATE TABLE WITH_LOCAL_DATE_TIME +( + ID BIGINT PRIMARY KEY, + TEST_TIME TIMESTAMP(9) WITHOUT TIME ZONE +); + +CREATE TABLE WITH_ID_ONLY +( + ID SERIAL PRIMARY KEY +); + +CREATE TABLE WITH_INSERT_ONLY +( + ID SERIAL PRIMARY KEY, + INSERT_ONLY VARCHAR(100) +); + +CREATE TABLE MULTIPLE_COLLECTIONS +( + ID SERIAL PRIMARY KEY, + NAME VARCHAR(100) +); + +CREATE TABLE SET_ELEMENT +( + MULTIPLE_COLLECTIONS BIGINT, + NAME VARCHAR(100) +); + +CREATE TABLE LIST_ELEMENT +( + MULTIPLE_COLLECTIONS BIGINT, + MULTIPLE_COLLECTIONS_KEY INT, + NAME VARCHAR(100) +); + +CREATE TABLE MAP_ELEMENT +( + MULTIPLE_COLLECTIONS BIGINT, + MULTIPLE_COLLECTIONS_KEY VARCHAR(10), + ENUM_MAP_OWNER BIGINT, + ENUM_MAP_OWNER_KEY VARCHAR(10), + NAME VARCHAR(100) +); + +CREATE TABLE AUTHOR +( + ID SERIAL PRIMARY KEY +); + +CREATE TABLE BOOK +( + AUTHOR BIGINT, + NAME VARCHAR(100) +); + +CREATE SEQUENCE ENUM_MAP_OWNER_ID_SEQ; + +CREATE TABLE ENUM_MAP_OWNER +( + ID BIGINT DEFAULT nextval('ENUM_MAP_OWNER_ID_SEQ') PRIMARY KEY, + NAME VARCHAR(100) +); + +CREATE TABLE WITH_ONE_TO_ONE +( + ID VARCHAR(100) +); + +CREATE TABLE REFERENCED +( + "renamed" VARCHAR(100), + ID BIGINT +); + +CREATE TABLE FIRST +( + ID BIGINT NOT NULL PRIMARY KEY, + NAME VARCHAR(20) NOT NULL +); + +CREATE TABLE SEC +( + ID BIGINT NOT NULL PRIMARY KEY, + FIRST BIGINT NOT NULL, + NAME VARCHAR(20) NOT NULL, + FOREIGN KEY (FIRST) REFERENCES FIRST (ID) +); + +CREATE TABLE THIRD +( + SEC BIGINT NOT NULL, + NAME VARCHAR(20) NOT NULL, + FOREIGN KEY (SEC) REFERENCES SEC (ID) +); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..952dd1e345 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-gaussdb.sql @@ -0,0 +1,14 @@ +CREATE SCHEMA OTHER; + +CREATE TABLE OTHER.DUMMY_ENTITY +( + ID SERIAL PRIMARY KEY, + NAME VARCHAR(30) +); + +CREATE TABLE OTHER.REFERENCED +( + DUMMY_ENTITY INTEGER, + NAME VARCHAR(30) +); + diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesBrokenTransactionManagerRefIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesBrokenTransactionManagerRefIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..78469c6d31 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesBrokenTransactionManagerRefIntegrationTests-gaussdb.sql @@ -0,0 +1,2 @@ +DROP TABLE Dummy_Entity +CREATE TABLE Dummy_Entity ( id SERIAL PRIMARY KEY) \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..78469c6d31 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-gaussdb.sql @@ -0,0 +1,2 @@ +DROP TABLE Dummy_Entity +CREATE TABLE Dummy_Entity ( id SERIAL PRIMARY KEY) \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..ed7a483c99 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-gaussdb.sql @@ -0,0 +1,4 @@ +DROP TABLE dummy_entity; +DROP TABLE element; +CREATE TABLE dummy_entity ( id SERIAL PRIMARY KEY, NAME VARCHAR(100)); +CREATE TABLE element (id SERIAL PRIMARY KEY, content BIGINT, Dummy_Entity_key BIGINT,dummy_entity BIGINT); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..882d8df894 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-gaussdb.sql @@ -0,0 +1,2 @@ +CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id SERIAL PRIMARY KEY, Stringy_number DECIMAL(20,10), DIRECTION INTEGER); +CREATE TABLE OTHER_ENTITY ( ID SERIAL PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..dc84d871df --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-gaussdb.sql @@ -0,0 +1,2 @@ +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity (id SERIAL PRIMARY KEY, PREFIX_ATTR1 BIGINT, PREFIX_ATTR2 VARCHAR(100)); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..fa4b1a1392 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-gaussdb.sql @@ -0,0 +1,13 @@ +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100) +); +DROP TABLE dummy_entity2; +CREATE TABLE dummy_entity2 +( + "ID" INTEGER PRIMARY KEY, + TEST VARCHAR(100), + PREFIX_ATTR BIGINT +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..e16ee36311 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-gaussdb.sql @@ -0,0 +1,15 @@ +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100), + PREFIX_TEST VARCHAR(100) +); +DROP TABLE dummy_entity2; +CREATE TABLE dummy_entity2 +( + "DUMMY_ID" BIGINT, + "ORDER_KEY" BIGINT, + TEST VARCHAR(100), + PRIMARY KEY ("DUMMY_ID", "ORDER_KEY") +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..c8128208d3 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-gaussdb.sql @@ -0,0 +1,22 @@ +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100), + PREFIX_TEST VARCHAR(100) +); +DROP TABLE dummy_entity2; +CREATE TABLE dummy_entity2 +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100) +); +-- +-- SELECT "dummy_entity"."ID" AS "ID", +-- "dummy_entity"."test" AS "test", +-- "dummy_entity"."prefix_test" AS "prefix_test", +-- "PREFIX_dummyEntity2"."id" AS "prefix_dummyentity2_id", +-- "PREFIX_dummyEntity2"."test" AS "prefix_dummyentity2_test" +-- FROM "dummy_entity" +-- LEFT OUTER JOIN "dummy_entity2" AS "PREFIX_dummyEntity2" ON +-- "PREFIX_dummyEntity2"."ID" = "dummy_entity"."ID" \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..8f79fc83e6 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-gaussdb.sql @@ -0,0 +1,16 @@ +DROP TABLE ReadOnlyIdEntity; +DROP TABLE PrimitiveIdEntity; +DROP TABLE ImmutableWithManualIdentity; +DROP TABLE EntityWithSeq; +DROP TABLE PersistableEntityWithSeq; +DROP TABLE PrimitiveIdEntityWithSeq; + +CREATE TABLE ReadOnlyIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100)); +CREATE TABLE PrimitiveIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100)); +CREATE TABLE ImmutableWithManualIdentity (ID BIGINT PRIMARY KEY, NAME VARCHAR(100)); +CREATE TABLE SimpleSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100)); +CREATE SEQUENCE simple_seq_seq START WITH 1; +CREATE TABLE PersistableSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100)); +CREATE SEQUENCE persistable_seq_seq START WITH 1; +CREATE TABLE PrimitiveIdSeq (ID BIGINT NOT NULL PRIMARY KEY, NAME VARCHAR(100)); +CREATE SEQUENCE primitive_seq_seq START WITH 1; \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryInsertExistingIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryInsertExistingIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..65f67a5bf1 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryInsertExistingIntegrationTests-gaussdb.sql @@ -0,0 +1,2 @@ +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity (id_Prop INTEGER PRIMARY KEY, NAME VARCHAR(100)); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..42e69437a7 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-gaussdb.sql @@ -0,0 +1,58 @@ +DROP TABLE dummy_entity; +DROP TABLE ROOT; +DROP TABLE INTERMEDIATE; +DROP TABLE LEAF; +DROP TABLE WITH_DELIMITED_COLUMN; +DROP TABLE ENTITY_WITH_SEQUENCE; +DROP SEQUENCE ENTITY_SEQUENCE; + +CREATE TABLE dummy_entity +( + id_Prop SERIAL PRIMARY KEY, + NAME VARCHAR(100), + POINT_IN_TIME TIMESTAMP, + OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE, + FLAG BOOLEAN, + REF BIGINT, + DIRECTION VARCHAR(100), + BYTES BYTEA +); + +CREATE TABLE ROOT +( + ID SERIAL PRIMARY KEY, + NAME VARCHAR(100) +); + +CREATE TABLE INTERMEDIATE +( + ID SERIAL PRIMARY KEY, + NAME VARCHAR(100), + ROOT BIGINT, + "ROOT_ID" BIGINT, + "ROOT_KEY" INTEGER +); + +CREATE TABLE LEAF +( + ID SERIAL PRIMARY KEY, + NAME VARCHAR(100), + INTERMEDIATE BIGINT, + "INTERMEDIATE_ID" BIGINT, + "INTERMEDIATE_KEY" INTEGER +); + +CREATE TABLE "WITH_DELIMITED_COLUMN" +( + ID SERIAL PRIMARY KEY, + "ORG.XTUNIT.IDENTIFIER" VARCHAR(100), + "STYPE" VARCHAR(100) +); + +CREATE TABLE ENTITY_WITH_SEQUENCE +( + ID BIGINT, + NAME VARCHAR(100) +); + +CREATE SEQUENCE "ENTITY_SEQUENCE" START WITH 1 INCREMENT BY 1 NO MAXVALUE; \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..27d9fa6d8a --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-gaussdb.sql @@ -0,0 +1,5 @@ +DROP TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS_RELATION; +DROP TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS; + +CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS ( id_Timestamp TIMESTAMP PRIMARY KEY, bool boolean, SOME_ENUM VARCHAR(100), big_Decimal DECIMAL(65), big_Integer BIGINT, date TIMESTAMP, local_Date_Time TIMESTAMP, zoned_Date_Time VARCHAR(30)); +CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS_RELATION ( "ID_TIMESTAMP" TIMESTAMP PRIMARY KEY, data VARCHAR(100)); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..368434103a --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-gaussdb.sql @@ -0,0 +1,5 @@ +DROP TABLE person; +DROP TABLE address; +CREATE TABLE person ( id SERIAL PRIMARY KEY, name VARCHAR(100)); +CREATE TABLE address ( id SERIAL PRIMARY KEY, street VARCHAR(100), person_id INT); +ALTER TABLE address ADD FOREIGN KEY (person_id) REFERENCES person(id); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..824a1e9481 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-gaussdb.sql @@ -0,0 +1,4 @@ +DROP TABLE element; +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity ( id SERIAL PRIMARY KEY, NAME VARCHAR(100)); +CREATE TABLE element (id SERIAL PRIMARY KEY, content VARCHAR(100), dummy_entity BIGINT); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsNoIdIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsNoIdIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..8c9eb5b48f --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsNoIdIntegrationTests-gaussdb.sql @@ -0,0 +1,4 @@ +DROP TABLE element; +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity ( id SERIAL PRIMARY KEY, NAME VARCHAR(100)); +CREATE TABLE element (content VARCHAR(100), dummy_entity BIGINT); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..a0a3d8157c --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-gaussdb.sql @@ -0,0 +1,36 @@ +DROP TABLE element; +DROP TABLE dummy_entity; + +DROP TABLE root; +DROP TABLE intermediate; +DROP TABLE leaf; + +CREATE TABLE dummy_entity +( + id SERIAL PRIMARY KEY, + NAME VARCHAR(100) +); +CREATE TABLE element +( + id SERIAL PRIMARY KEY, + content VARCHAR(100), + dummy_entity_key BIGINT, + dummy_entity BIGINT +); + +CREATE TABLE root +( + id SERIAL PRIMARY KEY +); +CREATE TABLE intermediate +( + id SERIAL PRIMARY KEY, + root BIGINT NOT NULL, + root_key INTEGER NOT NULL +); +CREATE TABLE leaf +( + name VARCHAR(100), + intermediate BIGINT NOT NULL, + intermediate_key INTEGER NOT NULL +); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..6d1c0b3e56 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-gaussdb.sql @@ -0,0 +1,4 @@ +DROP TABLE element; +DROP TABLE dummy_entity; +CREATE TABLE dummy_entity ( id SERIAL PRIMARY KEY, NAME VARCHAR(100)); +CREATE TABLE element (id SERIAL PRIMARY KEY, content VARCHAR(100),dummy_entity_key VARCHAR(100), dummy_entity BIGINT); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-gaussdb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-gaussdb.sql new file mode 100644 index 0000000000..0118aeda21 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-gaussdb.sql @@ -0,0 +1,2 @@ +DROP TABLE car; +CREATE TABLE car ( id SERIAL PRIMARY KEY, model VARCHAR(100)); \ No newline at end of file diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/GaussDBDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/GaussDBDialect.java new file mode 100644 index 0000000000..e0ca62efba --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/GaussDBDialect.java @@ -0,0 +1,173 @@ +/* + * Copyright 2019-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.relational.core.dialect; + +import java.net.InetAddress; +import java.net.URI; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.springframework.data.relational.core.sql.Functions; +import org.springframework.data.relational.core.sql.IdentifierProcessing; +import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; +import org.springframework.data.relational.core.sql.LockOptions; +import org.springframework.data.relational.core.sql.SQL; +import org.springframework.data.relational.core.sql.SimpleFunction; +import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.data.relational.core.sql.TableLike; + +/** + * An SQL dialect for GaussDB. + * + * Notes: this file is token from PostgresDialect and add specific changes for GaussDB + * + * @author liubao + */ +public class GaussDBDialect extends AbstractDialect { + + /** + * Singleton instance. + */ + public static final GaussDBDialect INSTANCE = new GaussDBDialect(); + + private static final Set> POSTGRES_SIMPLE_TYPES = Set.of(UUID.class, URL.class, URI.class, InetAddress.class, + Map.class); + + private final IdentifierProcessing identifierProcessing = IdentifierProcessing.create(Quoting.ANSI, + LetterCasing.LOWER_CASE); + + private final IdGeneration idGeneration = new IdGeneration() { + + @Override + public String createSequenceQuery(SqlIdentifier sequenceName) { + return "SELECT nextval('%s')".formatted(sequenceName.toSql(getIdentifierProcessing())); + } + }; + + protected GaussDBDialect() {} + + private static final LimitClause LIMIT_CLAUSE = new LimitClause() { + + @Override + public String getLimit(long limit) { + return "LIMIT " + limit; + } + + @Override + public String getOffset(long offset) { + return "OFFSET " + offset; + } + + @Override + public String getLimitOffset(long limit, long offset) { + return String.format("LIMIT %d OFFSET %d", limit, offset); + } + + @Override + public Position getClausePosition() { + return Position.AFTER_ORDER_BY; + } + }; + + private static final ObjectArrayColumns ARRAY_COLUMNS = ObjectArrayColumns.INSTANCE; + + @Override + public LimitClause limit() { + return LIMIT_CLAUSE; + } + + private final PostgresLockClause LOCK_CLAUSE = new PostgresLockClause(this.getIdentifierProcessing()); + + @Override + public LockClause lock() { + return LOCK_CLAUSE; + } + + @Override + public ArrayColumns getArraySupport() { + return ARRAY_COLUMNS; + } + + @Override + public Collection getConverters() { + return Collections.singletonList(TimestampAtUtcToOffsetDateTimeConverter.INSTANCE); + } + + static class PostgresLockClause implements LockClause { + + private final IdentifierProcessing identifierProcessing; + + PostgresLockClause(IdentifierProcessing identifierProcessing) { + this.identifierProcessing = identifierProcessing; + } + + @Override + public String getLock(LockOptions lockOptions) { + + List tables = lockOptions.getFrom().getTables(); + if (tables.isEmpty()) { + return ""; + } + + // get the first table and obtain last part if the identifier is a composed one. + SqlIdentifier identifier = tables.get(0).getName(); + SqlIdentifier last = identifier; + + for (SqlIdentifier sqlIdentifier : identifier) { + last = sqlIdentifier; + } + + // without schema + String tableName = last.toSql(this.identifierProcessing); + + return switch (lockOptions.getLockMode()) { + case PESSIMISTIC_WRITE -> "FOR UPDATE OF " + tableName; + case PESSIMISTIC_READ -> "FOR SHARE OF " + tableName; + }; + } + + @Override + public Position getClausePosition() { + return Position.AFTER_ORDER_BY; + } + } + + @Override + public IdentifierProcessing getIdentifierProcessing() { + return identifierProcessing; + } + + @Override + public Set> simpleTypes() { + return POSTGRES_SIMPLE_TYPES; + } + + @Override + public SimpleFunction getExistsFunction() { + return Functions.least(Functions.count(SQL.literalOf(1)), SQL.literalOf(1)); + } + + @Override + public IdGeneration getIdGeneration() { + return idGeneration; + } +} From 9909d89a9d337d9a9c13b8ec58b3e5a91e4eaff8 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Mon, 21 Apr 2025 14:16:35 +0800 Subject: [PATCH 2/3] Add GaussDB support for spring data jdbc Signed-off-by: liubao68 --- .../data/jdbc/testing/GaussDBContainer.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java index 7b8d13a3f3..4723a17276 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java @@ -33,12 +33,11 @@ public class GaussDBContainer> extends JdbcD public static final String NAME = "gaussdb"; - public static final String IMAGE = "opengauss/opengauss"; + public static final String IMAGE = "enmotech/opengauss-lite"; - public static final String DEFAULT_TAG = "latest"; + public static final String DEFAULT_TAG = "5.0.3"; - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("opengauss/opengauss") - .asCompatibleSubstituteFor("gaussdb"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(IMAGE); public static final Integer GaussDB_PORT = 8000; @@ -56,10 +55,6 @@ public GaussDBContainer() { this(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); } - public GaussDBContainer(final String dockerImageName) { - this(DockerImageName.parse(dockerImageName)); - } - public GaussDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); setWaitStrategy(new WaitStrategy() { From c6c46d1b504818b34b92f6dce7569feb706af875 Mon Sep 17 00:00:00 2001 From: liubao68 Date: Tue, 22 Apr 2025 16:20:07 +0800 Subject: [PATCH 3/3] update image to latest Signed-off-by: liubao68 --- .../springframework/data/jdbc/testing/GaussDBContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java index 4723a17276..c3da7fd50f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/GaussDBContainer.java @@ -33,9 +33,9 @@ public class GaussDBContainer> extends JdbcD public static final String NAME = "gaussdb"; - public static final String IMAGE = "enmotech/opengauss-lite"; + public static final String IMAGE = "opengauss/opengauss"; - public static final String DEFAULT_TAG = "5.0.3"; + public static final String DEFAULT_TAG = "7.0.0-RC1.B023"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(IMAGE);